[
  {
    "path": ".cargo/config.toml",
    "content": "[env]\nSLU_SERVICE_CONNECTION_TOKEN = \"__local__\"\nTS_RS_EXPORT_DIR = \"./gen/types\"\nTS_RS_IMPORT_EXTENSION = \"ts\"\nTS_RS_LARGE_INT = \"number\"\n"
  },
  {
    "path": ".cert/Seelen.pfx.pwd",
    "content": "Seelen\n"
  },
  {
    "path": ".cert/readme.md",
    "content": "# Self Signed Certificates\n\n## Creation\n\n```pwsh\nmsixherocli.exe newcert --directory .\\.cert --name Seelen --password Seelen --subject CN=7E60225C-94CB-4B2E-B17F-0159A11074CB --validUntil \"14/12/2026 6:31:10 pm\"\n```\n\n## Usage\n\nIn this directory, you will find the self-signed certificates used in the development environment. You can add it to\nyour system to trust nighly msix packages.\n\n1. Download Seelen.pfx\n2. Open a powershell terminal as administrator\n3. Go to the directory where you downloaded the file\n4. Run the following command\n\n```pwsh\n$password = ConvertTo-SecureString -String Seelen -Force -AsPlainText\nImport-PfxCertificate -FilePath .\\Seelen.pfx -CertStoreLocation Cert:\\LocalMachine\\root -Password $password\n```\n\n> [!NOTE]\n> These files expire each year and should be replaced with new ones.\n"
  },
  {
    "path": ".commitlintrc.yml",
    "content": "# commitlint.config.yml\nextends:\n  - \"@commitlint/config-conventional\"\n\nrules:\n  # Basic rules\n  header-max-length: [2, \"always\", 72]\n  body-max-line-length: [2, \"always\", 100]\n\n  # Commit type\n  type-enum:\n    - 2\n    - \"always\"\n    - [\n        \"feat\", # New feature\n        \"enh\", # Enhancement of an existing feature\n        \"fix\", # Bug fix\n        \"docs\", # Documentation changes\n        \"style\", # Code formatting, white spaces, etc.\n        \"refactor\", # Code refactoring\n        \"perf\", # Performance improvement\n        \"test\", # Adding or fixing tests\n        \"build\", # Changes affecting the build system or external dependencies\n        \"ci\", # Changes to CI configuration files and scripts\n        \"chore\", # Other changes that don't modify src or test files\n        \"delete\", # Deleting unused files\n        \"revert\", # Reverting to a previous commit\n      ]\n\n  scope-empty: [2, \"never\"]\n  subject-empty: [2, \"never\"]\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [eythaann]\nko_fi: eythaann\npatreon: eythaann\ncustom:\n  - https://www.paypal.me/eythaann\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report 🐛\nabout: Create a report to help us improve\ntitle: \"[BUG] Short description of the issue\"\nlabels: bug\nassignees: \"\"\n---\n\n### Description of the issue\n\nA clear and concise description of what's wrong.\n\n### To reproduce\n\nSteps to reproduce the behavior:\n\n1. Go to '...'\n2. Click on '....'\n3. Scroll to '....'\n4. See error\n\n### Expected behavior\n\nExplain what you expected to happen.\n\n### Evidence\n\n- **Screenshots/Videos**: If applicable, add visual proof\n- **Log File**: Please attach the latest log file from: `%LocalAppdata%\\com.seelen.seelen-ui\\logs` (This helps us\n  diagnose the issue)\n\n### Additional context\n\nAdd any other relevant information here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[FEAT] Include a pandora box\"\nlabels: enhancement, feature request\nassignees: \"\"\n---\n\n**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem\nis. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like** A clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features\nyou've considered.\n\n**Additional context** Add any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/actions/generate-update-manifest/action.yml",
    "content": "name: Generate Update Manifest\ndescription: Generates and uploads latest.json for Tauri updater\n\ninputs:\n  release-id:\n    description: Release ID to get the release from\n    required: true\n  version:\n    description: Version to use in the manifest\n    required: true\n  github-token:\n    description: GitHub token for API access\n    required: true\n\nruns:\n  using: composite\n  steps:\n    - name: Generate and Upload latest.json\n      uses: actions/github-script@v7\n      with:\n        github-token: ${{ inputs.github-token }}\n        script: |\n          const releaseId = '${{ inputs.release-id }}';\n          const version = '${{ inputs.version }}'.replaceAll('\"', '');\n\n          core.info(`Using version: ${version}`);\n\n          // Get the release\n          const release = await github.rest.repos.getRelease({\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            release_id: releaseId,\n          });\n\n          core.info(`Found release: ${release.data.name} (ID: ${release.data.id})`);\n\n          // Get all assets from the release\n          const { data: assets } = await github.rest.repos.listReleaseAssets({\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            release_id: release.data.id,\n          });\n\n          core.info(`Found ${assets.length} assets`);\n\n          // Filter relevant assets (exe and sig files)\n          const relevantAssets = assets.filter(asset =>\n            asset.name.endsWith('.exe') || asset.name.endsWith('.sig')\n          );\n\n          if (relevantAssets.length === 0) {\n            throw new Error('No relevant assets found in release');\n          }\n\n          // Build the update manifest\n          const update = {\n            version,\n            notes: `Seelen UI build ${version}`,\n            pub_date: new Date().toISOString(),\n            platforms: {\n              \"windows-x86_64\": { signature: \"\", url: \"\" },\n              \"windows-aarch64\": { signature: \"\", url: \"\" },\n            },\n          };\n\n          // Map assets to platforms\n          for (const asset of relevantAssets) {\n            const platform = asset.name.includes('x64')\n              ? 'windows-x86_64'\n              : asset.name.includes('arm64')\n                ? 'windows-aarch64'\n                : null;\n\n            if (!platform) {\n              core.warning(`Skipping asset with unknown platform: ${asset.name}`);\n              continue;\n            }\n\n            if (asset.name.endsWith('.sig')) {\n              // Download signature content\n              const sigResponse = await github.request('GET /repos/{owner}/{repo}/releases/assets/{asset_id}', {\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                asset_id: asset.id,\n                headers: { accept: 'application/octet-stream' },\n              });\n              update.platforms[platform].signature = Buffer.from(sigResponse.data).toString('utf-8');\n              core.info(`Added signature for ${platform}`);\n            }\n\n            if (asset.name.endsWith('.exe')) {\n              update.platforms[platform].url = asset.browser_download_url;\n              core.info(`Added URL for ${platform}: ${asset.browser_download_url}`);\n            }\n          }\n\n          // Validate and remove incomplete platforms\n          for (const [platform, data] of Object.entries(update.platforms)) {\n            if (!data.signature || !data.url) {\n              core.warning(`Platform ${platform} is incomplete - removing`);\n              delete update.platforms[platform];\n            }\n          }\n\n          if (Object.keys(update.platforms).length === 0) {\n            throw new Error('No valid platforms found in assets');\n          }\n\n          const manifestJson = JSON.stringify(update, null, 2);\n          core.info('Generated update manifest:');\n          core.info(manifestJson);\n\n          // Delete existing latest.json if it exists\n          const existingManifest = assets.find(a => a.name === 'latest.json');\n          if (existingManifest) {\n            core.info('Deleting existing latest.json');\n            await github.rest.repos.deleteReleaseAsset({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              asset_id: existingManifest.id,\n            });\n          }\n\n          // Upload the new manifest\n          core.info('Uploading latest.json to release');\n          const manifestBuffer = Buffer.from(manifestJson, 'utf-8');\n          await github.rest.repos.uploadReleaseAsset({\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            release_id: release.data.id,\n            name: 'latest.json',\n            data: manifestBuffer,\n            headers: {\n              'content-type': 'application/json',\n              'content-length': manifestBuffer.length,\n            },\n          });\n\n          core.info('✅ Successfully generated and uploaded latest.json');\n\n          // Delete .sig files from release assets\n          core.info('Deleting .sig files from release assets');\n          const sigAssets = assets.filter(a => a.name.endsWith('.sig'));\n\n          for (const sigAsset of sigAssets) {\n            core.info(`Deleting ${sigAsset.name}`);\n            await github.rest.repos.deleteReleaseAsset({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              asset_id: sigAsset.id,\n            });\n          }\n\n          core.info(`✅ Deleted ${sigAssets.length} .sig file(s) from release`);\n"
  },
  {
    "path": ".github/actions/setup/action.yml",
    "content": "name: Setup Development Environment\ndescription: Sets up Node.js, Deno, and Rust toolchain with caching\n\ninputs:\n  rust-components:\n    description: Additional Rust components to install (comma-separated)\n    required: false\n    default: \"\"\n  rust-targets:\n    description: Additional Rust targets to install (comma-separated)\n    required: false\n    default: \"\"\n  cache-key-prefix:\n    description: Prefix for cache keys\n    required: false\n    default: \"rust\"\n\nruns:\n  using: composite\n  steps:\n    - name: Setup Node.js\n      uses: actions/setup-node@v4\n      with:\n        node-version: 24\n        cache: npm\n\n    - name: Setup Deno\n      uses: denoland/setup-deno@v2\n      with:\n        deno-version: v2.x\n\n    - name: Setup Rust\n      uses: dtolnay/rust-toolchain@master\n      with:\n        toolchain: nightly-2025-09-12\n        components: ${{ inputs.rust-components }}\n        targets: ${{ inputs.rust-targets }}\n\n    - name: Add Rust targets\n      if: inputs.rust-targets != ''\n      shell: bash\n      run: |\n        IFS=',' read -ra TARGETS <<< \"${{ inputs.rust-targets }}\"\n        for target in \"${TARGETS[@]}\"; do\n          rustup target add \"$target\"\n        done\n\n    - name: Cache Rust dependencies\n      uses: actions/cache@v4\n      with:\n        path: |\n          ~/.cargo/bin/\n          ~/.cargo/registry/index/\n          ~/.cargo/registry/cache/\n          ~/.cargo/git/db/\n          target/\n        key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}\n        restore-keys: |\n          ${{ inputs.cache-key-prefix }}-${{ runner.os }}-\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: npm\n    directory: /\n    schedule:\n      interval: weekly\n  - package-ecosystem: cargo\n    directory: /\n    schedule:\n      interval: weekly\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Continuous Integration\n\non:\n  pull_request:\n    branches:\n      - master\n  workflow_dispatch:\n  workflow_call:\n\njobs:\n  js-test-and-linter:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n        with:\n          rust-components: rustfmt\n\n      - run: npm install\n      - run: deno fmt --check\n      - run: deno lint\n      - run: npm run type-check\n      - run: npm run test\n\n  rust-linter:\n    runs-on: windows-2025\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n\n      - name: Format\n        run: |-\n          rustup component add rustfmt\n          cargo fmt -- --check\n\n      - name: Linter\n        run: |-\n          rustup component add clippy\n          cargo clippy --locked --all-targets -- -D warnings\n\n  rust-test:\n    runs-on: windows-2025\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n        with:\n          cache-key-prefix: rust-test\n\n      - run: cargo test --locked --verbose\n"
  },
  {
    "path": ".github/workflows/dependabot-automerge.yml",
    "content": "name: Dependabot auto-merge\n\non: pull_request_target\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  automerge:\n    if: github.actor == 'dependabot[bot]' && github.event.pull_request.head.repo.fork == false\n    runs-on: ubuntu-latest\n    steps:\n      - name: Fetch Dependabot metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@v2\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Approve PR\n        run: gh pr review --approve \"$PR_URL\"\n        env:\n          PR_URL: ${{ github.event.pull_request.html_url }}\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Enable auto-merge (squash)\n        run: gh pr merge --auto --squash \"$PR_URL\"\n        env:\n          PR_URL: ${{ github.event.pull_request.html_url }}\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/discord-notify.yml",
    "content": "name: \"Discord Notify\"\n\non:\n  workflow_dispatch:\n    inputs:\n      tag:\n        description: \"Release tag to announce (e.g. v2.5.1)\"\n        required: true\n  workflow_call:\n    inputs:\n      tag:\n        description: \"Release tag to announce\"\n        type: string\n        required: true\n\njobs:\n  notify:\n    name: Send Announcement To Discord Server\n    runs-on: ubuntu-latest\n    steps:\n      - name: Discord notification\n        uses: actions/github-script@v7\n        env:\n          DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}\n        with:\n          script: |\n            const tag = '${{ inputs.tag }}';\n            const { data: release } = await github.rest.repos.getReleaseByTag({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag: tag,\n            });\n\n            let body = release.body || '';\n            // Strip HTML comments\n            body = body.replace(/<!--[\\s\\S]*?-->/g, '');\n            // Reduce heading levels (avoid h1/h2 dominating the embed)\n            body = body.replace(/^### /gm, '#### ').replace(/^## /gm, '### ').replace(/^# /gm, '## ');\n            // Normalise whitespace\n            body = body.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n').trim();\n            // Enforce Discord embed description limit\n            if (body.length > 4096) body = body.slice(0, 4093) + '...';\n\n            const payload = {\n              username: 'Seelen',\n              avatar_url: 'https://raw.githubusercontent.com/eythaann/Seelen-UI/master/documentation/images/logo_with_margins.png',\n              embeds: [{\n                title: release.name || tag,\n                description: body,\n                url: release.html_url,\n                color: 5814783,\n                footer: { text: 'Seelen Inc.' },\n              }],\n            };\n\n            const response = await fetch(process.env.DISCORD_WEBHOOK, {\n              method: 'POST',\n              headers: { 'Content-Type': 'application/json' },\n              body: JSON.stringify(payload),\n            });\n\n            if (!response.ok) {\n              const text = await response.text();\n              core.setFailed(`Discord webhook failed (${response.status}): ${text}`);\n            }\n"
  },
  {
    "path": ".github/workflows/msix.yml",
    "content": "name: Pull MSIX from Store\n\npermissions:\n  contents: write\n\non:\n  schedule:\n    - cron: \"0 0 * * *\" # Run the action every day\n  workflow_dispatch: # Manually run the action\n\njobs:\n  check-msix:\n    runs-on: ubuntu-latest\n    outputs:\n      MSIX_ALREADY_EXIST: ${{ steps.check_msix.outputs.result }}\n    steps:\n      - name: Check for MSIX file in latest release\n        id: check_msix\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const { owner, repo } = context.repo;\n            const latestRelease = await github.rest.repos.getLatestRelease({ owner, repo });\n\n            // Check if .msix file exists in the release assets\n            const msixAsset = latestRelease.data.assets.find(asset => asset.name.toLowerCase().endsWith('.msix'));\n            return msixAsset ? 'true' : 'false';\n          result-encoding: string\n\n      - name: Debug output\n        run: echo \"MSIX_ALREADY_EXIST value is ${{ steps.check_msix.outputs.result }}\"\n\n  upload-store-msix-to-release:\n    name: Upload Signed MSIX to release\n    needs: check-msix\n    if: ${{ needs.check-msix.outputs.MSIX_ALREADY_EXIST == 'false' }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Upload store MSIX to release\n        uses: JasonWei512/Upload-Microsoft-Store-MSIX-Package-to-GitHub-Release@v1\n        with:\n          store-id: 9P67C2D4T9FB\n          token: ${{ secrets.GITHUB_TOKEN }}\n          asset-name-pattern: Seelen.UI_{version}_{arch}\n\n  msix-to-winget:\n    name: MSIX to Winget\n    needs: upload-store-msix-to-release\n    runs-on: windows-2025\n    steps:\n      - name: Get Latest Release\n        id: get-version\n        uses: actions/github-script@v7\n        with:\n          script: |-\n            const { owner, repo } = context.repo;\n            const latestRelease = await github.rest.repos.getLatestRelease({ owner, repo });\n            const tag = latestRelease.data.tag_name;\n            const version = tag.replace(\"v\", \"\") + \".0\";\n            core.setOutput('version', version);\n            core.setOutput('release-tag', tag);\n            console.info(\"Release tag: \", tag, \" Version: \", version);\n\n      - uses: vedantmgoyal9/winget-releaser@main\n        with:\n          identifier: Seelen.SeelenUI\n          version: ${{ steps.get-version.outputs.version }}\n          release-tag: ${{ steps.get-version.outputs.release-tag }}\n          installers-regex: '\\.msix$'\n          fork-user: eythaann\n          token: ${{ secrets.WINGET_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/nightly.yml",
    "content": "name: Nightly\n\non:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - \"**.md\"\n      - \"documentation/**\"\n      - \".github/**\"\n      - \"crowdin.yml\"\n  workflow_dispatch:\n\npermissions:\n  contents: write\n\nconcurrency:\n  group: ${{ github.workflow }}\n  cancel-in-progress: true\n\njobs:\n  continuous-integration:\n    uses: ./.github/workflows/ci.yml\n\n  update-tag:\n    needs: continuous-integration\n    runs-on: ubuntu-latest\n    outputs:\n      version: ${{ steps.gen-version.outputs.result }}\n    steps:\n      - uses: actions/checkout@v4\n      - run: git fetch --tags --prune\n\n      - name: Create or update 'nightly' tag (force overwrite)\n        run: |\n          git tag -f nightly\n          git push origin --force --tags\n\n      - name: Generate Version\n        id: gen-version\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const fs = require('fs');\n\n            const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));\n            const currentVersion = packageJson.version;\n\n            const timestamp = new Date().toISOString().replace(/[-:T]/g, '').slice(2, 12);\n            const nightlyVersion = `${currentVersion}-nightly.${timestamp}`;\n\n            return nightlyVersion;\n\n  build-binaries:\n    needs: update-tag\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - platform: windows-2025\n            target: x86_64-pc-windows-msvc\n          - platform: windows-2025\n            target: aarch64-pc-windows-msvc\n\n    runs-on: ${{ matrix.platform }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n        with:\n          rust-targets: ${{ matrix.target }}\n          cache-key-prefix: rust-build-${{ matrix.target }}\n\n      - name: Install frontend dependencies\n        run: npm install\n\n      - name: Set Version\n        run: |\n          npx tsx scripts/versionish.ts ci ${{ needs.update-tag.outputs.version }}\n\n      - name: Build Hook DLL\n        run: cargo build --release --target ${{ matrix.target }} -p sluhk\n\n      - name: Build\n        env:\n          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}\n          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}\n        run: npx tauri build --ci --verbose --no-bundle --target ${{ matrix.target }}\n\n      - name: Upload binaries to artifacts\n        id: upload-binaries\n        uses: actions/upload-artifact@v4\n        with:\n          name: binaries-${{ matrix.target }}\n          path: |\n            target/${{ matrix.target }}/release/static/**/*\n            target/${{ matrix.target }}/release/*.exe\n            target/${{ matrix.target }}/release/*.dll\n            target/${{ matrix.target }}/release/SHA256SUMS\n            target/${{ matrix.target }}/release/SHA256SUMS.sig\n            target/${{ matrix.target }}/release/seelen_ui.pdb\n\n  merge-binaries:\n    needs: build-binaries\n    runs-on: ubuntu-latest\n    outputs:\n      artifact-id: ${{ steps.merge-binaries.outputs.artifact-id }}\n    steps:\n      - name: Merge binaries artifacts\n        id: merge-binaries\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: binaries\n          pattern: binaries-*\n          separate-directories: true\n          delete-merged: true\n\n  sign-binaries:\n    needs: merge-binaries\n    runs-on: ubuntu-latest\n    steps:\n      - name: Sign binaries with SignPath\n        uses: signpath/github-action-submit-signing-request@v1\n        with:\n          api-token: ${{ secrets.SIGNPATH_API_TOKEN }}\n          organization-id: 1a9e9b37-229a-4540-a639-137deebee4e1\n          project-slug: seelen-ui\n          signing-policy-slug: test-signing\n          artifact-configuration-slug: binaries\n          github-artifact-id: ${{ needs.merge-binaries.outputs.artifact-id }}\n          output-artifact-directory: signed-binaries\n          wait-for-completion: true\n\n      - name: Upload signed binaries by target\n        uses: actions/upload-artifact@v4\n        with:\n          name: signed-binaries-x86_64-pc-windows-msvc\n          path: signed-binaries/binaries-x86_64-pc-windows-msvc/**/*\n\n      - name: Upload signed binaries by target\n        uses: actions/upload-artifact@v4\n        with:\n          name: signed-binaries-aarch64-pc-windows-msvc\n          path: signed-binaries/binaries-aarch64-pc-windows-msvc/**/*\n\n  bundle:\n    needs:\n      - update-tag\n      - sign-binaries\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - platform: windows-2025\n            target: x86_64-pc-windows-msvc\n          - platform: windows-2025\n            target: aarch64-pc-windows-msvc\n\n    runs-on: ${{ matrix.platform }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n        with:\n          rust-targets: ${{ matrix.target }}\n          cache-key-prefix: rust-bundle-${{ matrix.target }}\n\n      - name: Install frontend dependencies\n        run: npm install\n\n      - name: Install MSIX dependencies\n        shell: pwsh\n        run: |\n          winget upgrade winget --accept-package-agreements --accept-source-agreements --disable-interactivity --force || Write-Output \"Ignoring winget update failure\"\n          winget install --id Microsoft.DotNet.AspNetCore.8 --accept-package-agreements --accept-source-agreements --force\n          winget install --id Microsoft.DotNet.DesktopRuntime.8 --accept-package-agreements --accept-source-agreements --force\n          winget install --id MarcinOtorowski.MSIXHero --accept-package-agreements --accept-source-agreements --force\n\n      - name: Set Version\n        run: |\n          npx tsx scripts/versionish.ts ci ${{ needs.update-tag.outputs.version }}\n\n      - name: Download signed binaries\n        uses: actions/download-artifact@v4\n        with:\n          name: signed-binaries-${{ matrix.target }}\n          path: target/${{ matrix.target }}/release\n\n      - name: Clean bundle folder from cache\n        shell: pwsh\n        run: |\n          $bundlePath = \"target/${{ matrix.target }}/release/bundle\"\n          if (Test-Path $bundlePath) {\n            Remove-Item -Path $bundlePath -Recurse -Force\n            Write-Output \"Old bundles files deleted: $bundlePath\"\n          }\n\n      - name: Bundle\n        run: npx tauri bundle --ci --verbose --target ${{ matrix.target }} --no-sign\n\n      - name: Bundle MSIX\n        run: npx tsx scripts/bundle.msix.ts --target ${{ matrix.target }}\n\n      - name: Upload bundles to artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bundles-${{ matrix.target }}\n          path: target/${{ matrix.target }}/release/bundle\n\n  merge-bundles:\n    needs: bundle\n    runs-on: ubuntu-latest\n    outputs:\n      artifact-id: ${{ steps.upload-merged.outputs.artifact-id }}\n    steps:\n      - name: Merge artifacts\n        id: upload-merged\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: bundles\n          pattern: bundles-*\n          delete-merged: true\n\n  sign-bundles:\n    needs:\n      - update-tag\n      - merge-bundles\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n\n      - name: Clean existing assets\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            const tagName = 'nightly';\n\n            const release = await github.rest.repos.getReleaseByTag({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag: tagName,\n            });\n\n            const { data: assets } = await github.rest.repos.listReleaseAssets({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              release_id: release.data.id,\n            });\n\n            core.info(`Found ${assets.length} existing assets to clean`);\n\n            const deletions = assets.map(asset => {\n              core.info(`Deleting ${asset.name}`);\n              return github.rest.repos.deleteReleaseAsset({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                asset_id: asset.id,\n              });\n            });\n\n            await Promise.all(deletions);\n            core.info('✅ All existing assets cleaned');\n\n      - name: Submit to SignPath\n        uses: signpath/github-action-submit-signing-request@v1\n        with:\n          api-token: ${{ secrets.SIGNPATH_API_TOKEN }}\n          organization-id: 1a9e9b37-229a-4540-a639-137deebee4e1\n          project-slug: seelen-ui\n          signing-policy-slug: test-signing\n          artifact-configuration-slug: nightly-bundles\n          github-artifact-id: ${{ needs.merge-bundles.outputs.artifact-id }}\n          output-artifact-directory: bundles\n\n      - name: File Tree\n        run: |-\n          tree bundles\n\n      - name: Tauri Updater Signature\n        env:\n          TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}\n          TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}\n        run: |\n          npm install -g @tauri-apps/cli\n\n          echo \"Removing existing .sig files to regenerate with Tauri updater keys...\"\n          find ./bundles -name \"*.sig\" -type f -delete\n          echo \"Existing signatures removed\"\n\n          VERSION=${{ needs.update-tag.outputs.version }}\n          PATH1=\"bundles/nsis/Seelen UI_${VERSION}_arm64-setup.exe\"\n          PATH2=\"bundles/nsis/Seelen UI_${VERSION}_x64-setup.exe\"\n\n          echo \"Signing ${PATH1}...\"\n          tauri signer sign --verbose \"$PATH1\"\n          echo \"Signing ${PATH2}...\"\n          tauri signer sign --verbose \"$PATH2\"\n\n      - name: Upload Signed Installers to release\n        uses: svenstaro/upload-release-action@v2\n        with:\n          tag: nightly\n          file: bundles/**/*\n          file_glob: true\n\n  generate-update-file:\n    needs:\n      - update-tag\n      - sign-bundles\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Get Release ID\n        id: get-release\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            const release = await github.rest.repos.getReleaseByTag({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag: 'nightly',\n            });\n            return release.data.id;\n      - uses: ./.github/actions/generate-update-manifest\n        with:\n          release-id: ${{ steps.get-release.outputs.result }}\n          version: ${{ needs.update-tag.outputs.version }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/publish-core.yml",
    "content": "name: \"Publish Core Library\"\n\non:\n  workflow_call:\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  publish-core:\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: ./libs/core\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: denoland/setup-deno@v2\n        with:\n          deno-version: v2.x\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n          registry-url: \"https://registry.npmjs.org\"\n\n      - name: Setup Rust\n        run: rustup --version\n\n      - name: Build Rust Bindings (ts-rs)\n        run: deno task build:rs\n\n      - name: Publish to JSR\n        run: deno publish --allow-dirty\n\n      - name: Build NPM package\n        run: deno task build:npm\n\n      - name: Publish to NPM\n        run: |\n          cd ./npm\n          npm --verbose publish --tag latest\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: \"Release\"\n\non:\n  push:\n    tags:\n      - \"v[0-9]+.[0-9]+.[0-9]+\"\n  workflow_dispatch:\n\npermissions:\n  contents: write\n  id-token: write\n\njobs:\n  continuous-integration:\n    uses: ./.github/workflows/ci.yml\n\n  get-version:\n    needs: continuous-integration\n    runs-on: ubuntu-latest\n    outputs:\n      version: ${{ steps.get-version.outputs.result }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Get version\n        id: get-version\n        uses: actions/github-script@v7\n        with:\n          result-encoding: string\n          script: |\n            const fs = require('fs');\n            const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));\n            return packageJson.version;\n\n  create-release:\n    needs: get-version\n    runs-on: ubuntu-latest\n    outputs:\n      release_id: ${{ steps.create-release.outputs.result }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Create release (draft)\n        id: create-release\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const fs = require('fs');\n            const version = \"${{ needs.get-version.outputs.version }}\";\n            const changelogContent = fs.readFileSync('changelog.md', 'utf-8');\n            const regex = new RegExp(`(?<=\\\\[${version}\\\\]\\\\s)([\\\\s\\\\S]*?)(?=\\\\s## \\\\[)`, 'g');\n            const releaseNotes = changelogContent.match(regex)?.[0].trim() || '';\n\n            const { data } = await github.rest.repos.createRelease({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag_name: `v${version}`,\n              name: `Seelen UI v${version}`,\n              body: releaseNotes,\n              generate_release_notes: true,\n              draft: true,\n              prerelease: false,\n            });\n            return data.id;\n\n  build-binaries:\n    needs: create-release\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - platform: windows-2025\n            target: x86_64-pc-windows-msvc\n          - platform: windows-2025\n            target: aarch64-pc-windows-msvc\n    runs-on: ${{ matrix.platform }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n        with:\n          rust-targets: ${{ matrix.target }}\n          cache-key-prefix: rust-build-${{ matrix.target }}\n      - name: Install frontend dependencies\n        run: npm install\n\n      - name: Build Hook DLL\n        run: cargo build --release --target ${{ matrix.target }} -p sluhk\n\n      - name: Build (no bundle)\n        env:\n          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}\n          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}\n        run: npx tauri build --ci --verbose --no-bundle --target ${{ matrix.target }}\n\n      - name: Upload binaries to artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: binaries-${{ matrix.target }}\n          path: |\n            target/${{ matrix.target }}/release/static/**/*\n            target/${{ matrix.target }}/release/*.exe\n            target/${{ matrix.target }}/release/*.dll\n            target/${{ matrix.target }}/release/SHA256SUMS\n            target/${{ matrix.target }}/release/SHA256SUMS.sig\n            target/${{ matrix.target }}/release/seelen_ui.pdb\n\n  merge-binaries:\n    needs: build-binaries\n    runs-on: ubuntu-latest\n    outputs:\n      artifact-id: ${{ steps.merge-binaries.outputs.artifact-id }}\n    steps:\n      - name: Merge binaries artifacts\n        id: merge-binaries\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: binaries\n          pattern: binaries-*\n          separate-directories: true\n          delete-merged: true\n\n  sign-binaries:\n    needs: merge-binaries\n    runs-on: ubuntu-latest\n    steps:\n      - name: Sign binaries with SignPath\n        uses: signpath/github-action-submit-signing-request@v1\n        with:\n          api-token: ${{ secrets.SIGNPATH_API_TOKEN }}\n          organization-id: 1a9e9b37-229a-4540-a639-137deebee4e1\n          project-slug: seelen-ui\n          signing-policy-slug: release-signing\n          artifact-configuration-slug: binaries\n          github-artifact-id: ${{ needs.merge-binaries.outputs.artifact-id }}\n          output-artifact-directory: signed-binaries\n          wait-for-completion: true\n\n      - name: Upload signed binaries by target\n        uses: actions/upload-artifact@v4\n        with:\n          name: signed-binaries-x86_64-pc-windows-msvc\n          path: signed-binaries/binaries-x86_64-pc-windows-msvc/**/*\n\n      - name: Upload signed binaries by target\n        uses: actions/upload-artifact@v4\n        with:\n          name: signed-binaries-aarch64-pc-windows-msvc\n          path: signed-binaries/binaries-aarch64-pc-windows-msvc/**/*\n\n  bundle:\n    needs:\n      - get-version\n      - sign-binaries\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - platform: windows-2025\n            target: x86_64-pc-windows-msvc\n          - platform: windows-2025\n            target: aarch64-pc-windows-msvc\n\n    runs-on: ${{ matrix.platform }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/setup\n        with:\n          rust-targets: ${{ matrix.target }}\n          cache-key-prefix: rust-bundle-${{ matrix.target }}\n\n      - name: Install frontend dependencies\n        run: npm install\n\n      - name: Install MSIX dependencies\n        shell: pwsh\n        run: |\n          winget upgrade winget --accept-package-agreements --accept-source-agreements --disable-interactivity --force || Write-Output \"Ignoring winget update failure\"\n          winget install --id Microsoft.DotNet.AspNetCore.8 --accept-package-agreements --accept-source-agreements --force\n          winget install --id Microsoft.DotNet.DesktopRuntime.8 --accept-package-agreements --accept-source-agreements --force\n          winget install --id MarcinOtorowski.MSIXHero --accept-package-agreements --accept-source-agreements --force\n\n      - name: Download signed binaries\n        uses: actions/download-artifact@v4\n        with:\n          name: signed-binaries-${{ matrix.target }}\n          path: target/${{ matrix.target }}/release\n\n      - name: Clean bundle folder from cache\n        shell: pwsh\n        run: |\n          $bundlePath = \"target/${{ matrix.target }}/release/bundle\"\n          if (Test-Path $bundlePath) {\n            Remove-Item -Path $bundlePath -Recurse -Force\n            Write-Output \"Old bundles files deleted: $bundlePath\"\n          }\n\n      - name: Bundle\n        run: npx tauri bundle --ci --verbose --target ${{ matrix.target }} --no-sign\n\n      - name: Rename normal bundle to temp\n        shell: pwsh\n        run: |\n          $arch = \"${{ matrix.target }}\".Contains(\"aarch64\") ? \"arm64\" : \"x64\"\n          $version = \"${{ needs.get-version.outputs.version }}\"\n          $bundlePath = \"target/${{ matrix.target }}/release/bundle/nsis\"\n          $setupFile = \"$bundlePath/Seelen UI_${version}_${arch}-setup.exe\"\n          $tempFile = \"$bundlePath/Seelen UI_${version}_${arch}-setup_temp.exe\"\n\n          if (Test-Path $setupFile) {\n            Move-Item -Path $setupFile -Destination $tempFile\n            Write-Output \"Renamed $setupFile to $tempFile\"\n          } else {\n            Write-Error \"Setup file not found: $setupFile\"\n            exit 1\n          }\n\n      - name: Set Fixed Runtime\n        shell: pwsh\n        run: |\n          $arch = \"${{ matrix.target }}\".Contains(\"aarch64\") ? \"arm64\" : \"x64\"\n          ./scripts/SetFixedRuntime.ps1 -Architecture $arch\n\n      - name: Copy Runtime to target directory\n        shell: pwsh\n        run: |\n          $runtimeSource = Get-ChildItem -Path \"src/runtime\" -Directory | Where-Object { $_.Name -match '^\\d+\\.\\d+\\.\\d+\\.\\d+$' } | Select-Object -First 1\n          if ($runtimeSource) {\n            $targetRuntimeDir = \"target/${{ matrix.target }}/release/runtime\"\n            New-Item -ItemType Directory -Force -Path $targetRuntimeDir | Out-Null\n            Copy-Item -Path $runtimeSource.FullName -Destination \"$targetRuntimeDir/$($runtimeSource.Name)\" -Recurse -Force\n            Write-Output \"Copied runtime from $($runtimeSource.FullName) to $targetRuntimeDir/$($runtimeSource.Name)\"\n          } else {\n            Write-Error \"Runtime version directory not found in src/runtime\"\n            exit 1\n          }\n\n      - name: Bundle with Fixed Runtime\n        run: npx tauri bundle --ci --verbose --target ${{ matrix.target }} --no-sign\n\n      - name: Rename bundles\n        shell: pwsh\n        run: |\n          $arch = \"${{ matrix.target }}\".Contains(\"aarch64\") ? \"arm64\" : \"x64\"\n          $version = \"${{ needs.get-version.outputs.version }}\"\n          $bundlePath = \"target/${{ matrix.target }}/release/bundle/nsis\"\n          $tempFile = \"$bundlePath/Seelen UI_${version}_${arch}-setup_temp.exe\"\n          $normalFile = \"$bundlePath/Seelen UI_${version}_${arch}-setup.exe\"\n          $fixedFile = \"$bundlePath/Seelen UI_${version}_${arch}-setup-fixed.exe\"\n\n          Write-Output \"Checking files in $bundlePath\"\n          Get-ChildItem -Path $bundlePath -Filter \"*.exe\" | ForEach-Object { Write-Output \"  Found: $($_.Name)\" }\n\n          if (Test-Path $normalFile) {\n            Move-Item -Path $normalFile -Destination $fixedFile -Force\n            Write-Output \"Renamed fixed runtime bundle to $fixedFile\"\n          } else {\n            Write-Output \"Warning: $normalFile does not exist\"\n          }\n\n          if (Test-Path $tempFile) {\n            Move-Item -Path $tempFile -Destination $normalFile -Force\n            Write-Output \"Renamed normal bundle back to $normalFile\"\n          } else {\n            Write-Output \"Warning: $tempFile does not exist\"\n          }\n\n      - name: Bundle MSIX\n        run: npx tsx scripts/bundle.msix.ts --target ${{ matrix.target }}\n\n      - name: Upload bundles to artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: bundles-${{ matrix.target }}\n          path: target/${{ matrix.target }}/release/bundle\n\n  merge-bundles:\n    needs: bundle\n    runs-on: ubuntu-latest\n    outputs:\n      artifact-id: ${{ steps.upload-merged.outputs.artifact-id }}\n    steps:\n      - name: Merge artifacts\n        id: upload-merged\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: bundles\n          pattern: bundles-*\n          delete-merged: true\n\n  sign-bundles:\n    needs:\n      - get-version\n      - create-release\n      - merge-bundles\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n\n      - name: Submit to SignPath\n        uses: signpath/github-action-submit-signing-request@v1\n        with:\n          api-token: ${{ secrets.SIGNPATH_API_TOKEN }}\n          organization-id: 1a9e9b37-229a-4540-a639-137deebee4e1\n          project-slug: seelen-ui\n          signing-policy-slug: release-signing\n          artifact-configuration-slug: bundles\n          github-artifact-id: ${{ needs.merge-bundles.outputs.artifact-id }}\n          output-artifact-directory: bundles\n\n      - name: File Tree\n        run: |-\n          tree bundles\n\n      - name: Tauri Updater Signature\n        env:\n          TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}\n          TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}\n        run: |\n          npm install -g @tauri-apps/cli\n\n          echo \"Removing existing .sig files to regenerate with Tauri updater keys...\"\n          find ./bundles -name \"*.sig\" -type f -delete\n          echo \"Existing signatures removed\"\n\n          VERSION=\"${{ needs.get-version.outputs.version }}\"\n          PATH1=\"bundles/nsis/Seelen UI_${VERSION}_arm64-setup.exe\"\n          PATH2=\"bundles/nsis/Seelen UI_${VERSION}_x64-setup.exe\"\n\n          echo \"Signing ${PATH1}...\"\n          tauri signer sign --verbose \"$PATH1\"\n          echo \"Signing ${PATH2}...\"\n          tauri signer sign --verbose \"$PATH2\"\n\n      - name: Remove self-signed MSIX files (store-signed handled elsewhere)\n        run: |\n          echo \"Removing self-signed .msix files from bundles...\"\n          find ./bundles -name \"*.msix\" -type f -delete || true\n          echo \"Self-signed .msix files removed (Store-signed MSIX will be added later by msix.yml workflow)\"\n\n      - name: Upload Signed Installers to release\n        uses: svenstaro/upload-release-action@v2\n        with:\n          release_id: ${{ needs.create-release.outputs.release_id }}\n          file: bundles/**/*\n          file_glob: true\n\n  publish-release:\n    needs: [create-release, sign-bundles]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Publish release (un-draft)\n        uses: actions/github-script@v7\n        env:\n          releaseId: ${{ needs.create-release.outputs.release_id }}\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            await github.rest.repos.updateRelease({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              release_id: process.env.releaseId,\n              draft: false,\n            });\n\n  microsoft-store-submission:\n    needs: [publish-release, merge-bundles]\n    runs-on: windows-2025\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Download merged bundles (unsigned)\n        uses: actions/download-artifact@v4\n        with:\n          name: bundles\n          path: target/release/bundle\n\n      # Use Store Broker to publish to Microsoft Store\n      - name: Submit to Partner Center (aka DevCenter)\n        shell: pwsh\n        run: |\n          ./scripts/SubmitToStore.ps1\n        env:\n          PartnerCenterStoreId: ${{ secrets.MS_PRODUCT_ID }}\n          PartnerCenterTenantId: ${{ secrets.MS_TENANT_ID }}\n          PartnerCenterClientId: ${{ secrets.MS_CLIENT_ID }}\n          PartnerCenterClientSecret: ${{ secrets.MS_CLIENT_SECRET }}\n          SBDisableTelemetry: true\n\n  generate-update-file:\n    needs: [get-version, create-release, publish-release]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/generate-update-manifest\n        with:\n          release-id: ${{ needs.create-release.outputs.release_id }}\n          version: ${{ needs.get-version.outputs.version }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n\n  discord:\n    needs: [get-version, publish-release]\n    uses: ./.github/workflows/discord-notify.yml\n    with:\n      tag: v${{ needs.get-version.outputs.version }}\n    secrets: inherit\n\n  publish-core-library:\n    needs: publish-release\n    runs-on: ubuntu-latest\n    permissions:\n      actions: write\n    steps:\n      - name: Trigger publish-core workflow\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            await github.rest.actions.createWorkflowDispatch({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              workflow_id: 'publish-core.yml',\n              ref: context.ref,\n            });\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n\n.vscode\n.env\n\ndist\ntarget\ngen\nsrc/runtime\n\n# Submission Files\n/scripts/SBTemp\n\n# rust-analyzer\nrustc-ice-*.txt\n\nCLAUDE.md\nGEMINI.md\n.claude"
  },
  {
    "path": ".npmrc",
    "content": "message = \"chore(release): v%s\""
  },
  {
    "path": "AGENTS.md",
    "content": "# AGENTS.md\n\nGuidance for AI agents working in this repository.\n\nSeelen UI is a customizable Windows desktop environment built with:\n\n- Rust + Tauri (backend)\n- TypeScript + React/Preact (frontend)\n- A monorepo layout with shared libs under `libs/`\n\n## Read First (Non-Negotiable)\n\nBuild speed / safety:\n\n- DO NOT use `cargo build --release` for testing, type-checking, or local iteration.\n- Prefer `cargo check` for fast Rust validation.\n- Use `cargo build` (debug) only when you need a binary.\n\nTranslations:\n\n- DO NOT run `npm run translate` during active development.\n- Add translations manually while iterating; run the translate command only right before a final commit.\n\nRust locking order (avoid deadlocks):\n\n1. CLI locks\n2. DATA locks\n3. EVENT locks\n\nBackend architecture rules:\n\n- System modules in `src/background/modules/` MUST follow the modern pattern (lazy init + lazy tauri registration).\n- Business logic must NOT call `emit_to_webviews` directly.\n\nWinRT / COM safety:\n\n- For WinRT objects with event subscriptions, use wrapper structs with `Drop` for automatic unregistration.\n- Windows-rs clones `TypedEventHandler` internally: store tokens, not handlers.\n\n## Common Commands\n\nInitial setup:\n\n```bash\nnpm install && npm run dev\n```\n\nDev / build:\n\n- `npm run dev` - Frontend dev workflow\n- `npm run build:ui` - Build UI bundles\n- `npm run tauri dev` - Run Tauri in dev mode\n- `cargo check` - Fast Rust type check\n- `cargo build` - Debug build\n\nQuality (Deno-based):\n\n- `deno lint`\n- `deno fmt`\n- `npm run type-check`\n- `npm test`\n\nCore library (`libs/core`):\n\n- `deno task build`\n- `deno task build:rs` - Regenerate Rust -> TypeScript bindings\n- `deno task build:npm`\n\n## Repo Map (Where Things Live)\n\nShared libraries:\n\n- `libs/core/` - Core library + Rust-generated TypeScript bindings\n- `libs/widgets-shared/` - Cross-widget state utilities (includes LazySignal)\n- `libs/slu-ipc/`, `libs/positioning/`, `libs/widgets-integrity/`\n\nMain app:\n\n- `src/background/` - Rust backend (modules, native integrations)\n- `src/service/` - System service components\n- `src/ui/` - Frontend apps (each subdirectory is an independent app)\n  - examples: `src/ui/settings/`, `src/ui/toolbar/`, `src/ui/launcher/`, `src/ui/window_manager/`\n\n## Frontend Conventions\n\nApp architecture:\n\n- UI apps use a hexagonal-ish layering: `infra/`, `app/`, `domain/`, `shared/`.\n- Keep boundaries clean: `domain/` is pure logic; `infra/` is UI + integration.\n\nStyling:\n\n- CSS Modules are the default.\n- Naming: kebab-case for CSS, camelCase for TS.\n\nInternationalization:\n\n- All user-visible strings must be i18n.\n- Translation files live under `i18n/translations/` (YAML).\n\n## Backend: System Modules (Modern Pattern)\n\nAll modules in `src/background/modules/` follow this pattern:\n\n- `application.rs` owns the singleton manager and emits internal events.\n- `infrastructure.rs` (or `handlers.rs`) owns Tauri commands and bridges internal events -> webviews.\n- Tauri event registration happens lazily on first command access (via `Once`).\n\nSuggested layout:\n\n```\nsrc/background/modules/<module>/\n  mod.rs\n  application.rs\n  infrastructure.rs  # or handlers.rs\n  domain.rs          # optional\n```\n\nMinimal pattern (infrastructure side):\n\n```rust\nuse std::sync::Once;\nuse seelen_core::handlers::SeelenEvent;\nuse crate::{app::emit_to_webviews, error::Result};\nuse super::{YourEvent, YourManager};\n\nfn get_manager() -> &'static YourManager {\n    static REGISTER: Once = Once::new();\n    REGISTER.call_once(|| {\n        YourManager::subscribe(|_event: YourEvent| {\n            // Keep this small and side-effect focused.\n            if let Ok(data) = get_your_data() {\n                emit_to_webviews(SeelenEvent::YourDataChanged, data);\n            }\n        });\n    });\n    YourManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_your_data() -> Result<Vec<YourType>> {\n    let manager = get_manager();\n    Ok(manager.get_data())\n}\n```\n\nMinimal pattern (application side):\n\n```rust\nuse std::sync::LazyLock;\n\npub struct YourManager {\n    // fields\n}\n\n#[derive(Debug, Clone)]\npub enum YourEvent {\n    DataChanged,\n}\n\nevent_manager!(YourManager, YourEvent);\n\nimpl YourManager {\n    fn new() -> Self {\n        Self { /* init */ }\n    }\n\n    pub fn instance() -> &'static Self {\n        static MANAGER: LazyLock<YourManager> = LazyLock::new(|| {\n            let mut m = YourManager::new();\n            m.init().log_error();\n            m\n        });\n        &MANAGER\n    }\n\n    fn init(&mut self) -> Result<()> {\n        self.setup_listeners()?;\n        Ok(())\n    }\n\n    fn setup_listeners(&mut self) -> Result<()> {\n        // Listen to OS signals; emit internal YourEvent::* (not webview events)\n        Ok(())\n    }\n\n    pub fn get_data(&self) -> Vec<YourType> {\n        // return data\n        vec![]\n    }\n}\n```\n\nWhen adding a new backend feature exposed to the UI, update `libs/core`:\n\n1. `libs/core/src/handlers/commands.rs`\n\n```rust\nslu_commands_declaration! {\n    GetYourData = get_your_data() -> Vec<YourType>,\n}\n```\n\n2. `libs/core/src/handlers/events.rs`\n\n```rust\nslu_events_declaration! {\n    YourDataChanged(Vec<YourType>) as \"your-module::data-changed\",\n}\n```\n\n3. Regenerate bindings: `cd libs/core && deno task build:rs`\n\n## WinRT Wrapper Pattern (Automatic Cleanup)\n\nUse wrappers for WinRT objects that register events.\n\nRules:\n\n- Store event tokens (WinRT tokens are often `i64`).\n- Do NOT store `TypedEventHandler` values in struct fields.\n- Implement `Drop` to unregister events.\n\nExample:\n\n```rust\npub struct WinRtWrapper {\n    pub object: SomeWinRtObject,\n    token: i64,\n}\n\nimpl WinRtWrapper {\n    pub fn create(object: SomeWinRtObject) -> Result<Self> {\n        let token = object.SomeEvent(&TypedEventHandler::new(Self::on_event))?;\n        Ok(Self { object, token })\n    }\n\n    fn on_event(\n        _sender: &Option<SomeWinRtObject>,\n        _args: &Option<SomeArgs>,\n    ) -> windows_core::Result<()> {\n        Ok(())\n    }\n}\n\nimpl Drop for WinRtWrapper {\n    fn drop(&mut self) {\n        self.object.RemoveSomeEvent(self.token).log_error();\n    }\n}\n```\n\n## Shared State: LazySignal (Cross-Widget)\n\nUse `LazySignal` (in `libs/widgets-shared/`) when state is:\n\n- fetched asynchronously (invoke/system APIs)\n- updated by async events\n- shared across widgets/webviews\n\nCritical usage pattern:\n\n1. Create lazy signal with async initializer.\n2. Register event listeners first (they may fire immediately).\n3. Call `.init()` last; it must not overwrite a value set by an event.\n\nExample:\n\n```ts\nimport { lazySignal } from \"libs/widgets-shared/LazySignal\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\n\nconst $data = lazySignal(async () => {\n  return await invoke(SeelenCommand.GetYourData);\n});\n\nsubscribe(SeelenEvent.YourDataChanged, (event) => {\n  $data.value = event.payload;\n});\n\nawait $data.init();\n```\n\n## Creating Svelte Widgets (High-Level)\n\nSeelen UI supports standalone Svelte widgets. Prefer following existing widget patterns; do not invent new build\nplumbing.\n\nTypical pieces:\n\n1. Static widget definition: `src/static/widgets/<widget-name>/`\n2. Svelte app: `src/ui/svelte/<widget-name>/`\n3. Theme styles: `src/static/themes/default/styles/<widget-name>.scss`\n4. i18n: translations (and keep the translation workflow rule)\n5. Optional Rust backend integration (use the modern module pattern)\n\nWidget checklist:\n\n- Static metadata and HTML exist under `src/static/widgets/<widget-name>/`\n- Svelte entry mounts into `#root` and calls `Widget.getCurrent().init(...)`\n- Shared, event-driven state uses LazySignal\n- Styling uses existing CSS variables; avoid global class conflicts\n\nShared styling for widgets:\n\n- Use `data-skin` attributes for common control styling (buttons, inputs) to avoid class collisions.\n\n## Rust Types: Tagged Enums (Serde)\n\nAvoid tuple variants for internally tagged enums.\n\nBad:\n\n```rust\n#[serde(tag = \"type\")]\npub enum Action {\n    WithData(String),\n}\n```\n\nGood:\n\n```rust\n#[serde(tag = \"type\")]\npub enum Action {\n    WithData { data: String },\n}\n```\n\n## Testing Expectations\n\n- Prefer quick feedback loops (`cargo check`, `npm run type-check`, `deno lint`).\n- Keep changes scoped; add tests when behavior changes.\n"
  },
  {
    "path": "CLA.md",
    "content": "Seelen UI - Individual Contributor License Agreement (CLA)\n\nThank you for your interest in contributing to the Seelen UI project. By submitting a contribution to the Project, you\nagree to the following terms and conditions:\n\n1. Grant of License: By submitting a contribution to the Project, you grant the Project Owner a non-exclusive,\n   perpetual, irrevocable, worldwide license to use, copy, modify, distribute, and sublicense your contribution, as well\n   as any derivative works thereof, under the terms of the open-source license under which the Project is distributed.\n\n2. Copyright: You represent that you are the legitimate author of the submitted contribution or that you have the\n   necessary rights to grant the license described in point 1 above.\n\n3. Warranties: You acknowledge that your contribution is provided \"as is\" and that you waive any warranties, express or\n   implied, including, but not limited to, the warranties of merchantability, fitness for a particular purpose, and\n   non-infringement.\n\n4. Representations and Warranties: You warrant that the contribution you submit does not infringe any third-party\n   intellectual property rights, and in the event of any third-party claim arising out of or related to your\n   contribution, you agree to indemnify and hold harmless the Project Owner and its affiliates, agents, licensees, and\n   sublicensees.\n\n5. Acceptance: By submitting your contribution to the Project, you agree to abide by these terms and conditions.\n"
  },
  {
    "path": "CODE_OF_CONDUCT",
    "content": "# Seelen UI Code of Conduct\n\n## 1. Our Pledge\n\nWe as members, contributors, and maintainers pledge to make participation in the Seelen UI community a harassment-free experience for everyone. We welcome all regardless of:\n\n- Age, body size, disability, ethnicity, gender identity/expression\n- Level of experience, nationality, personal appearance, race, religion\n- Sexual identity/orientation, or any other characteristic\n\n## 2. Expected Behavior\n\nAll community members must:\n\n- Use welcoming and inclusive language\n- Respect differing viewpoints and experiences\n- Gracefully accept constructive criticism\n- Focus on what's best for the community\n- Show empathy toward other members\n\n## 3. Unacceptable Behavior\n\nUnacceptable behaviors include:\n\n- Sexualized language/imagery and unwelcome advances\n- Trolling, insulting/derogatory comments, personal attacks\n- Public/private harassment\n- Publishing others' private information without permission\n- Other conduct reasonably considered inappropriate\n\n## 4. Enforcement Responsibilities\n\nProject maintainers will:\n\n- Remove/edit inappropriate content\n- Warn or ban offenders for inappropriate behavior\n- Interpret these standards appropriately\n\n## 5. Scope\n\nApplies to all community spaces including:\n\n- GitHub repositories and discussions\n- Discord/Slack channels\n- Social media accounts\n- In-person events\n\n## 6. Enforcement\n\nInstances of abusive behavior may be reported to:\n\n- Email: [support@seelen.io]\n- Discord: [Modmail/DM contact]\n- GitHub: [@eythaann]\n\nAll complaints will be reviewed and investigated promptly.\n\n## 7. Attribution\n\nThis Code is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at:\nhttps://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n\n[homepage]: https://www.contributor-covenant.org"
  },
  {
    "path": "CONTRIBUTING",
    "content": "# Contributing to Seelen UI\n\nThank you for your interest in contributing to this project! We welcome contributions from everyone. By participating in this project, you agree to abide by the following guidelines and terms.\n\n## How to Contribute\n\n1. Fork the repository and clone it to your local machine.\n2. Read the [Project Documentation](documentation/project.md) to understand the project structure and how to use it.\n3. Create a new branch for your contribution: `git checkout -b feature/new-feature`.\n4. Make your changes and ensure they are well-tested.\n5. Commit your changes:\n   - Ensure that your commits are signed. You can sign commits with `git commit -S -m 'Add new feature'`.\n   - Follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) syntax for commit messages, e.g., `feat: add new feature`, `fix(wm): resolve #567 issue`.\n6. Push to the branch: `git push origin feature/new-feature`.\n7. Submit a pull request.\n\n## Contributor License Agreement (CLA)\n\nBy submitting code as an individual, you agree to the [Contributor License Agreement](CLA.md).\n\n## Code of Conduct\n\nThis project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT). By participating, you are expected to uphold this code. Please report any unacceptable behavior to [Maintainer's Email].\n\n## Licensing\n\nAll contributions to this project are subject to the terms of the [GNU Affero General Public License v3.0](LICENSE). By contributing, you agree that your contributions will be licensed under the terms of this license.\n\n## Contact\n\nIf you have any questions or need further clarification, please feel free to join our [Discord server](https://discord.gg/ABfASx5ZAJ).\n"
  },
  {
    "path": "Cargo.toml",
    "content": "cargo-features = [\"profile-rustflags\", \"codegen-backend\"]\n\n[profile.dev]\ndebug = \"full\"\nstrip = false\nopt-level = 0\nincremental = true\nrustflags = [\"-Z\", \"threads=8\", \"-C\", \"link-arg=-fuse-ld=lld\"]\n\n[profile.rust-analyzer]\ninherits = \"dev\"\ndebug = false\n\n[profile.release]\ndebug = \"limited\"\nstrip = true\nopt-level = \"z\"\nlto = true\ncodegen-units = 1\nrustflags = [\"-Z\", \"threads=8\", \"-C\", \"link-arg=-fuse-ld=lld\"]\n\n[workspace]\nmembers = [\n  \"libs/core\",\n  \"libs/positioning\",\n  \"libs/slu-ipc\",\n  \"libs/utils\",\n  \"src\",\n  \"src/hook_dll\",\n]\nresolver = \"3\"\nlints = {}\n\n[workspace.dependencies]\ntokio = \"1.49.0\"\nthiserror = \"2.0.18\"\nserde = \"1.0\"\nserde_json = \"1.0\"\nserde_yaml = \"0.9.34\"\nlog = \"0.4\"\narc-swap = \"1.8.1\"\nbacktrace = \"0.3.76\"\nbase64 = \"0.22.1\"\nbattery = \"0.7.8\"\nclap = \"4.5.58\"\ncrossbeam-channel = \"0.5.15\"\ndiscord-rich-presence = \"0.2.5\"\nencoding_rs = \"0.8.35\"\nevalexpr = \"11.3.1\"\nfern = \"0.7.1\"\nfutures = \"0.3.31\"\nimage = \"0.25.9\"\ninterprocess = \"2.3.1\"\nitertools = \"0.14.0\"\nlazy_static = \"1.5.0\"\nnotify-debouncer-full = \"0.3.2\"\nminisign = \"0.8.0\"\nos_info = \"3.14.0\"\nowo-colors = \"4.2.3\"\nparking_lot = \"0.12.5\"\nphf = \"0.11.3\"\nquick-xml = \"0.38.4\"\nrand = \"0.9.2\"\nregex = \"1.12.3\"\nreqwest = \"0.12.28\"\nrust-i18n = \"3.1.5\"\nsha2 = \"0.10.9\"\nseelen-core = { path = \"libs/core\" }\nslu-ipc = { path = \"libs/slu-ipc\" }\nslu-utils = { path = \"libs/utils\" }\npositioning = { path = \"libs/positioning\" }\nsysinfo = \"0.38.3\"\ntauri = \"2.10.3\"\ntauri-build = \"2.5.6\"\ntauri-plugin-deep-link = \"2.4.7\"\ntauri-plugin-dialog = \"2.6.0\"\ntauri-plugin-fs = \"2.4.5\"\ntauri-plugin-http = \"2.5.7\"\ntauri-plugin-log = \"2.8.0\"\ntauri-plugin-process = \"2.3.1\"\ntauri-plugin-shell = \"2.3.5\"\ntauri-plugin-updater = \"2.10.0\"\ntranslators = \"0.1.5\"\nurl = \"2.5.8\"\nurlencoding = \"2.1.3\"\nuuid = \"1.20.0\"\nwalkdir = \"2.5.0\"\nwidestring = \"1.2.1\"\nwin-hotkeys = { git = \"https://github.com/Seelen-Inc/windows-keyboard-hook.git\", features = [\"serde\"] }\nwin-screenshot = \"4.0.14\"\nwindows = \"0.62.2\"\nwindows-future = \"0.3.2\"\nwmi = \"0.18.1\"\nwindows-core = \"0.62.2\"\nwinreg = \"0.55.0\"\ntime = \"0.3.47\"\nscc = \"2.4.0\"\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  <img src=\"documentation/images/logo.svg\" width=\"44\" align=\"top\" alt=\"Seelen UI Logo\" />\n  Seelen UI\n</h1>\n\n<h2 align=\"center\">\n  Fully Customizable Desktop Environment for Windows\n  <br/>\n  Available in 70+ Languages\n</h2>\n\n<div align=\"center\">\n\n[![Contributors](https://img.shields.io/github/contributors/eythaann/seelen-ui.svg)](https://github.com/eythaann/seelen-ui/graphs/contributors)\n[![Last Commit](https://img.shields.io/github/last-commit/eythaann/seelen-ui.svg)](https://github.com/eythaann/seelen-ui/commits/main)\n[![Version](https://img.shields.io/github/v/release/eythaann/seelen-ui.svg)](https://github.com/eythaann/seelen-ui/releases)\n[![Downloads](https://img.shields.io/github/downloads/eythaann/seelen-ui/total.svg)](https://github.com/eythaann/seelen-ui/releases)\n\n</div>\n\n<img src=\"./documentation/images/preview.png\" width=\"100%\" alt=\"Screenshot of Seelen UI desktop showing a customized desktop environment\">\n\n<table align=\"center\">\n  <tr>\n    <td align=\"center\" width=\"33%\">\n      <a\n        href=\"https://apps.microsoft.com/detail/Seelen%20UI/9p67c2d4t9fb?mode=full\"\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        aria-label=\"Download Seelen UI from Microsoft Store\">\n        <img src=\"https://get.microsoft.com/images/en-us%20dark.svg\" width=\"100%\" alt=\"Download Seelen UI from Microsoft Store\">\n      </a>\n    </td>\n    <td align=\"center\" width=\"33%\">\n      <a\n        href=\"https://discord.gg/ABfASx5ZAJ\"\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        aria-label=\"Join the Seelen UI Discord community\">\n        <img src=\"./documentation/images/discord-alt.png\" width=\"100%\" alt=\"Join the Seelen UI Discord community\">\n      </a>\n    </td>\n    <td align=\"center\" width=\"33%\">\n      <a\n        href='https://www.digitalocean.com/?refcode=955c7335abf5&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge'\n        target='_blank'\n        rel=\"noopener noreferrer\"\n        aria-label=\"DigitalOcean Referral Badge\"\n      >\n        <img src='https://web-platforms.sfo2.cdn.digitaloceanspaces.com/WWW/Badge%201.svg' width=\"100%\" alt='DigitalOcean Referral Badge' />\n      </a>\n    </td>\n  </tr>\n</table>\n\n## Overview\n\n[Seelen UI](https://seelen.io/apps/seelen-ui) is a tool designed to enhance your Windows desktop experience with a focus\non customization and productivity. It integrates smoothly into your system, providing a range of features that allow you\nto personalize your desktop and optimize your workflow.\n\n- **Be Creative**: Seelen UI lets you tailor your desktop to fit your style and needs. You can adjust menus, widgets,\n  icons, and other elements to create a personalized and visually appealing desktop environment.\n\n  ![Seelen UI Custom Theme](./documentation/images/theme_preview.png)\n\n<br/>\n\n- **Enhance Your Productivity**: Seelen UI helps you organize your desktop efficiently. With a Tiling Windows Manager,\n  windows automatically arrange themselves to support multitasking, making your work more streamlined.\n\n  ![Seelen UI Tiling Window Manager](./documentation/images/twm_preview.png)\n\n<br/>\n\n- **Enjoy your music**: With an integrated media module that's compatible with most music players, Seelen UI allows you\n  to enjoy your music seamlessly. You can pause, resume, and skip tracks at any time without the need to open additional\n  windows.\n\n  ![Seelen UI Media Module](./documentation/images/media_module_preview.png)\n\n<br/>\n\n- **Be faster!**: With an app launcher inspired by Rofi, Seelen UI provides a simple and intuitive way to quickly access\n  your applications and execute commands.\n\n  ![Seelen UI App Launcher](./documentation/images/app_launcher_preview.png)\n\n<br/>\n\n- **User-Friendly Configuration**: Seelen UI offers an intuitive interface for easy customization. Adjust settings such\n  as themes, taskbar layouts, icons, etc. With just a few clicks.\n\n  ![Seelen UI Settings](./documentation/images/settings_preview.png)\n\n<br/>\n\n## Installation\n\n> [!CAUTION]\n> Seelen UI requires the WebView runtime to be installed. On Windows 11, it comes pre-installed with the system.\n> However, on Windows 10, the WebView runtime is included with the `setup.exe` installer. Additionally, Microsoft Edge\n> is necessary to function correctly. Some users may have modified their system and removed Edge, so please ensure both\n> Edge and the WebView runtime are installed on your system.\n\n> [!NOTE]\n> On fresh installations of Windows, the app might show a white or dark screen. You only need to update your Windows\n> through Windows Update and restart your PC.\n\nYou can choose from different installation options based on your preference:\n\n### Microsoft Store <em>(recommended)</em>\n\nDownload the latest version from the [Store](https://www.microsoft.com/store/productId/9P67C2D4T9FB?ocid=pdpshare) page.\nThis is the recommended option because you will receive updates and a secure version of the program.\n\n_**Note**_: It may take around 1 to 3 business days for changes to be reflected in the Microsoft Store, as updates are\napproved by real people in the store.\n\n### Winget\n\nInstall the latest version using:\n\n```pwsh\nwinget install --id Seelen.SeelenUI\n```\n\nThis option also uses the signed `.msix` package and ensures you have the latest secure version. Similar to the\nMicrosoft Store, it may take around 1 to 3 business days for changes to be reflected in Winget, as updates are approved\nby real people in the `winget-pkg` project.\n\n### .msix Installer\n\nDownload the `.msix` installer from the [Releases](https://github.com/eythaann/seelen-ui/releases) page. This package is\nsigned, ensuring a secure installation. This is the same option as the Microsoft Store but is a portable installer.\n\n### .exe Installer\n\nDownload the latest version from the [Releases](https://github.com/eythaann/seelen-ui/releases) page and run the\n`setup.exe` installer. This option is less recommended as the installer is not signed, which may cause it to be flagged\nas a potential threat by some antivirus programs. The `setup.exe` is updated more quickly than the Microsoft Store or\nWinget versions and also it receives notifications updates on new release.\n\n## Usage\n\nOnce installed or extracted, simply open the program. The easy-to-use and intuitive GUI will guide you through the\nconfiguration process. Customize your desktop environment effortlessly.\n\n## Upcoming Features\n\nI’m excited to share some upcoming features for Seelen UI! Here’s a glimpse of what’s planned for the future:\n\n### ~~App Launcher~~ ✅\n\nI’m planning to develop an app launcher inspired by [Rofi](https://github.com/davatorium/rofi) on Linux. This feature\nwill provide a sleek and highly customizable way to quickly access your applications.\n\n![App Launcher Preview](https://raw.githubusercontent.com/adi1090x/files/master/rofi/previews/colorful/main.gif) _Image\ncourtesy of [rofi-themes](https://github.com/dctxmei/rofi-themes)_\n\n### Customizable Popup Widgets\n\nI aim to introduce a set of fully customizable popup widgets, similar to the features available in\n[EWW](https://github.com/elkowar/eww). These widgets will be highly configurable and adaptable to your needs, providing\nan enhanced and interactive way to manage your desktop environment.\n\n![Customizable Widgets Preview](https://raw.githubusercontent.com/adi1090x/widgets/main/previews/dashboard.png) _Image\ncourtesy of [adi1090x](https://github.com/adi1090x/widgets)_\n\n### Custom Alt + Tab (Task Switching)\n\nAn upgraded Alt + Tab system for task switching is on the horizon. This will offer a more visually appealing and\nfunctional experience, allowing for smoother transitions between open applications and windows.\n\n### Custom Virtual Desktops Viewer and Animations\n\nI’m also working on a custom virtual desktops viewer and dynamic animations to improve navigation between different\nworkspaces. This will provide a more intuitive and immersive multitasking experience.\n\nStay tuned for more updates as I develop these features. I appreciate your support and enthusiasm!\n\nHappy customizing!\n\nThe Seelen UI Team\n\n## Contributing\n\nWe welcome contributions!\n\n- Read the [Contribution Guidelines](CONTRIBUTING) to get started with terms.\n\n## License\n\nSee the [LICENSE](LICENSE) file for details.\n\n## Contact\n\nFor inquiries and support, please contact me on [Discord](https://discord.gg/ABfASx5ZAJ).\n\n## Sponsors\n\nWe're grateful for the support of our sponsors who help make Seelen UI possible.\n\n|                                                                                                         Sponsor                                                                                                          | Description                                                                                                  |\n| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------- |\n| [![DigitalOcean](https://web-platforms.sfo2.cdn.digitaloceanspaces.com/WWW/Badge%201.svg)](https://www.digitalocean.com/?refcode=955c7335abf5&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge) | **DigitalOcean** provides cloud infrastructure services that power our development and testing environments. |\n|                                                                [![SignPath](https://avatars.githubusercontent.com/u/34448643?s=60)](https://signpath.io/)                                                                | **SignPath** provides free code signing certificates, ensuring secure and trusted releases for our users.    |\n\n## See you later\n\n```\n                 .      .&     _,x&\"``\n                  & .   &'  ;.&&'\n            &.  . &.&     .0&&&;&\"\"`\n       .    '&  &.&&&  .&&&&&'\n     .&         ;&&& &&&&&'\n    &&          &&&&&&&&     &&&\n   0&    .     &&&&&&&&\"\"\n  &&   .0     &&&&&&&\n 0&& .&'     &&&&&&\n:&&&&&    . &&&&& \n0&&&&    & &&&&&\n&&&&'   &&&&&&&               .&&&x&\n&&&&   :&&&&&0.&'        , .&&&&&&&&&&;.\n&&&&.  &&&&&&&&        .&&&&&&&&&&'               .\n0&&&&  &&&&&&&       ,&&&&&&&&&&&&                &\n:&&&&; &&&&&0       ,;&&&&&&&&&&&             ;  .0\n 0&&&&&&&&&&0     ,;&&&&&&&&&&&&&             &  &;\n  0&&&&&&&&&&0   :',;\".&&&&&&\".&             && &0\n   0&&&&&&&&&0  ',;',&&&&&\" ,&'             &&&&0\n    0&&&&&&&&&0 ,x&&&&\" .&&&              &&&&0\n      0&&&&&& .&&&&\"'''\"&&\"&&            &&&&&0\n       0&& .&&;``       `&: :&         &&&&&&0\n          &\"' &&&&&&&&   &\"& &\"&   &&&&&&&&0\n            0&&&&&&&&&&&&&&&&&&&&&&&&&0\n               0&&&&&&&&&&&&&&&&&&&0         Seelen\n                    0&&&&&&&&&0\n```\n\n---\n\n📌 **Official Website**: [https://seelen.io](https://seelen.io)\n\nSeelen Inc © 2026 - All rights reserved\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nAt Seelen UI, we always recommend using the latest available version, as all versions are kept secure and up to date.\n\n| Version | Support            |\n| ------- | ------------------ |\n| Latest  | :white_check_mark: |\n\n## Reporting a Vulnerability\n\nIf you discover a vulnerability in Seelen UI, we appreciate you reporting it promptly.\n\n### How to Report\n\n- **Email:** Send an email to [support@seelen.io](mailto:support@seelen.io).\n- **Discord:** Send a message to the project staff on our official Discord server.\n\n### What to Expect\n\n- **Acknowledgment:** You will receive a confirmation of receipt within 48 hours.\n- **Updates:** We will keep you informed about the status of the investigation and any actions taken.\n- **Resolution:** If the report is accepted, we will work on a solution and implement it in the next update.\n\nThank you for helping us keep Seelen UI secure for all users.\n"
  },
  {
    "path": "changelog.md",
    "content": "# Changelog\n\n## [2.5.7-dev]\n\n### features\n\n- allow split multiple windows into separated items on dock.\n- trash bin module for dock.\n- allow change polling interval for system resources (cpu, ram, network usage, etc).\n\n### fix\n\n- not displaying tooltips on settings.\n- apps menu folder creation being skipped.\n- apps menu folder items not scrollable when overflowing.\n- some msix apps using resources mapping via 'pri' files not having icons (ex: iCloud).\n- PWA edge apps not extracting icons.\n- power menu and workspace viewer not scaling properly when users set text scale factor.\n- icons not refreshing on new installed apps.\n- not clearing icon extraction failure list on cache clear.\n- toolbar and calendar language not changing.\n- flyout widget being shown on media player close as a dot in the screen.\n\n## [2.5.6]\n\n### fix\n\n- dock pinned apps not being rendered correctly.\n\n## [2.5.5]\n\n### features\n\n- disable all css animations on performance mode extreme.\n- add more position options for flyouts.\n- show custom browser profile icons instead program one.\n- add 'pin to dock' option on apps menu widget.\n\n### refactor\n\n- dock state reimplemented.\n\n### fix\n\n- slow down on performace while dragging apps.\n- prevent win32 handle leaks.\n- user menu slow render.\n- calendar language not being changed.\n- media player stucked state.\n- some lnk files (ex: docker) not appearing on apps menu.\n- app not starting on corrupted settings files.\n\n## [2.5.4]\n\n### features\n\n- new configurations for toolbar customization.\n\n### enhancements\n\n- log system.\n- icon pack extractor and cache system.\n\n### fix\n\n- toolbar and dock state resetting on widget reload.\n\n## [2.5.3]\n\n### fix\n\n- can't open apps using apps menu.\n- app crashing for new users.\n\n## [2.5.2]\n\n### features\n\n- add new widget security layer for exposed commands.\n- apps menu optinal acrylic effect.\n- allow users decide if use or not hardware acceleration.\n\n### enhancements\n\n- widgets loading time.\n- settings resources lists.\n\n### refactors\n\n- remake wallpaper manager from react to svelte.\n\n### fix\n\n- infinity switching of items on toolbar.\n- app not starting correctly from service.\n- power menu clossing instantly.\n- settings by apps not working after saving new state.\n- context menu on toolbar bottom position.\n- external links being opened on webview instead external browser.\n- apps menu widget freeze on search.\n- change wallpaper shortcuts not working.\n- apps menu drag and drop.\n\n## [2.5.1]\n\n### enhancements\n\n- add liveness prove for widgets.\n\n### fix\n\n- app failing opening on MSIX version.\n\n## [2.5.0]\n\n### features\n\n- add corner actions on toolbar.\n- add toggle desktop module on dock.\n- allow layout sorting by windows dragging on the tiling window manager.\n- workspaces viewer gui.\n- wallpapers per monitor and per workspace.\n- wallpaper collections.\n- allow set start of week on calendars.\n- extended wallpapers across all monitors.\n- new start menu widget.\n- customizable flyouts (volume, brightness, workspaces, notification, etc)\n- resources usage plugins for toolbar.\n\n### enhancements\n\n- improve display changes events.\n- tiling window manager now swap windows by center point on drag end.\n- improve windows preview system.\n- improvements on wallpaper manager syncronization and changes.\n- settings UX improvements.\n- extraction on local video wallpapers thumbnails.\n- avoid reload the UI while playing as this can cause fps drops.\n- reduce memory usage!\n- copy button for icon list on settings.\n\n### refactor\n\n- toolbar items structure (breaking change).\n\n### fix\n\n- window manager moving windows being currently dragged by the user.\n- power menu on multi-monitor setups.\n- showing paused message on no video wallpapers.\n- app not working on local accounts.\n- app and service not running if already running on another session.\n- high cpu usage while user session inactive.\n- fix stale battery percentage on sleep wake up.\n- click on keyboard layout no changing the layout on the system.\n- window labels not being shown on dock.\n- tiling window manager not pausing on maximized/fullscreen apps.\n- run as admin not working on dock/taskbar.\n- single key shortcuts not taking in care holded keys.\n- wallpaper sometimes not showing on windows 11.\n- no emitting brightness events.\n- monitors missmatch.\n\n## [2.4.11]\n\n### fix\n\n- tray not working on MSIX installation.\n- shortcuts not being enabled by default but widgets yes.\n\n## [2.4.10]\n\n### refactor\n\n- entire rewrite of the bluetooth module.\n- internal widgets loader.\n- internal generated icon pack logic.\n\n### enhancements\n\n- expose complementary colors of the system accent to css.\n- change theme config url from input to local file selector.\n- lazy widgets initialization.\n- auto closing of popup widgets to save resources.\n- show paused message on wallpaper manager.\n\n### fix\n\n- no updating data attribute on toolbar for maximized windows on background.\n- ghost items on dock and window manager.\n- missing messages to the service on startup.\n- options field on theme settings rendering an input instead of selector.\n- missing events on app windows hook.\n- wasted space reserved for tiling window manager when dock/toolbar are disabled on monitor.\n- fix missing variables values on default percentages for theme variables.\n- #1290 dock and toolbar showing on other monitors when mouse is at edge of screen.\n\n## [2.4.9]\n\n### features\n\n- implement wallpaper downloads.\n- add new wallpaper type \"layered\" it allows create unique wallpaper via css.\n- new css variables added related to the date (hour, minute, day, month, year).\n\n## [2.4.8]\n\n### enhancements\n\n- toolbar overlapped and hidden state.\n- addition of pdb files on nightly builds.\n- interactable windows matching system.\n\n### fix\n\n- crash caused by wallpaper manager.\n- toolbar being shown while using fullscreen apps.\n- dock being shown while using fullscreen apps.\n\n## [2.4.7]\n\n### features\n\n- add new cli command to tranlate resource texts.\n\n### enhancements\n\n- improve window manager matching system.\n\n## [2.4.6]\n\n### features\n\n- add new cli command to bundle resources.\n- allow close window using middle click on dock item preview.\n\n### refactor\n\n- remove export resource button from settings.\n- remake wm ui from react to svelte (this reduced 10mb of ram usage).\n- remove native shell window hook, instead use win32 hooks via dlls.\n\n### fix\n\n- infity switching loop on workspaces.\n- memory leak on long sessions.\n- external links not working.\n- wallpaper manager corner barder radius.\n- windows overlaping the toolbar and dock when autohide is disabled.\n- Power Menu not showing corrently on multiple monitors (#1266).\n\n## [2.4.5]\n\n### features\n\n- new power menu widget.\n- widgets now can declare plugins to be loaded.\n- new fields on widgets metadata.\n- new system tray widget.\n\n### enhancements\n\n- disable individual shortcuts if attached widget is not enabled.\n- only show enabled themes as quick access in settings window.\n\n### refactor\n\n- refactor on widget declarations for bundled widgets.\n\n### fix\n\n- app cli was not working.\n- (#1258) missing translations on background process.\n- apps being filtering wrong.\n- stucked video wallpapers.\n- deadlocks on media manager.\n\n## [2.4.4]\n\n### features\n\n- add emergency shortcut to stop seelen process (Ctrl + Win + Alt + K).\n\n### enhancements\n\n- improve development ecosystem of resources via embedding of yaml files.\n- allow open dev tools on release builds.\n- optimize window movement animations.\n- don't show version warning if target version is not present.\n\n### fix\n\n- not considering multiple batteries on setups.\n- app not forcing restart on pc sleep/resume.\n- app/service ipc messages corrupted on encode/decode.\n- window manager animations enable logic.\n- window manager not forcing size of windows on tiling.\n- wallpaper paused while using Alt + Tab.\n- stucked menus on extreme performance mode.\n- stucked inline items after being dragged on toolbar.\n\n### refactor\n\n- move core library to main repository.\n- remove app tray icon registration.\n- remove windows tray icons related code.\n- discord RPC now is disabled by default.\n\n## [2.4.3]\n\n### fix\n\n- notifications not showing or working as expected.\n- autohide not working properly on some screen resolutions.\n- managing incorrect app windows on restart the app.\n\n## [2.4.2]\n\n### features\n\n- tiling window manager animations.\n- performance mode for seelen.\n\n### enhancements\n\n- improvement on power events.\n- reduce memory usage (RAM).\n\n### fix\n\n- third party widgets sometimes not starting.\n- can not save settings on empty wallpaper list.\n- not updating UI on remotion of resources.\n- button to update downloaded resources not working properly.\n- crash on max integer caused by media players.\n- media player popup not working properly.\n- app not working when system installed on different drive unit.\n- twm not considerationg the dock size.\n\n## [2.4.1]\n\n### features\n\n- add a button to delete resources.\n- add a button to update downloaded resources.\n\n### enhancements\n\n- cleanup of old service logs on start.\n- add time to service logs.\n\n### fix\n\n- toolbar not showing when user deleted basic system folders.\n- hotkeys stopping working after lock screen.\n- update app button not showing correctly on settings.\n\n## [2.4.0]\n\n### features\n\n- wallpaper resources.\n- filter for wallpapers.\n- tranfomation for wallpapers.\n- wallpaper animations on in/out.\n- unique wallpaper slider per monitor.\n- new shortcuts to create/delete and change virtual desktops.\n- add force restart of gui, via shortcut `Ctrl + Win + Alt + R`.\n- now workspaces are per monitor.\n- now workspaces persist over restarts.\n- on window manager windows now can be float or tiled.\n- implement focus change via keyboard hotkeys on tiling window manager.\n- implement container swapping via keyboard hotkeys on tiling window manager.\n- swap twm containers on window drag event.\n\n### refactor\n\n- monitors ids.\n- wallpaper manager.\n- virtual desktops.\n- tiling window manager.\n\n### fix\n\n- drag of text toolbar items not working.\n- live wallpapers blocking screensaver and lock screen.\n- sleep/wake up events not working.\n\n## [2.3.12]\n\n### enhancements\n\n- add new ways of customization for theme settings\n\n### fix\n\n- volume changed popup wrong placement.\n\n## [2.3.11]\n\n### features\n\n- allow add shared styles on themes to be applied to all the widgets.\n\n### enhancements\n\n- dock and toolbar autohide behaviors.\n- animations on popups and start of the own component library.\n\n### refactor\n\n- start migrating to preact signals.\n\n### fix\n\n- (#1019) maximized windows being detected as fullscreen.\n- icorrect resource id regex.\n- sorting of items on dock and toolbar.\n\n## [2.3.10]\n\n### hotfix\n\n- missing react icons.\n\n## [2.3.9]\n\n### features\n\n- discord rich presence.\n\n### refactor\n\n- icon packs implementation.\n- changed react to preact.\n\n### fix\n\n- app crashing on start by missing user folder (desktop, pictures, etc).\n\n## [2.3.8]\n\n### enhancements\n\n- show a warning to users with monitors drivers disabled.\n- allow change workspace using wheel on toolbar.\n\n### fix\n\n- (#897) stucked arrival notifications.\n- showing native notifications preview with the seelen ones (now only seelen arrival will be shown).\n- (#934) invisible edge window on dock.\n- (#925) invisible spotify widget on dock.\n- (#938) listing 6ghz networks as 5g networks.\n- (#962) JetBrains software isn't showing in dock.\n- (#923) Can't change media device on MSIX version.\n\n## [2.3.7]\n\n### enhancements\n\n- expose timeline on media players.\n- reduce bundled size of js code.\n- reduce verbosity on logs comming from widgets.\n\n## [2.3.6]\n\n### features\n\n- widgets implementation.\n- allow load/unload widgets via command line client.\n\n### refactor\n\n- improve internal code quality related to app console client.\n\n### fix\n\n- app not starting on start-menu cache corruption.\n- widgets being reloaded on ctrl + r.\n\n## [2.3.5]\n\n### fix\n\n- settings by monitor not working correctly.\n\n## [2.3.4]\n\n### enhancements\n\n- sort windows on dock by activation order.\n\n### fix\n\n- duplicated themes on settings GUI.\n- bad toolbar color on multiple maximized windows.\n\n## [2.3.3]\n\n### features\n\n- add open window label on dock for app items (configurable).\n- add input to write custom text items on the toolbar.\n- add export resource button on developer tools.\n- allow settings by theme.\n\n### enhancements\n\n- Settings UI refactor to follow the new resources ecosystem.\n- (#838) Improve dynamic color behavior on toolbar.\n\n### fix\n\n- not removing old icons mask on icon pack change.\n- styles for dock media item.\n- not scrollable dock on overflow (many items).\n- media player not being correctly updated on player close event.\n- (#636) input experience and another background process appearing on dock.\n\n## [2.3.2]\n\n### enhancements\n\n- mini performance improve on dock.\n\n### fix\n\n- resources not being updated correctly.\n\n## [2.3.1]\n\n### breaking changes\n\n- rename scope variables for toolbar plugins.\n\n### refactor\n\n- improve inner code quality on toolbar plugins.\n\n### enhancements\n\n- improve robustness on toolbar items to avoid blue screen.\n\n## [2.3.0]\n\n### breaking changes\n\n- remove mathjs eval by an more accurated eval for js code in toolbar plugins. This will break any plugin created before\n  v2.2.10.\n\n### features\n\n- add resources endpoint to home tab on settings.\n- add customizable and reusable popups implementation.\n- improvements on toolbar plugins system.\n- allow set buttons with custom actions on toolbar, via toolbar plugins.\n- add restore to default button for toolbar structure.\n- allow fetching remote data on toolbar plugins.\n\n### enhancements\n\n- reduce CPU usage on slu-service process.\n- improve ui on toolbar modules.\n\n### fix\n\n- media player styles on toolbar.\n- steam pin item on dock not working properly.\n- plugins not being updated on toolbar.\n\n## [2.2.9]\n\n### enhancements\n\n- store service logs in a file to help debugging.\n- wait for native shell on startup before start seelen ui.\n\n## [2.2.7]\n\n### fix\n\n- dock items not opening correctly.\n\n## [2.2.6]\n\n### feature\n\n- icons on icon packs now can declare a mask that could be used by themes.\n\n### enhancements\n\n- add custom icons to bluetooth devices.\n- allow set different icons by color scheme (light or dark) on icon packs.\n\n### fix\n\n- no dragable dock files and folders.\n- focusing widgets on creation.\n- not opening settings window when starting the app with an instance already running.\n\n## [2.2.4]\n\n### enhancements\n\n- add suspend/resume logic.\n\n### fix\n\n- not restoring native taskbar on close/crash.\n- clear all notifications button not updating UI.\n- not translated date on chinese and norwegian.\n\n## [2.2.3]\n\n### enhancements\n\n- wrap webview console as soon as posible to avoid missing errors on logs.\n- wait some seconds before remove media players to avoid shifting on chrome.\n\n### refactor\n\n- remove minified classnames and add do-not-use prefix to be clear to users.\n\n### fix\n\n- panic on media module when loading initial devices.\n- discord window without umid (for now umid was hardcoded).\n- focused app not updating on title change.\n- not considerating accesibility text scale factor on toolbar and dock.\n- wheel not changing volume level.\n- clear all notifications button not working correctly.\n\n## [2.2.2]\n\n### enhancements\n\n- add option to disable dynamic colors on toolbar.\n- reduce notification arrival time on screen from 10 seconds to 5 seconds.\n\n### fix\n\n- not showing some notifications.\n- crash when disabling a monitor on windows native settings.\n- unsyncronized clock on toolbar.\n\n## [2.2.1]\n\n### enhancements\n\n- add new bundled theme as example of animated icons with css.\n\n## [2.2.0]\n\n### features\n\n- add option to disable app thumbnail generation (dock).\n- allow lock the dock/toolbar items.\n- show instances counter of the same app on dock.\n- allow set the toolbar on different positions.\n- allow set custom start menu icon.\n- language selector for toolbar.\n- add dynamic color by focused app on toolbar.\n- add hibernate button on toolbar power menu.\n- add media volume mixer by apps and by device.\n- add clickable notifications, images, and more.\n\n### enhancements\n\n- settings shown each time on startup.\n- expand power module with power plan.\n\n### refactor\n\n- update windows-rs crate to 0.59.0.\n\n### fix\n\n- showing domain on username for local accounts.\n- showing application frame host instead real app name.\n- incorrect event order on win events.\n\n## [2.1.9]\n\n### fix\n\n- shortcuts not working on MSIX.\n- empty username for local accounts.\n\n## [2.1.8]\n\n### features\n\n- add button to clear the cached icons on settings.\n\n### enhancements\n\n- allow custom icons by extension on icon packs.\n\n### fix\n\n- error on file icons.\n- fix blue screen on toolbar when errors on template evaluation.\n\n## [2.1.7]\n\n### fix\n\n- power module not clickable on toolbar.\n- pinned items not working correctly for some apps.\n- electron apps without aumid.\n\n## [2.1.6]\n\n### fix\n\n- missing translations.\n- fullscreen match not beeing removed.\n\n## [2.1.5]\n\n### fix\n\n- devices and battery not clickables on toolbar.\n- missing icon on dock media module while not playing.\n- msix store not showing admin prompt.\n- links on settings not opening.\n\n## [2.1.4]\n\n### fix\n\n- crash on user module.\n- slow loading of toolbar.\n- no icons on PWA from edge browser.\n- icon packs not modifying icons on toolbar/dock media modules.\n- icon packs bad ordering, now the priority order is (umid > full-path > filename > extension).\n- bad dock execution path on apps with property store umid but no shortcut on start menu.\n\n## [2.1.3]\n\n### fix\n\n- bad user infomation on user module.\n\n## [2.1.2]\n\n### fix\n\n- style issue on toolbar user module.\n- showing unhandable tray icons (ex: nvidia old control panel).\n- installer being frozen on update.\n\n## [2.1.0]\n\n### features\n\n- allow custom animations on popups/dropdowns.\n- show open new window buttons on dock app items context menu.\n- toggle dock items using win + number.\n- notifications count on dock app items.\n- add brightness slider to quick settings on toolbar.\n- add user module on toolbar.\n\n### refactor\n\n- create separated system service to handle elevated actions.\n\n### fix\n\n- ghost windows caused by a refactor donde on v2.0.13.\n- not showing save button after icon packs change.\n- app failing when powershell is not part of the $PATH enviroment.\n- dock items no updatings paths on store updates.\n- unremovable workspace module on toolbar.\n- missing system tray icons.\n- date not being inmediately updated on settings change.\n- ghost notification hitbox preventing mouse events on windows.\n\n## [2.0.14]\n\n### hotfix\n\n- not creating the default (system) icon pack.\n\n## [2.0.13]\n\n### features\n\n- add kill process option on context menu for dock items.\n- multi-language calendar and date module on toolbar.\n- add icon packs selector on settings > general.\n- allow show only windows on monitor owner of Dock/Taskbar.\n\n### enhancements\n\n- allow search languages by their english label.\n\n### refactor\n\n- move dock state to the background.\n- remove pin sub-menu on dock.\n\n### fix\n\n- slu-service was being closed on exit code 1.\n- logging errors on monitor changes.\n- duplicated items on dock after drag items.\n\n## [2.0.12]\n\n### fix\n\n- msix version crashing on start.\n\n## [2.0.11]\n\n### features\n\n- add new setting on dock and toolbar to maintain overlap state by monitor.\n- add service to restart the seelen-ui app on crash.\n\n### enhancements\n\n- force run the app as an APPX if it was installed using msix.\n\n### refactor\n\n- custom http server to serve files instead bundle it in the executable on development (local).\n\n### fix\n\n- remove ghost settings window from dock.\n\n## [2.0.10]\n\n### fixes\n\n- fix settings `cancel` button not working correctly.\n- fix settings `save` button not saving the monitor settings correctly.\n\n## [2.0.9]\n\n### enhancements\n\n- add `XboxGameBarWidgets.exe` to the bundled apps settings list.\n\n### refactor\n\n- themes now use widget ids instead hard-coded keys.\n- improvements on events propagation.\n- improvements on settings by monitor implementation.\n\n### fix\n\n- window manager not working properly.\n- resolution, scale changes not refreshing the UI.\n\n## [2.0.8]\n\n### enhancements\n\n- add task manager shortcut on toolbar and dock context menu.\n- improve default no media style on dock.\n- add icons to context menus.\n\n### fix\n\n- randomized wallpaper slice freeze.\n- bad behavior on context menus and popups.\n\n## [2.0.7]\n\n### fix\n\n- crashing on fresh installations.\n\n## [2.0.6]\n\n### feature\n\n- add bases for future plugins and widgets sytems.\n- change wallpaper randomly.\n\n### enhancements\n\n- some UI/UX improvements on seelen wallpaper manager.\n- UI/UX improvements on wi-fi toolbar module.\n- seelen-ui added to user PATH enviroment variable.\n\n### fix\n\n- popups and context menus fast flashing.\n- media player app not appearing on media module (weg & toolbar).\n- touch looking the cursor.\n- improve start up failure behavior.\n\n## [2.0.5]\n\n### features\n\n- allow change default output volume using mouse wheel on media module items.\n- add mini calendar to date module.\n\n### enhancements\n\n- add new settings to delay the show and hide of dock and toolbar.\n\n### fix\n\n- dock and toolbar animations\n- fix update notifications for release and nightly channels.\n\n## [2.0.4]\n\n### fix\n\n- app crashing when changing settings on app launcher.\n- app previews on wrong position on dock.\n\n## [2.0.3]\n\n### fix\n\n- apps being runned as admin instead normal.\n\n## [2.0.2]\n\n### fix\n\n- infinite render loop on settings home page, fetching news.\n\n## [2.0.1]\n\n### refactor\n\n- unification of SeelenWeg pinned files, folder and apps in a single structure.\n\n### enhancements\n\n- improve open_file function to allow arguments.\n- allow to users select update channel between release, beta and nightly.\n\n### fix\n\n- not getting icons directly from .lnk files.\n- users not recieving update notification on settings header.\n- start-menu item on dock not closing native start menu.\n- default theme wallpaper showing cut on ultra-wide monitors.\n\n## [2.0.0]\n\n### breaking changes\n\n- Window Manager Layout Conditions was reimplemented, old conditions (v1) will fail.\n\n### refactor\n\n- refactors, more and more refactors, refactors for everyone.\n- reimplementation of Tiling Window Manager.\n- remove Update modal at startup by an update button on settings.\n\n### features\n\n- make the dock/taskbar solid when hide mode is `never`.\n- add app launcher (rofi for windows).\n- add seelen wall (rain-meter and wallpaper engine alternatives).\n- expose function to pin items into the dock.\n- settings by monitor.\n- window manager multimonitor support.\n- allow users change date format directly on UI settings.\n- add context menu to toolbar items.\n\n### enhancements\n\n- improve quality icons from all app/files items.\n- improve init loading performance.\n- improve fullscreen matching system.\n- reduce UI total size from 355mb (v1) to 121mb (v2-beta4) to 93mb (v2-beta8).\n- reduce Installer size from 75mb (v1) to 40mb (v2-beta4) to 28.8mb (v2-beta8).\n- allow drop files, apps and folders into the dock to pin them.\n- now Virtual Desktop shortcuts doesn't require Tiling WM be enabled to work.\n- now Themes are wrapped in a CSS layer, making easier the override theming.\n- allow change size of Window Manager Layouts via window resizing with the mouse.\n- allow close windows by middle clicking on dock items.\n- show icon of app in media players that are not uwp/msix.\n- show pwa apps like a separeted app from browser on dock.\n\n### fix\n\n- missing icons for files with a different extension than `exe`.\n- losing cursor events on clicking a dock item.\n- app allowing be closed via Alt + F4.\n- native taskbar being hidden regardless of whether the program starts successfully or not.\n- app continuing running when the program fails to start (case: WebView2 Runtime not installed).\n- no stoping correctly secondary processes/threads on app close.\n- showing unmanageable windows on dock.\n- restart seelen-ui button not working properly.\n- tray icons not working on others language than english.\n- edge tabs open in file explorer.\n\n## [1.10.6]\n\n### fix\n\n- tray module only working when the system language is english.\n\n## [1.10.5]\n\n### fix\n\n- app crashing on IMMDevice disconnection.\n\n## [1.10.4]\n\n### enhancements\n\n- clean weg items on load to remove duped items and apps/files that don't exist.\n- remove 1/2px thickness border on window manager border.\n- remove 1/2px black border on some previews of apps.\n\n### fix\n\n- can not restore settings window.\n- taskbar not been restored when changing weg enabled state.\n- taskbar been restored always as not autohide, now it will restored as initial state.\n\n## [1.10.3]\n\n### features\n\n- add beta channel\n\n### enhancements\n\n- add debugger cli toggles to tracing more info on logs.\n- media modules now exports the app related to the media player.\n\n### fix\n\n- saving ahk lib in wrong location.\n\n## [1.10.2]\n\n### fix\n\n- app crashing on enumerating many monitors or on large load.\n\n## [1.10.1]\n\n### fix\n\n- app crashing if uwp package has missing path.\n- app no working fine on multiple monitors.\n\n## [1.10.0]\n\n### features\n\n- add volume changed popup.\n- new custom virtual desktop implementation.\n- shortcut to toggle debug mode on hitboxes (Control + Win + Alt + H).\n\n### enhancements\n\n- remove black borders of windows previews on dock.\n- improve uwp app manage on dock/taskbar.\n\n### refactor\n\n- add strategy pattern to virtual desktops.\n\n### fix\n\n- topbar hitbox rect on auto hide equals to on-overlap.\n- bad matching fullscreen apps.\n- suspended process (ex: Settings) been shown on dock.\n- uwp icons not loading correctly.\n- bad focused app matching strategy.\n\n## [1.9.11]\n\n### features\n\n- add a option to hide apps from the dock/taskbar, requested on #5.\n- update tray labels when tray icons module are open.\n- add auto-hide option to the toolbar.\n\n### fix\n\n- route no maintaining on cancel changes on settings window.\n- cancel button no working correctly after save the settings multiple times.\n- tray module no forcing tray overflow creating on startup.\n- native taskbar not been restored on close.\n\n## [1.9.10]\n\n### features\n\n- add `getIcon` fn to the scopes of toolbar placeholders.\n\n### refactored\n\n- improve interfaces and documentation.\n\n### fix\n\n- styles of media module when dock is on left side.\n- opened apps been removing on weg items file change.\n- app crashing on update if language prop was null in the settings.json file.\n\n## [1.9.9]\n\n### refactored\n\n- internal interfaces to improve documentation and development.\n\n### enhancements\n\n- add language selector to the nsis installer.\n- allow search on lang selector on Seelen UI Settings.\n\n### fix\n\n- app no opening to new users.\n\n## [1.9.8]\n\n### enhancements\n\n- avoid recreate already existing folders.\n- separate lib and app in two crates.\n- improve click behavior on seelen weg item to make it more intuitive.\n\n### fix\n\n- can no disable run on startup.\n- text been cut on toolbar.\n- app crashing on wallpaper change on win11 24h2\n\n## [1.9.7]\n\n### enhancements\n\n- made all invoke handlers async\n\n### fix\n\n- crash on registering network event\n\n## [1.9.6]\n\n### fix\n\n- app crashing on 24h2\n\n## [1.9.5]\n\n### fix\n\n- app crashing by tray icon module\n\n## [1.9.4]\n\n### fix\n\n- app crashing for new users\n\n## [1.9.3]\n\n### performance\n\n- reduce load time from ~7s to ~4s\n\n### features\n\n- .slu and uri now are loaded correctly on seelen ui.\n- allow change wallpaper from seelen settings.\n\n### enhancements\n\n- add file associations for .slu files\n- add uri associations for seelen-ui:uri\n- improve settings editor experience by adding live reload feature.\n\n### fix\n\n- cli no working on production\n\n## [1.9.1]\n\n### fix\n\n- no listening window moving of virtual desktop events.\n- no closing or starting widgets on settings changes.\n- no listening monitors changes.\n- no loading toolbar modules on wake up\n\n## [1.9.0]\n\n### features\n\n- allow custom images on toolbar by `imgFromUrl`, `imgFromPath` and `imgFromExe` functions.\n- add notifications module to toolbar.\n- add exe path to window in generic module for toolbar.\n- add focused window icon to default toolbar layouts.\n\n### enhancements\n\n- icons now are recreated from exe path if icon was deleted.\n- uwp icons now are loaded from background.\n- improvements on themes selector.\n- improvements on system color detection and expose more system colors based in accent gamma.\n- improve theme creation experience by adding live reload feature.\n- improve toolbar layouts (placeholders) creation experience by adding live reload feature.\n- improve weg items editor experience by adding live reload feature.\n\n### refactor\n\n- deprecate `onClick` and add new `onClickV2` on toolbar modules.\n\n### fix\n\n- bad translations keys.\n- no restoring dock on closing fullscreened app.\n\n## [1.8.12]\n\n### fix\n\n- app installed by msix no opening.\n\n## [1.8.11]\n\n### fix\n\n- remove unnecessary 1px padding on toolbar.\n\n## [1.8.10]\n\n### enhancements\n\n- remove unnecessary loop on taskbar hiding function.\n\n### fix\n\n- no loading translations correctly on update modal.\n\n## [1.8.9]\n\n### enhancements\n\n- add translation to the rest of apps (dock, toolbar, and update modal).\n\n### fix\n\n- not hiding the taskbar at start.\n- opening multiple instances of the app.\n\n## [1.8.8]\n\n### fix\n\n- app not running on startup\n\n## [1.8.7]\n\n### fix\n\n- no updating themes on changes saved.\n\n## [1.8.6]\n\n### features\n\n- Add multi-language support! 🥳.\n- Add default media input/output selectors to media module in fancy toolbar.\n- Add start module to dock/taskbar (opens start menu).\n\n### enhancements\n\n- Flat default themes to allow easier overrides.\n\n### fix\n\n- Fix zorder on hovering on weg and toolbar respectively to wm borders.\n- Applying bad themes on apps.\n- Not hiding the taskbar at start.\n\n## [1.8.5]\n\n### fix\n\n- no executing seelen after update installation\n\n## [1.8.4]\n\n## [1.8.3]\n\n### refactor\n\n- migrate settings files from `$USER/.config/seelen` to `$APPDATA/com.seelen.seelen-ui`\n- load uwp apps info asynchronously\n\n### fix\n\n- crash on move toolbar item\n- can not remove media module\n\n## [1.8.2]\n\n### features\n\n- fancy toolbar items now can be dragged of position.\n- using fancy toolbar's layout now can be modified and saved as custom.yml.\n\n## [1.8.1]\n\n### features\n\n- styles can be specified in fancy toolbar placeholder item.\n- fancy toolbar item now will have an unique id, this can be specified in the placeholder file.\n\n### enhancements\n\n- replace \"bluetooth\" for \"devices\" on bundled fancy toolbar placeholders.\n\n## [1.8.0]\n\n### features\n\n- Media module added to the toolbar.\n- Media module added to SeelenWeg.\n\n  ![Media Module Example](documentation/images/media_module_preview.png)\n\n- SeelenWeg now has a context menu (Right Click Menu).\n\n### enhancements\n\n- enhancements on fullscreen events.\n\n### refactor\n\n- remove Default Wave animation on seelenweg (users will be able to add their own animations).\n\n### fix\n\n- no updating colors correctly on change light or dark mode on windows settings.\n- window manager enabled by default for new users.\n- showing tray icons with empty name.\n- no focusing seelen settings if it was minimized.\n\n## [1.7.7]\n\n### fix\n\n- no registering system events (battery/network/etc)\n\n## [1.7.6]\n\n### enhancements\n\n- improve logging on dev mode and fix missing target on production logged errors.\n- improve fullscreen matching.\n\n### fix\n\n- network icon showing incorrect interface icon (lan instead wifi).\n- no updating adapters list and using adapter on network changes.\n\n## [1.7.5]\n\n## [1.7.4]\n\n### enhancements\n\n- improvements on workflows to auto upload artifacts to the store.\n\n## [1.7.3]\n\n### enhancements\n\n- improvements on fullscreen events.\n\n## [1.7.2]\n\n### enhancements\n\n- disable tiling window manager on windows 10 from UI (can be forced on settings.json file)\n\n### fix\n\n- app crashing on windows 10\n- empty tray icons list on windows 10\n\n## [1.7.1]\n\n### enhancements\n\n- separate `information` and `developer tools` tabs in the settings.\n- add a option to open the install path in explorer.\n- focus settings window if already exist.\n- better performance on canceling changes in settings.\n- avoid loading innecesary files in modules that are not used.\n- update pinned apps path by filename on open (some apps change of path on updates so this will fix that).\n- show empty message on toolbar when no wlan networks are found.\n\n### fix\n\n- ahk error on save.\n\n## [1.7.0]\n\n### features\n\n- add Network toolbar module.\n- add WLAN selector to the Network toolbar module.\n- add css variable (--config-accent-color-rgb) to be used with css functions like `rgb` or `rgba`.\n\n### enhancements\n\n- now placeholders, layouts and themes can be loaded from data users folder (`AppData\\Roaming\\com.seelen.seelen-ui`)\n- now buttons and others components will use the user accent color.\n\n### fix\n\n- no max size on System Tray List module.\n\n## [1.6.4]\n\n### fix\n\n- xbox games showing missing icons on SeelenWeg.\n\n### enhancements\n\n- follow user accent color for tray list and windows borders\n\n### fix\n\n- no showing promoted (pinned on taskbar) tray icons.\n- toolbar no initialized correctly sometimes, now will retry if fails.\n- battery no updating level.\n- battery showing as always charging on default toolbar templates.\n- tray overflow module no working on different languages.\n\n### refactor\n\n- refactor on window_api and AppBar structures.\n\n## [1.6.3]\n\n### enhancements\n\n- only show a progress bar on update and not the complete installer GUI.\n\n### fix\n\n- main app no running if the forced creation of tray overflow fails.\n\n## [1.6.2]\n\n### features\n\n- now `batteries` and `battery` (same as: `batteries[0]`) are available on the scope of power toolbar module.\n\n### enhancements\n\n- add battery crate to handle batteries info directly from their drivers.\n- show if is smart charging.\n- now battery module wont be shown if batteries are not found.\n\n### fix\n\n- battery showing 255%.\n\n## [1.6.1]\n\n### fix\n\n- tray icons not showing on startup\n- hidden trays if icon was not found (now will show a missing icon)\n\n## [1.6.0]\n\n### features\n\n- add \"Run as admin\" option at context menu on Seelenweg.\n- allow receive commands using TCP connections.\n- Add System Tray Icons module, (incomplete, devices like usb or windows antivirus trays are still not supported).\n\n### enhancements\n\n- improve power (battery) events performance.\n- Window manager disabled by default to new users.\n\n### refactor\n\n- remove tauri single instance plugin by TCP connection.\n\n## [1.5.0]\n\n### features\n\n- new placeholder added to the bundle as alternative to default.\n- new workspace item available to be used in placeholders.\n\n### enhancements\n\n- support fullscreen apps (will hide the toolbar and the weg on fullscreen an app).\n\n### fix\n\n- showing incorrect format on dates at start of the app.\n- complex text with icons on toolbar items cause wraps.\n- missing icons on some uwp apps.\n\n### refactor\n\n- refactor on window event manager to allow synthetic events.\n\n## [1.4.1]\n\n### fix\n\n- no truncating text on toolbar items overflow.\n- rendering empty items on toolbar when empty placeholder is declared.\n\n## [1.4.0]\n\n### features\n\n- Modular Themes\n- Themes now allow tags to be categorized.\n- Allow add, organize, combine multiple themes as cascade layers.\n- Themes now allow folder structure to improve developers experience.\n\n### refactor\n\n- Now themes will use .yml files instead json to improve developers experience.\n- Themes schema updated, no backwards compatibility with json themes. (.json in themes folder will be ignored)\n\n### fix\n\n- No hiding the taskbar correctly.\n\n## [1.3.4]\n\n### enhancements\n\n- Add splash screen to Settings window.\n- Add discord link on Information Section.\n\n### refactor\n\n- Use TaskScheduler for autostart Seelen with priority and admin privileges.\n\n### fix\n\n- bad zorder on Weg and Toolbar under the WM borders\n\n## [1.3.3]\n\n### features\n\n- Multi-monitor support for Fancy Toolbar.\n- Multi-monitor support for Seelenweg.\n\n## [1.3.2]\n\n### enhancements\n\n- Remove unnecessary tooltip collision on toolbar items.\n\n### fix\n\n- Crash on restoring app in other virtual desktop using Weg.\n- Touch events not working on Toolbar and Weg.\n\n## [1.3.1]\n\n### fix\n\n- disable binding monitors and monitors on apps configurations for now.\n\n## [1.3.0]\n\n### features\n\n- Allow pin apps on Open using Apps Configurations.\n- Allow changes Shortcuts using UI.\n- Allow Binary Conditions in Apps Configurations Identifiers.\n- Allow change the Auto hide behavior for Seelenweg.\n\n### enhancements\n\n- Close AHK by itself if app is crashed or forcedly closed.\n- Configurations by apps are enabled again.\n- Allow open settings file from Extras/Information\n- Add opacity to toolbar (theme: default)\n\n### fix\n\n- Ahk not closing on app close or when user change options.\n\n## [1.2.4]\n\n### enhancements\n\n- Automatic MSIX bundle.\n- Add Github Actions for Releases.\n- Add Github Actions for Web Page.\n\n## [1.2.3]\n\n### features\n\n- Allow customize Fancy Toolbar modules using placeholders yaml files.\n- Add fast settings for toolbar allowing to adjust volume, brightness, etc.\n\n## [1.2.2]\n\n### enhancements\n\n- if app on weg is cloak, change of virtual desktop instead minimize/restore\n\n### fix\n\n- no closing AHK instances\n- floating size on fallback\n- reservation not working properly\n- ignore top most windows by default (normally these are tools or widgets)\n- minimization on weg not working properly if window manager is activated\n- change focus using commands not working with conditional layouts\n- randomly frozen app on start\n- no tiling UWP apps\n\n## [1.2.1]\n\n### enhancements\n\n- Allow quit from settings\n- Using Box-Content style in the position of windows instead outlined for a better user experience\n\n### fix\n\n- Managing windows without caption (Title bar)\n- can't update border configurations\n- hiding dock on switching virtual desktops\n\n## [1.2.0]\n\n### fix\n\n- Taskbar showing instead be always hidden\n\n## [1.1.1]\n\n### fix\n\n- Bad download URL in Update Notification\n- Showing update notification on installations by Windows Store\n\n## [1.1.0]\n\n### features\n\n- Add Smart Auto Hide for Seelenweg.\n- Add visible Separators Option\n- Enable animations for items into LEFT, TOP, RIGHT positions\n\n### enhancements\n\n- Now the copy handles option will return hexadecimal handles instead decimal (good for faster debug in tools like\n  spy++).\n\n### fix\n\n- duped handles\n- inconsistencies in separators width\n\n## [1.0.1]\n\n### fix\n\n- App downloaded form Microsoft Store was not running without admin.\n\n## [1.0.0]\n\n### refactored\n\n- Update notifications always enabled for nsis installer\n- Update notifications will not appear if app is installed using msix (Microsoft Store).\n\n### enhancements\n\n- Now by default if user is Admin, UAC will be triggered on run the app to allow a better integrated experience in\n  SeelenWeg and Komorebi Tiling Manager.\n\n## [1.0.0-prerelease.14]\n\n### features\n\n- add indicator to know opens and focused apps on SeelenWeg\n- allow set the position of seelenweg (left, top, right, bottom) 🎉\n\n### enhancements\n\n- only creates app icons the first time they are loaded\n\n### refactor\n\n- change themes implementation to allow customs css files\n\n### fix\n\n- incorrect icon for UWP (was using store icon instead taskbar icon)\n- replacing icons on each load\n- showing logs of opened apps on development\n- offset margins working like windows RECT instead like one side margins\n\n## [1.0.0-prerelease.13]\n\n### features\n\n- add Themes Feature 🎉 (incomplete only for Seelenweg for now)\n- add SeelenWeg (a Dock or Taskbar) beta feature\n- add SeelenWeg in to Settings\n- add ContextMenu for apps in SeelenWeg\n- allow move apps in the Weg 😄\n- add Grouped Apps in one item\n- live reload of Apps on events like change of title\n- UWP apps support\n\n### enhancements\n\n- move readme blob to documentation/images\n\n## [1.0.0-prerelease.12]\n\n### enhancements\n\n- add some traces on functions to save logs\n\n### fix\n\n- clean installation of komorebi no working\n\n## [1.0.0-prerelease.11]\n\n### refactor\n\n- little improvements on background code\n\n### fix\n\n- initial users can not save the settings\n\n## [1.0.0-prerelease.10]\n\n### features\n\n- add a update tab to allow users decide if will receive notifications for updates\n\n## [1.0.0-prerelease.9]\n\n## [1.0.0-prerelease.8]\n\n- add functionality to pause btn on tray menu\n\n## [1.0.0-prerelease.6]\n\n### added\n\n- Enable Updater 🎉\n\n## [1.0.0-prerelease.3]\n\n### fix\n\n- icon not showing on tray\n- poor icon quality on task bar\n- StartUp running bad exe file\n\n## [1.0.0-prerelease.2]\n\n## [1.0.0-prerelease.1]\n\n### added\n\n- implement tray icon\n\n### refactored\n\n- Migrate all app background from Electron ⚡ to Tauri 🦀\n- reimplement startup to use native system startup\n- reimplement included shortcuts with ahk\n- reimplement komorebi autostart\n- reimplement installer to use NSIS\n- refactor folder structure to isolate front-end apps\n\n## [1.0.0-beta.13]\n\n### enhancements\n\n- improve maximized windows experience\n\n### fixed\n\n- fix resize not working (now works like master)\n\n## [1.0.0-beta.12]\n\n### added\n\n- show current used versions on information\n- add grid layout preview\n- add win + k to open komorebi settings\n\n### refactored\n\n- update komorebi to 0.1.22\n\n### removed\n\n- remove invisible borders feature\n\n## [1.0.0-beta.11]\n\n### fixed\n\n- missing property on schema\n- white screen on start app\n\n## [1.0.0-beta.10]\n\n### added\n\n- add a new way to match applications by path\n\n### fixed\n\n- searching feature on apps\n- no focusing windows on change workspace\n- autostacking not working properly\n- workspaces rules not working\n\n## [1.0.0-beta.9]\n\n### added\n\n- add popups on actions 🦀\n- now can switch from installed and packaged and should work as the same\n\n### fixed\n\n- fix no removing old path\n- lag on many applications\n\n## [1.0.0-beta.8]\n\n### added\n\n- add more templates\n\n## [1.0.0-beta.7]\n\n### fixed\n\n- fix first install\n\n## [1.0.0-beta.6]\n\n### added\n\n- delete old paths on update\n\n### fixed\n\n- fix not saving templates\n- fix toggle ahk shortcuts does not run or stop the instance\n- running ahk when disabled\n- not updating the path of installation folder on update for windows tasks\n\n## [1.0.0-beta.5]\n\n### added\n\n- new searching option for applications\n- templates feature\n\n### fixed\n\n- including ghost apps on migration\n\n## [1.0.0-beta.4]\n\n### added\n\n- new feature of invisible borders per app\n- new easy way to hard restart the services and AHK\n\n### changed\n\n- delete border overflow and changed for invisible borders per app\n\n### fixed\n\n- components was not triggering dark mode correctly\n\n## [1.0.0-beta.3]\n\n### added\n\n- new apps templates\n- add AHK as a dependency to show to new users\n- add AHK settings\n\n## [1.0.0-beta.2]\n\n### added\n\n- export option for apps\n\n### fixed\n\n- delete bound monitor and workspace on an application\n- bad installation on setup\n"
  },
  {
    "path": "crowdin.yml",
    "content": "base_path: \".\"\nbase_url: \"https://api.crowdin.com\"\npreserve_hierarchy: true\nfiles:\n  - source: i18n/**/en.yml\n    translation: /%original_path%/%two_letters_code%.yml\n"
  },
  {
    "path": "deno.json",
    "content": "{\n  \"lint\": {\n    \"rules\": {\n      \"include\": [\n        \"verbatim-module-syntax\"\n      ],\n      \"exclude\": [\n        \"prefer-const\",\n        \"jsx-button-has-type\",\n        \"no-explicit-any\",\n        \"ban-types\",\n        \"no-window\",\n        \"no-window-prefix\",\n        \"no-sloppy-imports\"\n      ]\n    }\n  },\n  \"fmt\": {\n    \"lineWidth\": 120,\n    \"exclude\": [\n      \"**/i18n/translations/**\"\n    ]\n  }\n}\n"
  },
  {
    "path": "lefthook.yml",
    "content": "commit-msg:\n  commands:\n    lint-commit-msg:\n      run: npx commitlint --edit\n\npre-commit:\n  commands:\n    file-format:\n      priority: 1\n      run: deno fmt --check\n    rust-format:\n      priority: 2\n      glob: \"**/*.rs\"\n      run: cargo fmt -- --check\n    ts-type-check:\n      priority: 3\n      glob: \"**/*.{js,jsx,ts,tsx}\"\n      run: npm run type-check\n    js-linter:\n      priority: 5\n      glob: \"**/*.{js,jsx,ts,tsx}\"\n      run: deno lint\n    rust-linter:\n      priority: 6\n      glob: \"**/*.rs\"\n      run: cargo clippy --all-targets -- -D warnings\n\npre-push:\n  commands:\n    js-test:\n      priority: 1\n      glob: \"**/*.{js,jsx,ts,tsx}\"\n      run: npm run test\n    rust-test:\n      priority: 3\n      glob: \"**/*.rs\"\n      run: cargo test\n"
  },
  {
    "path": "libs/core/.gitignore",
    "content": "npm"
  },
  {
    "path": "libs/core/Cargo.toml",
    "content": "[package]\nname = \"seelen-core\"\nversion = \"2.5.7\"\nedition = \"2021\"\nrust-version = \"1.89\"\n\n[lints]\nworkspace = true\n\n[dependencies]\nserde = { workspace = true, features = [\"derive\"] }\nserde_json = { workspace = true }\nserde_yaml = { workspace = true }\nserde_alias = \"0.0.2\"\nschemars = { version = \"1.2.1\", features = [\"url2\", \"uuid1\", \"chrono04\"] }\nregex = { workspace = true }\nsys-locale = \"0.3.2\"\nuuid = { workspace = true, features = [\"v4\", \"serde\"] }\nts-rs = { version = \"12.0.0\", features = [\n  \"no-serde-warnings\",\n  \"serde-json-impl\",\n  \"url-impl\",\n  \"uuid-impl\",\n  \"chrono-impl\",\n] }\nbase64 = { workspace = true }\nurl = { workspace = true, features = [\"serde\"] }\ngrass = { version = \"0.13.4\", default-features = false, features = ['random'] }\nnum_enum = \"0.7.5\"\nchrono = { version = \"0.4.43\", features = [\"serde\"] }\npaste = \"1.0.15\"\n\n[features]\ngen-binds = []\n"
  },
  {
    "path": "libs/core/deno.json",
    "content": "{\n  \"name\": \"@seelen-ui/lib\",\n  \"version\": \"2.5.7\",\n  \"description\": \"Seelen UI Library for Widgets\",\n  \"license\": \"AGPL-3.0-only\",\n  \"exports\": {\n    \".\": \"./src/lib.ts\",\n    \"./types\": \"./gen/types/mod.ts\",\n    \"./tauri\": \"./src/re-exports/tauri.ts\"\n  },\n  \"tasks\": {\n    \"build:npm\": \"deno run -A ./scripts/build_npm.ts\",\n    \"build:rs\": \"deno run -A ./scripts/rust_bindings.ts\",\n    \"build\": \"deno task build:rs && deno task build:npm\"\n  },\n  \"test\": {\n    \"include\": [\n      \"src/**/*.test.ts\"\n    ]\n  },\n  \"imports\": {\n    \"@deno/dnt\": \"jsr:@deno/dnt@0.42.3\",\n    \"@seelen-ui/types\": \"./gen/types/mod.ts\",\n    \"@std/assert\": \"jsr:@std/assert@^1.0.19\",\n    \"@std/encoding\": \"jsr:@std/encoding@^1.0.10\",\n    \"@tauri-apps/api\": \"npm:@tauri-apps/api@^2.10.1\",\n    \"@tauri-apps/plugin-dialog\": \"npm:@tauri-apps/plugin-dialog@^2.6.0\",\n    \"@tauri-apps/plugin-fs\": \"npm:@tauri-apps/plugin-fs@^2.4.5\",\n    \"@tauri-apps/plugin-log\": \"npm:@tauri-apps/plugin-log@^2.8.0\",\n    \"@tauri-apps/plugin-process\": \"npm:@tauri-apps/plugin-process@^2.3.1\"\n  },\n  \"compilerOptions\": {\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"ES2023\"\n    ]\n  },\n  \"fmt\": {\n    \"lineWidth\": 120\n  },\n  \"lint\": {\n    \"rules\": {\n      \"include\": [\n        \"explicit-function-return-type\"\n      ],\n      \"exclude\": [\n        \"no-slow-types\"\n      ]\n    }\n  },\n  \"publish\": {\n    \"include\": [\n      \"src\",\n      \"gen\",\n      \"readme.md\"\n    ],\n    \"exclude\": [\n      \"src/**/*.test.ts\"\n    ]\n  },\n  \"exclude\": [\n    \"npm\"\n  ]\n}\n"
  },
  {
    "path": "libs/core/mocks/themes/v2.3.0.yml",
    "content": "id: \"@eythaann/variables-test\"\nmetadata:\n  displayName: Variables Test\n  description: this theme is just for testing\n  appTargetVersion: [2, 3, 0]\nsettings:\n  - syntax: <color>\n    name: --testing-settings1-1\n    label: Testing Color Setting 1\n    initialValue: \"#f00\"\n  - syntax: <color>\n    name: --testing-settings1-2\n    label: Testing Color Setting 2\n    initialValue: \"#00f\"\n  - syntax: <length>\n    name: --testing-sizes\n    label: Testing Length Setting 3\n    initialValue: 20\n    initialValueUnit: px\n  - syntax: <number>\n    name: --testing-number\n    label: Testing Number Setting\n    initialValue: 60\n  - syntax: <string>\n    name: --testing-string\n    label: Testing User Input\n    initialValue: \"hello world\"\n  - syntax: <url>\n    name: --testing-url\n    label: Testing User URL Input\n    initialValue: https://google.com\n"
  },
  {
    "path": "libs/core/mocks/themes/v2.3.12.yml",
    "content": "id: \"@eythaann/variables-test\"\nmetadata:\n  displayName: Variables Test\n  description: this theme is just for testing\n  appTargetVersion: [2, 3, 12]\nsettings:\n  - syntax: <color>\n    name: --testing-settings\n    label: Testing Color Setting1-1\n    initialValue: \"#f00\"\n  - syntax: <color>\n    name: --testing-settings1-2\n    label: Testing Color Setting 2\n    initialValue: \"#00f\"\n  - syntax: <length>\n    name: --testing-sizes\n    label: Testing Length Setting 3\n    initialValue: 20\n    initialValueUnit: px\n  - syntax: <number>\n    name: --testing-number\n    label: Testing Number Setting\n    initialValue: 60\n  - syntax: <string>\n    name: --testing-string\n    label: Testing User Input\n    initialValue: \"hello world\"\n  - syntax: <url>\n    name: --testing-url\n    label: Testing User URL Input\n    initialValue: https://google.com\n\n  - group:\n      header: This is a group header\n      items:\n        - syntax: <color>\n          name: --testing-settings2-1\n          label: Testing Color Setting\n          description: This is a description to test log text in the settings UI\n          initialValue: \"#f00\"\n        - syntax: <color>\n          name: --testing-settings2-2\n          label: Testing Color Setting 2\n          initialValue: \"#00f\"\n        - syntax: <length>\n          name: --testing-sizes2\n          label: Testing Length Setting 3\n          description: This is a description to test log text in the settings UI\n          initialValue: 20\n          initialValueUnit: px\n        - syntax: <number>\n          name: --testing-number2\n          label: Testing Number Setting\n          initialValue: 60\n        - syntax: <string>\n          name: --testing-string2\n          label: Testing User Input\n          initialValue: \"hello world\"\n        - syntax: <url>\n          name: --testing-url2\n          label: Testing User URL Input\n          initialValue: https://google.com\n\n        - group:\n            header: This is a sub group header\n            items:\n              - syntax: <color>\n                name: --testing-settings2-1\n                label: Testing Color Setting\n                initialValue: \"#f00\"\n              - syntax: <color>\n                name: --testing-settings2-2\n                label: Testing Color Setting 2\n                initialValue: \"#00f\"\n              - syntax: <length>\n                name: --testing-sizes2\n                label: Testing Length Setting 3\n                tip: This is a tooltip to test, log text in the settings UI here as well as the description below it as well as the label of the setting\n                description: Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua\n                initialValue: 20\n                initialValueUnit: px\n              - syntax: <number>\n                name: --testing-number2\n                label: Testing Number Setting\n                description: This is a description to test log text in the settings UI\n                initialValue: 60\n                options:\n                  - 60\n                  - 90\n                  - 120\n                  - 300\n              - syntax: <string>\n                name: --testing-string2\n                label: Testing User Input\n                initialValue: \"hello world\"\n              - syntax: <url>\n                name: --testing-url2\n                label: Testing User URL Input\n                initialValue: https://google.com\n"
  },
  {
    "path": "libs/core/mod.ts",
    "content": "// this re-export file is needed as a workaround for a bug in @deno/dnt\n//\n// remember usage `madge --circular .\\mod.ts` to find circular dependencies\nexport * from \"./src/lib.ts\";\n"
  },
  {
    "path": "libs/core/readme.md",
    "content": "<h1 align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/eythaann/Seelen-UI/812f64d29162fe49da6f621d5e2a3e4852b8b8b1/documentation/images/logo.svg\" width=\"44\" align=\"top\" alt=\"Seelen UI Logo\" />\n  Seelen UI Library\n</h1>\n\nThe **Seelen UI Library** is the core library for [Seelen UI](https://github.com/eythaann/seelen-ui), a highly\ncustomizable desktop UI. This library provides the necessary tools and types to create and manage widgets, plugins, and\nthemes for the Seelen UI application. It's a hybrid library with a Rust core and TypeScript/Deno bindings, designed for\nperformance and type safety.\n\n## Installation\n\n### TypeScript/Deno\n\nYou can use the library from JSR or npm.\n\n**JSR:**\n\n```sh\ndeno add @seelen-ui/lib\n```\n\n**NPM:**\n\n```sh\nnpm install @seelen-ui/lib\n```\n\n## Contributing\n\nContributions are welcome! Please read our [contributing guidelines](contributing.md) to get started.\n\n## License\n\nThis project is licensed under the AGPL-3.0 License. See the [LICENSE](LICENSE) file for details.\n\n## Links\n\n- [GitHub Repository](https://github.com/Seelen-Inc/slu-lib)\n- [NPM Package](https://npmjs.com/package/@seelen-ui/lib)\n- [JSR Package](https://jsr.io/@seelen-ui/lib)\n"
  },
  {
    "path": "libs/core/scripts/build_npm.ts",
    "content": "/// <reference lib=\"deno.ns\" />\n\nimport { build, type BuildOptions, emptyDir } from \"@deno/dnt\";\n\nimport denoJson from \"../deno.json\" with { type: \"json\" };\n\nconst { name, description, version, license } = denoJson;\nconst packageJson: BuildOptions[\"package\"] = {\n  name,\n  description,\n  version,\n  license,\n  repository: {\n    type: \"git\",\n    url: \"git+https://github.com/eythaann/Seelen-UI.git\",\n  },\n  bugs: {\n    url: \"https://github.com/eythaann/Seelen-UI/issues\",\n  },\n};\n\nawait emptyDir(\"./npm\"); // clear previous build\nawait build({\n  compilerOptions: {\n    lib: [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    target: \"ES2023\",\n  },\n  test: false, // this is performed by CI\n  typeCheck: false, // this is performed by CI\n  entryPoints: [{\n    name: \".\",\n    path: \"./mod.ts\",\n  }, {\n    name: \"./types\",\n    path: \"./gen/types/mod.ts\",\n  }, {\n    name: \"./tauri\",\n    path: \"./src/re-exports/tauri.ts\",\n  }],\n  outDir: \"./npm\",\n  shims: {},\n  importMap: \"deno.json\",\n  package: packageJson,\n  postBuild(): void {\n    Deno.copyFileSync(\"../../LICENSE\", \"npm/LICENSE\");\n    Deno.copyFileSync(\"readme.md\", \"npm/readme.md\");\n    Deno.removeSync(\"npm/src\", { recursive: true });\n  },\n});\n"
  },
  {
    "path": "libs/core/scripts/rust_bindings.ts",
    "content": "/// <reference lib=\"deno.ns\" />\n\nawait Deno.mkdir(\"./gen/types\", { recursive: true });\n// await Deno.mkdir('./src/validators', { recursive: true });\n\nconst GenTypesPath = await Deno.realPath(\"./gen/types\");\n// const GenJsonSchemasPath = await Deno.realPath('./gen/schemas');\n// const GenZodSchemasPath = await Deno.realPath('./src/validators');\n\nconst libPath = await Deno.realPath(\"./src/lib.ts\");\n\nconsole.log(\"[Task] Removing old bindings...\");\nawait Deno.remove(GenTypesPath, { recursive: true });\n// await Deno.remove(GenZodSchemasPath, { recursive: true });\n// recreate\nawait Deno.mkdir(GenTypesPath, { recursive: true });\n// await Deno.mkdir(GenZodSchemasPath, { recursive: true });\n\n{\n  console.log(\"[Task] Generating Typescript Bindings and JSON Schemas...\");\n  // yeah cargo test generates the typescript bindings, why? ask to @aleph-alpha/ts-rs xd\n  // btw internally we also decided to use tests to avoid having a binary.\n  // also this gill generate the json schemas\n  await new Deno.Command(\"cargo\", {\n    args: [\"test\", \"--features\", \"gen-binds\"],\n    stderr: \"inherit\",\n    stdout: \"inherit\",\n  }).output();\n}\n\n/* {\n  console.log('[Task] Converting JSON Schemas to Zod Schemas...');\n  for (const file of Deno.readDirSync(GenJsonSchemasPath)) {\n    if (file.isFile && file.name.endsWith('.schema.json')) {\n      const schema = JSON.parse(await Deno.readTextFile(`${GenJsonSchemasPath}/${file.name}`));\n      const { resolved } = await resolveRefs(schema);\n\n      const zodCode = jsonSchemaToZod(resolved, { module: 'esm' });\n      await Deno.writeTextFile(`${GenZodSchemasPath}/${file.name.replace('.schema.json', '.ts')}`, zodCode);\n    }\n  }\n} */\n\n{\n  console.log(\"[Task] Creating entry points...\");\n  /* const zodMod = await Deno.open(`${GenZodSchemasPath}/mod.ts`, {\n    create: true,\n    append: true,\n  });\n  for (const file of Deno.readDirSync(GenZodSchemasPath)) {\n    if (file.isFile && file.name.endsWith('.ts') && file.name !== 'mod.ts') {\n      await zodMod.write(\n        new TextEncoder().encode(`export { default as ${file.name.replace('.ts', '')} } from './${file.name}';\\n`),\n      );\n    }\n  } */\n\n  const typesMod = await Deno.open(`${GenTypesPath}/mod.ts`, {\n    create: true,\n    append: true,\n  });\n  for (const entry of Deno.readDirSync(GenTypesPath)) {\n    if (entry.isFile && entry.name.endsWith(\".ts\") && entry.name !== \"mod.ts\") {\n      await typesMod.write(\n        new TextEncoder().encode(`export * from './${entry.name}';\\n`),\n      );\n    }\n  }\n}\n\n{\n  console.log(\"[Task] Extracting Types Definitions...\");\n  const doc = await new Deno.Command(\"deno\", {\n    args: [\"doc\", \"--json\", \"--private\", `${GenTypesPath}/mod.ts`],\n    stderr: \"inherit\",\n    stdout: \"piped\",\n  }).output();\n  const docJson = JSON.parse(new TextDecoder().decode(doc.stdout));\n  await Deno.writeTextFile(\n    \"./gen/doc-types.json\",\n    JSON.stringify(docJson, null, 2),\n  );\n}\n\n{\n  console.log(\"[Task] Extracting Library Definitions...\");\n  const doc2 = await new Deno.Command(\"deno\", {\n    args: [\"doc\", \"--json\", \"--private\", libPath],\n    stderr: \"inherit\",\n    stdout: \"piped\",\n  }).output();\n  const docJson2 = JSON.parse(new TextDecoder().decode(doc2.stdout));\n  await Deno.writeTextFile(\n    \"./gen/doc-lib.json\",\n    JSON.stringify(docJson2, null, 2),\n  );\n}\n\nconsole.log(\"[Task] Formatting...\");\nawait new Deno.Command(\"cargo\", {\n  args: [\"fmt\"],\n  stderr: \"inherit\",\n  stdout: \"inherit\",\n}).output();\nawait new Deno.Command(\"deno\", {\n  args: [\"fmt\", \"--quiet\"],\n  stderr: \"inherit\",\n  stdout: \"inherit\",\n}).output();\nconsole.log(\"[Task] Done!\");\n"
  },
  {
    "path": "libs/core/src/constants/mod.rs",
    "content": "pub const SUPPORTED_LANGUAGES: &[SupportedLanguage] = &[\n    lang(\"Afrikaans\", \"Afrikaans\", \"af\"),\n    lang(\"አማርኛ\", \"Amharic\", \"am\"),\n    lang(\"العربية\", \"Arabic\", \"ar\"),\n    lang(\"Azərbaycan\", \"Azerbaijani\", \"az\"),\n    lang(\"Български\", \"Bulgarian\", \"bg\"),\n    lang(\"বাংলা\", \"Bengali\", \"bn\"),\n    lang(\"Bosanski\", \"Bosnian\", \"bs\"),\n    lang(\"Català\", \"Catalan\", \"ca\"),\n    lang(\"Čeština\", \"Czech\", \"cs\"),\n    lang(\"Cymraeg\", \"Welsh\", \"cy\"),\n    lang(\"Dansk\", \"Danish\", \"da\"),\n    lang(\"Deutsch\", \"German\", \"de\"),\n    lang(\"Ελληνικά\", \"Greek\", \"el\"),\n    lang(\"English\", \"English\", \"en\"),\n    lang(\"Español\", \"Spanish\", \"es\"),\n    lang(\"Eesti\", \"Estonian\", \"et\"),\n    lang(\"Euskara\", \"Basque\", \"eu\"),\n    lang(\"فارسی\", \"Persian\", \"fa\"),\n    lang(\"Suomi\", \"Finnish\", \"fi\"),\n    lang(\"Français\", \"French\", \"fr\"),\n    lang(\"ગુજરાતી\", \"Gujarati\", \"gu\"),\n    lang(\"עברית\", \"Hebrew\", \"he\"),\n    lang(\"हिन्दी\", \"Hindi\", \"hi\"),\n    lang(\"Hrvatski\", \"Croatian\", \"hr\"),\n    lang(\"Magyar\", \"Hungarian\", \"hu\"),\n    lang(\"Հայերեն\", \"Armenian\", \"hy\"),\n    lang(\"Indonesia\", \"Indonesian\", \"id\"),\n    lang(\"Íslenska\", \"Icelandic\", \"is\"),\n    lang(\"Italiano\", \"Italian\", \"it\"),\n    lang(\"日本語\", \"Japanese\", \"ja\"),\n    lang(\"ქართული\", \"Georgian\", \"ka\"),\n    lang(\"ភាសាខ្មែរ\", \"Khmer\", \"km\"),\n    lang(\"한국어\", \"Korean\", \"ko\"),\n    lang(\"Kurdî\", \"Kurdish\", \"ku\"),\n    lang(\"Lëtzebuergesch\", \"Luxembourgish\", \"lb\"),\n    lang(\"ລາວ\", \"Lao\", \"lo\"),\n    lang(\"Lietuvių\", \"Lithuanian\", \"lt\"),\n    lang(\"Latviešu\", \"Latvian\", \"lv\"),\n    lang(\"Македонски\", \"Macedonian\", \"mk\"),\n    lang(\"Монгол\", \"Mongolian\", \"mn\"),\n    lang(\"Malay\", \"Malay\", \"ms\"),\n    lang(\"Malti\", \"Maltese\", \"mt\"),\n    lang(\"नेपाली\", \"Nepali\", \"ne\"),\n    lang(\"Nederlands\", \"Dutch\", \"nl\"),\n    lang(\"Norsk\", \"Norwegian\", \"no\"),\n    lang(\"ਪੰਜਾਬੀ\", \"Punjabi\", \"pa\"),\n    lang(\"Polski\", \"Polish\", \"pl\"),\n    lang(\"پښتو\", \"Pashto\", \"ps\"),\n    lang(\"Português (Brasil)\", \"Portuguese (Brazil)\", \"pt-BR\"),\n    lang(\"Português (Portugal)\", \"Portuguese (Portugal)\", \"pt-PT\"),\n    lang(\"Română\", \"Romanian\", \"ro\"),\n    lang(\"Русский\", \"Russian\", \"ru\"),\n    lang(\"සිංහල\", \"Sinhala\", \"si\"),\n    lang(\"Slovenský\", \"Slovak\", \"sk\"),\n    lang(\"Soomaali\", \"Somali\", \"so\"),\n    lang(\"Српски\", \"Serbian\", \"sr\"),\n    lang(\"Svenska\", \"Swedish\", \"sv\"),\n    lang(\"Kiswahili\", \"Swahili\", \"sw\"),\n    lang(\"தமிழ்\", \"Tamil\", \"ta\"),\n    lang(\"తెలుగు\", \"Telugu\", \"te\"),\n    lang(\"Тоҷикӣ\", \"Tajik\", \"tg\"),\n    lang(\"ไทย\", \"Thai\", \"th\"),\n    lang(\"Tagalog\", \"Filipino\", \"tl\"),\n    lang(\"Türkçe\", \"Turkish\", \"tr\"),\n    lang(\"Українська\", \"Ukrainian\", \"uk\"),\n    lang(\"اردو\", \"Urdu\", \"ur\"),\n    lang(\"Oʻzbek\", \"Uzbek\", \"uz\"),\n    lang(\"Tiếng Việt\", \"Vietnamese\", \"vi\"),\n    lang(\"Yorùbá\", \"Yoruba\", \"yo\"),\n    lang(\"中文 (简体)\", \"Chinese (Simplified)\", \"zh-CN\"),\n    lang(\"中文 (繁體)\", \"Chinese (Traditional)\", \"zh-TW\"),\n    lang(\"isiZulu\", \"Zulu\", \"zu\"),\n];\n\npub struct SupportedLanguage {\n    pub label: &'static str,\n    pub en_label: &'static str,\n    pub value: &'static str,\n}\n\nconst fn lang(\n    label: &'static str,\n    en_label: &'static str,\n    value: &'static str,\n) -> SupportedLanguage {\n    SupportedLanguage {\n        label,\n        en_label,\n        value,\n    }\n}\n"
  },
  {
    "path": "libs/core/src/constants/mod.ts",
    "content": "export type SupportedLanguagesCode = (typeof SupportedLanguages)[number][\"value\"];\nexport interface SupportedLanguage {\n  label: string;\n  enLabel: string;\n  /** language code @example 'de' 'es' 'zh' 'en-US' 'en-UK' */\n  value: string;\n}\n\nexport const SupportedLanguages = [\n  { label: \"Afrikaans\", enLabel: \"Afrikaans\", value: \"af\" },\n  { label: \"አማርኛ\", enLabel: \"Amharic\", value: \"am\" },\n  { label: \"العربية\", enLabel: \"Arabic\", value: \"ar\" },\n  { label: \"Azərbaycan\", enLabel: \"Azerbaijani\", value: \"az\" },\n  { label: \"Български\", enLabel: \"Bulgarian\", value: \"bg\" },\n  { label: \"বাংলা\", enLabel: \"Bengali\", value: \"bn\" },\n  { label: \"Bosanski\", enLabel: \"Bosnian\", value: \"bs\" },\n  { label: \"Català\", enLabel: \"Catalan\", value: \"ca\" },\n  { label: \"Čeština\", enLabel: \"Czech\", value: \"cs\" },\n  { label: \"Cymraeg\", enLabel: \"Welsh\", value: \"cy\" },\n  { label: \"Dansk\", enLabel: \"Danish\", value: \"da\" },\n  { label: \"Deutsch\", enLabel: \"German\", value: \"de\" },\n  { label: \"Ελληνικά\", enLabel: \"Greek\", value: \"el\" },\n  { label: \"English\", enLabel: \"English\", value: \"en\" },\n  { label: \"Español\", enLabel: \"Spanish\", value: \"es\" },\n  { label: \"Eesti\", enLabel: \"Estonian\", value: \"et\" },\n  { label: \"Euskara\", enLabel: \"Basque\", value: \"eu\" },\n  { label: \"فارسی\", enLabel: \"Persian\", value: \"fa\" },\n  { label: \"Suomi\", enLabel: \"Finnish\", value: \"fi\" },\n  { label: \"Français\", enLabel: \"French\", value: \"fr\" },\n  { label: \"ગુજરાતી\", enLabel: \"Gujarati\", value: \"gu\" },\n  { label: \"עברית\", enLabel: \"Hebrew\", value: \"he\" },\n  { label: \"हिन्दी\", enLabel: \"Hindi\", value: \"hi\" },\n  { label: \"Hrvatski\", enLabel: \"Croatian\", value: \"hr\" },\n  { label: \"Magyar\", enLabel: \"Hungarian\", value: \"hu\" },\n  { label: \"Հայերեն\", enLabel: \"Armenian\", value: \"hy\" },\n  { label: \"Indonesia\", enLabel: \"Indonesian\", value: \"id\" },\n  { label: \"Íslenska\", enLabel: \"Icelandic\", value: \"is\" },\n  { label: \"Italiano\", enLabel: \"Italian\", value: \"it\" },\n  { label: \"日本語\", enLabel: \"Japanese\", value: \"ja\" },\n  { label: \"ქართული\", enLabel: \"Georgian\", value: \"ka\" },\n  { label: \"ភាសាខ្មែរ\", enLabel: \"Khmer\", value: \"km\" },\n  { label: \"한국어\", enLabel: \"Korean\", value: \"ko\" },\n  { label: \"Kurdî\", enLabel: \"Kurdish\", value: \"ku\" },\n  { label: \"Lëtzebuergesch\", enLabel: \"Luxembourgish\", value: \"lb\" },\n  { label: \"ລາວ\", enLabel: \"Lao\", value: \"lo\" },\n  { label: \"Lietuvių\", enLabel: \"Lithuanian\", value: \"lt\" },\n  { label: \"Latviešu\", enLabel: \"Latvian\", value: \"lv\" },\n  { label: \"Македонски\", enLabel: \"Macedonian\", value: \"mk\" },\n  { label: \"Монгол\", enLabel: \"Mongolian\", value: \"mn\" },\n  { label: \"Malay\", enLabel: \"Malay\", value: \"ms\" },\n  { label: \"Malti\", enLabel: \"Maltese\", value: \"mt\" },\n  { label: \"नेपाली\", enLabel: \"Nepali\", value: \"ne\" },\n  { label: \"Nederlands\", enLabel: \"Dutch\", value: \"nl\" },\n  { label: \"Norsk\", enLabel: \"Norwegian\", value: \"no\" },\n  { label: \"ਪੰਜਾਬੀ\", enLabel: \"Punjabi\", value: \"pa\" },\n  { label: \"Polski\", enLabel: \"Polish\", value: \"pl\" },\n  { label: \"پښتو\", enLabel: \"Pashto\", value: \"ps\" },\n  { label: \"Português (Brasil)\", enLabel: \"Portuguese (Brazil)\", value: \"pt-BR\" },\n  { label: \"Português (Portugal)\", enLabel: \"Portuguese (Portugal)\", value: \"pt-PT\" },\n  { label: \"Română\", enLabel: \"Romanian\", value: \"ro\" },\n  { label: \"Русский\", enLabel: \"Russian\", value: \"ru\" },\n  { label: \"සිංහල\", enLabel: \"Sinhala\", value: \"si\" },\n  { label: \"Slovenský\", enLabel: \"Slovak\", value: \"sk\" },\n  { label: \"Soomaali\", enLabel: \"Somali\", value: \"so\" },\n  { label: \"Српски\", enLabel: \"Serbian\", value: \"sr\" },\n  { label: \"Svenska\", enLabel: \"Swedish\", value: \"sv\" },\n  { label: \"Kiswahili\", enLabel: \"Swahili\", value: \"sw\" },\n  { label: \"தமிழ்\", enLabel: \"Tamil\", value: \"ta\" },\n  { label: \"తెలుగు\", enLabel: \"Telugu\", value: \"te\" },\n  { label: \"Тоҷикӣ\", enLabel: \"Tajik\", value: \"tg\" },\n  { label: \"ไทย\", enLabel: \"Thai\", value: \"th\" },\n  { label: \"Tagalog\", enLabel: \"Filipino\", value: \"tl\" },\n  { label: \"Türkçe\", enLabel: \"Turkish\", value: \"tr\" },\n  { label: \"Українська\", enLabel: \"Ukrainian\", value: \"uk\" },\n  { label: \"اردو\", enLabel: \"Urdu\", value: \"ur\" },\n  { label: \"Oʻzbek\", enLabel: \"Uzbek\", value: \"uz\" },\n  { label: \"Tiếng Việt\", enLabel: \"Vietnamese\", value: \"vi\" },\n  { label: \"Yorùbá\", enLabel: \"Yoruba\", value: \"yo\" },\n  { label: \"中文 (简体)\", enLabel: \"Chinese (Simplified)\", value: \"zh-CN\" },\n  { label: \"中文 (繁體)\", enLabel: \"Chinese (Traditional)\", value: \"zh-TW\" },\n  { label: \"isiZulu\", enLabel: \"Zulu\", value: \"zu\" },\n] as const;\n"
  },
  {
    "path": "libs/core/src/error.rs",
    "content": "macro_rules! define_app_errors {\n    ($(\n        $variant:ident($error_type:ty);\n    )*) => {\n        #[derive(Debug)]\n        pub enum SeelenLibError {\n            $(\n                $variant($error_type),\n            )*\n        }\n\n        $(\n            impl From<$error_type> for SeelenLibError {\n                fn from(err: $error_type) -> Self {\n                    SeelenLibError::$variant(err)\n                }\n            }\n        )*\n    };\n}\n\ndefine_app_errors!(\n    Custom(String);\n    Io(std::io::Error);\n    SerdeJson(serde_json::Error);\n    SerdeYaml(serde_yaml::Error);\n    Base64Decode(base64::DecodeError);\n    Grass(Box<grass::Error>);\n);\n\nimpl From<&str> for SeelenLibError {\n    fn from(err: &str) -> Self {\n        err.to_owned().into()\n    }\n}\n\nimpl std::fmt::Display for SeelenLibError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{self:?}\")\n    }\n}\n\npub type Result<T, E = SeelenLibError> = std::result::Result<T, E>;\n"
  },
  {
    "path": "libs/core/src/handlers/commands.rs",
    "content": "#[cfg(test)]\nuse crate::{\n    rect::Rect, resource::*, state::by_monitor::MonitorConfiguration,\n    state::by_wallpaper::WallpaperInstanceSettings, state::context_menu::*, state::*,\n    system_state::*,\n};\n#[cfg(test)]\nuse std::{collections::HashMap, path::PathBuf};\n\nmacro_rules! slu_commands_declaration {\n    ($($key:ident = $fn_name:ident($($args:tt)*) $(-> $return_type:ty)?,)*) => {\n        #[cfg(test)]\n        pub struct SeelenCommand;\n\n        #[cfg(test)]\n        impl SeelenCommand {\n            #[cfg(feature = \"gen-binds\")]\n            pub(crate) fn generate_ts_file(path: &str) {\n                let mut content: Vec<String> = std::vec::Vec::new();\n\n                content.push(\"// This file was generated via rust macros. Don't modify manually.\".to_owned());\n                content.push(\"export enum SeelenCommand {\".to_owned());\n                $(\n                    content.push(format!(\"  {} = '{}',\", stringify!($key), stringify!($fn_name)));\n                )*\n                content.push(\"}\\n\".to_owned());\n\n                std::fs::write(path, content.join(\"\\n\")).unwrap();\n            }\n        }\n\n        paste::paste! {\n            $(\n                $crate::__switch! {\n                    if { $($args)* }\n                    do {\n                        #[cfg(test)]\n                        #[derive(Deserialize, TS)]\n                        #[serde(rename_all = \"camelCase\")]\n                        #[allow(dead_code)]\n                        struct [<SeelenCommand $key Args>] {\n                            $($args)*\n                        }\n                    }\n                    else {}\n                }\n            )*\n\n            /// Internal used as mapping of commands to their arguments\n            #[cfg(test)]\n            #[allow(non_camel_case_types, dead_code)]\n            #[derive(Deserialize, TS)]\n            #[cfg_attr(feature = \"gen-binds\", ts(export))]\n            enum SeelenCommandArgument {\n                $(\n                    #[allow(non_snake_case)]\n                    $fn_name(Box<$crate::__switch! {\n                        if { $($args)* }\n                        do { [<SeelenCommand $key Args>] }\n                        else { () }\n                    }>),\n                )*\n            }\n        }\n\n        /// Internal used as mapping of commands to their return types\n        #[derive(Serialize, TS)]\n        #[cfg_attr(feature = \"gen-binds\", ts(export))]\n        #[allow(non_camel_case_types, dead_code)]\n        #[cfg(test)]\n        enum SeelenCommandReturn {\n            $(\n                $fn_name(Box<$crate::__switch! {\n                    if { $($return_type)? }\n                    do { $($return_type)? }\n                    else { () }\n                }>),\n            )*\n        }\n\n        #[macro_export]\n        macro_rules! command_handler_list {\n            () => {\n                tauri::generate_handler![\n                    $(\n                        $fn_name,\n                    )*\n                ]\n            };\n        }\n\n        pub use command_handler_list;\n    };\n}\n\nslu_commands_declaration! {\n    // virtual desktops\n    StateGetVirtualDesktops = get_virtual_desktops() -> VirtualDesktops,\n    SwitchWorkspace = switch_workspace(workspace_id: WorkspaceId),\n    CreateWorkspace = create_workspace(monitor_id: MonitorId) -> WorkspaceId,\n    DestroyWorkspace = destroy_workspace(workspace_id: WorkspaceId),\n    RenameWorkspace = rename_workspace(workspace_id: WorkspaceId, name: Option<String>),\n\n    // wallpaper\n    WallpaperNext = wallpaper_next(),\n    WallpaperPrev = wallpaper_prev(),\n    WallpaperSaveThumbnail = wallpaper_save_thumbnail(wallpaper_id: ResourceId, thumbnail_bytes: Vec<u8>),\n\n    // Logging\n    LogFromWebview = log_from_webview(level: u8, message: String, location: String),\n\n    // General\n    OpenFile = open_file(path: PathBuf),\n    SelectFileOnExplorer = select_file_on_explorer(path: PathBuf),\n    Run = run(program: PathBuf, args: Option<RelaunchArguments>, working_dir: Option<PathBuf>, elevated: bool),\n    SimulatePerm = simulate_perm(widget_id: String, perm: String),\n\n    IsDevMode = is_dev_mode() -> bool,\n    IsAppxPackage = is_appx_package() -> bool,\n    HasFixedRuntime = has_fixed_runtime() -> bool,\n\n    GetFocusedApp = get_focused_app() -> FocusedApp,\n    GetMousePosition = get_mouse_position() -> [i32; 2],\n    GetKeyState = get_key_state(key: String) -> bool,\n\n    GetUserEnvs = get_user_envs() -> HashMap<String, String>,\n    ShowStartMenu = show_start_menu(),\n    GetIcon = get_icon(\n        #[ts(optional = nullable)]\n        path: Option<PathBuf>,\n        #[ts(optional = nullable)]\n        umid: Option<String>\n    ),\n    ShowDesktop = show_desktop(),\n\n    RequestToUserInputShortcut = request_to_user_input_shortcut(callback_event: String),\n\n    CheckForUpdates = check_for_updates() -> bool,\n    // Restart the app after install the update so it returns a promise resolved with `never`\n    InstallLastAvailableUpdate = install_last_available_update(),\n\n    // System\n    SystemGetForegroundWindowColor = get_foreground_window_color() -> Color,\n    SystemGetMonitors = get_connected_monitors() -> Vec<PhysicalMonitor>,\n    SystemGetColors = get_system_colors() -> UIColors,\n    SystemGetLanguages = get_system_languages() -> Vec<SystemLanguage>,\n    SystemSetKeyboardLayout = set_system_keyboard_layout(id: String, handle: String),\n\n    // Seelen Settings\n    StateGetDefaultSettings = state_get_default_settings() -> Settings,\n    StateGetDefaultMonitorSettings = state_get_default_monitor_settings() -> MonitorConfiguration,\n    StateGetDefaultWallpaperSettings = state_get_default_wallpaper_settings() -> WallpaperInstanceSettings,\n\n    SetAutoStart = set_auto_start(enabled: bool),\n    GetAutoStartStatus = get_auto_start_status() -> bool,\n    RemoveResource = remove_resource(id: ResourceId, kind: ResourceKind),\n\n    StateGetThemes = state_get_themes() -> Vec<Theme>,\n    StateGetWegItems = state_get_weg_items() -> WegItems,\n    StateWriteWegItems = state_write_weg_items(items: WegItems),\n    StateGetToolbarItems = state_get_toolbar_items() -> ToolbarState,\n    StateWriteToolbarItems = state_write_toolbar_items(items: ToolbarState),\n    StateGetSettings = state_get_settings(path: Option<PathBuf>) -> Settings,\n    StateWriteSettings = state_write_settings(settings: Settings),\n    StateGetSettingsByApp = state_get_settings_by_app() -> Vec<AppConfig> ,\n    StateGetPlugins = state_get_plugins() -> Vec<Plugin>,\n    StateGetWidgets = state_get_widgets() -> Vec<Widget>,\n    StateGetIconPacks = state_get_icon_packs() -> Vec<IconPack>,\n    StateGetWallpapers = state_get_wallpapers() -> Vec<Wallpaper>,\n    StateSetCustomIconPack = state_add_icon_to_custom_icon_pack(icon: IconPackEntry),\n    StateDeleteCachedIcons = state_delete_cached_icons(),\n    StateRequestWallpaperAddition = state_request_wallpaper_addition(),\n    StateGetPerformanceMode = state_get_performance_mode() -> PerformanceMode,\n\n    // Widgets\n    TriggerWidget = trigger_widget(payload: WidgetTriggerPayload),\n    TriggerContextMenu = trigger_context_menu(menu: ContextMenu, forward_to: Option<String>),\n    SetCurrentWidgetStatus = set_current_widget_status(status: WidgetStatus),\n    GetSelfWindowId = get_self_window_handle() -> isize,\n    SetSelfPosition = set_self_position(rect: Rect),\n    BringSelfToTop = bring_self_to_top(),\n    WriteFile = write_data_file(filename: String, content: String),\n    ReadFile = read_data_file(filename: String) -> String,\n\n    // Shell\n    GetNativeShellWallpaper = get_native_shell_wallpaper() -> PathBuf,\n    SetNativeShellWallpaper = set_native_shell_wallpaper(path: PathBuf),\n\n    // User\n    GetUser = get_user() -> User,\n    GetUserFolderContent = get_user_folder_content(folder_type: FolderType) -> Vec<std::path::PathBuf>,\n    GetUserAppWindows = get_user_app_windows() -> Vec<UserAppWindow>,\n    GetUserAppWindowsPreviews = get_user_app_windows_previews() -> HashMap<isize, UserAppWindowPreview>,\n\n    // Media\n    GetMediaDevices = get_media_devices() -> [Vec<MediaDevice>; 2],\n    GetMediaSessions = get_media_sessions() -> Vec<MediaPlayer>,\n    MediaPrev = media_prev(id: String),\n    MediaTogglePlayPause = media_toggle_play_pause(id: String),\n    MediaNext = media_next(id: String),\n    SetVolumeLevel = set_volume_level(device_id: String, session_id: Option<String>, level: f32),\n    MediaToggleMute = media_toggle_mute(device_id: String, session_id: Option<String>),\n    MediaSetDefaultDevice = media_set_default_device(id: String, role: String),\n\n    // Brightness - Multi-monitor support\n    GetAllMonitorsBrightness = get_all_monitors_brightness() -> Vec<MonitorBrightness>,\n    SetMonitorBrightness = set_monitor_brightness(instance_name: String, level: u8),\n\n    // Power\n    GetPowerStatus = get_power_status() -> PowerStatus,\n    GetPowerMode = get_power_mode() -> PowerMode,\n    GetBatteries = get_batteries() -> Vec<Battery>,\n    LogOut = log_out(),\n    Suspend = suspend(),\n    Hibernate = hibernate(),\n    Restart = restart(),\n    Shutdown = shutdown(),\n    Lock = lock(),\n\n    // SeelenWeg\n    WegCloseApp = weg_close_app(hwnd: isize),\n    WegKillApp = weg_kill_app(hwnd: isize),\n    WegToggleWindowState = weg_toggle_window_state(hwnd: isize, was_focused: bool),\n    WegPinItem = weg_pin_item(path: PathBuf),\n\n    // Windows Manager\n    WmGetRenderTree = wm_get_render_tree() -> WmRenderTree,\n    SetAppWindowsPositions = set_app_windows_positions(positions: HashMap<isize, Rect>),\n    RequestFocus = request_focus(hwnd: isize),\n\n    // Slu Popups\n    CreatePopup = create_popup(config: SluPopupConfig) -> uuid::Uuid,\n    UpdatePopup = update_popup(instance_id: uuid::Uuid, config: SluPopupConfig),\n    ClosePopup = close_popup(instance_id: uuid::Uuid),\n    GetPopupConfig = get_popup_config(instance_id: uuid::Uuid) -> SluPopupConfig,\n\n    // Network\n    WlanGetProfiles = wlan_get_profiles() -> Vec<WlanProfile>,\n    WlanStartScanning = wlan_start_scanning(),\n    WlanStopScanning = wlan_stop_scanning(),\n    WlanConnect = wlan_connect(ssid: String, password: Option<String>, hidden: bool) -> bool,\n    WlanDisconnect = wlan_disconnect(),\n    GetNetworkDefaultLocalIp = get_network_default_local_ip() -> String,\n    GetNetworkAdapters = get_network_adapters() -> Vec<NetworkAdapter>,\n    GetNetworkInternetConnection = get_network_internet_connection() -> bool,\n\n    // system tray\n    GetSystemTrayIcons = get_system_tray_icons() -> Vec<SysTrayIcon>,\n    SendSystemTrayIconAction = send_system_tray_icon_action(id: SysTrayIconId, action: SystrayIconAction),\n\n    // Notifications\n    GetNotifications = get_notifications() -> Vec<AppNotification>,\n    NotificationsClose = notifications_close(id: u32),\n    NotificationsCloseAll = notifications_close_all(),\n    ActivateNotification = activate_notification(\n        umid: String,\n        args: String,\n        input_data: HashMap<String, String>,\n    ),\n\n    // Radios\n    GetRadios = get_radios() -> Vec<RadioDevice>,\n    SetRadioState = set_radios_state(kind: RadioDeviceKind, enabled: bool),\n\n    // System Info\n    GetSystemDisks = get_system_disks() -> Vec<Disk>,\n    GetSystemNetwork = get_system_network() -> Vec<NetworkStatistics>,\n    GetSystemMemory = get_system_memory() -> Memory,\n    GetSystemCores = get_system_cores() -> Vec<Core>,\n\n    // Bluetooth\n    GetBluetoothDevices = get_bluetooth_devices() -> Vec<BluetoothDevice>,\n    StartBluetoothScanning = start_bluetooth_scanning(),\n    StopBluetoothScanning = stop_bluetooth_scanning(),\n    RequestPairBluetoothDevice = request_pair_bluetooth_device(id: String) -> DevicePairingNeededAction,\n    ConfirmBluetoothDevicePairing = confirm_bluetooth_device_pairing(id: String, answer: DevicePairingAnswer),\n    DisconnectBluetoothDevice = disconnect_bluetooth_device(id: String),\n    ForgetBluetoothDevice = forget_bluetooth_device(id: String),\n\n    // Start Menu\n    GetStartMenuItems = get_start_menu_items() -> Vec<StartMenuItem>,\n    GetNativeStartMenu = get_native_start_menu() -> StartMenuLayout,\n\n    // Trash Bin\n    GetTrashBinInfo = get_trash_bin_info() -> TrashBinInfo,\n    TrashBinEmpty = trash_bin_empty(),\n}\n"
  },
  {
    "path": "libs/core/src/handlers/commands.ts",
    "content": "// This file was generated via rust macros. Don't modify manually.\nexport enum SeelenCommand {\n  StateGetVirtualDesktops = \"get_virtual_desktops\",\n  SwitchWorkspace = \"switch_workspace\",\n  CreateWorkspace = \"create_workspace\",\n  DestroyWorkspace = \"destroy_workspace\",\n  RenameWorkspace = \"rename_workspace\",\n  WallpaperNext = \"wallpaper_next\",\n  WallpaperPrev = \"wallpaper_prev\",\n  WallpaperSaveThumbnail = \"wallpaper_save_thumbnail\",\n  LogFromWebview = \"log_from_webview\",\n  OpenFile = \"open_file\",\n  SelectFileOnExplorer = \"select_file_on_explorer\",\n  Run = \"run\",\n  SimulatePerm = \"simulate_perm\",\n  IsDevMode = \"is_dev_mode\",\n  IsAppxPackage = \"is_appx_package\",\n  HasFixedRuntime = \"has_fixed_runtime\",\n  GetFocusedApp = \"get_focused_app\",\n  GetMousePosition = \"get_mouse_position\",\n  GetKeyState = \"get_key_state\",\n  GetUserEnvs = \"get_user_envs\",\n  ShowStartMenu = \"show_start_menu\",\n  GetIcon = \"get_icon\",\n  ShowDesktop = \"show_desktop\",\n  RequestToUserInputShortcut = \"request_to_user_input_shortcut\",\n  CheckForUpdates = \"check_for_updates\",\n  InstallLastAvailableUpdate = \"install_last_available_update\",\n  SystemGetForegroundWindowColor = \"get_foreground_window_color\",\n  SystemGetMonitors = \"get_connected_monitors\",\n  SystemGetColors = \"get_system_colors\",\n  SystemGetLanguages = \"get_system_languages\",\n  SystemSetKeyboardLayout = \"set_system_keyboard_layout\",\n  StateGetDefaultSettings = \"state_get_default_settings\",\n  StateGetDefaultMonitorSettings = \"state_get_default_monitor_settings\",\n  StateGetDefaultWallpaperSettings = \"state_get_default_wallpaper_settings\",\n  SetAutoStart = \"set_auto_start\",\n  GetAutoStartStatus = \"get_auto_start_status\",\n  RemoveResource = \"remove_resource\",\n  StateGetThemes = \"state_get_themes\",\n  StateGetWegItems = \"state_get_weg_items\",\n  StateWriteWegItems = \"state_write_weg_items\",\n  StateGetToolbarItems = \"state_get_toolbar_items\",\n  StateWriteToolbarItems = \"state_write_toolbar_items\",\n  StateGetSettings = \"state_get_settings\",\n  StateWriteSettings = \"state_write_settings\",\n  StateGetSettingsByApp = \"state_get_settings_by_app\",\n  StateGetPlugins = \"state_get_plugins\",\n  StateGetWidgets = \"state_get_widgets\",\n  StateGetIconPacks = \"state_get_icon_packs\",\n  StateGetWallpapers = \"state_get_wallpapers\",\n  StateSetCustomIconPack = \"state_add_icon_to_custom_icon_pack\",\n  StateDeleteCachedIcons = \"state_delete_cached_icons\",\n  StateRequestWallpaperAddition = \"state_request_wallpaper_addition\",\n  StateGetPerformanceMode = \"state_get_performance_mode\",\n  TriggerWidget = \"trigger_widget\",\n  TriggerContextMenu = \"trigger_context_menu\",\n  SetCurrentWidgetStatus = \"set_current_widget_status\",\n  GetSelfWindowId = \"get_self_window_handle\",\n  SetSelfPosition = \"set_self_position\",\n  BringSelfToTop = \"bring_self_to_top\",\n  WriteFile = \"write_data_file\",\n  ReadFile = \"read_data_file\",\n  GetNativeShellWallpaper = \"get_native_shell_wallpaper\",\n  SetNativeShellWallpaper = \"set_native_shell_wallpaper\",\n  GetUser = \"get_user\",\n  GetUserFolderContent = \"get_user_folder_content\",\n  GetUserAppWindows = \"get_user_app_windows\",\n  GetUserAppWindowsPreviews = \"get_user_app_windows_previews\",\n  GetMediaDevices = \"get_media_devices\",\n  GetMediaSessions = \"get_media_sessions\",\n  MediaPrev = \"media_prev\",\n  MediaTogglePlayPause = \"media_toggle_play_pause\",\n  MediaNext = \"media_next\",\n  SetVolumeLevel = \"set_volume_level\",\n  MediaToggleMute = \"media_toggle_mute\",\n  MediaSetDefaultDevice = \"media_set_default_device\",\n  GetAllMonitorsBrightness = \"get_all_monitors_brightness\",\n  SetMonitorBrightness = \"set_monitor_brightness\",\n  GetPowerStatus = \"get_power_status\",\n  GetPowerMode = \"get_power_mode\",\n  GetBatteries = \"get_batteries\",\n  LogOut = \"log_out\",\n  Suspend = \"suspend\",\n  Hibernate = \"hibernate\",\n  Restart = \"restart\",\n  Shutdown = \"shutdown\",\n  Lock = \"lock\",\n  WegCloseApp = \"weg_close_app\",\n  WegKillApp = \"weg_kill_app\",\n  WegToggleWindowState = \"weg_toggle_window_state\",\n  WegPinItem = \"weg_pin_item\",\n  WmGetRenderTree = \"wm_get_render_tree\",\n  SetAppWindowsPositions = \"set_app_windows_positions\",\n  RequestFocus = \"request_focus\",\n  CreatePopup = \"create_popup\",\n  UpdatePopup = \"update_popup\",\n  ClosePopup = \"close_popup\",\n  GetPopupConfig = \"get_popup_config\",\n  WlanGetProfiles = \"wlan_get_profiles\",\n  WlanStartScanning = \"wlan_start_scanning\",\n  WlanStopScanning = \"wlan_stop_scanning\",\n  WlanConnect = \"wlan_connect\",\n  WlanDisconnect = \"wlan_disconnect\",\n  GetNetworkDefaultLocalIp = \"get_network_default_local_ip\",\n  GetNetworkAdapters = \"get_network_adapters\",\n  GetNetworkInternetConnection = \"get_network_internet_connection\",\n  GetSystemTrayIcons = \"get_system_tray_icons\",\n  SendSystemTrayIconAction = \"send_system_tray_icon_action\",\n  GetNotifications = \"get_notifications\",\n  NotificationsClose = \"notifications_close\",\n  NotificationsCloseAll = \"notifications_close_all\",\n  ActivateNotification = \"activate_notification\",\n  GetRadios = \"get_radios\",\n  SetRadioState = \"set_radios_state\",\n  GetSystemDisks = \"get_system_disks\",\n  GetSystemNetwork = \"get_system_network\",\n  GetSystemMemory = \"get_system_memory\",\n  GetSystemCores = \"get_system_cores\",\n  GetBluetoothDevices = \"get_bluetooth_devices\",\n  StartBluetoothScanning = \"start_bluetooth_scanning\",\n  StopBluetoothScanning = \"stop_bluetooth_scanning\",\n  RequestPairBluetoothDevice = \"request_pair_bluetooth_device\",\n  ConfirmBluetoothDevicePairing = \"confirm_bluetooth_device_pairing\",\n  DisconnectBluetoothDevice = \"disconnect_bluetooth_device\",\n  ForgetBluetoothDevice = \"forget_bluetooth_device\",\n  GetStartMenuItems = \"get_start_menu_items\",\n  GetNativeStartMenu = \"get_native_start_menu\",\n  GetTrashBinInfo = \"get_trash_bin_info\",\n  TrashBinEmpty = \"trash_bin_empty\",\n}\n"
  },
  {
    "path": "libs/core/src/handlers/events.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::state::*;\nuse crate::system_state::*;\n\nmacro_rules! slu_events_declaration {\n    ($($name:ident$(($payload:ty))? as $value:literal,)*) => {\n        pub struct SeelenEvent;\n\n        #[allow(non_upper_case_globals)]\n        impl SeelenEvent {\n            $(\n                pub const $name: &'static str = $value;\n            )*\n\n            #[allow(dead_code)]\n            pub(crate) fn generate_ts_file(path: &str) {\n                let content: Vec<String> = vec![\n                    \"// This file was generated via rust macros. Don't modify manually.\".to_owned(),\n                    \"export enum SeelenEvent {\".to_owned(),\n                    $(\n                        format!(\"  {} = '{}',\", stringify!($name), Self::$name),\n                    )*\n                    \"}\\n\".to_owned(),\n                ];\n                std::fs::write(path, content.join(\"\\n\")).unwrap();\n            }\n        }\n\n        #[derive(Serialize, TS)]\n        #[cfg_attr(feature = \"gen-binds\", ts(export))]\n        pub enum SeelenEventPayload {\n            $(\n                #[serde(rename = $value)]\n                $name($crate::__switch! {\n                    if { $($payload)? }\n                    do { Box<$($payload)?> }\n                    else { () }\n                }),\n            )*\n        }\n    };\n}\n\nslu_events_declaration! {\n    VirtualDesktopsChanged(VirtualDesktops) as \"virtual-desktops::changed\",\n\n    GlobalFocusChanged(FocusedApp) as \"global-focus-changed\",\n    GlobalMouseMove([i32; 2]) as \"global-mouse-move\",\n\n    HandleLayeredHitboxes(bool) as \"handle-layered\",\n\n    SystemMonitorsChanged(Vec<PhysicalMonitor>) as \"system::monitors-changed\",\n    SystemLanguagesChanged(Vec<SystemLanguage>) as \"system::languages-changed\",\n    SystemMonitorsBrightnessChanged(Vec<MonitorBrightness>) as \"system::monitors-brightness-changed\",\n\n    UserChanged(User) as \"user-changed\",\n    UserFolderChanged(FolderChangedArgs) as \"user::known-folder-changed\",\n    UserAppWindowsChanged(Vec<UserAppWindow>) as \"user::windows-changed\",\n    UserAppWindowsPreviewsChanged(HashMap<isize, UserAppWindowPreview>) as \"user::windows-previews-changed\",\n\n    MediaSessions(Vec<MediaPlayer>) as \"media-sessions\",\n    MediaDevices([Vec<MediaDevice>; 2]) as \"media::devices\",\n    MediaInputs(Vec<MediaDevice>) as \"media-inputs\",\n    MediaOutputs(Vec<MediaDevice>) as \"media-outputs\",\n\n    NetworkDefaultLocalIp(String) as \"network-default-local-ip\",\n    NetworkAdapters(Vec<NetworkAdapter>) as \"network-adapters\",\n    NetworkInternetConnection(bool) as \"network-internet-connection\",\n    NetworkWlanScanned(Vec<WlanBssEntry>) as \"wlan-scanned\",\n\n    Notifications(Vec<AppNotification>) as \"notifications\",\n\n    PowerStatus(PowerStatus) as \"power-status\",\n    PowerMode(PowerMode) as \"power-mode\",\n    BatteriesStatus(Vec<Battery>) as \"batteries-status\",\n\n    ColorsChanged(UIColors) as \"colors-changed\",\n\n    WMSetReservation as \"wm::set-reservation\",\n    WMForceRetiling as \"wm::force-retiling\",\n    WMTreeChanged(WmRenderTree) as \"wm::tree-changed\",\n\n    PopupContentChanged(SluPopupConfig) as \"popup-content-changed\",\n\n    StateSettingsChanged(Settings) as \"settings-changed\",\n    StateSettingsByAppChanged(Vec<AppConfig>) as \"settings-by-app\",\n    StateThemesChanged(Vec<Theme>) as \"themes\",\n    StateIconPacksChanged(Vec<IconPack>) as \"icon-packs\",\n    StatePluginsChanged(Vec<Plugin>) as \"plugins-changed\",\n    StateWidgetsChanged(Vec<Widget>) as \"widgets-changed\",\n    StateWallpapersChanged(Vec<Wallpaper>) as \"UserResources::wallpapers-changed\",\n\n    // system tray\n    SystemTrayChanged(Vec<SysTrayIcon>) as \"system-tray::changed\",\n\n    StatePerformanceModeChanged(PerformanceMode) as \"state::performance-mode-changed\",\n\n    WidgetTriggered(WidgetTriggerPayload) as \"widget::triggered\",\n\n    // Radios\n    RadiosChanged(Vec<RadioDevice>) as \"radio::changed\",\n\n    // System Info\n    SystemDisksChanged(Vec<Disk>) as \"system::disks-changed\",\n    SystemNetworkChanged(Vec<NetworkStatistics>) as \"system::network-changed\",\n    SystemMemoryChanged(Memory) as \"system::memory-changed\",\n    SystemCoresChanged(Vec<Core>) as \"system::cores-changed\",\n\n    BluetoothDevicesChanged(Vec<BluetoothDevice>) as \"bluetooth-devices-changed\",\n\n    // Start Menu\n    StartMenuItemsChanged(Vec<StartMenuItem>) as \"start-menu::items-changed\",\n\n    // SeelenWeg\n    WegAddItem(WegItemData) as \"weg::add-item\",\n\n    // Trash Bin\n    TrashBinChanged(TrashBinInfo) as \"trash-bin::changed\",\n}\n"
  },
  {
    "path": "libs/core/src/handlers/events.ts",
    "content": "// This file was generated via rust macros. Don't modify manually.\nexport enum SeelenEvent {\n  VirtualDesktopsChanged = \"virtual-desktops::changed\",\n  GlobalFocusChanged = \"global-focus-changed\",\n  GlobalMouseMove = \"global-mouse-move\",\n  HandleLayeredHitboxes = \"handle-layered\",\n  SystemMonitorsChanged = \"system::monitors-changed\",\n  SystemLanguagesChanged = \"system::languages-changed\",\n  SystemMonitorsBrightnessChanged = \"system::monitors-brightness-changed\",\n  UserChanged = \"user-changed\",\n  UserFolderChanged = \"user::known-folder-changed\",\n  UserAppWindowsChanged = \"user::windows-changed\",\n  UserAppWindowsPreviewsChanged = \"user::windows-previews-changed\",\n  MediaSessions = \"media-sessions\",\n  MediaDevices = \"media::devices\",\n  MediaInputs = \"media-inputs\",\n  MediaOutputs = \"media-outputs\",\n  NetworkDefaultLocalIp = \"network-default-local-ip\",\n  NetworkAdapters = \"network-adapters\",\n  NetworkInternetConnection = \"network-internet-connection\",\n  NetworkWlanScanned = \"wlan-scanned\",\n  Notifications = \"notifications\",\n  PowerStatus = \"power-status\",\n  PowerMode = \"power-mode\",\n  BatteriesStatus = \"batteries-status\",\n  ColorsChanged = \"colors-changed\",\n  WMSetReservation = \"wm::set-reservation\",\n  WMForceRetiling = \"wm::force-retiling\",\n  WMTreeChanged = \"wm::tree-changed\",\n  PopupContentChanged = \"popup-content-changed\",\n  StateSettingsChanged = \"settings-changed\",\n  StateSettingsByAppChanged = \"settings-by-app\",\n  StateThemesChanged = \"themes\",\n  StateIconPacksChanged = \"icon-packs\",\n  StatePluginsChanged = \"plugins-changed\",\n  StateWidgetsChanged = \"widgets-changed\",\n  StateWallpapersChanged = \"UserResources::wallpapers-changed\",\n  SystemTrayChanged = \"system-tray::changed\",\n  StatePerformanceModeChanged = \"state::performance-mode-changed\",\n  WidgetTriggered = \"widget::triggered\",\n  RadiosChanged = \"radio::changed\",\n  SystemDisksChanged = \"system::disks-changed\",\n  SystemNetworkChanged = \"system::network-changed\",\n  SystemMemoryChanged = \"system::memory-changed\",\n  SystemCoresChanged = \"system::cores-changed\",\n  BluetoothDevicesChanged = \"bluetooth-devices-changed\",\n  StartMenuItemsChanged = \"start-menu::items-changed\",\n  WegAddItem = \"weg::add-item\",\n  TrashBinChanged = \"trash-bin::changed\",\n}\n"
  },
  {
    "path": "libs/core/src/handlers/mod.rs",
    "content": "mod commands;\nmod events;\n\npub use commands::*;\npub use events::*;\n"
  },
  {
    "path": "libs/core/src/handlers/mod.ts",
    "content": "import type { SeelenCommandArgument, SeelenCommandReturn, SeelenEventPayload } from \"@seelen-ui/types\";\nimport { invoke as tauriInvoke, type InvokeOptions } from \"@tauri-apps/api/core\";\nimport { type EventCallback, listen, type Options as ListenerOptions } from \"@tauri-apps/api/event\";\n\nimport type { SeelenCommand } from \"./commands.ts\";\nimport type { SeelenEvent } from \"./events.ts\";\n\ntype $keyof<Type> = [Type] extends [never] ? keyof Type\n  : Type extends Type ? keyof Type\n  : never;\n\ntype UnionToIntersection<Type> = {\n  [Key in $keyof<Type>]: Extract<\n    Type,\n    {\n      [key in Key]?: unknown;\n    }\n  >[Key];\n};\n\ntype MapNullToVoid<Obj> = {\n  [K in keyof Obj]: [Obj[K]] extends [null] ? void : Obj[K];\n};\n\ntype MapNullToUndefined<Obj> = {\n  [K in keyof Obj]: [Obj[K]] extends [null] ? undefined : Obj[K];\n};\n\nexport type AllSeelenCommandArguments = MapNullToUndefined<\n  UnionToIntersection<SeelenCommandArgument>\n>;\nexport type AllSeelenCommandReturns = MapNullToVoid<\n  UnionToIntersection<SeelenCommandReturn>\n>;\n\nexport type AllSeelenEventPayloads = UnionToIntersection<SeelenEventPayload>;\n\n/**\n * Will call to the background process\n * @args Command to be called\n * @args Command arguments\n * @return Result of the command\n */\nexport function invoke<T extends SeelenCommand>(\n  ...args: [AllSeelenCommandArguments[T]] extends [undefined] ? [\n      command: T,\n      args?: undefined,\n      options?: InvokeOptions,\n    ]\n    : [\n      command: T,\n      args: AllSeelenCommandArguments[T],\n      options?: InvokeOptions,\n    ]\n): Promise<AllSeelenCommandReturns[T]> {\n  const [command, commandArgs, options] = args;\n  return tauriInvoke(command, commandArgs, options);\n}\n\nexport type UnSubscriber = () => void;\n\nexport function subscribe<T extends SeelenEvent>(\n  event: T,\n  cb: EventCallback<AllSeelenEventPayloads[T]>,\n  options?: ListenerOptions,\n): Promise<UnSubscriber> {\n  return listen(event, cb, options);\n}\n\nexport * from \"./events.ts\";\nexport * from \"./commands.ts\";\n"
  },
  {
    "path": "libs/core/src/lib.rs",
    "content": "pub mod constants;\npub mod error;\npub mod handlers;\npub mod rect;\npub mod resource;\npub mod state;\npub mod system_state;\npub mod utils;\n\npub use chrono;\npub use error::SeelenLibError;\npub use rect::*;\n\n#[macro_use(Serialize, Deserialize)]\nextern crate serde;\n\n#[macro_use(JsonSchema)]\nextern crate schemars;\n\n#[macro_use(TS)]\nextern crate ts_rs;\n\n#[macro_use(FromPrimitive, IntoPrimitive)]\nextern crate num_enum;\n\n#[cfg(feature = \"gen-binds\")]\n#[test]\nfn generate_schemas() {\n    use state::{AppConfig, IconPack, Plugin, Settings, Theme, Widget};\n\n    fn write_schema<T>(path: &str)\n    where\n        T: schemars::JsonSchema,\n    {\n        let schema = schemars::schema_for!(T);\n        std::fs::write(path, serde_json::to_string_pretty(&schema).unwrap()).unwrap();\n    }\n\n    std::fs::create_dir_all(\"./gen/schemas\").unwrap();\n    write_schema::<Settings>(\"./gen/schemas/settings.schema.json\");\n    write_schema::<Vec<AppConfig>>(\"./gen/schemas/settings_by_app.schema.json\");\n\n    write_schema::<Theme>(\"./gen/schemas/theme.schema.json\");\n    write_schema::<Plugin>(\"./gen/schemas/plugin.schema.json\");\n    write_schema::<Widget>(\"./gen/schemas/widget.schema.json\");\n    write_schema::<IconPack>(\"./gen/schemas/icon_pack.schema.json\");\n\n    handlers::SeelenEvent::generate_ts_file(\"./src/handlers/events.ts\");\n    handlers::SeelenCommand::generate_ts_file(\"./src/handlers/commands.ts\");\n}\n"
  },
  {
    "path": "libs/core/src/lib.test.ts",
    "content": "import { assert } from \"@std/assert/assert\";\nimport * as SluLib from \"./lib.ts\";\n\nDeno.test(\"Library is importable on background (no window)\", () => {\n  assert(!!SluLib);\n});\n"
  },
  {
    "path": "libs/core/src/lib.ts",
    "content": "export * from \"./constants/mod.ts\";\nexport * from \"./state/mod.ts\";\nexport * from \"./system_state/mod.ts\";\nexport * from \"./utils/mod.ts\";\nexport * from \"./resource/mod.ts\";\nexport { type AllSeelenCommandReturns, invoke, SeelenCommand, SeelenEvent, subscribe } from \"./handlers/mod.ts\";\n"
  },
  {
    "path": "libs/core/src/re-exports/tauri.ts",
    "content": "export * from \"@tauri-apps/api\";\nexport * from \"@tauri-apps/api/dpi\";\n\nexport * as dialog from \"@tauri-apps/plugin-dialog\";\nexport * as fs from \"@tauri-apps/plugin-fs\";\nexport * as logger from \"@tauri-apps/plugin-log\";\nexport * as process from \"@tauri-apps/plugin-process\";\n"
  },
  {
    "path": "libs/core/src/rect.rs",
    "content": "use schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\n#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Rect {\n    pub left: i32,\n    pub top: i32,\n    /// The right edge (exclusive). Pixels at x >= right are outside the rectangle.\n    pub right: i32,\n    /// The bottom edge (exclusive). Pixels at y >= bottom are outside the rectangle.\n    pub bottom: i32,\n}\n\n#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Frame {\n    pub x: i32,\n    pub y: i32,\n    pub width: u32,\n    pub height: u32,\n}\n\nimpl Rect {\n    pub fn as_frame(&self) -> Frame {\n        Frame {\n            x: self.left,\n            y: self.top,\n            width: (self.right - self.left) as u32,\n            height: (self.bottom - self.top) as u32,\n        }\n    }\n\n    pub fn width(&self) -> i32 {\n        self.right - self.left\n    }\n\n    pub fn height(&self) -> i32 {\n        self.bottom - self.top\n    }\n\n    pub fn center(&self) -> Point {\n        Point::new(self.left + self.width() / 2, self.top + self.height() / 2)\n    }\n\n    pub fn corners(&self) -> [Point; 4] {\n        [\n            Point::new(self.left, self.top),\n            Point::new(self.right, self.top),\n            Point::new(self.right, self.bottom),\n            Point::new(self.left, self.bottom),\n        ]\n    }\n\n    pub fn intersection(&self, other: &Rect) -> Option<Rect> {\n        let left = self.left.max(other.left);\n        let top = self.top.max(other.top);\n        let right = self.right.min(other.right);\n        let bottom = self.bottom.min(other.bottom);\n\n        if left >= right || top >= bottom {\n            return None;\n        }\n\n        Some(Rect {\n            left,\n            top,\n            right,\n            bottom,\n        })\n    }\n\n    pub fn contains(&self, point: &Point) -> bool {\n        point.x >= self.left && point.x < self.right && point.y >= self.top && point.y < self.bottom\n    }\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Point {\n    pub x: i32,\n    pub y: i32,\n}\n\nimpl Point {\n    pub fn new(x: i32, y: i32) -> Self {\n        Self { x, y }\n    }\n\n    pub fn distance_squared(&self, other: &Point) -> i32 {\n        let dx = self.x.saturating_sub(other.x);\n        let dy = self.y.saturating_sub(other.y);\n        dx.saturating_pow(2).saturating_add(dy.saturating_pow(2))\n    }\n\n    pub fn distance(&self, other: &Point) -> f64 {\n        (self.distance_squared(other) as f64).sqrt()\n    }\n}\n"
  },
  {
    "path": "libs/core/src/resource/file.rs",
    "content": "use std::{\n    fs::File,\n    io::{Read, Seek, SeekFrom, Write},\n    path::Path,\n};\n\nuse base64::Engine;\nuse serde::{de::DeserializeOwned, Deserialize, Serialize};\nuse ts_rs::TS;\n\nuse crate::{error::Result, utils::TsUnknown};\n\nuse super::Resource;\n\n/// A container for Seelen UI resources.\n///\n/// This struct contains all the necessary data that a resource needs.\n/// It uses a custom `.slu` file extension format that can change over time\n/// with new versions.\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct SluResourceFile {\n    pub version: u32,\n    /// information about the downloaded resource\n    pub resource: Resource,\n    /// real resource data to be deserialized on load\n    pub data: TsUnknown,\n}\n\nimpl SluResourceFile {\n    pub fn decode<R: Read + Seek>(mut reader: R) -> Result<Self> {\n        let mut version = [0u8; 1];\n        reader.read_exact(&mut version)?;\n\n        match version[0] {\n            1 => {\n                reader.seek(SeekFrom::Current(3))?; // SLU mime type\n            }\n            2 => {\n                reader.seek(SeekFrom::Current(3))?; // SLU mime type\n                reader.seek(SeekFrom::Current(4))?; // 32 bits reserved\n            }\n            // todo for version 3, use zip crate\n            _ => {\n                return Err(\"unsupported slu file version\".into());\n            }\n        }\n\n        // read the rest of the body as content\n        let mut buffer = Vec::new();\n        reader.read_to_end(&mut buffer)?;\n        let decoded = base64::engine::general_purpose::STANDARD.decode(&buffer)?;\n        Ok(serde_yaml::from_slice(&decoded)?)\n    }\n\n    pub fn encode<W: Write>(&self, mut writer: W) -> Result<()> {\n        let data = serde_yaml::to_string(self)?;\n        let encoded = base64::engine::general_purpose::STANDARD.encode(data);\n\n        writer.write_all(&[2])?; // version\n        writer.write_all(\"SLU\".as_bytes())?; // SLU mime type\n        writer.write_all(&[0u8; 4])?; // 32 bits reserved\n        writer.write_all(encoded.as_bytes())?;\n        Ok(())\n    }\n\n    pub fn load(path: &Path) -> Result<Self> {\n        let file = File::open(path)?;\n        let decoded = Self::decode(&file)?;\n        decoded.resource.verify()?;\n        Ok(decoded)\n    }\n\n    pub fn store(&self, path: &Path) -> Result<()> {\n        let mut file = File::create(path)?;\n        self.encode(&mut file)\n    }\n\n    pub fn try_parse_into<T>(&self) -> Result<T>\n    where\n        T: DeserializeOwned,\n    {\n        let mut obj = serde_json::value::Map::new();\n\n        obj.insert(\n            \"id\".to_string(),\n            serde_json::Value::String(self.resource.id.to_string()),\n        );\n\n        obj.insert(\n            \"metadata\".to_string(),\n            serde_json::to_value(&self.resource.metadata)?,\n        );\n\n        let data = self.data.0.as_object().ok_or(\"invalid data\")?;\n        obj.append(&mut data.clone());\n\n        Ok(serde_json::from_value(obj.into())?)\n    }\n}\n"
  },
  {
    "path": "libs/core/src/resource/interface.rs",
    "content": "use std::{fs::File, path::Path};\n\nuse serde::{de::DeserializeOwned, Serialize};\n\nuse crate::{\n    error::Result,\n    resource::{deserialize_extended_yaml, ResourceKind, SluResourceFile},\n    utils::search_resource_entrypoint,\n};\n\nuse super::ResourceMetadata;\n\npub trait SluResource: Sized + Serialize + DeserializeOwned {\n    const KIND: ResourceKind;\n\n    fn metadata(&self) -> &ResourceMetadata;\n    fn metadata_mut(&mut self) -> &mut ResourceMetadata;\n\n    /// Try to load the resource from a file.\\\n    /// This won't run post loading processing, please use `load` instead.\n    fn load_from_file(path: &Path) -> Result<Self> {\n        let ext = path\n            .extension()\n            .ok_or(\"Invalid file extension\")?\n            .to_ascii_lowercase();\n\n        let resource: Self = match ext.to_string_lossy().as_ref() {\n            \"yml\" | \"yaml\" => deserialize_extended_yaml(path)?,\n            \"json\" | \"jsonc\" => {\n                let file = File::open(path)?;\n                file.lock_shared()?;\n                serde_json::from_reader(file)?\n            }\n            \"slu\" => {\n                let file = SluResourceFile::load(path)?;\n                if Self::KIND != file.resource.kind {\n                    return Err(format!(\n                        \"Resource file is not of expected kind: {:?} instead is {:?}\",\n                        Self::KIND,\n                        file.resource.kind\n                    )\n                    .into());\n                }\n\n                let mut parsed: Self = file.try_parse_into()?;\n                parsed.metadata_mut().internal.remote = Some(Box::new(file.resource.clone()));\n                parsed\n            }\n            _ => return Err(\"Invalid file extension\".into()),\n        };\n\n        Ok(resource)\n    }\n\n    /// Try to load the resource from a folder.\\\n    /// This won't run post loading processing, please use `load` instead.\n    fn load_from_folder(path: &Path) -> Result<Self> {\n        let file = search_resource_entrypoint(path).ok_or(\"No metadata file found\")?;\n        Self::load_from_file(&file)\n    }\n\n    /// Try to load the resource from a file or directory.\\\n    /// After deserialization, this will run post loading processing like `sanitize` and `validate`,\n    /// Also will set the internal metadata needed to handle the resource\n    fn load(path: &Path) -> Result<Self> {\n        let mut resource = if path.is_dir() {\n            Self::load_from_folder(path)?\n        } else {\n            Self::load_from_file(path)?\n        };\n\n        let meta = resource.metadata_mut();\n        meta.internal.path = path.to_path_buf();\n        meta.internal.filename = path\n            .file_name()\n            .unwrap_or_default()\n            .to_string_lossy()\n            .to_string();\n        meta.internal.written_at = path.metadata()?.modified()?.into();\n\n        resource.sanitize();\n        resource.validate()?;\n        Ok(resource)\n    }\n\n    /// Sanitize the resource data\n    fn sanitize(&mut self) {}\n\n    /// Validates the resource after sanitization\n    fn validate(&self) -> Result<()> {\n        Ok(())\n    }\n\n    /// Saves the resource in same path as it was loaded\n    fn save(&self) -> Result<()> {\n        let mut save_path = self.metadata().internal.path.to_path_buf();\n        if save_path.is_dir() {\n            std::fs::create_dir_all(&save_path)?;\n            save_path = search_resource_entrypoint(&save_path)\n                .unwrap_or_else(|| save_path.join(\"metadata.yml\"));\n        }\n\n        let extension = save_path\n            .extension()\n            .ok_or(\"Invalid path extension\")?\n            .to_string_lossy()\n            .to_lowercase();\n\n        match extension.as_str() {\n            \"slu\" => {\n                let mut slu_file = SluResourceFile::load(&save_path)?;\n                slu_file.data = serde_json::to_value(self)?.into();\n                slu_file.store(&save_path)?;\n            }\n            \"yml\" | \"yaml\" => {\n                let file = File::create(save_path)?;\n                serde_yaml::to_writer(file, self)?;\n            }\n            \"json\" | \"jsonc\" => {\n                let file = File::create(save_path)?;\n                serde_json::to_writer_pretty(file, self)?;\n            }\n            _ => {\n                return Err(\"Unsupported path extension\".into());\n            }\n        }\n        Ok(())\n    }\n\n    fn delete(&self) -> Result<()> {\n        let path = self.metadata().internal.path.to_path_buf();\n        if path.is_dir() {\n            std::fs::remove_dir_all(path)?;\n        } else {\n            std::fs::remove_file(path)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "libs/core/src/resource/metadata.rs",
    "content": "use std::{collections::HashMap, path::PathBuf};\n\nuse chrono::{DateTime, Utc};\nuse url::Url;\n\nuse crate::{\n    error::Result,\n    resource::{Resource, ResourceText},\n};\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct ResourceMetadata {\n    /// Map of language code to display name. Could be a string, mapped to `en`.\n    pub display_name: ResourceText,\n    /// Map of language code to description. Could be a string, mapped to `en`.\n    pub description: ResourceText,\n    /// Portrait image with aspect ratio of 1/1\n    pub portrait: Option<Url>,\n    /// Banner image with aspect ratio of 21/9, this is used when promoting the resource.\n    pub banner: Option<Url>,\n    /// Screenshots should use aspect ratio of 16/9\n    pub screenshots: Vec<Url>,\n    /// tags are keywords to be used for searching and indexing\n    pub tags: Vec<String>,\n    /// App target version that this resource is compatible with.\\\n    /// Developers are responsible to update the resource so when resource does not\n    /// match the current app version, the resource will be shown with a warning message\n    pub app_target_version: Option<(u32, u32, u32)>,\n    /// Extra metadata for the resource\n    pub extras: HashMap<String, String>,\n    #[serde(flatten, skip_deserializing)]\n    pub internal: InternalResourceMetadata,\n}\n\nimpl ResourceMetadata {\n    /// Returns the directory of where the resource is stored\n    pub fn directory(&self) -> Result<PathBuf> {\n        Ok(if self.internal.path.is_dir() {\n            self.internal.path.clone()\n        } else {\n            self.internal\n                .path\n                .parent()\n                .ok_or(\"No Parent\")?\n                .to_path_buf()\n        })\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct InternalResourceMetadata {\n    pub path: PathBuf,\n    pub filename: String,\n    pub bundled: bool,\n    /// Last date when the metadata file was written\n    pub written_at: DateTime<Utc>,\n    /// only present for remote/downloaded resources\n    pub remote: Option<Box<Resource>>,\n}\n\nimpl Default for ResourceMetadata {\n    fn default() -> Self {\n        Self {\n            display_name: ResourceText::Localized(HashMap::new()),\n            description: ResourceText::Localized(HashMap::new()),\n            portrait: None,\n            banner: None,\n            screenshots: Vec::new(),\n            tags: Vec::new(),\n            extras: HashMap::new(),\n            app_target_version: None,\n            internal: InternalResourceMetadata::default(),\n        }\n    }\n}\n"
  },
  {
    "path": "libs/core/src/resource/mod.rs",
    "content": "mod file;\nmod interface;\nmod metadata;\nmod resource_id;\nmod yaml_ext;\n\npub use file::*;\npub use interface::*;\npub use metadata::*;\npub use resource_id::*;\npub use yaml_ext::*;\n\nuse std::{\n    collections::{HashMap, HashSet},\n    hash::Hash,\n};\n\nuse chrono::{DateTime, Utc};\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\nuse uuid::Uuid;\n\nuse crate::error::Result;\n\n// =============================================================================\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(untagged)]\n/// Map of language code as key an translated values. Could be a string, mapped to `en`.\npub enum ResourceText {\n    En(String),\n    Localized(HashMap<String, String>),\n}\n\nimpl Default for ResourceText {\n    fn default() -> Self {\n        Self::En(String::new())\n    }\n}\n\nimpl ResourceText {\n    const MISSING_TEXT: &'static str = \"!?\";\n\n    /// Returns true if the text exists for the given lang\n    pub fn has(&self, lang: &str) -> bool {\n        match self {\n            ResourceText::En(_) => lang == \"en\",\n            ResourceText::Localized(map) => map.get(lang).is_some_and(|t| !t.is_empty()),\n        }\n    }\n\n    /// Returns the text by lang, uses `en` as fallback.\n    /// If no text fallback found will return `!?`\n    pub fn get(&self, lang: &str) -> &str {\n        match self {\n            ResourceText::En(value) => value,\n            ResourceText::Localized(map) => match map.get(lang) {\n                Some(value) => value,\n                None => match map.get(\"en\") {\n                    Some(value) => value,\n                    None => Self::MISSING_TEXT,\n                },\n            },\n        }\n    }\n\n    pub fn set(&mut self, lang: impl Into<String>, value: impl Into<String>) {\n        if let ResourceText::En(v) = self {\n            let mut dict = HashMap::new();\n            dict.insert(\"en\".to_string(), v.to_string());\n            *self = ResourceText::Localized(dict);\n        }\n\n        if let ResourceText::Localized(dict) = self {\n            dict.insert(lang.into(), value.into());\n        }\n    }\n}\n\n// =============================================================================\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum ResourceKind {\n    Theme,\n    IconPack,\n    Widget,\n    Plugin,\n    Wallpaper,\n    SoundPack,\n}\n\n// =============================================================================\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum ResourceStatus {\n    /// Initial state\n    Draft,\n    /// Waiting for review\n    Reviewing,\n    /// review done and rejected\n    Rejected,\n    /// review done and approved\n    Published,\n    /// soft delete by user\n    Deleted,\n}\n\n// =============================================================================\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum ResourceAttribute {\n    /// this resource is not working\n    NotWorking,\n    /// this resource is recommended by the staff\n    StaffLiked,\n}\n\n// =============================================================================\n\n/// Represents a resource in the cloud, uploaded by a user\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Resource {\n    /// unique id\n    pub id: Uuid,\n    /// id of the document containing the resource\n    pub data_id: Uuid,\n    /// user id who created the resource\n    pub creator_id: Uuid,\n    /// Visual id composed of creator username and resource name.\\\n    /// Warning: as username and resource name could be changed, this id is not stable.\\\n    /// Use it for display purposes only\n    pub friendly_id: ResourceId,\n    pub kind: ResourceKind,\n    pub metadata: ResourceMetadata,\n    pub created_at: DateTime<Utc>,\n    pub updated_at: DateTime<Utc>,\n\n    /// current status\n    pub status: ResourceStatus,\n    /// if status == ResourceStatus::Rejected, this is the reason for rejection\n    pub rejected_reason: Option<String>,\n    /// date when the resource was reviewed\n    pub reviewed_at: Option<DateTime<Utc>>,\n    /// user id who reviewed the resource\n    pub reviewed_by: Option<Uuid>,\n    /// should be filled if `status == ResourceStatus::Deleted`\n    pub deleted_at: Option<DateTime<Utc>>,\n\n    /// resource attributes\n    #[serde(default)]\n    pub attributes: HashSet<ResourceAttribute>,\n    /// resource version (increased every time the resource is updated)\n    pub version: u32,\n    /// number of stars\n    pub stars: u32,\n    /// number of downloads\n    pub downloads: u32,\n}\n\nimpl Resource {\n    pub fn verify(&self) -> Result<()> {\n        if let ResourceText::Localized(map) = &self.metadata.display_name {\n            if map.get(\"en\").is_none() {\n                return Err(\"missing mandatory english display name\".into());\n            }\n        }\n\n        if let ResourceText::Localized(map) = &self.metadata.description {\n            if map.get(\"en\").is_none() {\n                return Err(\"missing mandatory english description\".into());\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "libs/core/src/resource/mod.ts",
    "content": "export {};\n"
  },
  {
    "path": "libs/core/src/resource/resource_id.rs",
    "content": "use std::sync::LazyLock;\n\nuse serde::{de::Visitor, Deserialize, Deserializer, Serialize};\n\nuse crate::error::Result;\n\n/// For local resources this will be an visual id composed of the creator username and the resource name. e.g. `@username/resource-name`\n/// For downloaded resources this will be an UUID.\n#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, TS)]\n#[ts(type = \"string & { __brand: 'ResourceId' }\")]\npub enum ResourceId {\n    Local(String),\n    Remote(uuid::Uuid),\n}\n\nimpl ResourceId {\n    fn regex() -> &'static regex::Regex {\n        static REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {\n            regex::Regex::new(r\"^@[a-zA-Z][\\w\\-]{1,30}[a-zA-Z0-9]\\/[a-zA-Z][\\w\\-]+[a-zA-Z0-9]$\")\n                .unwrap()\n        });\n        &REGEX\n    }\n\n    pub fn is_valid(&self) -> bool {\n        match self {\n            ResourceId::Local(id) => Self::regex().is_match(id),\n            ResourceId::Remote(_) => true, // UUIDs are always valid\n        }\n    }\n\n    pub fn validate(&self) -> Result<(), String> {\n        if !self.is_valid() {\n            let id_str = match self {\n                ResourceId::Local(id) => id.as_str(),\n                ResourceId::Remote(_) => return Ok(()), // UUIDs are always valid\n            };\n            return Err(format!(\n                \"Invalid resource id ({}), should follow the regex: {}\",\n                id_str,\n                Self::regex()\n            ));\n        }\n        Ok(())\n    }\n\n    pub fn as_str(&self) -> &str {\n        match self {\n            ResourceId::Local(id) => id.as_str(),\n            ResourceId::Remote(_) => \"\",\n        }\n    }\n\n    pub fn starts_with(&self, prefix: &str) -> bool {\n        match self {\n            ResourceId::Local(id) => id.starts_with(prefix),\n            ResourceId::Remote(_) => false,\n        }\n    }\n\n    /// Creator username of the resource, will be none for remote resources\n    pub fn creator(&self) -> Option<String> {\n        match self {\n            ResourceId::Local(id) => Some(\n                id.split('/')\n                    .next()\n                    .unwrap()\n                    .trim_start_matches('@')\n                    .to_string(),\n            ),\n            ResourceId::Remote(_) => None,\n        }\n    }\n\n    /// Name of the resource, will be none for remote resources\n    pub fn resource_name(&self) -> Option<String> {\n        match self {\n            ResourceId::Local(id) => Some(id.split('/').nth(1).unwrap().to_string()),\n            ResourceId::Remote(_) => None,\n        }\n    }\n}\n\nimpl Default for ResourceId {\n    fn default() -> Self {\n        Self::Local(\"@unknown/unknown\".to_owned())\n    }\n}\n\nimpl From<&str> for ResourceId {\n    fn from(value: &str) -> Self {\n        Self::from(value.to_string())\n    }\n}\n\nimpl From<String> for ResourceId {\n    fn from(value: String) -> Self {\n        // Try to parse as UUID first, otherwise treat as local ID\n        if let Ok(uuid) = uuid::Uuid::try_parse(&value) {\n            ResourceId::Remote(uuid)\n        } else {\n            ResourceId::Local(value)\n        }\n    }\n}\n\nimpl From<uuid::Uuid> for ResourceId {\n    fn from(value: uuid::Uuid) -> Self {\n        ResourceId::Remote(value)\n    }\n}\n\nimpl std::fmt::Display for ResourceId {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            ResourceId::Local(id) => write!(f, \"{}\", id),\n            ResourceId::Remote(uuid) => write!(f, \"{}\", uuid),\n        }\n    }\n}\n\nimpl Serialize for ResourceId {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        match self {\n            ResourceId::Local(id) => serializer.serialize_str(id),\n            ResourceId::Remote(id) => serializer.serialize_str(&id.to_string()),\n        }\n    }\n}\n\nimpl<'de> Deserialize<'de> for ResourceId {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        struct ResourceIdVisitor;\n\n        impl<'de> Visitor<'de> for ResourceIdVisitor {\n            type Value = ResourceId;\n\n            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {\n                write!(\n                    formatter,\n                    \"a string matching the resource ID pattern: {}\",\n                    ResourceId::regex().as_str()\n                )\n            }\n\n            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n            where\n                E: serde::de::Error,\n            {\n                // this step is to allow deserialize old ids used on older schemas (themes)\n                let id = match value {\n                    \"toolbar\" => WidgetId::known_toolbar().0,\n                    \"weg\" => WidgetId::known_weg().0,\n                    \"wm\" => WidgetId::known_wm().0,\n                    \"wall\" => WidgetId::known_wall().0,\n                    \"settings\" => WidgetId::known_settings().0,\n                    \"launcher\" => \"@deprecated/launcher\".into(),\n                    \"popup\" => WidgetId::known_popup().0,\n                    _ => {\n                        // Try to parse as UUID first, otherwise treat as local ID\n                        if let Ok(uuid) = uuid::Uuid::try_parse(value) {\n                            ResourceId::Remote(uuid)\n                        } else {\n                            ResourceId::Local(value.to_string())\n                        }\n                    }\n                };\n\n                id.validate().map_err(serde::de::Error::custom)?;\n                Ok(id)\n            }\n        }\n\n        deserializer.deserialize_str(ResourceIdVisitor)\n    }\n}\n\nmacro_rules! resource_id_variant {\n    ($name:ident) => {\n        /// Visual id composed of the creator username and the resource name. e.g. `@username/resource-name`\n        #[derive(\n            Debug, Clone, Hash, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS,\n        )]\n        pub struct $name(ResourceId);\n        crate::identifier_impl!($name, ResourceId);\n\n        impl From<ResourceId> for $name {\n            fn from(value: ResourceId) -> Self {\n                Self(value)\n            }\n        }\n    };\n}\n\nresource_id_variant!(PluginId);\nresource_id_variant!(IconPackId);\nresource_id_variant!(ThemeId);\nresource_id_variant!(WidgetId);\nresource_id_variant!(WallpaperId);\n\nimpl WidgetId {\n    pub fn known_settings() -> Self {\n        \"@seelen/settings\".into()\n    }\n    pub fn known_weg() -> Self {\n        \"@seelen/weg\".into()\n    }\n    pub fn known_toolbar() -> Self {\n        \"@seelen/fancy-toolbar\".into()\n    }\n    pub fn known_wm() -> Self {\n        \"@seelen/window-manager\".into()\n    }\n    pub fn known_wall() -> Self {\n        \"@seelen/wallpaper-manager\".into()\n    }\n    pub fn known_popup() -> Self {\n        \"@seelen/popup\".into()\n    }\n}\n"
  },
  {
    "path": "libs/core/src/resource/yaml_ext.rs",
    "content": "// the idea with this module is improve YAML with extensibility, via custom keywords\n\nuse std::{fs::File, path::Path};\n\nuse serde_yaml::{Mapping, Value};\n\nuse crate::error::Result;\n\n/// Will deserialize a YAML file and parse the custom extended syntax\npub fn deserialize_extended_yaml<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T> {\n    let value = read_and_parse_yml(path)?;\n    Ok(serde_yaml::from_value(value)?)\n}\n\nfn read_and_parse_yml(path: &Path) -> Result<Value> {\n    let file = File::open(path)?;\n    file.lock_shared()?;\n    let base = path.parent().ok_or(\"No parent directory\")?;\n    let value: Value = serde_yaml::from_reader(file)?;\n    parse_yaml(base, value)\n}\n\nfn parse_yaml(base: &Path, value: Value) -> Result<Value> {\n    match value {\n        Value::Mapping(map) => {\n            let mut new_map = Mapping::new();\n            for (key, value) in map {\n                let value = parse_yaml(base, value)?;\n                new_map.insert(key, value);\n            }\n            Ok(Value::Mapping(new_map))\n        }\n        Value::Sequence(seq) => {\n            let mut new_seq = Vec::new();\n            for value in seq {\n                let value = parse_yaml(base, value)?;\n                new_seq.push(value);\n            }\n            Ok(Value::Sequence(new_seq))\n        }\n        Value::Tagged(tag) => {\n            if tag.tag == \"!include\" {\n                if let Value::String(relative_path) = tag.value {\n                    let to_include = base.join(relative_path);\n                    let text = if to_include\n                        .extension()\n                        .is_some_and(|ext| ext == \"scss\" || ext == \"sass\")\n                    {\n                        grass::from_path(&to_include, &grass::Options::default())?\n                    } else {\n                        std::fs::read_to_string(&to_include)?\n                    };\n                    return Ok(Value::String(text));\n                }\n            }\n\n            if tag.tag == \"!extend\" {\n                if let Value::String(relative_path) = tag.value {\n                    let value = read_and_parse_yml(&base.join(relative_path))?;\n                    return Ok(value);\n                }\n            }\n\n            Ok(Value::Tagged(tag))\n        }\n        _ => Ok(value),\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/icon_pack.rs",
    "content": "use std::path::PathBuf;\n\nuse chrono::{DateTime, Utc};\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\nuse crate::resource::{IconPackId, ResourceKind, ResourceMetadata, SluResource};\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct IconPack {\n    pub id: IconPackId,\n    #[serde(alias = \"info\")]\n    pub metadata: ResourceMetadata,\n    /// Special icon used when some other icon is not found\n    pub missing: Option<Icon>,\n    /// Icons defined in this icon pack\n    pub entries: Vec<IconPackEntry>,\n    /// This lists will be downloaded and stored locally\n    pub remote_entries: Vec<IconPackEntry>,\n    /// Indicates if the icon pack icons was downloaded from `remote_entries`\n    pub downloaded: bool,\n}\n\nimpl SluResource for IconPack {\n    const KIND: ResourceKind = ResourceKind::IconPack;\n\n    fn metadata(&self) -> &ResourceMetadata {\n        &self.metadata\n    }\n\n    fn metadata_mut(&mut self) -> &mut ResourceMetadata {\n        &mut self.metadata\n    }\n\n    fn sanitize(&mut self) {\n        self.missing = self.missing.take().filter(|e| e.is_valid());\n        self.entries.retain(|e| match e {\n            IconPackEntry::Unique(e) => match &e.icon {\n                Some(icon) => icon.is_valid(),\n                None => e.redirect.is_some(),\n            },\n            IconPackEntry::Shared(e) => e.icon.is_valid(),\n            IconPackEntry::Custom(e) => e.icon.is_valid(),\n        })\n    }\n}\n\nimpl IconPack {\n    /// replace existing entry if found, otherwise add it.\n    pub fn add_entry(&mut self, entry: IconPackEntry) {\n        if let Some(found) = self.find_similar_mut(&entry) {\n            *found = entry;\n        } else {\n            self.entries.push(entry);\n        }\n    }\n\n    /// search for same entry ignoring the icon.\n    pub fn find_similar_mut(&mut self, entry: &IconPackEntry) -> Option<&mut IconPackEntry> {\n        self.entries\n            .iter_mut()\n            .find(|existing| existing.matches(entry))\n    }\n\n    /// search for same entry ignoring the icon.\n    pub fn find_similar(&self, entry: &IconPackEntry) -> Option<&IconPackEntry> {\n        self.entries.iter().find(|existing| existing.matches(entry))\n    }\n\n    /// search for same entry ignoring the icon.\n    pub fn contains_similar(&self, entry: &IconPackEntry) -> bool {\n        self.find_similar(entry).is_some()\n    }\n}\n\n/// Key can be user model id, filename or a full path.\n/// In case of path this should be an executable or a lnk file or any other file that can\n/// have an unique/individual icon as are the applications, otherwise use `shared`.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct UniqueIconPackEntry {\n    /// Application user model id\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub umid: Option<String>,\n    /// Path or filename of the application, mostly this should be present,\n    /// but cases like PWAs on Edge can have no path and be only an UMID.\n    pub path: Option<PathBuf>,\n    /// In case of path be a lnk file this can be set to a different location to use the icon from.\n    /// If present, icon on this entry will be ignored\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub redirect: Option<PathBuf>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub icon: Option<Icon>,\n    /// Source file modification time for cache invalidation\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    #[ts(optional = nullable)]\n    pub source_mtime: Option<DateTime<Utc>>,\n}\n\n/// Intended to store file icons by extension\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct SharedIconPackEntry {\n    /// File extension without the dot, e.g. \"txt\"\n    pub extension: String,\n    pub icon: Icon,\n}\n\n/// Here specific/custom icons for widgets can be stored.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct CustomIconPackEntry {\n    /// we recomend following the widget id + icon name to avoid collisions\n    /// e.g. \"@username/widgetid::iconname\" but you can use whatever you want\n    pub key: String,\n    /// Value is the path to the icon relative to the icon pack folder.\n    pub icon: Icon,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(tag = \"type\", rename_all = \"camelCase\")]\npub enum IconPackEntry {\n    Unique(UniqueIconPackEntry),\n    Shared(SharedIconPackEntry),\n    Custom(CustomIconPackEntry),\n}\n\nimpl IconPackEntry {\n    pub fn matches(&self, entry: &IconPackEntry) -> bool {\n        match (self, entry) {\n            (IconPackEntry::Unique(self_unique), IconPackEntry::Unique(other_unique)) => {\n                self_unique.umid == other_unique.umid\n                    && self_unique.path == other_unique.path\n                    && self_unique.redirect == other_unique.redirect\n            }\n            (IconPackEntry::Shared(self_shared), IconPackEntry::Shared(other_shared)) => {\n                self_shared.extension == other_shared.extension\n            }\n            (IconPackEntry::Custom(self_custom), IconPackEntry::Custom(other_custom)) => {\n                self_custom.key == other_custom.key\n            }\n            _ => false,\n        }\n    }\n}\n\n/// The icon paths in this structure are relative to the icon pack folder.\n#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct Icon {\n    /// Icon to use if no light or dark icon is specified, if both light and dark are specified this can be omitted\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub base: Option<String>,\n    /// Alternative icon to use when system theme is light\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub light: Option<String>,\n    /// Alternative icon to use when system theme is dark\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub dark: Option<String>,\n    /// Mask to be applied over the icon, themes can use this to apply custom colors over the icon.\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub mask: Option<String>,\n    /// Whether the icon is a square or not\n    #[serde(skip_serializing_if = \"is_false\")]\n    pub is_aproximately_square: bool,\n}\n\nimpl Icon {\n    pub fn is_valid(&self) -> bool {\n        self.base.is_some() || (self.light.is_some() && self.dark.is_some())\n    }\n}\n\nfn is_false(b: &bool) -> bool {\n    !b\n}\n"
  },
  {
    "path": "libs/core/src/state/icon_pack.test.ts",
    "content": "import type { Icon, IconPack, IconPackId, ResourceMetadata } from \"@seelen-ui/types\";\nimport { IconPackManager } from \"./icon_pack.ts\";\nimport { assertEquals } from \"@std/assert\";\n\n// Custom assertion for null values\nfunction assertNull(value: unknown): void {\n  return assertEquals(value, null);\n}\n\nfunction onlyBase(x: string): Icon {\n  return {\n    base: x,\n    light: null,\n    dark: null,\n    mask: null,\n    isAproximatelySquare: false,\n  };\n}\n\n// Constants for test icons\nconst GOT_BY_PATH = \"GOT_BY_PATH\";\nconst GOT_BY_FILENAME = \"GOT_BY_FILENAME\";\nconst GOT_BY_EXTENSION = \"GOT_BY_EXTENSION\";\nconst GOT_BY_UMID = \"GOT_BY_UMID\";\n\nconst A_PATH = \"path\\\\to\\\\a\";\nconst B_PATH = \"path\\\\to\\\\b\";\nconst C_PATH = \"path\\\\to\\\\c\";\n\n// Deep clone helper to ensure test isolation\nconst cloneIconPack = (pack: IconPack): IconPack => JSON.parse(JSON.stringify(pack));\n\n// Factory function for mock icon packs\nconst createMockIconPacks = (): {\n  packA: IconPack;\n  packB: IconPack;\n  packC: IconPack;\n} => ({\n  packA: {\n    id: \"a\" as IconPackId,\n    metadata: { path: A_PATH } as ResourceMetadata,\n    missing: onlyBase(\"MissingIconA.png\"),\n    entries: [\n      // Unique entries (apps)\n      {\n        type: \"unique\",\n        umid: \"MSEdge\",\n        redirect: null,\n        path: \"C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedge.exe\",\n        icon: onlyBase(GOT_BY_UMID),\n      },\n      {\n        type: \"unique\",\n        umid: null,\n        redirect: null,\n        path: \"C:\\\\Windows\\\\explorer.exe\",\n        icon: onlyBase(GOT_BY_PATH),\n      },\n      {\n        type: \"unique\",\n        umid: null,\n        redirect: null,\n        path: \"C:\\\\Program Files (x86)\\\\Some\\\\App\\\\filenameApp.exe\",\n        icon: onlyBase(GOT_BY_PATH),\n      },\n      // Shared entries (file extensions)\n      {\n        type: \"shared\",\n        extension: \"txt\",\n        icon: onlyBase(GOT_BY_EXTENSION),\n      },\n      {\n        type: \"shared\",\n        extension: \"png\",\n        icon: onlyBase(GOT_BY_EXTENSION),\n      },\n      {\n        type: \"shared\",\n        extension: \"jpg\",\n        icon: onlyBase(GOT_BY_EXTENSION),\n      },\n      // Custom entries\n      {\n        type: \"custom\",\n        key: \"my-custom-icon\",\n        icon: onlyBase(\"CustomA.png\"),\n      },\n    ],\n    remoteEntries: [],\n    downloaded: false,\n  },\n  packB: {\n    id: \"b\" as IconPackId,\n    metadata: { path: B_PATH } as ResourceMetadata,\n    missing: onlyBase(\"MissingIconB.png\"),\n    entries: [\n      // Unique entries (apps)\n      {\n        type: \"unique\",\n        umid: null,\n        redirect: null,\n        path: \"C:\\\\Windows\\\\explorer.exe\",\n        icon: onlyBase(GOT_BY_PATH),\n      },\n      {\n        type: \"unique\",\n        umid: null,\n        redirect: null,\n        path: \"filenameApp.exe\",\n        icon: onlyBase(GOT_BY_FILENAME),\n      },\n      // Shared entries (file extensions)\n      {\n        type: \"shared\",\n        extension: \"txt\",\n        icon: onlyBase(GOT_BY_EXTENSION),\n      },\n      // Custom entries\n      {\n        type: \"custom\",\n        key: \"my-custom-icon\",\n        icon: onlyBase(\"CustomB.png\"),\n      },\n    ],\n    remoteEntries: [],\n    downloaded: false,\n  },\n  packC: {\n    id: \"c\" as IconPackId,\n    metadata: { path: C_PATH } as ResourceMetadata,\n    missing: null,\n    entries: [\n      // Unique entries (apps)\n      {\n        type: \"unique\",\n        umid: null,\n        path: \"C:\\\\folder\\\\app1.exe\",\n        icon: onlyBase(GOT_BY_PATH),\n        redirect: null,\n      },\n    ],\n    remoteEntries: [],\n    downloaded: false,\n  },\n});\n\n// Test context manager for cleaner test setup\nclass IconPackManagerTestContext {\n  private manager: IconPackManagerMock;\n\n  // Default active packs: ['b', 'a'] (note 'a' has higher priority as it's last)\n  constructor(initialActives: string[] = [\"b\", \"a\"]) {\n    const mocks = createMockIconPacks();\n    this.manager = new IconPackManagerMock(\n      [\n        cloneIconPack(mocks.packA),\n        cloneIconPack(mocks.packB),\n        cloneIconPack(mocks.packC),\n      ],\n      initialActives,\n    );\n  }\n\n  get instance(): IconPackManagerMock {\n    return this.manager;\n  }\n\n  // Fluent interface for changing active packs\n  withActives(actives: string[]): this {\n    this.manager.setActives(actives);\n    return this;\n  }\n}\n\n// Mock implementation of IconPackManager for testing\nclass IconPackManagerMock extends IconPackManager {\n  constructor(packs: IconPack[], activeKeys: string[]) {\n    super(packs, activeKeys);\n    this.resolveAvailableIcons();\n    this.cacheActiveIconPacks();\n  }\n\n  public setActives(actives: string[]): void {\n    this._activeIconPackIds = actives;\n    this.cacheActiveIconPacks();\n  }\n}\n\nDeno.test(\"IconPackManager\", async (t) => {\n  await t.step(\"Icon lookup functionality\", async (t) => {\n    await t.step(\"should return null for non-existent paths or UMIDs\", () => {\n      const ctx = new IconPackManagerTestContext();\n      // Non-existent path\n      assertNull(\n        ctx.instance.getIconPath({ path: \"C:\\\\nonexistent\\\\path.exe\" }),\n      );\n      // Non-existent UMID\n      assertNull(ctx.instance.getIconPath({ umid: \"NonexistentUMID\" }));\n    });\n\n    await t.step(\"should ignore inactive icon packs\", () => {\n      // Only 'a' and 'b' are active by default\n      const ctx = new IconPackManagerTestContext();\n      // This path only exists in packC which is inactive\n      assertNull(ctx.instance.getIconPath({ path: \"C:\\\\folder\\\\app1.exe\" }));\n    });\n\n    await t.step(\n      \"should respect cascading priority order (last has highest priority)\",\n      () => {\n        // Default order is ['b', 'a'] so 'a' has higher priority\n        const ctx = new IconPackManagerTestContext([\"b\", \"a\"]);\n\n        // 'a' should take priority for explorer.exe (last in active list)\n        assertEquals(\n          ctx.instance.getIconPath({ path: \"C:\\\\Windows\\\\explorer.exe\" }),\n          onlyBase(`${A_PATH}\\\\${GOT_BY_PATH}`),\n        );\n\n        // After changing priority to ['a', 'b'], 'b' should now have priority\n        assertEquals(\n          ctx.withActives([\"a\", \"b\"]).instance.getIconPath({\n            path: \"C:\\\\Windows\\\\explorer.exe\",\n          }),\n          onlyBase(`${B_PATH}\\\\${GOT_BY_PATH}`),\n        );\n      },\n    );\n\n    await t.step(\"should prioritize UMID over path matching\", () => {\n      const ctx = new IconPackManagerTestContext([\"b\", \"a\"]); // 'a' has priority\n      // Should match UMID in packA even though path exists in both packs\n      assertEquals(\n        ctx.instance.getIconPath({\n          path: \"C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedge.exe\",\n          umid: \"MSEdge\",\n        }),\n        onlyBase(`${A_PATH}\\\\${GOT_BY_UMID}`),\n      );\n    });\n\n    await t.step(\n      \"should match by filename when higher priority pack has filename match\",\n      () => {\n        // With ['b', 'a'] order, packB has filename match that should take priority\n        const ctx = new IconPackManagerTestContext([\"a\", \"b\"]);\n        assertEquals(\n          ctx.instance.getIconPath({\n            path: \"C:\\\\Program Files (x86)\\\\Some\\\\App\\\\filenameApp.exe\",\n          }),\n          onlyBase(`${B_PATH}\\\\${GOT_BY_FILENAME}`),\n        );\n      },\n    );\n\n    await t.step(\"should match files by extension with priority order\", () => {\n      const ctx = new IconPackManagerTestContext([\"b\", \"a\"]); // 'a' has priority\n\n      // .txt exists in both packs - should use 'a' (higher priority)\n      assertEquals(\n        ctx.instance.getIconPath({ path: \"C:\\\\Some\\\\App\\\\someFile.txt\" }),\n        onlyBase(`${A_PATH}\\\\${GOT_BY_EXTENSION}`),\n      );\n\n      // .png only exists in packA\n      assertEquals(\n        ctx.instance.getIconPath({ path: \"C:\\\\Some\\\\App\\\\someFile.png\" }),\n        onlyBase(`${A_PATH}\\\\${GOT_BY_EXTENSION}`),\n      );\n\n      // When we change priority to ['a', 'b'], 'b' should have priority for .txt\n      assertEquals(\n        ctx.withActives([\"a\", \"b\"]).instance.getIconPath({\n          path: \"C:\\\\Some\\\\App\\\\someFile.txt\",\n        }),\n        onlyBase(`${B_PATH}\\\\${GOT_BY_EXTENSION}`),\n      );\n    });\n  });\n\n  await t.step(\"Missing icon functionality\", async (t) => {\n    await t.step(\n      \"should return missing icon from highest priority pack (last in active list)\",\n      () => {\n        // With ['b', 'a'], 'a' has priority\n        const ctx = new IconPackManagerTestContext([\"b\", \"a\"]);\n        assertEquals(\n          ctx.instance.getMissingIconPath(),\n          onlyBase(`${A_PATH}\\\\MissingIconA.png`),\n        );\n      },\n    );\n\n    await t.step(\n      \"should fallback when higher priority pack has no missing icon\",\n      () => {\n        // packC has no missing icon, should fallback to packB\n        const ctx = new IconPackManagerTestContext([\"c\", \"b\"]);\n        assertEquals(\n          ctx.instance.getMissingIconPath(),\n          onlyBase(`${B_PATH}\\\\MissingIconB.png`),\n        );\n      },\n    );\n\n    await t.step(\n      \"should return null when no active packs have missing icons\",\n      () => {\n        const ctx = new IconPackManagerTestContext([\"c\"]); // packC has no missing icon\n        assertNull(ctx.instance.getMissingIconPath());\n      },\n    );\n  });\n\n  await t.step(\"Custom icon functionality\", async (t) => {\n    await t.step(\"should return custom icon from highest priority pack\", () => {\n      // With ['b', 'a'], 'a' has priority\n      const ctx = new IconPackManagerTestContext([\"b\", \"a\"]);\n      assertEquals(\n        ctx.instance.getCustomIconPath(\"my-custom-icon\"),\n        onlyBase(`${A_PATH}\\\\CustomA.png`),\n      );\n    });\n\n    await t.step(\n      \"should fallback when custom icon not found in higher priority pack\",\n      () => {\n        // packC has no custom icons, should fallback to packA\n        const ctx = new IconPackManagerTestContext([\"c\", \"a\"]);\n        assertEquals(\n          ctx.instance.getCustomIconPath(\"my-custom-icon\"),\n          onlyBase(`${A_PATH}\\\\CustomA.png`),\n        );\n      },\n    );\n\n    await t.step(\n      \"should return null when custom icon not found in any active pack\",\n      () => {\n        const ctx = new IconPackManagerTestContext([\"c\"]);\n        assertNull(ctx.instance.getCustomIconPath(\"non-existent-icon\"));\n      },\n    );\n  });\n\n  await t.step(\"Redirect functionality\", async (t) => {\n    await t.step(\n      \"should follow redirect and ignore icon when redirect is present\",\n      () => {\n        const ctx = new IconPackManagerTestContext([\"a\", \"b\"]);\n        // Add entry with both redirect and icon (icon should be ignored)\n        ctx.instance.iconPacks[0].entries.push({\n          type: \"unique\",\n          umid: \"RedirectTest\",\n          path: \"C:\\\\redirect\\\\source.exe\",\n          redirect: \"C:\\\\redirect\\\\target.exe\",\n          icon: onlyBase(A_PATH + \"\\\\ThisShouldBeIgnored.png\"),\n        });\n        // Add target entry to packB\n        ctx.instance.iconPacks[1].entries.push({\n          type: \"unique\",\n          umid: null,\n          path: \"C:\\\\redirect\\\\target.exe\",\n          redirect: null,\n          icon: onlyBase(B_PATH + \"\\\\RedirectTargetIcon.png\"),\n        });\n\n        assertEquals(\n          ctx.instance.getIconPath({ umid: \"RedirectTest\" }),\n          onlyBase(B_PATH + \"\\\\RedirectTargetIcon.png\"),\n        );\n      },\n    );\n\n    await t.step(\n      \"should not use icon when redirect points to non-existent path\",\n      () => {\n        const ctx = new IconPackManagerTestContext([\"a\"]);\n        // Add entry with redirect to non-existent path and icon\n        ctx.instance.iconPacks[0].entries.push({\n          type: \"unique\",\n          umid: \"BadRedirect\",\n          path: \"C:\\\\redirect\\\\source.exe\",\n          redirect: \"C:\\\\nonexistent\\\\path.exe\",\n          icon: onlyBase(A_PATH + \"\\\\ShouldNotUseThis.png\"), // <-- should be ignored inclusively if redirect points to non-existent path\n        });\n\n        assertNull(ctx.instance.getIconPath({ umid: \"BadRedirect\" }));\n      },\n    );\n\n    await t.step(\n      \"should follow redirect chain until icon is found or no more redirects\",\n      () => {\n        const ctx = new IconPackManagerTestContext([\"a\", \"b\", \"c\"]);\n        // First redirect (icon should be ignored)\n        ctx.instance.iconPacks[0].entries.push({\n          type: \"unique\",\n          umid: \"ChainRedirect\",\n          path: \"C:\\\\redirect\\\\start.exe\",\n          redirect: \"C:\\\\redirect\\\\middle.exe\",\n          icon: onlyBase(A_PATH + \"\\\\IgnoreThis.png\"),\n        });\n        // Second redirect (no icon)\n        ctx.instance.iconPacks[1].entries.push({\n          type: \"unique\",\n          umid: null,\n          path: \"C:\\\\redirect\\\\middle.exe\",\n          redirect: \"C:\\\\redirect\\\\final.exe\",\n          icon: null,\n        });\n        // Final target with icon\n        ctx.instance.iconPacks[2].entries.push({\n          type: \"unique\",\n          umid: null,\n          path: \"C:\\\\redirect\\\\final.exe\",\n          redirect: null,\n          icon: onlyBase(C_PATH + \"\\\\FinalIcon.png\"),\n        });\n\n        assertEquals(\n          ctx.instance.getIconPath({ umid: \"ChainRedirect\" }),\n          onlyBase(C_PATH + \"\\\\FinalIcon.png\"),\n        );\n      },\n    );\n\n    await t.step(\n      \"should return null if redirect chain ends without finding an icon\",\n      () => {\n        const ctx = new IconPackManagerTestContext([\"a\", \"b\"]);\n        // First redirect\n        ctx.instance.iconPacks[0].entries.push({\n          type: \"unique\",\n          umid: \"BrokenChain\",\n          path: \"C:\\\\redirect\\\\start.exe\",\n          redirect: \"C:\\\\redirect\\\\missing.exe\",\n          icon: onlyBase(A_PATH + \"\\\\IgnoreMe.png\"),\n        });\n\n        // No matching entry for the redirect target\n        assertNull(ctx.instance.getIconPath({ umid: \"BrokenChain\" }));\n      },\n    );\n\n    await t.step(\"should handle redirect to extension match\", () => {\n      const ctx = new IconPackManagerTestContext([\"a\", \"b\"]);\n      // Redirect to a file with specific extension\n      ctx.instance.iconPacks[0].entries.push({\n        type: \"unique\",\n        umid: \"RedirectToExtension\",\n        path: \"C:\\\\some\\\\app.exe\",\n        redirect: \"C:\\\\some\\\\file.txt\", // <-- redirect to txt file\n        icon: onlyBase(A_PATH + \"\\\\WrongIcon.png\"), // <-- should be ignored\n      });\n\n      assertEquals(\n        ctx.instance.getIconPath({ umid: \"RedirectToExtension\" }),\n        onlyBase(B_PATH + \"\\\\GOT_BY_EXTENSION\"),\n      );\n    });\n  });\n\n  await t.step(\"should handle circular references and return null\", () => {\n    const ctx = new IconPackManagerTestContext([\"a\", \"b\"]);\n\n    ctx.instance.iconPacks[0].entries.push({\n      type: \"unique\",\n      umid: \"CircularRef1\",\n      path: \"C:\\\\circle\\\\app1.exe\",\n      redirect: \"C:\\\\circle\\\\app2.exe\",\n      icon: onlyBase(\"IgnoredIcon1.png\"),\n    });\n\n    ctx.instance.iconPacks[1].entries.push({\n      type: \"unique\",\n      umid: \"CircularRef2\",\n      path: \"C:\\\\circle\\\\app2.exe\",\n      redirect: \"C:\\\\circle\\\\app1.exe\",\n      icon: onlyBase(\"IgnoredIcon2.png\"),\n    });\n\n    assertNull(ctx.instance.getIconPath({ umid: \"CircularRef1\" }));\n    assertNull(ctx.instance.getIconPath({ path: \"C:\\\\circle\\\\app1.exe\" }));\n    assertNull(ctx.instance.getIconPath({ path: \"C:\\\\circle\\\\app2.exe\" }));\n  });\n\n  await t.step(\"should detect self-references and return null\", () => {\n    const ctx = new IconPackManagerTestContext([\"a\"]);\n\n    // Configurar autorreferencia\n    ctx.instance.iconPacks[0].entries.push({\n      type: \"unique\",\n      umid: \"SelfRef\",\n      path: \"C:\\\\circle\\\\self.exe\",\n      redirect: \"C:\\\\circle\\\\self.exe\", // Se redirige a sí mismo\n      icon: onlyBase(\"IgnoredIcon.png\"),\n    });\n\n    assertNull(ctx.instance.getIconPath({ umid: \"SelfRef\" }));\n  });\n\n  await t.step(\n    \"should detect longer circular references and return null\",\n    () => {\n      const ctx = new IconPackManagerTestContext([\"a\", \"b\", \"c\"]);\n\n      // Configurar referencia circular más larga (A -> B -> C -> A)\n      ctx.instance.iconPacks[0].entries.push({\n        type: \"unique\",\n        umid: null,\n        path: \"C:\\\\circle\\\\a.exe\",\n        redirect: \"C:\\\\circle\\\\b.exe\",\n        icon: onlyBase(\"IgnoredA.png\"),\n      });\n\n      ctx.instance.iconPacks[1].entries.push({\n        type: \"unique\",\n        umid: null,\n        path: \"C:\\\\circle\\\\b.exe\",\n        redirect: \"C:\\\\circle\\\\c.exe\",\n        icon: onlyBase(\"IgnoredB.png\"),\n      });\n\n      ctx.instance.iconPacks[2].entries.push({\n        type: \"unique\",\n        umid: null,\n        path: \"C:\\\\circle\\\\c.exe\",\n        redirect: \"C:\\\\circle\\\\a.exe\",\n        icon: onlyBase(\"IgnoredC.png\"),\n      });\n\n      assertNull(ctx.instance.getIconPath({ path: \"C:\\\\circle\\\\a.exe\" }));\n    },\n  );\n});\n"
  },
  {
    "path": "libs/core/src/state/icon_pack.ts",
    "content": "import type {\n  Icon as IIcon,\n  IconPack,\n  IconPack as IIconPack,\n  SeelenCommandGetIconArgs,\n  UniqueIconPackEntry,\n} from \"@seelen-ui/types\";\nimport { List } from \"../utils/List.ts\";\nimport { newFromInvoke, newOnEvent } from \"../utils/State.ts\";\nimport { invoke, SeelenCommand, SeelenEvent, type UnSubscriber } from \"../handlers/mod.ts\";\nimport { Settings } from \"./settings/mod.ts\";\nimport type { UnlistenFn } from \"@tauri-apps/api/event\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\n\nexport class IconPackList extends List<IIconPack> {\n  static getAsync(): Promise<IconPackList> {\n    return newFromInvoke(this, SeelenCommand.StateGetIconPacks);\n  }\n\n  static onChange(cb: (payload: IconPackList) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.StateIconPacksChanged);\n  }\n}\n\n/**\n * Class helper to allow easy use of icon packs\n */\nexport class IconPackManager {\n  private callbacks: Set<() => void> = new Set();\n  private unlisteners: UnlistenFn[] = [];\n  private isListeningForChanges = false;\n\n  /// list of active icon packs and fully resolved paths\n  private activeIconPacks: IconPack[] = [];\n\n  protected constructor(\n    protected _availableIconPacks: IconPack[],\n    protected _activeIconPackIds: string[],\n  ) {}\n\n  get iconPacks(): IconPack[] {\n    return this._availableIconPacks;\n  }\n\n  get activeIconPackIds(): string[] {\n    return this._activeIconPackIds;\n  }\n\n  protected resolveAvailableIcons(): void {\n    for (const pack of this._availableIconPacks) {\n      const path = `${pack.metadata.path}`;\n\n      if (pack.missing) {\n        pack.missing = resolveIcon(path, pack.missing);\n      }\n\n      for (const entry of pack.entries) {\n        if (entry.type === \"unique\") {\n          entry.path = entry.path?.toLowerCase() || null;\n        }\n\n        if (entry.type === \"shared\") {\n          entry.extension = entry.extension.toLowerCase();\n        }\n\n        if (entry.icon) {\n          entry.icon = resolveIcon(path, entry.icon);\n        }\n      }\n    }\n  }\n\n  protected cacheActiveIconPacks(): void {\n    this.activeIconPacks = [];\n    for (const key of this._activeIconPackIds.toReversed()) {\n      const pack = this._availableIconPacks.find((p) => p.id === key);\n      if (pack) {\n        this.activeIconPacks.push(pack);\n      }\n    }\n  }\n\n  /**\n   * Creates an instance of IconPackManager. This intance will be updated when\n   * the list of icon packs or the settings changes, so just having one global instance is enough.\n   *\n   * @returns A new instance of IconPackManager\n   */\n  public static async create(): Promise<IconPackManager> {\n    const instance = new IconPackManager(\n      (await IconPackList.getAsync()).asArray(),\n      (await Settings.getAsync()).inner.activeIconPacks,\n    );\n    instance.resolveAvailableIcons();\n    instance.cacheActiveIconPacks();\n    return instance;\n  }\n\n  /**\n   * Registers a callback to be invoked when the list of active icon packs changes.\n   * This method also sets up listeners to detect changes in the icon pack list and\n   * the active icon packs settings. If no callbacks are registered beforehand, the\n   * listeners are initialized. When no callbacks remain registered, the listeners are stopped.\n   *\n   * @param {() => void} cb - The callback to be invoked when the list of active icon packs changes.\n   *                          This callback takes no arguments and returns no value.\n   * @returns {Promise<UnlistenFn>} A promise that resolves to an `UnlistenFn` function. When invoked,\n   *                                this function unregisters the callback and stops listening for changes\n   *                                if no other callbacks are registered.\n   *\n   * @example\n   * const manager = await IconPackManager.create();\n   * const unlisten = await manager.onChange(() => {\n   *   console.log(\"Icon packs changed: \", manager.actives);\n   * });\n   *\n   * // Later, to stop listening for changes:\n   * unlisten();\n   *\n   * @remarks\n   * - The `this` context inside the callback refers to the `IconPackManager` instance, provided the callback\n   *   is not rebound to another context (e.g., using `bind`, `call`, or `apply`).\n   * - If the callback is defined as an arrow function, `this` will be lexically bound to the surrounding context.\n   * - If the callback is a regular function, ensure it is bound correctly to avoid `this` being `undefined` (in strict mode)\n   *   or the global object (in non-strict mode).\n   *\n   * @see {@link IconPackManager} for the class this method belongs to.\n   * @see {@link UnlistenFn} for the type of the function returned by this method.\n   */\n  public async onChange(cb: () => void): Promise<UnlistenFn> {\n    this.callbacks.add(cb);\n\n    if (!this.isListeningForChanges) {\n      this.isListeningForChanges = true;\n      const unlistenerIcons = await IconPackList.onChange((list) => {\n        this._availableIconPacks = JSON.parse(JSON.stringify(list.all()));\n        this.resolveAvailableIcons();\n        this.cacheActiveIconPacks();\n        this.callbacks.forEach((cb) => cb());\n      });\n      const unlistenerSettings = await Settings.onChange((settings) => {\n        this._activeIconPackIds = settings.inner.activeIconPacks;\n        this.cacheActiveIconPacks();\n        this.callbacks.forEach((cb) => cb());\n      });\n      this.unlisteners = [unlistenerIcons, unlistenerSettings];\n    }\n\n    return () => {\n      this.callbacks.delete(cb);\n      if (this.callbacks.size === 0) {\n        this.unlisteners.forEach((unlisten) => unlisten());\n        this.unlisteners = [];\n        this.isListeningForChanges = false;\n      }\n    };\n  }\n\n  /**\n   * Returns the icon path for an app or file. If no icon is available, returns `null`.\n   *\n   * The search for icons follows this priority order:\n   * 1. UMID (App User Model Id)\n   * 2. Full path\n   * 3. Filename (this is only used to match executable files like .exe)\n   * 4. Extension\n   *\n   * @param {Object} args - Arguments for retrieving the icon path.\n   * @param {string} [args.path] - The full path to the app or file.\n   * @param {string} [args.umid] - The UMID of the app.\n   * @returns {string | null} - The path to the icon, or `null` if no icon is found.\n   *\n   * @example\n   * // Example 1: Get icon by full path\n   * const iconPath = instance.getIconPath({\n   *   path: \"C:\\\\Program Files\\\\Steam\\\\steam.exe\"\n   * });\n   *\n   * // Example 2: Get icon by UMID\n   * const iconPath = instance.getIconPath({\n   *   umid: \"Seelen.SeelenUI_p6yyn03m1894e!App\"\n   * });\n   */\n  public getIconPath(args: SeelenCommandGetIconArgs): IIcon | null {\n    const { path, umid, __seen = new Set<string>() } = args as\n      & SeelenCommandGetIconArgs\n      & { __seen?: Set<string> };\n\n    // If neither path nor UMID is provided, return null\n    if (!path && !umid) {\n      return null;\n    }\n\n    const lowerPath = path?.toLowerCase();\n\n    // Extract extension only when there's an actual dot after the last path separator,\n    // to avoid treating directory names without dots as extensions.\n    const lastDot = lowerPath?.lastIndexOf(\".\");\n    const lastSlash = lowerPath?.lastIndexOf(\"\\\\\") ?? -1;\n    const extension = (lastDot !== undefined && lastDot > lastSlash) ? lowerPath!.slice(lastDot + 1) : undefined;\n\n    // Add the starting path to __seen so that direct A→B→A cycles are caught in 2 hops.\n    if (lowerPath) {\n      __seen.add(lowerPath);\n    }\n\n    for (const pack of this.activeIconPacks) {\n      let entry: UniqueIconPackEntry | undefined;\n\n      if (umid) {\n        entry = pack.entries.find((e) => e.type === \"unique\" && !!e.umid && e.umid === umid) as UniqueIconPackEntry;\n      }\n\n      if (!entry && lowerPath) {\n        entry = pack.entries.find((e) => {\n          if (e.type !== \"unique\" || !e.path) {\n            return false;\n          }\n\n          if (e.path === lowerPath) {\n            return true;\n          }\n\n          // only search for filename in case of executable files\n          if (extension === \"exe\") {\n            const filename = lowerPath.split(\"\\\\\").pop();\n            // Use separator-aware check to avoid partial name matches (e.g. \"mysteam.exe\" != \"steam.exe\")\n            if (filename && (e.path === filename || e.path.endsWith(\"\\\\\" + filename))) {\n              return true;\n            }\n          }\n          return false;\n        }) as UniqueIconPackEntry;\n      }\n\n      if (entry) {\n        if (entry.redirect) {\n          // break circular references\n          if (__seen.has(entry.redirect)) {\n            return null;\n          }\n          __seen.add(entry.redirect);\n          return this.getIconPath(\n            { path: entry.redirect, __seen } as SeelenCommandGetIconArgs,\n          );\n        }\n\n        if (entry.icon) {\n          return entry.icon;\n        }\n      }\n    }\n\n    // search by file extension\n    if (!extension) {\n      return null;\n    }\n\n    for (const pack of this.activeIconPacks) {\n      const icon = pack.entries.find((e) => {\n        return e.type === \"shared\" && e.extension === extension;\n      });\n\n      if (icon) {\n        return icon.icon;\n      }\n    }\n\n    // If no icon is found in any icon pack, return null\n    return null;\n  }\n\n  public getIcon({ path, umid }: SeelenCommandGetIconArgs): IIcon | null {\n    const iconPath = this.getIconPath({ path, umid });\n    return iconPath ? resolveAsSrc(iconPath) : null;\n  }\n\n  /**\n   * Will return the special missing icon path from the highest priority icon pack.\n   * If no icon pack haves a missing icon, will return null.\n   */\n  public getMissingIconPath(): IIcon | null {\n    for (const pack of this.activeIconPacks) {\n      if (pack.missing) {\n        return pack.missing;\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Will return the special missing icon SRC from the highest priority icon pack.\n   * If no icon pack haves a missing icon, will return null.\n   */\n  public getMissingIcon(): IIcon | null {\n    const iconPath = this.getMissingIconPath();\n    return iconPath ? resolveAsSrc(iconPath) : null;\n  }\n\n  /**\n   * Will return the specific icon path from the highest priority icon pack.\n   * If no icon pack haves the searched icon, will return null.\n   */\n  public getCustomIconPath(name: string): IIcon | null {\n    for (const pack of this.activeIconPacks) {\n      const entry = pack.entries.find((e) => e.type === \"custom\" && e.key === name);\n      if (entry) {\n        return entry.icon;\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Will return the specific icon SRC from the highest priority icon pack.\n   * If no icon pack haves the searched icon, will return null.\n   */\n  public getCustomIcon(name: string): IIcon | null {\n    const iconPath = this.getCustomIconPath(name);\n    return iconPath ? resolveAsSrc(iconPath) : null;\n  }\n\n  /**\n   * This method doesn't take in care icon packs, just extracts the inherited icon into system's icon pack\n   * if it's not already there.\n   *\n   * @param filePath The path to the app could be umid o full path\n   * @example\n   * const iconPath = instance.extractIcon({\n   *   path: \"C:\\\\Program Files\\\\Steam\\\\steam.exe\"\n   * });\n   * const iconPath = instance.extractIcon({\n   *   umid: \"Seelen.SeelenUI_p6yyn03m1894e!App\"\n   * });\n   */\n  public static requestIconExtraction(\n    obj: SeelenCommandGetIconArgs,\n  ): Promise<void> {\n    return invoke(SeelenCommand.GetIcon, obj);\n  }\n\n  /**\n   * This will delete all stored icons on the system icon pack.\\\n   * All icons should be regenerated after calling this method.\n   */\n  public static clearCachedIcons(): Promise<void> {\n    return invoke(SeelenCommand.StateDeleteCachedIcons);\n  }\n}\n\nfunction resolveIcon(parent: string, icon: IIcon): IIcon {\n  return {\n    base: icon.base ? `${parent}\\\\${icon.base}` : null,\n    light: icon.light ? `${parent}\\\\${icon.light}` : null,\n    dark: icon.dark ? `${parent}\\\\${icon.dark}` : null,\n    mask: icon.mask ? `${parent}\\\\${icon.mask}` : null,\n    isAproximatelySquare: icon.isAproximatelySquare,\n  };\n}\n\nfunction resolveAsSrc(icon: IIcon): IIcon {\n  return {\n    base: icon.base ? convertFileSrc(icon.base) : null,\n    light: icon.light ? convertFileSrc(icon.light) : null,\n    dark: icon.dark ? convertFileSrc(icon.dark) : null,\n    mask: icon.mask ? convertFileSrc(icon.mask) : null,\n    isAproximatelySquare: icon.isAproximatelySquare,\n  };\n}\n"
  },
  {
    "path": "libs/core/src/state/mod.rs",
    "content": "mod icon_pack;\nmod placeholder;\nmod plugin;\nmod popups;\nmod settings;\nmod theme;\nmod wallpaper;\nmod weg_items;\nmod widget;\nmod wm_layout;\nmod workspaces;\n\npub use icon_pack::*;\npub use placeholder::*;\npub use plugin::*;\npub use popups::*;\npub use settings::*;\npub use theme::*;\npub use wallpaper::*;\npub use weg_items::*;\npub use widget::*;\npub use wm_layout::*;\npub use workspaces::*;\n"
  },
  {
    "path": "libs/core/src/state/mod.ts",
    "content": "export * from \"./theme/mod.ts\";\nexport * from \"./settings/mod.ts\";\nexport * from \"./icon_pack.ts\";\nexport * from \"./plugin/mod.ts\";\nexport * from \"./widget/mod.ts\";\nexport * from \"./wallpaper/mod.ts\";\n"
  },
  {
    "path": "libs/core/src/state/placeholder.rs",
    "content": "use std::collections::{HashMap, HashSet};\n\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\nuse url::Url;\n\nuse crate::{resource::PluginId, utils::TsUnknown};\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum ToolbarJsScope {\n    Date,\n    Notifications,\n    Media,\n    Network,\n    Keyboard,\n    User,\n    Bluetooth,\n    Power,\n    FocusedApp,\n    Workspaces,\n    Disk,\n    NetworkStatistics,\n    Memory,\n    Cpu,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct ToolbarItem {\n    /// Id to identify the item, should be unique. Preferably a uuid.\n    pub id: String,\n    /// List of scopes to be loaded in the item js execution scope.\n    pub scopes: HashSet<ToolbarJsScope>,\n    /// JS function definition for content to display in the item.\n    pub template: String,\n    /// JS function definition for content to display in tooltip of the item.\n    pub tooltip: Option<String>,\n    /// JS function definition badge content, will be displayed over the item, useful as notifications.\n    pub badge: Option<String>,\n    /// JS function definition that will be executed when the item is clicked.\n    #[serde(alias = \"onClickV2\")]\n    pub on_click: Option<String>,\n    /// Styles to be added to the item. This follow the same interface of React's `style` prop.\n    pub style: HashMap<String, Option<StyleValue>>,\n    /// Remote data to be added to the item scope.\n    pub remote_data: HashMap<String, RemoteDataDeclaration>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct RemoteDataDeclaration {\n    url: Url,\n    request_init: Option<TsUnknown>,\n    update_interval_seconds: Option<u32>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(untagged)]\npub enum StyleValue {\n    String(String),\n    Number(serde_json::Number),\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(repr(enum = name))]\npub enum WorkspaceToolbarItemMode {\n    #[default]\n    Dotted,\n    Named,\n    Numbered,\n}\n\nimpl ToolbarItem {\n    pub fn id(&self) -> String {\n        self.id.clone()\n    }\n\n    pub fn set_id(&mut self, id: String) {\n        self.id = id;\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(untagged)]\npub enum ToolbarItem2 {\n    Plugin(PluginId),\n    Inline(Box<ToolbarItem>),\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct ToolbarState {\n    /// Whether the reordering possible on the toolbar\n    pub is_reorder_disabled: bool,\n    /// Items to be displayed in the toolbar\n    pub left: Vec<ToolbarItem2>,\n    /// Items to be displayed in the toolbar\n    pub center: Vec<ToolbarItem2>,\n    /// Items to be displayed in the toolbar\n    pub right: Vec<ToolbarItem2>,\n}\n\nimpl ToolbarState {\n    fn migrate_plugin_id(id: PluginId) -> PluginId {\n        match id.as_str() {\n            \"@default/system-tray\" => \"@seelen/tb-system-tray\".into(),\n            \"@default/quick-settings\" => \"@seelen/tb-quick-settings\".into(),\n            \"@default/bluetooth\" => \"@seelen/tb-bluetooth-popup\".into(),\n            \"@default/keyboard\" => \"@seelen/tb-keyboard-selector\".into(),\n            \"@default/user\" => \"@seelen/tb-user-menu\".into(),\n            \"@default/network\" => \"@seelen/tb-network-popup\".into(),\n            \"@default/date\" => \"@seelen/tb-calendar-popup\".into(),\n            \"@default/media\" => \"@seelen/tb-media-popup\".into(),\n            \"@default/notifications\" => \"@seelen/tb-notifications\".into(),\n            _ => id,\n        }\n    }\n\n    fn sanitize_items(dict: &mut HashSet<String>, items: Vec<ToolbarItem2>) -> Vec<ToolbarItem2> {\n        let mut result = Vec::new();\n        for item in items {\n            match item {\n                ToolbarItem2::Plugin(id) => {\n                    let id = Self::migrate_plugin_id(id);\n                    let str_id = id.to_string();\n\n                    if !dict.contains(&str_id) && id.is_valid() {\n                        dict.insert(str_id);\n                        result.push(ToolbarItem2::Plugin(id));\n                    }\n                }\n                ToolbarItem2::Inline(mut item) => {\n                    // migration step for old default separator before v2.5\n                    if item.template.contains(\"window\") && item.scopes.is_empty() {\n                        item.scopes.insert(ToolbarJsScope::FocusedApp);\n                        item.template = item.template.replace(\"window\", \"focusedApp\");\n                    }\n\n                    if item.id().is_empty() {\n                        item.set_id(uuid::Uuid::new_v4().to_string());\n                    }\n\n                    if !dict.contains(&item.id()) {\n                        dict.insert(item.id());\n                        result.push(ToolbarItem2::Inline(item));\n                    }\n                }\n            }\n        }\n        result\n    }\n\n    pub fn sanitize(&mut self) {\n        let mut dict = HashSet::new();\n        self.left = Self::sanitize_items(&mut dict, std::mem::take(&mut self.left));\n        self.center = Self::sanitize_items(&mut dict, std::mem::take(&mut self.center));\n        self.right = Self::sanitize_items(&mut dict, std::mem::take(&mut self.right));\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/plugin/mod.rs",
    "content": "pub mod value;\n\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\nuse value::PluginValue;\n\nuse crate::resource::{PluginId, ResourceKind, ResourceMetadata, SluResource};\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Plugin {\n    pub id: PluginId,\n    #[serde(default)]\n    pub metadata: ResourceMetadata,\n    /// Optional icon to be used on settings. This have to be a valid react icon name.\\\n    /// You can find all icons here: https://react-icons.github.io/react-icons/.\n    #[serde(default = \"Plugin::default_icon\")]\n    pub icon: String,\n    #[serde(flatten)]\n    pub plugin: PluginValue,\n}\n\nimpl SluResource for Plugin {\n    const KIND: ResourceKind = ResourceKind::Plugin;\n\n    fn metadata(&self) -> &ResourceMetadata {\n        &self.metadata\n    }\n\n    fn metadata_mut(&mut self) -> &mut ResourceMetadata {\n        &mut self.metadata\n    }\n}\n\nimpl Plugin {\n    pub fn default_icon() -> String {\n        \"PiPuzzlePieceDuotone\".to_string()\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/plugin/mod.ts",
    "content": "import { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../../handlers/mod.ts\";\nimport { List } from \"../../utils/List.ts\";\nimport { newFromInvoke, newOnEvent } from \"../../utils/State.ts\";\nimport type { Plugin } from \"@seelen-ui/types\";\nimport { Widget } from \"../widget/mod.ts\";\n\nexport class PluginList extends List<Plugin> {\n  static getAsync(): Promise<PluginList> {\n    return newFromInvoke(this, SeelenCommand.StateGetPlugins);\n  }\n\n  static onChange(cb: (payload: PluginList) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.StatePluginsChanged);\n  }\n\n  forCurrentWidget(): Plugin[] {\n    const target = Widget.self.id;\n    return this.inner.filter((plugin) => plugin.target === target);\n  }\n}\n"
  },
  {
    "path": "libs/core/src/state/plugin/value.rs",
    "content": "use schemars::JsonSchema;\n\nuse crate::{\n    resource::WidgetId,\n    state::{ToolbarItem, WindowManagerLayout},\n    utils::TsUnknown,\n};\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(tag = \"target\", content = \"plugin\")]\npub enum KnownPlugin {\n    #[serde(rename = \"@seelen/fancy-toolbar\")]\n    FacyToolbar(ToolbarItem),\n    #[serde(rename = \"@seelen/window-manager\")]\n    WManager(WindowManagerLayout),\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\npub struct ThirdPartyPlugin {\n    /// The friendly id of the widget that will use this plugin\n    /// example: `@username/widget-name`\n    target: WidgetId,\n    /// The plugin data, this can be anything and depends on the widget using this plugin\n    /// to handle it, parse it and use it.\n    plugin: TsUnknown,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(untagged)]\npub enum PluginValue {\n    Known(Box<KnownPlugin>),\n    Any(ThirdPartyPlugin),\n}\n"
  },
  {
    "path": "libs/core/src/state/popups/mod.rs",
    "content": "use std::collections::HashMap;\n\nuse url::Url;\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct SluPopupConfig {\n    pub width: f64,\n    pub height: f64,\n    pub title: Vec<SluPopupContent>,\n    pub content: Vec<SluPopupContent>,\n    pub footer: Vec<SluPopupContent>,\n}\n\nimpl Default for SluPopupConfig {\n    fn default() -> Self {\n        Self {\n            width: 400.0,\n            height: 200.0,\n            title: vec![],\n            content: vec![],\n            footer: vec![],\n        }\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(\n    tag = \"type\",\n    rename_all = \"camelCase\",\n    rename_all_fields = \"camelCase\"\n)]\npub enum SluPopupContent {\n    Text {\n        value: String,\n        styles: Option<CssStyles>,\n    },\n    Icon {\n        /// react icon name. ex: `FaGithub`\n        name: String,\n        styles: Option<CssStyles>,\n    },\n    Image {\n        href: Url,\n        styles: Option<CssStyles>,\n    },\n    Button {\n        inner: Vec<SluPopupContent>,\n        styles: Option<CssStyles>,\n        /// event name to be emitted on click ex: `test::clicked`\n        on_click: String,\n    },\n    Group {\n        items: Vec<SluPopupContent>,\n        styles: Option<CssStyles>,\n    },\n}\n\nimpl SluPopupContent {\n    pub fn set_styles(&mut self, new_styles: CssStyles) {\n        match self {\n            SluPopupContent::Text { styles, .. } => *styles = Some(new_styles),\n            SluPopupContent::Icon { styles, .. } => *styles = Some(new_styles),\n            SluPopupContent::Image { styles, .. } => *styles = Some(new_styles),\n            SluPopupContent::Button { styles, .. } => *styles = Some(new_styles),\n            SluPopupContent::Group { styles, .. } => *styles = Some(new_styles),\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\npub struct CssStyles(HashMap<String, String>);\n\nimpl CssStyles {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    pub fn add(mut self, key: &str, value: &str) -> Self {\n        self.0.insert(key.to_string(), value.to_string());\n        self\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/by_monitor.rs",
    "content": "use schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse ts_rs::TS;\n\nuse crate::{\n    resource::WidgetId,\n    state::{by_widget::ThirdPartyWidgetSettings, WorkspaceId},\n};\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\npub struct MonitorSettingsByWidget(HashMap<WidgetId, ThirdPartyWidgetSettings>);\n\nimpl MonitorSettingsByWidget {\n    pub fn is_widget_enabled(&self, widget_id: &WidgetId) -> bool {\n        self.0\n            .get(widget_id)\n            .is_none_or(|settings| settings.enabled)\n    }\n\n    pub fn remove(&mut self, widget_id: &WidgetId) -> Option<ThirdPartyWidgetSettings> {\n        self.0.remove(widget_id)\n    }\n\n    pub fn insert(&mut self, widget_id: WidgetId, settings: ThirdPartyWidgetSettings) {\n        self.0.insert(widget_id, settings);\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct MonitorConfiguration {\n    /// dictionary of settings by widget\n    pub by_widget: MonitorSettingsByWidget,\n    /// Id of the wallpaper collection to use in this monitor.\\\n    /// If not set, the default wallpaper collection will be used.\n    pub wallpaper_collection: Option<uuid::Uuid>,\n    /// dictionary of settings by workspace on this monitor\n    pub by_workspace: HashMap<WorkspaceId, WorkspaceConfiguration>,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WorkspaceConfiguration {\n    /// Id of the wallpaper collection to use in this workspace.\\\n    /// If not set, the monitor's wallpaper collection will be used.\n    pub wallpaper_collection: Option<uuid::Uuid>,\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/by_theme.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::state::config::CssVariableName;\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\npub struct ThemeSettings(HashMap<CssVariableName, String>);\n"
  },
  {
    "path": "libs/core/src/state/settings/by_wallpaper.rs",
    "content": "#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WallpaperInstanceSettings {\n    /// playback speed for video backgrounds\n    pub playback_speed: PlaybackSpeed,\n    /// will flip the image/video vertically\n    pub flip_vertical: bool,\n    /// will flip the image/video horizontally\n    pub flip_horizontal: bool,\n    /// blur factor to apply to the image\n    pub blur: u32,\n    /// method to fill the monitor background\n    pub object_fit: ObjectFit,\n    /// position of the background\n    pub object_position: ObjectPosition,\n    /// number between 0 and 2\n    pub saturation: f32,\n    /// number between 0 and 2\n    pub contrast: f32,\n    /// will overlay the image/video with a color filter\n    pub with_overlay: bool,\n    pub overlay_mix_blend_mode: MixBlendMode,\n    pub overlay_color: String,\n    /// mute video backgrounds\n    pub muted: bool,\n}\n\nimpl Default for WallpaperInstanceSettings {\n    fn default() -> Self {\n        Self {\n            playback_speed: PlaybackSpeed::default(),\n            flip_vertical: false,\n            flip_horizontal: false,\n            blur: 0,\n            object_fit: ObjectFit::default(),\n            object_position: ObjectPosition::default(),\n            saturation: 1.0,\n            contrast: 1.0,\n            with_overlay: false,\n            overlay_mix_blend_mode: MixBlendMode::default(),\n            overlay_color: \"#ff0000\".to_string(),\n            muted: true,\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(repr(enum = name))]\npub enum ObjectFit {\n    Fill,\n    Contain,\n    #[default]\n    Cover,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(repr(enum = name))]\npub enum ObjectPosition {\n    Top,\n    #[default]\n    Center,\n    Bottom,\n    Left,\n    Right,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"kebab-case\")]\n#[ts(repr(enum = name))]\npub enum MixBlendMode {\n    Normal,\n    #[default]\n    Multiply,\n    Screen,\n    Overlay,\n    Darken,\n    Lighten,\n    ColorDodge,\n    ColorBurn,\n    HardLight,\n    SoftLight,\n    Difference,\n    Exclusion,\n    Hue,\n    Saturation,\n    Color,\n    Luminosity,\n    PlusDarker,\n    PlusLighter,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(repr(enum = name))]\npub enum PlaybackSpeed {\n    XDot25,\n    XDot5,\n    XDot75,\n    #[default]\n    X1,\n    X1Dot25,\n    X1Dot5,\n    X1Dot75,\n    X2,\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/by_widget.rs",
    "content": "use std::collections::HashMap;\n\nuse schemars::JsonSchema;\nuse uuid::Uuid;\n\nuse crate::{resource::WidgetId, utils::TsUnknown};\n\nuse super::{FancyToolbarSettings, SeelenWallSettings, SeelenWegSettings, WindowManagerSettings};\n\n#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default)]\npub struct SettingsByWidget {\n    #[serde(rename = \"@seelen/weg\")]\n    pub weg: SeelenWegSettings,\n    #[serde(rename = \"@seelen/fancy-toolbar\")]\n    pub fancy_toolbar: FancyToolbarSettings,\n    #[serde(rename = \"@seelen/window-manager\")]\n    pub wm: WindowManagerSettings,\n    #[serde(rename = \"@seelen/wallpaper-manager\")]\n    pub wall: SeelenWallSettings,\n    #[serde(flatten)]\n    pub others: HashMap<WidgetId, ThirdPartyWidgetSettings>,\n}\n\nimpl SettingsByWidget {\n    pub fn is_enabled(&self, widget_id: &WidgetId) -> bool {\n        match widget_id.as_str() {\n            \"@seelen/weg\" => self.weg.enabled,\n            \"@seelen/fancy-toolbar\" => self.fancy_toolbar.enabled,\n            \"@seelen/window-manager\" => self.wm.enabled,\n            \"@seelen/wallpaper-manager\" => self.wall.enabled,\n            _ => match self.others.get(widget_id) {\n                Some(settings) => settings.enabled,\n                // only official widgets are enabled by default\n                None => widget_id.starts_with(\"@seelen\"),\n            },\n        }\n    }\n\n    pub fn set_enabled(&mut self, widget_id: &WidgetId, enabled: bool) {\n        match widget_id.as_str() {\n            \"@seelen/weg\" => self.weg.enabled = enabled,\n            \"@seelen/fancy-toolbar\" => self.fancy_toolbar.enabled = enabled,\n            \"@seelen/window-manager\" => self.wm.enabled = enabled,\n            \"@seelen/wallpaper-manager\" => self.wall.enabled = enabled,\n            _ => match self.others.entry(widget_id.clone()) {\n                std::collections::hash_map::Entry::Occupied(mut o) => {\n                    o.get_mut().enabled = enabled;\n                }\n                std::collections::hash_map::Entry::Vacant(v) => {\n                    v.insert(ThirdPartyWidgetSettings {\n                        enabled,\n                        ..Default::default()\n                    });\n                }\n            },\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default)]\npub struct ThirdPartyWidgetSettings {\n    /// Enable or disable the widget\n    pub enabled: bool,\n    /// By intance will be used to store settings in case of multiple instances allowed on widget.\\\n    /// The map values will be merged with the root object and default values on settings declaration.\n    #[serde(rename = \"$instances\")]\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    #[ts(optional = nullable)]\n    pub instances: Option<HashMap<Uuid, HashMap<String, TsUnknown>>>,\n    #[serde(flatten)]\n    pub rest: HashMap<String, TsUnknown>,\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/mod.rs",
    "content": "/* In this file we use #[serde_alias(SnakeCase)] as backward compatibility from versions below v1.9.8 */\npub mod by_monitor;\npub mod by_theme;\npub mod by_wallpaper;\npub mod by_widget;\npub mod settings_by_app;\npub mod shortcuts;\n\npub use settings_by_app::*;\n\nuse std::collections::{HashMap, HashSet};\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::Path;\n\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse serde_alias::serde_alias;\nuse ts_rs::TS;\n\nuse crate::resource::WidgetId;\nuse crate::state::WallpaperCollection;\nuse crate::system_state::MonitorId;\nuse crate::{\n    error::Result,\n    rect::Rect,\n    resource::{IconPackId, PluginId, ThemeId, WallpaperId},\n    state::{\n        by_monitor::MonitorConfiguration, by_theme::ThemeSettings,\n        by_wallpaper::WallpaperInstanceSettings, by_widget::SettingsByWidget,\n        shortcuts::SluShortcutsSettings,\n    },\n};\n\n// ============== Fancy Toolbar Settings ==============\n\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct FancyToolbarSettings {\n    /// enable or disable the fancy toolbar\n    pub enabled: bool,\n    /// item size in px\n    pub item_size: u32,\n    /// Toolbar margin in px\n    pub margin: u32,\n    /// Toolbar padding in px\n    pub padding: u32,\n    /// position of the toolbar\n    pub position: FancyToolbarSide,\n    /// hide mode\n    pub hide_mode: HideMode,\n    /// delay to show the toolbar on Mouse Hover in milliseconds\n    pub delay_to_show: u32,\n    /// delay to hide the toolbar on Mouse Leave in milliseconds\n    pub delay_to_hide: u32,\n}\n\nimpl Default for FancyToolbarSettings {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            item_size: 16,\n            padding: 8,\n            margin: 0,\n            position: FancyToolbarSide::Top,\n            hide_mode: HideMode::Never,\n            delay_to_show: 100,\n            delay_to_hide: 800,\n        }\n    }\n}\n\nimpl FancyToolbarSettings {\n    /// total height of the toolbar\n    pub fn total_size(&self) -> u32 {\n        self.item_size + (self.padding * 2) + (self.margin * 2)\n    }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum FancyToolbarSide {\n    Top,\n    Bottom,\n}\n\n// ============== SeelenWeg Settings ==============\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum SeelenWegMode {\n    #[serde(alias = \"Full-Width\")]\n    FullWidth,\n    #[serde(alias = \"Min-Content\")]\n    MinContent,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WegTemporalItemsVisibility {\n    All,\n    OnMonitor,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WegPinnedItemsVisibility {\n    Always,\n    WhenPrimary,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum HideMode {\n    /// never hide\n    Never,\n    /// auto-hide always on\n    Always,\n    /// auto-hide only if is overlaped by the focused window\n    #[serde(alias = \"On-Overlap\")]\n    OnOverlap,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum SeelenWegSide {\n    Left,\n    Right,\n    Top,\n    Bottom,\n}\n\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct SeelenWegSettings {\n    /// enable or disable the seelenweg\n    pub enabled: bool,\n    /// Dock/Taskbar mode\n    pub mode: SeelenWegMode,\n    /// When to hide the dock\n    pub hide_mode: HideMode,\n    /// Split windows into separated items instead of grouped.\n    pub split_windows: bool,\n    /// Which temporal items to show on the dock instance (this can be overridden per monitor)\n    pub temporal_items_visibility: WegTemporalItemsVisibility,\n    /// Determines is the pinned item should be shown or not (this can be overridden per monitor).\n    pub pinned_items_visibility: WegPinnedItemsVisibility,\n    /// Dock position\n    pub position: SeelenWegSide,\n    /// enable or disable the instance counter visibility on weg instance\n    pub show_instance_counter: bool,\n    /// enable or disable the window title visibility for opened apps\n    pub show_window_title: bool,\n    /// enable or disable separators visibility\n    pub visible_separators: bool,\n    /// item size in px\n    pub size: u32,\n    /// zoomed item size in px\n    pub zoom_size: u32,\n    /// Dock/Taskbar margin in px\n    pub margin: u32,\n    /// Dock/Taskbar padding in px\n    pub padding: u32,\n    /// space between items in px\n    pub space_between_items: u32,\n    /// delay to show the toolbar on Mouse Hover in milliseconds\n    pub delay_to_show: u32,\n    /// delay to hide the toolbar on Mouse Leave in milliseconds\n    pub delay_to_hide: u32,\n    /// show end task button on context menu (needs developer mode enabled)\n    pub show_end_task: bool,\n}\n\nimpl Default for SeelenWegSettings {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            mode: SeelenWegMode::MinContent,\n            hide_mode: HideMode::OnOverlap,\n            position: SeelenWegSide::Bottom,\n            visible_separators: true,\n            show_instance_counter: true,\n            show_window_title: false,\n            temporal_items_visibility: WegTemporalItemsVisibility::All,\n            pinned_items_visibility: WegPinnedItemsVisibility::Always,\n            size: 40,\n            zoom_size: 70,\n            margin: 8,\n            padding: 8,\n            space_between_items: 8,\n            delay_to_show: 100,\n            delay_to_hide: 800,\n            show_end_task: false,\n            split_windows: false,\n        }\n    }\n}\n\nimpl SeelenWegSettings {\n    /// total height or width of the dock, depending on the Position\n    pub fn total_size(&self) -> u32 {\n        self.size + (self.padding * 2) + (self.margin * 2)\n    }\n}\n\n// ============== Window Manager Settings ==============\n\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WindowManagerSettings {\n    /// enable or disable the tiling window manager\n    pub enabled: bool,\n    /// enable or disable auto stacking by category\n    pub auto_stacking_by_category: bool,\n    /// window manager border\n    pub border: Border,\n    /// the resize size in % to be used when resizing via cli\n    pub resize_delta: f32,\n    /// default gap between containers\n    pub workspace_gap: u32,\n    /// default workspace padding\n    pub workspace_padding: u32,\n    /// default workspace margin\n    pub workspace_margin: Rect,\n    /// floating window settings\n    pub floating: FloatingWindowSettings,\n    /// default layout\n    pub default_layout: PluginId,\n    /// window manager animations\n    pub animations: WmAnimations,\n    /// window manager drag behavior\n    pub drag_behavior: WmDragBehavior,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WmDragBehavior {\n    /// While dragging the windows on the layout will be sorted.\n    Sort,\n    /// On drag end the dragged and the overlaped will be swapped.\n    Swap,\n}\n\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct Border {\n    pub enabled: bool,\n    pub width: f64,\n    pub offset: f64,\n}\n\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct FloatingWindowSettings {\n    pub width: f64,\n    pub height: f64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WmAnimations {\n    pub enabled: bool,\n    pub duration_ms: u64,\n    pub ease_function: String,\n}\n\nimpl Default for WmAnimations {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            duration_ms: 200,\n            ease_function: \"EaseOut\".into(),\n        }\n    }\n}\n\nimpl Default for Border {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            offset: 0.0,\n            width: 3.0,\n        }\n    }\n}\n\nimpl Default for FloatingWindowSettings {\n    fn default() -> Self {\n        Self {\n            width: 800.0,\n            height: 500.0,\n        }\n    }\n}\n\nimpl Default for WindowManagerSettings {\n    fn default() -> Self {\n        Self {\n            enabled: false,\n            auto_stacking_by_category: true,\n            border: Border::default(),\n            resize_delta: 10.0,\n            workspace_gap: 10,\n            workspace_padding: 10,\n            workspace_margin: Rect::default(),\n            floating: FloatingWindowSettings::default(),\n            default_layout: \"@default/wm-bspwm\".into(),\n            animations: WmAnimations::default(),\n            drag_behavior: WmDragBehavior::Sort,\n        }\n    }\n}\n\n// ================= Seelen Wall ================\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum MultimonitorBehaviour {\n    /// Each monitor has its own wallpaper\n    PerMonitor,\n    /// Single wallpaper extended across all monitors\n    Extend,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct SeelenWallSettings {\n    pub enabled: bool,\n    /// update interval in seconds\n    pub interval: u32,\n    /// randomize order\n    pub randomize: bool,\n    /// collection id, if none default wallpaper will be used\n    pub default_collection: Option<uuid::Uuid>,\n    /// multimonitor behaviour\n    pub multimonitor_behaviour: MultimonitorBehaviour,\n    /// deprecated, this field will be removed on v3\n    #[serde(alias = \"backgroundsV2\")]\n    pub deprecated_bgs: Option<Vec<WallpaperId>>,\n}\n\nimpl Default for SeelenWallSettings {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            interval: 300, // 5min\n            randomize: false,\n            default_collection: None,\n            multimonitor_behaviour: MultimonitorBehaviour::PerMonitor,\n            deprecated_bgs: None,\n        }\n    }\n}\n\n// ========================== Seelen Updates ==============================\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum UpdateChannel {\n    Release,\n    Beta,\n    Nightly,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct UpdaterSettings {\n    pub channel: UpdateChannel,\n}\n\nimpl Default for UpdaterSettings {\n    fn default() -> Self {\n        Self {\n            channel: UpdateChannel::Release,\n        }\n    }\n}\n\n// ========================== Start of Week ==============================\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\n#[derive(Default)]\npub enum StartOfWeek {\n    #[default]\n    Monday,\n    Sunday,\n    Saturday,\n}\n\n// ======================== Final Settings Struct ===============================\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Settings {\n    pub by_app: AppsConfigurationList,\n    /// list of monitors and their configurations\n    pub monitors_v3: HashMap<MonitorId, MonitorConfiguration>,\n    /// app shortcuts settings\n    pub shortcuts: SluShortcutsSettings,\n    /// list of selected themes as filename as backguard compatibility for versions before v2.3.8, will be removed in v3\n    #[serde(alias = \"selectedThemes\")]\n    pub old_active_themes: Vec<String>,\n    /// list of selected themes\n    pub active_themes: Vec<ThemeId>,\n    /// list of selected icon packs\n    pub active_icon_packs: Vec<IconPackId>,\n    /// enable or disable dev tools tab in settings\n    pub dev_tools: bool,\n    /// discord rich presence\n    pub drpc: bool,\n    /// language to use, if null the system locale is used\n    pub language: Option<String>,\n    /// MomentJS date format\n    pub date_format: String,\n    /// Start of week for calendar\n    pub start_of_week: StartOfWeek,\n    /// Updater Settings\n    pub updater: UpdaterSettings,\n    /// Custom settings for widgets\n    pub by_widget: SettingsByWidget,\n    /// Custom variables for themes by theme id\n    /// ### example\n    /// ```json\n    /// {\n    ///     \"@username/themeName\": {\n    ///         \"--css-variable-name\": \"123px\",\n    ///         \"--css-variable-name2\": \"#aabbccaa\",\n    ///     }\n    /// }\n    /// ```\n    pub by_theme: HashMap<ThemeId, ThemeSettings>,\n    /// settings for each background\n    pub by_wallpaper: HashMap<WallpaperId, WallpaperInstanceSettings>,\n    /// list of wallpaper collections\n    pub wallpaper_collections: Vec<WallpaperCollection>,\n    /// Performance options\n    pub performance_mode: PerformanceModeSettings,\n    /// enable or disable hardware acceleration\n    pub hardware_acceleration: bool,\n    /// interval to poll for system resources like cpu, memory, network usage, etc, in seconds.\n    pub polling_interval: u64,\n}\n\nimpl Default for Settings {\n    fn default() -> Self {\n        Self {\n            by_app: AppsConfigurationList::default(),\n            performance_mode: PerformanceModeSettings::default(),\n            shortcuts: SluShortcutsSettings::default(),\n            drpc: false,\n            old_active_themes: Vec::new(),\n            active_themes: vec![\"@default/theme\".into()],\n            active_icon_packs: vec![\"@system/icon-pack\".into()],\n            monitors_v3: HashMap::new(),\n            dev_tools: false,\n            language: Some(Self::get_system_language()),\n            date_format: \"ddd D MMM, hh:mm A\".to_owned(),\n            start_of_week: StartOfWeek::default(),\n            updater: UpdaterSettings::default(),\n            by_widget: SettingsByWidget::default(),\n            by_theme: HashMap::new(),\n            by_wallpaper: HashMap::new(),\n            wallpaper_collections: Vec::new(),\n            hardware_acceleration: true,\n            polling_interval: 3,\n        }\n    }\n}\n\nimpl Settings {\n    pub fn get_locale() -> Option<String> {\n        sys_locale::get_locale()\n    }\n\n    pub fn get_system_language() -> String {\n        match sys_locale::get_locale() {\n            Some(l) => l.split('-').next().unwrap_or(\"en\").to_string(),\n            None => \"en\".to_string(),\n        }\n    }\n\n    pub fn migrate(&mut self) -> Result<()> {\n        // Migrate step for v2.5\n        if let Some(backgrounds) = self.by_widget.wall.deprecated_bgs.take() {\n            if !backgrounds.is_empty() {\n                let collection = WallpaperCollection {\n                    id: uuid::Uuid::new_v4(),\n                    name: \"Migrated\".to_string(),\n                    wallpapers: backgrounds,\n                };\n\n                // Set as default collection if no default is set\n                if self.by_widget.wall.default_collection.is_none() {\n                    self.by_widget.wall.default_collection = Some(collection.id);\n                }\n\n                self.wallpaper_collections.push(collection);\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn dedup_themes(&mut self) {\n        let mut seen = HashSet::new();\n        self.active_themes.retain(|x| seen.insert(x.clone())); // dedup\n    }\n\n    pub fn dedup_icon_packs(&mut self) {\n        let mut seen = HashSet::new();\n        self.active_icon_packs.retain(|x| seen.insert(x.clone())); // dedup\n    }\n\n    pub fn sanitize(&mut self) -> Result<()> {\n        if self.language.is_none() {\n            self.language = Some(Self::get_system_language());\n        }\n\n        // ensure base is always selected\n        self.active_themes.insert(0, \"@default/theme\".into());\n        self.dedup_themes();\n        // ensure base is always selected\n        self.active_icon_packs.insert(0, \"@system/icon-pack\".into());\n        self.dedup_icon_packs();\n\n        self.shortcuts.sanitize();\n        self.by_app.prepare();\n\n        self.polling_interval = self.polling_interval.max(1);\n        Ok(())\n    }\n\n    pub fn load(path: impl AsRef<Path>) -> Result<Self> {\n        let path = path.as_ref();\n\n        let mut settings: Self = {\n            let file = File::open(path)?;\n            file.lock_shared()?;\n            serde_json::from_reader(&file)?\n        };\n\n        // Load shortcuts from sibling file if it exists\n        if let (Some(parent), Some(stem)) = (path.parent(), path.file_stem()) {\n            let path = parent.join(format!(\"{}_shortcuts.json\", stem.to_string_lossy()));\n            if path.exists() {\n                let file = File::open(&path)?;\n                file.lock_shared()?;\n                settings.shortcuts = serde_json::from_reader(&file)?;\n            }\n\n            let path = parent.join(format!(\"{}_by_app.yml\", stem.to_string_lossy()));\n            if path.exists() {\n                let file = File::open(&path)?;\n                file.lock_shared()?;\n                settings.by_app = serde_yaml::from_reader(&file)?;\n            }\n        }\n\n        settings.migrate()?;\n        settings.sanitize()?;\n        Ok(settings)\n    }\n\n    pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {\n        let path = path.as_ref();\n\n        {\n            // Create a copy without splitted fields\n            let mut settings_copy = serde_json::to_value(self)?;\n            let obj = settings_copy.as_object_mut().unwrap();\n            obj.remove(\"shortcuts\");\n            obj.remove(\"byApp\");\n\n            let mut file = File::create(path)?;\n            file.lock()?;\n            serde_json::to_writer_pretty(&file, &settings_copy)?;\n            file.flush()?;\n        }\n\n        // Save shortcuts to sibling file\n        if let (Some(parent), Some(stem)) = (path.parent(), path.file_stem()) {\n            let shortcuts_path = parent.join(format!(\"{}_shortcuts.json\", stem.to_string_lossy()));\n            let mut shortcuts_file = File::create(&shortcuts_path)?;\n            shortcuts_file.lock()?;\n            serde_json::to_writer_pretty(&shortcuts_file, &self.shortcuts)?;\n            shortcuts_file.flush()?;\n\n            let by_app_path = parent.join(format!(\"{}_by_app.yml\", stem.to_string_lossy()));\n            let mut by_app_file = File::create(&by_app_path)?;\n            by_app_file.lock()?;\n            serde_yaml::to_writer(&by_app_file, &self.by_app)?;\n            by_app_file.flush()?;\n        }\n\n        Ok(())\n    }\n\n    /// This indicates if the widget is enabled on general, doesn't take in care multi-instances\n    pub fn is_widget_enabled(&self, widget_id: &WidgetId) -> bool {\n        self.by_widget.is_enabled(widget_id)\n    }\n\n    pub fn set_widget_enabled(&mut self, widget_id: &WidgetId, enabled: bool) {\n        self.by_widget.set_enabled(widget_id, enabled);\n    }\n\n    pub fn is_widget_enabled_on_monitor(\n        &self,\n        widget_id: &WidgetId,\n        monitor_id: &MonitorId,\n    ) -> bool {\n        if !self.is_widget_enabled(widget_id) {\n            return false;\n        }\n        // default to true as new connected monitors should be enabled\n        self.monitors_v3\n            .get(monitor_id)\n            .is_none_or(|monitor_config| monitor_config.by_widget.is_widget_enabled(widget_id))\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct PerformanceModeSettings {\n    pub default: PerformanceMode,\n    pub on_battery: PerformanceMode,\n    pub on_energy_saver: PerformanceMode,\n}\n\nimpl Default for PerformanceModeSettings {\n    fn default() -> Self {\n        Self {\n            default: PerformanceMode::Disabled,\n            on_battery: PerformanceMode::Minimal,\n            on_energy_saver: PerformanceMode::Extreme,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum PerformanceMode {\n    /// Does nothing, all animations are enabled.\n    Disabled,\n    /// Disables windows animations and other heavy effects.\n    Minimal,\n    /// Disables all the animations.\n    Extreme,\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/mod.ts",
    "content": "import { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../../handlers/mod.ts\";\n\nimport type { Settings as ISettings, ThirdPartyWidgetSettings } from \"@seelen-ui/types\";\nimport { newFromInvoke, newOnEvent } from \"../../utils/State.ts\";\nimport { invoke } from \"../../handlers/mod.ts\";\nimport { Widget } from \"../widget/mod.ts\";\n\nexport interface Settings extends ISettings {}\nexport class Settings {\n  constructor(public inner: ISettings) {\n    Object.assign(this, this.inner);\n  }\n\n  static default(): Promise<Settings> {\n    return newFromInvoke(this, SeelenCommand.StateGetDefaultSettings);\n  }\n\n  static getAsync(): Promise<Settings> {\n    return newFromInvoke(this, SeelenCommand.StateGetSettings);\n  }\n\n  static onChange(cb: (payload: Settings) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.StateSettingsChanged);\n  }\n\n  static loadCustom(path: string): Promise<Settings> {\n    return newFromInvoke(this, SeelenCommand.StateGetSettings, { path });\n  }\n\n  /**\n   * Returns the settings for the current widget, taking in care of the replicas\n   * the returned object will be a merge of:\n   * - the default settings set on the widget definition\n   * - the stored user settings\n   * - the instance patch settings (if apply)\n   * - the monitor patch settings (if apply)\n   */\n  getCurrentWidgetConfig(): ThirdPartyWidgetSettings {\n    const currentWidget = Widget.getCurrent();\n\n    const widgetId = currentWidget.id;\n    const { monitorId, instanceId } = currentWidget.decoded;\n\n    const root = this.inner.byWidget[widgetId];\n    const instance = instanceId ? root?.$instances?.[instanceId] : undefined;\n    const monitor = monitorId ? this.inner.monitorsV3[monitorId]?.byWidget[widgetId] : undefined;\n\n    return {\n      ...currentWidget.getDefaultConfig(),\n      ...(root || {}),\n      ...(instance || {}),\n      ...(monitor || {}),\n    };\n  }\n\n  /** Will store the settings on disk */\n  save(): Promise<void> {\n    return invoke(SeelenCommand.StateWriteSettings, { settings: this.inner });\n  }\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/settings_by_app.rs",
    "content": "use regex::Regex;\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse serde_alias::serde_alias;\nuse ts_rs::TS;\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum AppExtraFlag {\n    /// Mark this app as non interactive window.\n    #[serde(alias = \"no-interactive\")]\n    NoInteractive,\n    /// Start the app in the center of the screen as floating in the wm.\n    #[serde(alias = \"float\", alias = \"wm-float\")]\n    WmFloat,\n    /// Forces the management of this app in the wm. (only if it is interactable and not pinned)\n    #[serde(alias = \"force\", alias = \"wm-force\")]\n    WmForce,\n    /// Unmanage this app in the wm.\n    #[serde(alias = \"unmanage\", alias = \"wm-unmanage\")]\n    WmUnmanage,\n    /// Pin this app in all the virtual desktops in the wm.\n    #[serde(alias = \"pinned\", alias = \"vd-pinned\")]\n    VdPinned,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum AppIdentifierType {\n    #[serde(alias = \"exe\")]\n    Exe,\n    #[serde(alias = \"class\")]\n    Class,\n    #[serde(alias = \"title\")]\n    Title,\n    #[serde(alias = \"path\")]\n    Path,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum MatchingStrategy {\n    #[serde(alias = \"equals\", alias = \"legacy\", alias = \"Legacy\")]\n    Equals,\n    #[serde(alias = \"startsWith\")]\n    StartsWith,\n    #[serde(alias = \"endsWith\")]\n    EndsWith,\n    #[serde(alias = \"contains\")]\n    Contains,\n    #[serde(alias = \"regex\")]\n    Regex,\n}\n\n#[serde_alias(SnakeCase)]\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct AppIdentifier {\n    /// Depending of the kind this can be case sensitive or not.\n    /// - `class` and `title` are case sensitive\n    /// - `exe` and `path` are case insensitive\n    pub id: String,\n    /// the way to match the application\n    pub kind: AppIdentifierType,\n    /// the strategy to use to determine if id matches with the application\n    pub matching_strategy: MatchingStrategy,\n    #[serde(default)]\n    pub negation: bool,\n    #[serde(default)]\n    pub and: Vec<AppIdentifier>,\n    #[serde(default)]\n    pub or: Vec<AppIdentifier>,\n    #[serde(skip)]\n    cache: AppIdentifierCache,\n}\n\n/// this struct is intented to improve performance\n#[derive(Debug, Default, Clone)]\npub struct AppIdentifierCache {\n    pub regex: Option<Regex>,\n    pub lower_id: Option<String>,\n}\n\nimpl AppIdentifier {\n    fn prepare(&mut self) {\n        if matches!(self.matching_strategy, MatchingStrategy::Regex) {\n            let result = Regex::new(&self.id);\n            if let Ok(re) = result {\n                self.cache.regex = Some(re);\n            }\n        }\n        if matches!(self.kind, AppIdentifierType::Path | AppIdentifierType::Exe) {\n            // Normalize path separators to backslash and uppercase for Windows paths\n            let normalized = self.id.replace('\\\\', \"/\");\n            self.cache.lower_id = Some(normalized.to_lowercase());\n        }\n\n        self.and.iter_mut().for_each(|i| i.prepare());\n        self.or.iter_mut().for_each(|i| i.prepare());\n    }\n\n    fn lower_id(&self) -> &str {\n        self.cache.lower_id.as_deref().unwrap()\n    }\n\n    /// path and filenames on Windows System should be uppercased before be passed to this function\n    /// Safety: will panic if cache was not performed before\n    fn validate(&self, title: &str, class: &str, exe: &str, path: &str) -> bool {\n        let mut self_result = match self.matching_strategy {\n            MatchingStrategy::Equals => match self.kind {\n                AppIdentifierType::Title => title.eq(&self.id),\n                AppIdentifierType::Class => class.eq(&self.id),\n                AppIdentifierType::Exe => exe.eq(self.lower_id()),\n                AppIdentifierType::Path => path.eq(self.lower_id()),\n            },\n            MatchingStrategy::StartsWith => match self.kind {\n                AppIdentifierType::Title => title.starts_with(&self.id),\n                AppIdentifierType::Class => class.starts_with(&self.id),\n                AppIdentifierType::Exe => exe.starts_with(self.lower_id()),\n                AppIdentifierType::Path => path.starts_with(self.lower_id()),\n            },\n            MatchingStrategy::EndsWith => match self.kind {\n                AppIdentifierType::Title => title.ends_with(&self.id),\n                AppIdentifierType::Class => class.ends_with(&self.id),\n                AppIdentifierType::Exe => exe.ends_with(self.lower_id()),\n                AppIdentifierType::Path => path.ends_with(self.lower_id()),\n            },\n            MatchingStrategy::Contains => match self.kind {\n                AppIdentifierType::Title => title.contains(&self.id),\n                AppIdentifierType::Class => class.contains(&self.id),\n                AppIdentifierType::Exe => exe.contains(self.lower_id()),\n                AppIdentifierType::Path => path.contains(self.lower_id()),\n            },\n            MatchingStrategy::Regex => match &self.cache.regex {\n                Some(regex) => match self.kind {\n                    AppIdentifierType::Title => regex.is_match(title),\n                    AppIdentifierType::Class => regex.is_match(class),\n                    AppIdentifierType::Exe => regex.is_match(exe),\n                    AppIdentifierType::Path => regex.is_match(path),\n                },\n                None => false,\n            },\n        };\n\n        if self.negation {\n            self_result = !self_result;\n        }\n\n        (self_result && {\n            self.and\n                .iter()\n                .all(|and| and.validate(title, class, exe, path))\n        }) || {\n            self.or\n                .iter()\n                .any(|or| or.validate(title, class, exe, path))\n        }\n    }\n}\n\n#[serde_alias(SnakeCase)]\n#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct AppConfig {\n    /// name of the app\n    pub name: String,\n    /// category to group the app under\n    pub category: Option<String>,\n    /// monitor index that the app should be bound to\n    pub bound_monitor: Option<usize>,\n    /// workspace index that the app should be bound to\n    pub bound_workspace: Option<usize>,\n    /// app identifier\n    pub identifier: AppIdentifier,\n    /// extra specific options/settings for the app\n    #[serde(default)]\n    pub options: Vec<AppExtraFlag>,\n    /// is this config bundled with seelen ui.\n    #[serde(skip_deserializing, skip_serializing_if = \"AppConfig::is_false\")]\n    pub is_bundled: bool,\n}\n\nimpl AppConfig {\n    pub fn prepare(&mut self) {\n        self.identifier.prepare();\n    }\n\n    fn is_false(b: &bool) -> bool {\n        !b\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\npub struct AppsConfigurationList(Vec<AppConfig>);\n\nimpl AppsConfigurationList {\n    pub fn prepare(&mut self) {\n        self.0.iter_mut().for_each(|config| config.prepare());\n    }\n\n    pub fn search(&self, title: &str, class: &str, exe: &str, path: &str) -> Option<&AppConfig> {\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        self.0.iter().find(|&config| {\n            config\n                .identifier\n                .validate(title, class, &normalized_exe, &normalized_path)\n        })\n    }\n\n    pub fn iter(&self) -> impl Iterator<Item = &AppConfig> {\n        self.0.iter()\n    }\n\n    pub fn clear(&mut self) {\n        self.0.clear();\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    pub fn extend(&mut self, configs: Vec<AppConfig>) {\n        self.0.extend(configs);\n    }\n\n    pub fn as_slice(&self) -> &[AppConfig] {\n        &self.0\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_system_apps_path_contains_matching() {\n        // Test the specific case: ShellExperienceHost.exe with SystemApps path matching\n        let mut identifier = AppIdentifier {\n            id: \"Windows\\\\SystemApps\".to_string(),\n            kind: AppIdentifierType::Path,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        // Prepare the identifier (this normalizes to lowercase with forward slashes)\n        identifier.prepare();\n\n        // Test path (will be normalized by search() to lowercase with forward slashes)\n        let path =\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\SHELLEXPERIENCEHOST_CW5N1H2TXYEWY\\\\SHELLEXPERIENCEHOST.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"SHELLEXPERIENCEHOST.EXE\";\n\n        // Normalize path and exe as search() does\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        // Should match because normalized path contains \"windows/systemapps\"\n        assert!(\n            identifier.validate(title, class, &normalized_exe, &normalized_path),\n            \"Path should match with Contains strategy\"\n        );\n    }\n\n    #[test]\n    fn test_system_apps_full_config() {\n        // Test a full AppConfig with the System Background Apps configuration\n        let mut config = AppConfig {\n            name: \"System Background Apps\".to_string(),\n            category: None,\n            bound_monitor: None,\n            bound_workspace: None,\n            identifier: AppIdentifier {\n                id: \"Windows\\\\SystemApps\".to_string(),\n                kind: AppIdentifierType::Path,\n                matching_strategy: MatchingStrategy::Contains,\n                negation: false,\n                and: vec![],\n                or: vec![],\n                cache: AppIdentifierCache::default(),\n            },\n            options: vec![AppExtraFlag::NoInteractive],\n            is_bundled: false,\n        };\n\n        config.prepare();\n\n        // Test that ShellExperienceHost.exe matches\n        let path =\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\SHELLEXPERIENCEHOST_CW5N1H2TXYEWY\\\\SHELLEXPERIENCEHOST.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"SHELLEXPERIENCEHOST.EXE\";\n\n        // Normalize as search() does\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        assert!(\n            config\n                .identifier\n                .validate(title, class, &normalized_exe, &normalized_path),\n            \"ShellExperienceHost.exe should match System Background Apps config\"\n        );\n\n        // Verify options\n        assert_eq!(config.options.len(), 1);\n        assert_eq!(config.options[0], AppExtraFlag::NoInteractive);\n    }\n\n    #[test]\n    fn test_apps_configuration_list_search() {\n        // Test searching in AppsConfigurationList\n        let mut list = AppsConfigurationList(vec![AppConfig {\n            name: \"System Background Apps\".to_string(),\n            category: None,\n            bound_monitor: None,\n            bound_workspace: None,\n            identifier: AppIdentifier {\n                id: \"Windows\\\\SystemApps\".to_string(),\n                kind: AppIdentifierType::Path,\n                matching_strategy: MatchingStrategy::Contains,\n                negation: false,\n                and: vec![],\n                or: vec![],\n                cache: AppIdentifierCache::default(),\n            },\n            options: vec![AppExtraFlag::NoInteractive],\n            is_bundled: true,\n        }]);\n\n        list.prepare();\n\n        // Search for ShellExperienceHost.exe\n        let path =\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\SHELLEXPERIENCEHOST_CW5N1H2TXYEWY\\\\SHELLEXPERIENCEHOST.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"SHELLEXPERIENCEHOST.EXE\";\n\n        let result = list.search(title, class, exe, path);\n        assert!(result.is_some(), \"Should find matching config\");\n\n        let found_config = result.unwrap();\n        assert_eq!(found_config.name, \"System Background Apps\");\n        assert!(found_config.options.contains(&AppExtraFlag::NoInteractive));\n    }\n\n    #[test]\n    fn test_path_contains_non_matching() {\n        // Test that non-SystemApps paths don't match\n        let mut identifier = AppIdentifier {\n            id: \"Windows\\\\SystemApps\".to_string(),\n            kind: AppIdentifierType::Path,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        identifier.prepare();\n\n        // Test with a regular Program Files path\n        let path = \"C:\\\\PROGRAM FILES\\\\SOME APP\\\\APP.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"APP.EXE\";\n\n        // Normalize as search() does\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        assert!(\n            !identifier.validate(title, class, &normalized_exe, &normalized_path),\n            \"Non-SystemApps path should not match\"\n        );\n    }\n\n    #[test]\n    fn test_path_case_insensitivity() {\n        // Test that path matching is case insensitive\n        let mut identifier = AppIdentifier {\n            id: \"windows\\\\systemapps\".to_string(), // lowercase\n            kind: AppIdentifierType::Path,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        identifier.prepare();\n\n        // Test with uppercase path (will be normalized to lowercase)\n        let path =\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\SHELLEXPERIENCEHOST_CW5N1H2TXYEWY\\\\SHELLEXPERIENCEHOST.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"SHELLEXPERIENCEHOST.EXE\";\n\n        // Normalize as search() does\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        assert!(\n            identifier.validate(title, class, &normalized_exe, &normalized_path),\n            \"Path matching should be case insensitive\"\n        );\n    }\n\n    #[test]\n    fn test_multiple_system_apps() {\n        // Test that the same config matches multiple SystemApps executables\n        let mut identifier = AppIdentifier {\n            id: \"Windows\\\\SystemApps\".to_string(),\n            kind: AppIdentifierType::Path,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        identifier.prepare();\n\n        // Test different SystemApps\n        let test_cases = vec![\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\SHELLEXPERIENCEHOST_CW5N1H2TXYEWY\\\\SHELLEXPERIENCEHOST.EXE\",\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\MICROSOFT.WINDOWS.STARTMENUEXPERIENCEHOST_CW5N1H2TXYEWY\\\\STARTMENUEXPERIENCEHOST.EXE\",\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\MICROSOFT.WINDOWS.SEARCH_CW5N1H2TXYEWY\\\\SEARCHAPP.EXE\",\n        ];\n\n        for path in test_cases {\n            // Normalize as search() does\n            let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n            assert!(\n                identifier.validate(\"\", \"\", \"\", &normalized_path),\n                \"Path {} should match SystemApps pattern\",\n                path\n            );\n        }\n    }\n\n    #[test]\n    fn test_app_extra_flag_deserialization() {\n        // Test that \"no-interactive\" deserializes correctly\n        let json = r#\"{\"name\":\"Test\",\"identifier\":{\"id\":\"test\",\"kind\":\"exe\",\"matchingStrategy\":\"equals\"},\"options\":[\"no-interactive\"]}\"#;\n        let config: AppConfig = serde_json::from_str(json).expect(\"Should deserialize\");\n\n        assert_eq!(config.options.len(), 1);\n        assert_eq!(config.options[0], AppExtraFlag::NoInteractive);\n    }\n\n    #[test]\n    fn test_matching_strategy_deserialization() {\n        // Test that \"contains\" deserializes correctly\n        let json = r#\"{\"name\":\"Test\",\"identifier\":{\"id\":\"test\",\"kind\":\"path\",\"matchingStrategy\":\"contains\"},\"options\":[]}\"#;\n        let config: AppConfig = serde_json::from_str(json).expect(\"Should deserialize\");\n\n        assert!(matches!(\n            config.identifier.matching_strategy,\n            MatchingStrategy::Contains\n        ));\n    }\n\n    #[test]\n    fn test_path_separator_normalization_forward_slash() {\n        // Test that both forward and backslashes work (both normalized to forward slash)\n        let mut identifier = AppIdentifier {\n            id: \"Windows/SystemApps\".to_string(), // Using forward slash\n            kind: AppIdentifierType::Path,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        identifier.prepare();\n\n        // Verify the cached id is normalized to lowercase with forward slashes\n        assert_eq!(identifier.lower_id(), \"windows/systemapps\");\n\n        // Should match when path uses backslashes (normalized by search())\n        let path =\n            \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\SHELLEXPERIENCEHOST_CW5N1H2TXYEWY\\\\SHELLEXPERIENCEHOST.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"SHELLEXPERIENCEHOST.EXE\";\n\n        // Normalize as search() does\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        assert!(\n            identifier.validate(title, class, &normalized_exe, &normalized_path),\n            \"Both forward and backslashes should be normalized to forward slash\"\n        );\n    }\n\n    #[test]\n    fn test_path_separator_mixed() {\n        // Test with mixed separators in the identifier\n        let mut identifier = AppIdentifier {\n            id: \"Windows\\\\SystemApps/Microsoft.Windows\".to_string(), // Mixed separators\n            kind: AppIdentifierType::Path,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        identifier.prepare();\n\n        // Verify all backslashes are normalized to forward slashes\n        assert_eq!(\n            identifier.lower_id(),\n            \"windows/systemapps/microsoft.windows\"\n        );\n\n        // Path with backslashes should match (normalized by search())\n        let path = \"C:\\\\WINDOWS\\\\SYSTEMAPPS\\\\MICROSOFT.WINDOWS.SEARCH_CW5N1H2TXYEWY\\\\SEARCHAPP.EXE\";\n        let title = \"\";\n        let class = \"\";\n        let exe = \"SEARCHAPP.EXE\";\n\n        // Normalize as search() does\n        let normalized_path = path.to_lowercase().replace(\"\\\\\", \"/\");\n        let normalized_exe = exe.to_lowercase();\n\n        assert!(\n            identifier.validate(title, class, &normalized_exe, &normalized_path),\n            \"Mixed separators should be normalized to forward slashes\"\n        );\n    }\n\n    #[test]\n    fn test_exe_separator_normalization() {\n        // Test that exe type also normalizes separators (though less common)\n        let mut identifier = AppIdentifier {\n            id: \"app.EXE\".to_string(),\n            kind: AppIdentifierType::Exe,\n            matching_strategy: MatchingStrategy::Contains,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        identifier.prepare();\n\n        // Verify the cached value has backslashes\n        assert_eq!(identifier.lower_id(), \"app.exe\");\n    }\n\n    #[test]\n    fn test_title_and_class_no_normalization() {\n        // Test that Title and Class types don't normalize slashes\n        let mut title_identifier = AppIdentifier {\n            id: \"Some/Title\".to_string(),\n            kind: AppIdentifierType::Title,\n            matching_strategy: MatchingStrategy::Equals,\n            negation: false,\n            and: vec![],\n            or: vec![],\n            cache: AppIdentifierCache::default(),\n        };\n\n        title_identifier.prepare();\n\n        // Title with forward slash should match exactly (no normalization)\n        assert!(\n            title_identifier.validate(\"Some/Title\", \"\", \"\", \"\"),\n            \"Title should not normalize separators\"\n        );\n\n        // Should NOT match with backslash\n        assert!(\n            !title_identifier.validate(\"Some\\\\Title\", \"\", \"\", \"\"),\n            \"Title should preserve forward slash\"\n        );\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/settings/shortcuts.rs",
    "content": "use std::collections::HashSet;\n\nuse uuid::Uuid;\n\nuse crate::resource::WidgetId;\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(tag = \"name\", rename_all = \"snake_case\")]\npub enum SluHotkeyAction {\n    ToggleAppsMenu,\n    ToggleWorkspacesView,\n    // ==========================\n    TaskNext {\n        select_on_key_up: bool,\n    },\n    TaskPrev {\n        select_on_key_up: bool,\n    },\n    // ==========================\n    PauseTiling,\n    ToggleFloat,\n    ToggleMonocle,\n    CycleStackNext,\n    CycleStackPrev,\n    ReserveTop,\n    ReserveBottom,\n    ReserveLeft,\n    ReserveRight,\n    ReserveFloat,\n    ReserveStack,\n    FocusTop,\n    FocusBottom,\n    FocusLeft,\n    FocusRight,\n    IncreaseWidth,\n    DecreaseWidth,\n    IncreaseHeight,\n    DecreaseHeight,\n    RestoreSizes,\n    MoveWindowUp,\n    MoveWindowDown,\n    MoveWindowLeft,\n    MoveWindowRight,\n    // ==========================\n    StartWegApp {\n        #[serde(alias = \"arg\")]\n        index: usize,\n    },\n    // ==========================\n    SwitchWorkspace {\n        #[serde(alias = \"arg\")]\n        index: usize,\n    },\n    MoveToWorkspace {\n        #[serde(alias = \"arg\")]\n        index: usize,\n    },\n    SendToWorkspace {\n        #[serde(alias = \"arg\")]\n        index: usize,\n    },\n    SwitchToNextWorkspace,\n    SwitchToPreviousWorkspace,\n    CreateNewWorkspace,\n    DestroyCurrentWorkspace,\n    // ==========================\n    CycleWallpaperNext,\n    CycleWallpaperPrev,\n    // ==========================\n    MiscOpenSettings,\n    MiscForceRestart,\n    MiscForceQuit,\n    MiscToggleLockTracing,\n    MiscToggleWinEventTracing,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\npub struct SluHotkey {\n    pub id: Uuid,\n    pub action: SluHotkeyAction,\n    pub keys: Vec<String>,\n    #[serde(default)]\n    pub readonly: bool,\n    /// This will be true for hotkeys intended to override system hotkeys.\n    #[serde(default)]\n    pub system: bool,\n    /// If present this shortcut will be only available if the widget is enabled.\n    #[serde(default)]\n    pub attached_to: Option<WidgetId>,\n}\n\nimpl SluHotkey {\n    pub fn new<'a, T, I>(action: SluHotkeyAction, keys: I) -> Self\n    where\n        T: AsRef<str> + 'a,\n        I: IntoIterator<Item = T>,\n    {\n        Self {\n            id: Uuid::new_v4(),\n            action,\n            keys: keys.into_iter().map(|k| k.as_ref().to_string()).collect(),\n            readonly: false,\n            system: false,\n            attached_to: None,\n        }\n    }\n\n    pub fn system(mut self) -> Self {\n        self.system = true;\n        self.readonly = true;\n        self\n    }\n\n    pub fn readonly(mut self) -> Self {\n        self.readonly = true;\n        self\n    }\n\n    pub fn attached_to(mut self, widget_id: impl Into<WidgetId>) -> Self {\n        self.attached_to = Some(widget_id.into());\n        self\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct SluShortcutsSettings {\n    pub enabled: bool,\n    pub app_commands: Vec<SluHotkey>,\n}\n\nimpl Default for SluShortcutsSettings {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            app_commands: Vec::new(),\n        }\n    }\n}\n\nimpl SluShortcutsSettings {\n    pub fn contains_action(&self, action: SluHotkeyAction) -> bool {\n        self.app_commands.iter().any(|h| h.action == action)\n    }\n\n    pub fn sanitize(&mut self) {\n        let defaults = Self::default_shortcuts();\n        for hotkey in defaults.app_commands {\n            // add missing hotkeys from defaults\n            if !self.contains_action(hotkey.action) {\n                self.app_commands.push(hotkey);\n            }\n        }\n\n        let mut seen_ids = HashSet::new();\n        self.app_commands.retain(|h| {\n            seen_ids.insert(h.id) && !h.keys.is_empty() && h.action != SluHotkeyAction::Unknown\n        });\n    }\n\n    pub fn get_mut(&mut self, action: SluHotkeyAction) -> Option<&mut SluHotkey> {\n        self.app_commands.iter_mut().find(|h| h.action == action)\n    }\n\n    pub fn default_shortcuts() -> Self {\n        let mut shorcuts = Self::_default_shortcuts();\n\n        for index in 0..10 {\n            let digit_key = if index == 9 {\n                String::from(\"0\")\n            } else {\n                format!(\"{}\", index + 1)\n            };\n\n            shorcuts.push(\n                SluHotkey::new(\n                    SluHotkeyAction::StartWegApp { index },\n                    [\"Win\", digit_key.as_str()],\n                )\n                .system()\n                .attached_to(\"@seelen/weg\"),\n            );\n\n            shorcuts.push(SluHotkey::new(\n                SluHotkeyAction::SwitchWorkspace { index },\n                [\"Alt\", digit_key.as_str()],\n            ));\n\n            shorcuts.push(SluHotkey::new(\n                SluHotkeyAction::MoveToWorkspace { index },\n                [\"Alt\", \"Shift\", digit_key.as_str()],\n            ));\n\n            shorcuts.push(SluHotkey::new(\n                SluHotkeyAction::SendToWorkspace { index },\n                [\"Win\", \"Shift\", digit_key.as_str()],\n            ));\n        }\n\n        Self {\n            enabled: true,\n            app_commands: shorcuts,\n        }\n    }\n\n    fn _default_shortcuts() -> Vec<SluHotkey> {\n        use SluHotkeyAction::*;\n\n        let wm = \"@seelen/window-manager\";\n\n        vec![\n            SluHotkey::new(ToggleAppsMenu, [\"Win\"])\n                .system()\n                .attached_to(\"@seelen/apps-menu\"),\n            // Task switching and viewer\n            SluHotkey::new(\n                TaskNext {\n                    select_on_key_up: true,\n                },\n                [\"Alt\", \"Tab\"],\n            )\n            .system()\n            .attached_to(\"@seelen/task-switcher\"),\n            SluHotkey::new(\n                TaskPrev {\n                    select_on_key_up: true,\n                },\n                [\"Alt\", \"Shift\", \"Tab\"],\n            )\n            .system()\n            .attached_to(\"@seelen/task-switcher\"),\n            SluHotkey::new(\n                TaskNext {\n                    select_on_key_up: false,\n                },\n                [\"Alt\", \"Ctrl\", \"Tab\"],\n            )\n            .system()\n            .attached_to(\"@seelen/task-switcher\"),\n            SluHotkey::new(\n                TaskPrev {\n                    select_on_key_up: false,\n                },\n                [\"Alt\", \"Ctrl\", \"Shift\", \"Tab\"],\n            )\n            .system()\n            .attached_to(\"@seelen/task-switcher\"),\n            // tiling window manager\n            SluHotkey::new(PauseTiling, [\"Win\", \"P\"]).attached_to(wm),\n            SluHotkey::new(ToggleFloat, [\"Win\", \"F\"]).attached_to(wm),\n            SluHotkey::new(ToggleMonocle, [\"Win\", \"M\"]).attached_to(wm),\n            //\n            SluHotkey::new(CycleStackNext, [\"Win\", \"Alt\", \"Right\"]).attached_to(wm),\n            SluHotkey::new(CycleStackPrev, [\"Win\", \"Alt\", \"Left\"]).attached_to(wm),\n            //\n            SluHotkey::new(ReserveTop, [\"Win\", \"Shift\", \"I\"]).attached_to(wm),\n            SluHotkey::new(ReserveBottom, [\"Win\", \"Shift\", \"K\"]).attached_to(wm),\n            SluHotkey::new(ReserveLeft, [\"Win\", \"Shift\", \"J\"]).attached_to(wm),\n            SluHotkey::new(ReserveRight, [\"Win\", \"Shift\", \"L\"]).attached_to(wm),\n            SluHotkey::new(ReserveFloat, [\"Win\", \"Shift\", \"U\"]).attached_to(wm),\n            SluHotkey::new(ReserveStack, [\"Win\", \"Shift\", \"O\"]).attached_to(wm),\n            //\n            SluHotkey::new(FocusTop, [\"Alt\", \"I\"]).attached_to(wm),\n            SluHotkey::new(FocusBottom, [\"Alt\", \"K\"]).attached_to(wm),\n            SluHotkey::new(FocusLeft, [\"Alt\", \"J\"]).attached_to(wm),\n            SluHotkey::new(FocusRight, [\"Alt\", \"L\"]).attached_to(wm),\n            //\n            SluHotkey::new(IncreaseWidth, [\"Win\", \"Alt\", \"=\"]).attached_to(wm),\n            SluHotkey::new(DecreaseWidth, [\"Win\", \"Alt\", \"-\"]).attached_to(wm),\n            SluHotkey::new(IncreaseHeight, [\"Win\", \"Ctrl\", \"=\"]).attached_to(wm),\n            SluHotkey::new(DecreaseHeight, [\"Win\", \"Ctrl\", \"-\"]).attached_to(wm),\n            SluHotkey::new(RestoreSizes, [\"Win\", \"Alt\", \"0\"]).attached_to(wm),\n            //\n            SluHotkey::new(MoveWindowUp, [\"Shift\", \"Alt\", \"I\"]).attached_to(wm),\n            SluHotkey::new(MoveWindowDown, [\"Shift\", \"Alt\", \"K\"]).attached_to(wm),\n            SluHotkey::new(MoveWindowLeft, [\"Shift\", \"Alt\", \"J\"]).attached_to(wm),\n            SluHotkey::new(MoveWindowRight, [\"Shift\", \"Alt\", \"L\"]).attached_to(wm),\n            // virtual desktop\n            SluHotkey::new(SwitchToNextWorkspace, [\"Ctrl\", \"Win\", \"Right\"]).system(),\n            SluHotkey::new(SwitchToPreviousWorkspace, [\"Ctrl\", \"Win\", \"Left\"]).system(),\n            SluHotkey::new(CreateNewWorkspace, [\"Ctrl\", \"Win\", \"D\"]).system(),\n            SluHotkey::new(DestroyCurrentWorkspace, [\"Ctrl\", \"Win\", \"F4\"]).system(),\n            SluHotkey::new(ToggleWorkspacesView, [\"Win\", \"Tab\"])\n                .system()\n                .attached_to(\"@seelen/workspaces-viewer\"),\n            // wallpaper manager\n            SluHotkey::new(CycleWallpaperNext, [\"Ctrl\", \"Win\", \"Up\"]),\n            SluHotkey::new(CycleWallpaperPrev, [\"Ctrl\", \"Win\", \"Down\"]),\n            // misc\n            SluHotkey::new(MiscOpenSettings, [\"Win\", \"K\"]),\n            SluHotkey::new(MiscForceRestart, [\"Ctrl\", \"Win\", \"Alt\", \"R\"]).readonly(),\n            SluHotkey::new(MiscForceQuit, [\"Ctrl\", \"Win\", \"Alt\", \"K\"]).readonly(),\n        ]\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/theme/config.rs",
    "content": "use std::sync::LazyLock;\n\nuse schemars::JsonSchema;\nuse serde::{de::Visitor, Deserialize, Deserializer};\n\nuse crate::{error::Result, resource::ResourceText};\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\npub struct ThemeSettingsDefinition(Vec<ThemeConfigDefinition>);\n\n#[derive(Debug, Clone, Serialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub enum ThemeConfigDefinition {\n    Group(ThemeConfigGroup),\n    #[serde(untagged)]\n    Item(Box<ThemeVariableDefinition>),\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct ThemeConfigGroup {\n    header: ResourceText,\n    items: Vec<ThemeConfigDefinition>,\n}\n\nimpl<'de> Deserialize<'de> for ThemeConfigDefinition {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        #[derive(Deserialize)]\n        struct GroupVariant {\n            group: ThemeConfigGroup,\n        }\n\n        let value =\n            serde_json::Value::deserialize(deserializer).map_err(serde::de::Error::custom)?;\n        if let Ok(parsed) = GroupVariant::deserialize(value.clone()) {\n            return Ok(ThemeConfigDefinition::Group(parsed.group));\n        }\n\n        Ok(ThemeConfigDefinition::Item(Box::new(\n            serde_json::from_value(value).map_err(serde::de::Error::custom)?,\n        )))\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(tag = \"syntax\")]\npub enum ThemeVariableDefinition {\n    /// This config definition will allow to users write any string.\\\n    /// Css syntax: https://developer.mozilla.org/en-US/docs/Web/CSS/string \\\n    /// ### example:\n    /// ```css\n    /// --var-name: \"user input\"\n    /// ```\n    #[serde(rename = \"<string>\")]\n    String(ThemeVariable<String>),\n    /// This config definition will allow to users select a color and\n    /// will be stored as a hex value, opacity is always allowed via UI.\\\n    /// Css syntax: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value \\\n    /// ### example:\n    /// ```css\n    /// --var-name: #ff22ee\n    /// --var-name: #ff22ee\n    /// ```\n    #[serde(rename = \"<color>\")]\n    Color(ThemeVariable<String>),\n    /// This will allow to the user set any lenght in any unit. (px, %, vw, etc).\n    /// If you need force a specific unit, use Number instead lenght and on theme code makes the conversion.\\\n    /// Css syntax: https://developer.mozilla.org/en-US/docs/Web/CSS/length \\\n    /// ### example:\n    /// ```css\n    /// --var-name: 10px\n    /// --var-name: 10%\n    /// --var-name: 10vw\n    /// ```\n    #[serde(\n        rename = \"<length-percentage>\",\n        alias = \"<length>\",\n        alias = \"<percentage>\"\n    )]\n    Length(ThemeVariableWithUnit<f64>),\n    /// This will allow to users to set any number, without units.\n    /// Css syntax: https://developer.mozilla.org/en-US/docs/Web/CSS/number \\\n    /// ### example:\n    /// ```css\n    /// --var-name: 10\n    /// ```\n    #[serde(rename = \"<number>\")]\n    Number(ThemeVariable<f64>),\n    /// This will allow to users to set any url.\\\n    /// Css syntax: https://developer.mozilla.org/en-US/docs/Web/CSS/url_value \\\n    /// ### example:\n    /// ```css\n    /// --var-name: url(\"https://example.com/image.png\")\n    /// ```\n    /// This will be rendered as a file input on select file the url version of the path will be stored.\n    /// Initial value will be ignored.\n    #[serde(rename = \"<url>\")]\n    Url(ThemeVariable<String>),\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct ThemeVariable<T> {\n    /// Css variable name, example: `--my-css-variable`\n    pub name: CssVariableName,\n    /// Label to show to the user on Settings.\n    pub label: ResourceText,\n    /// Extra details to show to the user under the label on Settings.\n    pub description: Option<ResourceText>,\n    /// Will be rendered as a icon with a tooltip side the label.\n    pub tip: Option<ResourceText>,\n\n    /// Initial variable value, if not manually set by the user.\n    pub initial_value: T,\n\n    /// syntax = <string> min length of the input.\\\n    /// syntax = <number> min value of the input.\n    pub min: Option<f64>,\n    /// syntax = <string> max length of the input.\\\n    /// syntax = <number> max value of the input.\n    pub max: Option<f64>,\n    /// Only used if syntax is `<number>`, setting this will make the input a slider\n    pub step: Option<f64>,\n\n    /// If present, this will be rendered as a selector of options instead of an input.\n    /// `initial_value` should be present in this list.\n    pub options: Option<Vec<T>>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct ThemeVariableWithUnit<T> {\n    #[serde(flatten)]\n    pub _extends: ThemeVariable<T>,\n    pub initial_value_unit: String,\n}\n\n/// Valid CSS variable name that starts with `--` and follows CSS naming conventions\n#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, JsonSchema, TS)]\npub struct CssVariableName(String);\n\nstatic CSS_VAR_REGEX: LazyLock<regex::Regex> =\n    LazyLock::new(|| regex::Regex::new(r\"^--[a-zA-Z_][\\w-]*$\").unwrap());\n\nimpl CssVariableName {\n    /// Creates a new CssVariableName after validation\n    pub fn from_string(name: &str) -> Result<Self> {\n        if !CSS_VAR_REGEX.is_match(name) {\n            return Err(format!(\n                \"Invalid CSS variable name '{name}'. Must start with '--' and follow CSS naming rules\"\n            )\n            .into());\n        }\n        Ok(Self(name.to_string()))\n    }\n}\n\nimpl std::fmt::Display for CssVariableName {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nimpl<'de> Deserialize<'de> for CssVariableName {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        struct CssVariableNameVisitor;\n\n        impl<'de> Visitor<'de> for CssVariableNameVisitor {\n            type Value = CssVariableName;\n\n            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {\n                write!(\n                    formatter,\n                    \"a valid CSS variable name starting with '--' and following CSS naming rules\"\n                )\n            }\n\n            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n            where\n                E: serde::de::Error,\n            {\n                CssVariableName::from_string(value).map_err(serde::de::Error::custom)\n            }\n        }\n\n        deserializer.deserialize_str(CssVariableNameVisitor)\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/theme/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\npub mod config;\n\nuse std::{collections::HashMap, path::Path};\n\nuse config::ThemeSettingsDefinition;\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\nuse crate::{\n    error::Result,\n    resource::{ResourceKind, ResourceMetadata, SluResource, ThemeId, WidgetId},\n    utils::search_resource_entrypoint,\n};\n\npub static ALLOWED_STYLE_EXTENSIONS: &[&str] = &[\"css\", \"scss\", \"sass\"];\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Theme {\n    pub id: ThemeId,\n    /// Metadata about the theme\n    #[serde(alias = \"info\")] // for backwards compatibility before v2.0\n    pub metadata: ResourceMetadata,\n    pub settings: ThemeSettingsDefinition,\n    /// Css Styles of the theme\n    pub styles: HashMap<WidgetId, String>,\n    /// Shared css styles for all widgets, commonly used to set styles\n    /// for the components library\n    pub shared_styles: String,\n}\n\nimpl SluResource for Theme {\n    const KIND: ResourceKind = ResourceKind::Theme;\n\n    fn metadata(&self) -> &ResourceMetadata {\n        &self.metadata\n    }\n\n    fn metadata_mut(&mut self) -> &mut ResourceMetadata {\n        &mut self.metadata\n    }\n\n    fn load_from_folder(path: &Path) -> Result<Theme> {\n        let mut theme = Self::load_old_folder_schema(path)?;\n\n        'outer: for entry in path.read_dir()?.flatten() {\n            let outer_path = entry.path();\n            if !outer_path.is_dir() {\n                let (Some(file_stem), Some(ext)) = (outer_path.file_stem(), outer_path.extension())\n                else {\n                    continue 'outer;\n                };\n\n                if file_stem == \"shared\" && ALLOWED_STYLE_EXTENSIONS.iter().any(|e| *e == ext) {\n                    let css = if ext == \"scss\" || ext == \"sass\" {\n                        grass::from_path(&outer_path, &grass::Options::default())?\n                    } else {\n                        std::fs::read_to_string(&outer_path)?\n                    };\n                    theme.shared_styles = css;\n                }\n                continue 'outer;\n            }\n\n            let creator_username = entry.file_name();\n            'inner: for entry in outer_path.read_dir()?.flatten() {\n                let path = entry.path();\n                if !path.is_file() {\n                    continue 'inner;\n                }\n\n                let (Some(resource_name), Some(ext)) = (path.file_stem(), path.extension()) else {\n                    continue 'inner;\n                };\n\n                if ALLOWED_STYLE_EXTENSIONS.iter().any(|e| *e == ext) {\n                    let css = if ext == \"scss\" || ext == \"sass\" {\n                        grass::from_path(&path, &grass::Options::default())?\n                    } else {\n                        std::fs::read_to_string(&path)?\n                    };\n                    theme.styles.insert(\n                        WidgetId::from(\n                            format!(\n                                \"@{}/{}\",\n                                creator_username.to_string_lossy(),\n                                resource_name.to_string_lossy()\n                            )\n                            .as_str(),\n                        ),\n                        css,\n                    );\n                }\n            }\n        }\n        Ok(theme)\n    }\n}\n\nimpl Theme {\n    /// Load theme from a folder using old deprecated paths since v2.1.0 will be removed in v3\n    fn load_old_folder_schema(path: &Path) -> Result<Theme> {\n        let file = search_resource_entrypoint(path).unwrap_or_else(|| {\n            path.join(\"theme.yml\") // backward compatibility to be removed in v3\n        });\n        let mut theme = Self::load_from_file(&file)?;\n\n        if path.join(\"theme.weg.css\").exists() {\n            theme.styles.insert(\n                WidgetId::known_weg(),\n                std::fs::read_to_string(path.join(\"theme.weg.css\"))?,\n            );\n        }\n        if path.join(\"theme.toolbar.css\").exists() {\n            theme.styles.insert(\n                WidgetId::known_toolbar(),\n                std::fs::read_to_string(path.join(\"theme.toolbar.css\"))?,\n            );\n        }\n        if path.join(\"theme.wm.css\").exists() {\n            theme.styles.insert(\n                WidgetId::known_wm(),\n                std::fs::read_to_string(path.join(\"theme.wm.css\"))?,\n            );\n        }\n        if path.join(\"theme.wall.css\").exists() {\n            theme.styles.insert(\n                WidgetId::known_wall(),\n                std::fs::read_to_string(path.join(\"theme.wall.css\"))?,\n            );\n        };\n        Ok(theme)\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/theme/mod.ts",
    "content": "import type {\n  ResourceId,\n  Settings as ISettings,\n  Theme as ITheme,\n  ThemeConfigDefinition,\n  ThemeId,\n  ThemeVariableDefinition,\n} from \"@seelen-ui/types\";\nimport { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../../handlers/mod.ts\";\nimport { List } from \"../../utils/List.ts\";\nimport { newFromInvoke, newOnEvent } from \"../../utils/State.ts\";\nimport { Widget } from \"../widget/mod.ts\";\n\nexport class ThemeList extends List<ITheme> {\n  static getAsync(): Promise<ThemeList> {\n    return newFromInvoke(this, SeelenCommand.StateGetThemes);\n  }\n\n  static onChange(cb: (payload: ThemeList) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.StateThemesChanged);\n  }\n\n  applyToDocument(activeIds: ThemeId[], variables: ISettings[\"byTheme\"]): void {\n    const enabledThemes: Theme[] = [];\n    for (const theme of this.asArray()) {\n      if (activeIds.includes(theme.id)) {\n        enabledThemes.push(new Theme(theme));\n      }\n    }\n    // sort by user order\n    enabledThemes.sort((a, b) => activeIds.indexOf(a.id) - activeIds.indexOf(b.id));\n\n    removeAllThemeStyles();\n    for (const theme of enabledThemes) {\n      theme.applyToDocument(variables[theme.id]);\n    }\n  }\n}\n\nexport interface Theme extends ITheme {}\n\nexport class Theme {\n  constructor(plain: ITheme) {\n    Object.assign(this, plain);\n  }\n\n  forEachVariableDefinition(cb: (def: ThemeVariableDefinition) => void): void {\n    iterateVariableDefinitions(this.settings, cb);\n  }\n\n  /** Will add the styles targeting the current widget id */\n  applyToDocument(varValues: ISettings[\"byTheme\"][ResourceId] = {}): void {\n    const widgetId = Widget.self.id;\n    let styles = ``;\n\n    this.forEachVariableDefinition((def) => {\n      if (!isValidCssVariableName(def.name)) {\n        return;\n      }\n      styles += `\n        @property ${def.name} {\n          syntax: \"${def.syntax}\";\n          inherits: true;\n          initial-value: ${def.initialValue}${\"initialValueUnit\" in def ? def.initialValueUnit : \"\"};\n        }\n      `;\n    });\n\n    const layerName = \"theme-\" +\n      this.id\n        .toLowerCase()\n        .replaceAll(\"@\", \"\")\n        .replaceAll(/[^a-zA-Z0-9\\-\\_]/g, \"_\");\n\n    styles += `@layer ${layerName}-shared {\\n${this.sharedStyles}\\n}\\n`;\n\n    const variablesContent = Object.entries(varValues)\n      .filter(([name, _value]) => isValidCssVariableName(name))\n      .map(([name, value]) => `${name}: ${value || \"\"};`)\n      .join(\"\\n\");\n    styles += `@layer ${layerName} {\\n:root {${variablesContent}}\\n${this.styles[widgetId] ?? \"\"}\\n}\\n`;\n\n    this.removeFromDocument(); // remove old styles\n    const styleElement = document.createElement(\"style\");\n    styleElement.id = this.id;\n    styleElement.textContent = styles;\n    styleElement.setAttribute(\"data-source\", \"theme\");\n    document.head.appendChild(styleElement);\n  }\n\n  removeFromDocument(): void {\n    document.getElementById(this.id)?.remove();\n  }\n}\n\nfunction isValidCssVariableName(name: string): boolean {\n  return /^--[\\w\\d-]*$/.test(name);\n}\n\nfunction iterateVariableDefinitions(\n  defs: ThemeConfigDefinition[],\n  cb: (def: ThemeVariableDefinition) => void,\n): void {\n  for (const def of defs) {\n    if (\"group\" in def) {\n      iterateVariableDefinitions(def.group.items, cb);\n    } else {\n      cb(def);\n    }\n  }\n}\n\nfunction removeAllThemeStyles(): void {\n  const elements = document.querySelectorAll(`style[data-source=\"theme\"]`);\n  for (const element of elements) {\n    if (element instanceof HTMLStyleElement) {\n      element.remove();\n    }\n  }\n}\n"
  },
  {
    "path": "libs/core/src/state/theme/tests.rs",
    "content": "use std::path::PathBuf;\n\nuse crate::{error::Result, resource::SluResource, state::Theme};\n\n#[test]\nfn test_compatibility_with_older_schemas() -> Result<()> {\n    Theme::load(&PathBuf::from(\"./mocks/themes/v2.3.0.yml\")).map_err(|e| format!(\"v2.3.0: {e}\"))?;\n    Theme::load(&PathBuf::from(\"./mocks/themes/v2.3.12.yml\"))\n        .map_err(|e| format!(\"v2.3.12: {e}\"))?;\n    Ok(())\n}\n"
  },
  {
    "path": "libs/core/src/state/theme/theming.ts",
    "content": "import { UIColors } from \"../../system_state/ui_colors.ts\";\nimport { RuntimeStyleSheet } from \"../../utils/DOM.ts\";\nimport { Settings } from \"../settings/mod.ts\";\nimport { ThemeList } from \"./mod.ts\";\n\n/**\n * This will apply the active themes for this widget, and automatically update\n * when the themes or settings change. Also will add the systehm ui colors to the document.\n */\nexport async function startThemingTool(): Promise<void> {\n  let settings = await Settings.getAsync();\n  let themes = await ThemeList.getAsync();\n\n  await ThemeList.onChange((newThemes) => {\n    themes = newThemes;\n    themes.applyToDocument(settings.activeThemes, settings.byTheme);\n  });\n\n  await Settings.onChange((newSettings) => {\n    settings = newSettings;\n    themes.applyToDocument(settings.activeThemes, settings.byTheme);\n  });\n\n  (await UIColors.getAsync()).setAsCssVariables();\n  await UIColors.onChange((colors) => colors.setAsCssVariables());\n\n  startDateCssVariables();\n\n  themes.applyToDocument(settings.activeThemes, settings.byTheme);\n}\n\nexport function startDateCssVariables(): void {\n  // Set initial values immediately\n  updateDateCssVariables();\n  // Update every minute (60000ms) to avoid overhead from seconds\n  setInterval(updateDateCssVariables, 60000);\n}\n\nfunction updateDateCssVariables(): void {\n  const now = new Date();\n  const locale = navigator.language;\n\n  // Time values\n  const hour = now.getHours(); // 0-23\n  const minute = now.getMinutes(); // 0-59\n\n  // Date name values using Intl API\n  const dayName = new Intl.DateTimeFormat(locale, { weekday: \"long\" }).format(now);\n  const monthName = new Intl.DateTimeFormat(locale, { month: \"long\" }).format(now);\n\n  // Date numeric values\n  const dayOfMonth = now.getDate(); // 1-31\n  const monthNumber = now.getMonth() + 1; // 1-12\n  const year = now.getFullYear(); // 2025, etc.\n\n  const styleSheet = new RuntimeStyleSheet(\"@runtime/date-variables\");\n\n  // Time variables\n  styleSheet.addVariable(\"--date-hour\", String(hour));\n  styleSheet.addVariable(\"--date-minute\", String(minute));\n\n  // Date name variables (localized)\n  styleSheet.addVariable(\"--date-day-name\", dayName);\n  styleSheet.addVariable(\"--date-month-name\", monthName);\n\n  // Date numeric variables\n  styleSheet.addVariable(\"--date-day\", String(dayOfMonth));\n  styleSheet.addVariable(\"--date-month\", String(monthNumber));\n  styleSheet.addVariable(\"--date-year\", String(year));\n\n  styleSheet.applyToDocument();\n}\n"
  },
  {
    "path": "libs/core/src/state/wallpaper/mod.rs",
    "content": "use std::path::Path;\n\nuse url::Url;\nuse uuid::Uuid;\n\nuse crate::{\n    error::Result,\n    resource::{\n        InternalResourceMetadata, ResourceKind, ResourceMetadata, ResourceText, SluResource,\n        WallpaperId,\n    },\n};\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Wallpaper {\n    pub id: WallpaperId,\n    pub metadata: ResourceMetadata,\n    pub r#type: WallpaperKind,\n\n    pub url: Option<Url>,\n    pub filename: Option<String>,\n\n    pub thumbnail_url: Option<Url>,\n    #[serde(alias = \"thumbnail_filename\")]\n    pub thumbnail_filename: Option<String>,\n\n    /// Only used if the wallpaper type is `Layered`.\\\n    /// Custom css that will be applied only on this wallpaper.\n    pub css: Option<String>,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WallpaperKind {\n    #[serde(alias = \"image\")]\n    Image,\n    #[serde(alias = \"video\")]\n    Video,\n    #[serde(alias = \"layered\")]\n    Layered,\n    /// used for wallpapers created before v2.4.9, will be changed on sanitization\n    #[default]\n    Unsupported,\n}\n\nimpl SluResource for Wallpaper {\n    const KIND: ResourceKind = ResourceKind::Wallpaper;\n\n    fn metadata(&self) -> &ResourceMetadata {\n        &self.metadata\n    }\n\n    fn metadata_mut(&mut self) -> &mut ResourceMetadata {\n        &mut self.metadata\n    }\n\n    fn sanitize(&mut self) {\n        // migration step for old wallpapers\n        if WallpaperKind::Unsupported == self.r#type {\n            if let Some(filename) = &self.filename {\n                if Self::SUPPORTED_VIDEOS\n                    .iter()\n                    .any(|ext| filename.ends_with(ext))\n                {\n                    self.r#type = WallpaperKind::Video;\n                }\n                if Self::SUPPORTED_IMAGES\n                    .iter()\n                    .any(|ext| filename.ends_with(ext))\n                {\n                    self.r#type = WallpaperKind::Image;\n                }\n            }\n        }\n\n        // remove thumbnail if doesn't exist\n        if let Some(filename) = &self.thumbnail_filename {\n            let thumbnail_path = self.metadata.internal.path.join(filename);\n            if !thumbnail_path.exists() {\n                self.thumbnail_filename = None;\n            }\n        }\n    }\n\n    fn validate(&self) -> Result<()> {\n        if self.r#type == WallpaperKind::Unsupported {\n            return Err(\"Unsupported wallpaper extension\".into());\n        }\n        Ok(())\n    }\n}\n\nimpl Wallpaper {\n    /// https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types\n    pub const SUPPORTED_IMAGES: [&str; 11] = [\n        \"apng\", \"avif\", \"gif\", \"jpg\", \"jpeg\", \"png\", \"svg\", \"webp\", \"bmp\", \"ico\", \"tiff\",\n    ];\n    /// https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Containers\n    pub const SUPPORTED_VIDEOS: [&str; 7] = [\"mp4\", \"webm\", \"ogg\", \"avi\", \"mov\", \"mkv\", \"mpeg\"];\n\n    /// path should be the path to the wallpaper image or video to be moved or copied to the wallpaper folder\n    pub fn create_from_file(path: &Path, folder_to_store: &Path, copy: bool) -> Result<Self> {\n        if !path.exists() || path.is_dir() {\n            return Err(\"File does not exist\".into());\n        }\n\n        let (Some(filename), Some(ext)) = (path.file_name(), path.extension()) else {\n            return Err(\"Invalid file name or extension\".into());\n        };\n        let filename = filename.to_string_lossy().to_string();\n        let ext = ext.to_string_lossy().to_string();\n\n        // as uuids can start with numbers and resources names can't start with numbers\n        // we prefix the uuid with an 'x'\n        let resource_name = uuid::Uuid::new_v4();\n        let id = format!(\"@user/x{}\", resource_name.as_simple()).into();\n\n        let metadata = ResourceMetadata {\n            display_name: ResourceText::En(filename.clone()),\n            internal: InternalResourceMetadata {\n                path: folder_to_store.join(\"metadata.yml\"),\n                ..Default::default()\n            },\n            ..Default::default()\n        };\n\n        std::fs::create_dir_all(folder_to_store)?;\n        if copy {\n            std::fs::copy(path, folder_to_store.join(&filename))?;\n        } else {\n            std::fs::rename(path, folder_to_store.join(&filename))?;\n        }\n\n        let r#type = if Self::SUPPORTED_IMAGES.contains(&ext.as_str()) {\n            WallpaperKind::Image\n        } else if Self::SUPPORTED_VIDEOS.contains(&ext.as_str()) {\n            WallpaperKind::Video\n        } else {\n            WallpaperKind::Unsupported\n        };\n\n        let wallpaper = Self {\n            id,\n            metadata,\n            r#type,\n            filename: Some(filename.clone()),\n            thumbnail_filename: if Self::SUPPORTED_IMAGES.contains(&ext.as_str()) {\n                Some(filename)\n            } else {\n                None\n            },\n            ..Default::default()\n        };\n        wallpaper.save()?;\n\n        Ok(wallpaper)\n    }\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct WallpaperCollection {\n    pub id: Uuid,\n    pub name: String,\n    pub wallpapers: Vec<WallpaperId>,\n}\n"
  },
  {
    "path": "libs/core/src/state/wallpaper/mod.ts",
    "content": "import type { Wallpaper as IWallpaper, WallpaperInstanceSettings } from \"@seelen-ui/types\";\n\nimport { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../../handlers/mod.ts\";\nimport { List } from \"../../utils/List.ts\";\nimport { newFromInvoke, newOnEvent } from \"../../utils/State.ts\";\n\nexport const SUPPORTED_IMAGE_WALLPAPER_EXTENSIONS = [\n  \"apng\",\n  \"avif\",\n  \"gif\",\n  \"jpg\",\n  \"jpeg\",\n  \"png\",\n  \"svg\",\n  \"webp\",\n  \"bmp\",\n  \"ico\",\n  \"tiff\",\n];\n\nexport const SUPPORTED_VIDEO_WALLPAPER_EXTENSIONS = [\n  \"mp4\",\n  \"webm\",\n  \"ogg\",\n  \"avi\",\n  \"mov\",\n  \"mkv\",\n  \"mpeg\",\n];\n\nexport class WallpaperList extends List<IWallpaper> {\n  static getAsync(): Promise<WallpaperList> {\n    return newFromInvoke(this, SeelenCommand.StateGetWallpapers);\n  }\n\n  static onChange(cb: (payload: WallpaperList) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.StateWallpapersChanged);\n  }\n}\n\nexport interface WallpaperConfiguration extends WallpaperInstanceSettings {}\nexport class WallpaperConfiguration {\n  constructor(plain: WallpaperInstanceSettings) {\n    Object.assign(this, plain);\n  }\n\n  static default(): Promise<WallpaperConfiguration> {\n    return newFromInvoke(this, SeelenCommand.StateGetDefaultWallpaperSettings);\n  }\n}\n"
  },
  {
    "path": "libs/core/src/state/weg_items.rs",
    "content": "use std::{collections::HashSet, path::PathBuf};\n\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\nuse crate::system_state::{Relaunch, RelaunchArguments};\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WegItemData {\n    /// internal UUID to differentiate items\n    pub id: uuid::Uuid,\n    /// display name of the item\n    pub display_name: String,\n    /// Application user model id.\n    pub umid: Option<String>,\n    /// path to file or program.\n    pub path: PathBuf,\n    /// the item will persist after all windows are closed\n    pub pinned: bool,\n    /// this item should not be pinnable\n    pub prevent_pinning: bool,\n    /// custom information to relaunch this app, if none, UMID or path should be used\n    pub relaunch: Option<Relaunch>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(tag = \"type\")]\npub enum WegItem {\n    #[serde(alias = \"PinnedApp\", alias = \"Pinned\")]\n    DeprecatedOldPinned(OldWegItemData),\n    AppOrFile(WegItemData),\n    Separator {\n        id: uuid::Uuid,\n    },\n    Media {\n        id: uuid::Uuid,\n    },\n    StartMenu {\n        id: uuid::Uuid,\n    },\n    ShowDesktop {\n        id: uuid::Uuid,\n    },\n    TrashBin {\n        id: uuid::Uuid,\n    },\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export, repr(enum = name)))]\npub enum WegItemType {\n    AppOrFile,\n    Separator,\n    Media,\n    StartMenu,\n    ShowDesktop,\n    TrashBin,\n}\n\nimpl WegItem {\n    pub fn id(&self) -> &uuid::Uuid {\n        match self {\n            WegItem::DeprecatedOldPinned(data) => &data.id,\n            WegItem::AppOrFile(data) => &data.id,\n            WegItem::Separator { id } => id,\n            WegItem::Media { id } => id,\n            WegItem::StartMenu { id } => id,\n            WegItem::ShowDesktop { id } => id,\n            WegItem::TrashBin { id } => id,\n        }\n    }\n\n    fn set_id(&mut self, identifier: uuid::Uuid) {\n        match self {\n            WegItem::DeprecatedOldPinned(data) => data.id = identifier,\n            WegItem::AppOrFile(data) => data.id = identifier,\n            WegItem::Separator { id } => *id = identifier,\n            WegItem::Media { id } => *id = identifier,\n            WegItem::StartMenu { id } => *id = identifier,\n            WegItem::ShowDesktop { id } => *id = identifier,\n            WegItem::TrashBin { id } => *id = identifier,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct WegItems {\n    /// Whether the reordering possible on the weg\n    pub is_reorder_disabled: bool,\n    pub left: Vec<WegItem>,\n    pub center: Vec<WegItem>,\n    pub right: Vec<WegItem>,\n}\n\nimpl WegItems {\n    fn migrate_item(item: WegItem) -> Option<WegItem> {\n        let WegItem::DeprecatedOldPinned(mut data) = item else {\n            return Some(item);\n        };\n\n        // migration step for items before v2.1.6\n        if data.subtype == OldWegItemSubtype::UnknownV2_1_6 {\n            data.subtype = if data.relaunch_program.to_lowercase().contains(\".exe\") {\n                OldWegItemSubtype::App\n            } else if data.path.is_dir() {\n                OldWegItemSubtype::Folder\n            } else {\n                OldWegItemSubtype::File\n            };\n        }\n\n        if data.subtype == OldWegItemSubtype::Folder {\n            return None;\n        }\n\n        // migration of old scheme before v2.5\n        if let Some(args) = &data.relaunch_args {\n            if data.relaunch_program.contains(\"explorer\")\n                && args.to_string().starts_with(\"shell:AppsFolder\")\n            {\n                data.relaunch_program = args.to_string();\n                data.relaunch_args = None;\n            }\n        }\n\n        if data.relaunch_program.is_empty() {\n            data.relaunch_program = data.path.to_string_lossy().to_string();\n        }\n\n        let relaunch = Some(Relaunch {\n            command: data.relaunch_program,\n            args: data.relaunch_args,\n            working_dir: data.relaunch_in,\n            icon: None,\n        });\n\n        Some(WegItem::AppOrFile(WegItemData {\n            id: data.id,\n            display_name: data.display_name,\n            umid: data.umid,\n            path: data.path,\n            pinned: true,\n            prevent_pinning: data.pin_disabled,\n            relaunch,\n        }))\n    }\n\n    fn migrate_items(items: Vec<WegItem>) -> Vec<WegItem> {\n        items.into_iter().filter_map(Self::migrate_item).collect()\n    }\n\n    pub fn migrate(&mut self) {\n        self.left = Self::migrate_items(std::mem::take(&mut self.left));\n        self.center = Self::migrate_items(std::mem::take(&mut self.center));\n        self.right = Self::migrate_items(std::mem::take(&mut self.right));\n    }\n\n    fn sanitize_items(dict: &mut HashSet<uuid::Uuid>, items: Vec<WegItem>) -> Vec<WegItem> {\n        let mut result = Vec::new();\n        for mut item in items {\n            if let WegItem::AppOrFile(data) = &item {\n                let should_ensure_path =\n                    data.umid.is_none() || data.path.extension().is_some_and(|e| e == \"lnk\");\n                if should_ensure_path && !data.path.exists() {\n                    continue;\n                }\n            }\n\n            if item.id().is_nil() {\n                item.set_id(uuid::Uuid::new_v4());\n            }\n\n            if !dict.contains(item.id()) {\n                dict.insert(*item.id());\n                result.push(item);\n            }\n        }\n        result\n    }\n\n    pub fn sanitize(&mut self) {\n        self.migrate();\n        let mut dict = HashSet::new();\n        self.left = Self::sanitize_items(&mut dict, std::mem::take(&mut self.left));\n        self.center = Self::sanitize_items(&mut dict, std::mem::take(&mut self.center));\n        self.right = Self::sanitize_items(&mut dict, std::mem::take(&mut self.right));\n    }\n}\n\n// ===================== DEPRECATED STRUCTS =====================\n\n#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum OldWegItemSubtype {\n    File,\n    Folder,\n    App,\n    #[default]\n    UnknownV2_1_6,\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct OldWegItemData {\n    /// internal UUID to differentiate items\n    pub id: uuid::Uuid,\n    /// Subtype of the item (mandatory, but is optional for backward compatibility)\n    pub subtype: OldWegItemSubtype,\n    /// Application user model id.\n    pub umid: Option<String>,\n    /// path to file, folder or program.\n    pub path: PathBuf,\n    /// program to be executed\n    pub relaunch_program: String,\n    /// arguments to be passed to the relaunch program\n    pub relaunch_args: Option<RelaunchArguments>,\n    /// path where ejecute the relaunch command\n    pub relaunch_in: Option<PathBuf>,\n    /// display name of the item\n    pub display_name: String,\n    /// This intention is to prevent pinned state change, when this is neccesary\n    #[serde(skip_deserializing)]\n    pub pin_disabled: bool,\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/context_menu.rs",
    "content": "use crate::{resource::ResourceText, state::Alignment};\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export, optional_fields = nullable))]\npub struct ContextMenu {\n    pub identifier: uuid::Uuid,\n    pub items: Vec<ContextMenuItem>,\n    /// Alignment of the context menu on the X axis relative to the trigger point.\n    pub align_x: Option<Alignment>,\n    /// Alignment of the context menu on the Y axis relative to the trigger point.\n    pub align_y: Option<Alignment>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(tag = \"type\", rename_all_fields = \"camelCase\")]\npub enum ContextMenuItem {\n    Separator,\n    Item {\n        key: String,\n        icon: Option<String>,\n        label: String,\n        /// event name to be emitted on click, `key` will be sent as payload\n        callback_event: String,\n        /// If not null, the item will display a checkbox.\n        /// `checked` field will be send as payload.\n        #[ts(optional = nullable)]\n        checked: Option<bool>,\n        #[ts(optional = nullable)]\n        disabled: Option<bool>,\n    },\n    Submenu {\n        identifier: uuid::Uuid,\n        icon: Option<String>,\n        label: ResourceText,\n        items: Vec<ContextMenuItem>,\n    },\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/declaration.rs",
    "content": "use std::collections::HashSet;\n\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Deserializer, Serialize};\nuse ts_rs::TS;\n\nuse crate::resource::ResourceText;\n\n/// The Widget Settings Declaration is a list of configuration definitions.\n/// Each definition can be either a group (with nested items) or a direct configuration item.\n///\n/// This structure is used to render and store widget settings in a user-friendly way,\n/// matching the style of the settings window. With this approach, custom configuration\n/// windows for specific widgets are not needed.\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct WidgetSettingsDeclarationList(Vec<WidgetConfigDefinition>);\n\nimpl WidgetSettingsDeclarationList {\n    /// Checks if there are duplicate keys in the settings declaration.\\\n    /// Reserved keys like \"enabled\" and \"$instances\" are also checked.\n    pub fn there_are_duplicates(&self) -> bool {\n        let mut seen: HashSet<&str> = HashSet::new();\n\n        // Reserved keys that cannot be used\n        seen.insert(\"enabled\");\n        seen.insert(\"$instances\");\n\n        for definition in &self.0 {\n            if Self::collect_keys_recursive(definition, &mut seen) {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn collect_keys_recursive<'a>(\n        definition: &'a WidgetConfigDefinition,\n        seen: &mut HashSet<&'a str>,\n    ) -> bool {\n        match definition {\n            WidgetConfigDefinition::Group(group) => {\n                for item in &group.items {\n                    if Self::collect_keys_recursive(item, seen) {\n                        return true;\n                    }\n                }\n            }\n            WidgetConfigDefinition::Item(item) => {\n                let key = item.get_key();\n                if seen.contains(key) {\n                    return true;\n                }\n                seen.insert(key);\n            }\n        }\n        false\n    }\n}\n\n/// A widget configuration definition that can be either a group container or a settings item\n#[derive(Debug, Clone, Serialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub enum WidgetConfigDefinition {\n    /// A group that contains nested configuration items.\n    /// Groups are used to organize related settings with headers.\n    Group(WidgetConfigGroup),\n    /// A direct configuration item (untagged variant for simpler JSON structure)\n    #[serde(untagged)]\n    Item(Box<WidgetSettingItem>),\n}\n\n/// A group of widget configuration items with a label\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct WidgetConfigGroup {\n    /// Label for this group, can use `t::` prefix for translation\n    pub label: ResourceText,\n    /// Optional description or tooltip for this group\n    pub description: Option<ResourceText>,\n    /// List of items or nested groups in this group\n    pub items: Vec<WidgetConfigDefinition>,\n}\n\nimpl<'de> Deserialize<'de> for WidgetConfigDefinition {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        #[derive(Deserialize)]\n        struct GroupVariant {\n            group: WidgetConfigGroup,\n        }\n\n        let value =\n            serde_json::Value::deserialize(deserializer).map_err(serde::de::Error::custom)?;\n\n        // Try to deserialize as a group first\n        if let Ok(parsed) = GroupVariant::deserialize(value.clone()) {\n            return Ok(WidgetConfigDefinition::Group(parsed.group));\n        }\n\n        // Otherwise deserialize as an item\n        Ok(WidgetConfigDefinition::Item(Box::new(\n            serde_json::from_value(value).map_err(serde::de::Error::custom)?,\n        )))\n    }\n}\n\n/// Individual widget setting item with type-specific configuration\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(tag = \"type\")]\npub enum WidgetSettingItem {\n    /// Toggle switch for boolean values.\\\n    /// Renders as a switch/toggle component in the UI.\n    #[serde(alias = \"switch\")]\n    Switch(WidgetSettingSwitch),\n\n    /// Selection from a list of options.\\\n    /// Can be rendered as a dropdown list or inline buttons depending on subtype.\n    #[serde(alias = \"select\")]\n    Select(WidgetSettingSelect),\n\n    /// Text input field.\\\n    /// Supports both single-line and multiline input with optional validation.\n    #[serde(alias = \"text\", alias = \"input-text\")]\n    InputText(WidgetSettingInputText),\n\n    /// Numeric input field.\\\n    /// Renders as a number input with optional min/max/step constraints.\n    #[serde(alias = \"number\", alias = \"input-number\")]\n    InputNumber(WidgetSettingInputNumber),\n\n    /// Slider/range input for numeric values.\\\n    /// Renders as a visual slider component.\n    #[serde(alias = \"range\")]\n    Range(WidgetSettingRange),\n\n    /// Color picker input.\\\n    /// Allows users to select colors with optional alpha/transparency support.\n    #[serde(alias = \"color\")]\n    Color(WidgetSettingColor),\n}\n\nimpl WidgetSettingItem {\n    /// Returns the unique key identifying this setting item\n    pub fn get_key(&self) -> &str {\n        match self {\n            WidgetSettingItem::Switch(item) => &item.base.key,\n            WidgetSettingItem::Select(item) => &item.base.key,\n            WidgetSettingItem::InputText(item) => &item.base.key,\n            WidgetSettingItem::InputNumber(item) => &item.base.key,\n            WidgetSettingItem::Range(item) => &item.base.key,\n            WidgetSettingItem::Color(item) => &item.base.key,\n        }\n    }\n}\n\n/// Common fields shared across all widget setting items\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingBase {\n    /// Unique key for this setting, used to identify it in the configuration.\\\n    /// Must be unique within the widget. Duplicates will be ignored.\n    pub key: String,\n\n    /// Label to display to the user\n    pub label: ResourceText,\n\n    /// Optional detailed description shown under the label\n    pub description: Option<ResourceText>,\n\n    /// Optional tooltip icon with extra information\n    pub tip: Option<ResourceText>,\n\n    /// Whether this setting can be configured per monitor in monitor-specific settings\n    pub allow_set_by_monitor: bool,\n\n    /// Keys of settings that must be enabled for this item to be active.\\\n    /// Uses JavaScript truthy logic (!!value) to determine if dependency is met.\n    pub dependencies: Vec<String>,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingSwitch {\n    #[serde(flatten)]\n    pub base: WidgetSettingBase,\n    /// Default value for this switch\n    pub default_value: bool,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingSelect {\n    #[serde(flatten)]\n    pub base: WidgetSettingBase,\n    /// Default selected value (must match one of the option values)\n    pub default_value: String,\n    /// List of available options\n    pub options: Vec<WidgetSelectOption>,\n    /// How to render the select options\n    pub subtype: WidgetSelectSubtype,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingInputText {\n    #[serde(flatten)]\n    pub base: WidgetSettingBase,\n    /// Default text value\n    pub default_value: String,\n    /// Whether to render as a multiline textarea\n    pub multiline: bool,\n    /// Minimum text length validation\n    pub min_length: Option<u32>,\n    /// Maximum text length validation\n    pub max_length: Option<u32>,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingInputNumber {\n    #[serde(flatten)]\n    pub base: WidgetSettingBase,\n    /// Default numeric value\n    pub default_value: f64,\n    /// Minimum allowed value\n    pub min: Option<f64>,\n    /// Maximum allowed value\n    pub max: Option<f64>,\n    /// Step increment for input controls\n    pub step: Option<f64>,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingRange {\n    #[serde(flatten)]\n    pub base: WidgetSettingBase,\n    /// Default value for the range slider\n    pub default_value: f64,\n    /// Minimum value of the range\n    pub min: Option<f64>,\n    /// Maximum value of the range\n    pub max: Option<f64>,\n    /// Step increment for the slider\n    pub step: Option<f64>,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WidgetSettingColor {\n    #[serde(flatten)]\n    pub base: WidgetSettingBase,\n    /// Default color value (hex format: #RRGGBB or #RRGGBBAA)\n    pub default_value: String,\n    /// Whether to allow alpha/transparency channel\n    pub allow_alpha: bool,\n}\n\n/// An option in a select widget setting\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct WidgetSelectOption {\n    /// Optional React icon name to display with this option\n    pub icon: Option<String>,\n    /// Label to display for this option (can use `t::` prefix)\n    pub label: ResourceText,\n    /// Value to store when this option is selected (must be unique)\n    pub value: String,\n}\n\n/// Visual style for rendering select options\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WidgetSelectSubtype {\n    /// Render as a dropdown list (default)\n    #[default]\n    List,\n    /// Render as inline buttons/tabs\n    Inline,\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/interfaces.ts",
    "content": "export interface WidgetInformation {\n  /** decoded webview label */\n  label: string;\n  /** Will be present if the widget replicas is set to by monitor */\n  monitorId: string | null;\n  /** Will be present if the widget replicas is set to multiple */\n  instanceId: string | null;\n  /** params present on the webview label */\n  params: { readonly [key in string]?: string };\n}\n\nexport interface InitWidgetOptions {\n  /**\n   * Will auto size the widget to the content size of the element\n   * @example\n   *  autoSizeByContent: document.body,\n   *  autoSizeByContent: document.getElementById(\"root\"),\n   * @default undefined\n   */\n  autoSizeByContent?: HTMLElement | null;\n  /**\n   * Will save the position and size of the widget on change.\n   * This is intedeed to be used when the size and position of the widget is\n   * allowed to be changed by the user, Normally used on desktop widgets.\n   *\n   * @default widget.preset === \"Desktop\"\n   */\n  saveAndRestoreLastRect?: boolean;\n  /**\n   * Will disable the css animations on the widget when performace mode is set to Extreme\n   *\n   * @default true\n   */\n  disableCssAnimations?: boolean;\n}\n\nexport interface ReadyWidgetOptions {\n  /**\n   * If show the widget on Ready\n   *\n   * @default !widget.lazy\n   */\n  show?: boolean;\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/mod.rs",
    "content": "pub mod context_menu;\npub mod declaration;\n\nuse std::{collections::HashMap, path::Path};\n\nuse declaration::WidgetSettingsDeclarationList;\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\nuse crate::{\n    error::Result,\n    resource::{ResourceKind, ResourceMetadata, SluResource, WidgetId},\n    state::Plugin,\n    system_state::MonitorId,\n    utils::{search_resource_entrypoint, TsUnknown},\n    Point,\n};\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Widget {\n    /// Resource id ex: `@seelen/weg`\n    pub id: WidgetId,\n    /// Optional icon to be used on settings. This have to be a valid react icon name.\\\n    /// You can find all icons here: https://react-icons.github.io/react-icons/.\n    pub icon: Option<String>,\n    /// Widget metadata, as texts, tags, images, etc.\n    pub metadata: ResourceMetadata,\n\n    /// Widget settings declaration, this is esentially a struct to be used by an\n    /// builder to create the widget settings UI on the Settings window.\n    pub settings: WidgetSettingsDeclarationList,\n    /// Widget configuration preset\n    pub preset: WidgetPreset,\n    /// If true, the widget webview won't be created until it is requested via trigger action.\n    pub lazy: bool,\n    /// How many instances are allowed of this widget.\n    pub instances: WidgetInstanceMode,\n    /// If true, the widget will not be shown on the Settings Navigation as a Tab, but it will\n    /// still be available on the widgets full list.\n    pub hidden: bool,\n    /// If true, the memory leak of webview2 (https://github.com/tauri-apps/tauri/issues/4026)\n    /// workaround, will be no applied for instances of this widget.\n    pub no_memory_leak_workaround: bool,\n\n    /// Way to load the widget\n    pub loader: WidgetLoader,\n    /// Optional widget js code\n    pub js: Option<String>,\n    /// Optional widget css\n    pub css: Option<String>,\n    /// Optional widget html\n    pub html: Option<String>,\n\n    /// Optional list of plugins to be installed side the widget.\n    /// Use this if your widget needs interaction with other widgets like\n    /// adding a context menu item that shows this widget.\n    pub plugins: Vec<Plugin>,\n}\n\nimpl SluResource for Widget {\n    const KIND: ResourceKind = ResourceKind::Widget;\n\n    fn metadata(&self) -> &ResourceMetadata {\n        &self.metadata\n    }\n\n    fn metadata_mut(&mut self) -> &mut ResourceMetadata {\n        &mut self.metadata\n    }\n\n    fn load_from_folder(path: &Path) -> Result<Widget> {\n        let file = search_resource_entrypoint(path).ok_or(\"No metadata file found\")?;\n        let mut widget = Self::load_from_file(&file)?;\n\n        for stem in [\"index.js\", \"main.js\", \"mod.js\"] {\n            if path.join(stem).exists() {\n                widget.js = Some(std::fs::read_to_string(path.join(stem))?);\n                break;\n            }\n        }\n\n        for stem in [\"index.css\", \"main.css\", \"mod.css\"] {\n            if path.join(stem).exists() {\n                widget.css = Some(std::fs::read_to_string(path.join(stem))?);\n                break;\n            }\n        }\n\n        for stem in [\"index.html\", \"main.html\", \"mod.html\"] {\n            if path.join(stem).exists() {\n                widget.html = Some(std::fs::read_to_string(path.join(stem))?);\n                break;\n            }\n        }\n\n        Ok(widget)\n    }\n\n    fn validate(&self) -> Result<()> {\n        if self.settings.there_are_duplicates() {\n            return Err(\"Widget settings declaration have duplicated keys\".into());\n        }\n        for plugin in &self.plugins {\n            plugin.validate()?\n        }\n        Ok(())\n    }\n\n    fn sanitize(&mut self) {\n        for plugin in &mut self.plugins {\n            plugin.sanitize()\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WidgetInstanceMode {\n    /// Default behavior, only one instance of this widget is allowed.\n    /// This is useful for widgets intended to work as custom config window.\n    #[default]\n    Single,\n    /// The widget is allowed to have multiple instances.\\\n    /// This allow to the user manually create more instances of this same widget.\n    Multiple,\n    /// Seelen UI will create an instance of this widget per each monitor connected.\\\n    /// This can be configured by the user using per monitor settings.\\\n    ReplicaByMonitor,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WidgetLoader {\n    /// Used for old internal widgets, similar to `Internal`.\n    Legacy,\n    /// Used for internal bundled widgets, this will load the code from internal resources.\n    InternalReact,\n    /// Used for internal bundled widgets, this will load the code from internal resources.\n    Internal,\n    /// Used for third party widgets, this will load the code from the `js`, `css`, and `html` fields\n    #[default]\n    ThirdParty,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WidgetPreset {\n    /// No special behavior, all should be manually configured\n    #[default]\n    None,\n    /// Always on bottom, no title bar, etc. Resizable by default.\n    Desktop,\n    /// Always on top, no title bar, etc.\n    Overlay,\n    /// Same as overlay, but will be automatically closed on unfocus;\n    /// Also this type of widgets can be manually open/closed/show/hide by other widgets or plugins.\n    /// On show the widget will be at the specified position, could be custom one, or will take the mouse cursor position.\n    ///\n    /// If a widget is of this type, the enabled property won't determine the visibility of the widget,\n    /// as this widget is only shown when explicitly requested.\n    ///\n    /// Widget instances mode will be ignored for this type of widgets, As popups should be always single instance.\n    Popup,\n}\n\n/// Arguments that could be passed on the trigger widget function, widgets decides if use it or not.\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export, optional_fields = nullable))]\npub struct WidgetTriggerPayload {\n    pub id: WidgetId,\n    pub monitor_id: Option<MonitorId>,\n    pub instance_id: Option<uuid::Uuid>,\n    /// Desired position to show the widget\n    pub desired_position: Option<Point>,\n    /// This will be used to align the widget at the desired position\n    /// - start will set the widget at the left of point,\n    /// - center will set the widget at the center of point,\n    /// - end will set the widget at the right of point\n    pub align_x: Option<Alignment>,\n    /// This will be used to align the widget at the desired position\n    /// - start will set the widget at the top of point,\n    /// - center will set the widget at the center of point,\n    /// - end will set the widget at the bottom of point\n    pub align_y: Option<Alignment>,\n    /// Custom arguments to be used by the widget recieving the trigger.\n    /// this can be anything, and depends on the widget to evaluate them.\n    pub custom_args: Option<HashMap<String, TsUnknown>>,\n}\n\nimpl WidgetTriggerPayload {\n    pub fn new(id: WidgetId) -> Self {\n        Self {\n            id,\n            monitor_id: None,\n            instance_id: None,\n            desired_position: None,\n            align_x: None,\n            align_y: None,\n            custom_args: None,\n        }\n    }\n\n    pub fn add_custom_arg(&mut self, key: impl AsRef<str>, value: impl Into<TsUnknown>) {\n        if self.custom_args.is_none() {\n            self.custom_args = Some(HashMap::new());\n        }\n        self.custom_args\n            .as_mut()\n            .unwrap()\n            .insert(key.as_ref().to_string(), value.into());\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum Alignment {\n    Start,\n    Center,\n    End,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export, repr(enum = name)))]\npub enum WidgetStatus {\n    /// Widget has been registered, but not yet loaded\n    Pending,\n    /// Webview window is being created\n    Creating,\n    /// Widget javascript, html and css is being loaded\n    Mounting,\n    /// Widget loaded and is ready\n    Ready,\n    /// Webview window failed to be created.\n    CrashedOnCreation,\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/mod.ts",
    "content": "import {\n  type Frame,\n  type Rect,\n  type ThirdPartyWidgetSettings,\n  type Widget as IWidget,\n  type WidgetConfigDefinition,\n  type WidgetId,\n  WidgetPreset,\n  type WidgetSettingItem,\n  WidgetStatus,\n  type WidgetTriggerPayload,\n} from \"@seelen-ui/types\";\nimport { invoke, SeelenCommand, SeelenEvent } from \"../../handlers/mod.ts\";\nimport { decodeBase64Url } from \"@std/encoding\";\nimport { debounce } from \"../../utils/async.ts\";\nimport { WidgetAutoSizer } from \"./sizing.ts\";\nimport { adjustPositionByPlacement, fitIntoMonitor, initMonitorsState } from \"./positioning.ts\";\nimport { startThemingTool } from \"../theme/theming.ts\";\nimport type { InitWidgetOptions, ReadyWidgetOptions, WidgetInformation } from \"./interfaces.ts\";\nimport { disableAnimationsOnPerformanceMode } from \"./performance.ts\";\nimport { getCurrentWebview, type Webview } from \"@tauri-apps/api/webview\";\nimport { getCurrentWindow, type Window } from \"@tauri-apps/api/window\";\n\ninterface WidgetInternalState {\n  hwnd: number;\n  initialized: boolean;\n  ready: boolean;\n  position: {\n    x: number;\n    y: number;\n  };\n  size: {\n    width: number;\n    height: number;\n  };\n  firstFocus: boolean;\n}\n\n/**\n * Represents the widget instance running in the current webview\n */\nexport class Widget {\n  /**\n   * Alternative accesor for the current running widget.\\\n   * Will throw if the library is being used on a non Seelen UI environment\n   */\n  static getCurrent(): Widget {\n    const scope = globalThis as ExtendedGlobalThis;\n    if (!scope.__SLU_WIDGET) {\n      throw new Error(\"The library is being used on a non Seelen UI environment\");\n    }\n    return (\n      scope.__SLU_WIDGET_INSTANCE || (scope.__SLU_WIDGET_INSTANCE = new Widget(scope.__SLU_WIDGET))\n    );\n  }\n\n  /** The current running widget */\n  static get self(): Widget {\n    return Widget.getCurrent();\n  }\n\n  /** widget id */\n  public readonly id: WidgetId;\n  /** widget definition */\n  public readonly def: IWidget;\n  /** decoded widget instance information */\n  public readonly decoded: WidgetInformation;\n  /** current webview where the widget is running */\n  public readonly webview: Webview;\n  /** current window where the widget is running */\n  public readonly window: Window;\n\n  private autoSizer?: WidgetAutoSizer;\n\n  private runtimeState: WidgetInternalState = {\n    hwnd: 0,\n    initialized: false,\n    ready: false,\n    position: {\n      x: 0,\n      y: 0,\n    },\n    size: {\n      width: 0,\n      height: 0,\n    },\n    firstFocus: true,\n  };\n\n  private constructor(widget: IWidget) {\n    this.def = widget;\n    this.webview = getCurrentWebview();\n    this.window = getCurrentWindow();\n\n    const [id, query] = getDecodedWebviewLabel();\n    const params = new URLSearchParams(query);\n    const paramsObj = Object.freeze(Object.fromEntries(params));\n\n    this.id = id as WidgetId;\n    this.decoded = Object.freeze({\n      label: `${id}${query ? `?${query}` : \"\"}`,\n      monitorId: paramsObj.monitorId || null,\n      instanceId: paramsObj.instanceId || null,\n      params: Object.freeze(Object.fromEntries(params)),\n    });\n  }\n\n  /** Returns the current window id of the widget */\n  get windowId(): number {\n    return this.runtimeState.hwnd;\n  }\n\n  /** Returns the current position and size of the widget */\n  get frame(): Frame {\n    return {\n      x: this.runtimeState.position.x,\n      y: this.runtimeState.position.y,\n      width: this.runtimeState.size.width,\n      height: this.runtimeState.size.height,\n    };\n  }\n\n  /** Returns the default config of the widget, declared on the widget definition */\n  public getDefaultConfig(): ThirdPartyWidgetSettings {\n    const config: ThirdPartyWidgetSettings = { enabled: true };\n    for (const definition of this.def.settings) {\n      Object.assign(config, getDefinitionDefaultValues(definition));\n    }\n    return config;\n  }\n\n  private applyInvisiblePreset(): Array<Promise<void>> {\n    return [\n      this.window.setDecorations(false), // no title bar\n      this.window.setShadow(false), // no shadows\n      // hide from native shell\n      this.window.setSkipTaskbar(true),\n      // as a (desktop/overlay) widget we don't wanna allow nothing of these\n      this.window.setMinimizable(false),\n      this.window.setMaximizable(false),\n      this.window.setClosable(false),\n    ];\n  }\n\n  /** Will apply the recommended settings for a desktop widget */\n  private async applyDesktopPreset(): Promise<void> {\n    await Promise.all([...this.applyInvisiblePreset(), this.window.setAlwaysOnBottom(true)]);\n  }\n\n  /** Will apply the recommended settings for an overlay widget */\n  private async applyOverlayPreset(): Promise<void> {\n    await Promise.all([...this.applyInvisiblePreset(), this.window.setAlwaysOnTop(true)]);\n  }\n\n  /** Will apply the recommended settings for a popup widget */\n  private async applyPopupPreset(): Promise<void> {\n    await Promise.all([...this.applyInvisiblePreset(), this.window.setAlwaysOnTop(true)]);\n\n    const hideWidget = debounce(() => {\n      this.hide(true);\n    }, 100);\n\n    this.window.onFocusChanged(({ payload: focused }) => {\n      if (focused) {\n        hideWidget.cancel();\n      } else {\n        hideWidget();\n      }\n    });\n\n    this.onTrigger(async ({ desiredPosition, alignX, alignY }) => {\n      // avoid flickering when clicking a button that triggers the widget\n      hideWidget.cancel();\n\n      if (this.autoSizer && alignX && alignY) {\n        this.autoSizer.originX = alignX;\n        this.autoSizer.originY = alignY;\n      }\n\n      if (desiredPosition) {\n        const adjusted = adjustPositionByPlacement({\n          frame: {\n            x: desiredPosition.x,\n            y: desiredPosition.y,\n            width: this.runtimeState.size.width,\n            height: this.runtimeState.size.height,\n          },\n          originX: alignX,\n          originY: alignY,\n        });\n\n        await this.setPosition({\n          left: adjusted.x,\n          top: adjusted.y,\n          right: adjusted.x + adjusted.width,\n          bottom: adjusted.y + adjusted.height,\n        });\n      }\n\n      await this.show();\n      await this.focus();\n    });\n  }\n\n  /**\n   * Will restore the saved position and size of the widget on start,\n   * after that will store the position and size of the widget on change.\n   */\n  public async persistPositionAndSize(): Promise<void> {\n    const storage = globalThis.window.localStorage;\n\n    const [x, y, width, height] = [`x`, `y`, `width`, `height`].map((k) => storage.getItem(`${k}`));\n    if (x && y && width && height) {\n      const frame = {\n        x: Number(x),\n        y: Number(y),\n        width: Number(width),\n        height: Number(height),\n      };\n\n      const safeFrame = fitIntoMonitor(frame);\n      await this.setPosition({\n        left: safeFrame.x,\n        top: safeFrame.y,\n        right: safeFrame.x + safeFrame.width,\n        bottom: safeFrame.y + safeFrame.height,\n      });\n    }\n\n    this.window.onMoved(\n      debounce((e) => {\n        const { x, y } = e.payload;\n        storage.setItem(`x`, x.toString());\n        storage.setItem(`y`, y.toString());\n        console.info(`Widget position saved: ${x} ${y}`);\n      }, 500),\n    );\n\n    this.window.onResized(\n      debounce((e) => {\n        const { width, height } = e.payload;\n        storage.setItem(`width`, width.toString());\n        storage.setItem(`height`, height.toString());\n        console.info(`Widget size saved: ${width} ${height}`);\n      }, 500),\n    );\n  }\n\n  /**\n   * Will initialize the widget based on the preset and mark it as `pending`, this function won't show the widget.\n   * This should be called before any other action on the widget. After this you should call\n   * `ready` to mark the widget as ready and show it.\n   */\n  public async init(options: InitWidgetOptions = {}): Promise<void> {\n    if (this.runtimeState.initialized) {\n      console.warn(`Widget already initialized`);\n      return;\n    }\n\n    this.runtimeState.hwnd = await invoke(SeelenCommand.GetSelfWindowId);\n    this.runtimeState.initialized = true;\n\n    if (options.autoSizeByContent) {\n      this.autoSizer = new WidgetAutoSizer(this, options.autoSizeByContent);\n    } else if (options.saveAndRestoreLastRect ?? this.def.preset === WidgetPreset.Desktop) {\n      await this.persistPositionAndSize();\n    }\n\n    switch (this.def.preset) {\n      case WidgetPreset.None:\n        break;\n      case WidgetPreset.Desktop:\n        await this.applyDesktopPreset();\n        break;\n      case WidgetPreset.Overlay:\n        await this.applyOverlayPreset();\n        break;\n      case WidgetPreset.Popup:\n        await this.applyPopupPreset();\n        break;\n    }\n\n    await startThemingTool();\n    await initMonitorsState();\n\n    if (options.disableCssAnimations ?? true) {\n      await disableAnimationsOnPerformanceMode();\n    } else {\n      console.trace(\"Animations won't be disabled because widget configuration\");\n    }\n\n    // state initialization\n    this.runtimeState.size = await this.window.outerSize();\n    this.runtimeState.position = await this.window.outerPosition();\n\n    this.window.onResized((e) => {\n      this.runtimeState.size.width = e.payload.width;\n      this.runtimeState.size.height = e.payload.height;\n    });\n\n    this.window.onMoved((e) => {\n      this.runtimeState.position.x = e.payload.x;\n      this.runtimeState.position.y = e.payload.y;\n    });\n  }\n\n  /**\n   * Will mark the widget as `ready` and pool pending triggers.\n   *\n   * If the widget is not lazy this will inmediately show the widget.\n   * Lazy widget should be shown on trigger action.\n   */\n  public async ready(options: ReadyWidgetOptions = {}): Promise<void> {\n    const { show = !this.def.lazy } = options;\n\n    if (!this.runtimeState.initialized) {\n      throw new Error(`Widget was not initialized before ready`);\n    }\n\n    if (this.runtimeState.ready) {\n      console.warn(`Widget is already ready`);\n      return;\n    }\n\n    this.runtimeState.ready = true;\n    await this.autoSizer?.execute();\n\n    if (show && !(await this.window.isVisible())) {\n      await this.show();\n      await this.focus();\n    }\n\n    // this will mark the widget as ready, and send pending trigger event if exists\n    await invoke(SeelenCommand.SetCurrentWidgetStatus, { status: WidgetStatus.Ready });\n  }\n\n  public onTrigger(cb: (args: WidgetTriggerPayload) => void): void {\n    this.webview.listen<WidgetTriggerPayload>(SeelenEvent.WidgetTriggered, ({ payload }) => {\n      cb(payload);\n    });\n  }\n\n  /** If animations are enabled this will animate the movement of the widget */\n  public setPosition(rect: Rect): Promise<void> {\n    this.runtimeState.position.x = rect.left;\n    this.runtimeState.position.y = rect.top;\n    this.runtimeState.size.width = rect.right - rect.left;\n    this.runtimeState.size.height = rect.bottom - rect.top;\n\n    return invoke(SeelenCommand.SetSelfPosition, {\n      rect: {\n        left: Math.round(rect.left),\n        top: Math.round(rect.top),\n        right: Math.round(rect.right),\n        bottom: Math.round(rect.bottom),\n      },\n    });\n  }\n\n  public async show(): Promise<void> {\n    debouncedClose.cancel();\n    await this.window.show();\n  }\n\n  /** Will force foreground the widget */\n  public async focus(): Promise<void> {\n    if (this.runtimeState.firstFocus) {\n      await getCurrentWebview().setFocus();\n      this.runtimeState.firstFocus = false;\n    }\n    await invoke(SeelenCommand.RequestFocus, { hwnd: this.runtimeState.hwnd }).catch(() => {\n      console.warn(`Failed to focus widget: ${this.decoded.label}`);\n    });\n  }\n\n  public hide(closeAfterInactivity?: boolean): void {\n    this.window.hide();\n    if (closeAfterInactivity) {\n      debouncedClose();\n    }\n  }\n}\n\nconst debouncedClose = debounce(() => {\n  Widget.self.window.close();\n}, 30_000);\n\ntype ExtendedGlobalThis = typeof globalThis & {\n  __SLU_WIDGET?: IWidget;\n  __SLU_WIDGET_INSTANCE?: Widget;\n};\n\nexport const SeelenSettingsWidgetId: WidgetId = \"@seelen/settings\" as WidgetId;\nexport const SeelenPopupWidgetId: WidgetId = \"@seelen/popup\" as WidgetId;\nexport const SeelenWegWidgetId: WidgetId = \"@seelen/weg\" as WidgetId;\nexport const SeelenToolbarWidgetId: WidgetId = \"@seelen/fancy-toolbar\" as WidgetId;\nexport const SeelenWindowManagerWidgetId: WidgetId = \"@seelen/window-manager\" as WidgetId;\nexport const SeelenWallWidgetId: WidgetId = \"@seelen/wallpaper-manager\" as WidgetId;\n\nfunction getDecodedWebviewLabel(): [WidgetId, string | undefined] {\n  const encondedLabel = getCurrentWebview().label;\n  const decodedLabel = new TextDecoder().decode(decodeBase64Url(encondedLabel));\n  const [id, query] = decodedLabel.split(\"?\");\n  if (!id) {\n    throw new Error(\"Missing widget id on webview label\");\n  }\n  return [id as WidgetId, query];\n}\n\nfunction getDefinitionDefaultValues(definition: WidgetConfigDefinition): Record<string, unknown> {\n  const config: Record<string, unknown> = {};\n\n  // Check if it's a group (has \"group\" property)\n  if (\"group\" in definition) {\n    // Recursively process all items in the group\n    for (const item of definition.group.items) {\n      Object.assign(config, getDefinitionDefaultValues(item));\n    }\n  } else {\n    // It's a setting item, extract key and defaultValue\n    const item = definition as WidgetSettingItem;\n    if (\"key\" in item && \"defaultValue\" in item) {\n      config[item.key] = item.defaultValue;\n    }\n  }\n\n  return config;\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/performance.ts",
    "content": "import { PerformanceMode } from \"@seelen-ui/types\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe } from \"../../handlers/mod.ts\";\n\nexport async function disableAnimationsOnPerformanceMode(): Promise<void> {\n  const initial = await invoke(SeelenCommand.StateGetPerformanceMode);\n  setDisableAnimations(initial);\n  subscribe(SeelenEvent.StatePerformanceModeChanged, (e) => {\n    setDisableAnimations(e.payload);\n  });\n}\n\nfunction setDisableAnimations(mode: PerformanceMode): void {\n  const root = document.documentElement;\n  if (mode === PerformanceMode.Extreme) {\n    root.setAttribute(\"data-animations-off\", \"\");\n  } else {\n    root.removeAttribute(\"data-animations-off\");\n  }\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/positioning.ts",
    "content": "import { Alignment, type Frame, type PhysicalMonitor } from \"@seelen-ui/types\";\nimport { invoke, subscribe } from \"../../handlers/mod.ts\";\nimport { SeelenCommand } from \"@seelen-ui/lib\";\nimport { SeelenEvent } from \"../../handlers/events.ts\";\n\ninterface args {\n  frame: Frame;\n  originX?: Alignment | null;\n  originY?: Alignment | null;\n}\n\nconst monitors = {\n  value: [] as PhysicalMonitor[],\n};\n\nexport async function initMonitorsState(): Promise<void> {\n  monitors.value = await invoke(SeelenCommand.SystemGetMonitors);\n  subscribe(SeelenEvent.SystemMonitorsChanged, ({ payload }) => {\n    monitors.value = payload;\n  });\n}\n\nfunction monitorFromPoint(x: number, y: number): PhysicalMonitor | undefined {\n  return monitors.value.find(\n    (m) => m.rect.left <= x && x < m.rect.right && m.rect.top <= y && y < m.rect.bottom,\n  );\n}\n\nfunction primaryMonitor(): PhysicalMonitor | undefined {\n  return monitors.value.find((m) => m.isPrimary);\n}\n\nexport function adjustPositionByPlacement({\n  frame: { x, y, width, height },\n  originX,\n  originY,\n}: args): Frame {\n  if (originX === Alignment.Center) {\n    x -= width / 2;\n  } else if (originX === Alignment.End) {\n    x -= width;\n  }\n\n  if (originY === Alignment.Center) {\n    y -= height / 2;\n  } else if (originY === Alignment.End) {\n    y -= height;\n  }\n\n  const newFrame = fitIntoMonitor({ x, y, width, height });\n  return {\n    x: Math.round(newFrame.x),\n    y: Math.round(newFrame.y),\n    width: Math.round(newFrame.width),\n    height: Math.round(newFrame.height),\n  };\n}\n\nexport function fitIntoMonitor({ x, y, width, height }: Frame): Frame {\n  const monitor = monitorFromPoint(Math.round(x), Math.round(y)) || primaryMonitor();\n  if (monitor) {\n    width = Math.min(width, monitor.rect.right - monitor.rect.left);\n    height = Math.min(height, monitor.rect.bottom - monitor.rect.top);\n\n    const x2 = x + width;\n    const y2 = y + height;\n\n    // check left edge\n    if (x < monitor.rect.left) {\n      x = monitor.rect.left;\n    }\n\n    // check top edge\n    if (y < monitor.rect.top) {\n      y = monitor.rect.top;\n    }\n\n    // check right edge\n    if (x2 > monitor.rect.right) {\n      x = monitor.rect.right - width;\n    }\n\n    // check bottom edge\n    if (y2 > monitor.rect.bottom) {\n      y = monitor.rect.bottom - height;\n    }\n  }\n\n  return {\n    x,\n    y,\n    width,\n    height,\n  };\n}\n"
  },
  {
    "path": "libs/core/src/state/widget/sizing.ts",
    "content": "import { PhysicalSize } from \"@tauri-apps/api/dpi\";\nimport type { Widget } from \"./mod.ts\";\nimport { Alignment } from \"@seelen-ui/types\";\n\nexport class WidgetAutoSizer {\n  /** From which side the widget will grow */\n  originX: Alignment = Alignment.Start;\n  /** From which side the widget will grow */\n  originY: Alignment = Alignment.Start;\n\n  constructor(\n    private widget: Widget,\n    private element: HTMLElement,\n  ) {\n    this.execute = this.execute.bind(this);\n    this.setup();\n  }\n\n  private setup(): () => void {\n    // Disable resizing by the user\n    this.widget.window.setResizable(false);\n\n    const observer = new ResizeObserver(this.execute);\n    observer.observe(this.element, {\n      box: \"border-box\",\n    });\n\n    return () => {\n      observer.disconnect();\n    };\n  }\n\n  async execute(): Promise<void> {\n    const { x, y, width, height } = this.widget.frame;\n\n    const frame = {\n      x,\n      y,\n      width: Math.ceil(this.element.scrollWidth * globalThis.window.devicePixelRatio),\n      height: Math.ceil(this.element.scrollHeight * globalThis.window.devicePixelRatio),\n    };\n\n    const widthDiff = frame.width - width;\n    const heightDiff = frame.height - height;\n\n    // Only update if the difference is more than 1px (avoid infinite loops from decimal differences)\n    if (widthDiff === 0 && heightDiff === 0) {\n      return;\n    }\n\n    console.trace(`Auto resize from ${width}x${height} to ${frame.width}x${frame.height}`);\n\n    if (this.originX === Alignment.Center) {\n      frame.x -= widthDiff / 2;\n    } else if (this.originX === Alignment.End) {\n      frame.x -= widthDiff;\n    }\n\n    if (this.originY === Alignment.Center) {\n      frame.y -= heightDiff / 2;\n    } else if (this.originY === Alignment.End) {\n      frame.y -= heightDiff;\n    }\n\n    // only update size no position on this case\n    if (frame.x === x && frame.y === y) {\n      await this.widget.window.setSize(new PhysicalSize(frame.width, frame.height));\n      return;\n    }\n\n    await this.widget.setPosition({\n      left: frame.x,\n      top: frame.y,\n      right: frame.x + frame.width,\n      bottom: frame.y + frame.height,\n    });\n  }\n}\n"
  },
  {
    "path": "libs/core/src/state/wm_layout.rs",
    "content": "use std::{cell::Cell, collections::HashMap};\n\nuse crate::system_state::MonitorId;\n\n#[derive(Debug, Serialize, Deserialize, JsonSchema, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct WmRenderTree(pub HashMap<MonitorId, WindowManagerLayout>);\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct WmNode {\n    /// Type determines the behavior of the node\n    #[serde(rename = \"type\")]\n    pub kind: WmNodeKind,\n    /// Lifetime of the node\n    pub lifetime: WmNodeLifetime,\n    /// Order in how the tree will be traversed (1 = first, 2 = second, etc.)\n    pub priority: u32,\n    /// How much of the remaining space this node will take\n    pub grow_factor: Cell<f32>,\n    /// Math Condition for the node to be shown, e.g: n >= 3\n    pub condition: Option<String>,\n    /// Active window handle (HWND) in the node.\n    #[serde(skip_deserializing)]\n    pub active: Option<isize>,\n    /// Window handles (HWND) in the node.\n    #[serde(skip_deserializing)]\n    pub windows: Vec<isize>,\n    /// Child nodes, this field is ignored for leaf and stack nodes.\n    pub children: Vec<WmNode>,\n    /// Max amount of windows in the stack. Set it to `null` for unlimited stack.\\\n    /// This field is ignored for non-stack nodes\n    pub max_stack_size: Option<usize>,\n}\n\nunsafe impl Send for WmNode {}\nunsafe impl Sync for WmNode {}\n\nimpl WmNode {\n    pub fn len(&self) -> usize {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => self.windows.len(),\n            WmNodeKind::Vertical | WmNodeKind::Horizontal => {\n                self.children.iter().map(|n| n.len()).sum()\n            }\n        }\n    }\n\n    pub fn capacity(&self) -> usize {\n        match self.kind {\n            WmNodeKind::Leaf => 1usize,\n            WmNodeKind::Stack => self.max_stack_size.unwrap_or(usize::MAX),\n            WmNodeKind::Vertical | WmNodeKind::Horizontal => {\n                let mut total = 0usize;\n                for n in &self.children {\n                    total = total.saturating_add(n.capacity());\n                }\n                total\n            }\n        }\n    }\n\n    pub fn is_full(&self) -> bool {\n        match self.kind {\n            WmNodeKind::Leaf => !self.windows.is_empty(),\n            WmNodeKind::Stack => self.max_stack_size.is_some_and(|max| self.len() >= max),\n            WmNodeKind::Vertical | WmNodeKind::Horizontal => {\n                self.children.iter().all(|n| n.is_full())\n            }\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n}\n\nimpl Default for WmNode {\n    fn default() -> Self {\n        Self {\n            kind: WmNodeKind::Leaf,\n            lifetime: WmNodeLifetime::Permanent,\n            priority: 1,\n            grow_factor: Cell::new(1.0),\n            condition: None,\n            active: None,\n            windows: Vec::new(),\n            children: Vec::new(),\n            max_stack_size: Some(3),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WmNodeKind {\n    /// node will not grow, this is the final node.\n    Leaf,\n    /// node will grow on z-axis\n    Stack,\n    /// node will grow on y-axis\n    Vertical,\n    /// node will grow on x-axis\n    Horizontal,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(repr(enum = name))]\npub enum WmNodeLifetime {\n    Temporal,\n    #[default]\n    Permanent,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct WindowManagerLayout {\n    pub structure: WmNode,\n    #[serde(skip_deserializing)]\n    pub floating_windows: Vec<isize>,\n}\n\nimpl Default for WindowManagerLayout {\n    fn default() -> Self {\n        Self {\n            structure: WmNode {\n                kind: WmNodeKind::Stack,\n                max_stack_size: None,\n                ..Default::default()\n            },\n            floating_windows: Vec::new(),\n        }\n    }\n}\n"
  },
  {
    "path": "libs/core/src/state/workspaces/mod.rs",
    "content": "use std::collections::{HashMap, HashSet};\n\nuse uuid::Uuid;\n\nuse crate::{error::Result, identifier_impl, resource::WallpaperId, system_state::MonitorId};\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[serde(default, rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct VirtualDesktops {\n    /// Workspaces per monitor\n    pub monitors: HashMap<MonitorId, VirtualDesktopMonitor>,\n    /// pinned windows will be not affected by switching workspaces\n    pub pinned: Vec<isize>,\n}\n\nimpl VirtualDesktops {\n    pub fn sanitize(&mut self) {\n        let mut seen = HashSet::new();\n        self.pinned.retain(|x| seen.insert(*x));\n\n        for monitor in self.monitors.values_mut() {\n            monitor.sanitize();\n            for workspace in &mut monitor.workspaces {\n                workspace.windows.retain(|x| seen.insert(*x));\n            }\n        }\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct VirtualDesktopMonitor {\n    pub workspaces: Vec<DesktopWorkspace>,\n    active_workspace: WorkspaceId,\n}\n\nimpl VirtualDesktopMonitor {\n    pub fn create() -> Self {\n        let workspace = DesktopWorkspace::create();\n        let current_workspace = workspace.id.clone();\n        Self {\n            workspaces: vec![workspace],\n            active_workspace: current_workspace,\n        }\n    }\n\n    pub fn sanitize(&mut self) {\n        if self.workspaces.is_empty() {\n            let workspace = DesktopWorkspace::create();\n            self.active_workspace = workspace.id.clone();\n            self.workspaces.push(workspace);\n        }\n\n        if !self\n            .workspaces\n            .iter()\n            .any(|ws| ws.id == self.active_workspace)\n        {\n            self.active_workspace = self.workspaces[0].id.clone();\n        }\n    }\n\n    pub fn active_workspace_id(&self) -> &WorkspaceId {\n        &self.active_workspace\n    }\n\n    pub fn active_workspace(&self) -> &DesktopWorkspace {\n        self.workspaces\n            .iter()\n            .find(|w| w.id == self.active_workspace)\n            .expect(\"current workspace not found\")\n    }\n\n    pub fn active_workspace_mut(&mut self) -> &mut DesktopWorkspace {\n        self.workspaces\n            .iter_mut()\n            .find(|w| w.id == self.active_workspace)\n            .expect(\"current workspace not found\")\n    }\n\n    /// Set the current workspace, return error if the workspace doesn't exist\n    pub fn set_active_workspace(&mut self, workspace_id: &WorkspaceId) -> Result<()> {\n        if self.workspaces.iter().any(|w| &w.id == workspace_id) {\n            self.active_workspace = workspace_id.clone();\n            Ok(())\n        } else {\n            Err(\"Invalid workspace id\".into())\n        }\n    }\n\n    /// Add a new workspace and return its id\n    pub fn add_workspace(&mut self) -> WorkspaceId {\n        let workspace = DesktopWorkspace::create();\n        let workspace_id = workspace.id.clone();\n        self.workspaces.push(workspace);\n        workspace_id\n    }\n\n    /// Remove a workspace by id\n    /// - Does nothing if there's only 1 workspace (minimum required)\n    /// - If the removed workspace was current, switches to the side one\n    /// - Moves all windows from the removed workspace to the side one in the array\n    pub fn remove_workspace(&mut self, workspace_id: &WorkspaceId) -> Result<()> {\n        // Don't remove if it's the only workspace\n        if self.workspaces.len() <= 1 {\n            return Err(\"Cannot remove the last workspace\".into());\n        }\n\n        // Find the index of the workspace to remove\n        let idx_to_delete = self\n            .workspaces\n            .iter()\n            .position(|w| &w.id == workspace_id)\n            .ok_or(\"Workspace not found\")?;\n\n        let idx_to_move = if idx_to_delete == 0 {\n            1\n        } else {\n            idx_to_delete - 1\n        };\n\n        // If the removed workspace was current, switch to the previous one\n        if &self.active_workspace == workspace_id {\n            self.active_workspace = self.workspaces[idx_to_move].id.clone();\n        }\n\n        // Move windows to the side workspace\n        let windows = self.workspaces[idx_to_delete].windows.clone();\n        for window in windows {\n            self.workspaces[idx_to_move].windows.push(window);\n        }\n        self.workspaces.remove(idx_to_delete);\n        Ok(())\n    }\n\n    /// Rename a workspace by id\n    pub fn rename_workspace(\n        &mut self,\n        workspace_id: &WorkspaceId,\n        name: Option<String>,\n    ) -> Result<()> {\n        let workspace = self\n            .workspaces\n            .iter_mut()\n            .find(|w| &w.id == workspace_id)\n            .ok_or(\"Workspace not found\")?;\n        workspace.name = name;\n        Ok(())\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct DesktopWorkspace {\n    pub id: WorkspaceId,\n    pub name: Option<String>,\n    /// react-icon icon name\n    pub icon: Option<String>,\n    pub wallpaper: Option<WallpaperId>,\n    #[serde(default)]\n    pub windows: Vec<isize>,\n}\n\nimpl DesktopWorkspace {\n    pub fn create() -> Self {\n        Self {\n            id: WorkspaceId(Uuid::new_v4().to_string()),\n            name: None,\n            icon: None,\n            wallpaper: None,\n            windows: Vec::new(),\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]\npub struct WorkspaceId(pub String);\n\nidentifier_impl!(WorkspaceId, String);\n"
  },
  {
    "path": "libs/core/src/system_state/bluetooth/appearance_values.yml",
    "content": "# https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/core/appearance_values.yaml\nappearance_values:\n  - category: 0x000\n    name: Unknown\n  - category: 0x001\n    name: Phone\n  - category: 0x002\n    name: Computer\n    subcategory:\n      - value: 0x01\n        name: Desktop Workstation\n      - value: 0x02\n        name: Server-class Computer\n      - value: 0x03\n        name: Laptop\n      - value: 0x04\n        name: Handheld PC/PDA (clamshell)\n      - value: 0x05\n        name: Palm-size PC/PDA\n      - value: 0x06\n        name: Wearable computer (watch size)\n      - value: 0x07\n        name: Tablet\n      - value: 0x08\n        name: Docking Station\n      - value: 0x09\n        name: All in One\n      - value: 0x0A\n        name: Blade Server\n      - value: 0x0B\n        name: Convertible\n      - value: 0x0C\n        name: Detachable\n      - value: 0x0D\n        name: IoT Gateway\n      - value: 0x0E\n        name: Mini PC\n      - value: 0x0F\n        name: Stick PC\n  - category: 0x003\n    name: Watch\n    subcategory:\n      - value: 0x01\n        name: Sports Watch\n      - value: 0x02\n        name: Smartwatch\n  - category: 0x004\n    name: Clock\n  - category: 0x005\n    name: Display\n  - category: 0x006\n    name: Remote Control\n  - category: 0x007\n    name: Eye-glasses\n  - category: 0x008\n    name: Tag\n  - category: 0x009\n    name: Keyring\n  - category: 0x00A\n    name: Media Player\n  - category: 0x00B\n    name: Barcode Scanner\n  - category: 0x00C\n    name: Thermometer\n    subcategory:\n      - value: 0x01\n        name: Ear Thermometer\n  - category: 0x00D\n    name: Heart Rate Sensor\n    subcategory:\n      - value: 0x01\n        name: Heart Rate Belt\n  - category: 0x00E\n    name: Blood Pressure\n    subcategory:\n      - value: 0x01\n        name: Arm Blood Pressure\n      - value: 0x02\n        name: Wrist Blood Pressure\n  - category: 0x00F\n    name: Human Interface Device\n    subcategory:\n      - value: 0x01\n        name: Keyboard\n      - value: 0x02\n        name: Mouse\n      - value: 0x03\n        name: Joystick\n      - value: 0x04\n        name: Gamepad\n      - value: 0x05\n        name: Digitizer Tablet\n      - value: 0x06\n        name: Card Reader\n      - value: 0x07\n        name: Digital Pen\n      - value: 0x08\n        name: Barcode Scanner\n      - value: 0x09\n        name: Touchpad\n      - value: 0x0A\n        name: Presentation Remote\n  - category: 0x010\n    name: Glucose Meter\n  - category: 0x011\n    name: Running Walking Sensor\n    subcategory:\n      - value: 0x01\n        name: In-Shoe Running Walking Sensor\n      - value: 0x02\n        name: On-Shoe Running Walking Sensor\n      - value: 0x03\n        name: On-Hip Running Walking Sensor\n  - category: 0x012\n    name: Cycling\n    subcategory:\n      - value: 0x01\n        name: Cycling Computer\n      - value: 0x02\n        name: Speed Sensor\n      - value: 0x03\n        name: Cadence Sensor\n      - value: 0x04\n        name: Power Sensor\n      - value: 0x05\n        name: Speed and Cadence Sensor\n  - category: 0x013\n    name: Control Device\n    subcategory:\n      - value: 0x01\n        name: Switch\n      - value: 0x02\n        name: Multi-switch\n      - value: 0x03\n        name: Button\n      - value: 0x04\n        name: Slider\n      - value: 0x05\n        name: Rotary Switch\n      - value: 0x06\n        name: Touch Panel\n      - value: 0x07\n        name: Single Switch\n      - value: 0x08\n        name: Double Switch\n      - value: 0x09\n        name: Triple Switch\n      - value: 0x0A\n        name: Battery Switch\n      - value: 0x0B\n        name: Energy Harvesting Switch\n      - value: 0x0C\n        name: Push Button\n      - value: 0x0D\n        name: Dial\n  - category: 0x014\n    name: Network Device\n    subcategory:\n      - value: 0x01\n        name: Access Point\n      - value: 0x02\n        name: Mesh Device\n      - value: 0x03\n        name: Mesh Network Proxy\n  - category: 0x015\n    name: Sensor\n    subcategory:\n      - value: 0x01\n        name: Motion Sensor\n      - value: 0x02\n        name: Air quality Sensor\n      - value: 0x03\n        name: Temperature Sensor\n      - value: 0x04\n        name: Humidity Sensor\n      - value: 0x05\n        name: Leak Sensor\n      - value: 0x06\n        name: Smoke Sensor\n      - value: 0x07\n        name: Occupancy Sensor\n      - value: 0x08\n        name: Contact Sensor\n      - value: 0x09\n        name: Carbon Monoxide Sensor\n      - value: 0x0A\n        name: Carbon Dioxide Sensor\n      - value: 0x0B\n        name: Ambient Light Sensor\n      - value: 0x0C\n        name: Energy Sensor\n      - value: 0x0D\n        name: Color Light Sensor\n      - value: 0x0E\n        name: Rain Sensor\n      - value: 0x0F\n        name: Fire Sensor\n      - value: 0x10\n        name: Wind Sensor\n      - value: 0x11\n        name: Proximity Sensor\n      - value: 0x12\n        name: Multi-Sensor\n      - value: 0x13\n        name: Flush Mounted Sensor\n      - value: 0x14\n        name: Ceiling Mounted Sensor\n      - value: 0x15\n        name: Wall Mounted Sensor\n      - value: 0x16\n        name: Multisensor\n      - value: 0x17\n        name: Energy Meter\n      - value: 0x18\n        name: Flame Detector\n      - value: 0x19\n        name: Vehicle Tire Pressure Sensor\n  - category: 0x016\n    name: Light Fixtures\n    subcategory:\n      - value: 0x01\n        name: Wall Light\n      - value: 0x02\n        name: Ceiling Light\n      - value: 0x03\n        name: Floor Light\n      - value: 0x04\n        name: Cabinet Light\n      - value: 0x05\n        name: Desk Light\n      - value: 0x06\n        name: Troffer Light\n      - value: 0x07\n        name: Pendant Light\n      - value: 0x08\n        name: In-ground Light\n      - value: 0x09\n        name: Flood Light\n      - value: 0x0A\n        name: Underwater Light\n      - value: 0x0B\n        name: Bollard with Light\n      - value: 0x0C\n        name: Pathway Light\n      - value: 0x0D\n        name: Garden Light\n      - value: 0x0E\n        name: Pole-top Light\n      - value: 0x0F\n        name: Spotlight\n      - value: 0x10\n        name: Linear Light\n      - value: 0x11\n        name: Street Light\n      - value: 0x12\n        name: Shelves Light\n      - value: 0x13\n        name: Bay Light\n      - value: 0x14\n        name: Emergency Exit Light\n      - value: 0x15\n        name: Light Controller\n      - value: 0x16\n        name: Light Driver\n      - value: 0x17\n        name: Bulb\n      - value: 0x18\n        name: Low-bay Light\n      - value: 0x19\n        name: High-bay Light\n  - category: 0x017\n    name: Fan\n    subcategory:\n      - value: 0x01\n        name: Ceiling Fan\n      - value: 0x02\n        name: Axial Fan\n      - value: 0x03\n        name: Exhaust Fan\n      - value: 0x04\n        name: Pedestal Fan\n      - value: 0x05\n        name: Desk Fan\n      - value: 0x06\n        name: Wall Fan\n  - category: 0x018\n    name: HVAC\n    subcategory:\n      - value: 0x01\n        name: Thermostat\n      - value: 0x02\n        name: Humidifier\n      - value: 0x03\n        name: De-humidifier\n      - value: 0x04\n        name: Heater\n      - value: 0x05\n        name: Radiator\n      - value: 0x06\n        name: Boiler\n      - value: 0x07\n        name: Heat Pump\n      - value: 0x08\n        name: Infrared Heater\n      - value: 0x09\n        name: Radiant Panel Heater\n      - value: 0x0A\n        name: Fan Heater\n      - value: 0x0B\n        name: Air Curtain\n  - category: 0x019\n    name: Air Conditioning\n  - category: 0x01A\n    name: Humidifier\n  - category: 0x01B\n    name: Heating\n    subcategory:\n      - value: 0x01\n        name: Radiator\n      - value: 0x02\n        name: Boiler\n      - value: 0x03\n        name: Heat Pump\n      - value: 0x04\n        name: Infrared Heater\n      - value: 0x05\n        name: Radiant Panel Heater\n      - value: 0x06\n        name: Fan Heater\n      - value: 0x07\n        name: Air Curtain\n  - category: 0x01C\n    name: Access Control\n    subcategory:\n      - value: 0x01\n        name: Access Door\n      - value: 0x02\n        name: Garage Door\n      - value: 0x03\n        name: Emergency Exit Door\n      - value: 0x04\n        name: Access Lock\n      - value: 0x05\n        name: Elevator\n      - value: 0x06\n        name: Window\n      - value: 0x07\n        name: Entrance Gate\n      - value: 0x08\n        name: Door Lock\n      - value: 0x09\n        name: Locker\n  - category: 0x01D\n    name: Motorized Device\n    subcategory:\n      - value: 0x01\n        name: Motorized Gate\n      - value: 0x02\n        name: Awning\n      - value: 0x03\n        name: Blinds or Shades\n      - value: 0x04\n        name: Curtains\n      - value: 0x05\n        name: Screen\n  - category: 0x01E\n    name: Power Device\n    subcategory:\n      - value: 0x01\n        name: Power Outlet\n      - value: 0x02\n        name: Power Strip\n      - value: 0x03\n        name: Plug\n      - value: 0x04\n        name: Power Supply\n      - value: 0x05\n        name: LED Driver\n      - value: 0x06\n        name: Fluorescent Lamp Gear\n      - value: 0x07\n        name: HID Lamp Gear\n      - value: 0x08\n        name: Charge Case\n      - value: 0x09\n        name: Power Bank\n  - category: 0x01F\n    name: Light Source\n    subcategory:\n      - value: 0x01\n        name: Incandescent Light Bulb\n      - value: 0x02\n        name: LED Lamp\n      - value: 0x03\n        name: HID Lamp\n      - value: 0x04\n        name: Fluorescent Lamp\n      - value: 0x05\n        name: LED Array\n      - value: 0x06\n        name: Multi-Color LED Array\n      - value: 0x07\n        name: Low voltage halogen\n      - value: 0x08\n        name: Organic light emitting diode (OLED)\n  - category: 0x020\n    name: Window Covering\n    subcategory:\n      - value: 0x01\n        name: Window Shades\n      - value: 0x02\n        name: Window Blinds\n      - value: 0x03\n        name: Window Awning\n      - value: 0x04\n        name: Window Curtain\n      - value: 0x05\n        name: Exterior Shutter\n      - value: 0x06\n        name: Exterior Screen\n  - category: 0x021\n    name: Audio Sink\n    subcategory:\n      - value: 0x01\n        name: Standalone Speaker\n      - value: 0x02\n        name: Soundbar\n      - value: 0x03\n        name: Bookshelf Speaker\n      - value: 0x04\n        name: Standmounted Speaker\n      - value: 0x05\n        name: Speakerphone\n  - category: 0x022\n    name: Audio Source\n    subcategory:\n      - value: 0x01\n        name: Microphone\n      - value: 0x02\n        name: Alarm\n      - value: 0x03\n        name: Bell\n      - value: 0x04\n        name: Horn\n      - value: 0x05\n        name: Broadcasting Device\n      - value: 0x06\n        name: Service Desk\n      - value: 0x07\n        name: Kiosk\n      - value: 0x08\n        name: Broadcasting Room\n      - value: 0x09\n        name: Auditorium\n  - category: 0x023\n    name: Motorized Vehicle\n    subcategory:\n      - value: 0x01\n        name: Car\n      - value: 0x02\n        name: Large Goods Vehicle\n      - value: 0x03\n        name: Vehicle 2-Wheels\n      - value: 0x04\n        name: Motorbike\n      - value: 0x05\n        name: Scooter\n      - value: 0x06\n        name: Moped\n      - value: 0x07\n        name: Vehicle 3-Wheels\n      - value: 0x08\n        name: Light Vehicle\n      - value: 0x09\n        name: Quad Bike\n      - value: 0x0A\n        name: Minibus\n      - value: 0x0B\n        name: Bus\n      - value: 0x0C\n        name: Trolley\n      - value: 0x0D\n        name: Agricultural Vehicle\n      - value: 0x0E\n        name: Camper / Caravan\n      - value: 0x0F\n        name: Recreational Vehicle / Motor Home\n  - category: 0x024\n    name: Domestic Appliance\n    subcategory:\n      - value: 0x01\n        name: Refrigerator\n      - value: 0x02\n        name: Freezer\n      - value: 0x03\n        name: Oven\n      - value: 0x04\n        name: Microwave\n      - value: 0x05\n        name: Toaster\n      - value: 0x06\n        name: Washing Machine\n      - value: 0x07\n        name: Dryer\n      - value: 0x08\n        name: Coffee maker\n      - value: 0x09\n        name: Clothes iron\n      - value: 0x0A\n        name: Curling iron\n      - value: 0x0B\n        name: Hair dryer\n      - value: 0x0C\n        name: Vacuum cleaner\n      - value: 0x0D\n        name: Robotic vacuum cleaner\n      - value: 0x0E\n        name: Rice cooker\n      - value: 0x0F\n        name: Clothes steamer\n  - category: 0x025\n    name: Wearable Audio Device\n    subcategory:\n      - value: 0x01\n        name: Earbud\n      - value: 0x02\n        name: Headset\n      - value: 0x03\n        name: Headphones\n      - value: 0x04\n        name: Neck Band\n  - category: 0x026\n    name: Aircraft\n    subcategory:\n      - value: 0x01\n        name: Light Aircraft\n      - value: 0x02\n        name: Microlight\n      - value: 0x03\n        name: Paraglider\n      - value: 0x04\n        name: Large Passenger Aircraft\n  - category: 0x027\n    name: AV Equipment\n    subcategory:\n      - value: 0x01\n        name: Amplifier\n      - value: 0x02\n        name: Receiver\n      - value: 0x03\n        name: Radio\n      - value: 0x04\n        name: Tuner\n      - value: 0x05\n        name: Turntable\n      - value: 0x06\n        name: CD Player\n      - value: 0x07\n        name: DVD Player\n      - value: 0x08\n        name: Bluray Player\n      - value: 0x09\n        name: Optical Disc Player\n      - value: 0x0A\n        name: Set-Top Box\n  - category: 0x028\n    name: Display Equipment\n    subcategory:\n      - value: 0x01\n        name: Television\n      - value: 0x02\n        name: Monitor\n      - value: 0x03\n        name: Projector\n  - category: 0x029\n    name: Hearing aid\n    subcategory:\n      - value: 0x01\n        name: In-ear hearing aid\n      - value: 0x02\n        name: Behind-ear hearing aid\n      - value: 0x03\n        name: Cochlear Implant\n  - category: 0x02A\n    name: Gaming\n    subcategory:\n      - value: 0x01\n        name: Home Video Game Console\n      - value: 0x02\n        name: Portable handheld console\n  - category: 0x02B\n    name: Signage\n    subcategory:\n      - value: 0x01\n        name: Digital Signage\n      - value: 0x02\n        name: Electronic Label\n  - category: 0x031\n    name: Pulse Oximeter\n    subcategory:\n      - value: 0x01\n        name: Fingertip Pulse Oximeter\n      - value: 0x02\n        name: Wrist Worn Pulse Oximeter\n  - category: 0x032\n    name: Weight Scale\n  - category: 0x033\n    name: Personal Mobility Device\n    subcategory:\n      - value: 0x01\n        name: Powered Wheelchair\n      - value: 0x02\n        name: Mobility Scooter\n  - category: 0x034\n    name: Continuous Glucose Monitor\n  - category: 0x035\n    name: Insulin Pump\n    subcategory:\n      - value: 0x01\n        name: \"Insulin Pump, durable pump\"\n      - value: 0x04\n        name: \"Insulin Pump, patch pump\"\n      - value: 0x08\n        name: Insulin Pen\n  - category: 0x036\n    name: Medication Delivery\n  - category: 0x037\n    name: Spirometer\n    subcategory:\n      - value: 0x01\n        name: Handheld Spirometer\n  - category: 0x051\n    name: Outdoor Sports Activity\n    subcategory:\n      - value: 0x01\n        name: Location Display\n      - value: 0x02\n        name: Location and Navigation Display\n      - value: 0x03\n        name: Location Pod\n      - value: 0x04\n        name: Location and Navigation Pod\n  - category: 0x052\n    name: Industrial Measurement Device\n    subcategory:\n      - value: 0x01\n        name: Torque Testing Device\n      - value: 0x02\n        name: Caliper\n      - value: 0x03\n        name: Dial Indicator\n      - value: 0x04\n        name: Micrometer\n      - value: 0x05\n        name: Height Gauge\n      - value: 0x06\n        name: Force Gauge\n  - category: 0x053\n    name: Industrial Tools\n    subcategory:\n      - value: 0x01\n        name: Machine Tool Holder\n      - value: 0x02\n        name: Generic Clamping Device\n      - value: 0x03\n        name: Clamping Jaws/Jaw Chuck\n      - value: 0x04\n        name: Clamping (Collet) Chuck\n      - value: 0x05\n        name: Clamping Mandrel\n      - value: 0x06\n        name: Vise\n      - value: 0x07\n        name: Zero-Point Clamping System\n      - value: 0x08\n        name: Torque Wrench\n      - value: 0x09\n        name: Torque Screwdriver\n"
  },
  {
    "path": "libs/core/src/system_state/bluetooth/build_low_energy_enums.rs",
    "content": "#[cfg(feature = \"gen-binds\")]\nmod tests {\n    #[derive(Deserialize)]\n    struct AppearanceDefinition {\n        pub appearance_values: Vec<AppearanceCategoryDefinition>,\n    }\n\n    #[derive(Deserialize)]\n    struct AppearanceCategoryDefinition {\n        pub category: u16,\n        pub name: String,\n        #[serde(default)]\n        pub subcategory: Vec<AppearanceSubcategoryDefinition>,\n    }\n\n    #[derive(Deserialize)]\n    struct AppearanceSubcategoryDefinition {\n        pub value: u16,\n        pub name: String,\n    }\n\n    #[test]\n    fn build_low_energy_enums() -> crate::error::Result<()> {\n        use regex::Regex;\n        use std::io::Write;\n\n        let yaml_str = include_str!(\"appearance_values.yml\");\n        let definition: AppearanceDefinition = serde_yaml::from_str(yaml_str)?;\n\n        let regex = Regex::new(r\"[^a-zA-Z0-9_]\").unwrap();\n\n        let mut file = std::fs::File::create(\"./src/system_state/bluetooth/low_energy_enums.rs\")?;\n        file.write_all(\n            \"// This file was generated via rust macros. Don't modify manually.\\n\".as_bytes(),\n        )?;\n        file.write_all(\n        \"// all this structs are based on official docs https://www.bluetooth.com/specifications/assigned-numbers\\n\\n\".as_bytes()\n        )?;\n\n        let mut category_enum = vec![\n            \"#[repr(u16)]\".to_string(),\n            \"#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS)]\".to_string(),\n            \"pub enum BLEAppearanceCategory {\".to_string(),\n            \"    #[default]\".to_string(),\n        ];\n\n        let mut appearance_enum = vec![\n            \"#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, TS)]\".to_string(),\n            \"#[serde(tag = \\\"category\\\", content = \\\"subcategory\\\")]\".to_string(),\n            \"pub enum BLEAppearance {\".to_string(),\n        ];\n\n        let mut appearance_impl = vec![\n            \"impl From<u16> for BLEAppearance {\".to_string(),\n            \"    fn from(value: u16) -> Self {\".to_string(),\n            \"        let category = BLEAppearanceCategory::from(value >> 6); // 10 bits\"\n                .to_string(),\n            \"        let subcategory = value & 0b111111; // 6 bits\\n\".to_string(),\n            \"        match category {\".to_string(),\n        ];\n\n        for category in definition.appearance_values {\n            let name = regex.replace_all(&category.name, \"\");\n            let sub_enum_name = format!(\"BLEAppearance{name}SubCategory\");\n\n            category_enum.push(format!(\"    {name} = 0x{:x},\", category.category));\n            appearance_enum.push(format!(\"    {name}({sub_enum_name}),\"));\n            appearance_impl.push(format!(\"            BLEAppearanceCategory::{name} => BLEAppearance::{name}({sub_enum_name}::from(subcategory)),\"));\n\n            let mut subcategory_enum = vec![\n            \"#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS)]\".to_string(),\n            \"#[cfg_attr(feature = \\\"gen-binds\\\", ts(export_to = \\\"BLEAppearanceSubCategory.ts\\\"))]\".to_string(),\n            \"#[repr(u16)]\".to_string(),\n            format!(\"pub enum {sub_enum_name} {{\"),\n        ];\n\n            for subcategory in category.subcategory {\n                let sub_name = regex.replace_all(&subcategory.name, \"\");\n                subcategory_enum.push(format!(\"    {} = 0x{:x},\", sub_name, subcategory.value));\n            }\n\n            subcategory_enum.push(\"    #[num_enum(catch_all)]\".to_string());\n            subcategory_enum.push(\"    Reserved(u16),\".to_string());\n            subcategory_enum.push(\"}\\n\\n\".to_string());\n\n            file.write_all(subcategory_enum.join(\"\\n\").as_bytes())?;\n        }\n\n        category_enum.push(\"}\\n\\n\".to_string());\n        file.write_all(category_enum.join(\"\\n\").as_bytes())?;\n\n        appearance_enum.push(\"}\\n\\n\".to_string());\n        file.write_all(appearance_enum.join(\"\\n\").as_bytes())?;\n\n        appearance_impl.push(\"        }\".to_string());\n        appearance_impl.push(\"    }\".to_string());\n        appearance_impl.push(\"}\".to_string());\n        file.write_all(appearance_impl.join(\"\\n\").as_bytes())?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/bluetooth/enums.rs",
    "content": "#[repr(u32)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum BluetoothMajorServiceClass {\n    LimitedDiscoverableMode = 0x1,\n    LowEnergyAudio = 0x2,\n    Reserved = 0x4,\n    /// Location identification\n    Positioning = 0x8,\n    /// LAN, Ad hoc, ...\n    Networking = 0x10,\n    /// Printing, Speakers, ...\n    Rendering = 0x20,\n    /// Scanner, Microphone, ...\n    Capturing = 0x40,\n    /// v-Inbox, v-Folder, ...\n    ObjectTransfer = 0x80,\n    /// Speaker, Microphone, Headset service, ...\n    Audio = 0x100,\n    /// Cordless telephony, Modem, Headset service, ...\n    Telephony = 0x200,\n    /// WEB-server, WAP-server, ...\n    Information = 0x400,\n}\n\n/// 5 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[ts(repr(enum = name))]\npub enum BluetoothMajorClass {\n    /// The ”Miscellaneous” Major Device Class is used where a more specific\n    /// Major Device Class code is not suitable.\\\n    /// A device that does not have a Major Class Code assigned can use the\n    /// ”Uncategorized: device code not specified” code until classified.\n    Miscellaneous = 0x0,\n    /// Computer (desktop, notebook, PDA, organizer, ...)\n    Computer = 0x1,\n    /// Phone (cellular, cordless, payphone, modem, ...)\n    Phone = 0x2,\n    /// LAN/NetworkAccessPoint\n    NetworkAccessPoint = 0x3,\n    /// Audio/Video (headset, speaker, stereo, video display, VCR, ...)\n    AudioVideo = 0x4,\n    /// Peripheral (mouse, joystick, keyboard, ...)\n    Peripheral = 0x5,\n    /// Imaging (printer, scanner, camera, display, ...)\n    Imaging = 0x6,\n    Wearable = 0x7,\n    Toy = 0x8,\n    Health = 0x9,\n    /// Device code not specified\n    #[default]\n    Uncategorized = 0x1F,\n}\n\n#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, TS)]\npub enum BluetoothMinorClass {\n    Miscellaneous { unused: u8 },\n    Computer(BluetoothComputerMinor),\n    Phone(BluetoothPhoneMinor),\n    NetworkAccessPoint(BluetoothNetworkMinor, BluetoothNetworkSubMinor),\n    AudioVideo(BluetoothAudioVideoMinor),\n    Peripheral(BluetoothPeripheralMinor, BluetoothPeripheralSubMinor),\n    Imaging(Vec<BluetoothImagingMinor>, BluetoothImagingSubMinor),\n    Wearable(BluetoothWearableMinor),\n    Toy(BluetoothToyMinor),\n    Health(BluetoothHealthMinor),\n    Uncategorized { unused: u8 },\n}\n\n/// 6 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothComputerMinor {\n    /// Code for device not specified\n    Uncategorized = 0x0,\n    /// Desktop Workstation, PC\n    Desktop = 0x1,\n    /// Server-class Computer\n    Server = 0x2,\n    Laptop = 0x3,\n    /// Handheld PC/PDA (e.g. clamshell)\n    Handheld = 0x4,\n    /// Palm-size PC/PDA\n    PalmSize = 0x5,\n    /// Wearable computer (e.g. watch)\n    Wearable = 0x6,\n    Tablet = 0x7,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 6 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothPhoneMinor {\n    /// Code for device not specified\n    Uncategorized = 0x0,\n    Cellular = 0x1,\n    Cordless = 0x2,\n    SmartPhone = 0x3,\n    /// Wired Modem or Voice Gateway\n    Wired = 0x4,\n    /// Common ISDN access device\n    Isdn = 0x5,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 3 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[ts(repr(enum = name))]\npub enum BluetoothNetworkMinor {\n    FullyAvailable = 0x0,\n    Used01To17Percent = 0x1,\n    Used17To33Percent = 0x2,\n    Used33To50Percent = 0x3,\n    Used50To67Percent = 0x4,\n    Used67To83Percent = 0x5,\n    Used83To99Percent = 0x6,\n    #[default]\n    NoServiceAvailable = 0x7,\n}\n\n/// 3 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothNetworkSubMinor {\n    Uncategorized = 0x0,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 6 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothAudioVideoMinor {\n    /// Code for device not specified\n    Uncategorized = 0x0,\n    /// Wearable Headset\n    Headset = 0x1,\n    /// Hands-free Headset\n    HandsFree = 0x2,\n    Microphone = 0x4, // 0x3 is reserved\n    Loudspeaker = 0x5,\n    Headphones = 0x6,\n    PortableAudio = 0x7,\n    CarAudio = 0x8,\n    /// Set-top box\n    SetTopBox = 0x9,\n    HiFiAudioDevice = 0xA,\n    Vcr = 0xB,\n    VideoCamera = 0xC,\n    CamCorder = 0xD,\n    VideoMonitor = 0xE,\n    VideoDisplayAndLoudspeaker = 0xF,\n    VideoConferencing = 0x10,\n    GamingToy = 0x12, // 0x11 is reserved\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 2 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[ts(repr(enum = name))]\npub enum BluetoothPeripheralMinor {\n    /// Code for device not specified\n    #[default]\n    Uncategorized = 0x0,\n    Keyboard = 0x1,\n    Pointing = 0x2,\n    ComboKeyboardPointing = 0x3,\n}\n\n/// 4 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothPeripheralSubMinor {\n    Uncategorized = 0x0,\n    Joystick = 0x1,\n    Gamepad = 0x2,\n    RemoteControl = 0x3,\n    Sensor = 0x4,\n    DigitizerTablet = 0x5,\n    CardReader = 0x6,\n    DigitalPen = 0x7,\n    /// Handheld scanner (e.g. barcodes, RFID)\n    HandheldScanner = 0x8,\n    /// Handheld Gestural Input Device (e.g., “wand” form factor)\n    HandheldGestural = 0x9,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 4 bits, flags that can be combined\n#[repr(u8)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum BluetoothImagingMinor {\n    Display = 0x1,\n    Camera = 0x2,\n    Scanner = 0x4,\n    Printer = 0x8,\n}\n\n/// 2 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothImagingSubMinor {\n    Uncategorized = 0x0,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 6 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothWearableMinor {\n    Wristwatch = 0x1,\n    Pager = 0x2,\n    Jacket = 0x3,\n    Helmet = 0x4,\n    Glasses = 0x5,\n    /// Pin (e.g., lapel pin, broach, badge)\n    Pin = 0x6,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 6 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothToyMinor {\n    Robot = 0x1,\n    Vehicle = 0x2,\n    /// Doll/Action Figure\n    Doll = 0x3,\n    Controller = 0x4,\n    Game = 0x5,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n\n/// 6 bits\n#[repr(u8)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BluetoothHealthMinor {\n    Undefined = 0x0,\n    BloodPressureMonitor = 0x1,\n    Thermometer = 0x2,\n    WeighingScale = 0x3,\n    GlucoseMeter = 0x4,\n    PulseOximeter = 0x5,\n    HeartPulseMonitor = 0x6,\n    HealthDataDisplay = 0x7,\n    StepCounter = 0x8,\n    BodyCompositionMonitor = 0x9,\n    PeakFlowMonitor = 0xA,\n    MedicationMonitor = 0xB,\n    KneeProsthesis = 0xC,\n    AnkleProsthesis = 0xD,\n    GenericHealthManager = 0xE,\n    PersonalMobilityDevice = 0xF,\n    #[num_enum(catch_all)]\n    Reserved(u8),\n}\n"
  },
  {
    "path": "libs/core/src/system_state/bluetooth/low_energy_enums.rs",
    "content": "// This file was generated via rust macros. Don't modify manually.\n// all this structs are based on official docs https://www.bluetooth.com/specifications/assigned-numbers\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceUnknownSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearancePhoneSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceComputerSubCategory {\n    DesktopWorkstation = 0x1,\n    ServerclassComputer = 0x2,\n    Laptop = 0x3,\n    HandheldPCPDAclamshell = 0x4,\n    PalmsizePCPDA = 0x5,\n    Wearablecomputerwatchsize = 0x6,\n    Tablet = 0x7,\n    DockingStation = 0x8,\n    AllinOne = 0x9,\n    BladeServer = 0xa,\n    Convertible = 0xb,\n    Detachable = 0xc,\n    IoTGateway = 0xd,\n    MiniPC = 0xe,\n    StickPC = 0xf,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceWatchSubCategory {\n    SportsWatch = 0x1,\n    Smartwatch = 0x2,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceClockSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceDisplaySubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceRemoteControlSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceEyeglassesSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceTagSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceKeyringSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceMediaPlayerSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceBarcodeScannerSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceThermometerSubCategory {\n    EarThermometer = 0x1,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceHeartRateSensorSubCategory {\n    HeartRateBelt = 0x1,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceBloodPressureSubCategory {\n    ArmBloodPressure = 0x1,\n    WristBloodPressure = 0x2,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceHumanInterfaceDeviceSubCategory {\n    Keyboard = 0x1,\n    Mouse = 0x2,\n    Joystick = 0x3,\n    Gamepad = 0x4,\n    DigitizerTablet = 0x5,\n    CardReader = 0x6,\n    DigitalPen = 0x7,\n    BarcodeScanner = 0x8,\n    Touchpad = 0x9,\n    PresentationRemote = 0xa,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceGlucoseMeterSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceRunningWalkingSensorSubCategory {\n    InShoeRunningWalkingSensor = 0x1,\n    OnShoeRunningWalkingSensor = 0x2,\n    OnHipRunningWalkingSensor = 0x3,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceCyclingSubCategory {\n    CyclingComputer = 0x1,\n    SpeedSensor = 0x2,\n    CadenceSensor = 0x3,\n    PowerSensor = 0x4,\n    SpeedandCadenceSensor = 0x5,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceControlDeviceSubCategory {\n    Switch = 0x1,\n    Multiswitch = 0x2,\n    Button = 0x3,\n    Slider = 0x4,\n    RotarySwitch = 0x5,\n    TouchPanel = 0x6,\n    SingleSwitch = 0x7,\n    DoubleSwitch = 0x8,\n    TripleSwitch = 0x9,\n    BatterySwitch = 0xa,\n    EnergyHarvestingSwitch = 0xb,\n    PushButton = 0xc,\n    Dial = 0xd,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceNetworkDeviceSubCategory {\n    AccessPoint = 0x1,\n    MeshDevice = 0x2,\n    MeshNetworkProxy = 0x3,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceSensorSubCategory {\n    MotionSensor = 0x1,\n    AirqualitySensor = 0x2,\n    TemperatureSensor = 0x3,\n    HumiditySensor = 0x4,\n    LeakSensor = 0x5,\n    SmokeSensor = 0x6,\n    OccupancySensor = 0x7,\n    ContactSensor = 0x8,\n    CarbonMonoxideSensor = 0x9,\n    CarbonDioxideSensor = 0xa,\n    AmbientLightSensor = 0xb,\n    EnergySensor = 0xc,\n    ColorLightSensor = 0xd,\n    RainSensor = 0xe,\n    FireSensor = 0xf,\n    WindSensor = 0x10,\n    ProximitySensor = 0x11,\n    MultiSensor = 0x12,\n    FlushMountedSensor = 0x13,\n    CeilingMountedSensor = 0x14,\n    WallMountedSensor = 0x15,\n    Multisensor = 0x16,\n    EnergyMeter = 0x17,\n    FlameDetector = 0x18,\n    VehicleTirePressureSensor = 0x19,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceLightFixturesSubCategory {\n    WallLight = 0x1,\n    CeilingLight = 0x2,\n    FloorLight = 0x3,\n    CabinetLight = 0x4,\n    DeskLight = 0x5,\n    TrofferLight = 0x6,\n    PendantLight = 0x7,\n    IngroundLight = 0x8,\n    FloodLight = 0x9,\n    UnderwaterLight = 0xa,\n    BollardwithLight = 0xb,\n    PathwayLight = 0xc,\n    GardenLight = 0xd,\n    PoletopLight = 0xe,\n    Spotlight = 0xf,\n    LinearLight = 0x10,\n    StreetLight = 0x11,\n    ShelvesLight = 0x12,\n    BayLight = 0x13,\n    EmergencyExitLight = 0x14,\n    LightController = 0x15,\n    LightDriver = 0x16,\n    Bulb = 0x17,\n    LowbayLight = 0x18,\n    HighbayLight = 0x19,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceFanSubCategory {\n    CeilingFan = 0x1,\n    AxialFan = 0x2,\n    ExhaustFan = 0x3,\n    PedestalFan = 0x4,\n    DeskFan = 0x5,\n    WallFan = 0x6,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceHVACSubCategory {\n    Thermostat = 0x1,\n    Humidifier = 0x2,\n    Dehumidifier = 0x3,\n    Heater = 0x4,\n    Radiator = 0x5,\n    Boiler = 0x6,\n    HeatPump = 0x7,\n    InfraredHeater = 0x8,\n    RadiantPanelHeater = 0x9,\n    FanHeater = 0xa,\n    AirCurtain = 0xb,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceAirConditioningSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceHumidifierSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceHeatingSubCategory {\n    Radiator = 0x1,\n    Boiler = 0x2,\n    HeatPump = 0x3,\n    InfraredHeater = 0x4,\n    RadiantPanelHeater = 0x5,\n    FanHeater = 0x6,\n    AirCurtain = 0x7,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceAccessControlSubCategory {\n    AccessDoor = 0x1,\n    GarageDoor = 0x2,\n    EmergencyExitDoor = 0x3,\n    AccessLock = 0x4,\n    Elevator = 0x5,\n    Window = 0x6,\n    EntranceGate = 0x7,\n    DoorLock = 0x8,\n    Locker = 0x9,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceMotorizedDeviceSubCategory {\n    MotorizedGate = 0x1,\n    Awning = 0x2,\n    BlindsorShades = 0x3,\n    Curtains = 0x4,\n    Screen = 0x5,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearancePowerDeviceSubCategory {\n    PowerOutlet = 0x1,\n    PowerStrip = 0x2,\n    Plug = 0x3,\n    PowerSupply = 0x4,\n    LEDDriver = 0x5,\n    FluorescentLampGear = 0x6,\n    HIDLampGear = 0x7,\n    ChargeCase = 0x8,\n    PowerBank = 0x9,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceLightSourceSubCategory {\n    IncandescentLightBulb = 0x1,\n    LEDLamp = 0x2,\n    HIDLamp = 0x3,\n    FluorescentLamp = 0x4,\n    LEDArray = 0x5,\n    MultiColorLEDArray = 0x6,\n    Lowvoltagehalogen = 0x7,\n    OrganiclightemittingdiodeOLED = 0x8,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceWindowCoveringSubCategory {\n    WindowShades = 0x1,\n    WindowBlinds = 0x2,\n    WindowAwning = 0x3,\n    WindowCurtain = 0x4,\n    ExteriorShutter = 0x5,\n    ExteriorScreen = 0x6,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceAudioSinkSubCategory {\n    StandaloneSpeaker = 0x1,\n    Soundbar = 0x2,\n    BookshelfSpeaker = 0x3,\n    StandmountedSpeaker = 0x4,\n    Speakerphone = 0x5,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceAudioSourceSubCategory {\n    Microphone = 0x1,\n    Alarm = 0x2,\n    Bell = 0x3,\n    Horn = 0x4,\n    BroadcastingDevice = 0x5,\n    ServiceDesk = 0x6,\n    Kiosk = 0x7,\n    BroadcastingRoom = 0x8,\n    Auditorium = 0x9,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceMotorizedVehicleSubCategory {\n    Car = 0x1,\n    LargeGoodsVehicle = 0x2,\n    Vehicle2Wheels = 0x3,\n    Motorbike = 0x4,\n    Scooter = 0x5,\n    Moped = 0x6,\n    Vehicle3Wheels = 0x7,\n    LightVehicle = 0x8,\n    QuadBike = 0x9,\n    Minibus = 0xa,\n    Bus = 0xb,\n    Trolley = 0xc,\n    AgriculturalVehicle = 0xd,\n    CamperCaravan = 0xe,\n    RecreationalVehicleMotorHome = 0xf,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceDomesticApplianceSubCategory {\n    Refrigerator = 0x1,\n    Freezer = 0x2,\n    Oven = 0x3,\n    Microwave = 0x4,\n    Toaster = 0x5,\n    WashingMachine = 0x6,\n    Dryer = 0x7,\n    Coffeemaker = 0x8,\n    Clothesiron = 0x9,\n    Curlingiron = 0xa,\n    Hairdryer = 0xb,\n    Vacuumcleaner = 0xc,\n    Roboticvacuumcleaner = 0xd,\n    Ricecooker = 0xe,\n    Clothessteamer = 0xf,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceWearableAudioDeviceSubCategory {\n    Earbud = 0x1,\n    Headset = 0x2,\n    Headphones = 0x3,\n    NeckBand = 0x4,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceAircraftSubCategory {\n    LightAircraft = 0x1,\n    Microlight = 0x2,\n    Paraglider = 0x3,\n    LargePassengerAircraft = 0x4,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceAVEquipmentSubCategory {\n    Amplifier = 0x1,\n    Receiver = 0x2,\n    Radio = 0x3,\n    Tuner = 0x4,\n    Turntable = 0x5,\n    CDPlayer = 0x6,\n    DVDPlayer = 0x7,\n    BlurayPlayer = 0x8,\n    OpticalDiscPlayer = 0x9,\n    SetTopBox = 0xa,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceDisplayEquipmentSubCategory {\n    Television = 0x1,\n    Monitor = 0x2,\n    Projector = 0x3,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceHearingaidSubCategory {\n    Inearhearingaid = 0x1,\n    Behindearhearingaid = 0x2,\n    CochlearImplant = 0x3,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceGamingSubCategory {\n    HomeVideoGameConsole = 0x1,\n    Portablehandheldconsole = 0x2,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceSignageSubCategory {\n    DigitalSignage = 0x1,\n    ElectronicLabel = 0x2,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearancePulseOximeterSubCategory {\n    FingertipPulseOximeter = 0x1,\n    WristWornPulseOximeter = 0x2,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceWeightScaleSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearancePersonalMobilityDeviceSubCategory {\n    PoweredWheelchair = 0x1,\n    MobilityScooter = 0x2,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceContinuousGlucoseMonitorSubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceInsulinPumpSubCategory {\n    InsulinPumpdurablepump = 0x1,\n    InsulinPumppatchpump = 0x4,\n    InsulinPen = 0x8,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceMedicationDeliverySubCategory {\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceSpirometerSubCategory {\n    HandheldSpirometer = 0x1,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceOutdoorSportsActivitySubCategory {\n    LocationDisplay = 0x1,\n    LocationandNavigationDisplay = 0x2,\n    LocationPod = 0x3,\n    LocationandNavigationPod = 0x4,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceIndustrialMeasurementDeviceSubCategory {\n    TorqueTestingDevice = 0x1,\n    Caliper = 0x2,\n    DialIndicator = 0x3,\n    Micrometer = 0x4,\n    HeightGauge = 0x5,\n    ForceGauge = 0x6,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\n#[cfg_attr(feature = \"gen-binds\", ts(export_to = \"BLEAppearanceSubCategory.ts\"))]\n#[repr(u16)]\npub enum BLEAppearanceIndustrialToolsSubCategory {\n    MachineToolHolder = 0x1,\n    GenericClampingDevice = 0x2,\n    ClampingJawsJawChuck = 0x3,\n    ClampingColletChuck = 0x4,\n    ClampingMandrel = 0x5,\n    Vise = 0x6,\n    ZeroPointClampingSystem = 0x7,\n    TorqueWrench = 0x8,\n    TorqueScrewdriver = 0x9,\n    #[num_enum(catch_all)]\n    Reserved(u16),\n}\n\n#[repr(u16)]\n#[derive(\n    Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, IntoPrimitive, Serialize, Deserialize, TS,\n)]\npub enum BLEAppearanceCategory {\n    #[default]\n    Unknown = 0x0,\n    Phone = 0x1,\n    Computer = 0x2,\n    Watch = 0x3,\n    Clock = 0x4,\n    Display = 0x5,\n    RemoteControl = 0x6,\n    Eyeglasses = 0x7,\n    Tag = 0x8,\n    Keyring = 0x9,\n    MediaPlayer = 0xa,\n    BarcodeScanner = 0xb,\n    Thermometer = 0xc,\n    HeartRateSensor = 0xd,\n    BloodPressure = 0xe,\n    HumanInterfaceDevice = 0xf,\n    GlucoseMeter = 0x10,\n    RunningWalkingSensor = 0x11,\n    Cycling = 0x12,\n    ControlDevice = 0x13,\n    NetworkDevice = 0x14,\n    Sensor = 0x15,\n    LightFixtures = 0x16,\n    Fan = 0x17,\n    HVAC = 0x18,\n    AirConditioning = 0x19,\n    Humidifier = 0x1a,\n    Heating = 0x1b,\n    AccessControl = 0x1c,\n    MotorizedDevice = 0x1d,\n    PowerDevice = 0x1e,\n    LightSource = 0x1f,\n    WindowCovering = 0x20,\n    AudioSink = 0x21,\n    AudioSource = 0x22,\n    MotorizedVehicle = 0x23,\n    DomesticAppliance = 0x24,\n    WearableAudioDevice = 0x25,\n    Aircraft = 0x26,\n    AVEquipment = 0x27,\n    DisplayEquipment = 0x28,\n    Hearingaid = 0x29,\n    Gaming = 0x2a,\n    Signage = 0x2b,\n    PulseOximeter = 0x31,\n    WeightScale = 0x32,\n    PersonalMobilityDevice = 0x33,\n    ContinuousGlucoseMonitor = 0x34,\n    InsulinPump = 0x35,\n    MedicationDelivery = 0x36,\n    Spirometer = 0x37,\n    OutdoorSportsActivity = 0x51,\n    IndustrialMeasurementDevice = 0x52,\n    IndustrialTools = 0x53,\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[serde(tag = \"category\", content = \"subcategory\")]\npub enum BLEAppearance {\n    Unknown(BLEAppearanceUnknownSubCategory),\n    Phone(BLEAppearancePhoneSubCategory),\n    Computer(BLEAppearanceComputerSubCategory),\n    Watch(BLEAppearanceWatchSubCategory),\n    Clock(BLEAppearanceClockSubCategory),\n    Display(BLEAppearanceDisplaySubCategory),\n    RemoteControl(BLEAppearanceRemoteControlSubCategory),\n    Eyeglasses(BLEAppearanceEyeglassesSubCategory),\n    Tag(BLEAppearanceTagSubCategory),\n    Keyring(BLEAppearanceKeyringSubCategory),\n    MediaPlayer(BLEAppearanceMediaPlayerSubCategory),\n    BarcodeScanner(BLEAppearanceBarcodeScannerSubCategory),\n    Thermometer(BLEAppearanceThermometerSubCategory),\n    HeartRateSensor(BLEAppearanceHeartRateSensorSubCategory),\n    BloodPressure(BLEAppearanceBloodPressureSubCategory),\n    HumanInterfaceDevice(BLEAppearanceHumanInterfaceDeviceSubCategory),\n    GlucoseMeter(BLEAppearanceGlucoseMeterSubCategory),\n    RunningWalkingSensor(BLEAppearanceRunningWalkingSensorSubCategory),\n    Cycling(BLEAppearanceCyclingSubCategory),\n    ControlDevice(BLEAppearanceControlDeviceSubCategory),\n    NetworkDevice(BLEAppearanceNetworkDeviceSubCategory),\n    Sensor(BLEAppearanceSensorSubCategory),\n    LightFixtures(BLEAppearanceLightFixturesSubCategory),\n    Fan(BLEAppearanceFanSubCategory),\n    HVAC(BLEAppearanceHVACSubCategory),\n    AirConditioning(BLEAppearanceAirConditioningSubCategory),\n    Humidifier(BLEAppearanceHumidifierSubCategory),\n    Heating(BLEAppearanceHeatingSubCategory),\n    AccessControl(BLEAppearanceAccessControlSubCategory),\n    MotorizedDevice(BLEAppearanceMotorizedDeviceSubCategory),\n    PowerDevice(BLEAppearancePowerDeviceSubCategory),\n    LightSource(BLEAppearanceLightSourceSubCategory),\n    WindowCovering(BLEAppearanceWindowCoveringSubCategory),\n    AudioSink(BLEAppearanceAudioSinkSubCategory),\n    AudioSource(BLEAppearanceAudioSourceSubCategory),\n    MotorizedVehicle(BLEAppearanceMotorizedVehicleSubCategory),\n    DomesticAppliance(BLEAppearanceDomesticApplianceSubCategory),\n    WearableAudioDevice(BLEAppearanceWearableAudioDeviceSubCategory),\n    Aircraft(BLEAppearanceAircraftSubCategory),\n    AVEquipment(BLEAppearanceAVEquipmentSubCategory),\n    DisplayEquipment(BLEAppearanceDisplayEquipmentSubCategory),\n    Hearingaid(BLEAppearanceHearingaidSubCategory),\n    Gaming(BLEAppearanceGamingSubCategory),\n    Signage(BLEAppearanceSignageSubCategory),\n    PulseOximeter(BLEAppearancePulseOximeterSubCategory),\n    WeightScale(BLEAppearanceWeightScaleSubCategory),\n    PersonalMobilityDevice(BLEAppearancePersonalMobilityDeviceSubCategory),\n    ContinuousGlucoseMonitor(BLEAppearanceContinuousGlucoseMonitorSubCategory),\n    InsulinPump(BLEAppearanceInsulinPumpSubCategory),\n    MedicationDelivery(BLEAppearanceMedicationDeliverySubCategory),\n    Spirometer(BLEAppearanceSpirometerSubCategory),\n    OutdoorSportsActivity(BLEAppearanceOutdoorSportsActivitySubCategory),\n    IndustrialMeasurementDevice(BLEAppearanceIndustrialMeasurementDeviceSubCategory),\n    IndustrialTools(BLEAppearanceIndustrialToolsSubCategory),\n}\n\nimpl From<u16> for BLEAppearance {\n    fn from(value: u16) -> Self {\n        let category = BLEAppearanceCategory::from(value >> 6); // 10 bits\n        let subcategory = value & 0b111111; // 6 bits\n\n        match category {\n            BLEAppearanceCategory::Unknown => {\n                BLEAppearance::Unknown(BLEAppearanceUnknownSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Phone => {\n                BLEAppearance::Phone(BLEAppearancePhoneSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Computer => {\n                BLEAppearance::Computer(BLEAppearanceComputerSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Watch => {\n                BLEAppearance::Watch(BLEAppearanceWatchSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Clock => {\n                BLEAppearance::Clock(BLEAppearanceClockSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Display => {\n                BLEAppearance::Display(BLEAppearanceDisplaySubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::RemoteControl => BLEAppearance::RemoteControl(\n                BLEAppearanceRemoteControlSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Eyeglasses => {\n                BLEAppearance::Eyeglasses(BLEAppearanceEyeglassesSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Tag => {\n                BLEAppearance::Tag(BLEAppearanceTagSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Keyring => {\n                BLEAppearance::Keyring(BLEAppearanceKeyringSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::MediaPlayer => {\n                BLEAppearance::MediaPlayer(BLEAppearanceMediaPlayerSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::BarcodeScanner => BLEAppearance::BarcodeScanner(\n                BLEAppearanceBarcodeScannerSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Thermometer => {\n                BLEAppearance::Thermometer(BLEAppearanceThermometerSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::HeartRateSensor => BLEAppearance::HeartRateSensor(\n                BLEAppearanceHeartRateSensorSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::BloodPressure => BLEAppearance::BloodPressure(\n                BLEAppearanceBloodPressureSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::HumanInterfaceDevice => BLEAppearance::HumanInterfaceDevice(\n                BLEAppearanceHumanInterfaceDeviceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::GlucoseMeter => {\n                BLEAppearance::GlucoseMeter(BLEAppearanceGlucoseMeterSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::RunningWalkingSensor => BLEAppearance::RunningWalkingSensor(\n                BLEAppearanceRunningWalkingSensorSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Cycling => {\n                BLEAppearance::Cycling(BLEAppearanceCyclingSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::ControlDevice => BLEAppearance::ControlDevice(\n                BLEAppearanceControlDeviceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::NetworkDevice => BLEAppearance::NetworkDevice(\n                BLEAppearanceNetworkDeviceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Sensor => {\n                BLEAppearance::Sensor(BLEAppearanceSensorSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::LightFixtures => BLEAppearance::LightFixtures(\n                BLEAppearanceLightFixturesSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Fan => {\n                BLEAppearance::Fan(BLEAppearanceFanSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::HVAC => {\n                BLEAppearance::HVAC(BLEAppearanceHVACSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::AirConditioning => BLEAppearance::AirConditioning(\n                BLEAppearanceAirConditioningSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Humidifier => {\n                BLEAppearance::Humidifier(BLEAppearanceHumidifierSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Heating => {\n                BLEAppearance::Heating(BLEAppearanceHeatingSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::AccessControl => BLEAppearance::AccessControl(\n                BLEAppearanceAccessControlSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::MotorizedDevice => BLEAppearance::MotorizedDevice(\n                BLEAppearanceMotorizedDeviceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::PowerDevice => {\n                BLEAppearance::PowerDevice(BLEAppearancePowerDeviceSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::LightSource => {\n                BLEAppearance::LightSource(BLEAppearanceLightSourceSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::WindowCovering => BLEAppearance::WindowCovering(\n                BLEAppearanceWindowCoveringSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::AudioSink => {\n                BLEAppearance::AudioSink(BLEAppearanceAudioSinkSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::AudioSource => {\n                BLEAppearance::AudioSource(BLEAppearanceAudioSourceSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::MotorizedVehicle => BLEAppearance::MotorizedVehicle(\n                BLEAppearanceMotorizedVehicleSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::DomesticAppliance => BLEAppearance::DomesticAppliance(\n                BLEAppearanceDomesticApplianceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::WearableAudioDevice => BLEAppearance::WearableAudioDevice(\n                BLEAppearanceWearableAudioDeviceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Aircraft => {\n                BLEAppearance::Aircraft(BLEAppearanceAircraftSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::AVEquipment => {\n                BLEAppearance::AVEquipment(BLEAppearanceAVEquipmentSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::DisplayEquipment => BLEAppearance::DisplayEquipment(\n                BLEAppearanceDisplayEquipmentSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Hearingaid => {\n                BLEAppearance::Hearingaid(BLEAppearanceHearingaidSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Gaming => {\n                BLEAppearance::Gaming(BLEAppearanceGamingSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::Signage => {\n                BLEAppearance::Signage(BLEAppearanceSignageSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::PulseOximeter => BLEAppearance::PulseOximeter(\n                BLEAppearancePulseOximeterSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::WeightScale => {\n                BLEAppearance::WeightScale(BLEAppearanceWeightScaleSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::PersonalMobilityDevice => BLEAppearance::PersonalMobilityDevice(\n                BLEAppearancePersonalMobilityDeviceSubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::ContinuousGlucoseMonitor => {\n                BLEAppearance::ContinuousGlucoseMonitor(\n                    BLEAppearanceContinuousGlucoseMonitorSubCategory::from(subcategory),\n                )\n            }\n            BLEAppearanceCategory::InsulinPump => {\n                BLEAppearance::InsulinPump(BLEAppearanceInsulinPumpSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::MedicationDelivery => BLEAppearance::MedicationDelivery(\n                BLEAppearanceMedicationDeliverySubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::Spirometer => {\n                BLEAppearance::Spirometer(BLEAppearanceSpirometerSubCategory::from(subcategory))\n            }\n            BLEAppearanceCategory::OutdoorSportsActivity => BLEAppearance::OutdoorSportsActivity(\n                BLEAppearanceOutdoorSportsActivitySubCategory::from(subcategory),\n            ),\n            BLEAppearanceCategory::IndustrialMeasurementDevice => {\n                BLEAppearance::IndustrialMeasurementDevice(\n                    BLEAppearanceIndustrialMeasurementDeviceSubCategory::from(subcategory),\n                )\n            }\n            BLEAppearanceCategory::IndustrialTools => BLEAppearance::IndustrialTools(\n                BLEAppearanceIndustrialToolsSubCategory::from(subcategory),\n            ),\n        }\n    }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/bluetooth/mod.rs",
    "content": "// all this structs are based on official docs https://www.bluetooth.com/specifications/assigned-numbers\n#[cfg(test)]\nmod build_low_energy_enums;\n\npub mod enums;\npub mod low_energy_enums;\n\nuse enums::*;\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct BluetoothDevice {\n    pub id: String,\n    pub name: String,\n    pub address: u64,\n    pub major_service_classes: Vec<BluetoothMajorServiceClass>,\n    pub major_class: BluetoothMajorClass,\n    pub minor_class: BluetoothMinorClass,\n    /// only available for low energy devices\n    pub appearance: Option<low_energy_enums::BLEAppearance>,\n    pub connected: bool,\n    pub paired: bool,\n    pub can_pair: bool,\n    pub can_disconnect: bool,\n    pub is_low_energy: bool,\n}\n\nimpl BluetoothDevice {\n    fn map_services_classes(class: u32) -> Vec<BluetoothMajorServiceClass> {\n        use BluetoothMajorServiceClass::*;\n        [\n            LimitedDiscoverableMode,\n            LowEnergyAudio,\n            Reserved,\n            Positioning,\n            Networking,\n            Rendering,\n            Capturing,\n            ObjectTransfer,\n            Audio,\n            Telephony,\n            Information,\n        ]\n        .into_iter()\n        .filter(|&service| class & service as u32 != 0)\n        .collect()\n    }\n\n    pub fn get_parts_of_class(\n        class: u32,\n    ) -> (\n        Vec<BluetoothMajorServiceClass>,\n        BluetoothMajorClass,\n        BluetoothMinorClass,\n    ) {\n        let major_service_classes = class >> 13;\n        let major_service_classes = Self::map_services_classes(major_service_classes);\n\n        let major_class = (class >> 8) & 0b11111; // 5 bits\n        let major_class = BluetoothMajorClass::from(major_class as u8);\n\n        let minor_class = ((class >> 2) & 0b111111) as u8; // 6 bits\n        let minor_class = match major_class {\n            BluetoothMajorClass::Miscellaneous => BluetoothMinorClass::Miscellaneous {\n                unused: minor_class,\n            },\n            BluetoothMajorClass::Computer => {\n                BluetoothMinorClass::Computer(BluetoothComputerMinor::from(minor_class))\n            }\n            BluetoothMajorClass::Phone => {\n                BluetoothMinorClass::Phone(BluetoothPhoneMinor::from(minor_class))\n            }\n            BluetoothMajorClass::NetworkAccessPoint => {\n                let minor_class = minor_class >> 3 & 0b111; // 3 bits\n                let sub_minor_class = minor_class & 0b111; // 3 bits\n                BluetoothMinorClass::NetworkAccessPoint(\n                    BluetoothNetworkMinor::from(minor_class),\n                    BluetoothNetworkSubMinor::from(sub_minor_class),\n                )\n            }\n            BluetoothMajorClass::AudioVideo => {\n                BluetoothMinorClass::AudioVideo(BluetoothAudioVideoMinor::from(minor_class))\n            }\n            BluetoothMajorClass::Peripheral => {\n                let minor_class = minor_class >> 4 & 0b11; // 2 bits\n                let sub_minor_class = minor_class & 0b1111; // 4 bits\n                BluetoothMinorClass::Peripheral(\n                    BluetoothPeripheralMinor::from(minor_class),\n                    BluetoothPeripheralSubMinor::from(sub_minor_class),\n                )\n            }\n            BluetoothMajorClass::Imaging => {\n                let minor_class = minor_class >> 2 & 0b1111; // 4 bits\n                let sub_minor_class = minor_class & 0b11; // 2 bits\n\n                use BluetoothImagingMinor::*;\n                let flags: Vec<BluetoothImagingMinor> = [Display, Camera, Scanner, Printer]\n                    .into_iter()\n                    .filter(|&flag| minor_class & flag as u8 != 0)\n                    .collect();\n\n                BluetoothMinorClass::Imaging(flags, BluetoothImagingSubMinor::from(sub_minor_class))\n            }\n            BluetoothMajorClass::Wearable => {\n                BluetoothMinorClass::Wearable(BluetoothWearableMinor::from(minor_class))\n            }\n            BluetoothMajorClass::Toy => {\n                BluetoothMinorClass::Toy(BluetoothToyMinor::from(minor_class))\n            }\n            BluetoothMajorClass::Health => {\n                BluetoothMinorClass::Health(BluetoothHealthMinor::from(minor_class))\n            }\n            BluetoothMajorClass::Uncategorized => BluetoothMinorClass::Uncategorized {\n                unused: minor_class,\n            },\n        };\n\n        (major_service_classes, major_class, minor_class)\n    }\n}\n\n#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct BluetoothDevicePairShowPinRequest {\n    pub pin: String,\n    pub confirmation_needed: bool,\n}\n\n#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[serde(tag = \"needs\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub enum DevicePairingNeededAction {\n    /// No extra action is needed\n    None,\n    /// The user only needs to confirm the pairing\n    ConfirmOnly,\n    /// Should be displayed to the user to be inserted in the other device\n    DisplayPin { pin: String },\n    /// An input pin should be provided\n    ProvidePin,\n    /// Pin should be displayed to the user and confirm that is the same as the other device\n    ConfirmPinMatch { pin: String },\n    /// An input pin should be provided\n    ProvidePasswordCredential,\n    /// An input address should be provided\n    ProvideAddress,\n}\n\n#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct DevicePairingAnswer {\n    pub accept: bool,\n    pub pin: Option<String>,\n    pub username: Option<String>,\n    pub password: Option<String>,\n    pub address: Option<String>,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/bluetooth/mod.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, type UnSubscriber } from \"../../handlers/mod.ts\";\nimport { List } from \"../../utils/List.ts\";\nimport type { BluetoothDevice } from \"@seelen-ui/types\";\nimport { newFromInvoke, newOnEvent } from \"../../utils/State.ts\";\n\nexport class BluetoothDevices extends List<BluetoothDevice> {\n  static getAsync(): Promise<BluetoothDevices> {\n    return newFromInvoke(this, SeelenCommand.GetBluetoothDevices);\n  }\n\n  static onChange(cb: (payload: BluetoothDevices) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.BluetoothDevicesChanged);\n  }\n\n  static async discover(): Promise<void> {\n    return await invoke(SeelenCommand.StartBluetoothScanning);\n  }\n\n  static async stopDiscovery(): Promise<void> {\n    return await invoke(SeelenCommand.StopBluetoothScanning);\n  }\n\n  static default(): BluetoothDevices {\n    return new this([]);\n  }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/components.rs",
    "content": "use std::path::PathBuf;\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\n#[serde(rename_all = \"camelCase\")]\npub struct Disk {\n    pub name: String,\n    pub file_system: String,\n    pub total_space: u64,\n    pub available_space: u64,\n    pub mount_point: PathBuf,\n    pub is_removable: bool,\n    pub read_bytes: u64,\n    pub written_bytes: u64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\n#[serde(rename_all = \"camelCase\")]\npub struct NetworkStatistics {\n    pub name: String,\n    pub received: u64,\n    pub transmitted: u64,\n    pub packets_received: u64,\n    pub packets_transmitted: u64,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\n#[serde(rename_all = \"camelCase\")]\npub struct Memory {\n    pub total: u64,\n    pub free: u64,\n    pub swap_total: u64,\n    pub swap_free: u64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\n#[serde(rename_all = \"camelCase\")]\npub struct Core {\n    pub name: String,\n    pub brand: String,\n    pub usage: f32,\n    pub frequency: u64,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/language.rs",
    "content": "#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct SystemLanguage {\n    pub id: String,\n    pub code: String,\n    pub name: String,\n    pub native_name: String,\n    /// List of loaded keyboard layouts for this language\n    pub keyboard_layouts: Vec<KeyboardLayout>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct KeyboardLayout {\n    /// KLID ex: \"00000409\" or \"0000080a\" or \"00010409\"\n    pub id: String,\n    /// HKL: locale input identifier\n    pub handle: String,\n    pub display_name: String,\n    pub active: bool,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct ImeStatus {\n    pub conversion_mode: u32,\n    pub sentence_mode: u32,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/language.ts",
    "content": "import { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../handlers/mod.ts\";\nimport { List } from \"../utils/List.ts\";\nimport { newFromInvoke, newOnEvent } from \"../utils/State.ts\";\nimport type { SystemLanguage } from \"@seelen-ui/types\";\n\nexport class LanguageList extends List<SystemLanguage> {\n  static getAsync(): Promise<LanguageList> {\n    return newFromInvoke(this, SeelenCommand.SystemGetLanguages);\n  }\n\n  static onChange(cb: (payload: LanguageList) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.SystemLanguagesChanged);\n  }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/media.rs",
    "content": "use std::path::PathBuf;\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaPlayerOwner {\n    pub name: String,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaPlayerTimeline {\n    /// The starting timestamp in nanoseconds (aparently it's always 0)\n    pub start: i64,\n    /// The total duration of the media item in nanoseconds\n    pub end: i64,\n    /// Current playback position in nanoseconds\n    pub position: i64,\n    /// The earliest timestamp at which the current media item can currently seek to. (in nanoseconds)\n    pub min_seek: i64,\n    /// The furthest timestamp at which the content can currently seek to. (in nanoseconds)\n    pub max_seek: i64,\n    pub last_updated_time: i64,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaPlayer {\n    pub umid: String,\n    pub title: String,\n    pub author: String,\n    pub thumbnail: Option<PathBuf>,\n    pub owner: MediaPlayerOwner,\n    pub timeline: MediaPlayerTimeline,\n    pub playing: bool,\n    pub default: bool,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaDeviceSession {\n    pub id: String,\n    pub instance_id: String,\n    pub process_id: u32,\n    pub name: String,\n    pub icon_path: Option<PathBuf>,\n    pub is_system: bool,\n    pub volume: f32,\n    pub muted: bool,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(repr(enum = name))]\npub enum MediaDeviceType {\n    Input,\n    Output,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaDevice {\n    pub id: String,\n    pub name: String,\n    pub r#type: MediaDeviceType,\n    pub is_default_multimedia: bool,\n    pub is_default_communications: bool,\n    pub sessions: Vec<MediaDeviceSession>,\n    pub volume: f32,\n    pub muted: bool,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/mod.rs",
    "content": "mod bluetooth;\nmod components;\nmod language;\nmod media;\nmod monitors;\nmod network;\nmod notification;\nmod power;\nmod radios;\nmod trash_bin;\nmod tray;\nmod ui_colors;\nmod user;\nmod user_apps;\nmod win_explorer;\n\npub use bluetooth::*;\npub use components::*;\npub use language::*;\npub use media::*;\npub use monitors::*;\npub use network::*;\npub use notification::*;\npub use power::*;\npub use radios::*;\npub use trash_bin::*;\npub use tray::*;\npub use ui_colors::*;\npub use user::*;\npub use user_apps::*;\npub use win_explorer::*;\n"
  },
  {
    "path": "libs/core/src/system_state/mod.ts",
    "content": "export * from \"./monitors.ts\";\nexport * from \"./ui_colors.ts\";\nexport * from \"./language.ts\";\nexport * from \"./user.ts\";\nexport * from \"./bluetooth/mod.ts\";\n"
  },
  {
    "path": "libs/core/src/system_state/monitors.rs",
    "content": "use crate::{identifier_impl, rect::Rect};\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct PhysicalMonitor {\n    pub id: MonitorId,\n    pub name: String,\n    pub rect: Rect,\n    pub scale_factor: f64,\n    pub is_primary: bool,\n}\n\n#[derive(Debug, Serialize, Deserialize, TS)]\npub struct Brightness {\n    pub min: u32,\n    pub max: u32,\n    pub current: u32,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct MonitorBrightness {\n    pub instance_name: String,\n    pub current_brightness: u8,\n    pub levels: u32,\n    pub available_levels: Vec<u8>,\n    pub active: bool,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]\npub struct MonitorId(pub String);\n\nidentifier_impl!(MonitorId, String);\n\nimpl Default for MonitorId {\n    fn default() -> Self {\n        Self(\"null\".to_string())\n    }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/monitors.ts",
    "content": "import { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../handlers/mod.ts\";\nimport type { PhysicalMonitor } from \"@seelen-ui/types\";\nimport { List } from \"../utils/List.ts\";\nimport { newFromInvoke, newOnEvent } from \"../utils/State.ts\";\n\nexport class ConnectedMonitorList extends List<PhysicalMonitor> {\n  static getAsync(): Promise<ConnectedMonitorList> {\n    return newFromInvoke(this, SeelenCommand.SystemGetMonitors);\n  }\n\n  static onChange(\n    cb: (payload: ConnectedMonitorList) => void,\n  ): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.SystemMonitorsChanged);\n  }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/network/mod.rs",
    "content": "use serde_alias::serde_alias;\n\n#[serde_alias(PascalCase)] // used by pwsh scripts\n#[derive(Debug, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct WlanProfile {\n    pub profile_name: String,\n    #[serde(alias = \"SSID\")]\n    pub ssid: String,\n    pub authentication: String,\n    pub encryption: String,\n    pub password: Option<String>,\n}\n\n#[derive(Debug, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct WlanBssEntry {\n    pub ssid: Option<String>,\n    pub bssid: String,\n    pub channel_frequency: u32,\n    pub signal: u32,\n    /// true if the network is a saved profile\n    pub known: bool,\n    /// true if the network is encrypted like WEP, WPA, or WPA2\n    pub secured: bool,\n    /// true if the interface is connected to this network\n    pub connected: bool,\n    /// true if the interface is connected to this network and is using this channel frequency\n    pub connected_channel: bool,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(repr(enum = name))]\npub enum AdapterStatus {\n    Up,\n    Down,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct NetworkAdapter {\n    // General information\n    pub name: String,\n    pub description: String,\n    pub status: AdapterStatus,\n    pub dns_suffix: String,\n    #[serde(rename = \"type\")]\n    pub interface_type: String,\n    // Address information\n    pub ipv6: Option<String>,\n    pub ipv4: Option<String>,\n    pub gateway: Option<String>,\n    pub mac: String,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/notification.rs",
    "content": "// All this structs/interfaces are taken from https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/schema-root\n\nuse serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct AppNotification {\n    pub id: u32,\n    pub app_umid: String,\n    pub app_name: String,\n    pub app_description: String,\n    pub date: i64,\n    pub content: Toast,\n}\n\n/// Base toast element, which contains at least a single visual element\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[serde(default)]\npub struct Toast {\n    pub header: Option<ToastHeader>,\n    pub visual: ToastVisual,\n    pub actions: Option<ToastActions>,\n    #[serde(rename = \"@launch\")]\n    pub launch: Option<String>,\n    #[serde(rename = \"@activationType\")]\n    pub activation_type: ToastActionActivationType,\n    #[serde(rename = \"@duration\")]\n    pub duration: ToastDuration,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastDuration {\n    #[default]\n    Short,\n    Long,\n    #[serde(other)]\n    Unknown,\n}\n\n/// Specifies a custom header that groups multiple notifications together within Action Center.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-header\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastHeader {\n    #[serde(rename = \"@id\")]\n    pub id: String,\n    #[serde(rename = \"@title\")]\n    pub title: String,\n    #[serde(rename = \"@arguments\")]\n    pub arguments: String,\n    #[serde(default, rename = \"@activationType\")]\n    pub activation_type: ToastActionActivationType,\n}\n\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-visual\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(default)]\npub struct ToastVisual {\n    pub binding: ToastBinding,\n    #[serde(rename = \"@baseUri\")]\n    pub base_uri: String,\n    #[serde(rename = \"@lang\")]\n    pub lang: String,\n    #[serde(rename = \"@version\")]\n    pub version: u32,\n    #[serde(rename = \"@addImageQuery\")]\n    pub add_image_query: bool,\n}\n\nimpl Default for ToastVisual {\n    fn default() -> Self {\n        ToastVisual {\n            binding: Default::default(),\n            base_uri: \"ms-appx:///\".to_owned(),\n            lang: \"none\".to_owned(),\n            version: 1,\n            add_image_query: false,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]\n#[serde(default)]\npub struct ToastBinding {\n    #[serde(rename = \"@template\")]\n    pub template: ToastTemplateType,\n    #[serde(rename = \"$value\")]\n    pub children: Vec<ToastBindingChild>,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastTemplateType {\n    ToastImageAndText01,\n    ToastImageAndText02,\n    ToastImageAndText03,\n    ToastImageAndText04,\n    ToastText01,\n    ToastText02,\n    ToastText03,\n    ToastText04,\n    #[default]\n    ToastGeneric,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub enum ToastBindingChild {\n    Text(ToastText),\n    Image(ToastImage),\n    Group(ToastGroup),\n    Progress(ToastProgress),\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[serde(default)]\npub struct ToastText {\n    #[serde(rename = \"@id\")]\n    pub id: Option<u32>,\n    #[serde(rename = \"$value\")]\n    pub content: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastImage {\n    #[serde(rename = \"@id\")]\n    pub id: Option<u32>,\n    #[serde(rename = \"@src\")]\n    pub src: String,\n    #[serde(rename = \"@alt\")]\n    pub alt: Option<String>,\n    #[serde(default, rename = \"@addImageQuery\")]\n    pub add_image_query: bool,\n    #[serde(rename = \"@placement\")]\n    pub placement: Option<ToastImagePlacement>,\n    #[serde(rename = \"@hint-crop\")]\n    pub hint_crop: Option<ToastImageCropType>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastImageCropType {\n    #[serde(alias = \"circle\")]\n    Circle,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub enum ToastImagePlacement {\n    #[serde(alias = \"appLogoOverride\")]\n    AppLogoOverride,\n    #[serde(alias = \"hero\")]\n    Hero,\n    #[serde(other)]\n    Unknown,\n}\n\n/// Semantically identifies that the content in the group must either be displayed as a whole,\n/// or not displayed if it cannot fit. Groups also allow creating multiple columns.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-group\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastGroup {\n    pub subgroup: Vec<ToastSubGroup>,\n}\n\n/// Specifies vertical columns that can contain text and images.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-subgroup\n#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]\n#[serde(default)]\npub struct ToastSubGroup {\n    #[serde(rename = \"$value\")]\n    pub children: Vec<ToastSubGroupChild>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub enum ToastSubGroupChild {\n    Text(ToastText),\n    Image(ToastImage),\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastProgress {\n    #[serde(rename = \"@title\")]\n    pub title: Option<String>,\n    #[serde(rename = \"@status\")]\n    pub status: String,\n    #[serde(rename = \"@value\")]\n    pub value: String,\n    #[serde(rename = \"@valueStringOverride\")]\n    pub value_string_override: Option<String>,\n}\n\n/// Container element for declaring up to five inputs and up to five button actions for the toast notification.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-actions\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[serde(default)]\npub struct ToastActions {\n    #[serde(rename = \"$value\")]\n    pub children: Vec<ToastActionsChild>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub enum ToastActionsChild {\n    Input(ToastInput),\n    Action(ToastAction),\n}\n\n/// Specifies an input, either text box or selection menu, shown in a toast notification.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-input\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastInput {\n    /// The ID associated with the input\n    #[serde(rename = \"@id\")]\n    pub id: String,\n    /// The type of input.\n    #[serde(rename = \"@type\")]\n    pub r#type: ToastInputType,\n    /// The placeholder displayed for text input.\n    #[serde(rename = \"@placeHolderContent\")]\n    pub placeholder: Option<String>,\n    /// Text displayed as a label for the input.\n    #[serde(rename = \"@title\")]\n    pub title: Option<String>,\n    /// Options for the input if it is of type selection.\n    #[serde(default)]\n    pub selection: Vec<ToastInputSelection>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastInputType {\n    #[serde(alias = \"text\")]\n    Text,\n    #[serde(alias = \"selection\")]\n    Selection,\n    #[serde(other)]\n    Unknown,\n}\n\n/// Specifies the id and text of a selection item.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-selection\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastInputSelection {\n    #[serde(rename = \"@id\")]\n    pub id: String,\n    #[serde(rename = \"@content\")]\n    pub content: String,\n}\n\n/// Specifies a button shown in a toast.\n///\n/// https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-action\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ToastAction {\n    #[serde(rename = \"@content\")]\n    pub content: String,\n    #[serde(rename = \"@arguments\")]\n    pub arguments: String,\n    #[serde(default, rename = \"@activationType\")]\n    pub activation_type: ToastActionActivationType,\n    #[serde(default, rename = \"@afterActivationBehavior\")]\n    pub after_activation_behavior: ToastActionAfterActivationBehavior,\n    /// if set to \"contextMenu\" then the action will be added to_string the context menu intead of the toast\n    #[serde(rename = \"@placement\")]\n    pub placement: Option<ToastActionPlacement>,\n    /// this is used as button icon\n    #[serde(rename = \"@imageUri\")]\n    pub image_uri: Option<String>,\n    #[serde(rename = \"@hint-inputid\")]\n    pub hint_inputid: Option<String>,\n    #[serde(rename = \"@hint-buttonStyle\")]\n    pub hint_button_style: Option<ToastActionButtonStyle>,\n    /// button tooltip\n    #[serde(rename = \"@hint-toolTip\")]\n    pub hint_tooltip: Option<String>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastActionButtonStyle {\n    #[serde(alias = \"success\")]\n    Sucess,\n    #[serde(alias = \"critical\")]\n    Critical,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastActionAfterActivationBehavior {\n    #[default]\n    #[serde(alias = \"default\")]\n    Default,\n    #[serde(alias = \"pendingUpdate\")]\n    PendingUpdate,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastActionActivationType {\n    #[default]\n    #[serde(alias = \"foreground\")]\n    Foreground,\n    #[serde(alias = \"background\")]\n    Background,\n    #[serde(alias = \"protocol\")]\n    Protocol,\n    #[serde(alias = \"system\")]\n    System,\n    #[serde(other)]\n    Unknown,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum ToastActionPlacement {\n    #[serde(alias = \"contextMenu\")]\n    ContextMenu,\n    #[serde(other)]\n    Unknown,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/power.rs",
    "content": "#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[allow(non_snake_case)]\npub struct PowerStatus {\n    pub ac_line_status: u8,\n    pub battery_flag: u8,\n    pub battery_life_percent: u8,\n    pub system_status_flag: u8,\n    pub battery_life_time: u32,\n    pub battery_full_life_time: u32,\n}\n\n// https://learn.microsoft.com/en-us/windows/win32/api/powersetting/ne-powersetting-effective_power_mode\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, TS)]\n#[repr(i32)]\n#[ts(repr(enum = name))]\npub enum PowerMode {\n    BatterySaver,\n    BetterBattery,\n    Balanced,\n    HighPerformance,\n    MaxPerformance,\n    GameMode,\n    MixedReality,\n    Unknown = i32::MAX,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct Battery {\n    // static info\n    pub vendor: Option<String>,\n    pub model: Option<String>,\n    pub serial_number: Option<String>,\n    pub technology: String,\n    // common information\n    pub state: String,\n    pub capacity: f32,\n    pub temperature: Option<f32>,\n    pub percentage: f32,\n    pub cycle_count: Option<u32>,\n    pub smart_charging: bool, // this is triggered by windows idk how but this is a simulation of that\n    // energy stats\n    pub energy: f32,\n    pub energy_full: f32,\n    pub energy_full_design: f32,\n    pub energy_rate: f32,\n    pub voltage: f32,\n    // charge stats\n    pub time_to_full: Option<f32>,\n    pub time_to_empty: Option<f32>,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/radios/mod.rs",
    "content": "/// Represents a radio device like a bluetooth - wifi etc.\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct RadioDevice {\n    pub id: String,\n    pub name: String,\n    pub kind: RadioDeviceKind,\n    /// True if the radio device is currently `On`.\n    pub is_enabled: bool,\n}\n\n#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[ts(repr(enum = name))]\npub enum RadioDeviceKind {\n    Other,\n    WiFi,\n    MobileBroadband,\n    Bluetooth,\n    FM,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/trash_bin.rs",
    "content": "use serde::{Deserialize, Serialize};\nuse ts_rs::TS;\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\n#[serde(rename_all = \"camelCase\")]\npub struct TrashBinInfo {\n    /// Number of items currently in the recycle bin\n    pub item_count: i64,\n    /// Total size of all items in bytes\n    pub size_in_bytes: i64,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/tray.rs",
    "content": "use std::path::PathBuf;\n\n/// Identifier for a systray icon.\n///\n/// A systray icon is either identified by a (window handle + uid) or\n/// its guid. Since a systray icon can be updated to also include a\n/// guid or window handle/uid later on, a stable ID is useful for\n/// consistently identifying an icon.\n#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, TS)]\npub enum SysTrayIconId {\n    HandleUid(isize, u32),\n    Guid(uuid::Uuid),\n}\n\nimpl std::fmt::Display for SysTrayIconId {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            SysTrayIconId::HandleUid(handle, uid) => write!(f, \"{:x}_{}\", handle, uid),\n            SysTrayIconId::Guid(guid) => write!(f, \"{}\", guid),\n        }\n    }\n}\n\nimpl std::str::FromStr for SysTrayIconId {\n    type Err = crate::error::SeelenLibError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        // Try parsing as handle and uid (format: \"handle:uid\").\n        if let Some((handle_str, uid_str)) = s.split_once(':') {\n            return Ok(SysTrayIconId::HandleUid(\n                handle_str.parse().map_err(|_| \"Invalid icon id\")?,\n                uid_str.parse().map_err(|_| \"Invalid icon id\")?,\n            ));\n        }\n\n        // Try parsing as a guid.\n        if let Ok(guid) = uuid::Uuid::parse_str(s) {\n            return Ok(SysTrayIconId::Guid(guid));\n        }\n\n        Err(\"Invalid icon id\".into())\n    }\n}\n\n#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[ts(export)]\npub struct SysTrayIcon {\n    /// Identifier for the icon. Will not change for the lifetime of the\n    /// icon.\n    ///\n    /// The Windows shell uses either a (window handle + uid) or its guid\n    /// to identify which icon to operate on.\n    ///\n    /// Read more: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw\n    pub stable_id: SysTrayIconId,\n\n    /// Application-defined identifier for the icon, used in combination\n    /// with the window handle.\n    ///\n    /// The uid only has to be unique for the window handle. Multiple\n    /// icons (across different window handles) can have the same uid.\n    pub uid: Option<u32>,\n\n    /// Handle to the window that contains the icon. Used in combination\n    /// with a uid.\n    ///\n    /// Note that multiple icons can have the same window handle.\n    pub window_handle: Option<isize>,\n\n    /// GUID for the icon.\n    ///\n    /// Used as an alternate way to identify the icon (versus its window\n    /// handle and uid).\n    pub guid: Option<uuid::Uuid>,\n\n    /// Tooltip to show for the icon on hover.\n    pub tooltip: String,\n\n    /// Handle to the icon bitmap.\n    pub icon_handle: Option<isize>,\n\n    /// Path to the icon image file.\n    pub icon_path: Option<PathBuf>,\n\n    /// Hash of the icon image.\n    ///\n    /// Used to determine if the icon image has changed without having to\n    /// compare the entire image.\n    pub icon_image_hash: Option<String>,\n\n    /// Application-defined message identifier.\n    ///\n    /// Used to send messages to the window that contains the icon.\n    pub callback_message: Option<u32>,\n\n    /// Version of the icon.\n    pub version: Option<u32>,\n\n    /// Whether the icon is visible in the system tray.\n    ///\n    /// This is determined by the `NIS_HIDDEN` flag in the icon's state.\n    pub is_visible: bool,\n}\n\n/// Actions that can be performed on a `SystrayIcon`.\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[ts(export, repr(enum = name))]\npub enum SystrayIconAction {\n    HoverEnter,\n    HoverLeave,\n    HoverMove,\n    LeftClick,\n    RightClick,\n    MiddleClick,\n    LeftDoubleClick,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/ui_colors.rs",
    "content": "/// https://learn.microsoft.com/is-is/uwp/api/windows.ui.viewmanagement.uicolortype?view=winrt-19041\n#[derive(Debug, Default, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct UIColors {\n    pub background: String,\n    pub foreground: String,\n    pub accent_darkest: String,\n    pub accent_darker: String,\n    pub accent_dark: String,\n    pub accent: String,\n    pub accent_light: String,\n    pub accent_lighter: String,\n    pub accent_lightest: String,\n    pub complement: Option<String>,\n}\n\n/// since v2.2.0 this should be used to handle every used color in the app\n#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct Color {\n    pub r: u8,\n    pub g: u8,\n    pub b: u8,\n    pub a: u8,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ColorFormat {\n    Rgba(u32),\n    Rgb(u32),\n    Bgra(u32),\n    Bgr(u32),\n}\n\nimpl Color {\n    pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {\n        Self { r, g, b, a }\n    }\n\n    pub fn parse(format: ColorFormat) -> Self {\n        match format {\n            ColorFormat::Rgba(rgba) => {\n                let [r, g, b, a] = rgba.to_be_bytes();\n                Color::new(r, g, b, a)\n            }\n            ColorFormat::Rgb(rgb) => {\n                let [_, r, g, b] = rgb.to_be_bytes();\n                Color::new(r, g, b, 0xFF)\n            }\n            ColorFormat::Bgra(bgra) => {\n                let [b, g, r, a] = bgra.to_be_bytes();\n                Color::new(r, g, b, a)\n            }\n            ColorFormat::Bgr(bgr) => {\n                let [_, b, g, r] = bgr.to_be_bytes();\n                Color::new(r, g, b, 0xFF)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/ui_colors.ts",
    "content": "import { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../handlers/mod.ts\";\nimport { RuntimeStyleSheet } from \"../utils/DOM.ts\";\nimport { newFromInvoke, newOnEvent } from \"../utils/State.ts\";\nimport type { Color as IColor, UIColors as IUIColors } from \"@seelen-ui/types\";\n\nexport class UIColors {\n  constructor(public inner: IUIColors) {}\n\n  static getAsync(): Promise<UIColors> {\n    return newFromInvoke(this, SeelenCommand.SystemGetColors);\n  }\n\n  static onChange(cb: (payload: UIColors) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.ColorsChanged);\n  }\n\n  static default(): UIColors {\n    return new this({\n      background: \"#ffffff\",\n      foreground: \"#000000\",\n      accent_darkest: \"#990000\",\n      accent_darker: \"#aa0000\",\n      accent_dark: \"#bb0000\",\n      accent: \"#cc0000\",\n      accent_light: \"#dd0000\",\n      accent_lighter: \"#ee0000\",\n      accent_lightest: \"#ff0000\",\n      complement: null,\n    });\n  }\n\n  setAsCssVariables(): void {\n    const oldStyles = new RuntimeStyleSheet(\"@deprecated/system-colors\");\n    const newStyles = new RuntimeStyleSheet(\"@runtime/system-colors\");\n\n    for (const [key, value] of Object.entries(this.inner)) {\n      if (typeof value !== \"string\") {\n        continue;\n      }\n\n      const color = Color.fromHex(value);\n      const { r, g, b } = color;\n\n      // replace rust snake case with kebab case\n      const name = key.replace(\"_\", \"-\");\n\n      // @deprecated old names\n      oldStyles.addVariable(`--config-${name}-color`, value.slice(0, 7));\n      oldStyles.addVariable(`--config-${name}-color-rgb`, `${r}, ${g}, ${b}`);\n\n      if (name.startsWith(\"accent\")) {\n        newStyles.addVariable(`--system-${name}-color`, value.slice(0, 7));\n      }\n    }\n\n    oldStyles.applyToDocument();\n    newStyles.applyToDocument();\n  }\n}\n\nexport interface Color extends IColor {}\n\nexport class Color {\n  constructor(obj: IColor) {\n    this.r = obj.r;\n    this.g = obj.g;\n    this.b = obj.b;\n    this.a = obj.a;\n  }\n\n  /** generates a random solid color */\n  static random(): Color {\n    return new Color({\n      r: Math.floor(Math.random() * 255),\n      g: Math.floor(Math.random() * 255),\n      b: Math.floor(Math.random() * 255),\n      a: 255,\n    });\n  }\n\n  static fromHex(hex: string): Color {\n    if (hex.startsWith(\"#\")) {\n      hex = hex.slice(1);\n    }\n\n    if (hex.length === 3) {\n      hex = hex\n        .split(\"\")\n        .map((char) => `${char}${char}`)\n        .join(\"\");\n    }\n\n    if (hex.length === 6) {\n      hex = hex.padStart(8, \"f\");\n    }\n\n    const color = parseInt(hex.replace(\"#\", \"\"), 16);\n    return new Color({\n      r: (color >> 24) & 255,\n      g: (color >> 16) & 255,\n      b: (color >> 8) & 255,\n      a: color & 255,\n    });\n  }\n\n  toHexString(): string {\n    return (\n      \"#\" +\n      this.r.toString(16).padStart(2, \"0\") +\n      this.g.toString(16).padStart(2, \"0\") +\n      this.b.toString(16).padStart(2, \"0\") +\n      (this.a === 255 ? \"\" : this.a.toString(16).padStart(2, \"0\"))\n    );\n  }\n\n  private getRuntimeStyleSheet(): HTMLStyleElement {\n    const styleId = \"slu-lib-runtime-color-variables\";\n    let styleElement = document.getElementById(styleId) as HTMLStyleElement;\n    if (!styleElement) {\n      styleElement = document.createElement(\"style\");\n      styleElement.id = styleId;\n      styleElement.textContent = \":root {\\n}\";\n      document.head.appendChild(styleElement);\n    }\n    return styleElement;\n  }\n\n  private insertIntoStyleSheet(obj: Record<string, string>): void {\n    const sheet = this.getRuntimeStyleSheet();\n    const lines = sheet.textContent!.split(\"\\n\");\n    lines.pop(); // remove the closing brace\n\n    for (const [key, value] of Object.entries(obj)) {\n      const old = lines.findIndex((line) => line.startsWith(key));\n      if (old !== -1) {\n        lines[old] = `${key}: ${value};`;\n      } else {\n        lines.push(`${key}: ${value};`);\n      }\n    }\n\n    lines.push(\"}\");\n    sheet.textContent = lines.join(\"\\n\");\n  }\n\n  /**\n   * @param name the name of the color\n   * the name will be parsed to lower kebab case and remove non-alphanumeric characters\n   * this will create some css variables as:\\\n   * `--color-{name}` -> #RRGGBBAA\\\n   */\n  setAsCssVariable(name: string): void {\n    const parsedName = name\n      .replace(\"_\", \"-\")\n      .replace(/[^a-zA-Z0-9\\-]/g, \"\")\n      .toLowerCase();\n\n    this.insertIntoStyleSheet({\n      [`--color-${parsedName}`]: this.toHexString(),\n    });\n  }\n\n  /**\n   * https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color\n   *\n   * @param accuracy if true will use an expensive but more accurate algorithm\n   * @returns a number between 0 and 255\n   */\n  calcLuminance(accuracy?: boolean): number {\n    if (accuracy) {\n      // gamma correction\n      const gR = this.r ** 2.2;\n      const gG = this.g ** 2.2;\n      const gB = this.b ** 2.2;\n      return (0.299 * gR + 0.587 * gG + 0.114 * gB) ** (1 / 2.2);\n    }\n    // standard algorithm\n    return 0.2126 * this.r + 0.7152 * this.g + 0.0722 * this.b;\n  }\n\n  complementary(): Color {\n    return new Color({\n      r: 255 - this.r,\n      g: 255 - this.g,\n      b: 255 - this.b,\n      a: this.a,\n    });\n  }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/user.rs",
    "content": "use std::path::PathBuf;\n\nuse ts_rs::TS;\n\n#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export, repr(enum = name)))]\npub enum FolderType {\n    Recent,\n    Desktop,\n    Downloads,\n    Documents,\n    Music,\n    Pictures,\n    Videos,\n}\n\nstatic ALL_FOLDERS: [FolderType; 7] = [\n    FolderType::Recent,\n    FolderType::Desktop,\n    FolderType::Downloads,\n    FolderType::Documents,\n    FolderType::Music,\n    FolderType::Pictures,\n    FolderType::Videos,\n];\n\nimpl FolderType {\n    pub fn values() -> &'static [FolderType] {\n        &ALL_FOLDERS\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct FolderChangedArgs {\n    pub of_folder: FolderType,\n    pub content: Vec<PathBuf>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct User {\n    pub name: String,\n    pub domain: String,\n    pub profile_home_path: PathBuf,\n    pub email: Option<String>,\n    pub one_drive_path: Option<PathBuf>,\n    pub profile_picture_path: Option<PathBuf>,\n    pub xbox_gamertag: Option<String>,\n}\n"
  },
  {
    "path": "libs/core/src/system_state/user.ts",
    "content": "import { SeelenCommand, SeelenEvent, type UnSubscriber } from \"../handlers/mod.ts\";\nimport { newFromInvoke, newOnEvent } from \"../utils/State.ts\";\nimport type { User } from \"@seelen-ui/types\";\n\nexport class UserDetails {\n  constructor(public user: User) {}\n\n  static getAsync(): Promise<UserDetails> {\n    return newFromInvoke(this, SeelenCommand.GetUser);\n  }\n\n  static onChange(cb: (payload: UserDetails) => void): Promise<UnSubscriber> {\n    return newOnEvent(cb, this, SeelenEvent.UserChanged);\n  }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/user_apps/mod.rs",
    "content": "use std::path::PathBuf;\n\nuse crate::{rect::Rect, system_state::MonitorId};\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct FocusedApp {\n    pub hwnd: isize,\n    pub monitor: MonitorId,\n    pub title: String,\n    pub class: String,\n    pub name: String,\n    pub exe: Option<PathBuf>,\n    pub umid: Option<String>,\n    pub is_maximized: bool,\n    pub is_fullscreened: bool,\n    pub is_seelen_overlay: bool,\n    /// this is the rect of the window, without the shadow.\n    pub rect: Option<Rect>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct UserAppWindow {\n    pub hwnd: isize,\n    pub monitor: MonitorId,\n    pub title: String,\n    pub app_name: String,\n    pub is_zoomed: bool,\n    pub is_iconic: bool,\n    pub is_fullscreen: bool,\n    /// this can be from the window property store, or inherited from the process\n    pub umid: Option<String>,\n    /// if the window is a frame, this information will be mapped to the process creator\n    pub process: ProcessInformation,\n    /// this app window can not be pinned\n    pub prevent_pinning: bool,\n    /// custom method to create start this application\n    pub relaunch: Option<Relaunch>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\npub struct ProcessInformation {\n    pub id: u32,\n    pub path: Option<PathBuf>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct UserAppWindowPreview {\n    pub hash: String,\n    pub path: PathBuf,\n    pub width: u32,\n    pub height: u32,\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(default, rename_all = \"camelCase\")]\npub struct Relaunch {\n    /// program to be executed\n    pub command: String,\n    /// arguments to be passed to the relaunch program\n    pub args: Option<RelaunchArguments>,\n    /// path where ejecute the relaunch command\n    pub working_dir: Option<PathBuf>,\n    /// custom relaunch/window icon\n    pub icon: Option<String>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[serde(untagged)]\npub enum RelaunchArguments {\n    Array(Vec<String>),\n    String(String),\n}\n\nimpl std::fmt::Display for RelaunchArguments {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let args = match self {\n            RelaunchArguments::String(args) => args.clone(),\n            RelaunchArguments::Array(args) => args.join(\" \").trim().to_owned(),\n        };\n        write!(f, \"{}\", args)\n    }\n}\n"
  },
  {
    "path": "libs/core/src/system_state/win_explorer.rs",
    "content": "use std::path::PathBuf;\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\npub struct StartMenuItem {\n    pub path: PathBuf,\n    pub umid: Option<String>,\n    pub toast_activator: Option<String>,\n    /// Will be present if the item is a shortcut\n    pub target: Option<PathBuf>,\n    /// Display name for the item\n    pub display_name: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[cfg_attr(feature = \"gen-binds\", ts(export))]\n#[serde(rename_all = \"camelCase\")]\npub struct StartMenuLayout {\n    pub pinned_list: Vec<StartMenuLayoutItem>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub enum StartMenuLayoutItem {\n    DestopAppId(String),\n    PackagedAppId(String),\n    DesktopAppLink(String),\n    SecondaryTile(String),\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct TrayIcon {\n    pub label: String,\n    pub registry: RegistryNotifyIcon,\n}\n\n#[derive(Debug, Clone, Serialize, TS)]\n#[serde(rename_all = \"camelCase\")]\npub struct RegistryNotifyIcon {\n    /// can be used as a unique identifier of the registered tray icon\n    pub key: String,\n    pub executable_path: PathBuf,\n    pub initial_tooltip: Option<String>,\n    /// PNG image of the cached icon\n    pub icon_snapshot: Option<Vec<u8>>,\n    pub icon_guid: Option<String>,\n    pub icon_uid: Option<u32>,\n    pub is_promoted: bool,\n    pub is_running: bool,\n}\n"
  },
  {
    "path": "libs/core/src/utils/DOM.ts",
    "content": "export class RuntimeStyleSheet {\n  #element: HTMLStyleElement;\n  #variables: Array<[string, string]> = [];\n  #styles: Array<string> = [];\n\n  constructor(styleId: string) {\n    let styleElement = document.getElementById(styleId) as HTMLStyleElement;\n    if (!styleElement) {\n      styleElement = document.createElement(\"style\");\n      styleElement.id = styleId;\n      document.head.appendChild(styleElement);\n    }\n    this.#element = styleElement;\n  }\n\n  addVariable(key: string, value: string): void {\n    this.#variables.push([key, value]);\n  }\n\n  addStyle(style: string): void {\n    this.#styles.push(style);\n  }\n\n  clear(): void {\n    this.#variables = [];\n    this.#styles = [];\n  }\n\n  applyToDocument(): void {\n    const vars = this.#variables.map(([key, value]) => `${key}: ${value};`).join(\"\\n\");\n    this.#element.textContent = `:root {\\n${vars}\\n}\\n\\n`;\n    this.#element.textContent += this.#styles.join(\"\\n\\n/* -=-=-=-=-=-=-=-=- */\\n\\n\");\n  }\n}\n"
  },
  {
    "path": "libs/core/src/utils/List.ts",
    "content": "/**\n * A generic, abstract class for managing an array-like collection of items.\n * @template T The type of elements stored in the list.\n */\nexport abstract class List<T = unknown> {\n  /**\n   * Constructor for the List class.\n   * @param inner The internal array that stores the elements.\n   * @throws Error if the provided array is not\n   */\n  constructor(protected inner: T[]) {\n    if (!inner) {\n      throw new Error(\"The inner array cannot be null or undefined.\");\n    }\n    if (!Array.isArray(inner)) {\n      throw new Error(\"The inner array must be an array.\");\n    }\n  }\n\n  public [Symbol.iterator](): Iterable<T> {\n    return this.inner[Symbol.iterator]();\n  }\n\n  public get length(): number {\n    return this.inner.length;\n  }\n\n  /**\n   * Provides direct access to the internal array of items.\n   * @returns A reference to the internal array of items.\n   */\n  public asArray(): T[] {\n    return this.inner;\n  }\n\n  /**\n   * Returns a copy of the internal array of items.\n   * This ensures the internal array remains immutable when accessed via this method.\n   * @returns A new array containing the elements of the internal array.\n   */\n  public all(): T[] {\n    return [...this.inner];\n  }\n}\n"
  },
  {
    "path": "libs/core/src/utils/State.ts",
    "content": "import { invoke as tauriInvoke } from \"@tauri-apps/api/core\";\nimport { listen as tauriListen, type Options as ListenerOptions } from \"@tauri-apps/api/event\";\n\nimport type {\n  AllSeelenCommandArguments,\n  AllSeelenCommandReturns,\n  AllSeelenEventPayloads,\n  SeelenCommand,\n  SeelenEvent,\n  UnSubscriber,\n} from \"../handlers/mod.ts\";\n\n// deno-lint-ignore no-explicit-any\ninterface ConstructorWithSingleArg<T = any> {\n  // deno-lint-ignore no-explicit-any\n  new (arg0: T): any;\n}\n\nexport async function newFromInvoke<\n  Command extends SeelenCommand,\n  This extends ConstructorWithSingleArg<AllSeelenCommandReturns[Command]>,\n>(\n  Class: This,\n  command: Command,\n  args?: NonNullable<AllSeelenCommandArguments[Command]>,\n): Promise<InstanceType<This>> {\n  return new Class(await tauriInvoke(command, args));\n}\n\nexport function newOnEvent<\n  Event extends SeelenEvent,\n  This extends ConstructorWithSingleArg<AllSeelenEventPayloads[Event]>,\n>(\n  cb: (instance: InstanceType<This>) => void,\n  Class: This,\n  event: Event,\n  options?: ListenerOptions,\n): Promise<UnSubscriber> {\n  return tauriListen(\n    event,\n    (eventData) => {\n      cb(new Class(eventData.payload as AllSeelenEventPayloads[Event]));\n    },\n    options,\n  );\n}\n"
  },
  {
    "path": "libs/core/src/utils/async.ts",
    "content": "// deno-lint-ignore no-explicit-any\nexport interface DebouncedFunction<T extends (...args: any[]) => any> {\n  (...args: Parameters<T>): void;\n  cancel(): void;\n  flush(): void;\n  pending(): boolean;\n}\n\n// deno-lint-ignore no-explicit-any\nexport function debounce<T extends (...args: any[]) => any>(\n  fn: T,\n  delay: number,\n): DebouncedFunction<T> {\n  let timeout: ReturnType<typeof setTimeout> | null = null;\n  let lastArgs: Parameters<T> | null = null;\n\n  const debounced = (...args: Parameters<T>) => {\n    lastArgs = args;\n\n    if (timeout) {\n      clearTimeout(timeout);\n    }\n\n    timeout = setTimeout(() => {\n      if (lastArgs) {\n        fn(...lastArgs);\n        lastArgs = null;\n      }\n      timeout = null;\n    }, delay);\n  };\n\n  debounced.cancel = () => {\n    if (timeout) {\n      clearTimeout(timeout);\n      timeout = null;\n    }\n    lastArgs = null;\n  };\n\n  debounced.flush = () => {\n    if (timeout) {\n      clearTimeout(timeout);\n      timeout = null;\n    }\n\n    if (lastArgs) {\n      fn(...lastArgs);\n      lastArgs = null;\n    }\n  };\n\n  debounced.pending = () => {\n    return timeout !== null;\n  };\n\n  return debounced as DebouncedFunction<T>;\n}\n"
  },
  {
    "path": "libs/core/src/utils/mod.rs",
    "content": "use std::path::{Path, PathBuf};\n\nuse schemars::JsonSchema;\n\n#[macro_export(local_inner_macros)]\nmacro_rules! __switch {\n    {\n        if { $($if:tt)+ }\n        do { $($do:tt)* }\n        else { $($else:tt)* }\n    } => { $($do)* };\n    {\n        if { }\n        do { $($do:tt)* }\n        else { $($else:tt)* }\n    } => { $($else)* };\n}\n\n#[macro_export(local_inner_macros)]\nmacro_rules! identifier_impl {\n    ($type:ty, $inner:ty) => {\n        impl std::ops::Deref for $type {\n            type Target = $inner;\n            fn deref(&self) -> &Self::Target {\n                &self.0\n            }\n        }\n\n        impl std::ops::DerefMut for $type {\n            fn deref_mut(&mut self) -> &mut Self::Target {\n                &mut self.0\n            }\n        }\n\n        impl From<&str> for $type {\n            fn from(value: &str) -> Self {\n                Self(<$inner>::from(value))\n            }\n        }\n\n        impl From<String> for $type {\n            fn from(value: String) -> Self {\n                Self(<$inner>::from(value))\n            }\n        }\n\n        impl std::fmt::Display for $type {\n            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n                ::std::write!(f, \"{}\", self.0)\n            }\n        }\n    };\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]\n#[ts(type = \"unknown\")]\npub struct TsUnknown(pub serde_json::Value);\n\nimpl<T: Into<serde_json::Value>> From<T> for TsUnknown {\n    fn from(value: T) -> Self {\n        TsUnknown(value.into())\n    }\n}\n\nstatic ALLOWED_ROOT_FILESTEMS: &[&str] = &[\"metadata\", \"index\", \"mod\", \"main\"];\nstatic ALLOWED_ROOT_EXTENSIONS: &[&str] = &[\"yml\", \"yaml\", \"slu\", \"json\"];\npub fn search_resource_entrypoint(folder: &Path) -> Option<PathBuf> {\n    if folder.is_file() {\n        return None;\n    }\n    for filestem in ALLOWED_ROOT_FILESTEMS {\n        for extension in ALLOWED_ROOT_EXTENSIONS {\n            let path = folder.join(format!(\"{filestem}.{extension}\"));\n            if path.is_file() {\n                return Some(path);\n            }\n        }\n    }\n    None\n}\n"
  },
  {
    "path": "libs/core/src/utils/mod.ts",
    "content": "export class Rect {\n  left = 0;\n  top = 0;\n  right = 0;\n  bottom = 0;\n}\n"
  },
  {
    "path": "libs/positioning/Cargo.toml",
    "content": "[package]\nname = \"positioning\"\nedition = \"2024\"\n\n[lints]\nworkspace = true\n\n[dependencies]\nthiserror = { workspace = true }\nlog = { workspace = true }\nkeyframe = \"1.1.1\"\nscc = { workspace = true }\nwindows = { workspace = true, features = [\"Win32_Foundation\", \"Win32_Graphics_Gdi\", \"Win32_UI_WindowsAndMessaging\"] }\n"
  },
  {
    "path": "libs/positioning/src/api/mod.rs",
    "content": "#[cfg(target_os = \"windows\")]\nmod windows;\n\n#[cfg(target_os = \"windows\")]\npub use windows::*;\n"
  },
  {
    "path": "libs/positioning/src/api/windows.rs",
    "content": "use windows::Win32::{\n    Foundation::{HWND, RECT},\n    Graphics::Gdi::{\n        RDW_ALLCHILDREN, RDW_ERASE, RDW_FRAME, RDW_INVALIDATE, RDW_UPDATENOW, RedrawWindow,\n        UpdateWindow,\n    },\n    UI::WindowsAndMessaging::{\n        BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos, GetClassNameW, GetWindowRect, HDWP,\n        MoveWindow, SWP_DEFERERASE, SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOOWNERZORDER,\n        SWP_NOREDRAW, SWP_NOSENDCHANGING, SWP_NOSIZE, SWP_NOZORDER, SetWindowPos,\n    },\n};\n\nuse crate::{error::Result, rect::Rect};\n\npub fn get_window_rect(window_id: isize) -> Result<Rect> {\n    let mut rect = RECT::default();\n    unsafe { GetWindowRect(HWND(window_id as _), &mut rect)? };\n    Ok(rect.into())\n}\n\n#[allow(dead_code)]\npub fn start_defered_positioning(amount: i32) -> Result<HDWP> {\n    let hdwp = unsafe { BeginDeferWindowPos(amount)? };\n    Ok(hdwp)\n}\n\n#[allow(dead_code)]\npub fn move_window(hwnd: isize, rect: &Rect, redraw: bool) -> Result<()> {\n    unsafe {\n        MoveWindow(\n            HWND(hwnd as _),\n            rect.x,\n            rect.y,\n            rect.width,\n            rect.height,\n            redraw,\n        )?;\n    }\n    Ok(())\n}\n\npub fn position_window(hwnd: isize, rect: &Rect, redraw: bool, no_size: bool) -> Result<()> {\n    let mut flags = SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOOWNERZORDER;\n\n    if !redraw {\n        flags |= SWP_NOREDRAW | SWP_DEFERERASE /* | SWP_NOCOPYBITS */;\n    }\n\n    if no_size {\n        flags |= SWP_NOSIZE;\n    }\n\n    unsafe {\n        SetWindowPos(\n            HWND(hwnd as _),\n            None,\n            rect.x,\n            rect.y,\n            rect.width,\n            rect.height,\n            flags,\n        )?;\n    }\n    Ok(())\n}\n\n#[allow(dead_code)]\npub fn defer_window_position(\n    hdwp: HDWP,\n    window_id: isize,\n    rect: &Rect,\n    no_size: bool,\n) -> Result<HDWP> {\n    let mut flags =\n        SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOZORDER;\n\n    if no_size {\n        flags |= SWP_NOSIZE;\n    }\n\n    let hdwp = unsafe {\n        DeferWindowPos(\n            hdwp,\n            HWND(window_id as _),\n            None,\n            rect.x,\n            rect.y,\n            rect.width,\n            rect.height,\n            flags,\n        )?\n    };\n    Ok(hdwp)\n}\n\n#[allow(dead_code)]\npub fn finish_defered_positioning(hdwp: HDWP) -> Result<()> {\n    unsafe { EndDeferWindowPos(hdwp)? };\n    Ok(())\n}\n\npub fn force_redraw_window(window_id: isize) -> Result<()> {\n    unsafe {\n        let hwnd = HWND(window_id as _);\n        RedrawWindow(\n            Some(hwnd),\n            None,\n            None,\n            RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME | RDW_ERASE,\n        )\n        .ok()?;\n        UpdateWindow(hwnd).ok()?;\n    }\n    Ok(())\n}\n\npub fn get_class(hwnd: isize) -> Result<String> {\n    let mut text: [u16; 512] = [0; 512];\n    let len = unsafe { GetClassNameW(HWND(hwnd as _), &mut text) };\n    let length = usize::try_from(len).unwrap_or(0);\n    Ok(String::from_utf16(&text[..length])?)\n}\n\npub fn is_explorer(hwnd: isize) -> Result<bool> {\n    let class = get_class(hwnd as _)?;\n    Ok(class == \"CabinetWClass\" || class == \"ExplorerWClass\")\n}\n"
  },
  {
    "path": "libs/positioning/src/easings.rs",
    "content": "use keyframe::EasingFunction;\n\n/// Easing based on https://easings.net/#\n#[derive(Clone, Copy)]\npub enum Easing {\n    Linear,\n\n    EaseIn,\n    EaseOut,\n    EaseInOut,\n\n    EaseInQuad,\n    EaseOutQuad,\n    EaseInOutQuad,\n\n    EaseInCubic,\n    EaseOutCubic,\n    EaseInOutCubic,\n\n    EaseInQuart,\n    EaseOutQuart,\n    EaseInOutQuart,\n\n    EaseInQuint,\n    EaseOutQuint,\n    EaseInOutQuint,\n\n    EaseInExpo,\n    EaseOutExpo,\n    EaseInOutExpo,\n\n    EaseInCirc,\n    EaseOutCirc,\n    EaseInOutCirc,\n\n    EaseInBack,\n    EaseOutBack,\n    EaseInOutBack,\n\n    EaseInElastic,\n    EaseOutElastic,\n    EaseInOutElastic,\n\n    EaseInBounce,\n    EaseOutBounce,\n    EaseInOutBounce,\n}\n\nimpl EasingFunction for Easing {\n    fn y(&self, x: f64) -> f64 {\n        use keyframe::functions::*;\n        match self {\n            Easing::Linear => Linear.y(x),\n            Easing::EaseIn => EaseIn.y(x),\n            Easing::EaseOut => EaseOut.y(x),\n            Easing::EaseInOut => EaseInOut.y(x),\n            Easing::EaseInQuad => EaseInQuad.y(x),\n            Easing::EaseOutQuad => EaseOutQuad.y(x),\n            Easing::EaseInOutQuad => EaseInOutQuad.y(x),\n            Easing::EaseInCubic => EaseInCubic.y(x),\n            Easing::EaseOutCubic => EaseOutCubic.y(x),\n            Easing::EaseInOutCubic => EaseInOutCubic.y(x),\n            Easing::EaseInQuart => EaseInQuart.y(x),\n            Easing::EaseOutQuart => EaseOutQuart.y(x),\n            Easing::EaseInOutQuart => EaseInOutQuart.y(x),\n            Easing::EaseInQuint => EaseInQuint.y(x),\n            Easing::EaseOutQuint => EaseOutQuint.y(x),\n            Easing::EaseInOutQuint => EaseInOutQuint.y(x),\n            Easing::EaseInExpo => Self::ease_in_expo(x),\n            Easing::EaseOutExpo => Self::ease_out_expo(x),\n            Easing::EaseInOutExpo => Self::ease_in_out_expo(x),\n            Easing::EaseInCirc => Self::ease_in_circ(x),\n            Easing::EaseOutCirc => Self::ease_out_circ(x),\n            Easing::EaseInOutCirc => Self::ease_in_out_circ(x),\n            Easing::EaseInBack => Self::ease_in_back(x),\n            Easing::EaseOutBack => Self::ease_out_back(x),\n            Easing::EaseInOutBack => Self::ease_in_out_back(x),\n            Easing::EaseInElastic => Self::ease_in_elastic(x),\n            Easing::EaseOutElastic => Self::ease_out_elastic(x),\n            Easing::EaseInOutElastic => Self::ease_in_out_elastic(x),\n            Easing::EaseInBounce => Self::ease_in_bounce(x),\n            Easing::EaseOutBounce => Self::ease_out_bounce(x),\n            Easing::EaseInOutBounce => Self::ease_in_out_bounce(x),\n        }\n    }\n}\n\nimpl Easing {\n    pub fn from_name(name: &str) -> Option<Easing> {\n        let name = name.to_lowercase();\n        let easing = match name.as_str() {\n            \"linear\" => Easing::Linear,\n            \"easein\" => Easing::EaseIn,\n            \"easeout\" => Easing::EaseOut,\n            \"easeinout\" => Easing::EaseInOut,\n            \"easeinquad\" => Easing::EaseInQuad,\n            \"easeoutquad\" => Easing::EaseOutQuad,\n            \"easeinoutquad\" => Easing::EaseInOutQuad,\n            \"easeincubic\" => Easing::EaseInCubic,\n            \"easeoutcubic\" => Easing::EaseOutCubic,\n            \"easeinoutcubic\" => Easing::EaseInOutCubic,\n            \"easeinquart\" => Easing::EaseInQuart,\n            \"easeoutquart\" => Easing::EaseOutQuart,\n            \"easeinoutquart\" => Easing::EaseInOutQuart,\n            \"easeinquint\" => Easing::EaseInQuint,\n            \"easeoutquint\" => Easing::EaseOutQuint,\n            \"easeinoutquint\" => Easing::EaseInOutQuint,\n            \"easeinexpo\" => Easing::EaseInExpo,\n            \"easeoutexpo\" => Easing::EaseOutExpo,\n            \"easeinoutexpo\" => Easing::EaseInOutExpo,\n            \"easeincirc\" => Easing::EaseInCirc,\n            \"easeoutcirc\" => Easing::EaseOutCirc,\n            \"easeinoutcirc\" => Easing::EaseInOutCirc,\n            \"easeinback\" => Easing::EaseInBack,\n            \"easeoutback\" => Easing::EaseOutBack,\n            \"easeinoutback\" => Easing::EaseInOutBack,\n            \"easeinelastic\" => Easing::EaseInElastic,\n            \"easeoutelastic\" => Easing::EaseOutElastic,\n            \"easeinoutelastic\" => Easing::EaseInOutElastic,\n            \"easeinbounce\" => Easing::EaseInBounce,\n            \"easeoutbounce\" => Easing::EaseOutBounce,\n            \"easeinoutbounce\" => Easing::EaseInOutBounce,\n            _ => {\n                return None;\n            }\n        };\n        Some(easing)\n    }\n\n    #[inline]\n    fn ease_in_expo(x: f64) -> f64 {\n        if x == 0.0 {\n            0.0\n        } else {\n            2.0f64.powf(10.0 * (x - 1.0))\n        }\n    }\n\n    #[inline]\n    fn ease_out_expo(x: f64) -> f64 {\n        if x == 1.0 {\n            1.0\n        } else {\n            1.0 - 2.0f64.powf(-10.0 * x)\n        }\n    }\n\n    #[inline]\n    fn ease_in_out_expo(x: f64) -> f64 {\n        if x == 0.0 {\n            0.0\n        } else if x == 1.0 {\n            1.0\n        } else if x < 0.5 {\n            2.0f64.powf(20.0 * x - 10.0) / 2.0\n        } else {\n            (2.0 - 2.0f64.powf(-20.0 * x + 10.0)) / 2.0\n        }\n    }\n\n    #[inline]\n    fn ease_in_circ(x: f64) -> f64 {\n        1.0 - f64::sqrt(1.0 - x * x)\n    }\n\n    #[inline]\n    fn ease_out_circ(x: f64) -> f64 {\n        f64::sqrt(1.0 - (x - 1.0).powi(2))\n    }\n\n    #[inline]\n    fn ease_in_out_circ(x: f64) -> f64 {\n        if x < 0.5 {\n            (1.0 - f64::sqrt(1.0 - (2.0 * x) * (2.0 * x))) / 2.0\n        } else {\n            (f64::sqrt(1.0 - (-2.0 * x + 2.0).powi(2)) + 1.0) / 2.0\n        }\n    }\n\n    #[inline]\n    fn ease_in_back(x: f64) -> f64 {\n        let c1 = 1.70158;\n        let c3 = c1 + 1.0;\n        c3 * x * x * x - c1 * x * x\n    }\n\n    #[inline]\n    fn ease_out_back(x: f64) -> f64 {\n        let c1 = 1.70158;\n        let c3 = c1 + 1.0;\n        1.0 + c3 * (x - 1.0).powi(3) + c1 * (x - 1.0).powi(2)\n    }\n\n    #[inline]\n    fn ease_in_out_back(x: f64) -> f64 {\n        let c1 = 1.70158;\n        let c2 = c1 * 1.525;\n        if x < 0.5 {\n            ((2.0 * x).powi(2) * ((c2 + 1.0) * 2.0 * x - c2)) / 2.0\n        } else {\n            ((2.0 * x - 2.0).powi(2) * ((c2 + 1.0) * (x * 2.0 - 2.0) + c2) + 2.0) / 2.0\n        }\n    }\n\n    #[inline]\n    fn ease_in_elastic(x: f64) -> f64 {\n        let c4 = 2.0 * std::f64::consts::FRAC_PI_3;\n\n        if x == 0.0 {\n            0.0\n        } else if x == 1.0 {\n            1.0\n        } else {\n            -2.0f64.powf(10.0 * x - 10.0) * f64::sin((x * 10.0 - 10.75) * c4)\n        }\n    }\n\n    #[inline]\n    fn ease_out_elastic(x: f64) -> f64 {\n        let c4 = 2.0 * std::f64::consts::FRAC_PI_3;\n\n        if x == 0.0 {\n            0.0\n        } else if x == 1.0 {\n            1.0\n        } else {\n            2.0f64.powf(-10.0 * x) * f64::sin((x * 10.0 - 0.75) * c4) + 1.0\n        }\n    }\n\n    #[inline]\n    fn ease_in_out_elastic(x: f64) -> f64 {\n        let c5 = (2.0 * std::f64::consts::PI) / 4.5;\n\n        if x == 0.0 {\n            0.0\n        } else if x == 1.0 {\n            1.0\n        } else if x < 0.5 {\n            -(2.0f64.powf(20.0 * x - 10.0) * f64::sin((20.0 * x - 11.125) * c5)) / 2.0\n        } else {\n            (2.0f64.powf(-20.0 * x + 10.0) * f64::sin((20.0 * x - 11.125) * c5)) / 2.0 + 1.0\n        }\n    }\n\n    #[inline]\n    fn ease_in_bounce(x: f64) -> f64 {\n        1.0 - Self::ease_out_bounce(1.0 - x)\n    }\n\n    #[inline]\n    fn ease_out_bounce(x: f64) -> f64 {\n        let n1 = 7.5625;\n        let d1 = 2.75;\n\n        if x < 1.0 / d1 {\n            n1 * x * x\n        } else if x < 2.0 / d1 {\n            let x_adjusted = x - 1.5 / d1;\n            n1 * x_adjusted * x_adjusted + 0.75\n        } else if x < 2.5 / d1 {\n            let x_adjusted = x - 2.25 / d1;\n            n1 * x_adjusted * x_adjusted + 0.9375\n        } else {\n            let x_adjusted = x - 2.625 / d1;\n            n1 * x_adjusted * x_adjusted + 0.984375\n        }\n    }\n\n    #[inline]\n    fn ease_in_out_bounce(x: f64) -> f64 {\n        if x < 0.5 {\n            (1.0 - Self::ease_out_bounce(1.0 - 2.0 * x)) / 2.0\n        } else {\n            (1.0 + Self::ease_out_bounce(2.0 * x - 1.0)) / 2.0\n        }\n    }\n}\n"
  },
  {
    "path": "libs/positioning/src/error.rs",
    "content": "#[derive(Debug, thiserror::Error)]\npub enum Error {\n    #[error(\"Windows: {0}\")]\n    Windows(#[from] windows::core::Error),\n    #[error(\"Starting positioning failed\")]\n    StartingPositioningFailed,\n    #[error(\"Positioning failed\")]\n    SetPositionFailed,\n    #[error(\"Utf16: {0}\")]\n    Utf16(#[from] std::string::FromUtf16Error),\n}\n\npub type Result<T> = core::result::Result<T, Error>;\n"
  },
  {
    "path": "libs/positioning/src/lib.rs",
    "content": "mod api;\npub mod easings;\npub mod error;\npub mod minimization;\npub mod rect;\n\nuse std::collections::HashMap;\nuse std::sync::Arc;\n\nuse crate::{\n    api::{force_redraw_window, get_window_rect, is_explorer, position_window},\n    easings::Easing,\n    error::Result,\n    rect::Rect,\n};\n\n#[derive(Debug, Default)]\npub struct PositionerBuilder {\n    /// key-pair of window id and its desired position\n    pub to_positioning: HashMap<isize, Rect>,\n}\n\nstruct WinDataForAnimation {\n    hwnd: isize,\n    from: Rect,\n    to: Rect,\n    is_size_changing: bool,\n    is_explorer: bool,\n}\n\nimpl PositionerBuilder {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    pub fn add(&mut self, window_id: isize, rect: Rect) {\n        self.to_positioning.insert(window_id, rect);\n    }\n\n    pub fn remove(&mut self, window_id: isize) {\n        self.to_positioning.remove(&window_id);\n    }\n\n    pub fn clear(&mut self) {\n        self.to_positioning.clear();\n    }\n\n    /// Place all windows to their desired position\n    pub fn place(&self) -> Result<()> {\n        for (window_id, rect) in self.to_positioning.iter() {\n            position_window(*window_id, rect, true, false)?;\n        }\n        Ok(())\n    }\n\n    /// Get the batch as a HashMap\n    pub fn build(self) -> HashMap<isize, Rect> {\n        self.to_positioning\n    }\n}\n\n/// Manages the animation of a single window\npub struct WindowAnimation {\n    hwnd: isize,\n    interrupt_signal: Option<std::sync::mpsc::Sender<()>>,\n    animation_thread: Option<std::thread::JoinHandle<()>>,\n}\n\nimpl WindowAnimation {\n    fn new() -> Self {\n        Self {\n            hwnd: 0,\n            interrupt_signal: None,\n            animation_thread: None,\n        }\n    }\n\n    /// Start animating this window. If already animating, interrupt and restart.\n    fn start<F>(\n        &mut self,\n        hwnd: isize,\n        target_rect: Rect,\n        easing: Easing,\n        duration_ms: u64,\n        on_end: Arc<F>,\n    ) -> Result<()>\n    where\n        F: Fn(Result<bool>) + Sync + Send + 'static,\n    {\n        // Interrupt any existing animation for this window\n        self.interrupt();\n        self.wait();\n\n        self.hwnd = hwnd;\n\n        // Get initial rect\n        let initial_rect = get_window_rect(hwnd)?;\n        let is_size_changing =\n            initial_rect.width != target_rect.width || initial_rect.height != target_rect.height;\n        let is_position_changing =\n            initial_rect.x != target_rect.x || initial_rect.y != target_rect.y;\n\n        // Skip if already in position\n        if !is_size_changing && !is_position_changing {\n            return Ok(());\n        }\n\n        let data = WinDataForAnimation {\n            hwnd,\n            from: initial_rect,\n            to: target_rect,\n            is_size_changing,\n            is_explorer: is_explorer(hwnd)?,\n        };\n\n        let (tx, rx) = std::sync::mpsc::channel::<()>();\n        let animation_duration = std::time::Duration::from_millis(duration_ms);\n\n        let thread = std::thread::spawn(move || {\n            let result = Self::perform(&data, easing, animation_duration, rx);\n            on_end(result);\n        });\n\n        self.interrupt_signal = Some(tx);\n        self.animation_thread = Some(thread);\n\n        Ok(())\n    }\n\n    /// Returns true if animation was interrupted/canceled\n    fn perform(\n        data: &WinDataForAnimation,\n        easing: Easing,\n        animation_duration: std::time::Duration,\n        interrupt_rx: std::sync::mpsc::Receiver<()>,\n    ) -> Result<bool> {\n        let start_time = std::time::Instant::now();\n        let mut progress = 0.0;\n        let mut interrupted = false;\n\n        let mut frames = 0;\n        let mut last_frame_time = start_time;\n        let min_frame_duration = std::time::Duration::from_millis(7); // ~ 144 fps as limit\n\n        while progress < 1.0 {\n            if interrupt_rx.try_recv().is_ok() {\n                interrupted = true;\n                break;\n            }\n\n            let elapsed = start_time.elapsed();\n            progress =\n                (elapsed.as_millis() as f64 / animation_duration.as_millis() as f64).min(1.0);\n\n            let rect = keyframe::ease(easing, data.from, data.to, progress);\n            position_window(data.hwnd, &rect, data.is_explorer, !data.is_size_changing)?;\n\n            frames += 1;\n            let elapsed = last_frame_time.elapsed();\n            if elapsed < min_frame_duration {\n                std::thread::sleep(min_frame_duration - elapsed);\n            }\n            last_frame_time = std::time::Instant::now();\n        }\n\n        if !interrupted {\n            log::trace!(\"Animation({:?}) completed in {frames} frames\", data.hwnd);\n            let _ = force_redraw_window(data.hwnd);\n        }\n\n        Ok(interrupted)\n    }\n\n    pub fn is_running(&self) -> bool {\n        self.animation_thread.is_some()\n    }\n\n    fn interrupt(&mut self) {\n        if let Some(signal) = self.interrupt_signal.take() {\n            let _ = signal.send(());\n        }\n    }\n\n    fn wait(&mut self) {\n        if let Some(thread) = self.animation_thread.take() {\n            let _ = thread.join();\n        }\n    }\n}\n\nimpl Drop for WindowAnimation {\n    fn drop(&mut self) {\n        self.interrupt();\n        self.wait();\n    }\n}\n\n/// Orchestrates animations for multiple windows, allowing per-window interruption\npub struct AnimationOrchestrator {\n    animations: scc::HashMap<isize, WindowAnimation>,\n}\n\nimpl AnimationOrchestrator {\n    pub fn new() -> Self {\n        Self {\n            animations: scc::HashMap::new(),\n        }\n    }\n\n    /// Animate a batch of windows with the given duration and easing.\n    /// If a window in the batch is already animating, it will be interrupted and restarted.\n    /// Other windows not in the batch will continue animating uninterrupted.\n    pub fn animate_batch<F>(\n        &self,\n        batch: HashMap<isize, Rect>,\n        duration_ms: u64,\n        easing: Easing,\n        on_end: F,\n    ) -> Result<()>\n    where\n        F: Fn(Result<bool>) + Sync + Send + 'static,\n    {\n        let on_end = Arc::new(on_end);\n        for (hwnd, rect) in batch {\n            self.animate_window(hwnd, rect, duration_ms, easing, on_end.clone())?;\n        }\n        Ok(())\n    }\n\n    fn animate_window<F>(\n        &self,\n        hwnd: isize,\n        target_rect: Rect,\n        duration_ms: u64,\n        easing: Easing,\n        on_end: Arc<F>,\n    ) -> Result<()>\n    where\n        F: Fn(Result<bool>) + Sync + Send + 'static,\n    {\n        // Start animation (this will interrupt any existing animation for this window only)\n        let mut animation = self\n            .animations\n            .entry(hwnd)\n            .or_insert_with(WindowAnimation::new);\n        animation.start(hwnd, target_rect, easing, duration_ms, on_end)?;\n        Ok(())\n    }\n}\n\nimpl Default for AnimationOrchestrator {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "libs/positioning/src/minimization.rs",
    "content": "/* use windows::Win32::{\n    Foundation::{HWND, POINT},\n    UI::WindowsAndMessaging::{\n        AnimateWindow, GetWindowPlacement, IsIconic, SetWindowPlacement, ShowWindow, AW_ACTIVATE, AW_HIDE, AW_HOR_NEGATIVE, AW_HOR_POSITIVE, AW_SLIDE, SHOW_WINDOW_CMD, SW_FORCEMINIMIZE, SW_MINIMIZE, SW_SHOWMINNOACTIVE, WINDOWPLACEMENT, WPF_SETMINPOSITION\n    },\n};\n\nuse crate::error::Result;\n\nfn show_window(hwnd: HWND, cmd: SHOW_WINDOW_CMD) -> Result<()> {\n    let _ = unsafe { ShowWindow(hwnd, cmd) };\n    Ok(())\n}\n\nfn get_window_placement(hwnd: HWND) -> Result<WINDOWPLACEMENT> {\n    let mut placement = WINDOWPLACEMENT {\n        length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,\n        ..Default::default()\n    };\n    unsafe { GetWindowPlacement(hwnd, &mut placement)? };\n    Ok(placement)\n}\n\nfn set_window_placement(hwnd: HWND, placement: &WINDOWPLACEMENT) -> Result<()> {\n    unsafe { SetWindowPlacement(hwnd, placement)? };\n    Ok(())\n}\n\npub fn minimize_to_position(hwnd: HWND, x: i32, y: i32) -> Result<()> {\n    if unsafe { IsIconic(hwnd).as_bool() } {\n        return Ok(());\n    }\n    /* let mut placement = get_window_placement(hwnd)?;\n    println!(\"PLACEMENT: {placement:?}\");\n    placement.flags = placement.flags | WPF_SETMINPOSITION;\n    placement.ptMinPosition = POINT { x, y };\n    println!(\"PLACEMENT: {placement:?}\");\n    set_window_placement(hwnd, &placement)?;\n\n    let placement2 = get_window_placement(hwnd)?;\n    println!(\"PLACEMENT: {placement2:?}\"); */\n\n    unsafe { AnimateWindow(hwnd, 1000, AW_SLIDE | AW_HOR_NEGATIVE | AW_HIDE)? };\n    std::thread::sleep(std::time::Duration::from_millis(1000));\n    // show_window(hwnd, SW_MINIMIZE)?;\n\n    Ok(())\n}\n */\n"
  },
  {
    "path": "libs/positioning/src/rect.rs",
    "content": "use keyframe::CanTween;\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct Rect {\n    pub x: i32,\n    pub y: i32,\n    pub width: i32,\n    pub height: i32,\n}\n\nimpl CanTween for Rect {\n    fn ease(from: Self, to: Self, time: impl keyframe::num_traits::Float) -> Self {\n        #[inline(always)]\n        fn ease_field(from: i32, to: i32, time: impl keyframe::num_traits::Float) -> i32 {\n            if from == to {\n                to\n            } else {\n                f64::ease(from as f64, to as f64, time).ceil() as i32\n            }\n        }\n\n        Self {\n            x: ease_field(from.x, to.x, time),\n            y: ease_field(from.y, to.y, time),\n            width: ease_field(from.width, to.width, time),\n            height: ease_field(from.height, to.height, time),\n        }\n    }\n}\n\nimpl From<windows::Win32::Foundation::RECT> for Rect {\n    fn from(rect: windows::Win32::Foundation::RECT) -> Self {\n        Self {\n            x: rect.left,\n            y: rect.top,\n            width: rect.right - rect.left,\n            height: rect.bottom - rect.top,\n        }\n    }\n}\n\nimpl From<Rect> for windows::Win32::Foundation::RECT {\n    fn from(rect: Rect) -> Self {\n        Self {\n            left: rect.x,\n            top: rect.y,\n            right: rect.x + rect.width,\n            bottom: rect.y + rect.height,\n        }\n    }\n}\n"
  },
  {
    "path": "libs/slu-ipc/Cargo.toml",
    "content": "[package]\nname = \"slu-ipc\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[lints]\nworkspace = true\n\n[dependencies]\ntokio = { workspace = true }\nthiserror = { workspace = true }\nlog = { workspace = true }\ninterprocess = { workspace = true, features = [\"tokio\"] }\nseelen-core = { workspace = true }\nserde = { workspace = true, features = [\"derive\"] }\nserde_json = { workspace = true }\nuuid = { workspace = true, features = [\"serde\"] }\nwindows = { workspace = true, features = [\n    \"Win32_System_RemoteDesktop\",\n    \"Win32_System_Pipes\",\n] }"
  },
  {
    "path": "libs/slu-ipc/src/app.rs",
    "content": "use std::sync::Arc;\n\nuse interprocess::os::windows::named_pipe::{\n    DuplexPipeStream, PipeListenerOptions, pipe_mode::Bytes,\n    tokio::DuplexPipeStream as AsyncDuplexPipeStream,\n};\nuse windows::Win32::System::RemoteDesktop::{ProcessIdToSessionId, WTSGetActiveConsoleSessionId};\n\nuse crate::{\n    common::{\n        IPC, create_security_descriptor, read_from_ipc_stream, send_to_ipc_stream,\n        send_to_ipc_stream_blocking, write_to_ipc_stream,\n    },\n    error::Result,\n    messages::{AppMessage, IpcResponse},\n};\n\npub struct AppIpc {\n    _priv: (),\n}\n\nimpl IPC for AppIpc {\n    fn path() -> String {\n        let session_id = current_session_id().unwrap_or(0);\n        Self::path_with_session(session_id)\n    }\n}\n\nimpl AppIpc {\n    /// Constructs the pipe path for a specific session ID\n    pub fn path_with_session(session_id: u32) -> String {\n        format!(r\"\\\\.\\pipe\\seelen-ui-{}\", session_id)\n    }\n\n    pub fn start<F>(cb: F) -> Result<()>\n    where\n        F: Fn(AppMessage) -> IpcResponse + Send + Sync + 'static,\n    {\n        let sd = create_security_descriptor()?;\n\n        let listener = PipeListenerOptions::new()\n            .path(Self::path())\n            .security_descriptor(Some(sd))\n            .create_tokio_duplex::<Bytes>()?;\n\n        tokio::spawn(async move {\n            let callback = Arc::new(cb);\n            while let Ok(stream) = listener.accept().await {\n                let callback = callback.clone();\n                tokio::spawn(async move {\n                    if let Err(err) = Self::process_connection(&stream, callback).await\n                        && let Err(send_err) =\n                            Self::response_to_client(&stream, IpcResponse::Err(err.to_string()))\n                                .await\n                    {\n                        log::error!(\n                            \"Failed to send error response: {send_err} || Original error: {err}\"\n                        );\n                    }\n                });\n            }\n        });\n        Ok(())\n    }\n\n    async fn process_connection<F>(stream: &AsyncDuplexPipeStream<Bytes>, cb: Arc<F>) -> Result<()>\n    where\n        F: Fn(AppMessage) -> IpcResponse,\n    {\n        let data = read_from_ipc_stream(stream).await?;\n        if data.is_empty() {\n            return Self::response_to_client(stream, IpcResponse::Success).await;\n        }\n\n        let message = AppMessage::from_bytes(&data)?;\n        log::trace!(\"IPC command received: {message:?}\");\n        Self::response_to_client(stream, cb(message)).await?;\n        Ok(())\n    }\n\n    async fn response_to_client(\n        stream: &AsyncDuplexPipeStream<Bytes>,\n        res: IpcResponse,\n    ) -> Result<()> {\n        write_to_ipc_stream(stream, &res.to_bytes()?).await\n    }\n\n    /// Sends a message to the current session asynchronously\n    pub async fn send(message: AppMessage) -> Result<()> {\n        let stream = AsyncDuplexPipeStream::connect_by_path(Self::path()).await?;\n        send_to_ipc_stream(&stream, &message.to_bytes()?)\n            .await?\n            .ok()\n    }\n\n    /// Sends a message to the current session synchronously\n    pub fn send_sync(message: &AppMessage) -> Result<()> {\n        let stream = DuplexPipeStream::connect_by_path(Self::path())?;\n        let data = message.to_bytes()?;\n        send_to_ipc_stream_blocking(&stream, &data)?;\n        Ok(())\n    }\n}\n\n/// Gets the current session ID of the process\npub fn current_session_id() -> Result<u32> {\n    let process_id = std::process::id();\n    let mut session_id = 0;\n    unsafe { ProcessIdToSessionId(process_id, &mut session_id)? };\n    Ok(session_id)\n}\n\n/// Gets the current interactive session, if any\npub fn current_interactive_session_id() -> Option<u32> {\n    let session_id = unsafe { WTSGetActiveConsoleSessionId() };\n    if session_id == u32::MAX {\n        None\n    } else {\n        Some(session_id)\n    }\n}\n"
  },
  {
    "path": "libs/slu-ipc/src/common.rs",
    "content": "use std::{\n    io::{BufRead, Write},\n    time::Duration,\n};\n\nuse interprocess::os::windows::{\n    named_pipe::{\n        DuplexPipeStream, pipe_mode::Bytes, tokio::DuplexPipeStream as AsyncDuplexPipeStream,\n    },\n    security_descriptor::{AsSecurityDescriptorMutExt, SecurityDescriptor},\n};\nuse tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};\n\nuse crate::{error::Result, messages::IpcResponse};\n\n/// https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-control\npub static SE_DACL_PROTECTED: u16 = 4096u16;\n\n/// End of transmission block marker for IPC messages\npub const END_OF_TRANSMISSION_BLOCK: u8 = 0x17;\n\n/// Timeout for IPC operations\npub const IPC_TIMEOUT: Duration = Duration::from_secs(3);\n\n/// Maximum number of retries for failed IPC operations\npub const MAX_RETRIES: u32 = 3;\n\n/// IPC trait for common connection operations\npub trait IPC {\n    fn path() -> String;\n\n    #[allow(async_fn_in_trait)]\n    async fn server_process_id() -> Result<u32> {\n        let stream = AsyncDuplexPipeStream::connect_by_path(Self::path()).await?;\n        let pid = stream.server_process_id()?;\n        write_to_ipc_stream(&stream, &[]).await?;\n        Ok(pid)\n    }\n\n    fn test_connection() -> Result<()> {\n        let stream = DuplexPipeStream::connect_by_path(Self::path())?;\n        let response = send_to_ipc_stream_blocking(&stream, &[])?;\n        response.ok()\n    }\n\n    fn can_stablish_connection() -> bool {\n        Self::test_connection().is_ok()\n    }\n}\n\n/// Creates a security descriptor for IPC pipes\npub fn create_security_descriptor() -> Result<SecurityDescriptor> {\n    let mut sd = SecurityDescriptor::new()?;\n    unsafe { sd.set_dacl(std::ptr::null_mut(), false)? };\n    sd.set_control(SE_DACL_PROTECTED, SE_DACL_PROTECTED)?;\n    Ok(sd)\n}\n\n/// Reads data from an async IPC stream with timeout\npub async fn read_from_ipc_stream(stream: &AsyncDuplexPipeStream<Bytes>) -> Result<Vec<u8>> {\n    let mut reader = BufReader::new(stream);\n    let mut buf = Vec::new();\n\n    tokio::time::timeout(IPC_TIMEOUT, async {\n        reader.read_until(END_OF_TRANSMISSION_BLOCK, &mut buf).await\n    })\n    .await\n    .map_err(|_| crate::error::Error::Timeout(\"Failed to read from IPC stream\".to_string()))??;\n\n    buf.pop();\n    Ok(buf)\n}\n\n/// Writes data to an async IPC stream with timeout\npub async fn write_to_ipc_stream(stream: &AsyncDuplexPipeStream<Bytes>, buf: &[u8]) -> Result<()> {\n    let mut writter = BufWriter::new(stream);\n\n    tokio::time::timeout(IPC_TIMEOUT, async {\n        writter.write_all(buf).await?;\n        writter.write_all(&[END_OF_TRANSMISSION_BLOCK]).await?;\n        writter.flush().await?;\n        Ok::<(), std::io::Error>(())\n    })\n    .await\n    .map_err(|_| crate::error::Error::Timeout(\"Failed to write to IPC stream\".to_string()))??;\n\n    Ok(())\n}\n\n/// Sends data and receives response from an async IPC stream\npub async fn send_to_ipc_stream(\n    stream: &AsyncDuplexPipeStream<Bytes>,\n    buf: &[u8],\n) -> Result<IpcResponse> {\n    write_to_ipc_stream(stream, buf).await?;\n    let buf = read_from_ipc_stream(stream).await?;\n    IpcResponse::from_bytes(&buf)\n}\n\n/// Blocking version to test connections without needed of tokio runtime\npub fn send_to_ipc_stream_blocking(\n    stream: &DuplexPipeStream<Bytes>,\n    buf: &[u8],\n) -> Result<IpcResponse> {\n    let mut writter = std::io::BufWriter::new(stream);\n    writter.write_all(buf)?;\n    writter.write_all(&[END_OF_TRANSMISSION_BLOCK])?;\n    writter.flush()?;\n\n    let mut reader = std::io::BufReader::new(stream);\n    let mut buf = Vec::new();\n    reader.read_until(END_OF_TRANSMISSION_BLOCK, &mut buf)?;\n    buf.pop();\n\n    IpcResponse::from_bytes(&buf)\n}\n\n/// Sends data with retry logic and exponential backoff\npub async fn send_with_retry<F, Fut>(send_fn: F) -> Result<()>\nwhere\n    F: Fn() -> Fut,\n    Fut: std::future::Future<Output = Result<IpcResponse>>,\n{\n    let mut last_error = None;\n\n    for attempt in 0..MAX_RETRIES {\n        match send_fn().await {\n            Ok(response) => return response.ok(),\n            Err(err) => {\n                last_error = Some(err);\n\n                if attempt < MAX_RETRIES - 1 {\n                    // Exponential backoff: 100ms, 200ms, 400ms\n                    let delay = Duration::from_millis(100 * 2u64.pow(attempt));\n                    log::debug!(\n                        \"IPC send failed (attempt {}/{}), retrying in {}ms\",\n                        attempt + 1,\n                        MAX_RETRIES,\n                        delay.as_millis()\n                    );\n                    tokio::time::sleep(delay).await;\n                }\n            }\n        }\n    }\n\n    Err(last_error.unwrap_or_else(|| {\n        crate::error::Error::Timeout(\"Unknown error during IPC send\".to_string())\n    }))\n}\n"
  },
  {
    "path": "libs/slu-ipc/src/error.rs",
    "content": "use thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum Error {\n    #[error(\"IO Error: {0}\")]\n    Io(#[from] std::io::Error),\n    #[error(\"Service Error: {0}\")]\n    IpcResponseError(String),\n    #[error(\"Serde Json Error: {0}\")]\n    SerdeJson(#[from] serde_json::Error),\n    #[error(\"IPC Timeout: {0}\")]\n    Timeout(String),\n    #[error(\"Windows: {0}\")]\n    Windows(#[from] windows::core::Error),\n}\n\npub type Result<T = ()> = core::result::Result<T, Error>;\n"
  },
  {
    "path": "libs/slu-ipc/src/lib.rs",
    "content": "pub mod app;\npub mod common;\npub mod error;\npub mod messages;\npub mod service;\n\n// Re-export main types for convenience\npub use app::AppIpc;\npub use common::IPC;\npub use service::ServiceIpc;\n"
  },
  {
    "path": "libs/slu-ipc/src/messages.rs",
    "content": "use std::collections::HashMap;\n\nuse seelen_core::{rect::Rect, state::Settings};\nuse serde::{Deserialize, Serialize};\n\nuse crate::error::{Error, Result};\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum IpcResponse {\n    Success,\n    Err(String),\n}\n\nimpl IpcResponse {\n    pub fn ok(self) -> Result<()> {\n        match self {\n            IpcResponse::Success => Ok(()),\n            IpcResponse::Err(err) => Err(Error::IpcResponseError(err)),\n        }\n    }\n\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {\n        Ok(serde_json::from_slice(bytes)?)\n    }\n\n    pub fn to_bytes(&self) -> Result<Vec<u8>> {\n        Ok(serde_json::to_vec(self)?)\n    }\n}\n\n// ==============================================\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(untagged)]\npub enum AppMessage {\n    /// Command-line messages\n    Cli(Vec<String>),\n    /// System tray change event\n    TrayChanged(Win32TrayEvent),\n    /// Debug message for logging and diagnostics\n    Debug(String),\n}\n\nimpl AppMessage {\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {\n        Ok(serde_json::from_slice(bytes)?)\n    }\n\n    pub fn to_bytes(&self) -> Result<Vec<u8>> {\n        Ok(serde_json::to_vec(self)?)\n    }\n}\n\n// ==============================================\n\n/// Seelen UI Service Actions\n#[allow(dead_code)]\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum SvcAction {\n    Stop,\n    SetStartup(bool),\n    SetSettings(Box<Settings>),\n    ShowWindow {\n        hwnd: isize,\n        command: i32,\n    },\n    ShowWindowAsync {\n        hwnd: isize,\n        command: i32,\n    },\n    SetWindowPosition {\n        hwnd: isize,\n        rect: Rect,\n        flags: u32,\n    },\n    DeferWindowPositions {\n        list: HashMap<isize, Rect>,\n        animated: bool,\n        animation_duration: u64,\n        easing: String,\n    },\n    SetForeground(isize),\n    StartShortcutRegistration,\n    StopShortcutRegistration,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct SvcMessage {\n    pub token: String,\n    pub action: SvcAction,\n}\n\nimpl SvcMessage {\n    pub fn signature() -> &'static str {\n        std::env!(\"SLU_SERVICE_CONNECTION_TOKEN\")\n    }\n\n    pub fn is_signature_valid(&self) -> bool {\n        self.token == SvcMessage::signature()\n    }\n\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {\n        Ok(serde_json::from_slice(bytes)?)\n    }\n\n    pub fn to_bytes(&self) -> Result<Vec<u8>> {\n        Ok(serde_json::to_vec(self)?)\n    }\n}\n\n// ========== Launcher ==========\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum LauncherMessage {\n    GuiStarted,\n    Quit,\n}\n\nimpl LauncherMessage {\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {\n        Ok(serde_json::from_slice(bytes)?)\n    }\n\n    pub fn to_bytes(&self) -> Result<Vec<u8>> {\n        Ok(serde_json::to_vec(self)?)\n    }\n}\n\n// ========== Tray ==========\n\n/// System tray icon data\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\npub struct IconEventData {\n    pub uid: Option<u32>,\n    pub window_handle: Option<isize>,\n    pub guid: Option<uuid::Uuid>,\n    pub tooltip: Option<String>,\n    pub icon_handle: Option<isize>,\n    pub callback_message: Option<u32>,\n    pub version: Option<u32>,\n    pub is_visible: bool,\n}\n\n/// System tray events captured by the hook\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(tag = \"type\")]\npub enum Win32TrayEvent {\n    IconAdd { data: IconEventData },\n    IconUpdate { data: IconEventData },\n    IconRemove { data: IconEventData },\n}\n"
  },
  {
    "path": "libs/slu-ipc/src/service.rs",
    "content": "use std::{future::Future, sync::Arc};\n\nuse interprocess::os::windows::named_pipe::{\n    PipeListenerOptions, pipe_mode::Bytes, tokio::DuplexPipeStream as AsyncDuplexPipeStream,\n};\n\nuse crate::{\n    app::current_session_id,\n    common::{\n        IPC, create_security_descriptor, read_from_ipc_stream, send_to_ipc_stream, send_with_retry,\n        write_to_ipc_stream,\n    },\n    error::Result,\n    messages::{IpcResponse, SvcAction, SvcMessage},\n};\n\npub struct ServiceIpc {\n    _priv: (),\n}\n\nimpl IPC for ServiceIpc {\n    fn path() -> String {\n        let session_id = current_session_id().unwrap_or(0);\n        Self::path_with_session(session_id)\n    }\n}\n\nimpl ServiceIpc {\n    /// Constructs the pipe path for a specific session ID\n    pub fn path_with_session(session_id: u32) -> String {\n        format!(r\"\\\\.\\pipe\\seelen-ui-service-{}\", session_id)\n    }\n}\n\nimpl ServiceIpc {\n    pub fn start<R, F>(cb: F) -> Result<()>\n    where\n        R: Future<Output = IpcResponse> + Send + Sync,\n        F: Fn(SvcAction) -> R + Send + Sync + 'static,\n    {\n        let sd = create_security_descriptor()?;\n\n        let listener = PipeListenerOptions::new()\n            .path(Self::path())\n            .security_descriptor(Some(sd))\n            .create_tokio_duplex::<Bytes>()?;\n\n        tokio::spawn(async move {\n            let callback = Arc::new(cb);\n            while let Ok(stream) = listener.accept().await {\n                let callback = callback.clone();\n                tokio::spawn(async move {\n                    if let Err(err) = Self::process_connection(&stream, callback).await\n                        && let Err(send_err) =\n                            Self::response_to_client(&stream, IpcResponse::Err(err.to_string()))\n                                .await\n                    {\n                        log::error!(\n                            \"Failed to send error response: {send_err} || Original error: {err}\"\n                        );\n                    }\n                });\n            }\n        });\n        Ok(())\n    }\n\n    async fn process_connection<F, R>(\n        stream: &AsyncDuplexPipeStream<Bytes>,\n        cb: Arc<F>,\n    ) -> Result<()>\n    where\n        R: Future<Output = IpcResponse> + Send + Sync,\n        F: Fn(SvcAction) -> R + Send + Sync + 'static,\n    {\n        let data = read_from_ipc_stream(stream).await?;\n        if data.is_empty() {\n            return Self::response_to_client(stream, IpcResponse::Success).await;\n        }\n\n        let message = SvcMessage::from_bytes(&data)?;\n        if !message.is_signature_valid() {\n            Self::response_to_client(\n                stream,\n                IpcResponse::Err(\"Unauthorized connection\".to_owned()),\n            )\n            .await?;\n            return Ok(());\n        }\n\n        log::trace!(\"IPC command received: {:?}\", message.action);\n        Self::response_to_client(stream, cb(message.action).await).await?;\n        Ok(())\n    }\n\n    async fn response_to_client(\n        stream: &AsyncDuplexPipeStream<Bytes>,\n        res: IpcResponse,\n    ) -> Result<()> {\n        write_to_ipc_stream(stream, &res.to_bytes()?).await\n    }\n\n    pub async fn send(message: SvcAction) -> Result<()> {\n        let data = SvcMessage {\n            token: SvcMessage::signature().to_string(),\n            action: message,\n        }\n        .to_bytes()?;\n\n        send_with_retry(|| Self::try_send(&data)).await\n    }\n\n    async fn try_send(data: &[u8]) -> Result<IpcResponse> {\n        let stream = AsyncDuplexPipeStream::connect_by_path(Self::path()).await?;\n        send_to_ipc_stream(&stream, data).await\n    }\n}\n"
  },
  {
    "path": "libs/ui/icons.ts",
    "content": "// This file is generated on build, do not edit.\nexport type IconName =\n  | keyof typeof import(\"react-icons/ai\")\n  | keyof typeof import(\"react-icons/bi\")\n  | keyof typeof import(\"react-icons/bs\")\n  | keyof typeof import(\"react-icons/cg\")\n  | keyof typeof import(\"react-icons/ci\")\n  | keyof typeof import(\"react-icons/di\")\n  | keyof typeof import(\"react-icons/fa\")\n  | keyof typeof import(\"react-icons/fa6\")\n  | keyof typeof import(\"react-icons/fc\")\n  | keyof typeof import(\"react-icons/fi\")\n  | keyof typeof import(\"react-icons/gi\")\n  | keyof typeof import(\"react-icons/go\")\n  | keyof typeof import(\"react-icons/gr\")\n  | keyof typeof import(\"react-icons/hi\")\n  | keyof typeof import(\"react-icons/hi2\")\n  | keyof typeof import(\"react-icons/im\")\n  | keyof typeof import(\"react-icons/io\")\n  | keyof typeof import(\"react-icons/io5\")\n  | keyof typeof import(\"react-icons/lia\")\n  | keyof typeof import(\"react-icons/lu\")\n  | keyof typeof import(\"react-icons/md\")\n  | keyof typeof import(\"react-icons/pi\")\n  | keyof typeof import(\"react-icons/ri\")\n  | keyof typeof import(\"react-icons/rx\")\n  | keyof typeof import(\"react-icons/si\")\n  | keyof typeof import(\"react-icons/sl\")\n  | keyof typeof import(\"react-icons/tb\")\n  | keyof typeof import(\"react-icons/tfi\")\n  | keyof typeof import(\"react-icons/ti\")\n  | keyof typeof import(\"react-icons/vsc\")\n  | keyof typeof import(\"react-icons/wi\");\n"
  },
  {
    "path": "libs/ui/react/components/BackgroundByLayers/infra.module.css",
    "content": ".background {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n\n  .layer {\n    width: 100%;\n    height: 100%;\n    position: absolute;\n  }\n}\n\n.container {\n  position: relative;\n}\n"
  },
  {
    "path": "libs/ui/react/components/BackgroundByLayers/infra.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling\";\nimport type { HTMLAttributes } from \"react\";\n\nimport cs from \"./infra.module.css\";\n\ninterface PropsV2 extends HTMLAttributes<HTMLDivElement> {\n  className?: string;\n  /** for backward compatibility */\n  prefix?: string;\n}\n\nexport function BackgroundByLayersV2({ children, className, prefix, ...divProps }: PropsV2) {\n  let background = (\n    <div className={cx(cs.background, \"bg-layers\")} style={children ? { zIndex: -1 } : undefined}>\n      {Array.from({ length: 10 }, (_, index) => (\n        <div\n          key={index}\n          className={prefix\n            ? cx(cs.layer, `bg-layer-${index + 1}`, `${prefix}-bg-layer-${index + 1}`)\n            : cx(cs.layer, `bg-layer-${index + 1}`)}\n        />\n      ))}\n    </div>\n  );\n\n  if (!children) {\n    /** for backward compatibility with V1 */\n    return background;\n  }\n\n  return (\n    <div className={cx(cs.container, className)} {...divProps}>\n      {background}\n      {children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Icon/FileIcon.tsx",
    "content": "import { IconPackManager } from \"@seelen-ui/lib\";\nimport type { SeelenCommandGetIconArgs } from \"@seelen-ui/lib/types\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useComputed, useSignal, useSignalEffect } from \"@preact/signals\";\nimport { useEffect, useRef } from \"react\";\nimport type { ImgHTMLAttributes } from \"react\";\n\nimport { darkMode, iconPackManager } from \"./common.ts\";\nimport { MissingIcon } from \"./MissingIcon.tsx\";\nimport cs from \"./index.module.css\";\n\ninterface FileIconProps extends SeelenCommandGetIconArgs, Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\"> {\n  /** if true, no missing icon will be rendered in case no icon found */\n  noFallback?: boolean;\n}\n\nexport function FileIcon({ path, umid, noFallback, ...imgProps }: FileIconProps) {\n  const $path = useSignal(path);\n  const $umid = useSignal(umid);\n  $path.value = path;\n  $umid.value = umid;\n\n  const icon = useComputed(() => {\n    const found = iconPackManager.value.value.getIcon({ path: $path.value, umid: $umid.value });\n    if (found) {\n      return {\n        src: (darkMode.value ? found.dark : found.light) || found.base,\n        mask: found.mask,\n        isAproximatelySquare: found.isAproximatelySquare,\n      };\n    }\n    return { src: null as string | null, mask: null as string | null, isAproximatelySquare: false };\n  });\n\n  const prevSrcRef = useRef<string | null>(null);\n\n  // On mount: always request icon extraction\n  useEffect(() => {\n    IconPackManager.requestIconExtraction({ path, umid });\n  }, []);\n\n  // When icon changes: if src went from non-null to null, re-request extraction\n  useSignalEffect(() => {\n    const src = icon.value.src;\n    if (prevSrcRef.current !== null && src === null) {\n      IconPackManager.requestIconExtraction({ path, umid });\n    }\n    prevSrcRef.current = src;\n  });\n\n  const { src, mask, isAproximatelySquare } = icon.value;\n\n  const { ref: _ref, ...figureProps } = imgProps;\n  const dataProps = Object.entries(figureProps as Record<string, unknown>)\n    .filter(([k]) => k.startsWith(\"data-\"))\n    .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, unknown>);\n\n  if (src) {\n    return (\n      <figure\n        {...figureProps}\n        className={cx(cs.outer, imgProps.className)}\n        data-shape={isAproximatelySquare ? \"square\" : \"unknown\"}\n        data-path={path ?? undefined}\n        data-umid={umid ?? undefined}\n      >\n        <img {...dataProps} src={src} />\n        {mask && (\n          <div\n            {...dataProps}\n            className={cs.mask}\n            style={{ maskImage: `url('${mask}')` }}\n          />\n        )}\n      </figure>\n    );\n  }\n\n  if (noFallback) {\n    return null;\n  }\n\n  return <MissingIcon {...figureProps} />;\n}\n"
  },
  {
    "path": "libs/ui/react/components/Icon/MissingIcon.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useComputed } from \"@preact/signals\";\nimport type { ImgHTMLAttributes } from \"react\";\n\nimport { darkMode, iconPackManager } from \"./common.ts\";\nimport cs from \"./index.module.css\";\n\ninterface MissingIconProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\"> {}\n\nexport function MissingIcon({ ref: _ref, ...props }: MissingIconProps) {\n  const icon = useComputed(() => {\n    const found = iconPackManager.value.value.getMissingIcon();\n    if (found) {\n      return {\n        src: (darkMode.value ? found.dark : found.light) || found.base,\n        mask: found.mask,\n      };\n    }\n    return { src: null as string | null, mask: null as string | null };\n  });\n\n  const { src, mask } = icon.value;\n\n  const dataProps = Object.entries(props as Record<string, unknown>)\n    .filter(([k]) => k.startsWith(\"data-\"))\n    .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, unknown>);\n\n  return (\n    <figure {...props} className={cx(cs.outer, props.className)}>\n      <img {...dataProps} src={src || \"\"} />\n      {mask && (\n        <div\n          {...dataProps}\n          className={cs.mask}\n          style={{ maskImage: `url('${mask}')` }}\n        />\n      )}\n    </figure>\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Icon/SpecificIcon.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useComputed, useSignal } from \"@preact/signals\";\nimport type { ImgHTMLAttributes } from \"react\";\n\nimport { darkMode, iconPackManager } from \"./common.ts\";\nimport cs from \"./index.module.css\";\n\ninterface SpecificIconProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\"> {\n  name: string;\n}\n\nexport function SpecificIcon({ name, ref: _ref, ...imgProps }: SpecificIconProps) {\n  const $name = useSignal(name);\n  $name.value = name;\n\n  const icon = useComputed(() => {\n    const found = iconPackManager.value.value.getCustomIcon($name.value);\n    if (found) {\n      return {\n        src: (darkMode.value ? found.dark : found.light) || found.base,\n        mask: found.mask,\n        isAproximatelySquare: found.isAproximatelySquare,\n      };\n    }\n    return { src: null as string | null, mask: null as string | null, isAproximatelySquare: false };\n  });\n\n  const { src, mask, isAproximatelySquare } = icon.value;\n\n  if (!src) {\n    return null;\n  }\n\n  return (\n    <figure\n      {...imgProps}\n      className={cx(cs.outer, imgProps.className)}\n      data-shape={isAproximatelySquare ? \"square\" : \"unknown\"}\n    >\n      <img src={src} />\n      {mask && (\n        <div\n          className={cs.mask}\n          style={{ maskImage: `url('${mask}')` }}\n        />\n      )}\n    </figure>\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Icon/common.ts",
    "content": "import { IconPackManager } from \"@seelen-ui/lib\";\nimport { signal } from \"@preact/signals\";\n\nconst manager = await IconPackManager.create();\nexport const iconPackManager = signal({ _version: 0, value: manager });\nmanager.onChange(() => {\n  iconPackManager.value = { _version: iconPackManager.value._version + 1, value: manager };\n});\n\nconst darkModeQuery = globalThis.matchMedia(\"(prefers-color-scheme: dark)\");\nexport const darkMode = signal(darkModeQuery.matches);\ndarkModeQuery.addEventListener(\"change\", () => {\n  darkMode.value = darkModeQuery.matches;\n});\n"
  },
  {
    "path": "libs/ui/react/components/Icon/index.module.css",
    "content": ".reactIcon {\n  height: 1rem;\n  width: max-content;\n  min-width: max-content;\n  display: inline-block;\n\n  > svg {\n    vertical-align: middle;\n  }\n}\n\n/* the layer is to allow these styles to be overridden by themes */\n@layer IconBaseStyle {\n  .outer {\n    position: relative;\n\n    img {\n      height: 100%;\n      object-fit: contain;\n    }\n\n    .mask {\n      position: absolute;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      mask-repeat: no-repeat;\n      mask-size: contain;\n      mask-position: center;\n      mask-mode: luminance;\n      background-color: var(--system-accent-light-color);\n    }\n  }\n}\n"
  },
  {
    "path": "libs/ui/react/components/Icon/index.tsx",
    "content": "import { forwardRef, type HTMLAttributes } from \"preact/compat\";\n\nimport { cx } from \"../../utils/styling.ts\";\nimport InlineSVG from \"../InlineSvg/index.tsx\";\nimport cs from \"./index.module.css\";\nimport type { IconName } from \"libs/ui/icons.ts\";\n\ninterface ReactIconProps extends HTMLAttributes<HTMLElement> {\n  iconName: IconName;\n  size?: string | number;\n  color?: string;\n  style?: React.CSSProperties;\n}\n\n/** React Icons */\nexport const Icon = forwardRef<HTMLElement, ReactIconProps>((props, ref) => {\n  const { iconName, size, color, className, style, ...rest } = props;\n\n  return (\n    <InlineSVG\n      ref={ref}\n      {...rest}\n      src={`/icons/${iconName}.svg`}\n      className={cx(\"slu-icon\", cs.reactIcon, className)}\n      style={{ height: size, color, ...(style || {}) }}\n    />\n  );\n});\n\nexport * from \"./FileIcon.tsx\";\nexport * from \"./MissingIcon.tsx\";\nexport * from \"./SpecificIcon.tsx\";\n"
  },
  {
    "path": "libs/ui/react/components/InlineSvg/index.module.css",
    "content": ".inlineSvg {\n  > svg {\n    width: 100%;\n    height: 100%;\n  }\n}\n"
  },
  {
    "path": "libs/ui/react/components/InlineSvg/index.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling\";\nimport { forwardRef, type HTMLAttributes, useEffect, useState } from \"react\";\n\nimport cs from \"./index.module.css\";\n\ninterface Props extends HTMLAttributes<HTMLElement> {\n  src: string;\n}\n\nconst InlineSVG = forwardRef<HTMLElement, Props>(({ src, className, ...rest }, ref) => {\n  const [svgContent, setSvgContent] = useState<string | null>(null);\n\n  useEffect(() => {\n    const fetchSVG = async () => {\n      try {\n        const response = await fetch(src);\n        if (!response.ok) {\n          throw new Error(`Failed to fetch SVG: ${response.statusText}`);\n        }\n        const svgText = await response.text();\n        setSvgContent(svgText);\n      } catch (err: any) {\n        console.error(err);\n      }\n    };\n\n    fetchSVG();\n  }, [src]);\n\n  return (\n    <i\n      ref={ref}\n      {...rest}\n      className={cx(cs.inlineSvg, className)}\n      dangerouslySetInnerHTML={{ __html: svgContent ?? \"\" }}\n    />\n  );\n});\n\nexport default InlineSVG;\n"
  },
  {
    "path": "libs/ui/react/components/ResourceText/index.tsx",
    "content": "import type { ResourceText as IResourceText } from \"@seelen-ui/lib/types\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { useTranslation } from \"react-i18next\";\nimport { unified } from \"unified\";\nimport remarkParse from \"remark-parse\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkRehype from \"remark-rehype\";\nimport rehypeStringify from \"rehype-stringify\";\nimport { useEffect } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\n\ninterface Props {\n  className?: string;\n  text?: IResourceText;\n}\n\nexport function ResourceText({ text, className }: Props) {\n  const {\n    i18n: { language },\n  } = useTranslation();\n\n  if (!text) {\n    return null;\n  }\n  if (typeof text === \"string\") {\n    return <span className={className}>{text}</span>;\n  }\n\n  const text2 = text[language] || text[\"en\"];\n  if (!text2) {\n    return null;\n  }\n  return <span className={className}>{text2}</span>;\n}\n\ninterface MarkdownViewerProps {\n  text: IResourceText;\n}\n\nexport function ResourceTextAsMarkdown({ text }: MarkdownViewerProps) {\n  const html = useSignal(\"\");\n  const {\n    i18n: { language },\n  } = useTranslation();\n\n  useEffect(() => {\n    let input = typeof text === \"string\" ? text : text[language] || text[\"en\"];\n    if (!input) {\n      html.value = \"\";\n      return;\n    }\n    safeMarkdownToHtml(input).then((content) => (html.value = content));\n  }, [text, language]);\n\n  if (!html.value) {\n    return null;\n  }\n\n  return (\n    <div\n      className=\"richText\"\n      dangerouslySetInnerHTML={{ __html: html.value }}\n      onClick={(e) => {\n        const target = e.target as HTMLElement;\n        const anchor = target.closest(\"a\");\n        if (anchor?.href) {\n          // force links on markdown being opened on browser\n          e.preventDefault();\n          invoke(SeelenCommand.OpenFile, { path: anchor.href });\n        }\n      }}\n    />\n  );\n}\n\n/** this can be used on untrusted markdown, ex user inputs */\nexport async function safeMarkdownToHtml(markdown: string): Promise<string> {\n  const result = await unified()\n    .use(remarkParse)\n    .use(remarkGfm) // enable GitHub Flavored Markdown\n    .use(remarkRehype) // allow conversion of markdown to html\n    .use(rehypeStringify) // convert html to string\n    .process(markdown);\n  return result.toString();\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/components/ImageWallpaper.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\nimport { useMemo } from \"preact/hooks\";\n\nimport type { DefinedWallProps } from \"../types\";\nimport { getWallpaperStyles } from \"../utils.ts\";\nimport cs from \"../index.module.css\";\n\nexport function ImageWallpaper({ definition, config, onLoad }: DefinedWallProps) {\n  const imageSrc = useMemo(\n    () => convertFileSrc(definition.metadata.path + \"\\\\\" + definition.filename!),\n    [definition.metadata.path, definition.filename],\n  );\n\n  const handleError = (e: Event) => {\n    const target = e.target as HTMLImageElement;\n    console.error(\"Image failed to load:\", {\n      src: imageSrc,\n      naturalWidth: target.naturalWidth,\n      naturalHeight: target.naturalHeight,\n    });\n  };\n\n  return (\n    <img\n      id={definition.id}\n      className={cx(cs.wallpaper, \"wallpaper\")}\n      style={getWallpaperStyles(config)}\n      src={imageSrc}\n      onLoad={onLoad}\n      onError={handleError}\n      decoding=\"async\"\n      loading=\"eager\"\n    />\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/components/ThemedWallpaper.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useEffect } from \"preact/hooks\";\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport type { BaseProps } from \"../types\";\nimport { getWallpaperStyles } from \"../utils.ts\";\nimport cs from \"../index.module.css\";\n\nexport function ThemedWallpaper({\n  definition,\n  config,\n  onLoad,\n}: Pick<BaseProps, \"definition\" | \"config\" | \"onLoad\">) {\n  useEffect(() => {\n    onLoad?.();\n  }, []);\n\n  if (!definition || !config) {\n    return (\n      <div className={cx(cs.wallpaper, cs.defaultWallpaper)}>\n        <BackgroundByLayersV2 />\n      </div>\n    );\n  }\n\n  return (\n    <div id={definition.id} className={cs.wallpaper} style={getWallpaperStyles(config)}>\n      <style>{`@scope { ${definition.css || \"\"} }`}</style>\n      <BackgroundByLayersV2 />\n    </div>\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/components/VideoWallpaper.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\nimport { useEffect, useMemo, useRef } from \"preact/hooks\";\n\nimport type { DefinedWallProps } from \"../types\";\nimport { getPlaybackRate, getWallpaperStyles } from \"../utils.ts\";\nimport cs from \"../index.module.css\";\n\nconst MAX_RETRIES = 3;\nconst WAITING_TIMEOUT_MS = 3000;\nconst STALL_CHECK_INTERVAL_MS = 5000;\n\nexport function VideoWallpaper({ definition, config, muted, paused, onLoad }: DefinedWallProps) {\n  const ref = useRef<HTMLVideoElement>(null);\n  const waitingTimeoutRef = useRef<ReturnType<typeof setTimeout>>();\n  const retryCountRef = useRef(0);\n  const lastTimeUpdateRef = useRef(0);\n  const isLoadedRef = useRef(false);\n\n  const videoSrc = useMemo(\n    () => convertFileSrc(definition.metadata.path + \"\\\\\" + definition.filename!),\n    [definition.metadata.path, definition.filename],\n  );\n\n  // Monitor for stalls by checking timeupdate\n  useEffect(() => {\n    const checkInterval = setInterval(() => {\n      if (ref.current && !paused && isLoadedRef.current) {\n        const currentTime = ref.current.currentTime;\n\n        // If time hasn't changed and video should be playing, it's stalled\n        if (\n          currentTime === lastTimeUpdateRef.current &&\n          ref.current.readyState < HTMLMediaElement.HAVE_FUTURE_DATA &&\n          retryCountRef.current < MAX_RETRIES\n        ) {\n          console.debug(\"Video appears stalled, attempting recovery\");\n          retryCountRef.current++;\n          ref.current.load();\n          ref.current.currentTime = currentTime;\n          ref.current.play().catch((err) => {\n            console.error(\"Failed to resume video after stall:\", err);\n          });\n        }\n\n        lastTimeUpdateRef.current = currentTime;\n      }\n    }, STALL_CHECK_INTERVAL_MS);\n\n    return () => clearInterval(checkInterval);\n  }, [paused]);\n\n  // Cleanup on unmount to prevent memory leaks\n  useEffect(() => {\n    // https://github.com/facebook/react/issues/15583\n    // this is a workaround for a bug in js that causes memory leak on video elements\n    return () => {\n      if (waitingTimeoutRef.current) {\n        clearTimeout(waitingTimeoutRef.current);\n      }\n      if (ref.current) {\n        ref.current.pause();\n        ref.current.removeAttribute(\"src\");\n        ref.current.load();\n        if (globalThis.gc) {\n          setTimeout(() => globalThis.gc?.(), 100);\n        }\n      }\n    };\n  }, []);\n\n  // Handle pause/play state changes\n  useEffect(() => {\n    if (ref.current && paused !== undefined) {\n      if (paused) {\n        ref.current.pause();\n      } else if (ref.current.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {\n        ref.current.play().catch((err) => {\n          console.error(\"Failed to play video:\", err);\n        });\n      }\n    }\n  }, [paused]);\n\n  const handleWaiting = () => {\n    // Clear any existing timeout\n    if (waitingTimeoutRef.current) {\n      clearTimeout(waitingTimeoutRef.current);\n    }\n\n    // Set a timeout to detect if truly stuck\n    waitingTimeoutRef.current = setTimeout(() => {\n      if (ref.current && retryCountRef.current < MAX_RETRIES) {\n        console.debug(\n          `Video stuck in waiting state, retry ${retryCountRef.current + 1}/${MAX_RETRIES}`,\n        );\n        retryCountRef.current++;\n\n        const currentTime = ref.current.currentTime;\n\n        // Full reload to recover from stuck state\n        ref.current.load();\n\n        // Restore position if it wasn't at the beginning\n        if (currentTime > 0) {\n          ref.current.currentTime = currentTime;\n        }\n\n        if (!paused) {\n          ref.current.play().catch((err) => {\n            console.error(\"Failed to resume video after waiting timeout:\", err);\n          });\n        }\n      }\n    }, WAITING_TIMEOUT_MS);\n  };\n\n  const handlePlaying = () => {\n    // Clear timeout when playing successfully\n    if (waitingTimeoutRef.current) {\n      clearTimeout(waitingTimeoutRef.current);\n      waitingTimeoutRef.current = undefined;\n    }\n    // Reset retry count on successful playback\n    retryCountRef.current = 0;\n  };\n\n  const handleStalled = () => {\n    // Stalled = browser thinks it can play but isn't fetching data\n    if (ref.current && retryCountRef.current < MAX_RETRIES) {\n      console.debug(\"Video network stalled, forcing reload\");\n      retryCountRef.current++;\n\n      const currentTime = ref.current.currentTime;\n      ref.current.load();\n      ref.current.currentTime = currentTime;\n\n      if (!paused) {\n        ref.current.play().catch((err) => {\n          console.error(\"Failed to resume video after stall:\", err);\n        });\n      }\n    }\n  };\n\n  const handleCanPlay = () => {\n    if (ref.current && !paused) {\n      ref.current.play().catch((err) => {\n        console.error(\"Failed to play video on canplay:\", err);\n      });\n    }\n  };\n\n  const handleLoadedMetadata = () => {\n    isLoadedRef.current = true;\n    onLoad?.();\n  };\n\n  const handleError = (e: Event) => {\n    const target = e.target as HTMLVideoElement;\n    const error = target.error;\n\n    if (error) {\n      console.error(\"Video error:\", {\n        code: error.code,\n        message: error.message,\n        src: videoSrc,\n      });\n\n      // Attempt recovery on certain errors\n      if (error.code === MediaError.MEDIA_ERR_NETWORK && retryCountRef.current < MAX_RETRIES) {\n        console.debug(\"Network error, attempting recovery\");\n        retryCountRef.current++;\n        setTimeout(() => {\n          if (ref.current) {\n            ref.current.load();\n            if (!paused) {\n              ref.current.play().catch((err) => {\n                console.error(\"Failed to recover from network error:\", err);\n              });\n            }\n          }\n        }, 1000);\n      }\n    }\n  };\n\n  const handleTimeUpdate = () => {\n    if (ref.current) {\n      lastTimeUpdateRef.current = ref.current.currentTime;\n    }\n  };\n\n  return (\n    <video\n      id={definition.id}\n      className={cx(cs.wallpaper, \"wallpaper\")}\n      style={getWallpaperStyles(config)}\n      ref={ref}\n      src={videoSrc}\n      controls={false}\n      muted={muted || config.muted}\n      autoPlay={!paused}\n      loop\n      playsInline\n      disableRemotePlayback\n      preload=\"auto\"\n      playbackRate={getPlaybackRate(config.playbackSpeed)}\n      onLoadedMetadata={handleLoadedMetadata}\n      onWaiting={handleWaiting}\n      onPlaying={handlePlaying}\n      onStalled={handleStalled}\n      onCanPlay={handleCanPlay}\n      onError={handleError}\n      onTimeUpdate={handleTimeUpdate}\n    />\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/index.module.css",
    "content": ".container {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n\n  .wallpaper {\n    width: 100%;\n    height: 100%;\n  }\n\n  .overlay {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n  }\n\n  .pausedMessage {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n\n    background-color: rgba(0, 0, 0, 0.5);\n    color: white;\n    padding: 12px 20px;\n    font-size: 14px;\n    font-weight: 500;\n    border-radius: 10px;\n    backdrop-filter: blur(10px);\n    z-index: 1000;\n  }\n}\n\n.defaultWallpaper {\n  > :global(.bg-layers) {\n    > :global(.bg-layer-1) {\n      background-color: #cdcdcd;\n    }\n\n    > :global(.bg-layer-2) {\n      background-image: url(\"data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8' standalone='no'%3F%3E%3C!-- Created with Inkscape (http://www.inkscape.org/) --%3E%3Csvg width='1920' height='1080' viewBox='0 0 507.99999 285.75' version='1.1' id='svg1' xml:space='preserve' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs1'%3E%3CclipPath clipPathUnits='userSpaceOnUse' id='clipPath11'%3E%3Crect style='opacity:1;fill:%23a8a8a8;fill-opacity:1;fill-rule:nonzero;stroke:%23ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1' id='rect12' width='515.69818' height='280.8439' x='3.8769755' y='2.7772453' /%3E%3C/clipPath%3E%3C/defs%3E%3Cg id='g8' transform='matrix(0.98507229,0,0,1.0174691,-3.8191012,-2.8257612)' style='display:inline;stroke:%23ffffff;stroke-width:0.792846;stroke-dasharray:none;stroke-opacity:1' clip-path='url(%23clipPath11)'%3E%3Cpath style='display:inline;fill:%23a8a8a8;fill-opacity:1;stroke:%23a8a8a8;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='m 235.24329,283.26004 c 9.14773,-16.60847 29.47199,-18.6562 45.50475,-24.51787 15.13284,-7.67616 15.82635,-27.76418 10.62079,-41.87543 -4.88698,-18.23738 15.43954,-29.81469 31.29976,-27.81827 15.1631,-0.0577 32.08431,-6.92295 36.72071,-22.61916 5.76713,-17.14528 -4.22578,-36.14451 -19.92343,-43.97664 -17.88776,-10.82259 -43.16674,-12.73618 -53.83086,-33.006243 -7.61067,-18.281836 12.06641,-38.128182 30.54205,-34.606026 19.59562,1.768802 35.11137,16.959616 44.14823,32.978487 9.31394,13.959382 34.02585,11.183709 33.89003,-7.195746 C 395.43894,59.43346 376.19949,45.073344 359.96274,35.269619 343.39788,24.197088 322.71052,30.329097 304.54507,26.906237 287.28235,23.577717 286.83702,2.7509165 287.79259,3.0571695 c 0,0 4.08887,-3.33729875 15.132,-0.2105399 C 344.2353,3.2532069 385.56225,2.2982364 426.87582,3.2090872 409.359,2.7807894 395.2047,28.885493 413.922,38.252727 c 19.95936,11.871045 43.47282,-1.132569 64.74428,4.937209 16.92744,3.344004 33.62632,19.027115 29.46943,37.5369 -4.43539,22.253284 -27.42545,34.114794 -48.08181,37.320364 -19.8836,4.03073 -44.74641,7.95199 -53.94192,28.8025 -8.29021,20.03664 1.60939,46.24717 -16.17056,62.40173 -15.68147,12.55201 -40.55674,-0.65554 -54.00321,16.20736 -9.50975,18.08682 5.25377,41.03467 -10.84601,58.19132 -29.94187,1.22369 -59.90321,-0.12226 -89.84891,-0.39007 z M 435.98934,194.5716 c -24.39235,-2.91259 -11.24601,-56.38424 9.695,-53.68902 20.94101,2.69522 12.23611,55.16349 -9.695,53.68902 z' id='path1' /%3E%3Cpath style='display:inline;fill:%23767676;fill-opacity:1;stroke:%23767676;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='m 220.526,283.78395 c -6.8569,0.0344 -16.12431,4.26963 -9.83769,-5.45355 6.01236,-22.46822 -11.72804,-41.28385 -14.19222,-62.70661 -2.20799,-17.51156 10.67907,-34.5273 27.93468,-37.70333 15.12637,-3.71814 35.44817,-4.36971 42.36252,-21.07131 5.98141,-14.79097 -6.38829,-30.42361 -21.05875,-32.95421 -18.25394,-4.44733 -41.22583,0.48783 -54.44299,-16.06701 -12.01141,-13.896537 -9.13628,-41.120972 10.08713,-46.902169 15.20383,-4.671379 24.82305,13.193548 17.07332,25.43317 -4.39134,14.098039 15.8395,22.515149 24.76851,12.230247 12.09343,-11.7421 5.29369,-31.36086 -6.43182,-40.671818 -15.2829,-13.991449 -37.06971,-15.052004 -54.87164,-24.00367 -12.60602,-5.496963 -8.01085,-25.421139 6.04803,-23.685377 14.28501,0.499898 28.5035,11.12347 41.87969,6.663634 6.22089,-2.074143 9.97415,-7.3142981 13.37601,-14.0528071 13.01704,0.2380293 31.32771,-1.1700027 44.12817,0.035931 0.1844,13.8535421 11.8366,25.6408451 25.81289,24.6939011 14.84258,-0.529991 30.49165,-1.514868 43.90281,6.22964 15.63968,8.744936 32.30915,20.767334 36.96111,39.030914 3.4133,12.75372 -7.49849,28.585164 -21.54108,24.856012 -16.82893,-4.987683 -20.89855,-24.977872 -34.70466,-34.192643 -14.57475,-12.444704 -40.88629,-12.300978 -50.86685,5.870609 -7.89544,13.328353 1.20543,29.6333 14.03513,35.714816 19.58903,10.37953 45.37673,14.49363 56.67798,35.86783 10.28148,18.46141 -2.6001,43.7219 -23.35265,46.75906 -13.89957,2.53517 -32.4029,-0.65598 -40.9803,13.46342 -8.0848,16.21318 9.15222,35.82172 -4.13439,50.58663 -14.55471,15.82873 -41.11163,9.37691 -51.9,30.09998 -1.01901,6.16065 -5.41389,1.62905 -9.91054,2.02069 -1.00797,-10e-6 -5.81443,-0.092 -6.8224,-0.092 z' id='path2' /%3E%3Cpath style='display:inline;fill:%23606060;fill-opacity:1;stroke:%23606060;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='M 64.816729,283.69898 C 70.174633,266.20245 82.788469,244.11983 97.265442,233.46347 118.57119,218.47583 129.8972,191.69774 126.20774,165.97295 122.14534,130.25949 97.000125,102.44357 73.55139,77.239743 58.101854,61.38378 49.024298,40.967288 42.541056,20.099397 39.896088,11.153859 25.219187,-1.8294131 42.253833,2.48426 107.64461,2.4176594 176.92942,2.7480117 242.31962,3.0025621 238.07564,18.107846 218.85958,22.827564 206.10347,15.508986 196.16741,8.6578195 172.98303,6.4687984 174.4211,23.9694 c 5.19599,15.369872 24.37666,14.830575 37.04566,20.345525 18.5663,5.690027 39.86457,20.01469 38.00139,41.881275 -0.31444,14.31095 -20.982,23.74985 -29.5924,10.611327 -5.6452,-11.308047 9.53651,-29.655572 -8.30784,-35.435552 -15.97603,-5.450735 -29.30836,12.569697 -27.40275,27.305618 0.0582,19.180507 18.57969,32.423037 36.61579,32.248527 16.61838,1.14908 39.10923,1.58886 46.08292,20.09828 5.88686,14.55786 -6.51525,29.53835 -20.81544,32.10012 -16.72807,4.03778 -38.19825,5.31154 -46.1908,23.45432 -9.77588,19.4092 3.44862,39.08711 9.61307,57.23565 2.3249,8.94734 5.51814,27.94972 -3.69953,32.17581 -52.32988,-0.12245 -89.39771,3.75399 -140.954441,-2.29132 z M 326.29911,173.17891 c -16.5659,0.003 -35.28972,-14.49183 -32.40221,-32.33347 5.60615,-16.20536 25.94415,-10.74337 37.3002,-4.4724 12.69282,3.28997 24.33049,19.71275 12.57697,30.57021 -4.53303,4.56547 -11.18834,6.33772 -17.47496,6.23566 z' id='path3' /%3E%3Cpath style='display:inline;fill:%23494949;fill-opacity:1;stroke:%23494949;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='M 1.5114306,285.99047 V 2.5680694 C 12.693011,2.5751194 23.874592,2.5821724 35.056172,2.5892234 45.93452,24.351979 49.846372,49.738088 66.272319,68.539964 85.812786,92.153 109.71132,113.41056 120.46523,142.95857 c 11.34838,27.64598 7.94791,63.23983 -15.40914,83.74255 -14.217985,12.58371 -34.606136,35.72578 -40.534146,58.49271 -1.756132,6.74455 -40.307514,0.79664 -63.0105134,0.79664 z m 144.8057994,0 h -29.1157 c 8.4792,-8.38038 17.72345,-17.43841 18.2864,-30.17462 1.44459,-13.08049 3.29092,-31.54658 19.02457,-34.79682 14.93984,-2.61489 26.35681,12.56149 25.71278,26.40697 -0.0591,12.96874 -1.88116,25.93496 -4.79236,38.56447 z M 118.99551,87.863335 C 104.39719,87.044541 99.954574,70.921436 93.735846,60.353103 88.421271,48.716426 71.519147,46.794743 68.939073,33.518689 68.252837,18.360223 86.660657,23.098465 95.315096,27.174919 c 17.661674,4.983484 35.267834,-2.366157 52.638774,-4.527623 16.86818,-2.238794 18.81645,17.389648 3.64021,19.405613 -14.6292,0.214286 -16.16343,16.796753 -16.3822,27.882136 -0.84803,8.463424 -6.71916,18.096033 -16.21637,17.92829 z' id='path8' /%3E%3C/g%3E%3C/svg%3E%0A\");\n      background-size: cover;\n      background-repeat: no-repeat;\n      height: 100%;\n      width: auto;\n      aspect-ratio: 16 / 9;\n    }\n\n    > :global(.bg-layer-3) {\n      background: linear-gradient(\n        to right,\n        var(--system-accent-darker-color),\n        var(--system-accent-color),\n        var(--system-accent-lighter-color)\n      );\n      mix-blend-mode: multiply;\n      /* filter: saturate(2) brightness(1.2) contrast(0.6); */\n    }\n  }\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/index.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { WallpaperConfiguration } from \"@seelen-ui/lib\";\nimport { WallpaperKind } from \"@seelen-ui/lib/types\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport type { ComponentChildren } from \"preact\";\n\nimport { ThemedWallpaper } from \"./components/ThemedWallpaper\";\nimport { ImageWallpaper } from \"./components/ImageWallpaper\";\nimport { VideoWallpaper } from \"./components/VideoWallpaper\";\nimport type { BaseProps } from \"./types\";\nimport cs from \"./index.module.css\";\n\nconst defaultWallpaperConfig = await WallpaperConfiguration.default();\n\nexport function Wallpaper(props: BaseProps) {\n  const { definition, config = defaultWallpaperConfig } = props;\n\n  const $loaded = useSignal(false);\n\n  function onLoad() {\n    $loaded.value = true;\n    props.onLoad?.();\n  }\n\n  let element: ComponentChildren = null;\n\n  switch (definition?.type) {\n    case WallpaperKind.Image:\n      element = <ImageWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;\n      break;\n    case WallpaperKind.Video:\n      // Use thumbnail as static image for optimization when static prop is true\n      if (props.static) {\n        if (definition.thumbnailFilename) {\n          const thumbnailDefinition = {\n            ...definition,\n            filename: definition.thumbnailFilename,\n          };\n          element = (\n            <ImageWallpaper\n              {...props}\n              onLoad={onLoad}\n              definition={thumbnailDefinition}\n              config={config}\n            />\n          );\n        }\n        break;\n      }\n\n      element = <VideoWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;\n      break;\n    case WallpaperKind.Layered:\n      element = <ThemedWallpaper {...props} onLoad={onLoad} definition={definition} config={config} />;\n      break;\n  }\n\n  if (!element) {\n    element = <ThemedWallpaper {...props} onLoad={onLoad} />; // Default Wallpaper\n  }\n\n  return (\n    <div\n      className={cx(cs.container, \"wallpaper-container\", {\n        rendering: $loaded.value,\n        \"will-unrender\": props.out,\n      })}\n    >\n      {element}\n      {config.withOverlay && $loaded.value && (\n        <div\n          className={cx(cs.overlay, \"wallpaper-overlay\")}\n          style={{\n            mixBlendMode: config.overlayMixBlendMode,\n            backgroundColor: config.overlayColor,\n          }}\n        />\n      )}\n      {props.pausedMessage && props.paused && $loaded.value && definition?.type === \"Video\" && (\n        <div className={cs.pausedMessage}>{props.pausedMessage}</div>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/types.ts",
    "content": "import type { Wallpaper, WallpaperInstanceSettings } from \"@seelen-ui/lib/types\";\n\nexport interface BaseProps {\n  definition?: Wallpaper;\n  config?: WallpaperInstanceSettings;\n  onLoad?: () => void;\n  out?: boolean;\n\n  muted?: boolean;\n  paused?: boolean;\n  pausedMessage?: string;\n  /** Use the thumbnail of the wallpaper instead of the video */\n  static?: boolean;\n}\n\nexport interface DefinedWallProps extends BaseProps {\n  definition: Wallpaper;\n  config: WallpaperInstanceSettings;\n}\n"
  },
  {
    "path": "libs/ui/react/components/Wallpaper/utils.ts",
    "content": "import type { PlaybackSpeed, WallpaperInstanceSettings } from \"@seelen-ui/lib/types\";\nimport type { CSSProperties } from \"preact/compat\";\n\nexport function getPlaybackRate(speed: PlaybackSpeed): number {\n  switch (speed) {\n    case \"xDot25\":\n      return 0.25;\n    case \"xDot5\":\n      return 0.5;\n    case \"xDot75\":\n      return 0.75;\n    case \"x1\":\n      return 1;\n    case \"x1Dot25\":\n      return 1.25;\n    case \"x1Dot5\":\n      return 1.5;\n    case \"x1Dot75\":\n      return 1.75;\n    case \"x2\":\n      return 2;\n  }\n\n  return 1;\n}\n\nexport function getWallpaperStyles(config: WallpaperInstanceSettings) {\n  const styles: CSSProperties = {};\n  const transforms: string[] = [];\n  const filters: string[] = [];\n\n  const { flipHorizontal, flipVertical, blur, saturation, contrast, objectFit, objectPosition } = config;\n\n  styles.objectFit = objectFit;\n  styles.objectPosition = objectPosition;\n\n  if (flipHorizontal) {\n    transforms.push(\"scaleX(-1)\");\n  }\n\n  if (flipVertical) {\n    transforms.push(\"scaleY(-1)\");\n  }\n\n  if (blur > 0) {\n    filters.push(`blur(${blur}px)`);\n  }\n\n  if (saturation !== 1) {\n    filters.push(`saturate(${saturation})`); // 0 is allowed\n  }\n\n  if (contrast !== 1) {\n    filters.push(`contrast(${contrast})`); // 0 is allowed\n  }\n\n  if (transforms.length > 0) {\n    styles.transform = transforms.join(\" \");\n  }\n\n  if (filters.length > 0) {\n    styles.filter = filters.join(\" \");\n  }\n\n  return styles;\n}\n"
  },
  {
    "path": "libs/ui/react/utils/DndKit/utils.ts",
    "content": "export interface DndContainer<T> {\n  id: T;\n  items: T[];\n}\n\nfunction getContainerIdx<T>(\n  id: T,\n  containers: DndContainer<T>[],\n) {\n  return containers.findIndex((c) => c.id === id || (c.items as unknown[]).includes(id));\n}\n\nexport function genericHandleDragOver<T extends string>(\n  event: any,\n  containers: DndContainer<T>[],\n  onChange: (newState: DndContainer<T>[]) => void,\n) {\n  const { source, target } = event.operation;\n  if (!target) return;\n\n  const activeContainerIdx = getContainerIdx(source.id as T, containers);\n  const overContainerIdx = getContainerIdx(target.id as T, containers);\n  if (activeContainerIdx === -1 || overContainerIdx === -1) return; // container not found\n\n  const activeContainer = containers.at(activeContainerIdx)!;\n  const overContainer = containers.at(overContainerIdx)!;\n  if (activeContainer.id === overContainer.id) return; // moving within the same container (not changing containers)\n\n  const activeItem = activeContainer.items.find((item) => item === source.id);\n  if (!activeItem) return;\n\n  const newOverContainerItems = [...overContainer.items];\n  const overItemIdx = overContainer.items.findIndex((item) => item === target.id);\n  if (overItemIdx !== -1) {\n    newOverContainerItems.splice(overItemIdx, 0, activeItem);\n  } else {\n    newOverContainerItems.push(activeItem);\n  }\n\n  const newActiveContainerItems = activeContainer.items.filter((item) => item !== source.id);\n\n  const newState = [...containers];\n  newState[activeContainerIdx] = {\n    ...activeContainer,\n    items: newActiveContainerItems,\n  };\n  newState[overContainerIdx] = {\n    ...overContainer,\n    items: newOverContainerItems,\n  };\n\n  onChange(newState);\n}\n"
  },
  {
    "path": "libs/ui/react/utils/LazySignal.ts",
    "content": "import { Signal } from \"@preact/signals\";\n\n/**\n * Structure done to work with async event programming\n */\nexport class LazySignal<T> extends Signal<T> {\n  private initialized = false;\n  private intializing: Promise<Signal<T>> | null = null;\n\n  constructor(private initializer: () => Promise<T>) {\n    super();\n    this.setByPayload = this.setByPayload.bind(this);\n  }\n\n  get value(): T {\n    if (!this.initialized) {\n      throw new Error(\"LazySignal was not initialized\");\n    }\n    return super.value;\n  }\n\n  set value(value: T) {\n    this.initialized = true;\n    super.value = value;\n  }\n\n  /**\n   * Will call the initializer and set the value if not already set\n   * via another setters.\n   */\n  public init(): Promise<Signal<T>> {\n    if (!this.intializing) {\n      this.intializing = this.initializer().then((initial) => {\n        if (!this.initialized) {\n          this.value = initial;\n        }\n        return this;\n      });\n    }\n    return this.intializing;\n  }\n\n  /**\n   * Utility function to be used as event handler\n   */\n  public setByPayload({ payload }: { payload: T }) {\n    this.value = payload;\n  }\n}\n\n/**\n * The LazySignal double-check pattern ensures that if an event updates\n * the value during initialization, that event value won't be overwritten by\n * the stale initial fetch.\n *\n * How to use:\n *\n * 1. Create lazy signal with async initializer\n * 2. Set up event listeners that can fire anytime\n * 3. Initialize - won't overwrite if event already fired during fetch\n */\nexport function lazySignal<T>(initial: () => Promise<T>): LazySignal<T> {\n  return new LazySignal(initial);\n}\n"
  },
  {
    "path": "libs/ui/react/utils/hooks.ts",
    "content": "import { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport {\n  debounce,\n  type DebouncedFunc,\n  type DebouncedFuncLeading,\n  isEqual,\n  throttle,\n  type ThrottleSettings,\n} from \"lodash\";\nimport { useEffect, useMemo, useRef } from \"react\";\n\nexport function useWindowFocusChange(cb: (focused: boolean) => void) {\n  useEffect(() => {\n    const promise = getCurrentWindow().onFocusChanged((event) => {\n      cb(event.payload);\n    });\n    return () => {\n      promise.then((unlisten) => unlisten());\n    };\n  }, []);\n}\n\n/** Will reset the interval on deps change */\nexport function useInterval(cb: () => void, ms: number, deps: any[] = []) {\n  const ref = useRef<number | null>(null);\n  const clearLastInterval = () => {\n    if (ref.current) {\n      clearInterval(ref.current);\n    }\n  };\n  useEffect(() => {\n    clearLastInterval();\n    ref.current = window.setInterval(cb, ms);\n    return clearLastInterval;\n  }, [ms, ...deps]);\n}\n\nexport function useTimeout(cb: () => void, ms: number, deps: any[] = []) {\n  const ref = useRef<number | null>(null);\n  const clearLastTimeout = () => {\n    if (ref.current) {\n      clearTimeout(ref.current);\n    }\n  };\n  useEffect(() => {\n    clearLastTimeout();\n    ref.current = window.setTimeout(cb, ms);\n    return clearLastTimeout;\n  }, [ms, ...deps]);\n}\n\nexport function useSyncClockInterval(\n  cb: () => void,\n  on: \"minutes\" | \"seconds\",\n  deps: any[] = [],\n) {\n  const ref = useRef<number | null>(null);\n\n  const clearLastInterval = () => {\n    if (ref.current) {\n      clearInterval(ref.current);\n    }\n  };\n\n  useEffect(() => {\n    clearLastInterval();\n\n    const now = new Date();\n    let msToWaitForClockSync = 0;\n    if (on === \"minutes\") {\n      const secondsUntilNextMinute = 60 - now.getSeconds();\n      msToWaitForClockSync = secondsUntilNextMinute * 1000 -\n        now.getMilliseconds();\n    } else if (on === \"seconds\") {\n      msToWaitForClockSync = 1000 - now.getMilliseconds();\n    }\n\n    setTimeout(() => {\n      cb();\n      let interval = on === \"minutes\" ? 60 * 1000 : 1000;\n      ref.current = window.setInterval(cb, interval);\n    }, msToWaitForClockSync);\n\n    return clearLastInterval;\n  }, [on, ...deps]);\n}\n\nexport function useDeepCompareEffect(\n  callback: () => void,\n  dependencies: any[],\n) {\n  const currentDependenciesRef = useRef<any[]>();\n  if (!isEqual(currentDependenciesRef.current, dependencies)) {\n    currentDependenciesRef.current = dependencies;\n  }\n  useEffect(callback, [currentDependenciesRef.current]);\n}\n\nexport function useDebounce<F extends (...args: any[]) => void>(\n  callback: F,\n  ms: number,\n): DebouncedFunc<F> {\n  const ref = useRef<F>();\n\n  useEffect(() => {\n    ref.current = callback;\n  }, [callback]);\n\n  const debouncedCallback = useMemo(() => {\n    const func = (...args: Parameters<F>) => {\n      ref.current?.(...args);\n    };\n    return debounce(func, ms);\n  }, []);\n\n  return debouncedCallback;\n}\n\nexport function useThrottle<F extends (...args: any[]) => void>(\n  callback: F,\n  ms: number,\n  options?: ThrottleSettings,\n): DebouncedFuncLeading<F> {\n  const ref = useRef<F>();\n\n  useEffect(() => {\n    ref.current = callback;\n  }, [callback]);\n\n  const throttledCallback = useMemo(() => {\n    const func = (...args: Parameters<F>) => {\n      ref.current?.(...args);\n    };\n    return throttle(func, ms, options);\n  }, []);\n\n  return throttledCallback;\n}\n"
  },
  {
    "path": "libs/ui/react/utils/index.ts",
    "content": "import type { ResourceText } from \"@seelen-ui/lib/types\";\n\nexport function getRootContainer(): HTMLElement {\n  const element = document.getElementById(\"root\");\n  if (!element) {\n    throw new Error(\"Root element not found\");\n  }\n  return element;\n}\n\nexport function toPhysicalPixels(size: number): number {\n  return Math.round(size * globalThis.devicePixelRatio);\n}\n\nexport function getResourceText(text: ResourceText, locale: string): string {\n  if (typeof text === \"string\") {\n    return text;\n  }\n  return text[locale] || text[\"en\"] || \"Unknown\";\n}\n\n// Difference between Windows epoch (1601) and Unix epoch (1970) in milliseconds\nconst EPOCH_DIFF_MILLISECONDS = 11644473600000n;\n\n/** Convert Windows FileTime to Js Unix Date */\nexport function WindowsDateFileTimeToDate(fileTime: bigint | number) {\n  if (typeof fileTime === \"number\") fileTime = BigInt(fileTime);\n  return new Date(Number(fileTime / 10000n - EPOCH_DIFF_MILLISECONDS));\n}\n"
  },
  {
    "path": "libs/ui/react/utils/layered.ts",
    "content": "import { SeelenEvent } from \"@seelen-ui/lib\";\nimport { window as TauriWindow } from \"@seelen-ui/lib/tauri\";\n\nclass LayeredHitbox {\n  private _isIgnoringCursorEvents: boolean = true;\n  public firstClick: boolean = true;\n  public isLayeredEnabled: boolean = true;\n\n  get isIgnoringCursorEvents(): boolean {\n    return this._isIgnoringCursorEvents;\n  }\n\n  set isIgnoringCursorEvents(value: boolean) {\n    if (value == false) {\n      this.firstClick = true;\n    }\n    this._isIgnoringCursorEvents = value;\n  }\n}\n\nexport async function declareDocumentAsLayeredHitbox(\n  shouldAllowMouseEvent: (element: Element) => boolean = (element) => element != document.body,\n): Promise<void> {\n  const window = TauriWindow.getCurrentWindow();\n\n  const webviewRect = { x: 0, y: 0, width: 0, height: 0 };\n\n  await window.setIgnoreCursorEvents(true);\n  const data = new LayeredHitbox();\n\n  window.onMoved((e) => {\n    webviewRect.x = e.payload.x;\n    webviewRect.y = e.payload.y;\n  });\n\n  window.onResized((e) => {\n    webviewRect.width = e.payload.width;\n    webviewRect.height = e.payload.height;\n  });\n\n  const { x, y } = await window.outerPosition();\n  webviewRect.x = x;\n  webviewRect.y = y;\n  const { width, height } = await window.outerSize();\n  webviewRect.width = width;\n  webviewRect.height = height;\n\n  window.listen<boolean>(SeelenEvent.HandleLayeredHitboxes, (event) => {\n    data.isLayeredEnabled = event.payload;\n  });\n\n  window.listen<[x: number, y: number]>(SeelenEvent.GlobalMouseMove, (event) => {\n    if (!data.isLayeredEnabled) {\n      return;\n    }\n\n    const [mouseX, mouseY] = event.payload;\n    const { x: windowX, y: windowY, width: windowWidth, height: windowHeight } = webviewRect;\n\n    // check if the mouse is inside the window\n    const isHoverWindow = mouseX >= windowX &&\n      mouseX <= windowX + windowWidth &&\n      mouseY >= windowY &&\n      mouseY <= windowY + windowHeight;\n\n    if (!isHoverWindow) {\n      return;\n    }\n\n    const adjustedX = (mouseX - windowX) / globalThis.devicePixelRatio;\n    const adjustedY = (mouseY - windowY) / globalThis.devicePixelRatio;\n\n    const elementAtPoint = document.elementFromPoint(adjustedX, adjustedY);\n    if (!elementAtPoint) {\n      return;\n    }\n\n    const shouldAllow = shouldAllowMouseEvent(elementAtPoint);\n    if (shouldAllow == data.isIgnoringCursorEvents) {\n      data.isIgnoringCursorEvents = !shouldAllow;\n      window.setIgnoreCursorEvents(!shouldAllow);\n    }\n  });\n\n  globalThis.addEventListener(\"touchstart\", (e) => {\n    const shouldAllow = shouldAllowMouseEvent(e.target as Element);\n    if (shouldAllow == data.isIgnoringCursorEvents) {\n      data.isIgnoringCursorEvents = !shouldAllow;\n      window.setIgnoreCursorEvents(!shouldAllow);\n    }\n  });\n}\n"
  },
  {
    "path": "libs/ui/react/utils/signals.ts",
    "content": "import { UIColors, Widget } from \"@seelen-ui/lib\";\nimport { lazySignal } from \"./LazySignal\";\n\nconst window = Widget.self.window;\n\nexport const $is_this_webview_focused = lazySignal(() => window.isFocused());\nawait window.onFocusChanged(async () => {\n  // the payload value is not used, cuz on startup it gives wrong value.\n  $is_this_webview_focused.value = await window.isFocused();\n});\nawait $is_this_webview_focused.init();\n\nexport const $system_colors = lazySignal(async () => (await UIColors.getAsync()).inner);\nawait UIColors.onChange((colors) => ($system_colors.value = colors.inner));\nawait $system_colors.init();\n"
  },
  {
    "path": "libs/ui/react/utils/styling.ts",
    "content": "import { useEffect, useState } from \"react\";\n\ntype Args = undefined | string | { [x: string]: any };\nexport const cx = (...args: Args[]): string => {\n  return args\n    .map((arg) => {\n      if (!arg) {\n        return;\n      }\n\n      if (typeof arg === \"string\") {\n        return arg;\n      }\n\n      let classnames = \"\";\n      Object.keys(arg).forEach((key) => {\n        if (arg[key]) {\n          classnames += ` ${key}`;\n        }\n      });\n\n      return classnames.trimStart();\n    })\n    .join(\" \");\n};\n\nexport function isDarkModeEnabled() {\n  return globalThis.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\nexport function useDarkMode() {\n  const [isDarkMode, setIsDarkMode] = useState(isDarkModeEnabled());\n\n  useEffect(() => {\n    const mediaQuery = globalThis.matchMedia(\"(prefers-color-scheme: dark)\");\n    const listener = () => setIsDarkMode(mediaQuery.matches);\n    mediaQuery.addEventListener(\"change\", listener);\n    return () => mediaQuery.removeEventListener(\"change\", listener);\n  }, []);\n\n  return isDarkMode;\n}\n"
  },
  {
    "path": "libs/ui/svelte/components/BackgroundByLayers/BackgroundByLayers.svelte",
    "content": "<script lang=\"ts\">\n  let props = $props();\n</script>\n\n<div {...props} class={[\"bg-container\", props.class]}>\n  <div class=\"bg-layers\">\n    {#each Array.from({ length: 10 }, (_, i) => i) as index}\n      <div class={`bg-layer bg-layer-${index + 1}`}></div>\n    {/each}\n  </div>\n  {@render props.children?.()}\n</div>\n\n<style>\n  :global(.bg-container) {\n    position: relative;\n  }\n\n  :global(.bg-layers) {\n    position: absolute;\n    top: 0;\n    right: 0;\n    bottom: 0;\n    left: 0;\n  }\n\n  :global(.bg-layer) {\n    width: 100%;\n    height: 100%;\n    position: absolute;\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/BackgroundByLayers/index.ts",
    "content": "export { default as BackgroundByLayers } from \"./BackgroundByLayers.svelte\";\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/FileIcon.svelte",
    "content": "<script lang=\"ts\">\n  import type { ClassValue } from \"svelte/elements\";\n  import type { SeelenCommandGetIconArgs } from \"@seelen-ui/lib/types\";\n  import { IconPackManager } from \"@seelen-ui/lib\";\n  import { iconPackManager, type IconState } from \"./common.svelte.ts\";\n  import MissingIcon from \"./MissingIcon.svelte\";\n  import { prefersDarkColorScheme } from \"../../runes/DarkMode.svelte.ts\";\n\n  interface Props extends SeelenCommandGetIconArgs {\n    class?: ClassValue;\n    lazy?: boolean;\n    [key: string]: any;\n  }\n\n  let { path, umid, class: className, lazy, ...imgProps }: Props = $props();\n\n  let mounted = { value: false };\n\n  let previousSrc = $state<string | null>(null);\n  let icon: IconState = $derived.by(() => {\n    // Depend on _version to trigger reactivity when icon pack changes\n    iconPackManager._version;\n    const icon = iconPackManager.value.getIcon({ path, umid });\n    if (icon) {\n      return {\n        src: (prefersDarkColorScheme.value ? icon.dark : icon.light) || icon.base,\n        mask: icon.mask,\n        isAproximatelySquare: icon.isAproximatelySquare,\n      };\n    }\n\n    return { src: null, mask: null, isAproximatelySquare: false };\n  });\n\n  // Watch for src becoming null (trigger icon extraction)\n  $effect(() => {\n    if (!mounted.value) {\n      IconPackManager.requestIconExtraction({ path, umid });\n      mounted.value = true;\n    }\n\n    // Trigger icon extraction when src goes from non-null to null\n    if (previousSrc !== null && icon.src === null) {\n      IconPackManager.requestIconExtraction({ path, umid });\n    }\n    previousSrc = icon.src;\n  });\n</script>\n\n{#if icon.src}\n  <figure\n    {...imgProps}\n    class={[\"slu-icon-outer\", className]}\n    data-shape={icon.isAproximatelySquare ? \"square\" : \"unknown\"}\n    data-path={path ?? undefined}\n    data-umid={umid ?? undefined}\n  >\n    <img src={icon.src} alt=\"\" loading={lazy ? \"lazy\" : \"eager\"} draggable=\"false\" />\n    {#if icon.mask}\n      <div class=\"slu-icon-mask\" style=\"mask-image: url('{icon.mask}')\"></div>\n    {/if}\n  </figure>\n{:else}\n  <MissingIcon {...imgProps} class={className} />\n{/if}\n\n<style>\n  :global(.slu-icon-outer) {\n    position: relative;\n  }\n\n  :global(.slu-icon-outer img) {\n    height: 100%;\n    object-fit: contain;\n  }\n\n  :global(.slu-icon-mask) {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    mask-repeat: no-repeat;\n    mask-size: contain;\n    mask-position: center;\n    mask-mode: luminance;\n    background-color: var(--system-accent-light-color);\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/Icon.svelte",
    "content": "<script lang=\"ts\">\n  import type { ClassValue } from \"svelte/elements\";\n  import InlineSVG from \"./InlineSVG.svelte\";\n  import type { IconName } from \"libs/ui/icons\";\n\n  interface Props {\n    iconName: IconName;\n    size?: string | number;\n    color?: string;\n    class?: ClassValue;\n    [key: string]: any;\n  }\n\n  let { iconName, size, color, class: className, ...rest }: Props = $props();\n\n  const computedStyle = $derived.by(() => {\n    const styles: string[] = [];\n    if (size) {\n      const sizeValue = typeof size === \"number\" ? `${size}px` : size;\n      styles.push(`height: ${sizeValue}`);\n    }\n    if (color) {\n      styles.push(`color: ${color}`);\n    }\n    return styles.join(\"; \");\n  });\n</script>\n\n<InlineSVG\n  {...rest}\n  src={`/icons/${iconName}.svg`}\n  class={[\"slu-icon\", className]}\n  style={computedStyle}\n/>\n\n<style>\n  :global(.slu-icon) {\n    height: 1rem;\n    width: max-content;\n    min-width: max-content;\n    display: inline-block;\n\n    > :global(svg) {\n      vertical-align: middle;\n    }\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/InlineSVG.svelte",
    "content": "<script lang=\"ts\">\n  import type { ClassValue } from \"svelte/elements\";\n  import { fetchSVG, svgs } from \"./InlineSVGState.svelte\";\n\n  interface Props {\n    src: string;\n    class?: ClassValue;\n    [key: string]: any;\n  }\n\n  let { src, class: className, ...rest }: Props = $props();\n\n  let svgContent = $derived(svgs[src]);\n\n  $effect(() => {\n    fetchSVG(src);\n  });\n</script>\n\n{#if svgContent}\n  <i {...rest} class={[\"inline-svg\", className]}>\n    {@html svgContent}\n  </i>\n{/if}\n\n<style>\n  .inline-svg :global(> svg) {\n    width: 100%;\n    height: 100%;\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/InlineSVGState.svelte.ts",
    "content": "export const svgs = $state<Record<string, string>>({});\n\nexport async function fetchSVG(src: string) {\n  try {\n    const response = await fetch(src);\n    if (!response.ok) {\n      throw new Error(`Failed to fetch SVG: ${response.statusText}`);\n    }\n    const svgText = await response.text();\n    svgs[src] = svgText;\n  } catch (e) {\n    console.error(e);\n  }\n}\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/MissingIcon.svelte",
    "content": "<script lang=\"ts\">\n  import type { ClassValue } from \"svelte/elements\";\n  import { iconPackManager, type IconState } from \"./common.svelte.ts\";\n  import { prefersDarkColorScheme } from \"../../runes/DarkMode.svelte.ts\";\n\n  interface Props {\n    class?: ClassValue;\n    [key: string]: any;\n  }\n\n  let { class: className, ...rest }: Props = $props();\n\n  let state: IconState = $derived.by(() => {\n    // Depend on _version to trigger reactivity when icon pack changes\n    iconPackManager._version;\n    const icon = iconPackManager.value.getMissingIcon();\n    if (icon) {\n      return {\n        src: (prefersDarkColorScheme.value ? icon.dark : icon.light) || icon.base,\n        mask: icon.mask,\n        isAproximatelySquare: icon.isAproximatelySquare,\n      };\n    }\n    return { src: null, mask: null, isAproximatelySquare: false };\n  });\n</script>\n\n<figure\n  {...rest}\n  class={[\"slu-icon-outer\", className]}\n  data-shape={state.isAproximatelySquare ? \"square\" : \"unknown\"}\n>\n  <img src={state.src || \"\"} alt=\"\" draggable=\"false\" />\n  {#if state.mask}\n    <div class=\"slu-icon-mask\" style=\"mask-image: url('{state.mask}')\"></div>\n  {/if}\n</figure>\n\n<style>\n  :global(.slu-icon-outer) {\n    position: relative;\n  }\n\n  :global(.slu-icon-outer img) {\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n  }\n\n  :global(.slu-icon-mask) {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    mask-repeat: no-repeat;\n    mask-size: contain;\n    mask-position: center;\n    mask-mode: luminance;\n    background-color: var(--system-accent-light-color);\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/SpecificIcon.svelte",
    "content": "<script lang=\"ts\">\n  import type { ClassValue } from \"svelte/elements\";\n  import { iconPackManager, type IconState } from \"./common.svelte.ts\";\n  import { prefersDarkColorScheme } from \"../../runes/DarkMode.svelte.ts\";\n  import MissingIcon from \"./MissingIcon.svelte\";\n\n  interface Props {\n    name: string;\n    class?: ClassValue;\n    lazy?: boolean;\n    [key: string]: any;\n  }\n\n  let { name, class: className, lazy, ...imgProps }: Props = $props();\n\n  let state: IconState = $derived.by(() => {\n    // Depend on _version to trigger reactivity when icon pack changes\n    iconPackManager._version;\n    const icon = iconPackManager.value.getCustomIcon(name);\n    if (icon) {\n      return {\n        src: (prefersDarkColorScheme.value ? icon.dark : icon.light) || icon.base,\n        mask: icon.mask,\n        isAproximatelySquare: icon.isAproximatelySquare,\n      };\n    }\n    return { src: null, mask: null, isAproximatelySquare: false };\n  });\n</script>\n\n{#if state.src}\n  <figure\n    {...imgProps}\n    class={[\"slu-icon-outer\", className]}\n    data-shape={state.isAproximatelySquare ? \"square\" : \"unknown\"}\n  >\n    <img src={state.src} alt=\"\" loading={lazy ? \"lazy\" : \"eager\"} draggable=\"false\" />\n    {#if state.mask}\n      <div class=\"slu-icon-mask\" style=\"mask-image: url('{state.mask}')\"></div>\n    {/if}\n  </figure>\n{:else}\n  <MissingIcon {...imgProps} class={className} />\n{/if}\n\n<style>\n  :global(.slu-icon-outer) {\n    position: relative;\n  }\n\n  :global(.slu-icon-outer img) {\n    height: 100%;\n    object-fit: contain;\n  }\n\n  :global(.slu-icon-mask) {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    mask-repeat: no-repeat;\n    mask-size: contain;\n    mask-position: center;\n    mask-mode: luminance;\n    background-color: var(--system-accent-light-color);\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/common.svelte.ts",
    "content": "import { IconPackManager } from \"@seelen-ui/lib\";\n\nconst manager = await IconPackManager.create();\n\nexport const iconPackManager = $state({\n  _version: 0,\n  value: manager,\n});\n\nmanager.onChange(() => {\n  // trick to make svelte re-render\n  iconPackManager._version++;\n});\n\nexport interface IconState {\n  src: string | null;\n  mask: string | null;\n  isAproximatelySquare: boolean;\n}\n"
  },
  {
    "path": "libs/ui/svelte/components/Icon/index.ts",
    "content": "export { default as Icon } from \"./Icon.svelte\";\nexport { default as InlineSVG } from \"./InlineSVG.svelte\";\nexport { default as MissingIcon } from \"./MissingIcon.svelte\";\nexport { default as SpecificIcon } from \"./SpecificIcon.svelte\";\nexport { default as FileIcon } from \"./FileIcon.svelte\";\nexport { iconPackManager } from \"./common.svelte\";\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/Wallpaper.svelte",
    "content": "<script lang=\"ts\">\n  import { WallpaperKind } from \"@seelen-ui/lib/types\";\n\n  import ThemedWallpaper from \"./components/ThemedWallpaper.svelte\";\n  import ImageWallpaper from \"./components/ImageWallpaper.svelte\";\n  import VideoWallpaper from \"./components/VideoWallpaper.svelte\";\n  import type { BaseProps } from \"./types\";\n  import { defaultWallpaperConfig } from \"./utils\";\n\n  let {\n    definition,\n    config = defaultWallpaperConfig,\n    onLoad,\n    out,\n    pausedMessage,\n    paused,\n    static: staticProp,\n    muted,\n  }: BaseProps = $props();\n\n  let loaded = $state(false);\n\n  function handleLoad() {\n    loaded = true;\n    onLoad?.();\n  }\n</script>\n\n<div class=\"wallpaper-container\" class:rendering={loaded} class:will-unrender={out}>\n  {#if definition?.type === WallpaperKind.Image}\n    <ImageWallpaper {definition} {config} onLoad={handleLoad} />\n  {:else if definition?.type === WallpaperKind.Video}\n    {#if staticProp && definition.thumbnailFilename}\n      <ImageWallpaper\n        definition={{ ...definition, filename: definition.thumbnailFilename }}\n        {config}\n        onLoad={handleLoad}\n      />\n    {:else}\n      <VideoWallpaper {definition} {config} {muted} {paused} onLoad={handleLoad} />\n    {/if}\n  {:else if definition?.type === WallpaperKind.Layered}\n    <ThemedWallpaper {definition} {config} onLoad={handleLoad} />\n  {:else}\n    <ThemedWallpaper onLoad={handleLoad} />\n  {/if}\n\n  {#if config.withOverlay && loaded}\n    <div\n      class=\"wallpaper-overlay\"\n      style={`mix-blend-mode: ${config.overlayMixBlendMode}; background-color: ${config.overlayColor};`}\n    ></div>\n  {/if}\n\n  {#if pausedMessage && paused && loaded && definition?.type === \"Video\"}\n    <div class=\"paused-message\">{pausedMessage}</div>\n  {/if}\n</div>\n\n<style>\n  .wallpaper-container {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n  }\n\n  .wallpaper-overlay {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n  }\n\n  .paused-message {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n\n    background-color: rgba(0, 0, 0, 0.5);\n    color: white;\n    padding: 12px 20px;\n    font-size: 14px;\n    font-weight: 500;\n    border-radius: 10px;\n    backdrop-filter: blur(10px);\n    z-index: 1000;\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/components/ImageWallpaper.svelte",
    "content": "<script lang=\"ts\">\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import type { DefinedWallProps } from \"../types\";\n  import { getWallpaperStyles } from \"../utils\";\n\n  let { definition, config, onLoad }: DefinedWallProps = $props();\n\n  const imageSrc = $derived(convertFileSrc(definition.metadata.path + \"\\\\\" + definition.filename!));\n\n  function handleError(e: Event) {\n    const target = e.target as HTMLImageElement;\n    console.error(\"Image failed to load:\", {\n      src: imageSrc,\n      naturalWidth: target.naturalWidth,\n      naturalHeight: target.naturalHeight,\n    });\n  }\n</script>\n\n<img\n  id={definition.id}\n  class=\"wallpaper\"\n  style={getWallpaperStyles(config)}\n  src={imageSrc}\n  onload={onLoad}\n  onerror={handleError}\n  decoding=\"async\"\n  loading=\"eager\"\n  alt=\"\"\n/>\n\n<style>\n  :global(.wallpaper) {\n    width: 100%;\n    height: 100%;\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/components/ThemedWallpaper.svelte",
    "content": "<script lang=\"ts\">\n  import { onMount } from \"svelte\";\n  import BackgroundByLayers from \"../../BackgroundByLayers/BackgroundByLayers.svelte\";\n  import type { BaseProps } from \"../types\";\n  import { getWallpaperStyles } from \"../utils\";\n\n  let { definition, config, onLoad }: Pick<BaseProps, \"definition\" | \"config\" | \"onLoad\"> =\n    $props();\n\n  onMount(() => {\n    onLoad?.();\n  });\n</script>\n\n{#if !definition || !config}\n  <BackgroundByLayers class={[\"wallpaper\", \"default-wallpaper\"]} />\n{:else}\n  <BackgroundByLayers id={definition.id} class=\"wallpaper\" style={getWallpaperStyles(config)}>\n    {@html `<style>@scope { ${definition.css || \"\"} }</style>`}\n  </BackgroundByLayers>\n{/if}\n\n<style>\n  :global(.wallpaper) {\n    width: 100%;\n    height: 100%;\n  }\n\n  :global(.default-wallpaper) {\n    > :global(.bg-layers) {\n      > :global(.bg-layer-1) {\n        background-color: #cdcdcd;\n      }\n\n      > :global(.bg-layer-2) {\n        background-image: url(\"data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8' standalone='no'%3F%3E%3C!-- Created with Inkscape (http://www.inkscape.org/) --%3E%3Csvg width='1920' height='1080' viewBox='0 0 507.99999 285.75' version='1.1' id='svg1' xml:space='preserve' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs1'%3E%3CclipPath clipPathUnits='userSpaceOnUse' id='clipPath11'%3E%3Crect style='opacity:1;fill:%23a8a8a8;fill-opacity:1;fill-rule:nonzero;stroke:%23ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1' id='rect12' width='515.69818' height='280.8439' x='3.8769755' y='2.7772453' /%3E%3C/clipPath%3E%3C/defs%3E%3Cg id='g8' transform='matrix(0.98507229,0,0,1.0174691,-3.8191012,-2.8257612)' style='display:inline;stroke:%23ffffff;stroke-width:0.792846;stroke-dasharray:none;stroke-opacity:1' clip-path='url(%23clipPath11)'%3E%3Cpath style='display:inline;fill:%23a8a8a8;fill-opacity:1;stroke:%23a8a8a8;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='m 235.24329,283.26004 c 9.14773,-16.60847 29.47199,-18.6562 45.50475,-24.51787 15.13284,-7.67616 15.82635,-27.76418 10.62079,-41.87543 -4.88698,-18.23738 15.43954,-29.81469 31.29976,-27.81827 15.1631,-0.0577 32.08431,-6.92295 36.72071,-22.61916 5.76713,-17.14528 -4.22578,-36.14451 -19.92343,-43.97664 -17.88776,-10.82259 -43.16674,-12.73618 -53.83086,-33.006243 -7.61067,-18.281836 12.06641,-38.128182 30.54205,-34.606026 19.59562,1.768802 35.11137,16.959616 44.14823,32.978487 9.31394,13.959382 34.02585,11.183709 33.89003,-7.195746 C 395.43894,59.43346 376.19949,45.073344 359.96274,35.269619 343.39788,24.197088 322.71052,30.329097 304.54507,26.906237 287.28235,23.577717 286.83702,2.7509165 287.79259,3.0571695 c 0,0 4.08887,-3.33729875 15.132,-0.2105399 C 344.2353,3.2532069 385.56225,2.2982364 426.87582,3.2090872 409.359,2.7807894 395.2047,28.885493 413.922,38.252727 c 19.95936,11.871045 43.47282,-1.132569 64.74428,4.937209 16.92744,3.344004 33.62632,19.027115 29.46943,37.5369 -4.43539,22.253284 -27.42545,34.114794 -48.08181,37.320364 -19.8836,4.03073 -44.74641,7.95199 -53.94192,28.8025 -8.29021,20.03664 1.60939,46.24717 -16.17056,62.40173 -15.68147,12.55201 -40.55674,-0.65554 -54.00321,16.20736 -9.50975,18.08682 5.25377,41.03467 -10.84601,58.19132 -29.94187,1.22369 -59.90321,-0.12226 -89.84891,-0.39007 z M 435.98934,194.5716 c -24.39235,-2.91259 -11.24601,-56.38424 9.695,-53.68902 20.94101,2.69522 12.23611,55.16349 -9.695,53.68902 z' id='path1' /%3E%3Cpath style='display:inline;fill:%23767676;fill-opacity:1;stroke:%23767676;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='m 220.526,283.78395 c -6.8569,0.0344 -16.12431,4.26963 -9.83769,-5.45355 6.01236,-22.46822 -11.72804,-41.28385 -14.19222,-62.70661 -2.20799,-17.51156 10.67907,-34.5273 27.93468,-37.70333 15.12637,-3.71814 35.44817,-4.36971 42.36252,-21.07131 5.98141,-14.79097 -6.38829,-30.42361 -21.05875,-32.95421 -18.25394,-4.44733 -41.22583,0.48783 -54.44299,-16.06701 -12.01141,-13.896537 -9.13628,-41.120972 10.08713,-46.902169 15.20383,-4.671379 24.82305,13.193548 17.07332,25.43317 -4.39134,14.098039 15.8395,22.515149 24.76851,12.230247 12.09343,-11.7421 5.29369,-31.36086 -6.43182,-40.671818 -15.2829,-13.991449 -37.06971,-15.052004 -54.87164,-24.00367 -12.60602,-5.496963 -8.01085,-25.421139 6.04803,-23.685377 14.28501,0.499898 28.5035,11.12347 41.87969,6.663634 6.22089,-2.074143 9.97415,-7.3142981 13.37601,-14.0528071 13.01704,0.2380293 31.32771,-1.1700027 44.12817,0.035931 0.1844,13.8535421 11.8366,25.6408451 25.81289,24.6939011 14.84258,-0.529991 30.49165,-1.514868 43.90281,6.22964 15.63968,8.744936 32.30915,20.767334 36.96111,39.030914 3.4133,12.75372 -7.49849,28.585164 -21.54108,24.856012 -16.82893,-4.987683 -20.89855,-24.977872 -34.70466,-34.192643 -14.57475,-12.444704 -40.88629,-12.300978 -50.86685,5.870609 -7.89544,13.328353 1.20543,29.6333 14.03513,35.714816 19.58903,10.37953 45.37673,14.49363 56.67798,35.86783 10.28148,18.46141 -2.6001,43.7219 -23.35265,46.75906 -13.89957,2.53517 -32.4029,-0.65598 -40.9803,13.46342 -8.0848,16.21318 9.15222,35.82172 -4.13439,50.58663 -14.55471,15.82873 -41.11163,9.37691 -51.9,30.09998 -1.01901,6.16065 -5.41389,1.62905 -9.91054,2.02069 -1.00797,-10e-6 -5.81443,-0.092 -6.8224,-0.092 z' id='path2' /%3E%3Cpath style='display:inline;fill:%23606060;fill-opacity:1;stroke:%23606060;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='M 64.816729,283.69898 C 70.174633,266.20245 82.788469,244.11983 97.265442,233.46347 118.57119,218.47583 129.8972,191.69774 126.20774,165.97295 122.14534,130.25949 97.000125,102.44357 73.55139,77.239743 58.101854,61.38378 49.024298,40.967288 42.541056,20.099397 39.896088,11.153859 25.219187,-1.8294131 42.253833,2.48426 107.64461,2.4176594 176.92942,2.7480117 242.31962,3.0025621 238.07564,18.107846 218.85958,22.827564 206.10347,15.508986 196.16741,8.6578195 172.98303,6.4687984 174.4211,23.9694 c 5.19599,15.369872 24.37666,14.830575 37.04566,20.345525 18.5663,5.690027 39.86457,20.01469 38.00139,41.881275 -0.31444,14.31095 -20.982,23.74985 -29.5924,10.611327 -5.6452,-11.308047 9.53651,-29.655572 -8.30784,-35.435552 -15.97603,-5.450735 -29.30836,12.569697 -27.40275,27.305618 0.0582,19.180507 18.57969,32.423037 36.61579,32.248527 16.61838,1.14908 39.10923,1.58886 46.08292,20.09828 5.88686,14.55786 -6.51525,29.53835 -20.81544,32.10012 -16.72807,4.03778 -38.19825,5.31154 -46.1908,23.45432 -9.77588,19.4092 3.44862,39.08711 9.61307,57.23565 2.3249,8.94734 5.51814,27.94972 -3.69953,32.17581 -52.32988,-0.12245 -89.39771,3.75399 -140.954441,-2.29132 z M 326.29911,173.17891 c -16.5659,0.003 -35.28972,-14.49183 -32.40221,-32.33347 5.60615,-16.20536 25.94415,-10.74337 37.3002,-4.4724 12.69282,3.28997 24.33049,19.71275 12.57697,30.57021 -4.53303,4.56547 -11.18834,6.33772 -17.47496,6.23566 z' id='path3' /%3E%3Cpath style='display:inline;fill:%23494949;fill-opacity:1;stroke:%23494949;stroke-width:0.78817;stroke-dasharray:none;stroke-opacity:1' d='M 1.5114306,285.99047 V 2.5680694 C 12.693011,2.5751194 23.874592,2.5821724 35.056172,2.5892234 45.93452,24.351979 49.846372,49.738088 66.272319,68.539964 85.812786,92.153 109.71132,113.41056 120.46523,142.95857 c 11.34838,27.64598 7.94791,63.23983 -15.40914,83.74255 -14.217985,12.58371 -34.606136,35.72578 -40.534146,58.49271 -1.756132,6.74455 -40.307514,0.79664 -63.0105134,0.79664 z m 144.8057994,0 h -29.1157 c 8.4792,-8.38038 17.72345,-17.43841 18.2864,-30.17462 1.44459,-13.08049 3.29092,-31.54658 19.02457,-34.79682 14.93984,-2.61489 26.35681,12.56149 25.71278,26.40697 -0.0591,12.96874 -1.88116,25.93496 -4.79236,38.56447 z M 118.99551,87.863335 C 104.39719,87.044541 99.954574,70.921436 93.735846,60.353103 88.421271,48.716426 71.519147,46.794743 68.939073,33.518689 68.252837,18.360223 86.660657,23.098465 95.315096,27.174919 c 17.661674,4.983484 35.267834,-2.366157 52.638774,-4.527623 16.86818,-2.238794 18.81645,17.389648 3.64021,19.405613 -14.6292,0.214286 -16.16343,16.796753 -16.3822,27.882136 -0.84803,8.463424 -6.71916,18.096033 -16.21637,17.92829 z' id='path8' /%3E%3C/g%3E%3C/svg%3E%0A\");\n        background-size: cover;\n        background-repeat: no-repeat;\n        height: 100%;\n        width: auto;\n        aspect-ratio: 16 / 9;\n      }\n\n      > :global(.bg-layer-3) {\n        background: linear-gradient(\n          to right,\n          var(--system-accent-darker-color),\n          var(--system-accent-color),\n          var(--system-accent-lighter-color)\n        );\n        mix-blend-mode: multiply;\n      }\n    }\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/components/VideoWallpaper.svelte",
    "content": "<script lang=\"ts\">\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import { onMount, onDestroy } from \"svelte\";\n  import type { DefinedWallProps } from \"../types\";\n  import { getPlaybackRate, getWallpaperStyles } from \"../utils\";\n\n  let { definition, config, muted, paused, onLoad }: DefinedWallProps = $props();\n\n  const MAX_RETRIES = 3;\n  const WAITING_TIMEOUT_MS = 3000;\n  const STALL_CHECK_INTERVAL_MS = 5000;\n\n  let videoElement: HTMLVideoElement | undefined = $state();\n  let waitingTimeout: ReturnType<typeof setTimeout> | undefined;\n  let retryCount = 0;\n  let lastTimeUpdate = 0;\n  let isLoaded = false;\n  let stallCheckInterval: ReturnType<typeof setInterval> | undefined;\n\n  const videoSrc = $derived(convertFileSrc(definition.metadata.path + \"\\\\\" + definition.filename!));\n\n  // Monitor for stalls by checking timeupdate\n  function startStallCheck() {\n    if (stallCheckInterval) {\n      clearInterval(stallCheckInterval);\n    }\n\n    stallCheckInterval = setInterval(() => {\n      if (videoElement && !paused && isLoaded) {\n        const currentTime = videoElement.currentTime;\n\n        // If time hasn't changed and video should be playing, it's stalled\n        if (\n          currentTime === lastTimeUpdate &&\n          videoElement.readyState < HTMLMediaElement.HAVE_FUTURE_DATA &&\n          retryCount < MAX_RETRIES\n        ) {\n          console.debug(\"Video appears stalled, attempting recovery\");\n          retryCount++;\n          videoElement.load();\n          videoElement.currentTime = currentTime;\n          videoElement.play().catch((err) => {\n            console.error(\"Failed to resume video after stall:\", err);\n          });\n        }\n\n        lastTimeUpdate = currentTime;\n      }\n    }, STALL_CHECK_INTERVAL_MS);\n  }\n\n  // Handle pause/play state changes\n  $effect(() => {\n    if (videoElement && paused !== undefined) {\n      if (paused) {\n        videoElement.pause();\n      } else if (videoElement.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {\n        videoElement.play().catch((err) => {\n          console.error(\"Failed to play video:\", err);\n        });\n      }\n    }\n  });\n\n  function handleWaiting() {\n    // Clear any existing timeout\n    if (waitingTimeout) {\n      clearTimeout(waitingTimeout);\n    }\n\n    // Set a timeout to detect if truly stuck\n    waitingTimeout = setTimeout(() => {\n      if (videoElement && retryCount < MAX_RETRIES) {\n        console.debug(`Video stuck in waiting state, retry ${retryCount + 1}/${MAX_RETRIES}`);\n        retryCount++;\n\n        const currentTime = videoElement.currentTime;\n\n        // Full reload to recover from stuck state\n        videoElement.load();\n\n        // Restore position if it wasn't at the beginning\n        if (currentTime > 0) {\n          videoElement.currentTime = currentTime;\n        }\n\n        if (!paused) {\n          videoElement.play().catch((err) => {\n            console.error(\"Failed to resume video after waiting timeout:\", err);\n          });\n        }\n      }\n    }, WAITING_TIMEOUT_MS);\n  }\n\n  function handlePlaying() {\n    // Clear timeout when playing successfully\n    if (waitingTimeout) {\n      clearTimeout(waitingTimeout);\n      waitingTimeout = undefined;\n    }\n    // Reset retry count on successful playback\n    retryCount = 0;\n  }\n\n  function handleStalled() {\n    // Stalled = browser thinks it can play but isn't fetching data\n    if (videoElement && retryCount < MAX_RETRIES) {\n      console.debug(\"Video network stalled, forcing reload\");\n      retryCount++;\n\n      const currentTime = videoElement.currentTime;\n      videoElement.load();\n      videoElement.currentTime = currentTime;\n\n      if (!paused) {\n        videoElement.play().catch((err) => {\n          console.error(\"Failed to resume video after stall:\", err);\n        });\n      }\n    }\n  }\n\n  function handleCanPlay() {\n    if (videoElement && !paused) {\n      videoElement.play().catch((err) => {\n        console.error(\"Failed to play video on canplay:\", err);\n      });\n    }\n  }\n\n  function handleLoadedMetadata() {\n    isLoaded = true;\n    onLoad?.();\n  }\n\n  function handleError(e: Event) {\n    const target = e.target as HTMLVideoElement;\n    const error = target.error;\n\n    if (error) {\n      console.error(\"Video error:\", {\n        code: error.code,\n        message: error.message,\n        src: videoSrc,\n      });\n\n      // Attempt recovery on certain errors\n      if (error.code === MediaError.MEDIA_ERR_NETWORK && retryCount < MAX_RETRIES) {\n        console.debug(\"Network error, attempting recovery\");\n        retryCount++;\n        setTimeout(() => {\n          if (videoElement) {\n            videoElement.load();\n            if (!paused) {\n              videoElement.play().catch((err) => {\n                console.error(\"Failed to recover from network error:\", err);\n              });\n            }\n          }\n        }, 1000);\n      }\n    }\n  }\n\n  function handleTimeUpdate() {\n    if (videoElement) {\n      lastTimeUpdate = videoElement.currentTime;\n    }\n  }\n\n  onMount(() => {\n    startStallCheck();\n  });\n\n  onDestroy(() => {\n    // Cleanup on unmount to prevent memory leaks\n    // https://github.com/facebook/react/issues/15583\n    // this is a workaround for a bug in js that causes memory leak on video elements\n    if (waitingTimeout) {\n      clearTimeout(waitingTimeout);\n    }\n    if (stallCheckInterval) {\n      clearInterval(stallCheckInterval);\n    }\n    if (videoElement) {\n      videoElement.pause();\n      videoElement.removeAttribute(\"src\");\n      videoElement.load();\n      if (globalThis.gc) {\n        setTimeout(() => globalThis.gc?.(), 100);\n      }\n    }\n  });\n\n  $effect(() => {\n    if (videoElement) {\n      videoElement.playbackRate = getPlaybackRate(config.playbackSpeed)\n    }\n  })\n</script>\n\n<video\n  bind:this={videoElement}\n  id={definition.id}\n  class=\"wallpaper\"\n  style={getWallpaperStyles(config)}\n  src={videoSrc}\n  controls={false}\n  muted={muted || config.muted}\n  autoplay={!paused}\n  loop\n  playsinline\n  disableRemotePlayback\n  preload=\"auto\"\n  onloadedmetadata={handleLoadedMetadata}\n  onwaiting={handleWaiting}\n  onplaying={handlePlaying}\n  onstalled={handleStalled}\n  oncanplay={handleCanPlay}\n  onerror={handleError}\n  ontimeupdate={handleTimeUpdate}\n></video>\n\n<style>\n  :global(.wallpaper) {\n    width: 100%;\n    height: 100%;\n  }\n</style>\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/index.ts",
    "content": "export { default as Wallpaper } from \"./Wallpaper.svelte\";\nexport type { BaseProps } from \"./types\";\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/types.ts",
    "content": "import type { Wallpaper, WallpaperInstanceSettings } from \"@seelen-ui/lib/types\";\n\nexport interface BaseProps {\n  definition?: Wallpaper;\n  config?: WallpaperInstanceSettings;\n  onLoad?: () => void;\n  out?: boolean;\n\n  muted?: boolean;\n  paused?: boolean;\n  pausedMessage?: string;\n  /** Use the thumbnail of the wallpaper instead of the video */\n  static?: boolean;\n}\n\nexport interface DefinedWallProps extends BaseProps {\n  definition: Wallpaper;\n  config: WallpaperInstanceSettings;\n}\n"
  },
  {
    "path": "libs/ui/svelte/components/Wallpaper/utils.ts",
    "content": "import type { PlaybackSpeed, WallpaperInstanceSettings } from \"@seelen-ui/lib/types\";\nimport { WallpaperConfiguration } from \"@seelen-ui/lib\";\n\nexport const defaultWallpaperConfig = await WallpaperConfiguration.default();\n\nexport function getPlaybackRate(speed: PlaybackSpeed): number {\n  switch (speed) {\n    case \"xDot25\":\n      return 0.25;\n    case \"xDot5\":\n      return 0.5;\n    case \"xDot75\":\n      return 0.75;\n    case \"x1\":\n      return 1;\n    case \"x1Dot25\":\n      return 1.25;\n    case \"x1Dot5\":\n      return 1.5;\n    case \"x1Dot75\":\n      return 1.75;\n    case \"x2\":\n      return 2;\n  }\n\n  return 1;\n}\n\nexport function getWallpaperStyles(config: WallpaperInstanceSettings): string {\n  const transforms: string[] = [];\n  const filters: string[] = [];\n  const styles: string[] = [];\n\n  const { flipHorizontal, flipVertical, blur, saturation, contrast, objectFit, objectPosition } = config;\n\n  styles.push(`object-fit: ${objectFit}`);\n  styles.push(`object-position: ${objectPosition}`);\n\n  if (flipHorizontal) {\n    transforms.push(\"scaleX(-1)\");\n  }\n\n  if (flipVertical) {\n    transforms.push(\"scaleY(-1)\");\n  }\n\n  if (blur > 0) {\n    filters.push(`blur(${blur}px)`);\n  }\n\n  if (saturation !== 1) {\n    filters.push(`saturate(${saturation})`); // 0 is allowed\n  }\n\n  if (contrast !== 1) {\n    filters.push(`contrast(${contrast})`); // 0 is allowed\n  }\n\n  if (transforms.length > 0) {\n    styles.push(`transform: ${transforms.join(\" \")}`);\n  }\n\n  if (filters.length > 0) {\n    styles.push(`filter: ${filters.join(\" \")}`);\n  }\n\n  return styles.join(\"; \");\n}\n"
  },
  {
    "path": "libs/ui/svelte/runes/DarkMode.svelte.ts",
    "content": "const darkModeQuery = globalThis.matchMedia(\"(prefers-color-scheme: dark)\");\nexport const prefersDarkColorScheme = $state({ value: darkModeQuery.matches });\n\ndarkModeQuery.addEventListener(\"change\", (e) => {\n  prefersDarkColorScheme.value = e.matches;\n});\n"
  },
  {
    "path": "libs/ui/svelte/utils/LazyRune.svelte.ts",
    "content": "/**\n * Structure done to work with async event programming\n *\n * The LazyRune double-check pattern ensures that if an event updates\n * the value during initialization, that event value won't be overwritten by\n * the stale initial fetch.\n *\n * How to use:\n *\n * 1. Create lazy rune with async initializer\n * 2. Set up event listeners that can fire anytime\n * 3. Initialize - won't overwrite if event already fired during fetch\n *\n * @example\n * ```typescript\n * // 1. Create lazy rune with async initializer\n * const colors = lazyRune(async () => (await UIColors.getAsync()).inner);\n *\n * // 2. Set up event listeners that can fire anytime\n * await UIColors.onChange(colors.setByPayload);\n *\n * // 3. Initialize - won't overwrite if event already fired\n * await colors.init();\n *\n * // 4. Use the value (will be reactive in Svelte components)\n * $effect(() => {\n *   console.log(colors.value);\n * });\n * ```\n */\nexport class LazyRune<T> {\n  private _value = $state<T>();\n  private initialized = false;\n\n  constructor(private initializer: () => Promise<T> | T) {\n    this.setByPayload = this.setByPayload.bind(this);\n  }\n\n  /**\n   * Gets the current value. Throws error if not initialized yet.\n   * This property is reactive and will trigger updates in Svelte components.\n   */\n  get value(): T {\n    if (!this.initialized) {\n      throw new Error(\"LazyRune was not initialized\");\n    }\n    return this._value as T;\n  }\n\n  /**\n   * Sets the value and marks as initialized.\n   * This will trigger reactivity in Svelte components.\n   */\n  set value(value: T) {\n    this.initialized = true;\n    this._value = value;\n  }\n\n  /**\n   * Will call the initializer and set the value if not already set\n   * via another setters.\n   *\n   * This uses a double-check pattern to prevent race conditions:\n   * - Check if initialized\n   * - Call initializer\n   * - Check again after await (event may have set value during fetch)\n   * - Only set if still not initialized\n   */\n  public async init(): Promise<void> {\n    if (!this.initialized) {\n      const awaited = await this.initializer();\n      // double check - event may have fired during async initialization\n      if (!this.initialized) {\n        this.value = awaited;\n      }\n    }\n  }\n\n  /**\n   * Utility function to be used as event handler.\n   * Useful for Tauri event listeners.\n   *\n   * @example\n   * ```typescript\n   * await UIColors.onChange(colors.setByPayload);\n   * ```\n   */\n  public setByPayload({ payload }: { payload: T }): void {\n    this.value = payload;\n  }\n\n  /**\n   * Checks if the rune has been initialized.\n   * Useful for conditional rendering or logic.\n   */\n  public isInitialized(): boolean {\n    return this.initialized;\n  }\n}\n\n/**\n * Factory function to create a new LazyRune instance.\n *\n * @param initial - Function that returns the initial value (can be async)\n * @returns A new LazyRune instance\n *\n * @example\n * ```typescript\n * const systemColors = lazyRune(async () => {\n *   const colors = await UIColors.getAsync();\n *   return colors.inner;\n * });\n *\n * // Set up event listeners\n * await UIColors.onChange(systemColors.setByPayload);\n *\n * // Initialize\n * await systemColors.init();\n * ```\n */\nexport function lazyRune<T>(initial: () => Promise<T> | T): LazyRune<T> {\n  return new LazyRune(initial);\n}\n"
  },
  {
    "path": "libs/ui/svelte/utils/PersistentRune.svelte.ts",
    "content": "import type { ZodSchema } from \"zod\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { debounce } from \"lodash\";\n\nexport class PersistentRune<T> {\n  private _value: T;\n  private storeKey: string;\n  private schema?: ZodSchema<T>;\n  private debouncedSave: ReturnType<typeof debounce>;\n\n  private constructor(storeKey: string, initial: T, schema?: ZodSchema<T>) {\n    this.storeKey = storeKey;\n    this.schema = schema;\n    this._value = $state(initial);\n    this.debouncedSave = debounce(this.saveToFile.bind(this), 500);\n  }\n\n  static async create<T>(\n    storeKey: string,\n    initial: T,\n    schema?: ZodSchema<T>,\n  ): Promise<PersistentRune<T>> {\n    const rune = new PersistentRune(storeKey, initial, schema);\n    const stored = await rune.loadFromFile();\n    rune._value = stored ?? initial;\n    return rune;\n  }\n\n  private async loadFromFile(): Promise<T | null> {\n    try {\n      const content = await invoke(SeelenCommand.ReadFile, {\n        filename: `${this.storeKey}.json`,\n      });\n\n      const parsed = JSON.parse(content);\n      if (this.schema) {\n        return this.schema.parse(parsed);\n      }\n\n      return parsed as T;\n    } catch {\n      // File doesn't exist or is invalid, ignore\n      return null;\n    }\n  }\n\n  private async saveToFile(value: T): Promise<void> {\n    try {\n      await invoke(SeelenCommand.WriteFile, {\n        filename: `${this.storeKey}.json`,\n        content: JSON.stringify(value),\n      });\n    } catch (error) {\n      console.error(`Failed to save to file (key: ${this.storeKey}):`, error);\n    }\n  }\n\n  get value(): T {\n    return this._value;\n  }\n\n  set value(value: T) {\n    this._value = value;\n    this.debouncedSave(value);\n  }\n}\n\n/**\n * Helper function to create a PersistentRune\n *\n * @example\n * const count = await persistentRune('counter', 0);\n * count.value++; // Automatically saved to file\n *\n * @example with zod validation\n * import { z } from 'zod';\n * const schema = z.number();\n * const count = await persistentRune('counter', 0, schema);\n * count.value++; // Validated and saved to file\n */\nexport function persistentRune<T>(\n  storeKey: string,\n  initial: T,\n  schema?: ZodSchema<T>,\n): Promise<PersistentRune<T>> {\n  return PersistentRune.create(storeKey, initial, schema);\n}\n"
  },
  {
    "path": "libs/ui/svelte/utils/hooks.svelte.ts",
    "content": "interface Transition {\n  readonly loading: boolean;\n  readonly error: unknown;\n  start: (cb: () => Promise<void>) => Promise<void>;\n  clearError: () => void;\n}\n\nexport function useTransition(): Transition {\n  let loading = $state(false);\n  let error = $state();\n\n  async function startTransition(cb: () => Promise<void>): Promise<void> {\n    loading = true;\n    error = undefined;\n\n    try {\n      await cb();\n    } catch (e: unknown) {\n      console.error(e);\n      error = e;\n    } finally {\n      loading = false;\n    }\n  }\n\n  function clearError() {\n    error = null;\n  }\n\n  return {\n    get loading() {\n      return loading;\n    },\n    get error() {\n      return error;\n    },\n    start: startTransition,\n    clearError,\n  };\n}\n"
  },
  {
    "path": "libs/ui/svelte/utils/i18n.ts",
    "content": "import { derived, get, writable } from \"svelte/store\";\n\nexport const locale = writable(\"en\");\nconst messages = writable<Record<string, any>>({});\n\nfunction translate(locale: string, key: string, vars: Record<string, string> = {}) {\n  // Let's throw some errors if we're trying to use keys/locales that don't exist.\n  // We could improve this by using Typescript and/or fallback values.\n  if (!key) throw new Error(\"no key provided to $t()\");\n  if (!locale) throw new Error(`no translation for key \"${key}\"`);\n\n  // Grab the translation from the translations object.\n  // Support nested keys like \"profile.log_out\"\n  const keys = key.split(\".\");\n  let text = get(messages)[locale];\n  for (const k of keys) {\n    text = text?.[k];\n  }\n\n  if (!text) {\n    console.error(`no translation found for ${locale}.${key}`);\n    // Try fallback to English\n    let fallback = get(messages)[\"en\"];\n    for (const k of keys) {\n      fallback = fallback?.[k];\n    }\n    text = fallback || key;\n  }\n\n  if (typeof text !== \"string\") {\n    console.error(`translation for ${locale}.${key} is not a string`);\n    text = key;\n  }\n\n  // Replace any passed in variables in the translation string.\n  Object.entries(vars).map(([k, v]) => {\n    text = text.replaceAll(`{{${k}}}`, v);\n  });\n\n  return text;\n}\n\nexport const t = derived(\n  locale,\n  ($locale) => (key: string, vars?: Record<string, string>) => translate($locale, key, vars),\n);\n\nexport function setMessages(newMessages: Record<string, any>) {\n  messages.set(newMessages);\n}\n"
  },
  {
    "path": "libs/ui/svelte/utils/index.ts",
    "content": "export * from \"./LazyRune.svelte\";\nexport * from \"./PersistentRune.svelte\";\nexport * from \"./i18n\";\n\n// Difference between Windows epoch (1601) and Unix epoch (1970) in milliseconds\nconst EPOCH_DIFF_MILLISECONDS = 11644473600000n;\n\n/** Convert Windows FileTime to Js Unix Date */\nexport function WindowsDateFileTimeToDate(fileTime: bigint | number): Date {\n  if (typeof fileTime === \"number\") fileTime = BigInt(fileTime);\n  return new Date(Number(fileTime / 10000n - EPOCH_DIFF_MILLISECONDS));\n}\n\n/** Get relative time string from a date (e.g., \"2 hours ago\", \"3 days ago\") */\nexport function relativeTimeFromNow(date: Date): string {\n  const now = new Date();\n  const diffMs = now.getTime() - date.getTime();\n  const diffSeconds = Math.floor(diffMs / 1000);\n  const diffMinutes = Math.floor(diffSeconds / 60);\n  const diffHours = Math.floor(diffMinutes / 60);\n  const diffDays = Math.floor(diffHours / 24);\n  const diffWeeks = Math.floor(diffDays / 7);\n  const diffMonths = Math.floor(diffDays / 30);\n  const diffYears = Math.floor(diffDays / 365);\n\n  const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: \"auto\" });\n\n  if (diffYears > 0) {\n    return rtf.format(-diffYears, \"year\");\n  } else if (diffMonths > 0) {\n    return rtf.format(-diffMonths, \"month\");\n  } else if (diffWeeks > 0) {\n    return rtf.format(-diffWeeks, \"week\");\n  } else if (diffDays > 0) {\n    return rtf.format(-diffDays, \"day\");\n  } else if (diffHours > 0) {\n    return rtf.format(-diffHours, \"hour\");\n  } else if (diffMinutes > 0) {\n    return rtf.format(-diffMinutes, \"minute\");\n  } else {\n    return rtf.format(-diffSeconds, \"second\");\n  }\n}\n"
  },
  {
    "path": "libs/ui/utils.ts",
    "content": "export function nanosecondsToPlayingTime(nanoseconds: number): string {\n  const hours = Math.floor(nanoseconds / 3_600_000_000_000);\n  const minutes = Math.floor(nanoseconds / 60_000_000_000);\n  const seconds = Math.floor((nanoseconds - minutes * 60_000_000_000) / 1_000_000_000);\n\n  if (hours > 0) {\n    return `${hours}:${minutes}:${seconds}`;\n  } else {\n    return `${minutes}:${seconds}`;\n  }\n}\n\nexport function brightnessIcon(brightness: number) {\n  if (brightness >= 60) {\n    return \"TbBrightnessUp\";\n  }\n  return brightness >= 30 ? \"TbBrightnessDown\" : \"TbBrightnessDownFilled\";\n}\n\nexport function outputVolumeIcon(muted: boolean, volume: number) {\n  if (muted) {\n    return \"IoVolumeMuteOutline\";\n  }\n\n  if (volume >= 0.66) {\n    return \"IoVolumeHighOutline\";\n  }\n\n  if (volume >= 0.33) {\n    return \"IoVolumeMediumOutline\";\n  }\n\n  return volume === 0 ? \"IoVolumeOffOutline\" : \"IoVolumeLowOutline\";\n}\n\nexport function crashPageByMemory() {\n  console.debug(\"Attempting to crash the page by allocating excessive memory...\");\n  let arr = [];\n  // Create an array with the largest possible number of elements repeatedly\n  // use interval to avoid blocking the main thread\n  setInterval(() => {\n    arr.push(new Array(1_000_000).fill(0)); // Allocates memory in chunks\n  }, 1);\n}\n\nexport function freezePageByLoop() {\n  console.debug(\"Attempting to freeze the page with an infinite loop...\");\n  while (true) {\n    // An empty loop is sufficient to block the main thread\n  }\n}\n"
  },
  {
    "path": "libs/utils/Cargo.toml",
    "content": "[package]\nname = \"slu-utils\"\nversion = \"0.0.1\"\nedition = \"2021\"\nrust-version = \"1.89\"\n\n[dependencies]\nminisign = { workspace = true }\nsha2 = { workspace = true }\nbase64 = { workspace = true }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "libs/utils/src/checksums.rs",
    "content": "use std::{\n    collections::HashMap,\n    path::{Path, PathBuf},\n};\n\nuse sha2::{Digest, Sha256};\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Diff {\n    Missing,\n    Mismatch,\n    Extra,\n}\n\n/// Calculates SHA-256 hash of data\npub fn calculate_sha256(data: &[u8]) -> String {\n    format!(\"{:x}\", Sha256::digest(data))\n}\n\n/// SHA256SUMS file representation\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\npub struct CheckSums(HashMap<PathBuf, String>);\n\nimpl CheckSums {\n    pub fn new() -> Self {\n        Self(HashMap::new())\n    }\n\n    /// Add file content with hash calculation, normalizing path\n    ///\n    /// This method calculates the SHA-256 hash of the provided content\n    /// and stores it with the normalized path (forward slashes).\n    pub fn raw_add<P: AsRef<Path>>(&mut self, content: &[u8], path: P) {\n        let hash = calculate_sha256(content);\n        let normalized = PathBuf::from(path.as_ref().to_string_lossy().replace(\"\\\\\", \"/\"));\n        self.0.insert(normalized, hash);\n    }\n\n    /// Add file by reading, hashing, and normalizing path\n    pub fn add<P: AsRef<Path>>(&mut self, path: P) -> Result<(), String> {\n        let path = path.as_ref();\n        let content =\n            std::fs::read(path).map_err(|e| format!(\"Failed to read {}: {}\", path.display(), e))?;\n\n        self.raw_add(&content, path);\n        Ok(())\n    }\n\n    /// Parse SHA256SUMS content: `<hash>  <filepath>`\n    pub fn parse(content: &[u8]) -> Result<Self, String> {\n        let content = std::str::from_utf8(content).map_err(|_| \"SHA256SUMS is not valid UTF-8\")?;\n        let mut checksums = Self::new();\n\n        for (line_num, line) in content.lines().enumerate() {\n            let line = line.trim();\n            if line.is_empty() {\n                continue;\n            }\n\n            let parts: Vec<&str> = line.splitn(2, \"  \").collect();\n            if parts.len() != 2 {\n                return Err(format!(\n                    \"Invalid format at line {}: expected '<hash>  <filepath>'\",\n                    line_num + 1\n                ));\n            }\n\n            let hash = parts[0].trim();\n            let filepath = parts[1].trim();\n\n            if hash.len() != 64 || !hash.chars().all(|c| c.is_ascii_hexdigit()) {\n                return Err(format!(\n                    \"Invalid hash at line {}: expected 64 hex characters\",\n                    line_num + 1\n                ));\n            }\n\n            let normalized = PathBuf::from(filepath.replace(\"\\\\\", \"/\"));\n            checksums.0.insert(normalized, hash.to_lowercase());\n        }\n\n        if checksums.is_empty() {\n            return Err(\"Empty or invalid SHA256SUMS\".to_string());\n        }\n\n        Ok(checksums)\n    }\n\n    /// Write checksums to file\n    pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {\n        let content = self.to_plain_text();\n        std::fs::write(path.as_ref(), content)\n            .map_err(|e| format!(\"Failed to write {}: {}\", path.as_ref().display(), e))\n    }\n\n    /// Convert to SHA256SUMS string format\n    pub fn to_plain_text(&self) -> String {\n        let mut entries: Vec<_> = self.0.iter().collect();\n        entries.sort_by_key(|(path, _)| path.to_string_lossy().to_string());\n\n        let mut output = String::new();\n        for (path, hash) in entries {\n            let path_str = path.to_string_lossy().replace(\"\\\\\", \"/\");\n            output.push_str(&format!(\"{}  {}\\n\", hash, path_str));\n        }\n        output\n    }\n\n    /// Compare with another CheckSums\n    pub fn compare(&self, other: &Self) -> Vec<(Diff, PathBuf)> {\n        let mut diffs = Vec::new();\n\n        for (path, hash) in &self.0 {\n            match other.0.get(path) {\n                None => diffs.push((Diff::Missing, path.clone())),\n                Some(other_hash) if hash != other_hash => {\n                    diffs.push((Diff::Mismatch, path.clone()))\n                }\n                _ => {}\n            }\n        }\n\n        for path in other.0.keys() {\n            if !self.0.contains_key(path) {\n                diffs.push((Diff::Extra, path.clone()));\n            }\n        }\n\n        diffs\n    }\n\n    pub fn get<P: AsRef<Path>>(&self, path: P) -> Option<&String> {\n        self.0.get(path.as_ref())\n    }\n\n    pub fn contains<P: AsRef<Path>>(&self, path: P) -> bool {\n        self.0.contains_key(path.as_ref())\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_calculate_sha256() {\n        assert_eq!(\n            calculate_sha256(b\"hello world\"),\n            \"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9\"\n        );\n    }\n\n    #[test]\n    fn test_checksums_parse() {\n        let content = \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2  file.txt\";\n        let checksums = CheckSums::parse(content.as_bytes()).unwrap();\n        assert_eq!(checksums.len(), 1);\n        assert!(checksums.contains(\"file.txt\"));\n    }\n\n    #[test]\n    fn test_checksums_compare() {\n        let mut c1 = CheckSums::new();\n        c1.0.insert(PathBuf::from(\"a.txt\"), \"hash1\".to_string());\n\n        let mut c2 = CheckSums::new();\n        c2.0.insert(PathBuf::from(\"a.txt\"), \"hash2\".to_string());\n\n        let diffs = c1.compare(&c2);\n        assert_eq!(diffs.len(), 1);\n        assert_eq!(diffs[0].0, Diff::Mismatch);\n        assert_eq!(diffs[0].1, PathBuf::from(\"a.txt\"));\n    }\n}\n"
  },
  {
    "path": "libs/utils/src/debounce.rs",
    "content": "use std::pin::Pin;\nuse std::sync::{mpsc, Arc, Mutex};\nuse std::time::{self, Duration};\n\npub fn debounce<F, T>(closure: F, delay: Duration) -> Debounce<T>\nwhere\n    F: Fn(T) + Send + Sync + 'static,\n    T: Send + Sync + 'static,\n{\n    let (sender, receiver) = mpsc::channel();\n    let sender = Arc::new(Mutex::new(sender));\n    let debounce_config = Arc::new(Mutex::new(DebounceConfig {\n        closure: Box::pin(closure),\n        delay,\n    }));\n\n    let dup_debounce_config = debounce_config.clone();\n    let debounce = Debounce {\n        sender: Some(sender),\n        thread: Some(std::thread::spawn(move || {\n            let debounce_config = dup_debounce_config;\n            let mut current_param = None; // The last parameter saved for execution\n            let mut last_call_time = None; // The last time a call was received\n\n            loop {\n                let result = if current_param.is_none() {\n                    receiver\n                        .recv()\n                        .map_err(|_| mpsc::RecvTimeoutError::Disconnected)\n                } else {\n                    receiver.recv_timeout(debounce_config.lock().unwrap().delay)\n                };\n\n                let now = time::Instant::now();\n                match result {\n                    Ok(Some(param)) => {\n                        // New call received, save parameter and update last call time\n                        current_param = Some(param);\n                        last_call_time = Some(now);\n                    }\n                    Ok(None) => {\n                        // Terminate signal - execute pending param if any\n                        if let Some(param) = current_param.take() {\n                            let config = debounce_config.lock().unwrap();\n                            (*config.closure)(param);\n                        }\n                        last_call_time = None;\n                    }\n                    Err(mpsc::RecvTimeoutError::Timeout) => {\n                        // Timeout occurred - check if enough time has passed since last call\n                        if let Some(param) = current_param.take() {\n                            let config = debounce_config.lock().unwrap();\n                            let should_execute = last_call_time.is_none()\n                                || now.duration_since(last_call_time.unwrap()) >= config.delay;\n\n                            if should_execute {\n                                (*config.closure)(param);\n                                last_call_time = None;\n                            } else {\n                                // Not enough time has passed, keep waiting\n                                current_param = Some(param);\n                            }\n                        }\n                    }\n                    Err(mpsc::RecvTimeoutError::Disconnected) => break,\n                }\n            }\n        })),\n        debounce_config,\n    };\n    debounce\n}\n\nstruct DebounceConfig<T> {\n    closure: Pin<Box<dyn Fn(T) + Send + Sync + 'static>>,\n    delay: Duration,\n}\n\n#[cfg(debug_assertions)]\nimpl<T> Drop for DebounceConfig<T> {\n    fn drop(&mut self) {\n        println!(\"drop DebounceConfig {:?}\", format!(\"{:p}\", self));\n    }\n}\n\n#[allow(dead_code)]\npub struct Debounce<T> {\n    sender: Option<Arc<Mutex<mpsc::Sender<Option<T>>>>>,\n    thread: Option<std::thread::JoinHandle<()>>,\n    debounce_config: Arc<Mutex<DebounceConfig<T>>>,\n}\n\nimpl<T> Debounce<T> {\n    pub fn call(&self, param: T) {\n        self.sender\n            .as_ref()\n            .unwrap()\n            .lock()\n            .unwrap()\n            .send(Some(param))\n            .unwrap();\n    }\n    pub fn terminate(&self) {\n        self.sender\n            .as_ref()\n            .unwrap()\n            .lock()\n            .unwrap()\n            .send(None)\n            .unwrap();\n    }\n}\nimpl<T> Drop for Debounce<T> {\n    fn drop(&mut self) {\n        self.terminate();\n        #[cfg(debug_assertions)]\n        println!(\"drop Debounce {:?}\", format!(\"{:p}\", self));\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn it_works() {\n        let effect_run_times = Arc::new(Mutex::new(0));\n        let param = Arc::new(Mutex::new(0));\n        let dup_effect_run_times = effect_run_times.clone();\n        let dup_param = param.clone();\n        let debounce_fn = debounce(\n            move |param| {\n                *dup_effect_run_times.lock().unwrap() += 1;\n                *dup_param.lock().unwrap() = param;\n            },\n            std::time::Duration::from_millis(100),\n        );\n        {\n            // Multiple rapid calls - only the last one should execute after delay\n            debounce_fn.call(1);\n            debounce_fn.call(2);\n            debounce_fn.call(3);\n            std::thread::sleep(std::time::Duration::from_millis(150));\n            assert_eq!(*effect_run_times.lock().unwrap(), 1); // Execute only once\n            assert_eq!(*param.lock().unwrap(), 3); // With the last parameter\n        }\n\n        {\n            // Single call after waiting\n            debounce_fn.call(4);\n            std::thread::sleep(std::time::Duration::from_millis(150));\n            assert_eq!(*effect_run_times.lock().unwrap(), 2);\n            assert_eq!(*param.lock().unwrap(), 4);\n        }\n\n        {\n            // Multiple calls, then terminate before execution\n            debounce_fn.call(5);\n            debounce_fn.call(6);\n            std::thread::sleep(std::time::Duration::from_millis(50)); // Wait less than delay\n            debounce_fn.terminate(); // Terminate during debounce period\n            std::thread::sleep(std::time::Duration::from_millis(100));\n            assert_eq!(*effect_run_times.lock().unwrap(), 3); // Should execute on terminate\n            assert_eq!(*param.lock().unwrap(), 6); // With the last parameter\n        }\n    }\n\n    #[test]\n    fn debounce_resets_on_new_calls() {\n        let effect_run_times = Arc::new(Mutex::new(0));\n        let param = Arc::new(Mutex::new(0));\n        let dup_effect_run_times = effect_run_times.clone();\n        let dup_param = param.clone();\n        let debounce_fn = debounce(\n            move |param| {\n                *dup_effect_run_times.lock().unwrap() += 1;\n                *dup_param.lock().unwrap() = param;\n            },\n            std::time::Duration::from_millis(100),\n        );\n\n        // Call every 50ms for 250ms total - debounce should keep resetting\n        debounce_fn.call(1);\n        std::thread::sleep(std::time::Duration::from_millis(50));\n        debounce_fn.call(2);\n        std::thread::sleep(std::time::Duration::from_millis(50));\n        debounce_fn.call(3);\n        std::thread::sleep(std::time::Duration::from_millis(50));\n        debounce_fn.call(4);\n\n        // No execution yet because debounce keeps resetting\n        assert_eq!(*effect_run_times.lock().unwrap(), 0);\n\n        // Wait for debounce to complete\n        std::thread::sleep(std::time::Duration::from_millis(150));\n        assert_eq!(*effect_run_times.lock().unwrap(), 1);\n        assert_eq!(*param.lock().unwrap(), 4); // Last parameter\n    }\n}\n"
  },
  {
    "path": "libs/utils/src/lib.rs",
    "content": "mod debounce;\nmod throttle;\n\npub mod checksums;\npub mod signature;\n\npub use debounce::*;\npub use throttle::*;\n"
  },
  {
    "path": "libs/utils/src/signature.rs",
    "content": "use std::io::Cursor;\n\nuse base64::Engine;\nuse minisign::{PublicKeyBox, SecretKeyBox, SignatureBox};\n\n/// Sign data with minisign\npub fn sign_minisign(\n    data: &[u8],\n    secret_key_base64: &str,\n    password: String,\n) -> Result<String, String> {\n    let secret_key_bytes = base64::engine::general_purpose::STANDARD\n        .decode(secret_key_base64)\n        .map_err(|e| format!(\"Failed to decode secret key: {}\", e))?;\n\n    let secret_key_str = String::from_utf8(secret_key_bytes)\n        .map_err(|e| format!(\"Secret key is not valid UTF-8: {}\", e))?;\n\n    let sk_box = SecretKeyBox::from_string(&secret_key_str)\n        .map_err(|e| format!(\"Invalid secret key format: {}\", e))?;\n\n    let secret_key = sk_box\n        .into_secret_key(Some(password))\n        .map_err(|e| format!(\"Failed to decrypt secret key: {}\", e))?;\n\n    let data_reader = Cursor::new(data);\n    let signature_box = minisign::sign(None, &secret_key, data_reader, None, None)\n        .map_err(|e| format!(\"Failed to sign data: {}\", e))?;\n\n    Ok(signature_box.to_string())\n}\n\n/// Verify minisign signature\npub fn verify_minisign(\n    data: &[u8],\n    signature_content: &str,\n    public_key_base64: &str,\n) -> Result<(), String> {\n    let public_key_bytes = base64::engine::general_purpose::STANDARD\n        .decode(public_key_base64)\n        .map_err(|e| format!(\"Failed to decode public key: {}\", e))?;\n\n    let public_key_str = String::from_utf8(public_key_bytes)\n        .map_err(|e| format!(\"Public key is not valid UTF-8: {}\", e))?;\n\n    let public_key = PublicKeyBox::from_string(&public_key_str)\n        .map_err(|e| format!(\"Invalid public key format: {}\", e))?\n        .into_public_key()\n        .map_err(|e| format!(\"Failed to parse public key: {}\", e))?;\n\n    let signature = SignatureBox::from_string(signature_content)\n        .map_err(|e| format!(\"Invalid signature format: {}\", e))?;\n\n    let data_reader = Cursor::new(data);\n    minisign::verify(&public_key, &signature, data_reader, true, false, false)\n        .map_err(|_| \"Signature verification failed\".to_string())?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "libs/utils/src/throttle.rs",
    "content": "use std::pin::Pin;\nuse std::sync::{mpsc, Arc, Mutex};\nuse std::time::{self, Duration};\n\npub fn throttle<F, T>(closure: F, delay: Duration) -> Throttle<T>\nwhere\n    F: Fn(T) + Send + Sync + 'static,\n    T: Send + Sync + 'static,\n{\n    let (sender, receiver) = mpsc::channel();\n    let sender = Arc::new(Mutex::new(sender));\n    let throttle_config = Arc::new(Mutex::new(ThrottleConfig {\n        closure: Box::pin(closure),\n        delay,\n    }));\n\n    let dup_throttle_config = throttle_config.clone();\n    let throttle = Throttle {\n        sender: Some(sender),\n        thread: Some(std::thread::spawn(move || {\n            let throttle_config = dup_throttle_config;\n            let mut current_param = None; // The last parameter saved for execution\n            let mut last_execution_time = None; // The last time the closure was executed\n\n            loop {\n                let result = if current_param.is_none() {\n                    receiver\n                        .recv()\n                        .map_err(|_| mpsc::RecvTimeoutError::Disconnected)\n                } else {\n                    receiver.recv_timeout(throttle_config.lock().unwrap().delay)\n                };\n\n                let now = time::Instant::now();\n                match result {\n                    Ok(Some(param)) => {\n                        let config = throttle_config.lock().unwrap();\n                        let should_execute = last_execution_time.is_none()\n                            || now.duration_since(last_execution_time.unwrap()) >= config.delay;\n\n                        if should_execute {\n                            (*config.closure)(param);\n                            current_param = None;\n                            last_execution_time = Some(now);\n                        } else {\n                            current_param = Some(param);\n                        }\n                    }\n                    Ok(None) => current_param = None, // Terminate signal\n                    Err(mpsc::RecvTimeoutError::Timeout) => {\n                        if let Some(param) = current_param.take() {\n                            let config = throttle_config.lock().unwrap();\n                            (*config.closure)(param);\n                            // Note: Timeout execution does not update last_execution_time\n                            // This allows immediate execution on next call if enough time has passed\n                        }\n                    }\n                    Err(mpsc::RecvTimeoutError::Disconnected) => break,\n                }\n            }\n        })),\n        throttle_config,\n    };\n    throttle\n}\n\nstruct ThrottleConfig<T> {\n    closure: Pin<Box<dyn Fn(T) + Send + Sync + 'static>>,\n    delay: Duration,\n}\n\n#[cfg(debug_assertions)]\nimpl<T> Drop for ThrottleConfig<T> {\n    fn drop(&mut self) {\n        println!(\"drop ThrottleConfig {:?}\", format!(\"{:p}\", self));\n    }\n}\n\n#[allow(dead_code)]\npub struct Throttle<T> {\n    sender: Option<Arc<Mutex<mpsc::Sender<Option<T>>>>>,\n    thread: Option<std::thread::JoinHandle<()>>,\n    throttle_config: Arc<Mutex<ThrottleConfig<T>>>,\n}\n\nimpl<T> Throttle<T> {\n    pub fn call(&self, param: T) {\n        self.sender\n            .as_ref()\n            .unwrap()\n            .lock()\n            .unwrap()\n            .send(Some(param))\n            .unwrap();\n    }\n    pub fn terminate(&self) {\n        self.sender\n            .as_ref()\n            .unwrap()\n            .lock()\n            .unwrap()\n            .send(None)\n            .unwrap();\n    }\n}\nimpl<T> Drop for Throttle<T> {\n    fn drop(&mut self) {\n        self.terminate();\n        #[cfg(debug_assertions)]\n        println!(\"drop Throttle {:?}\", format!(\"{:p}\", self));\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn it_works() {\n        let effect_run_times = Arc::new(Mutex::new(0));\n        let param = Arc::new(Mutex::new(0));\n        let dup_effect_run_times = effect_run_times.clone();\n        let dup_param = param.clone();\n        let throttle_fn = throttle(\n            move |param| {\n                *dup_effect_run_times.lock().unwrap() += 1;\n                *dup_param.lock().unwrap() = param;\n            },\n            std::time::Duration::from_millis(100),\n        );\n        {\n            throttle_fn.call(1);\n            throttle_fn.call(2);\n            throttle_fn.call(3);\n            std::thread::sleep(std::time::Duration::from_millis(200));\n            assert_eq!(*effect_run_times.lock().unwrap(), 2); // Execute the last parameter after delay\n            assert_eq!(*param.lock().unwrap(), 3);\n        }\n\n        {\n            throttle_fn.call(4);\n            std::thread::sleep(std::time::Duration::from_millis(200));\n            assert_eq!(*effect_run_times.lock().unwrap(), 3);\n            assert_eq!(*param.lock().unwrap(), 4);\n        }\n\n        {\n            throttle_fn.call(5);\n            throttle_fn.call(6);\n            throttle_fn.terminate(); // Terminate before last execution\n            std::thread::sleep(std::time::Duration::from_millis(200));\n            assert_eq!(*effect_run_times.lock().unwrap(), 4);\n            assert_eq!(*param.lock().unwrap(), 5);\n        }\n    }\n}\n"
  },
  {
    "path": "libs/widgets-shared/styles/RichText.css",
    "content": ":root {\n  --spacing-2xs: 0.25rem; /* 4px  */\n  --spacing-xs: 0.5rem; /* 8px  */\n  --spacing-s: 0.75rem; /* 12px */\n  --spacing-m: 1rem; /* 16px */\n  --spacing-l: 1.25rem; /* 20px */\n  --spacing-xl: 1.5rem; /* 24px */\n  --spacing-2xl: 2rem; /* 32px */\n\n  --config-border-radius: 10px;\n}\n\n.richText {\n  p {\n    margin-bottom: var(--spacing-s);\n    text-align: justify;\n\n    sup {\n      vertical-align: super;\n    }\n  }\n\n  a {\n    display: inline-flex;\n    color: var(--color-blue-800);\n\n    &:hover {\n      text-decoration: underline;\n    }\n\n    &:visited {\n      color: var(--color-purple-900);\n    }\n  }\n\n  strong,\n  b {\n    font-weight: 600;\n  }\n\n  i,\n  em {\n    font-style: italic;\n  }\n\n  blockquote {\n    padding-left: var(--spacing-l);\n    margin-bottom: var(--spacing-s);\n    border-left: 2px solid var(--color-gray-400);\n  }\n\n  ul,\n  ol {\n    margin-left: var(--spacing-l);\n    margin-bottom: var(--spacing-s);\n  }\n\n  ul > li {\n    list-style-type: disc;\n    ul > li {\n      list-style-type: circle;\n      ul > li {\n        list-style-type: square;\n      }\n    }\n  }\n\n  ol > li {\n    list-style-type: decimal;\n    ol > li {\n      list-style-type: lower-alpha;\n      ol > li {\n        list-style-type: lower-roman;\n      }\n    }\n  }\n\n  code {\n    background-color: var(--color-gray-200);\n    padding: var(--spacing-2xs) var(--spacing-xs);\n    border-radius: var(--spacing-2xs);\n  }\n\n  pre {\n    code {\n      display: block;\n      overflow-x: auto;\n      border-radius: var(--config-border-radius);\n      background-color: var(--color-persist-gray-900);\n      color: var(--color-persist-gray-100);\n      padding: var(--spacing-m) var(--spacing-l);\n      margin-bottom: var(--spacing-s);\n    }\n  }\n\n  table {\n    border-radius: var(--config-border-radius);\n    border-collapse: collapse;\n    margin-bottom: var(--spacing-s);\n\n    th {\n      font-weight: 600;\n\n      &:not([align]) {\n        text-align: left;\n      }\n    }\n\n    th,\n    td {\n      border: 1px solid var(--color-gray-400);\n      padding: var(--spacing-xs) var(--spacing-s);\n    }\n  }\n\n  /* Reset last item margin */\n  *:last-child {\n    margin-bottom: 0;\n  }\n}\n"
  },
  {
    "path": "libs/widgets-shared/styles/colors.css",
    "content": "/**\n * Color System Documentation\n *\n * This file defines a contrast-based color system where numbers represent\n * contrast intensity against the theme's background, NOT absolute lightness.\n *\n * CONTRAST-ADAPTIVE COLORS (--color-*):\n * - Numbers indicate contrast level against the current theme background\n * - 100 = lowest contrast (subtle, barely visible)\n * - 900 = highest contrast (bold, maximum visibility)\n * - Automatically inverts between light/dark modes to maintain contrast relationships\n *\n * Example:\n *   Light mode: --color-red-100 = #ffebe7 (light red, subtle on white)\n *   Dark mode:  --color-red-100 = #7b0000 (dark red, subtle on black)\n *\n * FIXED COLORS (--color-fixed-*):\n * - Colors that remain constant regardless of theme\n * - Use when you need a specific color value that should never change\n * - Numbers still follow 100-900 scale but represent absolute lightness\n *\n * Usage Guidelines:\n * - Use --color-* for UI elements that need consistent contrast (backgrounds, text, borders)\n * - Use --color-fixed-* for brand colors, illustrations, or theme-independent visuals\n */\n\n:root {\n  color-scheme: light dark;\n}\n\n/* =============================== GRAYS =============================== */\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --color-white: oklch(0% 0 0);\n\n    --color-gray-50: oklch(2% 0 0);\n    --color-gray-100: oklch(15% 0 0);\n    --color-gray-200: oklch(25% 0 0);\n    --color-gray-300: oklch(35% 0 0);\n    --color-gray-400: oklch(45% 0 0);\n    --color-gray-500: oklch(55% 0 0);\n    --color-gray-600: oklch(65% 0 0);\n    --color-gray-700: oklch(75% 0 0);\n    --color-gray-800: oklch(85% 0 0);\n    --color-gray-900: oklch(95% 0 0);\n\n    --color-black: oklch(100% 0 0);\n  }\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    --color-white: oklch(100% 0 0);\n\n    --color-gray-50: oklch(98% 0 0);\n    --color-gray-100: oklch(95% 0 0);\n    --color-gray-200: oklch(85% 0 0);\n    --color-gray-300: oklch(75% 0 0);\n    --color-gray-400: oklch(65% 0 0);\n    --color-gray-500: oklch(55% 0 0);\n    --color-gray-600: oklch(45% 0 0);\n    --color-gray-700: oklch(35% 0 0);\n    --color-gray-800: oklch(25% 0 0);\n    --color-gray-900: oklch(15% 0 0);\n\n    --color-black: oklch(0% 0 0);\n  }\n}\n\n:root {\n  --color-fixed-white: oklch(100% 0 0);\n\n  --color-fixed-gray-50: oklch(98% 0 0);\n  --color-fixed-gray-100: oklch(95% 0 0);\n  --color-fixed-gray-200: oklch(85% 0 0);\n  --color-fixed-gray-300: oklch(75% 0 0);\n  --color-fixed-gray-400: oklch(65% 0 0);\n  --color-fixed-gray-500: oklch(55% 0 0);\n  --color-fixed-gray-600: oklch(45% 0 0);\n  --color-fixed-gray-700: oklch(35% 0 0);\n  --color-fixed-gray-800: oklch(25% 0 0);\n  --color-fixed-gray-900: oklch(15% 0 0);\n\n  --color-fixed-black: oklch(0% 0 0);\n}\n\n/* =============================== COLORS =============================== */\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --color-red-50: oklch(10% 0.036 30);\n    --color-red-100: oklch(18% 0.064 30);\n    --color-red-200: oklch(27% 0.096 30);\n    --color-red-300: oklch(36% 0.129 30);\n    --color-red-400: oklch(45% 0.161 30);\n    --color-red-500: oklch(54% 0.193 30);\n    --color-red-600: oklch(63% 0.225 30);\n    --color-red-700: oklch(72% 0.178 30);\n    --color-red-800: oklch(81% 0.111 30);\n    --color-red-900: oklch(90% 0.054 30);\n\n    --color-orange-50: oklch(10% 0.024 50);\n    --color-orange-100: oklch(18% 0.043 50);\n    --color-orange-200: oklch(27% 0.064 50);\n    --color-orange-300: oklch(36% 0.086 50);\n    --color-orange-400: oklch(45% 0.108 50);\n    --color-orange-500: oklch(54% 0.129 50);\n    --color-orange-600: oklch(63% 0.151 50);\n    --color-orange-700: oklch(72% 0.173 50);\n    --color-orange-800: oklch(81% 0.121 50);\n    --color-orange-900: oklch(90% 0.059 50);\n\n    --color-yellow-50: oklch(10% 0.019 100);\n    --color-yellow-100: oklch(18% 0.034 100);\n    --color-yellow-200: oklch(27% 0.052 100);\n    --color-yellow-300: oklch(36% 0.069 100);\n    --color-yellow-400: oklch(45% 0.086 100);\n    --color-yellow-500: oklch(54% 0.103 100);\n    --color-yellow-600: oklch(63% 0.121 100);\n    --color-yellow-700: oklch(72% 0.138 100);\n    --color-yellow-800: oklch(81% 0.155 100);\n    --color-yellow-900: oklch(90% 0.173 100);\n\n    --color-green-50: oklch(10% 0.031 150);\n    --color-green-100: oklch(18% 0.055 150);\n    --color-green-200: oklch(27% 0.083 150);\n    --color-green-300: oklch(36% 0.11 150);\n    --color-green-400: oklch(45% 0.138 150);\n    --color-green-500: oklch(54% 0.166 150);\n    --color-green-600: oklch(63% 0.193 150);\n    --color-green-700: oklch(72% 0.221 150);\n    --color-green-800: oklch(81% 0.249 150);\n    --color-green-900: oklch(90% 0.162 150);\n\n    --color-seafoam-50: oklch(10% 0.019 180);\n    --color-seafoam-100: oklch(18% 0.035 180);\n    --color-seafoam-200: oklch(27% 0.053 180);\n    --color-seafoam-300: oklch(36% 0.07 180);\n    --color-seafoam-400: oklch(45% 0.088 180);\n    --color-seafoam-500: oklch(54% 0.106 180);\n    --color-seafoam-600: oklch(63% 0.123 180);\n    --color-seafoam-700: oklch(72% 0.141 180);\n    --color-seafoam-800: oklch(81% 0.159 180);\n    --color-seafoam-900: oklch(90% 0.141 180);\n\n    --color-cyan-50: oklch(10% 0.021 230);\n    --color-cyan-100: oklch(18% 0.037 230);\n    --color-cyan-200: oklch(27% 0.056 230);\n    --color-cyan-300: oklch(36% 0.075 230);\n    --color-cyan-400: oklch(45% 0.094 230);\n    --color-cyan-500: oklch(54% 0.113 230);\n    --color-cyan-600: oklch(63% 0.132 230);\n    --color-cyan-700: oklch(72% 0.151 230);\n    --color-cyan-800: oklch(81% 0.103 230);\n    --color-cyan-900: oklch(90% 0.053 230);\n\n    --color-blue-50: oklch(10% 0.041 260);\n    --color-blue-100: oklch(18% 0.074 260);\n    --color-blue-200: oklch(27% 0.111 260);\n    --color-blue-300: oklch(36% 0.147 260);\n    --color-blue-400: oklch(45% 0.184 260);\n    --color-blue-500: oklch(54% 0.219 260);\n    --color-blue-600: oklch(63% 0.171 260);\n    --color-blue-700: oklch(72% 0.126 260);\n    --color-blue-800: oklch(81% 0.083 260);\n    --color-blue-900: oklch(90% 0.042 260);\n\n    --color-indigo-50: oklch(10% 0.049 280);\n    --color-indigo-100: oklch(18% 0.088 280);\n    --color-indigo-200: oklch(27% 0.132 280);\n    --color-indigo-300: oklch(36% 0.176 280);\n    --color-indigo-400: oklch(45% 0.22 280);\n    --color-indigo-500: oklch(54% 0.224 280);\n    --color-indigo-600: oklch(63% 0.175 280);\n    --color-indigo-700: oklch(72% 0.129 280);\n    --color-indigo-800: oklch(81% 0.085 280);\n    --color-indigo-900: oklch(90% 0.043 280);\n\n    --color-purple-50: oklch(10% 0.044 300);\n    --color-purple-100: oklch(18% 0.08 300);\n    --color-purple-200: oklch(27% 0.12 300);\n    --color-purple-300: oklch(36% 0.16 300);\n    --color-purple-400: oklch(45% 0.2 300);\n    --color-purple-500: oklch(54% 0.24 300);\n    --color-purple-600: oklch(63% 0.204 300);\n    --color-purple-700: oklch(72% 0.15 300);\n    --color-purple-800: oklch(81% 0.098 300);\n    --color-purple-900: oklch(90% 0.05 300);\n\n    --color-fuchsia-50: oklch(10% 0.039 335);\n    --color-fuchsia-100: oklch(18% 0.07 335);\n    --color-fuchsia-200: oklch(27% 0.106 335);\n    --color-fuchsia-300: oklch(36% 0.141 335);\n    --color-fuchsia-400: oklch(45% 0.176 335);\n    --color-fuchsia-500: oklch(54% 0.212 335);\n    --color-fuchsia-600: oklch(63% 0.247 335);\n    --color-fuchsia-700: oklch(72% 0.274 335);\n    --color-fuchsia-800: oklch(81% 0.17 335);\n    --color-fuchsia-900: oklch(90% 0.082 335);\n\n    --color-magenta-50: oklch(10% 0.036 360);\n    --color-magenta-100: oklch(18% 0.065 360);\n    --color-magenta-200: oklch(27% 0.098 360);\n    --color-magenta-300: oklch(36% 0.131 360);\n    --color-magenta-400: oklch(45% 0.164 360);\n    --color-magenta-500: oklch(54% 0.197 360);\n    --color-magenta-600: oklch(63% 0.23 360);\n    --color-magenta-700: oklch(72% 0.197 360);\n    --color-magenta-800: oklch(81% 0.122 360);\n    --color-magenta-900: oklch(90% 0.059 360);\n  }\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    --color-red-50: oklch(95% 0.025 30);\n    --color-red-100: oklch(90% 0.054 30);\n    --color-red-200: oklch(81% 0.111 30);\n    --color-red-300: oklch(72% 0.178 30);\n    --color-red-400: oklch(63% 0.225 30);\n    --color-red-500: oklch(54% 0.193 30);\n    --color-red-600: oklch(45% 0.161 30);\n    --color-red-700: oklch(36% 0.129 30);\n    --color-red-800: oklch(27% 0.096 30);\n    --color-red-900: oklch(18% 0.064 30);\n\n    --color-orange-50: oklch(95% 0.028 50);\n    --color-orange-100: oklch(90% 0.059 50);\n    --color-orange-200: oklch(81% 0.121 50);\n    --color-orange-300: oklch(72% 0.173 50);\n    --color-orange-400: oklch(63% 0.151 50);\n    --color-orange-500: oklch(54% 0.129 50);\n    --color-orange-600: oklch(45% 0.108 50);\n    --color-orange-700: oklch(36% 0.086 50);\n    --color-orange-800: oklch(27% 0.064 50);\n    --color-orange-900: oklch(18% 0.043 50);\n\n    --color-yellow-50: oklch(95% 0.099 100);\n    --color-yellow-100: oklch(90% 0.173 100);\n    --color-yellow-200: oklch(81% 0.155 100);\n    --color-yellow-300: oklch(72% 0.138 100);\n    --color-yellow-400: oklch(63% 0.121 100);\n    --color-yellow-500: oklch(54% 0.103 100);\n    --color-yellow-600: oklch(45% 0.086 100);\n    --color-yellow-700: oklch(36% 0.069 100);\n    --color-yellow-800: oklch(27% 0.052 100);\n    --color-yellow-900: oklch(18% 0.034 100);\n\n    --color-green-50: oklch(95% 0.074 150);\n    --color-green-100: oklch(90% 0.162 150);\n    --color-green-200: oklch(81% 0.249 150);\n    --color-green-300: oklch(72% 0.221 150);\n    --color-green-400: oklch(63% 0.193 150);\n    --color-green-500: oklch(54% 0.166 150);\n    --color-green-600: oklch(45% 0.138 150);\n    --color-green-700: oklch(36% 0.11 150);\n    --color-green-800: oklch(27% 0.083 150);\n    --color-green-900: oklch(18% 0.055 150);\n\n    --color-seafoam-50: oklch(95% 0.065 180);\n    --color-seafoam-100: oklch(90% 0.141 180);\n    --color-seafoam-200: oklch(81% 0.159 180);\n    --color-seafoam-300: oklch(72% 0.141 180);\n    --color-seafoam-400: oklch(63% 0.123 180);\n    --color-seafoam-500: oklch(54% 0.106 180);\n    --color-seafoam-600: oklch(45% 0.088 180);\n    --color-seafoam-700: oklch(36% 0.07 180);\n    --color-seafoam-800: oklch(27% 0.053 180);\n    --color-seafoam-900: oklch(18% 0.035 180);\n\n    --color-cyan-50: oklch(95% 0.026 230);\n    --color-cyan-100: oklch(90% 0.053 230);\n    --color-cyan-200: oklch(81% 0.103 230);\n    --color-cyan-300: oklch(72% 0.151 230);\n    --color-cyan-400: oklch(63% 0.132 230);\n    --color-cyan-500: oklch(54% 0.113 230);\n    --color-cyan-600: oklch(45% 0.094 230);\n    --color-cyan-700: oklch(36% 0.075 230);\n    --color-cyan-800: oklch(27% 0.056 230);\n    --color-cyan-900: oklch(18% 0.037 230);\n\n    --color-blue-50: oklch(95% 0.021 260);\n    --color-blue-100: oklch(90% 0.042 260);\n    --color-blue-200: oklch(81% 0.083 260);\n    --color-blue-300: oklch(72% 0.126 260);\n    --color-blue-400: oklch(63% 0.171 260);\n    --color-blue-500: oklch(54% 0.219 260);\n    --color-blue-600: oklch(45% 0.184 260);\n    --color-blue-700: oklch(36% 0.147 260);\n    --color-blue-800: oklch(27% 0.111 260);\n    --color-blue-900: oklch(18% 0.074 260);\n\n    --color-indigo-50: oklch(95% 0.021 280);\n    --color-indigo-100: oklch(90% 0.043 280);\n    --color-indigo-200: oklch(81% 0.085 280);\n    --color-indigo-300: oklch(72% 0.129 280);\n    --color-indigo-400: oklch(63% 0.175 280);\n    --color-indigo-500: oklch(54% 0.224 280);\n    --color-indigo-600: oklch(45% 0.22 280);\n    --color-indigo-700: oklch(36% 0.176 280);\n    --color-indigo-800: oklch(27% 0.132 280);\n    --color-indigo-900: oklch(18% 0.088 280);\n\n    --color-purple-50: oklch(95% 0.024 300);\n    --color-purple-100: oklch(90% 0.05 300);\n    --color-purple-200: oklch(81% 0.098 300);\n    --color-purple-300: oklch(72% 0.15 300);\n    --color-purple-400: oklch(63% 0.204 300);\n    --color-purple-500: oklch(54% 0.24 300);\n    --color-purple-600: oklch(45% 0.2 300);\n    --color-purple-700: oklch(36% 0.16 300);\n    --color-purple-800: oklch(27% 0.12 300);\n    --color-purple-900: oklch(18% 0.08 300);\n\n    --color-fuchsia-50: oklch(95% 0.039 335);\n    --color-fuchsia-100: oklch(90% 0.082 335);\n    --color-fuchsia-200: oklch(81% 0.17 335);\n    --color-fuchsia-300: oklch(72% 0.274 335);\n    --color-fuchsia-400: oklch(63% 0.247 335);\n    --color-fuchsia-500: oklch(54% 0.212 335);\n    --color-fuchsia-600: oklch(45% 0.176 335);\n    --color-fuchsia-700: oklch(36% 0.141 335);\n    --color-fuchsia-800: oklch(27% 0.106 335);\n    --color-fuchsia-900: oklch(18% 0.07 335);\n\n    --color-magenta-50: oklch(95% 0.028 360);\n    --color-magenta-100: oklch(90% 0.059 360);\n    --color-magenta-200: oklch(81% 0.122 360);\n    --color-magenta-300: oklch(72% 0.197 360);\n    --color-magenta-400: oklch(63% 0.23 360);\n    --color-magenta-500: oklch(54% 0.197 360);\n    --color-magenta-600: oklch(45% 0.164 360);\n    --color-magenta-700: oklch(36% 0.131 360);\n    --color-magenta-800: oklch(27% 0.098 360);\n    --color-magenta-900: oklch(18% 0.065 360);\n  }\n}\n\n:root {\n  --color-fixed-red-50: oklch(95% 0.025 30);\n  --color-fixed-red-100: oklch(90% 0.054 30);\n  --color-fixed-red-200: oklch(81% 0.111 30);\n  --color-fixed-red-300: oklch(72% 0.178 30);\n  --color-fixed-red-400: oklch(63% 0.225 30);\n  --color-fixed-red-500: oklch(54% 0.193 30);\n  --color-fixed-red-600: oklch(45% 0.161 30);\n  --color-fixed-red-700: oklch(36% 0.129 30);\n  --color-fixed-red-800: oklch(27% 0.096 30);\n  --color-fixed-red-900: oklch(18% 0.064 30);\n\n  --color-fixed-orange-50: oklch(95% 0.028 50);\n  --color-fixed-orange-100: oklch(90% 0.059 50);\n  --color-fixed-orange-200: oklch(81% 0.121 50);\n  --color-fixed-orange-300: oklch(72% 0.173 50);\n  --color-fixed-orange-400: oklch(63% 0.151 50);\n  --color-fixed-orange-500: oklch(54% 0.129 50);\n  --color-fixed-orange-600: oklch(45% 0.108 50);\n  --color-fixed-orange-700: oklch(36% 0.086 50);\n  --color-fixed-orange-800: oklch(27% 0.064 50);\n  --color-fixed-orange-900: oklch(18% 0.043 50);\n\n  --color-fixed-yellow-50: oklch(95% 0.099 100);\n  --color-fixed-yellow-100: oklch(90% 0.173 100);\n  --color-fixed-yellow-200: oklch(81% 0.155 100);\n  --color-fixed-yellow-300: oklch(72% 0.138 100);\n  --color-fixed-yellow-400: oklch(63% 0.121 100);\n  --color-fixed-yellow-500: oklch(54% 0.103 100);\n  --color-fixed-yellow-600: oklch(45% 0.086 100);\n  --color-fixed-yellow-700: oklch(36% 0.069 100);\n  --color-fixed-yellow-800: oklch(27% 0.052 100);\n  --color-fixed-yellow-900: oklch(18% 0.034 100);\n\n  --color-fixed-green-50: oklch(95% 0.074 150);\n  --color-fixed-green-100: oklch(90% 0.162 150);\n  --color-fixed-green-200: oklch(81% 0.249 150);\n  --color-fixed-green-300: oklch(72% 0.221 150);\n  --color-fixed-green-400: oklch(63% 0.193 150);\n  --color-fixed-green-500: oklch(54% 0.166 150);\n  --color-fixed-green-600: oklch(45% 0.138 150);\n  --color-fixed-green-700: oklch(36% 0.11 150);\n  --color-fixed-green-800: oklch(27% 0.083 150);\n  --color-fixed-green-900: oklch(18% 0.055 150);\n\n  --color-fixed-seafoam-50: oklch(95% 0.065 180);\n  --color-fixed-seafoam-100: oklch(90% 0.141 180);\n  --color-fixed-seafoam-200: oklch(81% 0.159 180);\n  --color-fixed-seafoam-300: oklch(72% 0.141 180);\n  --color-fixed-seafoam-400: oklch(63% 0.123 180);\n  --color-fixed-seafoam-500: oklch(54% 0.106 180);\n  --color-fixed-seafoam-600: oklch(45% 0.088 180);\n  --color-fixed-seafoam-700: oklch(36% 0.07 180);\n  --color-fixed-seafoam-800: oklch(27% 0.053 180);\n  --color-fixed-seafoam-900: oklch(18% 0.035 180);\n\n  --color-fixed-cyan-50: oklch(95% 0.026 230);\n  --color-fixed-cyan-100: oklch(90% 0.053 230);\n  --color-fixed-cyan-200: oklch(81% 0.103 230);\n  --color-fixed-cyan-300: oklch(72% 0.151 230);\n  --color-fixed-cyan-400: oklch(63% 0.132 230);\n  --color-fixed-cyan-500: oklch(54% 0.113 230);\n  --color-fixed-cyan-600: oklch(45% 0.094 230);\n  --color-fixed-cyan-700: oklch(36% 0.075 230);\n  --color-fixed-cyan-800: oklch(27% 0.056 230);\n  --color-fixed-cyan-900: oklch(18% 0.037 230);\n\n  --color-fixed-blue-50: oklch(95% 0.021 260);\n  --color-fixed-blue-100: oklch(90% 0.042 260);\n  --color-fixed-blue-200: oklch(81% 0.083 260);\n  --color-fixed-blue-300: oklch(72% 0.126 260);\n  --color-fixed-blue-400: oklch(63% 0.171 260);\n  --color-fixed-blue-500: oklch(54% 0.219 260);\n  --color-fixed-blue-600: oklch(45% 0.184 260);\n  --color-fixed-blue-700: oklch(36% 0.147 260);\n  --color-fixed-blue-800: oklch(27% 0.111 260);\n  --color-fixed-blue-900: oklch(18% 0.074 260);\n\n  --color-fixed-indigo-50: oklch(95% 0.021 280);\n  --color-fixed-indigo-100: oklch(90% 0.043 280);\n  --color-fixed-indigo-200: oklch(81% 0.085 280);\n  --color-fixed-indigo-300: oklch(72% 0.129 280);\n  --color-fixed-indigo-400: oklch(63% 0.175 280);\n  --color-fixed-indigo-500: oklch(54% 0.224 280);\n  --color-fixed-indigo-600: oklch(45% 0.22 280);\n  --color-fixed-indigo-700: oklch(36% 0.176 280);\n  --color-fixed-indigo-800: oklch(27% 0.132 280);\n  --color-fixed-indigo-900: oklch(18% 0.088 280);\n\n  --color-fixed-purple-50: oklch(95% 0.024 300);\n  --color-fixed-purple-100: oklch(90% 0.05 300);\n  --color-fixed-purple-200: oklch(81% 0.098 300);\n  --color-fixed-purple-300: oklch(72% 0.15 300);\n  --color-fixed-purple-400: oklch(63% 0.204 300);\n  --color-fixed-purple-500: oklch(54% 0.24 300);\n  --color-fixed-purple-600: oklch(45% 0.2 300);\n  --color-fixed-purple-700: oklch(36% 0.16 300);\n  --color-fixed-purple-800: oklch(27% 0.12 300);\n  --color-fixed-purple-900: oklch(18% 0.08 300);\n\n  --color-fixed-fuchsia-50: oklch(95% 0.039 335);\n  --color-fixed-fuchsia-100: oklch(90% 0.082 335);\n  --color-fixed-fuchsia-200: oklch(81% 0.17 335);\n  --color-fixed-fuchsia-300: oklch(72% 0.274 335);\n  --color-fixed-fuchsia-400: oklch(63% 0.247 335);\n  --color-fixed-fuchsia-500: oklch(54% 0.212 335);\n  --color-fixed-fuchsia-600: oklch(45% 0.176 335);\n  --color-fixed-fuchsia-700: oklch(36% 0.141 335);\n  --color-fixed-fuchsia-800: oklch(27% 0.106 335);\n  --color-fixed-fuchsia-900: oklch(18% 0.07 335);\n\n  --color-fixed-magenta-50: oklch(95% 0.028 360);\n  --color-fixed-magenta-100: oklch(90% 0.059 360);\n  --color-fixed-magenta-200: oklch(81% 0.122 360);\n  --color-fixed-magenta-300: oklch(72% 0.197 360);\n  --color-fixed-magenta-400: oklch(63% 0.23 360);\n  --color-fixed-magenta-500: oklch(54% 0.197 360);\n  --color-fixed-magenta-600: oklch(45% 0.164 360);\n  --color-fixed-magenta-700: oklch(36% 0.131 360);\n  --color-fixed-magenta-800: oklch(27% 0.098 360);\n  --color-fixed-magenta-900: oklch(18% 0.065 360);\n}\n"
  },
  {
    "path": "libs/widgets-shared/styles/reset.css",
    "content": "@layer reset {\n  :root {\n    color-scheme: light dark; /* enables light and dark mode as well light-dark() function */\n\n    interpolate-size: allow-keywords; /* This enables animations between keywords (auto, min-content, etc) */\n    transition-behavior: allow-discrete; /* allow transitions between discrete values (display, content-visibility) */\n\n    scroll-behavior: smooth;\n    -webkit-overflow-scrolling: touch;\n\n    font-size: 100%; /* determines the size of the rem unit */\n    text-rendering: optimizeLegibility;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n\n  *,\n  *:after,\n  *:before {\n    margin: 0;\n    padding: 0;\n    border: 0;\n    outline: none;\n    box-sizing: border-box;\n    vertical-align: baseline;\n  }\n\n  /* Avoid selection of text on desktop, mobile apps */\n  :not(input):not(textarea),\n  :not(input):not(textarea)::after,\n  :not(input):not(textarea)::before {\n    -webkit-user-select: none;\n    user-select: none;\n  }\n\n  /* Show normal cursor on desktop, mobile apps except links and inputs */\n  :not(a):not(a *):not(input):not(textarea) {\n    cursor: default !important;\n  }\n\n  img,\n  image,\n  picture,\n  video,\n  iframe,\n  figure {\n    max-height: 100%;\n    max-width: 100%;\n    width: 100%;\n    display: block;\n  }\n\n  a {\n    display: block;\n  }\n\n  p a {\n    display: inline;\n  }\n\n  li {\n    list-style-type: none;\n  }\n\n  h1,\n  h2,\n  h3,\n  h4,\n  h5,\n  h6,\n  p,\n  span,\n  a,\n  strong,\n  blockquote,\n  i,\n  b,\n  em,\n  pre {\n    font-size: 1em;\n    font-weight: inherit;\n    font-style: inherit;\n    text-decoration: none;\n    color: inherit;\n  }\n\n  form,\n  input,\n  textarea,\n  select,\n  button,\n  label {\n    font-family: inherit;\n    font-size: inherit;\n    hyphens: auto;\n    background-color: transparent;\n    display: block;\n    color: inherit;\n  }\n\n  table,\n  tr,\n  td {\n    border-collapse: collapse;\n    border-spacing: 0;\n  }\n\n  svg {\n    width: 100%;\n    display: block;\n  }\n\n  body {\n    font-size: 1em;\n    line-height: 1.4em;\n    font-family: system-ui;\n    color: var(--color-gray-900);\n    background-color: var(--color-gray-50);\n    hyphens: auto;\n  }\n\n  hr {\n    border: 1px solid;\n    margin: 0.5em 0;\n    opacity: 0.8;\n    color: var(--color-gray-200);\n  }\n}\n\n/* Antd reset */\n.ant-popover:not(.ant-color-picker):not(.ant-popconfirm) {\n  .ant-popover-inner {\n    background: transparent !important;\n    border-radius: 0 !important;\n    box-shadow: none !important;\n    padding: 0 !important;\n  }\n}\n\n.ant-dropdown-menu-submenu,\n.ant-dropdown-menu,\n.ant-menu {\n  background: transparent !important;\n  box-shadow: none !important;\n  display: flex !important;\n  flex-direction: column !important;\n  gap: 4px !important;\n  padding: 4px !important;\n\n  .ant-menu-item,\n  .ant-dropdown-menu-item {\n    padding: 10px !important;\n    height: min-content !important;\n    width: 100% !important;\n    line-height: 12px !important;\n    margin: 0 !important;\n    border-radius: 8px !important;\n\n    &:active {\n      background-color: transparent !important;\n    }\n  }\n\n  .ant-menu-item-selected {\n    background-color: transparent !important;\n  }\n\n  .ant-menu-item:not(.ant-menu-item-danger),\n  .ant-dropdown-menu-item:not(.ant-dropdown-menu-item-danger) {\n    color: inherit !important;\n\n    &:hover {\n      backdrop-filter: brightness(0.7) !important;\n    }\n  }\n\n  .ant-menu-item-divider,\n  .ant-dropdown-menu-item-divider {\n    backdrop-filter: brightness(0.9) !important;\n  }\n\n  .ant-dropdown-menu-submenu-title {\n    color: inherit !important;\n  }\n}\n\n.ant-picker-cell {\n  padding: 0 !important;\n}\n\n.ant-tooltip:has(.ant-tooltip-inner:empty) {\n  display: none;\n}\n"
  },
  {
    "path": "libs/widgets-shared/styles/spacings.css",
    "content": ":root {\n  --spacing-2xs: 0.25rem; /* 4px  */\n  --spacing-xs: 0.5rem; /* 8px  */\n  --spacing-s: 0.75rem; /* 12px */\n  --spacing-m: 1rem; /* 16px */\n  --spacing-l: 1.25rem; /* 20px */\n  --spacing-xl: 1.5rem; /* 24px */\n  --spacing-2xl: 2rem; /* 32px */\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"seelen-ui\",\n  \"version\": \"2.5.7\",\n  \"description\": \"Seelen UI Project\",\n  \"type\": \"module\",\n  \"license\": \"AGPL-3.0-or-later\",\n  \"scripts\": {\n    \"tauri\": \"tauri\",\n    \"tauri:update\": \"tsx scripts/UpdateTauri.ts\",\n    \"build:ui\": \"tsx scripts/build.ts\",\n    \"build:lib\": \"cd ./libs/core && deno task build && cd ../..\",\n    \"dev\": \"cargo build && tauri dev\",\n    \"test\": \"\",\n    \"translate\": \"tsx ./scripts/translate/mod.ts\",\n    \"type-check\": \"tsc --noEmit && svelte-check --tsconfig ./tsconfig.json && cd ./libs/core && deno check && cd ../..\",\n    \"version:start\": \"tsx ./scripts/versionish.ts start\",\n    \"version:finish\": \"tsx ./scripts/versionish.ts finish\",\n    \"preinstall\": \"npm run build:lib\",\n    \"postinstall\": \"lefthook install\"\n  },\n  \"keywords\": [],\n  \"author\": {\n    \"name\": \"eythaann\",\n    \"email\": \"eythan.cvt@gmail.com\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/eythaann/seelen-ui.git\"\n  },\n  \"dependencies\": {\n    \"@dnd-kit/abstract\": \"^0.3.2\",\n    \"@dnd-kit/helpers\": \"^0.3.2\",\n    \"@dnd-kit/react\": \"^0.3.2\",\n    \"@dnd-kit/svelte\": \"^0.3.2\",\n    \"@m31coding/fuzzy-search\": \"^2.0.0\",\n    \"@preact/compat\": \"^18.3.1\",\n    \"@preact/signals\": \"^2.8.2\",\n    \"@tauri-apps/api\": \"^2.10.1\",\n    \"@tauri-apps/cli\": \"^2.10.1\",\n    \"@types/object-inspect\": \"^1.13.0\",\n    \"esbuild-svelte\": \"^0.9.3\",\n    \"object-inspect\": \"^1.13.4\",\n    \"preact\": \"^10.29.0\",\n    \"preact-render-to-string\": \"^6.6.5\",\n    \"react-router\": \"^7.13.1\",\n    \"rehype-stringify\": \"^10.0.1\",\n    \"remark-gfm\": \"^4.0.1\",\n    \"remark-parse\": \"^11.0.0\",\n    \"remark-rehype\": \"^11.1.2\",\n    \"svelte-preprocess\": \"^6.0.3\",\n    \"unified\": \"^11.0.5\"\n  },\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"^20.4.2\",\n    \"@commitlint/config-conventional\": \"^19.2.2\",\n    \"@nyariv/sandboxjs\": \"^0.8.35\",\n    \"@seelen-ui/lib\": \"file:./libs/core/npm\",\n    \"@seelen/translation-toolkit\": \"^1.1.10\",\n    \"@types/express\": \"^5.0.0\",\n    \"@types/js-yaml\": \"^4.0.9\",\n    \"@types/lodash\": \"^4.17.24\",\n    \"@types/node\": \"^25.5.0\",\n    \"@types/react-dom\": \"^19.0.0\",\n    \"@types/yargs\": \"^17.0.33\",\n    \"antd\": \"^6.3.3\",\n    \"esbuild\": \"^0.27.4\",\n    \"esbuild-css-modules-plugin\": \"^3.1.4\",\n    \"express\": \"^4.21.2\",\n    \"framer-motion\": \"^12.37.0\",\n    \"i18next\": \"^25.8.18\",\n    \"js-yaml\": \"^4.1.0\",\n    \"lefthook\": \"^2.1.4\",\n    \"lodash\": \"^4.17.21\",\n    \"moment\": \"^2.30.1\",\n    \"react-i18next\": \"^15.0.0\",\n    \"react-icons\": \"^5.5.0\",\n    \"real-react-dom\": \"npm:react-dom@^19.0.0\",\n    \"svelte\": \"^5.53.12\",\n    \"svelte-check\": \"^4.3.5\",\n    \"toml\": \"^3.0.0\",\n    \"tsx\": \"^4.19.3\",\n    \"typescript\": \"^5.9.3\",\n    \"yargs\": \"^18.0.0\",\n    \"zod\": \"^3.24.3\"\n  }\n}\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\n# https://releases.rs/docs/1.91.0\nchannel = \"nightly-2025-09-12\"\n"
  },
  {
    "path": "scripts/PalletteGenerator.ts",
    "content": "// generate-oklch-scale.ts\n\nfunction oklchToP3(L: number, C: number, H: number) {\n  const hRad = (H * Math.PI) / 180;\n\n  const a = C * Math.cos(hRad);\n  const b = C * Math.sin(hRad);\n\n  const l = L + 0.3963377774 * a + 0.2158037573 * b;\n  const m = L - 0.1055613458 * a - 0.0638541728 * b;\n  const s = L - 0.0894841775 * a - 1.291485548 * b;\n\n  const l3 = l * l * l;\n  const m3 = m * m * m;\n  const s3 = s * s * s;\n\n  // OKLab → XYZ\n  const X = 1.2270138511 * l3 - 0.5577999807 * m3 + 0.281256149 * s3;\n  const Y = -0.0405801784 * l3 + 1.1122568696 * m3 - 0.0716766787 * s3;\n  const Z = -0.0763812845 * l3 - 0.4214819784 * m3 + 1.5861632204 * s3;\n\n  // XYZ → Display-P3\n  return {\n    r: 2.493496911941425 * X - 0.9313836179191239 * Y - 0.40271078445071684 * Z,\n    g: -0.8294889695615747 * X + 1.7626640603183463 * Y + 0.023624685841943577 * Z,\n    b: 0.03584583024378447 * X - 0.07617238926804182 * Y + 0.9568845240076872 * Z,\n  };\n}\n\nfunction inP3(r: number, g: number, b: number) {\n  const eps = 1e-5;\n  return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;\n}\n\nfunction maxChromaP3(L: number, H: number) {\n  let low = 0;\n  let high = 1;\n\n  for (let i = 0; i < 30; i++) {\n    const mid = (low + high) / 2;\n    const { r, g, b } = oklchToP3(L, mid, H);\n\n    if (inP3(r, g, b)) low = mid;\n    else high = mid;\n  }\n\n  return low;\n}\n\nfunction oklchToSRGB(L: number, C: number, H: number) {\n  const hRad = (H * Math.PI) / 180;\n\n  const a = C * Math.cos(hRad);\n  const b = C * Math.sin(hRad);\n\n  const l = L + 0.3963377774 * a + 0.2158037573 * b;\n  const m = L - 0.1055613458 * a - 0.0638541728 * b;\n  const s = L - 0.0894841775 * a - 1.291485548 * b;\n\n  const l3 = l * l * l;\n  const m3 = m * m * m;\n  const s3 = s * s * s;\n\n  const r = 4.0767416621 * l3 + -3.3077115913 * m3 + 0.2309699292 * s3;\n\n  const g = -1.2684380046 * l3 + 2.6097574011 * m3 + -0.3413193965 * s3;\n\n  const b2 = -0.0041960863 * l3 + -0.7034186147 * m3 + 1.707614701 * s3;\n\n  return { r, g, b: b2 };\n}\n\nfunction inSRGB(r: number, g: number, b: number) {\n  return r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1;\n}\n\nfunction _maxChromaSRGB(L: number, H: number) {\n  let low = 0;\n  let high = 0.4;\n\n  for (let i = 0; i < 30; i++) {\n    const mid = (low + high) / 2;\n\n    const { r, g, b } = oklchToSRGB(L, mid, H);\n\n    if (inSRGB(r, g, b)) {\n      low = mid;\n    } else {\n      high = mid;\n    }\n  }\n\n  return low;\n}\n\nconst LIGHT_STEPS = {\n  50: 95,\n  100: 90,\n  200: 81,\n  300: 72,\n  400: 63,\n  500: 54,\n  600: 45,\n  700: 36,\n  800: 27,\n  900: 18,\n};\n\nconst DARK_STEPS = {\n  50: 10,\n  100: 18,\n  200: 27,\n  300: 36,\n  400: 45,\n  500: 54,\n  600: 63,\n  700: 72,\n  800: 81,\n  900: 90,\n};\n\nconst COLOR_LIST = {\n  red: 30,\n  orange: 50,\n  yellow: 100,\n  green: 150,\n  seafoam: 180,\n  cyan: 230,\n  blue: 260,\n  indigo: 280,\n  purple: 300,\n  fuchsia: 335,\n  magenta: 360,\n};\n\nfunction generateScale(name: string, hue: number, steps: Record<number, number>) {\n  const lines: string[] = [];\n\n  for (const [step, lightness] of Object.entries(steps)) {\n    const L = lightness / 100;\n\n    const C = maxChromaP3(L, hue) * 0.8;\n\n    const truncatedC = truncate(C, 3).toString().padEnd(5, \"0\");\n\n    lines.push(`    --color-${name}-${step}: oklch(${lightness}% ${truncatedC} ${hue});`);\n  }\n\n  return lines.join(\"\\n\");\n}\n\nfunction generateScheme(scheme: \"light\" | \"dark\" | \"none\", steps: Record<number, number>) {\n  const lines: string[] = [];\n  const hasScheme = scheme !== \"none\";\n\n  if (hasScheme) {\n    lines.push(`@media (prefers-color-scheme: ${scheme}) {`);\n  }\n  lines.push(`  :root {`);\n\n  let groups: string[] = [];\n  for (const [name, hue] of Object.entries(COLOR_LIST)) {\n    groups.push(generateScale(hasScheme ? name : `fixed-${name}`, hue, steps));\n  }\n\n  lines.push(groups.join(\"\\n\\n\"));\n  lines.push(\"  }\");\n  if (hasScheme) {\n    lines.push(\"}\");\n  }\n\n  return lines.join(\"\\n\");\n}\n\nfunction truncate(num: number, decimals: number): number {\n  const factor = 10 ** decimals;\n  return Math.trunc(num * factor) / factor;\n}\n\nconsole.log(generateScheme(\"dark\", DARK_STEPS));\nconsole.log(\"\");\nconsole.log(generateScheme(\"light\", LIGHT_STEPS));\nconsole.log(\"\");\nconsole.log(generateScheme(\"none\", LIGHT_STEPS));\n"
  },
  {
    "path": "scripts/SetFixedRuntime.ps1",
    "content": "param(\n    [Parameter(Mandatory = $false)]\n    [ValidateSet(\"x64\", \"arm64\")]\n    [string]$Architecture = \"x64\",\n\n    [Parameter(Mandatory = $false)]\n    [string]$Version = \"143.0.3650.139\"\n)\n\n$ErrorActionPreference = \"Stop\"\n\nWrite-Host \"Setting up Fixed Runtime for architecture: $Architecture, version: $Version\"\n\n$RuntimeDir = \".\\src\\runtime\"\n$ConfigPath = \".\\src\\tauri.conf.json\"\n\nmkdir $RuntimeDir -Force\n\n# Build download URL\n$FileName = \"Microsoft.WebView2.FixedVersionRuntime.$Version.$Architecture.cab\"\n$DownloadUrl = \"https://github.com/Seelen-Corp/webview2-archive/releases/download/$Version/$FileName\"\n$CabFilePath = Join-Path $RuntimeDir $FileName\n\n# Download the .cab file if it doesn't exist\nif (Test-Path $CabFilePath) {\n    Write-Host \"File already exists, skipping download: $FileName\"\n}\nelse {\n    Write-Host \"Downloading from: $DownloadUrl\" -ForegroundColor Cyan\n    try {\n        Invoke-WebRequest -Uri $DownloadUrl -OutFile $CabFilePath -UseBasicParsing\n        Write-Host \"Download completed: $FileName\" -ForegroundColor Green\n    }\n    catch {\n        Write-Error \"Failed to download file from $DownloadUrl. Error: $_\"\n        exit 1\n    }\n}\n\n$CabFile = Get-Item $CabFilePath\n\n$RuntimeFolderName = $Version\n\n# Clean up old files\nRemove-Item \"$RuntimeDir\\$($CabFile.BaseName)\" -Force -Recurse -ErrorAction SilentlyContinue\nRemove-Item \"$RuntimeDir\\$Version\" -Force -Recurse -ErrorAction SilentlyContinue\n\n# Extraction\n$file = $CabFile.FullName\n$destination = \"$RuntimeDir\"\nStart-Process -FilePath \"cmd.exe\" -ArgumentList \"/c expand.exe `\"$file`\" -f:* `\"$destination`\" > nul 2>&1\" -Wait -WindowStyle Hidden | Out-Null\nMove-Item \"$RuntimeDir\\$($CabFile.BaseName)\" \"$RuntimeDir\\$Version\"\nWrite-Host \"Runtime expanded successfully\" -ForegroundColor Green\n\n# Update tauri config file\n$RelativePath = \"runtime/$RuntimeFolderName\"\n$Config = Get-Content $ConfigPath -Raw | ConvertFrom-Json\n$WebviewConfig = [PSCustomObject]@{\n    type = \"fixedRuntime\"\n    path = $RelativePath\n}\n$Config.bundle.windows | Add-Member -Force -MemberType NoteProperty -Name \"webviewInstallMode\" -Value $WebviewConfig\n\n$Config | ConvertTo-Json -Depth 100 | Set-Content $ConfigPath -Encoding UTF8\n\nWrite-Host \"Configuration updated successfully\" -ForegroundColor Green\nWrite-Host \"webviewInstallMode.path: $RelativePath\"\n"
  },
  {
    "path": "scripts/SubmitToStore.ps1",
    "content": "Set-ExecutionPolicy RemoteSigned -Force\n\n# Prepare materials from previous steps and runner environment variables\n$jsonContent = Get-Content -Path \".\\package.json\" -Raw\n$jsonObject = $jsonContent | ConvertFrom-Json\n$version = $jsonObject.version\n\n# Look for both x64 and arm64 MSIX files\n$msixFiles = @(\n    \".\\target\\release\\bundle\\msix\\Seelen.UI_${version}.0_x64.msix\",\n    \".\\target\\release\\bundle\\msix\\Seelen.UI_${version}.0_arm64.msix\"\n)\n\n# Filter existing files\n$existingMsixFiles = $msixFiles | Where-Object { Test-Path $_ }\n\nif ($existingMsixFiles.Count -eq 0) {\n    Write-Error \"No MSIX files found\"\n    exit 1\n}\n\nWrite-Host \"Found MSIX files:\"\n$existingMsixFiles | ForEach-Object { Write-Host \"  - $_\" }\n\n# Use first file as primary, but pass all to New-SubmissionPackage\n$appxUploadFilePath = $existingMsixFiles[0]\n\n# $appxUploadFilePath = $PSBoundParameters[\"appxPathParam\"]\n$username = $env:PartnerCenterClientId\n$password = ConvertTo-SecureString $env:PartnerCenterClientSecret -AsPlainText -Force\n$appStoreId = $env:PartnerCenterStoreId\n$tenantId = $env:PartnerCenterTenantId\n\n$scriptPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent\n\n# ********* Create temporary directory for submission artifacts *********\n$sbTempFolderPath = New-Item -Type Directory -Force -Path (Join-Path -Path $scriptPath -ChildPath 'SBTemp')\n\n# ********* Install StoreBroker and import PowerShell Module *********\nSet-PSRepository -Name \"PSGallery\" -InstallationPolicy Trusted\nInstall-Module -Name StoreBroker\n\n# ********* Authenticate Store Broker *********\n$cred = New-Object System.Management.Automation.PSCredential ($username, $password)\nSet-StoreBrokerAuthentication -TenantId $tenantId -Credential $cred\n\n# ********* Prepare Submission Package *********\n$configFilePath = (Join-Path -Path $scriptPath -ChildPath 'submission.json')\n\n# StoreBroker supports multiple packages - pass all MSIX files as array\nif ($existingMsixFiles.Count -eq 1) {\n    New-SubmissionPackage -ConfigPath $configFilePath -AppxPath $appxUploadFilePath -OutPath $sbTempFolderPath -OutName 'submission'\n} else {\n    New-SubmissionPackage -ConfigPath $configFilePath -AppxPath $existingMsixFiles -OutPath $sbTempFolderPath -OutName 'submission'\n}\n\n# ********* UPDATE SUBMISSION *********\n$submissionDataPath = Join-Path -Path $sbTempFolderPath -ChildPath 'submission.json'\n$submissionPackagePath = Join-Path -Path $sbTempFolderPath -ChildPath 'submission.zip'\n\nUpdate-ApplicationSubmission -Verbose -Force -ReplacePackages -AutoCommit -AppId $appStoreId -SubmissionDataPath $submissionDataPath -PackagePath $submissionPackagePath -NoStatus"
  },
  {
    "path": "scripts/UpdateTauri.ts",
    "content": "import { execSync } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport toml from \"toml\";\n\nimport packageJson from \"../package.json\";\n\nlet dependencies = {\n  ...packageJson.dependencies,\n  ...packageJson.devDependencies,\n};\nlet toUpdate: string[] = [];\n\nfor (let key in dependencies) {\n  if (key.startsWith(\"@tauri-apps/\")) {\n    toUpdate.push(key);\n  }\n}\n\n// run install instead update to get the latest version\nlet command = `npm install ${toUpdate.join(\" \")}`;\nconsole.info(`${command}\\n`);\nexecSync(command, { stdio: \"inherit\" });\n\nconst cargoToml = toml.parse(readFileSync(\"src/Cargo.toml\", \"utf-8\"));\ndependencies = {\n  ...cargoToml[\"build-dependencies\"],\n  ...cargoToml.dependencies,\n};\ntoUpdate = [];\n\nfor (let key in dependencies) {\n  if (key.startsWith(\"tauri\")) {\n    toUpdate.push(key);\n  }\n}\n\ncommand = `cargo upgrade -p ${toUpdate.join(\" -p \")}`;\nconsole.info(`${command}\\n`);\nexecSync(command, { stdio: \"inherit\" });\n"
  },
  {
    "path": "scripts/build/README.md",
    "content": "# Build System\n\nThis directory contains the modular build system for Seelen UI applications.\n\n## Structure\n\n```\nscripts/build/\n├── README.md           # This file\n├── config.ts           # Shared configuration and argument parsing\n├── types.ts            # TypeScript type definitions\n├── server.ts           # Development server\n├── builders/           # Framework-specific builders\n│   ├── react.ts        # React/Preact build configuration\n│   └── svelte.ts       # Svelte build configuration\n├── plugins/            # Custom esbuild plugins\n│   └── index.ts        # Copy public files plugin\n└── steps/              # Build steps\n    ├── cleanup.ts      # Clean dist directory\n    ├── discover.ts     # Discover entry points\n    └── icons.ts        # Extract icons from react-icons\n```\n\n## Build Process\n\nThe build process is orchestrated by `scripts/build.ts` and follows these steps:\n\n1. **Parse Arguments** - Parse command-line flags (`--production`, `--serve`)\n2. **Extract Icons** - Extract SVG icons from react-icons package (once)\n3. **Clean Dist** - Remove old build artifacts (preserves icons)\n4. **Discover Entry Points** - Find all application entry points\n5. **Build React** - Bundle React/Preact applications\n6. **Build Svelte** - Bundle Svelte applications\n7. **Start Server** - Start development server (if `--serve` flag)\n\n## Framework Support\n\n### React/Preact\n\n- Entry point: `src/ui/{app}/index.tsx`\n- Uses Preact as a drop-in replacement for React\n- Supports CSS Modules with camelCase conversion\n- Configuration: `builders/react.ts`\n\n### Svelte\n\n- Entry point: `src/ui/{app}/index.svelte`\n- Requires Svelte esbuild plugin (to be configured)\n- Configuration: `builders/svelte.ts`\n\n### Vanilla TypeScript\n\n- Entry point: `src/ui/{app}/index.ts`\n- Bundled with React build configuration\n- No framework overhead\n\n## Adding New Frameworks\n\nTo add support for a new framework:\n\n1. Create a new builder in `builders/{framework}.ts`\n2. Implement the build configuration and build function\n3. Update `steps/discover.ts` to detect the new framework\n4. Import and call the builder in `scripts/build.ts`\n\n## Configuration\n\n### Build Options\n\n- `--production` - Enable production mode (minification, no sourcemaps)\n- `--serve` - Start development server with watch mode\n\n### Constants\n\nDefined in `config.ts`:\n\n- `DEV_SERVER_PORT` - Development server port (3579)\n- `DIST_DIR` - Output directory (./dist)\n- `ICONS_DIR` - Icons directory (./dist/icons)\n- `UI_DIR` - UI source directory (src/ui)\n\n## Custom Plugins\n\n### Copy Public Plugin\n\nDefined in `plugins/index.ts`\n\n- Copies `public/` folders from each app to the dist directory\n- Reorganizes nested output folders to root level\n- Cleans up temporary directories\n\n## Development\n\nTo run the build in development mode:\n\n```bash\nnpm run build:ui\n```\n\nTo run with dev server:\n\n```bash\nnpm run build:ui -- --serve\n```\n\nTo run in production mode:\n\n```bash\nnpm run build:ui -- --production\n```\n"
  },
  {
    "path": "scripts/build/builders/react.ts",
    "content": "// React/Preact build configuration\n\nimport esbuild from \"esbuild\";\nimport CssModulesPlugin from \"esbuild-css-modules-plugin\";\nimport { createCopyPublicPlugin, createLoggerPlugin } from \"../plugins/index.ts\";\nimport type { BuildArgs } from \"../types.ts\";\nimport { DIST_DIR, NODE_MODULES_DIR, UI_DIR } from \"../config.ts\";\n\n/**\n * Creates esbuild configuration for React/Preact applications\n * Uses Preact as a drop-in replacement for React for better performance\n */\nexport function createReactBuildConfig(\n  entryPoints: string[],\n  appFolders: string[],\n  args: BuildArgs,\n  isWatchMode: boolean,\n): esbuild.BuildOptions {\n  return {\n    entryPoints,\n    bundle: true,\n    minify: args.isProd,\n    sourcemap: !args.isProd,\n    treeShaking: true,\n    format: \"esm\",\n    target: \"esnext\",\n    platform: \"browser\",\n    outdir: DIST_DIR,\n    outbase: `./${UI_DIR}`,\n    jsx: \"automatic\",\n    loader: {\n      \".yml\": \"text\",\n    },\n    plugins: [\n      createLoggerPlugin(\"React\", entryPoints.length, isWatchMode),\n      CssModulesPlugin({\n        localsConvention: \"camelCase\",\n        pattern: \"do-not-use-on-themes-[local]-[hash]\",\n        targets: {}, // this disables the transpilation of features.\n      }),\n      createCopyPublicPlugin(appFolders),\n    ],\n    alias: {\n      react: `${NODE_MODULES_DIR}/preact/compat/`,\n      \"react/jsx-runtime\": `${NODE_MODULES_DIR}/preact/jsx-runtime`,\n      \"react-dom\": `${NODE_MODULES_DIR}/preact/compat/`,\n      \"react-dom/*\": `${NODE_MODULES_DIR}/preact/compat/*`,\n    },\n  };\n}\n\n/**\n * Builds React/Preact applications using esbuild\n */\nexport async function buildReact(\n  entryPoints: string[],\n  appFolders: string[],\n  args: BuildArgs,\n): Promise<void> {\n  if (entryPoints.length === 0) {\n    return;\n  }\n\n  const isWatchMode = args.serve;\n  const config = createReactBuildConfig(entryPoints, appFolders, args, isWatchMode);\n\n  if (isWatchMode) {\n    const ctx = await esbuild.context(config);\n    await ctx.watch();\n  } else {\n    await esbuild.build(config);\n  }\n}\n"
  },
  {
    "path": "scripts/build/builders/svelte.ts",
    "content": "// Svelte build configuration\n\nimport esbuild from \"esbuild\";\nimport { createCopyPublicPlugin, createLoggerPlugin } from \"../plugins/index.ts\";\nimport type { BuildArgs } from \"../types.ts\";\nimport sveltePlugin from \"esbuild-svelte\";\nimport { sveltePreprocess } from \"svelte-preprocess\";\nimport { DIST_DIR, UI_DIR } from \"../config.ts\";\n\n/**\n * Creates esbuild configuration for Svelte applications\n * Note: This is a placeholder configuration. You'll need to add the appropriate\n * Svelte esbuild plugin (e.g., esbuild-svelte) to your dependencies and configure it here.\n */\nexport function createSvelteBuildConfig(\n  entryPoints: string[],\n  appFolders: string[],\n  args: BuildArgs,\n  isWatchMode: boolean,\n): esbuild.BuildOptions {\n  return {\n    entryPoints,\n    bundle: true,\n    minify: args.isProd,\n    sourcemap: !args.isProd,\n    treeShaking: true,\n    format: \"esm\",\n    target: \"esnext\",\n    platform: \"browser\",\n    outdir: DIST_DIR,\n    outbase: `./${UI_DIR}`,\n    loader: {\n      \".yml\": \"text\",\n    },\n    conditions: [\"svelte\"], // needed to support some imports\n    plugins: [\n      createLoggerPlugin(\"Svelte\", entryPoints.length, isWatchMode),\n      sveltePlugin({\n        cache: false,\n        preprocess: sveltePreprocess({\n          postcss: {\n            plugins: [],\n          },\n        }),\n        filterWarnings: (warning) => !warning.filename?.includes(\"node_modules\"),\n      }),\n      createCopyPublicPlugin(appFolders),\n    ],\n    // Add any Svelte-specific aliases here if needed\n    alias: {},\n  };\n}\n\n/**\n * Builds Svelte applications using esbuild\n */\nexport async function buildSvelte(\n  entryPoints: string[],\n  appFolders: string[],\n  args: BuildArgs,\n): Promise<void> {\n  if (entryPoints.length === 0) {\n    return;\n  }\n\n  const isWatchMode = args.serve;\n  const config = createSvelteBuildConfig(entryPoints, appFolders, args, isWatchMode);\n\n  if (isWatchMode) {\n    const ctx = await esbuild.context(config);\n    await ctx.watch();\n  } else {\n    await esbuild.build(config);\n  }\n}\n"
  },
  {
    "path": "scripts/build/builders/vanilla.ts",
    "content": "// Vanilla TypeScript build configuration\n\nimport esbuild from \"esbuild\";\nimport { createCopyPublicPlugin, createLoggerPlugin } from \"../plugins/index.ts\";\nimport type { BuildArgs } from \"../types.ts\";\nimport { DIST_DIR, UI_DIR } from \"../config.ts\";\n\n/**\n * Creates esbuild configuration for Vanilla TypeScript applications\n */\nexport function createVanillaBuildConfig(\n  entryPoints: string[],\n  appFolders: string[],\n  args: BuildArgs,\n  isWatchMode: boolean,\n): esbuild.BuildOptions {\n  return {\n    entryPoints,\n    bundle: true,\n    minify: args.isProd,\n    sourcemap: !args.isProd,\n    treeShaking: true,\n    format: \"esm\",\n    target: \"esnext\",\n    platform: \"browser\",\n    outdir: DIST_DIR,\n    outbase: `./${UI_DIR}`,\n    loader: {\n      \".yml\": \"text\",\n      \".svg\": \"text\",\n    },\n    plugins: [\n      createLoggerPlugin(\"Vanilla\", entryPoints.length, isWatchMode),\n      createCopyPublicPlugin(appFolders),\n    ],\n  };\n}\n\n/**\n * Builds Vanilla TypeScript applications using esbuild\n */\nexport async function buildVanilla(\n  entryPoints: string[],\n  appFolders: string[],\n  args: BuildArgs,\n): Promise<void> {\n  if (entryPoints.length === 0) {\n    return;\n  }\n\n  const isWatchMode = args.serve;\n  const config = createVanillaBuildConfig(entryPoints, appFolders, args, isWatchMode);\n\n  if (isWatchMode) {\n    const ctx = await esbuild.context(config);\n    await ctx.watch();\n  } else {\n    await esbuild.build(config);\n  }\n}\n"
  },
  {
    "path": "scripts/build/config.ts",
    "content": "// Shared build configuration\n\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport process from \"node:process\";\nimport type { BuildArgs } from \"./types.ts\";\n\nexport async function parseArgs(): Promise<BuildArgs> {\n  const argv = await yargs(hideBin(process.argv))\n    .option(\"production\", {\n      type: \"boolean\",\n      description: \"Enable Production Minified Bundle\",\n      default: false,\n    })\n    .option(\"serve\", {\n      type: \"boolean\",\n      description: \"Run a local server\",\n      default: false,\n    }).argv;\n\n  return {\n    isProd: !!argv.production,\n    serve: !!argv.serve,\n  };\n}\n\nexport const DEV_SERVER_PORT = 3579;\nexport const DIST_DIR = \"./dist\";\nexport const ICONS_DIR = \"./dist/icons\";\nexport const UI_DIR = \"src/ui\";\nexport const SHARED_DIR = \"shared\";\nexport const NODE_MODULES_DIR = \"./node_modules\";\n"
  },
  {
    "path": "scripts/build/plugins/index.ts",
    "content": "// Custom esbuild plugins\n\nimport type esbuild from \"esbuild\";\nimport fs from \"fs\";\n\n/**\n * Custom plugin to handle post-build operations:\n * - Copies public folders for each widget\n * - Moves nested dist folders to root\n * - Cleans up temporary directories\n */\nexport function createCopyPublicPlugin(appFolders: string[]): esbuild.Plugin {\n  return {\n    name: \"copy-public-by-entry\",\n    setup(build) {\n      build.onEnd(() => {\n        try {\n          // Copy public folder for each widget\n          for (const folder of appFolders) {\n            const source = `src/ui/${folder}/public`;\n            const target = `dist/${folder}`;\n\n            if (fs.existsSync(source)) {\n              fs.cpSync(source, target, { recursive: true, force: true });\n            }\n          }\n\n          // Move nested folders to root\n          const distSrcPath = \"dist/src/ui\";\n          if (fs.existsSync(distSrcPath)) {\n            for (const folder of fs.readdirSync(distSrcPath)) {\n              const source = `dist/src/ui/${folder}`;\n              const target = `dist/${folder}`;\n              fs.cpSync(source, target, { recursive: true, force: true });\n            }\n            fs.rmSync(\"dist/src\", { recursive: true, force: true });\n          }\n        } catch (error) {\n          console.error(\"Error in copy-public-by-entry plugin:\", error);\n        }\n      });\n    },\n  };\n}\n\n/**\n * Logger plugin to track build lifecycle events\n * - Logs build start with framework name\n * - Logs build completion with timing and result status\n * - Logs errors and warnings if any occur\n */\nexport function createLoggerPlugin(\n  frameworkName: string,\n  entryPointsCount: number,\n  isWatchMode: boolean,\n): esbuild.Plugin {\n  let startTime = 0;\n\n  return {\n    name: \"logger\",\n    setup(build) {\n      build.onStart(() => {\n        startTime = Date.now();\n        const mode = isWatchMode ? \"watch\" : \"build\";\n        console.info(\n          `⚙  ${frameworkName}: Starting ${mode} for ${entryPointsCount} app${entryPointsCount !== 1 ? \"s\" : \"\"}...`,\n        );\n      });\n\n      build.onEnd((result) => {\n        const duration = Date.now() - startTime;\n        const hasErrors = result.errors.length > 0;\n        const hasWarnings = result.warnings.length > 0;\n\n        if (hasErrors) {\n          console.error(\n            `✗ ${frameworkName}: Build failed with ${result.errors.length} error${\n              result.errors.length !== 1 ? \"s\" : \"\"\n            } (${duration}ms)`,\n          );\n          return;\n        }\n\n        if (hasWarnings) {\n          console.warn(\n            `⚠ ${frameworkName}: Built with ${result.warnings.length} warning${\n              result.warnings.length !== 1 ? \"s\" : \"\"\n            } (${duration}ms)`,\n          );\n        }\n\n        if (isWatchMode) {\n          console.info(`✓ ${frameworkName}: Watching for changes (${duration}ms)`);\n        } else {\n          console.info(`✓ ${frameworkName}: Build completed (${duration}ms)`);\n        }\n      });\n    },\n  };\n}\n"
  },
  {
    "path": "scripts/build/server.ts",
    "content": "// Development server\n\nimport express from \"express\";\nimport { DEV_SERVER_PORT, DIST_DIR } from \"./config.ts\";\n\n/**\n * Starts a local development server to serve built assets\n * Serves static files from the dist directory\n */\nexport function startDevServer(): void {\n  const app = express();\n\n  app.use(express.static(DIST_DIR));\n\n  app.listen(DEV_SERVER_PORT, () => {\n    console.info(`\\nDevelopment server running at http://localhost:${DEV_SERVER_PORT}`);\n    console.info(\"Watching for changes...\\n\");\n  });\n}\n"
  },
  {
    "path": "scripts/build/steps/cleanup.ts",
    "content": "// Cleanup utilities\n\nimport fs from \"fs\";\nimport path from \"path\";\nimport { DIST_DIR } from \"../config.ts\";\n\n/**\n * Cleans the dist directory, preserving the icons folder\n * Removes all files and folders except the icons directory\n */\nexport function cleanDist(): void {\n  console.info(\"Cleaning old artifacts...\");\n\n  if (!fs.existsSync(DIST_DIR)) {\n    fs.mkdirSync(DIST_DIR, { recursive: true });\n    return;\n  }\n\n  const entries = fs.readdirSync(DIST_DIR);\n\n  for (const entry of entries) {\n    const entryPath = path.join(DIST_DIR, entry);\n\n    // Preserve icons directory\n    if (entry === \"icons\") {\n      continue;\n    }\n\n    fs.rmSync(entryPath, { recursive: true, force: true });\n  }\n}\n"
  },
  {
    "path": "scripts/build/steps/discover.ts",
    "content": "// Entry points discovery\n\nimport fs from \"fs\";\nimport path from \"path\";\nimport { UI_DIR } from \"../config.ts\";\nimport type { EntryPointInfo, FrameworkType } from \"../types.ts\";\n\n/**\n * Supported framework folders\n * These are the expected framework directories in the UI folder\n */\nexport const FRAMEWORK_FOLDERS = [\"react\", \"svelte\", \"vanilla\"] as const;\n\n/**\n * Discovers all application folders within a framework folder\n */\nexport function discoverAppFolders(frameworkFolder: string): string[] {\n  const frameworkPath = path.join(UI_DIR, frameworkFolder);\n\n  if (!fs.existsSync(frameworkPath)) {\n    return [];\n  }\n\n  return fs\n    .readdirSync(frameworkPath)\n    .filter((item) => {\n      const itemPath = path.join(frameworkPath, item);\n      return fs.statSync(itemPath).isDirectory();\n    })\n    .map((appFolder) => path.join(frameworkFolder, appFolder));\n}\n\n/**\n * Discovers entry points for each framework\n * Checks for vanilla (.ts), react (.tsx), and svelte (.svelte) entry points\n */\nexport function discoverEntryPoints(): EntryPointInfo[] {\n  const entryPoints: EntryPointInfo[] = [];\n\n  for (const frameworkFolder of FRAMEWORK_FOLDERS) {\n    const appFolders = discoverAppFolders(frameworkFolder);\n\n    for (const appFolder of appFolders) {\n      const tsxPath = `./src/ui/${appFolder}/index.tsx`;\n      const tsPath = `./src/ui/${appFolder}/index.ts`;\n      const sveltePath = `./src/ui/${appFolder}/index.ts`;\n\n      let framework: FrameworkType;\n      let entryPath: string;\n\n      // Determine framework based on folder name and file extension\n      if (frameworkFolder === \"react\" && fs.existsSync(tsxPath)) {\n        framework = \"react\";\n        entryPath = tsxPath;\n      } else if (frameworkFolder === \"svelte\" && fs.existsSync(sveltePath)) {\n        framework = \"svelte\";\n        entryPath = sveltePath;\n      } else if (frameworkFolder === \"vanilla\" && fs.existsSync(tsPath)) {\n        framework = \"vanilla\";\n        entryPath = tsPath;\n      } else {\n        continue; // Skip if no valid entry point found\n      }\n\n      entryPoints.push({\n        path: entryPath,\n        framework,\n        folder: appFolder,\n      });\n    }\n  }\n\n  return entryPoints;\n}\n\n/**\n * Groups entry points by framework type\n */\nexport function groupEntryPointsByFramework(\n  entryPoints: EntryPointInfo[],\n): Record<FrameworkType, string[]> {\n  const grouped: Record<FrameworkType, string[]> = {\n    react: [],\n    svelte: [],\n    vanilla: [],\n  };\n\n  for (const entry of entryPoints) {\n    grouped[entry.framework].push(entry.path);\n  }\n\n  return grouped;\n}\n"
  },
  {
    "path": "scripts/build/steps/icons.ts",
    "content": "// Icon extraction step\n\nimport fs from \"fs\";\nimport path from \"path\";\n// preact-render-to-string doesn't work here: react-icons emits real React elements\n// ($$typeof: Symbol(react.element)), not Preact VNodes, so we need the real React SSR renderer.\nimport { renderToStaticMarkup } from \"real-react-dom/server\";\nimport { ICONS_DIR, NODE_MODULES_DIR } from \"../config.ts\";\n\n/**\n * Extracts SVG icons from react-icons package and generates TypeScript types\n * This step only runs if the icons directory doesn't exist\n */\nexport async function extractIcons(): Promise<void> {\n  if (fs.existsSync(ICONS_DIR)) {\n    console.info(\"Icons already extracted, skipping...\");\n    return;\n  }\n\n  console.info(\"Extracting SVG Lazy Icons\");\n  console.time(\"Lazy Icons\");\n\n  fs.mkdirSync(ICONS_DIR, { recursive: true });\n\n  let tsFile = \"// This file is generated on build, do not edit.\\nexport type IconName =\";\n  const reactIconsPath = path.join(NODE_MODULES_DIR, \"react-icons\");\n  const entries = fs.readdirSync(reactIconsPath);\n\n  for (const entry of entries) {\n    const entryPath = path.join(reactIconsPath, entry);\n    const isDir = fs.statSync(entryPath).isDirectory();\n\n    if (!isDir || entry === \"lib\") {\n      continue;\n    }\n\n    console.info(`Extracting icon family: ${entry}`);\n\n    const family = await import(`react-icons/${entry}`);\n    for (const [name, ElementConstructor] of Object.entries(family)) {\n      if (typeof ElementConstructor !== \"function\") {\n        continue;\n      }\n      const element = ElementConstructor({ size: \"1em\" });\n      const svg = renderToStaticMarkup(element);\n      if (!svg.startsWith(\"<svg\")) {\n        throw new Error(`Invalid SVG: ${svg}`);\n      }\n      fs.writeFileSync(path.join(ICONS_DIR, `${name}.svg`), svg);\n    }\n\n    tsFile += `\\n  | keyof typeof import(\"react-icons/${entry}\")`;\n  }\n\n  tsFile += \";\\n\";\n  fs.writeFileSync(\"./libs/ui/icons.ts\", tsFile);\n\n  console.timeEnd(\"Lazy Icons\");\n}\n"
  },
  {
    "path": "scripts/build/types.ts",
    "content": "// Build configuration types and interfaces\n\nexport interface BuildArgs {\n  isProd: boolean;\n  serve: boolean;\n}\n\nexport interface BuildContext {\n  isProd: boolean;\n  serve: boolean;\n  appFolders: string[];\n  entryPoints: string[];\n}\n\nexport type FrameworkType = \"react\" | \"svelte\" | \"vanilla\";\n\nexport interface EntryPointInfo {\n  path: string;\n  framework: FrameworkType;\n  folder: string;\n}\n"
  },
  {
    "path": "scripts/build.ts",
    "content": "// Main build orchestrator\n// This file coordinates the build process for Seelen UI applications\n\nimport { parseArgs } from \"./build/config.ts\";\nimport { extractIcons } from \"./build/steps/icons.ts\";\nimport { cleanDist } from \"./build/steps/cleanup.ts\";\nimport { discoverEntryPoints, groupEntryPointsByFramework } from \"./build/steps/discover.ts\";\nimport { buildReact } from \"./build/builders/react.ts\";\nimport { buildSvelte } from \"./build/builders/svelte.ts\";\nimport { buildVanilla } from \"./build/builders/vanilla.ts\";\nimport { startDevServer } from \"./build/server.ts\";\nimport process from \"node:process\";\n\n/**\n * Main build function\n * Orchestrates the entire build process:\n * 1. Parse command-line arguments\n * 2. Extract icons (in parallel with cleaning)\n * 3. Clean dist directory (in parallel with icons)\n * 4. Discover entry points\n * 5. Build all frameworks in parallel (React, Svelte, Vanilla)\n * 6. Start dev server (if --serve flag is set) - serves the entire dist folder\n */\nasync function main() {\n  const args = await parseArgs();\n  console.info(`Build mode: ${args.isProd ? \"production\" : \"development\"}`);\n  console.info(`Serve: ${args.serve ? \"enabled\" : \"disabled\"}\\n`);\n\n  // Step 1 & 2: Extract icons and clean dist directory in parallel\n  await Promise.all([extractIcons(), Promise.resolve(cleanDist())]);\n\n  // Step 3: Discover entry points\n  const entryPoints = discoverEntryPoints();\n  const groupedEntryPoints = groupEntryPointsByFramework(entryPoints);\n\n  console.info(`\\nDiscovered entry points:`);\n  console.info(`  React: ${groupedEntryPoints.react.length}`);\n  console.info(`  Svelte: ${groupedEntryPoints.svelte.length}`);\n  console.info(`  Vanilla: ${groupedEntryPoints.vanilla.length}`);\n  console.info();\n\n  // Collect all app folders for public file copying\n  const allAppFolders = entryPoints.map((entry) => entry.folder);\n\n  // Step 4: Build all frameworks in parallel\n  console.time(\"Total build time\");\n\n  await Promise.all([\n    buildReact(groupedEntryPoints.react, allAppFolders, args),\n    buildSvelte(groupedEntryPoints.svelte, allAppFolders, args),\n    buildVanilla(groupedEntryPoints.vanilla, allAppFolders, args),\n  ]);\n\n  console.timeEnd(\"Total build time\");\n\n  // Step 5: Start dev server if requested (serves entire dist folder)\n  if (args.serve) {\n    startDevServer();\n  }\n\n  console.info(\"\\n✓ Build complete!\\n\");\n}\n\n// Run the build\nmain().catch((error) => {\n  console.error(\"Build failed:\", error);\n  process.exit(1);\n});\n"
  },
  {
    "path": "scripts/bundle.msix.ts",
    "content": "import { execSync } from \"child_process\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nimport packageJson from \"../package.json\";\nimport process from \"node:process\";\n\nasync function getArgs() {\n  const argv = await yargs(hideBin(process.argv)).option(\"target\", {\n    type: \"string\",\n    description: \"target to get the files from\",\n    default: \"release\",\n  }).argv;\n  return {\n    target: argv.target,\n  };\n}\n\nconst [major, minor, patch, pre, _build_number] = packageJson.version.split(/[\\.\\+\\-]/);\nif (major === undefined || minor === undefined || patch === undefined) {\n  throw new Error(\"Invalid package version\");\n}\n\nconst { target } = await getArgs();\n\n// Determine architecture for MSIX naming\nconst archMap: Record<string, string> = {\n  \"x86_64-pc-windows-msvc\": \"x64\",\n  \"aarch64-pc-windows-msvc\": \"arm64\",\n};\nconst arch = archMap[target] || \"x64\";\n\nconsole.info(`Building MSIX for ${arch}...`);\nconst buildFolder = `target/${target}/release/msix`;\nconst bundleFolder = `target/${target}/release/bundle/msix`;\n\nfs.rmSync(buildFolder, { recursive: true, force: true }); // clean up\nfs.mkdirSync(buildFolder, { recursive: true });\nfs.mkdirSync(bundleFolder, { recursive: true });\n\n// we skip revision here because greater numbers than 65535 are not supported on msix\nconst appxPackageVersion = `${major}.${minor}.${patch}.0`;\nconst fileVersion = pre ? packageJson.version : appxPackageVersion;\nconst installer_msix_path = path.resolve(`${bundleFolder}/Seelen.UI_${fileVersion}_${arch}.msix`);\n\n// Add manifest\nconst manifest = fs\n  .readFileSync(\"src/templates/AppxManifest.xml\", \"utf-8\")\n  .replace(\"{{version}}\", appxPackageVersion)\n  .replace(\"{{architecture}}\", arch);\nfs.writeFileSync(`${buildFolder}/AppxManifest.xml`, manifest);\n\n// Add binaries\nfs.copyFileSync(`target/${target}/release/seelen-ui.exe`, `${buildFolder}/seelen-ui.exe`);\nfs.copyFileSync(`target/${target}/release/slu-service.exe`, `${buildFolder}/slu-service.exe`);\n\n// add pdb files if debug\nif (pre || target === \"./\") {\n  fs.copyFileSync(`target/${target}/release/seelen_ui.pdb`, `${buildFolder}/seelen_ui.pdb`);\n}\n\n// dlls\nfs.copyFileSync(`target/${target}/release/sluhk.dll`, `${buildFolder}/sluhk.dll`);\n\n// integrity files\nfs.copyFileSync(`target/${target}/release/SHA256SUMS`, `${buildFolder}/SHA256SUMS`);\nfs.copyFileSync(`target/${target}/release/SHA256SUMS.sig`, `${buildFolder}/SHA256SUMS.sig`);\n\n// Add resources\nfs.cpSync(\"src/static\", `${buildFolder}/static`, { recursive: true });\n\ntry {\n  // create installer bundle\n  let out = execSync(`msixHeroCli pack -d ${buildFolder} -p ${installer_msix_path}`);\n  console.info(out.toString());\n\n  // sign installer with local certificate (this is for testing only) store changes the cert in the windows store\n  let out2 = execSync(\n    `msixHeroCli sign -f ./.cert/Seelen.pfx -p Seelen -t http://time.certum.pl ${installer_msix_path}`,\n  );\n  console.info(out2.toString());\n} catch (error) {\n  console.error(\"\\n\\n\", error?.toString());\n  process.exit(1);\n}\n"
  },
  {
    "path": "scripts/clean.ps1",
    "content": "Remove-Item -Force -Recurse .\\node_modules\nRemove-Item -Force -Recurse .\\dist\nRemove-Item -Force -Recurse .\\libs\\core\\gen\nRemove-Item -Force -Recurse .\\libs\\core\\npm\n"
  },
  {
    "path": "scripts/submission.json",
    "content": "{\n  \"packageParameters\": {\n    \"DisableAutoPackageNameFormatting\": true\n  },\n  \"appSubmission\": {\n    \"appId\": \"9P67C2D4T9FB\"\n  }\n}\n"
  },
  {
    "path": "scripts/translate/mod.ps1",
    "content": "# Script specifically for i18n folders\n$rootPath = \"./src/static\"\n$allPaths = @()\n\n# Find all i18n folders recursively\n$i18nFolders = Get-ChildItem -Path $rootPath -Directory -Recurse -Filter \"i18n\"\n\nif ($i18nFolders.Count -eq 0) {\n    Write-Host \"No 'i18n' folders found\" -ForegroundColor Red\n    exit\n}\n\n# Process each i18n folder and collect file paths\n$i18nFolders | ForEach-Object {\n    $i18nFolder = $_\n    $i18nPath = $i18nFolder.FullName\n\n    # Get all files inside the i18n folder\n    Get-ChildItem -Path $i18nPath -File | ForEach-Object {\n        $allPaths += $_.FullName\n    }\n}\n\n# Display summary\nWrite-Host \"`n📁 i18n folders found: $($i18nFolders.Count)\" -ForegroundColor Green\nWrite-Host \"📄 Total files in i18n: $($allPaths.Count)\" -ForegroundColor Cyan\n\n# Process each file with the translation tool\nWrite-Host \"`n🚀 Starting translation process...\" -ForegroundColor Yellow\n\n$allPaths | ForEach-Object {\n    $path = $_\n    Write-Host \"\"\n    Write-Host \"🔧 Processing: $path\" -ForegroundColor Magenta\n    &\".\\target\\debug\\seelen-ui.exe\" resource translate $path\n}\n\nWrite-Host \"`n✅ Translation process completed!\" -ForegroundColor Green"
  },
  {
    "path": "scripts/translate/mod.ts",
    "content": "import { GoogleTranslator, ObjectTranslator } from \"@seelen/translation-toolkit\";\nimport { SupportedLanguages } from \"@seelen-ui/lib\";\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport yaml from \"js-yaml\";\n\nconst translator = new GoogleTranslator({ source: \"en\" });\n\nconst targetLanguages = SupportedLanguages.filter((lang) => lang.value !== \"en\");\n\nfunction deepSortObject<T>(obj: T): T {\n  if (Array.isArray(obj)) {\n    // if it's an array, recursively sort its elements\n    return obj.map(deepSortObject) as unknown as T;\n  } else if (obj !== null && typeof obj === \"object\") {\n    // if it's an object, sort its entries\n    const sortedEntries = Object.entries(obj)\n      .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) // Sort keys\n      .map(([key, value]) => [key, deepSortObject(value)]); // Recursively sort values\n\n    return Object.fromEntries(sortedEntries) as T;\n  }\n  // if it's not an array or object, return it as is\n  return obj;\n}\n\nasync function completeTranslationsFor(localesDir: string) {\n  const enPath = `${localesDir}/en.yml`;\n  const strYaml = readFileSync(enPath, \"utf8\");\n  const en = deepSortObject(yaml.load(strYaml) as object);\n  writeFileSync(enPath, yaml.dump(en)); // overwrite sorted\n\n  const yamlTranslator = new ObjectTranslator(en, translator);\n\n  for (const targetLang of targetLanguages) {\n    const filePath = `${localesDir}/${targetLang.value}.yml`;\n\n    let translation: any = {};\n    if (existsSync(filePath)) {\n      translation = yaml.load(readFileSync(filePath, \"utf8\"));\n    }\n\n    const translated = await yamlTranslator.translate_to(targetLang.value, translation);\n    writeFileSync(filePath, yaml.dump(deepSortObject(translated)));\n  }\n}\n\nawait completeTranslationsFor(\"src/ui/react/toolbar/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/react/weg/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/react/settings/i18n/translations\");\n\nawait completeTranslationsFor(\"src/ui/svelte/wallpaper_manager/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/power-menu/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/bluetooth-popup/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/network-popup/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/apps-menu/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/keyboard-selector/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/user-menu/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/calendar-popup/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/media-popup/i18n/translations\");\nawait completeTranslationsFor(\"src/ui/svelte/notifications/i18n/translations\");\n\nawait completeTranslationsFor(\"src/background/i18n\");\n"
  },
  {
    "path": "scripts/versionish.ts",
    "content": "import fs from \"fs\";\nimport process from \"node:process\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { execSync } from \"child_process\";\n\nfunction updateCargoVersion(filePath: string, version: string): void {\n  let content = fs.readFileSync(filePath, \"utf-8\");\n  content = content.replace(/^version\\s*=\\s*\".*\"/m, `version = \"${version}\"`);\n  fs.writeFileSync(filePath, content);\n}\n\nfunction updateJsonVersion(filePath: string, version: string): void {\n  const json = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\n  json.version = version;\n  fs.writeFileSync(filePath, JSON.stringify(json, null, 2));\n}\n\nfunction updateChangelog(version: string, forceUpdate = false): void {\n  if (version.includes(\"-\") && !forceUpdate) {\n    console.log(\"Skipping changelog update for pre-release version\");\n    return;\n  }\n\n  let content = fs.readFileSync(\"changelog.md\", \"utf-8\");\n  content = content.replace(\"# Changelog\", `# Changelog\\n\\n## [${version}]`);\n  fs.writeFileSync(\"changelog.md\", content);\n  console.log(`✓ Changelog updated for version ${version}`);\n}\n\nfunction replaceChangelogVersion(oldVersion: string, newVersion: string): void {\n  let content = fs.readFileSync(\"changelog.md\", \"utf-8\");\n  content = content.replace(`## [${oldVersion}]`, `## [${newVersion}]`);\n  fs.writeFileSync(\"changelog.md\", content);\n  console.log(`✓ Changelog version updated from ${oldVersion} to ${newVersion}`);\n}\n\nfunction createGitCommit(message: string): void {\n  execSync(\"git add .\", { stdio: \"inherit\" });\n  execSync(`git commit -m \"${message}\"`, { stdio: \"inherit\" });\n  console.log(`✓ Git commit created: ${message}`);\n}\n\nfunction createGitTag(tag: string): void {\n  execSync(`git tag ${tag}`, { stdio: \"inherit\" });\n  console.log(`✓ Git tag created: ${tag}`);\n}\n\nfunction updateAllVersions(version: string, skipLockfiles = false): void {\n  // Update library versions\n  console.log(\"Updating library versions...\");\n  updateCargoVersion(\"./libs/core/Cargo.toml\", version);\n  updateJsonVersion(\"./libs/core/deno.json\", version);\n  console.log(\"✓ Library versions updated\");\n\n  // Update app versions\n  console.log(\"Updating app versions...\");\n  updateCargoVersion(\"./src/Cargo.toml\", version);\n  const packageJson = JSON.parse(fs.readFileSync(\"./package.json\", \"utf-8\"));\n  packageJson.version = version;\n  fs.writeFileSync(\"./package.json\", JSON.stringify(packageJson, null, 2) + \"\\n\");\n  console.log(\"✓ App versions updated\");\n\n  // Update lockfiles (skip in CI)\n  if (!skipLockfiles) {\n    execSync(\"cargo check\", { stdio: \"inherit\" });\n    execSync(\"npm install\", { stdio: \"inherit\" });\n  } else {\n    console.log(\"⊘ Skipping lockfile updates (CI mode)\");\n  }\n}\n\nasync function main(args: string[]) {\n  await yargs(hideBin(args))\n    .version(false)\n    .command(\n      \"start <version>\",\n      \"Start the development of a new release\",\n      (yargs) => {\n        return yargs.positional(\"version\", {\n          type: \"string\",\n          describe: \"Version to start\",\n          demandOption: true,\n        });\n      },\n      ({ version }) => {\n        console.log(`Starting release with version: ${version}`);\n\n        updateAllVersions(version);\n        updateChangelog(`${version}-dev`, true);\n\n        console.log(`\\n✓ Version ${version} set successfully`);\n\n        createGitCommit(`chore(release): start v${version}`);\n      },\n    )\n    .command(\n      \"ci <version>\",\n      \"Set version for CI builds (no git commit)\",\n      (yargs) => {\n        return yargs.positional(\"version\", {\n          type: \"string\",\n          describe: \"Version to set\",\n          demandOption: true,\n        });\n      },\n      ({ version }) => {\n        console.log(`Setting version for CI: ${version}`);\n\n        updateAllVersions(version, true);\n\n        console.log(`\\n✓ Version ${version} set successfully (no commit)`);\n      },\n    )\n    .command(\n      \"finish\",\n      \"Finish the current nightly release\",\n      () => {},\n      () => {\n        const packageJson = JSON.parse(fs.readFileSync(\"./package.json\", \"utf-8\"));\n        const currentVersion = packageJson.version;\n\n        console.log(`Finishing release: ${currentVersion}`);\n\n        replaceChangelogVersion(`${currentVersion}-dev`, currentVersion);\n\n        console.log(`\\n✓ Version ${currentVersion} set successfully`);\n\n        createGitCommit(`chore(release): finish v${currentVersion}`);\n        createGitTag(`v${currentVersion}`);\n      },\n    )\n    .demandCommand(1, \"You must provide a command (start, ci, or finish)\")\n    .help().argv;\n}\n\nmain(process.argv);\n"
  },
  {
    "path": "src/Cargo.toml",
    "content": "[package]\nname = \"seelen-ui\"\nversion = \"2.5.7\"\ndescription = \"Seelen UI Background\"\nauthors = [\"eythaann\"]\nlicense = \"AGPL-3.0-or-later\"\nrepository = \"https://github.com/eythaann/seelen-ui.git\"\ndefault-run = \"seelen-ui\"\nedition = \"2021\"\nrust-version = \"1.89\"\n\n[[bin]]\nname = \"seelen-ui\"\npath = \"background/main.rs\"\n\n[[bin]]\nname = \"slu-service\"\npath = \"service/main.rs\"\n\n[build-dependencies]\ntauri-build = { workspace = true, features = [] }\nslu-utils = { workspace = true }\n\n[dependencies]\nslu-ipc = { workspace = true }\nslu-utils = { workspace = true }\npositioning = { workspace = true }\ntauri = { workspace = true, features = [\"devtools\", \"protocol-asset\"] }\ntauri-plugin-fs = { workspace = true }\ntauri-plugin-dialog = { workspace = true }\ntauri-plugin-shell = { workspace = true }\ntauri-plugin-process = { workspace = true }\ntauri-plugin-updater = { workspace = true }\ntauri-plugin-deep-link = { workspace = true }\ntauri-plugin-http = { workspace = true }\nserde = { workspace = true, features = [\"derive\"] }\nserde_json = { workspace = true }\nserde_yaml = { workspace = true }\nlazy_static = { workspace = true }\nparking_lot = { workspace = true }\nlog = { workspace = true }\nuuid = { workspace = true }\nimage = { workspace = true, features = [\"ico\"] }\nwidestring = { workspace = true }\nitertools = { workspace = true }\nclap = { workspace = true, features = [\"derive\", \"string\"] }\nos_info = { workspace = true }\ncrossbeam-channel = { workspace = true }\nregex = { workspace = true }\nphf = { workspace = true }\nsysinfo = { workspace = true }\nbattery = { workspace = true }\nwinreg = { workspace = true }\nwindows-core = { workspace = true } # windows-rs already depends and reexports this, but we need it as a direct dependency (implement macro)\nwindows-future = { workspace = true }\nwin-screenshot = { workspace = true }\nbase64 = { workspace = true }\narc-swap = { workspace = true }\nnotify-debouncer-full = { workspace = true }\nencoding_rs = { workspace = true }\nevalexpr = { workspace = true }\nquick-xml = { workspace = true, features = [\"serialize\", \"encoding\"] }\nbacktrace = { workspace = true }\nowo-colors = { workspace = true }\nrust-i18n = { workspace = true }\nurl = { workspace = true }\nurlencoding = { workspace = true }\nfern = { workspace = true }\nreqwest = { workspace = true }\ntranslators = { workspace = true, features = [\"google\", \"tokio-async\"] }\nrand = { workspace = true }\ndiscord-rich-presence = { workspace = true }\nwin-hotkeys = { workspace = true }\nwalkdir = { workspace = true }\ntokio = { workspace = true }\nseelen-core = { workspace = true }\nwmi = { workspace = true }\nwindows = { workspace = true, features = [\n    \"Win32_Foundation\",\n    \"ApplicationModel\",\n    \"ApplicationModel_Core\",\n    \"ApplicationModel_Background\",\n    \"Foundation_Collections\",\n    \"Foundation_Numerics\",\n    \"Management_Deployment\",\n    \"Win32_Globalization\",\n    \"Win32_UI_Input_KeyboardAndMouse\",\n    \"Win32_UI_Input_Ime\",\n    \"Win32_UI_WindowsAndMessaging\",\n    \"Win32_UI_TextServices\",\n    \"Win32_UI_Shell\",\n    \"Win32_UI_Shell_Common\",\n    \"Win32_UI_Shell_PropertiesSystem\",\n    \"Win32_UI_Accessibility\",\n    \"Win32_UI_Controls\",\n    \"Win32_UI_Notifications\",\n    \"Win32_Graphics_Dwm\",\n    \"Win32_Graphics_Gdi\",\n    \"Win32_UI_HiDpi\",\n    \"Win32_System_DataExchange\",\n    \"Win32_System_Ole\",\n    \"Win32_System_Com\",\n    \"Win32_System_Com_StructuredStorage\",\n    \"Win32_System_Variant\",\n    \"Win32_System_LibraryLoader\",\n    \"Win32_System_SystemServices\",\n    \"Win32_System_SystemInformation\",\n    \"Win32_System_Console\",\n    \"Win32_System_Registry\",\n    \"Win32_System_Environment\",\n    \"Win32_Security\",\n    \"Win32_Security_Authorization\",\n    \"Win32_System_Kernel\",\n    \"Win32_System_IO\",\n    \"Win32_System_Services\",\n    \"Win32_System_EventLog\",\n    \"Win32_System_TaskScheduler\",\n    \"Win32_System_Threading\",\n    \"Win32_System_WinRT\",\n    \"Win32_System_StationsAndDesktops\",\n    \"Win32_System_RemoteDesktop\",\n    \"Win32_System_ProcessStatus\",\n    \"Wdk_System_Threading\",\n    \"Wdk_System_SystemInformation\",\n    \"Wdk_System_SystemServices\",\n    \"Win32_System_Power\",\n    \"Win32_System_Shutdown\",\n    \"Win32_System_Diagnostics\",\n    \"Win32_System_Diagnostics_Debug\",\n    \"Win32_System_Memory\",\n    \"Win32_Storage_FileSystem\",\n    \"Win32_Storage_EnhancedStorage\",\n    \"Win32_Storage_Packaging_Appx\",\n    \"Win32_Security_Authentication_Identity\",\n    \"Win32_Media_KernelStreaming\",\n    \"Win32_Media_Audio_Endpoints\",\n    \"Win32_Media_Audio_Apo\",\n    \"Win32_Media_DeviceManager\",\n    \"Graphics\",\n    \"Graphics_Display\",\n    \"Media\",\n    \"Media_Audio\",\n    \"Media_Render\",\n    \"Media_Capture\",\n    \"Media_Effects\",\n    \"Media_Control\",\n    \"Storage_Streams\",\n    \"Win32_Devices_Display\",\n    \"Win32_Devices_DeviceAndDriverInstallation\",\n    \"Win32_Devices_FunctionDiscovery\",\n    \"UI_Core\",\n    \"UI_Notifications_Management\",\n    \"UI_ViewManagement\",\n    \"UI_StartScreen\",\n    \"Win32_NetworkManagement_IpHelper\",\n    \"Win32_NetworkManagement_Ndis\",\n    \"Win32_Networking_WinSock\",\n    \"Win32_Networking_NetworkListManager\",\n    \"Win32_NetworkManagement_WiFi\",\n    \"Networking_NetworkOperators\",\n    \"Networking_Connectivity\",\n    \"Security_Credentials\",\n    \"Devices_Custom\",\n    \"Devices_Bluetooth\",\n    \"Devices_Bluetooth_Rfcomm\",\n    \"Devices_Bluetooth_GenericAttributeProfile\",\n    \"Devices_Enumeration\",\n    \"Devices_Radios\",\n    \"Devices_WiFi\",\n    \"Devices_Display\",\n    \"Devices_Display_Core\",\n    \"Data_Xml_Dom\",\n] }\ntime = { workspace = true, features = [\"formatting\", \"local-offset\"] }\nscc = { workspace = true }\nopen = \"5.3.3\"\nfutures = { workspace = true }\n\n[features]\n# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.\n# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.\ncustom-protocol = [\"tauri/custom-protocol\"]\n"
  },
  {
    "path": "src/background/app.rs",
    "content": "use std::sync::{\n    atomic::{AtomicBool, Ordering},\n    Arc, LazyLock,\n};\n\nuse parking_lot::Mutex;\nuse seelen_core::system_state::MonitorId;\nuse slu_ipc::messages::SvcAction;\nuse tauri::{AppHandle, Emitter, Wry};\nuse windows::Win32::System::TaskScheduler::{ITaskService, TaskScheduler};\n\nuse crate::{\n    app_instance::LegacyWidgetMonitorContainer,\n    cli::ServicePipe,\n    error::{Result, ResultLogExt},\n    hook::register_win_hook,\n    log_error,\n    migrations::Migrations,\n    modules::{\n        monitors::{MonitorManager, MonitorManagerEvent},\n        system_settings::application::{SystemSettings, SystemSettingsEvent},\n    },\n    state::application::{FullState, FULL_STATE},\n    trace_lock,\n    utils::discord::start_discord_rpc,\n    widgets::{wallpaper_manager::SeelenWall, weg::SeelenWeg},\n    windows_api::{\n        event_window::{create_background_window, IS_INTERACTIVE_SESSION},\n        Com,\n    },\n    APP_HANDLE,\n};\n\npub static SEELEN: LazyLock<Arc<Mutex<Seelen>>> =\n    LazyLock::new(|| Arc::new(Mutex::new(Seelen::default())));\n\nstatic SEELEN_IS_RUNNING: AtomicBool = AtomicBool::new(false);\n\n/// Tauri app handle\npub fn get_app_handle<'a>() -> &'a AppHandle<Wry> {\n    APP_HANDLE\n        .get()\n        .expect(\"get_app_handle called but app is still not initialized\")\n}\n\npub fn emit_to_webviews<S>(event: &str, payload: S)\nwhere\n    S: serde::Serialize + Clone,\n{\n    // log::trace!(\"Emitting {event} to webviews\");\n    if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n        // log::debug!(\"Skipping event {event} because session is not active\");\n        return;\n    }\n    get_app_handle().emit(event, payload).log_error();\n}\n\n/** Struct should be initialized first before calling any other methods */\n#[derive(Default)]\npub struct Seelen {\n    pub widgets_per_display: Vec<LegacyWidgetMonitorContainer>,\n    pub wall: Option<SeelenWall>,\n}\n\n/* ============== Getters ============== */\nimpl Seelen {\n    pub fn instances_mut(&mut self) -> &mut Vec<LegacyWidgetMonitorContainer> {\n        &mut self.widgets_per_display\n    }\n\n    pub fn is_running() -> bool {\n        SEELEN_IS_RUNNING.load(std::sync::atomic::Ordering::Acquire)\n    }\n}\n\n/* ============== Methods ============== */\nimpl Seelen {\n    fn add_wall(&mut self) -> Result<()> {\n        if self.wall.is_none() {\n            let wall = SeelenWall::new()?;\n            log_error!(wall.update_position());\n            self.wall = Some(wall)\n        }\n        Ok(())\n    }\n\n    fn refresh_windows_positions(&mut self) -> Result<()> {\n        if let Some(wall) = &self.wall {\n            wall.update_position()?;\n        }\n        for instance in &mut self.widgets_per_display {\n            instance.ensure_positions()?;\n        }\n        Ok(())\n    }\n\n    pub fn on_settings_change(&mut self, state: &FullState) -> Result<()> {\n        rust_i18n::set_locale(state.locale());\n        ServicePipe::request(SvcAction::SetSettings(Box::new(state.settings.clone())))?;\n\n        if state.is_weg_enabled() {\n            SeelenWeg::hide_native_taskbar();\n        } else {\n            SeelenWeg::restore_native_taskbar()?;\n        }\n\n        match state.is_wall_enabled() {\n            true => self.add_wall()?,\n            false => self.wall = None,\n        }\n\n        for monitor in &mut self.widgets_per_display {\n            monitor.load_settings(state)?;\n        }\n\n        self.refresh_windows_positions()?;\n        Ok(())\n    }\n\n    fn on_monitor_event(event: MonitorManagerEvent) {\n        let mut guard = trace_lock!(SEELEN);\n        match event {\n            MonitorManagerEvent::ViewAdded(id) => {\n                log_error!(guard.add_monitor(id));\n            }\n            MonitorManagerEvent::ViewsChanged => {\n                log_error!(guard.refresh_windows_positions());\n            }\n            MonitorManagerEvent::ViewRemoved(id) => {\n                log_error!(guard.remove_monitor(&id));\n            }\n        }\n    }\n\n    fn on_system_settings_change(event: SystemSettingsEvent) {\n        if event == SystemSettingsEvent::TextScaleChanged {\n            log_error!(trace_lock!(SEELEN).refresh_windows_positions());\n        }\n    }\n\n    pub fn start(&mut self) -> Result<()> {\n        Migrations::run()?;\n\n        let state = FULL_STATE.load();\n        rust_i18n::set_locale(state.locale());\n\n        if state.is_wall_enabled() {\n            self.add_wall()?;\n        }\n\n        log::trace!(\"Enumerating Monitors & Creating Instances\");\n        for view in MonitorManager::instance().read_all_views()? {\n            self.add_monitor(view.primary_target()?.stable_id()?)?;\n        }\n\n        self.refresh_windows_positions()?;\n\n        create_background_window()?;\n        register_win_hook()?;\n        MonitorManager::subscribe(Self::on_monitor_event);\n        SystemSettings::subscribe(Self::on_system_settings_change);\n\n        start_discord_rpc()?;\n        ServicePipe::request(SvcAction::SetSettings(Box::new(state.settings.clone())))?;\n        SEELEN_IS_RUNNING.store(true, std::sync::atomic::Ordering::SeqCst);\n        Ok(())\n    }\n\n    /// Stop and release all resources\n    pub fn stop(&self) {\n        SEELEN_IS_RUNNING.store(false, std::sync::atomic::Ordering::SeqCst);\n    }\n\n    fn add_monitor(&mut self, monitor_id: MonitorId) -> Result<()> {\n        let state = FULL_STATE.load();\n        self.widgets_per_display\n            .push(LegacyWidgetMonitorContainer::new(monitor_id, &state)?);\n        self.refresh_windows_positions()?;\n        Ok(())\n    }\n\n    fn remove_monitor(&mut self, id: &MonitorId) -> Result<()> {\n        self.widgets_per_display\n            .retain(|m| &m.view_primary_target_id != id);\n        self.refresh_windows_positions()?;\n        Ok(())\n    }\n\n    pub fn is_auto_start_enabled() -> Result<bool> {\n        Com::run_with_context(|| unsafe {\n            let task_service: ITaskService = Com::create_instance(&TaskScheduler)?;\n            task_service.Connect(\n                &Default::default(),\n                &Default::default(),\n                &Default::default(),\n                &Default::default(),\n            )?;\n            let is_task_enabled = task_service\n                .GetFolder(&\"\\\\Seelen\".into())\n                .and_then(|folder| folder.GetTask(&\"Seelen UI Service\".into()))\n                .and_then(|task| task.Definition())\n                .and_then(|definition| definition.Triggers())\n                .and_then(|triggers| triggers.get_Item(1))\n                .is_ok();\n            Ok(is_task_enabled)\n        })\n    }\n\n    pub fn set_auto_start(enabled: bool) -> Result<()> {\n        ServicePipe::request(SvcAction::SetStartup(enabled))\n    }\n}\n"
  },
  {
    "path": "src/background/app_instance.rs",
    "content": "use seelen_core::system_state::MonitorId;\n\nuse crate::{\n    error::Result,\n    modules::monitors::MonitorManager,\n    state::application::FullState,\n    widgets::{\n        manager::WIDGET_MANAGER, toolbar::FancyToolbar, weg::SeelenWeg,\n        window_manager::instance::WindowManagerV2,\n    },\n};\n\n/// This struct stores the widgets for a display view\npub struct LegacyWidgetMonitorContainer {\n    // the primary target id of the display view for this container was created\n    pub view_primary_target_id: MonitorId,\n    // legacy widgets\n    pub toolbar: Option<FancyToolbar>,\n    pub weg: Option<SeelenWeg>,\n    pub wm: Option<WindowManagerV2>,\n}\n\nimpl LegacyWidgetMonitorContainer {\n    pub fn new(view_primary_target_id: MonitorId, settings: &FullState) -> Result<Self> {\n        let mut instance = Self {\n            view_primary_target_id,\n            toolbar: None,\n            weg: None,\n            wm: None,\n        };\n        instance.load_settings(settings)?;\n        instance.ensure_positions()?;\n        Ok(instance)\n    }\n\n    pub fn ensure_positions(&mut self) -> Result<()> {\n        let monitor = MonitorManager::instance()\n            .get_display_view_for_target(&self.view_primary_target_id)?\n            .as_win32_view()?;\n\n        if let Some(bar) = &mut self.toolbar {\n            bar.set_position(&monitor)?;\n        }\n        if let Some(weg) = &mut self.weg {\n            weg.set_position(&monitor)?;\n        }\n        if let Some(wm) = &mut self.wm {\n            wm.set_position(&monitor)?;\n            WindowManagerV2::force_retiling()?;\n        }\n        Ok(())\n    }\n\n    fn add_toolbar(&mut self) -> Result<()> {\n        if self.toolbar.is_none() {\n            self.toolbar = Some(FancyToolbar::new(&self.view_primary_target_id)?);\n        }\n        Ok(())\n    }\n\n    fn add_weg(&mut self) -> Result<()> {\n        if self.weg.is_none() {\n            self.weg = Some(SeelenWeg::new(&self.view_primary_target_id)?);\n        }\n        Ok(())\n    }\n\n    fn add_wm(&mut self) -> Result<()> {\n        if self.wm.is_none() {\n            self.wm = Some(WindowManagerV2::new(&self.view_primary_target_id)?)\n        }\n        Ok(())\n    }\n\n    pub fn load_settings(&mut self, state: &FullState) -> Result<()> {\n        if state.is_bar_enabled_on_monitor(&self.view_primary_target_id) {\n            self.add_toolbar()?;\n        } else {\n            self.toolbar = None;\n        }\n\n        if state.is_weg_enabled_on_monitor(&self.view_primary_target_id) {\n            self.add_weg()?;\n        } else {\n            self.weg = None;\n        }\n\n        if state.is_window_manager_enabled_on_monitor(&self.view_primary_target_id) {\n            self.add_wm()?;\n        } else {\n            self.wm = None;\n        }\n\n        WIDGET_MANAGER.refresh()?;\n        Ok(())\n    }\n}\n\nunsafe impl Send for LegacyWidgetMonitorContainer {}\nunsafe impl Sync for LegacyWidgetMonitorContainer {}\n"
  },
  {
    "path": "src/background/cli/application/art.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Copy, Serialize, Deserialize, clap::ValueEnum)]\npub enum ArtVariant {\n    SeelenLogo,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, clap::Args)]\npub struct ArtCli {\n    variant: ArtVariant,\n}\n\nimpl ArtCli {\n    pub fn process_direct(self) {\n        match self.variant {\n            ArtVariant::SeelenLogo => {\n                println!(\"{SEELEN_LOGO_ASCII}\");\n            }\n        }\n    }\n}\n\nstatic SEELEN_LOGO_ASCII: &str = r#\"\n                   .      .&     _,x&\"``\n                    & .   &'  ;.&&'\n              &.  . &.&     .0&&&;&\"\"`\n         .    '&  &.&&&  .&&&&&'\n       .&         ;&&& &&&&&'\n      &&          &&&&&&&&     &&&\n     0&    .     &&&&&&&&\"\"\n    &&   .0     &&&&&&&\n   0&& .&'     &&&&&&\n  :&&&&&    . &&&&& \n  0&&&&    & &&&&&\n  &&&&'   &&&&&&&               .&&&x&\n  &&&&   :&&&&&0.&'        , .&&&&&&&&&&;.\n  &&&&.  &&&&&&&&        .&&&&&&&&&&'               .\n  0&&&&  &&&&&&&       ,&&&&&&&&&&&&                &\n  :&&&&; &&&&&0       ,;&&&&&&&&&&&             ;  .0\n   0&&&&&&&&&&0     ,;&&&&&&&&&&&&&             &  &;\n    0&&&&&&&&&&0   :',;\".&&&&&&\".&             && &0\n     0&&&&&&&&&0  ',;',&&&&&\" ,&'             &&&&0\n      0&&&&&&&&&0 ,x&&&&\" .&&&              &&&&0\n        0&&&&&& .&&&&\"'''\"&&\"&&            &&&&&0\n         0&& .&&;``       `&: :&         &&&&&&0\n            &\"' &&&&&&&&   &\"& &\"&   &&&&&&&&0\n              0&&&&&&&&&&&&&&&&&&&&&&&&&0\n                 0&&&&&&&&&&&&&&&&&&&0\n                      0&&&&&&&&&0         Seelen Corp\n\"#;\n"
  },
  {
    "path": "src/background/cli/application/debugger.rs",
    "content": "use std::sync::atomic::Ordering;\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::{error::Result, hook::LOG_WIN_EVENTS, utils::TRACE_LOCK_ENABLED};\n\n/// Debugger cli\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\npub struct DebuggerCli {\n    #[command(subcommand)]\n    subcommand: SubCommand,\n}\n\n#[derive(Debug, Serialize, Deserialize, clap::Subcommand)]\nenum SubCommand {\n    /// Toggles the tracing of window events\n    ToggleWinEvents,\n    /// Toggles the tracing of mutex lock\n    ToggleTraceLock,\n}\n\nimpl DebuggerCli {\n    pub fn process(self) -> Result<()> {\n        match self.subcommand {\n            SubCommand::ToggleWinEvents => {\n                LOG_WIN_EVENTS.store(!LOG_WIN_EVENTS.load(Ordering::Acquire), Ordering::Release);\n            }\n            SubCommand::ToggleTraceLock => {\n                TRACE_LOCK_ENABLED.store(\n                    !TRACE_LOCK_ENABLED.load(Ordering::Acquire),\n                    Ordering::Release,\n                );\n            }\n        };\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/cli/application/mod.rs",
    "content": "mod art;\nmod debugger;\nmod uri;\nmod win32;\n\nuse std::sync::atomic::Ordering;\n\nuse clap::Parser;\nuse debugger::DebuggerCli;\nuse slu_ipc::{messages::AppMessage, AppIpc};\nuse win32::Win32Cli;\nuse windows::Win32::System::Console::{AttachConsole, GetConsoleWindow, ATTACH_PARENT_PROCESS};\n\nuse crate::{\n    cli::application::{art::ArtCli, uri::process_uri},\n    error::Result,\n    resources::cli::ResourceManagerCli,\n    virtual_desktops::cli::VirtualDesktopCli,\n    widgets::{\n        cli::WidgetCli, popups::cli::PopupsCli, show_settings,\n        task_switcher::cli::TaskSwitcherClient, wallpaper_manager::cli::WallpaperCli,\n        weg::cli::WegCli, window_manager::cli::WindowManagerCli,\n    },\n};\n\n/// Defines how a CLI command should be executed\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum CommandExecutionMode {\n    /// Command executes directly in the console that invokes it\n    Direct,\n    /// Command is sent to the main Seelen UI instance via IPC\n    MainInstance,\n}\n\n/// Trait for CLI commands to define their execution mode\npub trait SluCliCommand {\n    /// Returns the execution mode for this command.\n    /// Default implementation returns MainInstance.\n    fn execution_mode(&self) -> CommandExecutionMode {\n        CommandExecutionMode::MainInstance\n    }\n}\n\n/// Determines how the CLI invocation should be handled\n#[derive(Debug)]\nenum CliRoutingStrategy {\n    /// Execute command directly in this process and exit (e.g., Win32 utils, Bundle, Translate)\n    ExecuteDirect,\n    /// Send command to main instance via IPC and exit (e.g., Settings, VirtualDesk, Load/Unload)\n    /// The main instance will re-parse the args and execute the command\n    RedirectToMainInstance,\n    /// No command to execute, continue with normal app startup\n    StartApp,\n}\n\n/// Seelen Command Line Interface\n#[derive(Debug, clap::Parser)]\n#[command(version, name = \"Seelen UI\")]\npub struct AppCli {\n    /// Unused flag\n    #[arg(long, default_value_t)]\n    silent: bool,\n    /// Prints some extra information on the console.\n    #[arg(long, default_value_t)]\n    verbose: bool,\n    /// Path or URI to load.\n    uri: Option<String>,\n    #[command(subcommand)]\n    command: Option<AppCliCommand>,\n}\n\n#[derive(Debug, clap::Subcommand)]\npub enum AppCliCommand {\n    /// Opens the Seelen settings gui.\n    Settings,\n    VirtualDesk(VirtualDesktopCli),\n    Debugger(DebuggerCli),\n    WindowManager(WindowManagerCli),\n    Popup(PopupsCli),\n    Weg(WegCli),\n    Widget(WidgetCli),\n    Resource(ResourceManagerCli),\n    Win32(Win32Cli),\n    Art(ArtCli),\n    TaskSwitcher(TaskSwitcherClient),\n    Wallpaper(WallpaperCli),\n}\n\nimpl SluCliCommand for AppCliCommand {\n    fn execution_mode(&self) -> CommandExecutionMode {\n        match self {\n            AppCliCommand::Win32(_) => CommandExecutionMode::Direct,\n            AppCliCommand::Art(_) => CommandExecutionMode::Direct,\n            AppCliCommand::Resource(r) => r.execution_mode(),\n            // All other commands use the default MainInstance mode\n            _ => CommandExecutionMode::MainInstance,\n        }\n    }\n}\n\n/// Main entry point for CLI handling.\n///\n/// Flow:\n/// 1. Parse CLI arguments\n/// 2. Configure global flags (startup, silent, verbose)\n/// 3. Determine routing strategy based on command\n/// 4. Execute according to strategy:\n///    - ExecuteDirect: Run in this process and exit\n///    - RedirectToMainInstance: Send to running instance via IPC and exit\n///    - StartApp: Normal app startup, continue execution\npub async fn handle_console_client() -> Result<()> {\n    let cli = parse_cli_args();\n    configure_global_flags(&cli);\n\n    // Determine how to handle this invocation\n    let strategy = determine_routing_strategy(&cli);\n\n    if cli.verbose {\n        println!(\"Routing strategy: {strategy:?}\");\n    }\n\n    // Execute according to strategy\n    match strategy {\n        CliRoutingStrategy::ExecuteDirect => {\n            attach_console();\n            cli.process_direct().await?;\n            std::process::exit(0);\n        }\n        CliRoutingStrategy::RedirectToMainInstance => {\n            attach_console();\n            cli.send_to_main_instance().await?;\n            std::process::exit(0);\n        }\n        CliRoutingStrategy::StartApp => {\n            // Normal app startup, continue execution\n            Ok(())\n        }\n    }\n}\n\n/// Parse CLI arguments, exits process on parse errors (--help, --version, etc.)\nfn parse_cli_args() -> AppCli {\n    match AppCli::try_parse() {\n        Ok(cli) => cli,\n        Err(e) => {\n            attach_console();\n            e.exit();\n        }\n    }\n}\n\n/// Configure global flags based on CLI arguments\nfn configure_global_flags(cli: &AppCli) {\n    if cli.silent {\n        crate::SILENT.store(true, Ordering::SeqCst);\n    }\n\n    if cli.verbose {\n        crate::VERBOSE.store(true, Ordering::SeqCst);\n        println!(\"Received args: {:#?}\", std::env::args().collect::<Vec<_>>());\n        println!(\"Parsed CLI: {cli:#?}\");\n    }\n}\n\n/// Determines how this CLI invocation should be routed\nfn determine_routing_strategy(cli: &AppCli) -> CliRoutingStrategy {\n    // If we have a command, check its execution mode\n    if let Some(command) = &cli.command {\n        match command.execution_mode() {\n            CommandExecutionMode::Direct => {\n                return CliRoutingStrategy::ExecuteDirect;\n            }\n            CommandExecutionMode::MainInstance => {\n                return CliRoutingStrategy::RedirectToMainInstance;\n            }\n        }\n    }\n\n    // URIs are always redirected to main instance\n    if cli.uri.is_some() {\n        return CliRoutingStrategy::RedirectToMainInstance;\n    }\n\n    // No command or URI = normal app startup\n    CliRoutingStrategy::StartApp\n}\n\nimpl AppCli {\n    /// Processes commands that execute directly in the console (async)\n    pub async fn process_direct(self) -> Result<()> {\n        match self.command {\n            Some(cmd) => cmd.process_direct().await,\n            None => Ok(()),\n        }\n    }\n\n    /// intended to be called on the main instance\n    pub fn process(self) -> Result<()> {\n        if let Some(uri) = self.uri {\n            return process_uri(&uri);\n        }\n\n        match self.command {\n            Some(cmd) => cmd.process(),\n            None => Ok(()),\n        }\n    }\n\n    /// will fail if no instance is running\n    pub async fn send_to_main_instance(self) -> Result<()> {\n        let mut args = Vec::new();\n        let working_dir = std::env::current_dir()?;\n\n        for arg in std::env::args() {\n            if arg.starts_with(\"./\")\n                || arg.starts_with(\".\\\\\")\n                || arg.starts_with(\"../\")\n                || arg.starts_with(\"..\\\\\")\n            {\n                args.push(working_dir.join(arg).to_string_lossy().to_string());\n                continue;\n            }\n            args.push(arg);\n        }\n\n        if self.verbose {\n            println!(\"Sending {args:#?}\");\n        }\n\n        AppIpc::send(AppMessage::Cli(args))\n            .await\n            .map_err(|_| \"Can't stablish connection, ensure Seelen UI is running.\")?;\n        Ok(())\n    }\n}\n\nimpl AppCliCommand {\n    /// Processes commands that execute directly in console (async)\n    pub async fn process_direct(self) -> Result<()> {\n        match self {\n            AppCliCommand::Win32(command) => {\n                command.process_direct()?;\n            }\n            AppCliCommand::Art(command) => {\n                command.process_direct();\n            }\n            AppCliCommand::Resource(command) => {\n                command.process_direct().await?;\n            }\n            _ => {\n                return Err(\"Command does not support direct execution\".into());\n            }\n        }\n        Ok(())\n    }\n\n    pub fn process(self) -> Result<()> {\n        match self {\n            AppCliCommand::Settings => {\n                show_settings()?;\n            }\n            AppCliCommand::VirtualDesk(command) => {\n                command.process()?;\n            }\n            AppCliCommand::Debugger(command) => {\n                command.process()?;\n            }\n            AppCliCommand::WindowManager(command) => {\n                command.process()?;\n            }\n            AppCliCommand::Weg(command) => {\n                command.process()?;\n            }\n            AppCliCommand::Widget(command) => {\n                command.run()?;\n            }\n            AppCliCommand::Resource(command) => {\n                command.process()?;\n            }\n            AppCliCommand::Popup(command) => {\n                command.process()?;\n            }\n            AppCliCommand::TaskSwitcher(command) => {\n                command.process()?;\n            }\n            AppCliCommand::Wallpaper(command) => {\n                command.process()?;\n            }\n            _ => {\n                return Err(\"Command does not support instance execution\".into());\n            }\n        }\n        Ok(())\n    }\n}\n\n// attach console could fail if not console to attach is present\npub fn attach_console() -> bool {\n    let already_attached = unsafe { !GetConsoleWindow().is_invalid() };\n    already_attached || unsafe { AttachConsole(ATTACH_PARENT_PROCESS).is_ok() }\n}\n"
  },
  {
    "path": "src/background/cli/application/uri.rs",
    "content": "use std::{\n    ffi::OsStr,\n    path::{Path, PathBuf},\n};\n\nuse itertools::Itertools;\nuse seelen_core::{\n    resource::{Resource, ResourceId, ResourceKind, SluResource, SluResourceFile},\n    state::{CssStyles, IconPack, SluPopupConfig, SluPopupContent, Wallpaper, WallpaperCollection},\n};\nuse tauri::Listener;\nuse uuid::Uuid;\n\nuse crate::{\n    error::Result,\n    get_tokio_handle, log_error,\n    state::application::{download_remote_icons, FULL_STATE},\n    utils::{constants::SEELEN_COMMON, date_based_hex_id},\n    widgets::popups::POPUPS_MANAGER,\n};\n\npub const URI: &str = \"seelen-ui.uri:\";\n\npub fn process_uri(uri: &str) -> Result<()> {\n    log::trace!(\"Loading URI: {uri}\");\n\n    if !uri.starts_with(URI) {\n        let path = PathBuf::from(uri);\n        if !path.is_file() || path.extension() != Some(OsStr::new(\"slu\")) || !path.exists() {\n            return Err(\"Invalid file to load\".into());\n        }\n\n        let file = SluResourceFile::load(&path)?;\n        store_file_on_respective_user_folder(&file)?;\n        let id = POPUPS_MANAGER.lock().create(SluPopupConfig::default())?;\n        update_popup_to_added_resource(&id, &file.resource)?;\n        return Ok(());\n    }\n\n    let path = uri.trim_start_matches(URI).trim_start_matches(\"/\");\n    let parts = path.split(\"/\").map(|s| s.to_string()).collect_vec();\n\n    if parts.len() != 3 {\n        return Err(\"Invalid URI format\".into());\n    }\n\n    let [_method, enviroment, resource_id] = parts.as_slice() else {\n        return Err(\"Invalid URI format\".into());\n    };\n\n    let Ok(resource_id) = Uuid::parse_str(resource_id) else {\n        return Err(\"Invalid URI format\".into());\n    };\n\n    let env_prefix = if enviroment == \"production\" {\n        \"\".to_string()\n    } else {\n        format!(\".{enviroment}\")\n    };\n\n    let url = format!(\"https://product{env_prefix}.seelen.io/resource/download/{resource_id}\");\n    get_tokio_handle().spawn(async move {\n        log_error!(download_resource(&url).await);\n    });\n\n    Ok(())\n}\n\nfn path_by_resource_kind(kind: &ResourceKind) -> &Path {\n    match kind {\n        ResourceKind::Theme => SEELEN_COMMON.user_themes_path(),\n        ResourceKind::IconPack => SEELEN_COMMON.user_icons_path(),\n        ResourceKind::Widget => SEELEN_COMMON.user_widgets_path(),\n        ResourceKind::Plugin => SEELEN_COMMON.user_plugins_path(),\n        ResourceKind::Wallpaper => SEELEN_COMMON.user_wallpapers_path(),\n        ResourceKind::SoundPack => SEELEN_COMMON.user_sounds_path(),\n    }\n}\n\nfn store_file_on_respective_user_folder(file: &SluResourceFile) -> Result<PathBuf> {\n    let mut path_to_store = path_by_resource_kind(&file.resource.kind).to_path_buf();\n    if file.resource.kind == ResourceKind::IconPack || file.resource.kind == ResourceKind::Wallpaper\n    {\n        path_to_store.push(file.resource.id.to_string());\n        std::fs::create_dir_all(&path_to_store)?;\n        path_to_store.push(\"metadata.slu\");\n    } else {\n        path_to_store.push(format!(\"{}.slu\", file.resource.id));\n    }\n    file.store(&path_to_store)?;\n    Ok(path_to_store)\n}\n\nasync fn download_resource(url: &str) -> Result<()> {\n    let popup_id = {\n        POPUPS_MANAGER.lock().create(SluPopupConfig {\n            title: vec![SluPopupContent::Group {\n                items: vec![\n                    SluPopupContent::Icon {\n                        name: \"TbCloudDownload\".to_string(),\n                        styles: Some(\n                            CssStyles::new()\n                                .add(\"color\", \"var(--color-blue-800)\")\n                                .add(\"height\", \"1.2rem\"),\n                        ),\n                    },\n                    SluPopupContent::Text {\n                        value: t!(\"resource.downloading\").to_string(),\n                        styles: None,\n                    },\n                ],\n                styles: Some(CssStyles::new().add(\"alignItems\", \"center\")),\n            }],\n            content: vec![SluPopupContent::Text {\n                value: t!(\"resource.downloading_body\").to_string(),\n                styles: None,\n            }],\n            ..Default::default()\n        })?\n    };\n\n    let file = match _download_resource(url).await {\n        Ok(file) => file,\n        Err(err) => {\n            POPUPS_MANAGER\n                .lock()\n                .update(&popup_id, error_popup_config(format!(\"{err:?}\").as_str()))?;\n            return Err(err);\n        }\n    };\n\n    update_popup_to_added_resource(&popup_id, &file.resource)?;\n    Ok(())\n}\n\nasync fn _download_resource(url: &str) -> Result<SluResourceFile> {\n    let res = reqwest::get(url).await?;\n    let file = res.json::<SluResourceFile>().await?;\n    let saved_path = store_file_on_respective_user_folder(&file)?;\n\n    if file.resource.kind == ResourceKind::IconPack {\n        let mut pack = IconPack::load(saved_path.parent().unwrap())?;\n        download_remote_icons(&mut pack).await?;\n    }\n\n    if file.resource.kind == ResourceKind::Wallpaper {\n        let mut wallpaper = Wallpaper::load(saved_path.parent().unwrap())?;\n        download_remote_wallpapers(&mut wallpaper).await?;\n    }\n\n    Ok(file)\n}\n\nfn update_popup_to_added_resource(popup_id: &Uuid, resource: &Resource) -> Result<()> {\n    let mut pupups_manager = POPUPS_MANAGER.lock();\n\n    let config = resource_to_popup_config(resource)?;\n    pupups_manager.update(popup_id, config)?;\n\n    let used_id = ResourceId::Remote(resource.id);\n    let kind = resource.kind.clone();\n\n    let popup_id = *popup_id;\n    let display_name = {\n        let state = FULL_STATE.load();\n        resource\n            .metadata\n            .display_name\n            .get(state.locale())\n            .to_owned()\n    };\n\n    let webview = pupups_manager\n        .get_window_handle(&popup_id)\n        .ok_or(\"Popup not found\")?;\n    let event = format!(\"resource::{}::enable\", resource.id);\n\n    let token = webview.once(event, move |_e| {\n        std::thread::spawn(move || {\n            FULL_STATE.rcu(move |state| {\n                let mut state = state.cloned();\n                match kind {\n                    ResourceKind::Theme => {\n                        state.settings.active_themes.push(used_id.clone().into());\n                    }\n                    ResourceKind::IconPack => {\n                        state\n                            .settings\n                            .active_icon_packs\n                            .push(used_id.clone().into());\n                    }\n                    ResourceKind::Widget => {\n                        state\n                            .settings\n                            .set_widget_enabled(&used_id.clone().into(), true);\n                    }\n                    ResourceKind::Wallpaper => {\n                        let collection = WallpaperCollection {\n                            id: Uuid::new_v4(),\n                            name: display_name.clone(),\n                            wallpapers: vec![used_id.clone().into()],\n                        };\n                        state.settings.by_widget.wall.default_collection = Some(collection.id);\n                        state.settings.wallpaper_collections.push(collection);\n                    }\n                    _ => {}\n                }\n                state\n            });\n\n            log_error!(FULL_STATE.load().write_settings());\n            log_error!(POPUPS_MANAGER.lock().close_popup(&popup_id));\n        });\n    });\n\n    pupups_manager\n        .listeners\n        .entry(resource.id)\n        .or_default()\n        .push(token);\n    Ok(())\n}\n\nfn resource_to_popup_config(resource: &Resource) -> Result<SluPopupConfig> {\n    let mut popup = SluPopupConfig {\n        width: 480.0,\n        height: 260.0,\n        ..Default::default()\n    };\n\n    popup.title.push(SluPopupContent::Group {\n        items: vec![\n            SluPopupContent::Icon {\n                name: \"GrCircleInformation\".to_string(),\n                styles: Some(\n                    CssStyles::new()\n                        .add(\"color\", \"var(--color-blue-900)\")\n                        .add(\"height\", \"1.2rem\"),\n                ),\n            },\n            SluPopupContent::Text {\n                value: t!(\"resource.added\").to_string(),\n                styles: None,\n            },\n        ],\n        styles: Some(CssStyles::new().add(\"alignItems\", \"center\")),\n    });\n\n    let image_styles = CssStyles::new()\n        .add(\"width\", \"90px\")\n        .add(\"minWidth\", \"90px\")\n        .add(\"height\", \"90px\")\n        .add(\"borderRadius\", \"14px\")\n        .add(\"backgroundColor\", \"var(--color-gray-200)\")\n        .add(\"display\", \"flex\")\n        .add(\"alignItems\", \"center\")\n        .add(\"justifyContent\", \"center\");\n\n    let image = if let Some(url) = &resource.metadata.portrait {\n        SluPopupContent::Image {\n            href: url.clone(),\n            styles: Some(image_styles),\n        }\n    } else {\n        SluPopupContent::Icon {\n            name: \"GrStatusUnknown\".to_string(),\n            styles: Some(image_styles),\n        }\n    };\n\n    let state = FULL_STATE.load();\n    let locale = state.locale();\n    popup.content = vec![SluPopupContent::Group {\n        items: vec![\n            image,\n            SluPopupContent::Group {\n                items: vec![\n                    SluPopupContent::Text {\n                        value: resource.metadata.display_name.get(locale).to_owned(),\n                        styles: Some(\n                            CssStyles::new()\n                                .add(\"fontWeight\", \"bold\")\n                                .add(\"fontSize\", \"2rem\")\n                                .add(\"lineHeight\", \"1.2em\"),\n                        ),\n                    },\n                    SluPopupContent::Text {\n                        value: resource.metadata.description.get(locale).to_owned(),\n                        styles: None,\n                    },\n                ],\n                styles: Some(CssStyles::new().add(\"flexDirection\", \"column\")),\n            },\n        ],\n        styles: None,\n    }];\n\n    popup.footer = vec![SluPopupContent::Button {\n        inner: vec![SluPopupContent::Text {\n            value: t!(\"resource.enable\").to_string(),\n            styles: None,\n        }],\n        on_click: format!(\"resource::{}::enable\", resource.id),\n        styles: None,\n    }];\n\n    Ok(popup)\n}\n\nfn error_popup_config(err: &str) -> SluPopupConfig {\n    SluPopupConfig {\n        title: vec![SluPopupContent::Group {\n            items: vec![\n                SluPopupContent::Icon {\n                    name: \"BiSolidError\".to_string(),\n                    styles: Some(\n                        CssStyles::new()\n                            .add(\"color\", \"var(--color-red-800)\")\n                            .add(\"height\", \"1.2rem\"),\n                    ),\n                },\n                SluPopupContent::Text {\n                    value: t!(\"resource.download_failed_title\").to_string(),\n                    styles: None,\n                },\n            ],\n            styles: Some(CssStyles::new().add(\"alignItems\", \"center\")),\n        }],\n        content: vec![\n            SluPopupContent::Text {\n                value: t!(\"resource.download_failed_body\").to_string(),\n                styles: None,\n            },\n            SluPopupContent::Text {\n                value: \"=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\".to_string(),\n                styles: Some(CssStyles::new().add(\"color\", \"var(--color-gray-400)\")),\n            },\n            SluPopupContent::Text {\n                value: format!(\"Error: {err}\"),\n                styles: None,\n            },\n        ],\n        ..Default::default()\n    }\n}\n\nasync fn download_remote_wallpapers(wallpaper: &mut Wallpaper) -> Result<()> {\n    if wallpaper.url.is_none() && wallpaper.thumbnail_url.is_none() {\n        return Ok(());\n    }\n\n    let folder_to_store = wallpaper.metadata.directory()?;\n\n    // Download the main wallpaper file\n    if let Some(url) = &wallpaper.url {\n        let filename = download_remote_asset(url, &folder_to_store).await?;\n        wallpaper.filename = Some(filename);\n    }\n\n    // Download the thumbnail\n    if let Some(thumbnail_url) = &wallpaper.thumbnail_url {\n        let thumbnail_filename = download_remote_asset(thumbnail_url, &folder_to_store).await?;\n        wallpaper.thumbnail_filename = Some(thumbnail_filename);\n    }\n\n    wallpaper.save()?;\n    Ok(())\n}\n\nasync fn download_remote_asset(url: &url::Url, folder_to_store: &Path) -> Result<String> {\n    if !folder_to_store.is_dir() {\n        std::fs::create_dir_all(folder_to_store)?;\n    }\n\n    let Some(extension) = url.path().split('.').next_back() else {\n        return Err(\"Could not determine file extension from URL\".into());\n    };\n\n    let res = reqwest::get(url.as_str()).await?;\n    let bytes = res.bytes().await?;\n\n    let filename = format!(\"{}.{}\", date_based_hex_id(), extension);\n    let file_path = folder_to_store.join(&filename);\n\n    std::fs::write(&file_path, &bytes)?;\n    Ok(filename)\n}\n"
  },
  {
    "path": "src/background/cli/application/win32.rs",
    "content": "use serde::{Deserialize, Serialize};\n\nuse crate::{error::Result, windows_api::WindowsApi};\n\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\npub struct Win32Cli {\n    #[command(subcommand)]\n    subcommand: SubCommand,\n}\n\n#[derive(Debug, Serialize, Deserialize, clap::Subcommand)]\nenum SubCommand {\n    SetDefaultAudioDevice { id: String, role: String },\n}\n\nimpl Win32Cli {\n    pub fn process_direct(&self) -> Result<()> {\n        match &self.subcommand {\n            SubCommand::SetDefaultAudioDevice { id, role } => {\n                WindowsApi::set_default_audio_device(id, role)?;\n            }\n        };\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/cli/infrastructure.rs",
    "content": "use slu_ipc::{messages::SvcAction, ServiceIpc};\nuse tauri::WebviewWindow;\n\nuse crate::{error::Result, widgets::popups::shortcut_registering::REG_SHORTCUT_DATA};\n\n#[tauri::command(async)]\npub async fn request_to_user_input_shortcut(\n    window: WebviewWindow,\n    callback_event: String,\n) -> Result<()> {\n    ServiceIpc::send(SvcAction::StartShortcutRegistration).await?;\n\n    let mut data = REG_SHORTCUT_DATA.lock();\n    data.response_view_label = Some(window.label().to_string());\n    data.response_event = Some(callback_event);\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/cli/mod.rs",
    "content": "mod infrastructure;\nmod self_pipe;\nmod svc_pipe;\n\npub mod application;\n\npub use infrastructure::*;\npub use self_pipe::SelfPipe;\npub use svc_pipe::ServicePipe;\n"
  },
  {
    "path": "src/background/cli/self_pipe.rs",
    "content": "use clap::Parser;\nuse slu_ipc::{\n    messages::{AppMessage, IpcResponse},\n    AppIpc,\n};\n\nuse crate::{cli::application::AppCli, error::Result, modules::system_tray::SystemTrayManager};\n\npub struct SelfPipe;\nimpl SelfPipe {\n    fn _handle_cli_message(mut argv: Vec<String>) -> Result<()> {\n        if argv.is_empty() {\n            return Ok(());\n        }\n\n        let first = argv.first().unwrap();\n        if !first.contains(\"seelen-ui\") {\n            argv.insert(0, \"seelen-ui.exe\".to_string());\n        }\n\n        if let Ok(cli) = AppCli::try_parse_from(argv) {\n            if let Err(err) = cli.process() {\n                log::error!(\"Failed to process command: {err}\");\n                return Err(err);\n            }\n        }\n        Ok(())\n    }\n\n    fn handle_message(message: AppMessage) -> IpcResponse {\n        match message {\n            AppMessage::Cli(argv) => {\n                if let Err(err) = Self::_handle_cli_message(argv) {\n                    return IpcResponse::Err(err.to_string());\n                }\n            }\n            AppMessage::TrayChanged(event) => {\n                SystemTrayManager::handle_tray_event(event);\n            }\n            AppMessage::Debug(_msg) => {}\n        }\n        IpcResponse::Success\n    }\n\n    pub fn start_listener() -> Result<()> {\n        AppIpc::start(Self::handle_message)?;\n        Ok(())\n    }\n\n    pub async fn request_open_settings() -> Result<()> {\n        AppIpc::send(AppMessage::Cli(vec![\"settings\".to_owned()])).await?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/cli/svc_pipe.rs",
    "content": "use std::path::PathBuf;\n\nuse slu_ipc::{messages::SvcAction, ServiceIpc, IPC};\nuse tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};\nuse windows::Win32::{\n    System::TaskScheduler::{IExecAction2, ITaskService, TaskScheduler},\n    UI::Shell::FOLDERID_LocalAppData,\n};\nuse windows_core::Interface;\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    get_tokio_handle,\n    utils::{pwsh::PwshScript, was_installed_using_msix},\n    windows_api::{Com, WindowsApi},\n};\n\npub struct ServicePipe;\n\nimpl ServicePipe {\n    /// will ignore any response\n    pub fn request(message: SvcAction) -> Result<()> {\n        get_tokio_handle().spawn(async move {\n            if let Err(err) = ServiceIpc::send(message.clone()).await {\n                log::error!(\"Error sending message to service {err}. Message: {message:?}\");\n            }\n        });\n        Ok(())\n    }\n\n    pub fn is_running() -> bool {\n        ServiceIpc::can_stablish_connection()\n    }\n\n    pub fn service_path() -> Result<PathBuf> {\n        let service_path = if was_installed_using_msix() {\n            WindowsApi::known_folder(FOLDERID_LocalAppData)?\n                .join(\"Microsoft\\\\WindowsApps\\\\slu-service.exe\")\n        } else {\n            std::env::current_exe()?.with_file_name(\"slu-service.exe\")\n        };\n        Ok(service_path)\n    }\n\n    fn start_service_task() -> Result<()> {\n        Com::run_with_context(|| unsafe {\n            let task_service: ITaskService = Com::create_instance(&TaskScheduler)?;\n            task_service.Connect(\n                &Default::default(),\n                &Default::default(),\n                &Default::default(),\n                &Default::default(),\n            )?;\n            let folder = task_service.GetFolder(&\"\\\\Seelen\".into())?;\n            let task = folder.GetTask(&\"Seelen UI Service\".into())?;\n\n            let actions = task.Definition()?.Actions()?;\n            // ask to microsoft what that hell this start counting from 1 instead 0\n            let action: IExecAction2 = actions.get_Item(1)?.cast()?;\n            let mut action_path = windows_core::BSTR::new();\n            action.Path(&mut action_path)?;\n\n            let service_path = Self::service_path()?.to_string_lossy().to_lowercase();\n            match action_path.to_string().to_lowercase() == service_path {\n                true => {\n                    task.Run(&Default::default())?;\n                    Ok(())\n                }\n                false => {\n                    Err(\"Service task is not pointing to the correct service executable\".into())\n                }\n            }\n        })\n    }\n\n    // the service should be running since installer will start it or startup task scheduler\n    // so if the service is not running, we need to start it (common on msix setup)\n    pub async fn start_service() -> Result<()> {\n        let Err(err) = Self::start_service_task() else {\n            return Ok(());\n        };\n\n        log::debug!(\"Can not start service via task scheduler: {err}\");\n\n        let script = PwshScript::new(format!(\n            \"Start-Process '{}' -Verb runAs\",\n            Self::service_path()?.display()\n        ))\n        .inline_command()\n        .elevated();\n\n        let result = script.execute().await;\n        if let Err(err) = result {\n            let try_again = get_app_handle()\n                .dialog()\n                .message(t!(\"service.not_running_description\"))\n                .title(t!(\"service.not_running\"))\n                .kind(MessageDialogKind::Info)\n                .buttons(MessageDialogButtons::OkCustom(\n                    t!(\"service.not_running_ok\").to_string(),\n                ))\n                .blocking_show();\n            if try_again {\n                script.execute().await?;\n            }\n            return Err(err);\n        }\n\n        let mut counter = 0;\n        while !Self::is_running() && counter < 10 {\n            log::debug!(\"Waiting for service IPC...\");\n            counter += 1;\n            tokio::time::sleep(std::time::Duration::from_millis(100)).await;\n        }\n\n        if counter == 10 {\n            get_app_handle()\n                .dialog()\n                .message(t!(\"service.not_running_description\"))\n                .title(t!(\"service.not_running\"))\n                .kind(MessageDialogKind::Error)\n                .buttons(MessageDialogButtons::Ok)\n                .blocking_show();\n            return Err(\"Service not running\".into());\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/error.rs",
    "content": "macro_rules! define_app_errors {\n    ($(\n        $variant:ident($error_type:ty);\n    )*) => {\n        $(\n            impl From<$error_type> for AppError {\n                fn from(err: $error_type) -> Self {\n                    let backtrace = backtrace::Backtrace::new();\n                    AppError { msg: format!(\"{}({:?})\", stringify!($variant), err), backtrace }\n                }\n            }\n        )*\n    };\n}\n\n#[macro_export]\nmacro_rules! log_error {\n    ($result:expr) => {\n        if let Err(err) = $result {\n            log::error!(\"{:?}\", err);\n        }\n    };\n    ($result:expr, $context:expr) => {\n        if let Err(err) = $result {\n            log::error!(\"Context: {:?} Err: {:?}\", $context, err);\n        }\n    };\n}\n\npub struct AppError {\n    msg: String,\n    backtrace: backtrace::Backtrace,\n}\n\ndefine_app_errors!(\n    App(String);\n    Io(std::io::Error);\n    Tauri(tauri::Error);\n    Logger(log::SetLoggerError);\n    Lib(seelen_core::SeelenLibError);\n    TauriShell(tauri_plugin_shell::Error);\n    Windows(windows::core::Error);\n    WMI(wmi::WMIError);\n    SerdeJson(serde_json::Error);\n    SerdeYaml(serde_yaml::Error);\n    SerdeXml(quick_xml::de::DeError);\n    Utf8(std::string::FromUtf8Error);\n    Utf16(std::string::FromUtf16Error);\n    CrossbeamRecv(crossbeam_channel::RecvError);\n    TryFromInt(std::num::TryFromIntError);\n    Image(image::ImageError);\n    Battery(battery::Error);\n    FileNotify(notify_debouncer_full::notify::Error);\n    Base64Decode(base64::DecodeError);\n    WideStringNull(widestring::error::MissingNulTerminator);\n    Reqwest(tauri_plugin_http::reqwest::Error);\n    Updater(tauri_plugin_updater::Error);\n    WinScreenshot(win_screenshot::capture::WSError);\n    EvalExpr(evalexpr::EvalexprError);\n    TryFromSliceError(std::array::TryFromSliceError);\n    ParseIntError(std::num::ParseIntError);\n    Translator(translators::Error);\n    WinHotkey(win_hotkeys::error::WHKError);\n    SluIpc(slu_ipc::error::Error);\n    Tokio(tokio::task::JoinError);\n    Positioning(positioning::error::Error);\n    Time(time::error::Error);\n);\n\nimpl std::fmt::Debug for AppError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        for line in self.msg.lines() {\n            if !line.is_empty() {\n                writeln!(f, \"{line}\")?;\n            }\n        }\n\n        let frames = self.backtrace.frames();\n        let mut index = 0;\n        for frame in frames {\n            for symbol in frame.symbols() {\n                let name = match symbol.name() {\n                    Some(name) => name.to_string(),\n                    None => continue,\n                };\n\n                // skip backtrace traces\n                if name.starts_with(\"backtrace\") {\n                    continue;\n                }\n\n                // 2) skip trace of other modules/libraries specially tracing of tao and tauri libs\n                if !name.starts_with(\"seelen_ui\") {\n                    index += 1;\n                    continue;\n                }\n\n                // 3) skip convertion of erros to AppError\n                if name.starts_with(\"seelen_ui::error::impl\") && name.ends_with(\"from\") {\n                    index += 1;\n                    continue;\n                }\n\n                writeln!(f, \"    {index}: {name}\")?;\n                if let Some(file) = symbol.filename() {\n                    write!(f, \"        at: \\\"{}\", file.to_string_lossy())?;\n                    if let Some(line) = symbol.lineno() {\n                        write!(f, \":{line}\")?;\n                        if let Some(col) = symbol.colno() {\n                            write!(f, \":{col}\")?;\n                        }\n                    }\n                    writeln!(f, \"\\\"\")?;\n                } else {\n                    writeln!(f, \"    at: <unknown>\")?\n                }\n\n                index += 1;\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl std::fmt::Display for AppError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{self:?}\")\n    }\n}\n\nimpl From<&str> for AppError {\n    fn from(err: &str) -> Self {\n        err.to_owned().into()\n    }\n}\n\n// needed to tauri::command macro (exposed functions to frontend)\nimpl From<AppError> for tauri::ipc::InvokeError {\n    fn from(val: AppError) -> Self {\n        tauri::ipc::InvokeError::from(val.to_string())\n    }\n}\n\nimpl From<tauri_plugin_shell::process::Output> for AppError {\n    fn from(output: tauri_plugin_shell::process::Output) -> Self {\n        let msg = if !output.stderr.is_empty() {\n            let (cow, _used, _has_errors) = encoding_rs::GBK.decode(&output.stderr);\n            cow.to_string().to_owned()\n        } else {\n            let (cow, _used, _has_errors) = encoding_rs::GBK.decode(&output.stdout);\n            cow.to_string().to_owned()\n        };\n        let backtrace = backtrace::Backtrace::new();\n        AppError { msg, backtrace }\n    }\n}\n\nimpl<T> From<crossbeam_channel::SendError<T>> for AppError {\n    fn from(_err: crossbeam_channel::SendError<T>) -> Self {\n        \"Crossbeam channel disconnected\".into()\n    }\n}\n\npub trait WindowsResultExt {\n    /// Call this when convertion a `BOOL` into a result using the win32 crate `BOOL::ok()`\n    ///\n    /// For some reason `BOOL` is 0 that means failure, but the error code in the `Result` is `0`\n    /// and message is `succesfully completed`\n    ///\n    /// Warn: Be careful when using this like win32 api documentation sometimes expect this type of behaviours...\n    fn filter_fake_error(self) -> core::result::Result<(), windows::core::Error>;\n}\n\npub trait ResultLogExt {\n    /// Take the result and log it if there is an error\n    fn log_error(self);\n}\n\n// todo remove this trait\npub trait ErrorMap<T> {\n    fn wrap_error(self) -> core::result::Result<T, AppError>;\n}\n\nimpl WindowsResultExt for core::result::Result<(), windows::core::Error> {\n    fn filter_fake_error(self) -> core::result::Result<(), windows::core::Error> {\n        match self {\n            Ok(_) => Ok(()),\n            Err(error) => {\n                // I really hate windows api for this types of behaviours\n                if error.code().is_ok() {\n                    // let app_error = AppError::from(error);\n                    // log::warn!(\"(maybe?) fake win32 error, was skipped: {:?}\", app_error);\n                    Ok(())\n                } else {\n                    Err(error)\n                }\n            }\n        }\n    }\n}\n\nimpl<T, E: Into<AppError>> ErrorMap<T> for core::result::Result<T, E> {\n    #[inline(always)]\n    fn wrap_error(self) -> core::result::Result<T, AppError> {\n        self.map_err(Into::into)\n    }\n}\n\nimpl<T, E: Into<AppError>> ResultLogExt for core::result::Result<T, E> {\n    #[inline(always)]\n    #[track_caller]\n    fn log_error(self) {\n        if let Err(err) = self {\n            log::error!(\"{:?}\", err.into());\n        }\n    }\n}\n\npub type Result<T = ()> = core::result::Result<T, AppError>;\n"
  },
  {
    "path": "src/background/exposed.rs",
    "content": "use std::{collections::HashMap, os::windows::process::CommandExt, path::PathBuf};\n\nuse owo_colors::OwoColorize;\nuse seelen_core::{\n    command_handler_list,\n    constants::SUPPORTED_LANGUAGES,\n    resource::ResourceText,\n    system_state::{Color, RelaunchArguments, StartMenuLayout, StartMenuLayoutItem},\n};\n\nuse tauri::{Builder, WebviewWindow, Wry};\nuse tauri_plugin_shell::ShellExt;\nuse translators::Translator;\nuse windows::Win32::System::Threading::{CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW};\n\nuse crate::{\n    app::{get_app_handle, Seelen},\n    error::Result,\n    utils::{\n        self,\n        constants::SEELEN_COMMON,\n        icon_extractor::{request_icon_extraction_from_file, request_icon_extraction_from_umid},\n        pwsh::PwshScript,\n    },\n    widgets::permissions::{request_widget_permission, WidgetPerm},\n    windows_api::{hdc::DeviceContext, string_utils::WindowsString, window::Window, WindowsApi},\n};\n\n#[tauri::command(async)]\npub fn log_from_webview(level: u8, message: String, location: String) {\n    let level = match level {\n        1 => log::Level::Trace,\n        2 => log::Level::Debug,\n        3 => log::Level::Info,\n        4 => log::Level::Warn,\n        _ => log::Level::Error,\n    };\n    log::log!(target: &location, level, \"{message}\");\n}\n\npub fn open_file_inner(path: String) -> Result<()> {\n    std::process::Command::new(\"cmd\")\n        .raw_arg(\"/c\")\n        .raw_arg(\"start\")\n        .raw_arg(\"\\\"\\\"\")\n        .raw_arg(format!(\"\\\"{path}\\\"\"))\n        .creation_flags(CREATE_NO_WINDOW.0 | CREATE_NEW_PROCESS_GROUP.0)\n        .stdin(std::process::Stdio::null())\n        .stdout(std::process::Stdio::null())\n        .stderr(std::process::Stdio::null())\n        .spawn()?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn open_file(webview: tauri::WebviewWindow, path: String) -> Result<()> {\n    request_widget_permission(&webview, WidgetPerm::OpenFile)?;\n    open_file_inner(path)\n}\n\n#[tauri::command(async)]\nfn select_file_on_explorer(path: String) -> Result<()> {\n    get_app_handle()\n        .shell()\n        .command(SEELEN_COMMON.system_dir().join(\"explorer.exe\"))\n        .args([\"/select,\", &path])\n        .spawn()?;\n    Ok(())\n}\n\n#[tauri::command(async)]\nasync fn run(\n    webview: tauri::WebviewWindow,\n    program: String,\n    args: Option<RelaunchArguments>,\n    working_dir: Option<PathBuf>,\n    elevated: bool,\n) -> Result<()> {\n    request_widget_permission(&webview, WidgetPerm::Run)?;\n    let args = args.map(|args| args.to_string());\n    WindowsApi::execute(program, args, working_dir, elevated)\n}\n\n#[tauri::command(async)]\nfn is_dev_mode() -> bool {\n    tauri::is_dev()\n}\n\n#[tauri::command(async)]\nfn has_fixed_runtime() -> bool {\n    crate::utils::has_fixed_runtime()\n}\n\n#[tauri::command(async)]\nfn is_appx_package() -> bool {\n    crate::utils::is_running_as_appx()\n}\n\n#[tauri::command(async)]\npub fn get_user_envs() -> HashMap<String, String> {\n    std::env::vars().collect::<HashMap<String, String>>()\n}\n\n#[tauri::command(async)]\nasync fn set_auto_start(enabled: bool) -> Result<()> {\n    Seelen::set_auto_start(enabled)\n}\n\n#[tauri::command(async)]\nasync fn get_auto_start_status() -> Result<bool> {\n    Seelen::is_auto_start_enabled()\n}\n\n// used to request icon extraction\n#[tauri::command(async)]\nfn get_icon(path: Option<PathBuf>, umid: Option<String>) -> Result<()> {\n    if let Some(umid) = umid {\n        request_icon_extraction_from_umid(&umid.into());\n    }\n    if let Some(path) = path {\n        request_icon_extraction_from_file(&path);\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\nasync fn check_for_updates() -> Result<bool> {\n    Ok(utils::updater::check_for_updates().await?.is_some())\n}\n\n#[tauri::command(async)]\nfn get_foreground_window_color(webview: WebviewWindow<tauri::Wry>) -> Result<Color> {\n    let webview = Window::from(webview.hwnd()?.0 as isize);\n    let foreground = Window::get_foregrounded();\n\n    if webview.monitor() != foreground.monitor() {\n        return Ok(Color::default());\n    }\n\n    if !foreground.is_visible() || foreground.is_desktop() {\n        return Ok(Color::default());\n    }\n\n    let hdc = DeviceContext::create(None);\n    let rect = foreground.inner_rect()?;\n    let x = rect.left + (rect.right - rect.left) / 2;\n    Ok(hdc.get_pixel(x, rect.top + 2))\n}\n\n#[tauri::command(async)]\nasync fn install_last_available_update() -> Result<()> {\n    let update = utils::updater::check_for_updates()\n        .await?\n        .ok_or(\"There is no update available\")?;\n    utils::updater::trace_update_intallation(update).await?;\n    get_app_handle().restart();\n    #[allow(unreachable_code)]\n    Ok(())\n}\n\npub async fn translate_file(path: PathBuf, source_lang: Option<String>) -> Result<()> {\n    let file = std::fs::File::open(&path)?;\n    let mut texts: ResourceText = serde_yaml::from_reader(file)?;\n\n    let code = match source_lang {\n        Some(source_lang) => source_lang,\n        None => \"en\".to_string(),\n    };\n\n    if !texts.has(&code) {\n        return Err(format!(\"Source Language ({code}) not found.\").into());\n    }\n\n    let source = texts.get(&code).to_owned();\n    let total = SUPPORTED_LANGUAGES.len();\n\n    let longest_lang = SUPPORTED_LANGUAGES\n        .iter()\n        .map(|lang| lang.en_label.len())\n        .max()\n        .unwrap_or(0);\n\n    for (idx, lang) in SUPPORTED_LANGUAGES.iter().enumerate() {\n        let step = if idx < 9 {\n            format!(\"0{}\", idx + 1)\n        } else {\n            (idx + 1).to_string()\n        };\n\n        // fill with spaces to fit max length\n        let label = format!(\n            \"{}{}\",\n            lang.en_label,\n            \" \".repeat(longest_lang - lang.en_label.len())\n        );\n\n        if texts.has(lang.value) {\n            println!(\n                \"[{step}/{total}] {} => {}\",\n                label.bright_black(),\n                \"Skipped\".bright_black()\n            );\n            continue;\n        }\n\n        match _translate_text(&source, &code, lang.value).await {\n            Ok(value) => {\n                println!(\n                    \"[{step}/{total}] {} => \\\"{}\\\"\",\n                    label.bold().bright_green(),\n                    value\n                );\n                texts.set(lang.value.to_string(), value);\n            }\n            Err(err) => {\n                eprintln!(\n                    \"[{step}/{total}] {} => Error translating to {} ({}): {}\",\n                    label.bold().bright_red(),\n                    lang.en_label,\n                    lang.value,\n                    err\n                );\n            }\n        }\n    }\n\n    let file = std::fs::File::create(&path)?;\n    serde_yaml::to_writer(file, &texts)?;\n    Ok(())\n}\n\nasync fn _translate_text(source: &str, source_lang: &str, mut target_lang: &str) -> Result<String> {\n    use translators::GoogleTranslator;\n    let translator = GoogleTranslator::default();\n\n    if target_lang == \"zh\" {\n        target_lang = \"zh-CN\";\n    }\n\n    if target_lang == \"pt\" {\n        target_lang = \"pt-BR\";\n    }\n\n    let translated = translator\n        .translate_async(source, source_lang, target_lang)\n        .await?;\n    Ok(translated)\n}\n\n#[tauri::command(async)]\nasync fn get_native_start_menu() -> Result<StartMenuLayout> {\n    let output_path = SEELEN_COMMON.app_cache_dir().join(\"start-layout.json\");\n    let output_path_str = output_path.to_string_lossy().to_string();\n\n    let script =\n        PwshScript::new(format!(\"Export-StartLayout -Path '{}'\", output_path_str)).inline_command();\n    script.execute().await?;\n\n    let file = std::fs::File::open(&output_path)?;\n    let mut layout: StartMenuLayout = serde_json::from_reader(file)?;\n\n    for item in &mut layout.pinned_list {\n        if let StartMenuLayoutItem::DesktopAppLink(path) = item {\n            let source = WindowsString::from_str(path);\n            let expanded = WindowsApi::resolve_environment_variables(&source)?;\n            *item = StartMenuLayoutItem::DesktopAppLink(expanded.to_string());\n        }\n    }\n\n    Ok(layout)\n}\n\npub fn register_invoke_handler(app_builder: Builder<Wry>) -> Builder<Wry> {\n    use crate::cli::*;\n    use crate::state::infrastructure::*;\n    use crate::virtual_desktops::handlers::*;\n    use crate::widgets::permissions::*;\n    use crate::widgets::popups::handlers::*;\n    use crate::widgets::weg::handler::*;\n    use crate::widgets::window_manager::handler::*;\n    use crate::widgets::*;\n\n    use crate::modules::apps::infrastructure::*;\n    use crate::modules::media::devices::infrastructure::*;\n    use crate::modules::media::players::infrastructure::*;\n    use crate::modules::monitors::brightness::infrastructure::*;\n    use crate::modules::monitors::infrastructure::*;\n    use crate::modules::network::infrastructure::*;\n    use crate::modules::notifications::infrastructure::*;\n    use crate::modules::power::infrastructure::*;\n    use crate::modules::radios::bluetooth::handlers::*;\n    use crate::modules::radios::handlers::*;\n    use crate::modules::start::infrastructure::*;\n    use crate::modules::system::tauri::*;\n    use crate::modules::system_settings::infrastructure::*;\n    use crate::modules::system_settings::language::infrastructure::*;\n    use crate::modules::system_tray::infrastructure::*;\n    use crate::modules::trash_bin::infrastructure::*;\n    use crate::modules::user::infrastructure::*;\n\n    use crate::resources::commands::*;\n\n    app_builder.invoke_handler(command_handler_list!())\n}\n"
  },
  {
    "path": "src/background/hook.rs",
    "content": "use std::{\n    collections::HashSet,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc,\n    },\n    time::Duration,\n};\n\nuse parking_lot::RwLock;\nuse seelen_core::handlers::SeelenEvent;\nuse windows::Win32::{\n    Foundation::HWND,\n    UI::{\n        Accessibility::{SetWinEventHook, HWINEVENTHOOK},\n        WindowsAndMessaging::{\n            DispatchMessageW, GetMessageW, TranslateMessage, EVENT_MAX, EVENT_MIN,\n            HSHELL_MONITORCHANGED, MSG, OBJID_WINDOW,\n        },\n    },\n};\n\nuse crate::{\n    app::{emit_to_webviews, Seelen, SEELEN},\n    error::{Result, ResultLogExt},\n    event_manager, log_error,\n    state::application::FULL_STATE,\n    trace_lock,\n    utils::spawn_named_thread,\n    widgets::{weg::SeelenWeg, window_manager::instance::WindowManagerV2},\n    windows_api::{\n        event_window::{\n            BgWindowProc, HSHELL_FULLSCREEN_ENTER, HSHELL_FULLSCREEN_EXIT, IS_INTERACTIVE_SESSION,\n            WM_SHELLHOOKMESSAGE,\n        },\n        input::Mouse,\n        window::{event::WinEvent, Window},\n        WindowEnumerator,\n    },\n};\n\npub static LOG_WIN_EVENTS: AtomicBool = AtomicBool::new(false);\n\npub struct HookManager;\n\nevent_manager!(HookManager, (WinEvent, Window));\n\nimpl HookManager {\n    /// this will be called without waiting for the event to be processed\n    /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook#remarks\n    extern \"system\" fn raw_win_event_hook_recv(\n        _hook_handle: HWINEVENTHOOK,\n        event: u32,\n        origin: HWND,\n        id_object: i32,\n        _id_child: i32,\n        _id_event_thread: u32,\n        _dwms_event_time: u32,\n    ) {\n        if id_object != OBJID_WINDOW.0 || !Seelen::is_running() {\n            return;\n        }\n\n        // CRITICAL: Skip event processing when session is not interactive (locked/switched)\n        // This prevents excessive CPU usage from processing thousands of events per second\n        // when the user has locked the screen or switched users\n        if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n            return;\n        }\n\n        // deprecated code refactor this on future.\n        if FULL_STATE.load().is_weg_enabled() {\n            // raw events should be only used for a fastest and immediately processing\n            log_error!(SeelenWeg::process_raw_win_event(event, origin));\n        }\n\n        let event = WinEvent::from(event);\n        let origin = Window::from(origin);\n        Self::send((event, origin));\n        event.debounce_as_needed(&origin);\n    }\n\n    fn start() {\n        let eid = Self::subscribe(|(event, mut origin)| {\n            if event == WinEvent::SystemForeground {\n                origin = Window::get_foregrounded(); // sometimes this event is emited with the wrong origin\n            }\n            Self::legacy_process_event(event, origin);\n        });\n        Self::set_event_handler_priority(&eid, 1);\n\n        BgWindowProc::subscribe(|(msg, wparam, lparam)| {\n            if msg == WM_SHELLHOOKMESSAGE.load(Ordering::Acquire) {\n                // println!(\"WM_SHELLHOOKMESSAGE: {msg} {wparam} {lparam}\");\n                match wparam as u32 {\n                    HSHELL_MONITORCHANGED => {\n                        Self::send((WinEvent::SyntheticMonitorChanged, Window::from(lparam)));\n                    }\n                    HSHELL_FULLSCREEN_ENTER => {\n                        Self::send((WinEvent::SyntheticFullscreenStart, Window::from(lparam)));\n                    }\n                    HSHELL_FULLSCREEN_EXIT => {\n                        Self::send((WinEvent::SyntheticFullscreenEnd, Window::from(lparam)));\n                    }\n                    _ => {}\n                }\n            }\n        });\n\n        spawn_named_thread(\"WinEventHook\", move || unsafe {\n            SetWinEventHook(\n                EVENT_MIN,\n                EVENT_MAX,\n                None,\n                Some(HookManager::raw_win_event_hook_recv),\n                0,\n                0,\n                0,\n            );\n\n            let mut msg: MSG = MSG::default();\n            while GetMessageW(&mut msg, None, 0, 0).as_bool() {\n                let _ = TranslateMessage(&msg);\n                DispatchMessageW(&msg);\n            }\n        });\n    }\n\n    #[allow(dead_code)]\n    fn log_event(event: WinEvent, origin: Window) {\n        use owo_colors::OwoColorize;\n        if event == WinEvent::ObjectLocationChange {\n            return;\n        }\n        let event_value = event.green();\n        if event == WinEvent::ObjectDestroy {\n            return println!(\"{event_value:?}({:0x})\", origin.address());\n        }\n        println!(\"{event_value:?} | {origin:?}\");\n    }\n\n    fn legacy_process_event(event: WinEvent, origin: Window) {\n        // Self::log_event(event, origin);\n\n        // TODO: optimize this, update independent fields instead of the whole struct\n        {\n            let shoup_update_focused = matches!(\n                event,\n                WinEvent::SystemForeground\n                    | WinEvent::SynThrottledForegroundRectChange\n                    | WinEvent::ObjectNameChange\n                    | WinEvent::SystemMoveSizeStart\n                    | WinEvent::SystemMoveSizeEnd\n                    | WinEvent::SyntheticFullscreenStart\n                    | WinEvent::SyntheticFullscreenEnd\n            );\n\n            if shoup_update_focused && origin.is_focused() {\n                emit_to_webviews(\n                    SeelenEvent::GlobalFocusChanged,\n                    origin.as_focused_app_information(),\n                );\n            }\n        }\n\n        let app_state = FULL_STATE.load();\n\n        if app_state.is_window_manager_enabled() {\n            log_error!(WindowManagerV2::process_win_event(event, &origin), event);\n        }\n\n        {\n            let mut seelen = trace_lock!(SEELEN);\n            if let Some(wall) = &mut seelen.wall {\n                log_error!(wall.process_win_event(event, &origin), event);\n            }\n        };\n\n        let mut seelen = trace_lock!(SEELEN);\n        for instance in seelen.instances_mut() {\n            if let Some(toolbar) = &mut instance.toolbar {\n                log_error!(toolbar.process_win_event(event, &origin), event);\n            }\n            if let Some(weg) = &mut instance.weg {\n                log_error!(weg.process_individual_win_event(event, &origin), event);\n            }\n        }\n    }\n}\n\npub fn register_win_hook() -> Result<()> {\n    log::trace!(\"Registering Windows and Virtual Desktop Hooks\");\n    init_zombie_window_killer();\n    HookManager::start();\n\n    let eid = HookManager::subscribe(|(event, origin)| match event {\n        WinEvent::SystemMoveSizeStart => {\n            origin.set_dragging(true);\n        }\n        WinEvent::SystemMoveSizeEnd => {\n            origin.set_dragging(false);\n        }\n        _ => (),\n    });\n    HookManager::set_event_handler_priority(&eid, 3);\n\n    // todo move this to input/mouse/keyboard module\n    spawn_named_thread(\"MouseEventHook\", || {\n        let mut last_pos = seelen_core::Point::default();\n        let sleep_time = Duration::from_millis(100); // 10fps\n        loop {\n            // Pause when session is not interactive to reduce CPU usage\n            if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                std::thread::sleep(Duration::from_secs(1));\n                continue;\n            }\n\n            if let Ok(pos) = Mouse::get_cursor_pos() {\n                if last_pos != pos {\n                    emit_to_webviews(SeelenEvent::GlobalMouseMove, &[pos.x, pos.y]);\n                    last_pos = pos;\n                }\n            }\n            std::thread::sleep(sleep_time);\n        }\n    });\n\n    Ok(())\n}\n\npub fn init_zombie_window_killer() {\n    let existing_windows = Arc::new(RwLock::new(HashSet::new()));\n\n    let dict = existing_windows.clone();\n    HookManager::subscribe(move |(event, origin)| match event {\n        WinEvent::ObjectCreate => {\n            dict.write().insert(origin.address());\n        }\n        WinEvent::ObjectDestroy => {\n            dict.write().remove(&origin.address());\n        }\n        _ => {}\n    });\n\n    // Spawns a task that periodically checks for \"zombie windows\" - windows\n    // that have been destroyed (e.g., through task kill or abnormal termination) but didn't\n    // properly emit the ObjectDestroy event. This thread detects such windows\n    // and emits the missing destruction events to ensure proper cleanup.\n    spawn_named_thread(\"Zombie Window Exterminator\", move || {\n        WindowEnumerator::new()\n            .for_each_and_descendants(|window| {\n                existing_windows.write().insert(window.address());\n            })\n            .log_error();\n\n        loop {\n            std::thread::sleep(std::time::Duration::from_secs(1));\n\n            // Pause when session is not interactive to reduce CPU usage\n            if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                continue;\n            }\n\n            let guard = existing_windows.write();\n            for addr in guard.iter() {\n                let window = Window::from(*addr);\n                if !window.is_window() {\n                    log::trace!(\"Reaping window: {:0x}\", window.address());\n                    HookManager::send((WinEvent::ObjectDestroy, window));\n                }\n            }\n        }\n    });\n}\n"
  },
  {
    "path": "src/background/i18n/af.yml",
    "content": "cancel: Kanselleer\ndone: Gedoen\nelevated:\n  description: >-\n    Seelen UI loop met administrateur regte.\n\n    Dit voeg geen kenmerke by of verbeter dit nie, en dit kan 'n\n    sekuriteitsrisiko inhou.\n\n    Vir beter sekuriteit, herbegin dit asseblief sonder\n    administrateurtoestemmings.\n  title: Loop as administrateur\nfile_explorer: Lêerverkenner\nresource:\n  added: '''N Nuwe hulpbron is bygevoeg'\n  download_failed_body: >-\n    Iets het verkeerd geloop tydens die aflaai van die bron, meld dit asseblief\n    aan die Seelen -ontwikkelaarspan.\n  download_failed_title: Aflaai van hulpbronne misluk\n  downloading: Aflaai bates ...\n  downloading_body: Dit kan 'n paar sekondes duur, moenie die venster toemaak nie.\n  enable: In staat stel\nruntime:\n  corrupted_data: Korrupte data\n  corrupted_file: '''N Konfigurasielêer word beskadig, met behulp van standaardwaardes.'\n  corrupted_file_path: Korrupte lêerpad\n  download: Gaan na die aflaai bladsy\n  files_integrity: >-\n    Ons kon nie die integriteit van die toepassinglêers verifieer nie.\n\n    Dit kan gebeur as lêers gewysig, korrupteer is of die installasie onvolledig\n    is.\n\n    Om sekuriteitsredes kan Seelen UI nie voortgaan nie.\n\n    Herinstalleer dit asseblief vanaf die amptelike webwerf.\n  files_integrity_title: Sekuriteitwaarskuwing - Lêersintegriteitkontrole het misluk\n  not_found: WebView2 Looptyd nie gevind nie\n  not_found_description: Seelen UI vereis Webview2 Runtime. Installeer dit asseblief.\n  outdated: WebView2 Looptyd verouderd\n  outdated_description: >-\n    Seelen UI vereis Webview2 Runtime %{min_version} of hoër. Dateer dit\n    asseblief op.\nservice:\n  not_running: Seelen UI-diens loop nie\n  not_running_description: >-\n    Seelen UI Service (slu-diens) is 'n afhanklikheid/helper vir die toepassing\n    om behoorlik te werk.\n  not_running_ok: Begin diens\nshortcut:\n  register:\n    placeholder: Druk op enige sleutelkombinasie\n    title: Nuwe kortpad registreer\nwidget_liveness:\n  failed_description: |-\n    Die legstuk '%{widget_name}' het te veel keer opgehou reageer.\n\n    Jy kan probeer om die toepassing te herbegin.\n  failed_title: Legstukfout\nwidget_permissions:\n  perm_open_file: maak lêers oop\n  perm_run: programme uit te voer\n  request_description: |-\n    Die legstuk '%{widget_name}' versoek toestemming om %{command}.\n\n    Wil jy dit toelaat?\n  request_title: Widget Toestemming Versoek\n"
  },
  {
    "path": "src/background/i18n/am.yml",
    "content": "cancel: ይቅር\ndone: ተከናውኗል\nelevated:\n  description: |-\n    Seelen UI ከአስተዳዳሪ ልዩ መብቶች ጋር እየሰራ ነው።\n    ይህ ምንም አይነት ባህሪን አይጨምርም ወይም አያሻሽልም፣ እና የደህንነት ስጋት ሊፈጥር ይችላል።\n    ለተሻለ ደህንነት፣ እባክዎን ያለአስተዳዳሪ ፈቃድ እንደገና ያስጀምሩት።\n  title: እንደ አስተዳዳሪ በመሮጥ ላይ\nfile_explorer: ፋይል አሳሽ\nresource:\n  added: አዲስ ሀብት ታክሏል\n  download_failed_body: ሀብቱን በማወጅበት ጊዜ አንድ ነገር ተሳስቷል, እባክዎን ለታና ancer ቡድን ያሳውቁ.\n  download_failed_title: የሀብት ማውረድ አልተሳካም\n  downloading: ንብረቶችን ማውረድ ...\n  downloading_body: ይህ ደግሞ ጥቂት ሰኮንዶች ሊወስድ ይችላል, እባክዎን መስኮቱን አይዝጉ.\n  enable: አንቃ\nruntime:\n  corrupted_data: የተበላሸ ውሂብ\n  corrupted_file: የውቅረት ፋይል ተበላሽቷል, ይልቁንስ ነባሪ እሴቶችን በመጠቀም ተበላሽቷል.\n  corrupted_file_path: የተበላሸ ፋይል መንገድ\n  download: ወደ ማውረድ ገጽ ይሂዱ\n  files_integrity: |-\n    የመተግበሪያ ፋይሎችን ትክክለኛነት ማረጋገጥ አልቻልንም።\n    ፋይሎች ከተሻሻሉ፣ ከተበላሹ ወይም መጫኑ ካልተሟላ ይሄ ሊከሰት ይችላል።\n    ለደህንነት ሲባል Seelen UI መቀጠል አይችልም።\n    እባክዎ ከኦፊሴላዊው ድር ጣቢያ እንደገና ይጫኑት።\n  files_integrity_title: የደህንነት ማንቂያ - የፋይሎች ትክክለኛነት ማረጋገጥ አልተሳካም።\n  not_found: WebView2 Runtime አልተገኘም።\n  not_found_description: Seelen UI Webview2 Runtime ያስፈልገዋል። እባክዎ ይጫኑት።\n  outdated: WebView2 Runtime ጊዜው አልፎበታል።\n  outdated_description: Seelen UI Webview2 Runtime %{min_version} ወይም ከዚያ በላይ ይፈልጋል። እባክዎ ያዘምኑት።\nservice:\n  not_running: Seelen UI አገልግሎት እየሰራ አይደለም።\n  not_running_description: Seelen UI አገልግሎት (slu-አገልግሎት) መተግበሪያው በትክክል እንዲሰራ ጥገኛ/ረዳት ነው።\n  not_running_ok: አገልግሎት ጀምር\nshortcut:\n  register:\n    placeholder: ማንኛውንም የቁልፍ ጥምረት ተጫን\n    title: አዲስ አቋራጭ መመዝገብ\nwidget_liveness:\n  failed_description: |-\n    መግብር '%{widget_name}' ብዙ ጊዜ ምላሽ መስጠት አቁሟል።\n\n    መተግበሪያውን እንደገና ለማስጀመር መሞከር ይችላሉ።\n  failed_title: የመግብር ስህተት\nwidget_permissions:\n  perm_open_file: ፋይሎችን ይክፈቱ\n  perm_run: ፕሮግራሞችን አሂድ\n  request_description: |-\n    መግብር '%{widget_name}' ለ%{command} ፍቃድ እየጠየቀ ነው።\n\n    ይህንን መፍቀድ ይፈልጋሉ?\n  request_title: የመግብር ፍቃድ ጥያቄ\n"
  },
  {
    "path": "src/background/i18n/ar.yml",
    "content": "cancel: يلغي\ndone: منتهي\nelevated:\n  description: |-\n    تعمل واجهة Seelen UI بامتيازات المسؤول.\n    وهذا لا يضيف أو يحسن أي ميزات، وقد يشكل خطرًا أمنيًا.\n    للحصول على أمان أفضل، يرجى إعادة تشغيله دون الحصول على أذونات المسؤول.\n  title: التشغيل كمسؤول\nfile_explorer: مستكشف الملفات\nresource:\n  added: تمت إضافة مورد جديد\n  download_failed_body: حدث خطأ ما أثناء تنزيل المورد، يُرجى إبلاغ فريق مطوري سيلين بذلك.\n  download_failed_title: فشل تنزيل الموارد\n  downloading: تنزيل الأصول...\n  downloading_body: قد يستغرق ذلك بضع ثوانٍ، يُرجى عدم إغلاق النافذة.\n  enable: التمكين\nruntime:\n  corrupted_data: بيانات تالفة\n  corrupted_file: ملف التكوين تالف، باستخدام قيم افتراضية بدلاً من ذلك.\n  corrupted_file_path: مسار الملف التالف\n  download: انتقل إلى صفحة التنزيل\n  files_integrity: |-\n    لم نتمكن من التحقق من سلامة ملفات التطبيق.\n    يمكن أن يحدث هذا في حالة تعديل الملفات أو تلفها أو عدم اكتمال التثبيت.\n    لأسباب أمنية، لا يمكن لـ Seelen UI الاستمرار.\n    يرجى إعادة تثبيته من الموقع الرسمي.\n  files_integrity_title: تنبيه أمني - فشل التحقق من سلامة الملفات\n  not_found: لم يتم العثور على وقت تشغيل WebView2\n  not_found_description: تتطلب واجهة مستخدم Seelen وقت تشغيل Webview2. الرجاء تثبيته.\n  outdated: وقت تشغيل WebView2 قديم\n  outdated_description: >-\n    تتطلب واجهة مستخدم Seelen وقت تشغيل Webview2 %{min_version} أو أعلى. يرجى\n    تحديثه.\nservice:\n  not_running: خدمة Seelen UI لا تعمل\n  not_running_description: تعتبر خدمة Seelen UI (خدمة slu) بمثابة تبعية/مساعد للتطبيق للعمل بشكل صحيح.\n  not_running_ok: ابدأ الخدمة\nshortcut:\n  register:\n    placeholder: اضغط على أي مجموعة مفاتيح\n    title: تسجيل اختصار جديد\nwidget_liveness:\n  failed_description: |-\n    توقفت الأداة '%{widget_name}' عن الاستجابة عدة مرات.\n\n    يمكنك محاولة إعادة تشغيل التطبيق.\n  failed_title: خطأ في القطعة\nwidget_permissions:\n  perm_open_file: فتح الملفات\n  perm_run: تشغيل البرامج\n  request_description: |-\n    تطلب الأداة '%{widget_name}' إذنًا لـ %{command}.\n\n    هل تريد السماح بهذا؟\n  request_title: طلب إذن القطعة\n"
  },
  {
    "path": "src/background/i18n/az.yml",
    "content": "cancel: Ləğv etmək\ndone: Tamamlandı\nelevated:\n  description: >-\n    Seelen UI administrator imtiyazları ilə işləyir.\n\n    Bu, heç bir xüsusiyyət əlavə etmir və ya təkmilləşdirmir və təhlükəsizlik\n    riski yarada bilər.\n\n    Daha yaxşı təhlükəsizlik üçün administrator icazəsi olmadan onu yenidən\n    başladın.\n  title: Administrator olaraq çalışır\nfile_explorer: Fayl Explorer\nresource:\n  added: Yeni bir qaynaq əlavə edildi\n  download_failed_body: >-\n    Resursu yükləyərkən bir şey səhv oldu, xahiş edirəm Seelen geliştirici\n    komandasına bildirin.\n  download_failed_title: Resurs yükləmə uğursuz oldu\n  downloading: Aktivləri yükləmək ...\n  downloading_body: Bu bir neçə saniyə çəkə bilər, xahiş edirəm pəncərəni bağlamayın.\n  enable: Aktivləşdirmək\nruntime:\n  corrupted_data: Zərərli məlumatlar\n  corrupted_file: >-\n    Bunun əvəzinə standart dəyərlərdən istifadə edərək bir konfiqurasiya faylı\n    pozulur.\n  corrupted_file_path: Pozulmuş fayl yolu\n  download: Yükləmə səhifəsinə keçin\n  files_integrity: >-\n    Tətbiq fayllarının bütövlüyünü yoxlaya bilmədik.\n\n    Bu, fayllar dəyişdirildikdə, zədələndikdə və ya quraşdırma tamamlanmadıqda\n    baş verə bilər.\n\n    Təhlükəsizlik səbəbləri ilə Seelen UI davam edə bilməz.\n\n    Lütfən, onu rəsmi saytdan yenidən quraşdırın.\n  files_integrity_title: >-\n    Təhlükəsizlik Xəbərdarlığı - Faylların bütövlüyünün yoxlanılması uğursuz\n    oldu\n  not_found: WebView2 İcra vaxtı tapılmadı\n  not_found_description: Seelen UI Webview2 Runtime tələb edir. Zəhmət olmasa quraşdırın.\n  outdated: WebView2 İcra vaxtı köhnəlmişdir\n  outdated_description: >-\n    Seelen UI Webview2 Runtime %{min_version} və ya daha yüksək tələb edir.\n    Zəhmət olmasa yeniləyin.\nservice:\n  not_running: Seelen UI Xidməti işləmir\n  not_running_description: >-\n    Seelen UI Xidməti (slu-xidmət) tətbiqin düzgün işləməsi üçün\n    asılılıq/köməkçidir.\n  not_running_ok: Xidmətə başlayın\nshortcut:\n  register:\n    placeholder: Hər hansı bir açar birləşməsini basın\n    title: Yeni qısa yol qeydiyyatı\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' vidceti çox dəfə cavab vermədi.\n\n    Proqramı yenidən başlatmağa cəhd edə bilərsiniz.\n  failed_title: Vidcet xətası\nwidget_permissions:\n  perm_open_file: faylları açın\n  perm_run: proqramları işlədin\n  request_description: |-\n    '%{widget_name}' vidceti %{command} üçün icazə tələb edir.\n\n    Buna icazə vermək istəyirsiniz?\n  request_title: Widget İcazə Sorğusu\n"
  },
  {
    "path": "src/background/i18n/bg.yml",
    "content": "cancel: Отказ\ndone: Готово\nelevated:\n  description: >-\n    Seelen UI работи с администраторски права.\n\n    Това не добавя или подобрява никакви функции и може да представлява риск за\n    сигурността.\n\n    За по-добра сигурност, моля, рестартирайте го без администраторски\n    разрешения.\n  title: Работи като администратор\nfile_explorer: File Explorer\nresource:\n  added: Добавен е нов ресурс\n  download_failed_body: >-\n    Нещо се е объркало при изтеглянето на ресурса, моля, докладвайте за това на\n    екипа на разработчиците на Seelen.\n  download_failed_title: Изтеглянето на ресурса е неуспешно\n  downloading: Изтегляне на активи...\n  downloading_body: Това може да отнеме няколко секунди, моля, не затваряйте прозореца.\n  enable: Активиране на\nruntime:\n  corrupted_data: Повредени данни\n  corrupted_file: >-\n    Конфигурационният файл е повреден, като вместо него се използват стойности\n    по подразбиране.\n  corrupted_file_path: Повреден път на файла\n  download: Отидете на страницата за изтегляне\n  files_integrity: >-\n    Не можахме да проверим целостта на файловете на приложението.\n\n    Това може да се случи, ако файловете са били модифицирани, повредени или\n    инсталацията е непълна.\n\n    От съображения за сигурност Seelen UI не може да продължи.\n\n    Моля, преинсталирайте го от официалния уебсайт.\n  files_integrity_title: >-\n    Предупреждение за сигурност - Проверката на целостта на файловете е\n    неуспешна\n  not_found: WebView2 Runtime не е намерен\n  not_found_description: Seelen UI изисква Webview2 Runtime. Моля, инсталирайте го.\n  outdated: WebView2 Runtime е остарял\n  outdated_description: >-\n    Seelen UI изисква Webview2 Runtime %{min_version} или по-нова версия. Моля,\n    актуализирайте го.\nservice:\n  not_running: Услугата Seelen UI не работи\n  not_running_description: >-\n    Seelen UI Service (slu-service) е зависимост/помощник за правилното\n    функциониране на приложението.\n  not_running_ok: Стартирайте услугата\nshortcut:\n  register:\n    placeholder: Натиснете всяка комбинация от клавиши\n    title: Регистриране на нов пряк път\nwidget_liveness:\n  failed_description: |-\n    Приспособлението „%{widget_name}“ спря да отговаря твърде много пъти.\n\n    Можете да опитате да рестартирате приложението.\n  failed_title: Грешка в притурката\nwidget_permissions:\n  perm_open_file: отворени файлове\n  perm_run: стартирайте програми\n  request_description: |-\n    Приспособлението „%{widget_name}“ иска разрешение за %{command}.\n\n    Искате ли да разрешите това?\n  request_title: Заявка за разрешение за джаджа\n"
  },
  {
    "path": "src/background/i18n/bn.yml",
    "content": "cancel: বাতিল\ndone: সম্পন্ন\nelevated:\n  description: >-\n    Seelen UI প্রশাসকের বিশেষাধিকারের সাথে চলছে।\n\n    এটি কোনও বৈশিষ্ট্য যুক্ত বা উন্নত করে না এবং এটি একটি নিরাপত্তা ঝুঁকি তৈরি\n    করতে পারে।\n\n    উন্নত নিরাপত্তার জন্য, প্রশাসকের অনুমতি ছাড়াই এটি পুনরায় চালু করুন।\n  title: প্রশাসক হিসাবে চলমান\nfile_explorer: ফাইল এক্সপ্লোরার\nresource:\n  added: একটি নতুন সংস্থান যুক্ত করা হয়েছে\n  download_failed_body: >-\n    রিসোর্সটি ডাউনলোড করার সময় কিছু ভুল হয়ে গেছে, দয়া করে এটি সিলেন বিকাশকারী\n    দলকে রিপোর্ট করুন।\n  download_failed_title: রিসোর্স ডাউনলোড ব্যর্থ\n  downloading: সম্পদ ডাউনলোড করা ...\n  downloading_body: এটি কয়েক সেকেন্ড সময় নিতে পারে, দয়া করে উইন্ডোটি বন্ধ করবেন না।\n  enable: সক্ষম করুন\nruntime:\n  corrupted_data: দূষিত ডেটা\n  corrupted_file: পরিবর্তে ডিফল্ট মানগুলি ব্যবহার করে একটি কনফিগারেশন ফাইল দূষিত হয়।\n  corrupted_file_path: দূষিত ফাইল পাথ\n  download: ডাউনলোড পেজে যান\n  files_integrity: >-\n    আমরা অ্যাপ্লিকেশন ফাইলগুলির অখণ্ডতা যাচাই করতে পারিনি৷\n\n    এটি ঘটতে পারে যদি ফাইলগুলি সংশোধন করা হয়, দূষিত হয় বা ইনস্টলেশনটি\n    অসম্পূর্ণ থাকে।\n\n    নিরাপত্তার কারণে, Seelen UI চালিয়ে যেতে পারে না।\n\n    অফিসিয়াল ওয়েবসাইট থেকে এটি পুনরায় ইনস্টল করুন.\n  files_integrity_title: নিরাপত্তা সতর্কতা - ফাইলের অখণ্ডতা পরীক্ষা ব্যর্থ হয়েছে৷\n  not_found: WebView2 রানটাইম পাওয়া যায়নি\n  not_found_description: Seelen UI এর জন্য Webview2 রানটাইম প্রয়োজন। এটা ইনস্টল করুন.\n  outdated: WebView2 রানটাইম পুরানো\n  outdated_description: >-\n    Seelen UI এর জন্য Webview2 রানটাইম %{min_version} বা উচ্চতর প্রয়োজন। এটা\n    আপডেট করুন.\nservice:\n  not_running: Seelen UI পরিষেবা চলছে না\n  not_running_description: >-\n    সিলেন ইউআই সার্ভিস (স্লু-সার্ভিস) অ্যাপটি সঠিকভাবে কাজ করার জন্য একটি\n    নির্ভরতা/সহায়ক।\n  not_running_ok: পরিষেবা শুরু করুন\nshortcut:\n  register:\n    placeholder: যে কোনও কী সংমিশ্রণ টিপুন\n    title: নতুন শর্টকাট নিবন্ধন\nwidget_liveness:\n  failed_description: |-\n    উইজেট '%{widget_name}' অনেকবার সাড়া দেওয়া বন্ধ করেছে।\n\n    আপনি অ্যাপটি পুনরায় চালু করার চেষ্টা করতে পারেন।\n  failed_title: উইজেট ত্রুটি\nwidget_permissions:\n  perm_open_file: ফাইল খুলুন\n  perm_run: প্রোগ্রাম চালান\n  request_description: |-\n    উইজেট '%{widget_name}' %{command}-এর কাছে অনুমতির অনুরোধ করছে।\n\n    আপনি এই অনুমতি দিতে চান?\n  request_title: উইজেট অনুমতি অনুরোধ\n"
  },
  {
    "path": "src/background/i18n/bs.yml",
    "content": "cancel: Otkazati\ndone: Gotov\nelevated:\n  description: >-\n    Seelen UI radi sa administratorskim privilegijama.\n\n    Ovo ne dodaje niti poboljšava nijednu funkciju i može predstavljati\n    sigurnosni rizik.\n\n    Radi bolje sigurnosti, ponovo ga pokrenite bez administratorskih dozvola.\n  title: Pokreće se kao administrator\nfile_explorer: File Explorer\nresource:\n  added: Dodan je novi resurs\n  download_failed_body: >-\n    Nešto je pošlo po zlu prilikom preuzimanja resursa, prijavite ga u Timu\n    SEELEN programera.\n  download_failed_title: Preuzimanje resursa nije uspjelo\n  downloading: Preuzimanje imovine ...\n  downloading_body: Ovo bi moglo potrajati nekoliko sekundi, molim vas nemojte zatvoriti prozor.\n  enable: Omogućiti\nruntime:\n  corrupted_data: Oštećeni podaci\n  corrupted_file: >-\n    Konfiguracijska datoteka je oštećena, koristeći zadane vrijednosti umjesto\n    toga.\n  corrupted_file_path: Korumpirana staza datoteke\n  download: Idite na stranicu za preuzimanje\n  files_integrity: >-\n    Nismo mogli provjeriti integritet datoteka aplikacije.\n\n    Ovo se može dogoditi ako su datoteke izmijenjene, oštećene ili instalacija\n    nije dovršena.\n\n    Iz sigurnosnih razloga, Seelen UI ne može nastaviti.\n\n    Ponovo ga instalirajte sa službene web stranice.\n  files_integrity_title: Sigurnosno upozorenje - provjera integriteta datoteka nije uspjela\n  not_found: WebView2 Runtime nije pronađeno\n  not_found_description: Seelen UI zahtijeva Webview2 Runtime. Molimo instalirajte ga.\n  outdated: WebView2 Runtime je zastarjelo\n  outdated_description: >-\n    Seelen UI zahtijeva Webview2 Runtime %{min_version} ili noviju. Ažurirajte\n    ga.\nservice:\n  not_running: Seelen UI usluga ne radi\n  not_running_description: >-\n    Seelen UI Service (slu-service) je zavisnost/pomoćnik za pravilno\n    funkcioniranje aplikacije.\n  not_running_ok: Pokreni servis\nshortcut:\n  register:\n    placeholder: Pritisnite bilo koju kombinaciju tipki\n    title: Registriranje nove prečice\nwidget_liveness:\n  failed_description: |-\n    Vidžet '%{widget_name}' je prestao da odgovara previše puta.\n\n    Možete pokušati ponovo pokrenuti aplikaciju.\n  failed_title: Widget Error\nwidget_permissions:\n  perm_open_file: otvorite fajlove\n  perm_run: pokrenuti programe\n  request_description: |-\n    Vidžet '%{widget_name}' traži dozvolu za %{command}.\n\n    Želite li ovo dozvoliti?\n  request_title: Zahtjev za dozvolu za widget\n"
  },
  {
    "path": "src/background/i18n/ca.yml",
    "content": "cancel: Cancel·lar\ndone: Fer\nelevated:\n  description: >-\n    La interfície d'usuari de Seelen s'està executant amb privilegis\n    d'administrador.\n\n    Això no afegeix ni millora cap funció i pot suposar un risc de seguretat.\n\n    Per a una millor seguretat, reinicieu-lo sense permís d'administrador.\n  title: Executant com a administrador\nfile_explorer: Explorador de fitxers\nresource:\n  added: S'ha afegit un nou recurs\n  download_failed_body: >-\n    Alguna cosa va passar malament mentre descarregueu el recurs, informeu -lo a\n    l'equip de desenvolupadors de Seelen.\n  download_failed_title: Descàrrega de recursos ha fallat\n  downloading: Descarregar actius ...\n  downloading_body: Això pot trigar uns segons, si us plau, no tanqueu la finestra.\n  enable: Capacitar\nruntime:\n  corrupted_data: Dades corrompudes\n  corrupted_file: >-\n    En canvi, un fitxer de configuració està corromput, utilitzant valors\n    predeterminats.\n  corrupted_file_path: Ruta del fitxer corromput\n  download: Aneu a la pàgina de descàrrega\n  files_integrity: >-\n    No hem pogut verificar la integritat dels fitxers de l'aplicació.\n\n    Això pot passar si els fitxers es van modificar, es van malmetre o la\n    instal·lació està incompleta.\n\n    Per motius de seguretat, la interfície d'usuari de Seelen no pot continuar.\n\n    Torneu-lo a instal·lar des del lloc web oficial.\n  files_integrity_title: 'Alerta de seguretat: la comprovació d''integritat dels fitxers ha fallat'\n  not_found: No s'ha trobat el temps d'execució de WebView2\n  not_found_description: La interfície d'usuari de Seelen requereix Webview2 Runtime. Instal·leu-lo.\n  outdated: Temps d'execució de WebView2 obsolet\n  outdated_description: >-\n    La IU de Seelen requereix el temps d'execució de Webview2 %{min_version} o\n    superior. Si us plau, actualitzeu-lo.\nservice:\n  not_running: El servei d'IU de Seelen no s'executa\n  not_running_description: >-\n    Seelen UI Service (servei slu) és una dependència/ajuda perquè l'aplicació\n    funcioni correctament.\n  not_running_ok: Iniciar servei\nshortcut:\n  register:\n    placeholder: Premeu qualsevol combinació de tecles\n    title: Registre de nou drecera\nwidget_liveness:\n  failed_description: |-\n    El widget '%{widget_name}' va deixar de respondre massa vegades.\n\n    Pots provar de reiniciar l'aplicació.\n  failed_title: Error del widget\nwidget_permissions:\n  perm_open_file: obrir fitxers\n  perm_run: executar programes\n  request_description: |-\n    El widget '%{widget_name}' demana permís a %{command}.\n\n    Vols permetre això?\n  request_title: Sol·licitud de permís de widget\n"
  },
  {
    "path": "src/background/i18n/cs.yml",
    "content": "cancel: Zrušit\ndone: Hotovo\nelevated:\n  description: >-\n    Seelen UI běží s oprávněními správce.\n\n    To nepřidává ani nevylepšuje žádné funkce a může představovat bezpečnostní\n    riziko.\n\n    Pro lepší zabezpečení jej restartujte bez oprávnění správce.\n  title: Spuštění jako správce\nfile_explorer: Průzkumník souborů\nresource:\n  added: Byl přidán nový zdroj\n  download_failed_body: >-\n    Při stahování zdroje se něco pokazilo, nahlaste to prosím týmu vývojářů\n    Seelenu.\n  download_failed_title: Stažení prostředků se nezdařilo\n  downloading: Stahování aktiv...\n  downloading_body: To může trvat několik sekund, nezavírejte proto okno.\n  enable: Povolit\nruntime:\n  corrupted_data: Poškozená data\n  corrupted_file: Konfigurační soubor je poškozen a místo něj se používají výchozí hodnoty.\n  corrupted_file_path: Poškozená cesta k souboru\n  download: Přejděte na stránku stahování\n  files_integrity: >-\n    Nepodařilo se nám ověřit integritu souborů aplikace.\n\n    K tomu může dojít, pokud byly soubory změněny, poškozeny nebo instalace není\n    úplná.\n\n    Z bezpečnostních důvodů nemůže Seelen UI pokračovat.\n\n    Nainstalujte jej znovu z oficiálních stránek.\n  files_integrity_title: Výstraha zabezpečení – Kontrola integrity souborů se nezdařila\n  not_found: WebView2 Runtime nebyl nalezen\n  not_found_description: Seelen UI vyžaduje Webview2 Runtime. Nainstalujte jej prosím.\n  outdated: WebView2 Runtime je zastaralé\n  outdated_description: >-\n    Seelen UI vyžaduje Webview2 Runtime %{min_version} nebo vyšší. Aktualizujte\n    jej.\nservice:\n  not_running: Služba Seelen UI neběží\n  not_running_description: >-\n    Seelen UI Service (slu-service) je závislost/pomocník pro správné fungování\n    aplikace.\n  not_running_ok: Spusťte službu\nshortcut:\n  register:\n    placeholder: Stiskněte libovolnou kombinaci klíčů\n    title: Registrace nové zkratky\nwidget_liveness:\n  failed_description: |-\n    Widget '%{widget_name}' přestal reagovat příliš mnohokrát.\n\n    Můžete zkusit restartovat aplikaci.\n  failed_title: Chyba widgetu\nwidget_permissions:\n  perm_open_file: otevřít soubory\n  perm_run: spouštět programy\n  request_description: |-\n    Widget '%{widget_name}' požaduje oprávnění k %{command}.\n\n    Chcete to povolit?\n  request_title: Žádost o povolení k widgetu\n"
  },
  {
    "path": "src/background/i18n/cy.yml",
    "content": "cancel: Chansliff\ndone: Wedi gwneud\nelevated:\n  description: >-\n    Mae Seelen UI yn rhedeg gyda breintiau gweinyddwr.\n\n    Nid yw hyn yn ychwanegu nac yn gwella unrhyw nodweddion, a gall achosi risg\n    diogelwch.\n\n    Er mwyn gwella diogelwch, ailgychwynnwch ef heb ganiatâd gweinyddwr.\n  title: Rhedeg fel Gweinyddwr\nfile_explorer: Archwiliwr Ffeil\nresource:\n  added: Ychwanegwyd adnodd newydd\n  download_failed_body: >-\n    Aeth rhywbeth o'i le wrth lawrlwytho'r adnodd, rhowch wybod iddo i dîm\n    datblygwyr Seelen.\n  download_failed_title: Methodd Lawrlwytho Adnoddau\n  downloading: Lawrlwytho asedau ...\n  downloading_body: Efallai y bydd hyn yn cymryd ychydig eiliadau, peidiwch â chau'r ffenestr.\n  enable: Galluoga\nruntime:\n  corrupted_data: Data llygredig\n  corrupted_file: Mae ffeil ffurfweddu yn llygredig, gan ddefnyddio gwerthoedd diofyn yn lle.\n  corrupted_file_path: Llwybr Ffeil Llygredig\n  download: Ewch i'r dudalen lawrlwytho\n  files_integrity: >-\n    Ni allem wirio cywirdeb y ffeiliau cais.\n\n    Gall hyn ddigwydd os cafodd ffeiliau eu haddasu, eu llygru, neu os yw'r\n    gosodiad yn anghyflawn.\n\n    Am resymau diogelwch, ni all Seelen UI barhau.\n\n    Ailosodwch ef o'r wefan swyddogol.\n  files_integrity_title: Rhybudd Diogelwch - Methwyd Gwirio Cywirdeb Ffeiliau\n  not_found: WebView2 Runtime heb ei ganfod\n  not_found_description: Mae Seelen UI angen Amser Rhedeg Webview2. Os gwelwch yn dda gosod.\n  outdated: WebView2 Runtime wedi dyddio\n  outdated_description: >-\n    Mae Seelen UI angen amser rhedeg Webview2 %{ min_version} neu uwch.\n    Diweddarwch ef.\nservice:\n  not_running: Gwasanaeth UI Seelen ddim yn rhedeg\n  not_running_description: >-\n    Mae Seelen UI Service (slu-service) yn ddibyniaeth / cynorthwyydd i'r ap\n    weithio'n iawn.\n  not_running_ok: Dechrau gwasanaeth\nshortcut:\n  register:\n    placeholder: Pwyswch unrhyw gyfuniad allweddol\n    title: Cofrestru llwybr byr newydd\nwidget_liveness:\n  failed_description: |-\n    Mae'r teclyn '%{widget_name}' wedi stopio ymateb gormod o weithiau.\n\n    Gallwch geisio ailgychwyn yr app.\n  failed_title: Gwall Teclyn\nwidget_permissions:\n  perm_open_file: agor ffeiliau\n  perm_run: rhedeg rhaglenni\n  request_description: |-\n    Mae'r teclyn '%{widget_name}' yn gofyn am ganiatâd i %{command}.\n\n    Ydych chi am ganiatáu hyn?\n  request_title: Cais am Ganiatâd Widget\n"
  },
  {
    "path": "src/background/i18n/da.yml",
    "content": "cancel: Ophæve\ndone: Færdig\nelevated:\n  description: >-\n    Seelen UI kører med administratorrettigheder.\n\n    Dette tilføjer eller forbedrer ikke nogen funktioner, og det kan udgøre en\n    sikkerhedsrisiko.\n\n    For bedre sikkerhed skal du genstarte den uden administratortilladelser.\n  title: Kører som administrator\nfile_explorer: Fil Explorer\nresource:\n  added: En ny ressource er blevet tilføjet\n  download_failed_body: >-\n    Noget gik galt under download af ressourcen, rapporter det venligst til\n    Seelen-udviklerteamet.\n  download_failed_title: Download af ressourcer mislykkedes\n  downloading: Download af aktiver...\n  downloading_body: Det kan tage et par sekunder, så lad være med at lukke vinduet.\n  enable: Gør det muligt\nruntime:\n  corrupted_data: Beskadigede data\n  corrupted_file: En konfigurationsfil er beskadiget og bruger standardværdier i stedet.\n  corrupted_file_path: Beskadiget filsti\n  download: Gå til downloadsiden\n  files_integrity: >-\n    Vi kunne ikke bekræfte integriteten af ​​applikationsfilerne.\n\n    Dette kan ske, hvis filer er blevet ændret, ødelagt, eller installationen er\n    ufuldstændig.\n\n    Af sikkerhedsmæssige årsager kan Seelen UI ikke fortsætte.\n\n    Geninstaller det fra den officielle hjemmeside.\n  files_integrity_title: Sikkerhedsadvarsel - Filintegritetstjek mislykkedes\n  not_found: WebView2 Runtime ikke fundet\n  not_found_description: Seelen UI kræver Webview2 Runtime. Installer det venligst.\n  outdated: WebView2 Runtime forældet\n  outdated_description: >-\n    Seelen UI kræver Webview2 Runtime %{min_version} eller højere. Opdater det\n    venligst.\nservice:\n  not_running: Seelen UI Service kører ikke\n  not_running_description: >-\n    Seelen UI Service (slu-service) er en afhængighed/hjælper for, at appen\n    fungerer korrekt.\n  not_running_ok: Start service\nshortcut:\n  register:\n    placeholder: Tryk på enhver nøglekombination\n    title: Registrering af ny genvej\nwidget_liveness:\n  failed_description: |-\n    Widgetten '%{widget_name}' holdt op med at svare for mange gange.\n\n    Du kan prøve at genstarte appen.\n  failed_title: Widget-fejl\nwidget_permissions:\n  perm_open_file: åbne filer\n  perm_run: køre programmer\n  request_description: |-\n    Widgetten '%{widget_name}' anmoder om tilladelse til %{command}.\n\n    Vil du tillade dette?\n  request_title: Anmodning om widgettilladelse\n"
  },
  {
    "path": "src/background/i18n/de.yml",
    "content": "cancel: Stornieren\ndone: Erledigt\nelevated:\n  description: >-\n    Seelen UI läuft mit Administratorrechten.\n\n    Dadurch werden keine Funktionen hinzugefügt oder verbessert und es kann ein\n    Sicherheitsrisiko darstellen.\n\n    Aus Sicherheitsgründen starten Sie es bitte ohne Administratorrechte neu.\n  title: Als Administrator ausführen\nfile_explorer: Datei-Explorer\nresource:\n  added: Es wurde eine neue Ressource hinzugefügt\n  download_failed_body: >-\n    Beim Herunterladen der Ressource ist ein Fehler aufgetreten. Bitte melden\n    Sie dies dem Seelen-Entwicklerteam.\n  download_failed_title: Ressourcendownload fehlgeschlagen\n  downloading: Herunterladen von Assets...\n  downloading_body: Dies kann ein paar Sekunden dauern, bitte schließen Sie das Fenster nicht.\n  enable: Aktivieren Sie\nruntime:\n  corrupted_data: Beschädigte Daten\n  corrupted_file: >-\n    Eine Konfigurationsdatei ist beschädigt und verwendet stattdessen\n    Standardwerte.\n  corrupted_file_path: Korrupter Dateipfad\n  download: Gehen Sie zur Download-Seite\n  files_integrity: >-\n    Wir konnten die Integrität der Anwendungsdateien nicht überprüfen.\n\n    Dies kann passieren, wenn Dateien geändert oder beschädigt wurden oder die\n    Installation unvollständig ist.\n\n    Aus Sicherheitsgründen kann Seelen UI nicht fortgesetzt werden.\n\n    Bitte installieren Sie es erneut von der offiziellen Website.\n  files_integrity_title: Sicherheitswarnung – Dateiintegritätsprüfung fehlgeschlagen\n  not_found: WebView2-Laufzeit nicht gefunden\n  not_found_description: >-\n    Für die Seelen-Benutzeroberfläche ist Webview2 Runtime erforderlich. Bitte\n    installieren Sie es.\n  outdated: WebView2-Laufzeit veraltet\n  outdated_description: >-\n    Für die Seelen-Benutzeroberfläche ist Webview2 Runtime %{min_version} oder\n    höher erforderlich. Bitte aktualisieren Sie es.\nservice:\n  not_running: Der Seelen-UI-Dienst läuft nicht\n  not_running_description: >-\n    Der Seelen UI Service (slu-service) ist eine Abhängigkeit/Helfer für die\n    ordnungsgemäße Funktion der App.\n  not_running_ok: Dienst starten\nshortcut:\n  register:\n    placeholder: Drücken Sie eine beliebige Tastenkombination\n    title: Registrierung einer neuen Verknüpfung\nwidget_liveness:\n  failed_description: |-\n    Das Widget „%{widget_name}“ reagierte zu oft nicht mehr.\n\n    Sie können versuchen, die App neu zu starten.\n  failed_title: Widget-Fehler\nwidget_permissions:\n  perm_open_file: Dateien öffnen\n  perm_run: Programme ausführen\n  request_description: |-\n    Das Widget „%{widget_name}“ fordert die Berechtigung für %{command} an.\n\n    Möchten Sie dies zulassen?\n  request_title: Widget-Berechtigungsanfrage\n"
  },
  {
    "path": "src/background/i18n/el.yml",
    "content": "cancel: Ματαίωση\ndone: Γινώμενος\nelevated:\n  description: >-\n    Το Seelen UI εκτελείται με δικαιώματα διαχειριστή.\n\n    Αυτό δεν προσθέτει ή βελτιώνει καμία δυνατότητα και μπορεί να θέσει σε\n    κίνδυνο την ασφάλεια.\n\n    Για καλύτερη ασφάλεια, επανεκκινήστε το χωρίς δικαιώματα διαχειριστή.\n  title: Τρέχει ως Διαχειριστής\nfile_explorer: Εξερεύνηση αρχείων\nresource:\n  added: Προστέθηκε ένας νέος πόρος\n  download_failed_body: >-\n    Κάτι πήγε στραβά κατά τη λήψη του πόρου, παρακαλούμε αναφέρετέ το στην ομάδα\n    ανάπτυξης του Seelen.\n  download_failed_title: Η λήψη πόρων απέτυχε\n  downloading: Λήψη περιουσιακών στοιχείων...\n  downloading_body: Αυτό μπορεί να διαρκέσει μερικά δευτερόλεπτα, μην κλείσετε το παράθυρο.\n  enable: Ενεργοποίηση\nruntime:\n  corrupted_data: Κατεστραμμένα δεδομένα\n  corrupted_file: Ένα αρχείο ρυθμίσεων έχει καταστραφεί, χρησιμοποιώντας προεπιλεγμένες τιμές.\n  corrupted_file_path: Κατεστραμμένη διαδρομή αρχείου\n  download: Μεταβείτε στη σελίδα λήψης\n  files_integrity: >-\n    Δεν μπορέσαμε να επαληθεύσουμε την ακεραιότητα των αρχείων εφαρμογής.\n\n    Αυτό μπορεί να συμβεί εάν τα αρχεία έχουν τροποποιηθεί, καταστραφεί ή η\n    εγκατάσταση δεν έχει ολοκληρωθεί.\n\n    Για λόγους ασφαλείας, το Seelen UI δεν μπορεί να συνεχίσει.\n\n    Εγκαταστήστε το ξανά από τον επίσημο ιστότοπο.\n  files_integrity_title: Ειδοποίηση ασφαλείας - Ο έλεγχος ακεραιότητας αρχείων απέτυχε\n  not_found: Ο χρόνος εκτέλεσης WebView2 δεν βρέθηκε\n  not_found_description: Το Seelen UI απαιτεί Webview2 Runtime. Εγκαταστήστε το.\n  outdated: Ο χρόνος εκτέλεσης WebView2 είναι ξεπερασμένος\n  outdated_description: >-\n    Η διεπαφή χρήστη Seelen απαιτεί Webview2 Runtime %{min_version} ή υψηλότερη.\n    Ενημερώστε το.\nservice:\n  not_running: Η υπηρεσία διεπαφής χρήστη Seelen δεν εκτελείται\n  not_running_description: >-\n    Η υπηρεσία Seelen UI (slu-service) είναι μια εξάρτηση/βοήθεια για τη σωστή\n    λειτουργία της εφαρμογής.\n  not_running_ok: Ξεκινήστε την υπηρεσία\nshortcut:\n  register:\n    placeholder: Πατήστε οποιοδήποτε συνδυασμό πλήκτρων\n    title: Εγγραφή νέας συντόμευσης\nwidget_liveness:\n  failed_description: >-\n    Το γραφικό στοιχείο '%{widget_name}' σταμάτησε να ανταποκρίνεται πάρα πολλές\n    φορές.\n\n\n    Μπορείτε να δοκιμάσετε να επανεκκινήσετε την εφαρμογή.\n  failed_title: Σφάλμα widget\nwidget_permissions:\n  perm_open_file: ανοιχτά αρχεία\n  perm_run: τρέξτε προγράμματα\n  request_description: |-\n    Το γραφικό στοιχείο '%{widget_name}' ζητά άδεια για %{command}.\n\n    Θέλετε να το επιτρέψετε αυτό;\n  request_title: Αίτημα άδειας widget\n"
  },
  {
    "path": "src/background/i18n/en.yml",
    "content": "cancel: Cancel\ndone: Done\nelevated:\n  description: |-\n    Seelen UI is running with administrator privileges.\n    This does not add or improve any features, and it may pose a security risk.\n    For better security, please restart it without administrator permissions.\n  title: Running as Administrator\nfile_explorer: File Explorer\nresource:\n  added: A new resource has been added\n  download_failed_body: >-\n    Something went wrong while downloading the resource, please report it to the\n    Seelen developer team.\n  download_failed_title: Resource download failed\n  downloading: Downloading assets...\n  downloading_body: This might take a few seconds, please don't close the window.\n  enable: Enable\nruntime:\n  corrupted_data: Corrupted data\n  corrupted_file: A configuration file is corrupted, using default values instead.\n  corrupted_file_path: Corrupted file path\n  download: Go to download page\n  files_integrity: >-\n    We couldn't verify the integrity of the application files.\n\n    This can happen if files were modified, corrupted, or the installation is\n    incomplete.\n\n    For security reasons, Seelen UI cannot continue.\n\n    Please reinstall it from the official website.\n  files_integrity_title: Security Alert - Files Integrity Check Failed\n  not_found: WebView2 Runtime not found\n  not_found_description: Seelen UI requires Webview2 Runtime. Please install it.\n  outdated: WebView2 Runtime outdated\n  outdated_description: >-\n    Seelen UI requires Webview2 Runtime %{min_version} or higher. Please update\n    it.\nservice:\n  not_running: Seelen UI Service not running\n  not_running_description: >-\n    Seelen UI Service (slu-service) is a dependency/helper for the app to work\n    properly.\n  not_running_ok: Start service\nshortcut:\n  register:\n    placeholder: Press any key combination\n    title: Registering new shortcut\nwidget_liveness:\n  failed_description: |-\n    The widget '%{widget_name}' stopped responding too many times.\n\n    You can try restarting the app.\n  failed_title: Widget Error\nwidget_permissions:\n  perm_open_file: open files\n  perm_run: run programs\n  request_description: |-\n    The widget '%{widget_name}' is requesting permission to %{command}.\n\n    Do you want to allow this?\n  request_title: Widget Permission Request\n"
  },
  {
    "path": "src/background/i18n/es.yml",
    "content": "cancel: Cancelar\ndone: Hecho\nelevated:\n  description: >-\n    La interfaz de usuario de Seelen se ejecuta con privilegios de\n    administrador.\n\n    Esto no agrega ni mejora ninguna característica y puede representar un\n    riesgo de seguridad.\n\n    Para mayor seguridad, reinícielo sin permisos de administrador.\n  title: Corriendo como administradora\nfile_explorer: Exploradora de archivos\nresource:\n  added: Se ha añadido un nuevo recurso\n  download_failed_body: >-\n    Algo ha ido mal al descargar el recurso, por favor informa de ello al equipo\n    de desarrolladores de Seelen.\n  download_failed_title: Fallo en la descarga de recursos\n  downloading: Descarga de activos...\n  downloading_body: Esto puede tardar unos segundos, por favor no cierre la ventana.\n  enable: Activar\nruntime:\n  corrupted_data: Datos dañados\n  corrupted_file: >-\n    Un archivo de configuración está dañado, utilizando valores por defecto en\n    su lugar.\n  corrupted_file_path: Ruta de archivo dañada\n  download: Ir a la página de descarga\n  files_integrity: >-\n    No pudimos verificar la integridad de los archivos de la aplicación.\n\n    Esto puede suceder si los archivos fueron modificados, dañados o la\n    instalación está incompleta.\n\n    Por razones de seguridad, Seelen UI no puede continuar.\n\n    Vuelva a instalarlo desde el sitio web oficial.\n  files_integrity_title: \"Alerta de seguridad: error en la comprobación de integridad de archivos\"\n  not_found: Tiempo de ejecución de WebView2 no encontrado\n  not_found_description: >-\n    La interfaz de usuario de Seelen requiere Webview2 Runtime. Por favor\n    instálelo.\n  outdated: Tiempo de ejecución de WebView2 desactualizado\n  outdated_description: >-\n    La interfaz de usuario de Seelen requiere Webview2 Runtime %{min_version} o\n    superior. Por favor actualícelo.\nservice:\n  not_running: El servicio Seelen UI no se ejecuta\n  not_running_description: >-\n    Seelen UI Service (slu-service) es una dependencia/ayudante para que la\n    aplicación funcione correctamente.\n  not_running_ok: Iniciar servicio\nshortcut:\n  register:\n    placeholder: Presione cualquier combinación de teclas\n    title: Registrando un nuevo atajo\nwidget_liveness:\n  failed_description: |-\n    El widget '%{widget_name}' dejó de responder demasiadas veces.\n\n    Puedes intentar reiniciar la aplicación.\n  failed_title: Error de widget\nwidget_permissions:\n  perm_open_file: abrir archivos\n  perm_run: ejecutar programas\n  request_description: |-\n    El widget '%{widget_name}' solicita permiso para %{command}.\n\n    ¿Quieres permitir esto?\n  request_title: Solicitud de permiso de widget\n"
  },
  {
    "path": "src/background/i18n/et.yml",
    "content": "cancel: Tühistama\ndone: Tehtud\nelevated:\n  description: >-\n    Seelen UI töötab administraatoriõigustega.\n\n    See ei lisa ega täiusta ühtegi funktsiooni ning võib kujutada endast\n    turvariski.\n\n    Parema turvalisuse tagamiseks taaskäivitage see ilma\n    administraatoriõigusteta.\n  title: Töötab administraatorina\nfile_explorer: File Explorer\nresource:\n  added: Lisatud on uus ressurss\n  download_failed_body: >-\n    Midagi läks ressursi allalaadimisel valesti, palun teatage sellest Seeleni\n    arendusmeeskonnale.\n  download_failed_title: Ressursi allalaadimine ebaõnnestus\n  downloading: Varade allalaadimine...\n  downloading_body: See võib võtta paar sekundit, palun ärge sulgege akent.\n  enable: Lubage\nruntime:\n  corrupted_data: Vigastatud andmed\n  corrupted_file: Konfiguratsioonifail on rikutud, kasutades selle asemel vaikeväärtusi.\n  corrupted_file_path: Vigastatud faili tee\n  download: Minge allalaadimislehele\n  files_integrity: |-\n    Meil ei õnnestunud rakendusfailide terviklikkust kontrollida.\n    See võib juhtuda, kui faile on muudetud, rikutud või installimine on poolik.\n    Turvakaalutlustel ei saa Seelen UI jätkata.\n    Installige see uuesti ametlikult veebisaidilt.\n  files_integrity_title: Turvahoiatus – failide terviklikkuse kontroll ebaõnnestus\n  not_found: WebView2 käitusaega ei leitud\n  not_found_description: Seeleni kasutajaliides nõuab Webview2 Runtime'i. Palun installige see.\n  outdated: WebView2 käitusaeg on aegunud\n  outdated_description: >-\n    Seeleni kasutajaliides nõuab Webview2 Runtime %{min_version} või uuemat.\n    Palun värskendage seda.\nservice:\n  not_running: Seelen UI teenus ei tööta\n  not_running_description: >-\n    Seeleni kasutajaliidese teenus (slu-teenus) on rakenduse nõuetekohaseks\n    tööks sõltuvus/abimees.\n  not_running_ok: Käivitage teenus\nshortcut:\n  register:\n    placeholder: Vajutage mis tahes võtmekombinatsiooni\n    title: Uue otsetee registreerimine\nwidget_liveness:\n  failed_description: |-\n    Vidin '%{widget_name}' lõpetas reageerimise liiga palju kordi.\n\n    Võite proovida rakenduse taaskäivitada.\n  failed_title: Vidina viga\nwidget_permissions:\n  perm_open_file: failid avada\n  perm_run: käivitada programme\n  request_description: |-\n    Vidin '%{widget_name}' taotleb luba käsule %{command}.\n\n    Kas soovite seda lubada?\n  request_title: Vidina loa taotlus\n"
  },
  {
    "path": "src/background/i18n/eu.yml",
    "content": "cancel: Indargabetu\ndone: Egin\nelevated:\n  description: >-\n    Seelen UI administratzaile-pribilegioekin exekutatzen ari da.\n\n    Honek ez du ezaugarririk gehitzen edo hobetzen, eta segurtasun arriskua sor\n    dezake.\n\n    Segurtasun hobea lortzeko, mesedez berrabiarazi administratzaile baimenik\n    gabe.\n  title: Administratzaile gisa exekutatzen\nfile_explorer: Fitxategien arakatzailea\nresource:\n  added: Baliabide berri bat gehitu da\n  download_failed_body: >-\n    Zerbait gaizki joan da baliabidea deskargatzean, jakinarazi Seelen\n    garatzaileen taldeari.\n  download_failed_title: Baliabideen deskargak huts egin du\n  downloading: Aktiboak deskargatzen ...\n  downloading_body: Honek segundo batzuk iraun ditzake, mesedez, ez itxi leihoa.\n  enable: Ahalbidetu\nruntime:\n  corrupted_data: Hondatutako datuak\n  corrupted_file: >-\n    Konfigurazio fitxategia hondatuta dago, horren ordez balio lehenetsiak\n    erabiliz.\n  corrupted_file_path: Fitxategiaren bide hondatua\n  download: Joan deskarga orrira\n  files_integrity: >-\n    Ezin izan dugu egiaztatu aplikazioaren fitxategien osotasuna.\n\n    Hau gerta daiteke fitxategiak aldatu, hondatuta edo instalazioa osatu gabe\n    badago.\n\n    Segurtasun arrazoiengatik, Seelen UI ezin da jarraitu.\n\n    Mesedez, berriro instalatu webgune ofizialetik.\n  files_integrity_title: Segurtasun alerta - Huts egin du fitxategien osotasuna egiaztatzea\n  not_found: WebView2 Runtime ez da aurkitu\n  not_found_description: Seelen UI-k Webview2 Runtime behar du. Mesedez, instala ezazu.\n  outdated: WebView2 Exekuzio-denbora zaharkituta dago\n  outdated_description: >-\n    Seelen UI-k Webview2 Runtime %{min_version} edo handiagoa behar du. Mesedez,\n    eguneratu.\nservice:\n  not_running: Seelen UI Zerbitzua ez dago martxan\n  not_running_description: >-\n    Seelen UI Zerbitzua (slu-zerbitzua) menpekotasun/laguntzailea da aplikazioak\n    behar bezala funtziona dezan.\n  not_running_ok: Hasi zerbitzua\nshortcut:\n  register:\n    placeholder: Sakatu edozein tekla konbinazio\n    title: Lasterbide berria erregistratzea\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' widgetak gehiegitan erantzuteari utzi zion.\n\n    Aplikazioa berrabiarazten saia zaitezke.\n  failed_title: Widgetaren errorea\nwidget_permissions:\n  perm_open_file: ireki fitxategiak\n  perm_run: exekutatu programak\n  request_description: |-\n    '%{widget_name}' widget-ak %{command}-i baimena eskatzen dio.\n\n    Hau onartu nahi duzu?\n  request_title: Widget-en baimen-eskaera\n"
  },
  {
    "path": "src/background/i18n/fa.yml",
    "content": "cancel: لغو کردن\ndone: انجام شده\nelevated:\n  description: |-\n    رابط کاربری Seelen با امتیازات سرپرست اجرا می شود.\n    این هیچ ویژگی اضافه یا بهبود نمی بخشد و ممکن است خطر امنیتی ایجاد کند.\n    برای امنیت بهتر، لطفاً آن را بدون مجوزهای سرپرست راه اندازی مجدد کنید.\n  title: در حال اجرا به عنوان مدیر\nfile_explorer: فایل اکسپلورر\nresource:\n  added: یک منبع جدید اضافه شده است\n  download_failed_body: >-\n    هنگام بارگیری منبع ، مشکلی پیش آمد ، لطفاً آن را به تیم توسعه دهنده Seelen\n    گزارش دهید.\n  download_failed_title: بارگیری منابع ناموفق است\n  downloading: بارگیری دارایی ...\n  downloading_body: این ممکن است چند ثانیه طول بکشد ، لطفاً پنجره را ببندید.\n  enable: فعال کردن\nruntime:\n  corrupted_data: داده های فاسد شده\n  corrupted_file: به جای آن ، یک فایل پیکربندی خراب است.\n  corrupted_file_path: مسیر پرونده فاسد شده\n  download: به صفحه دانلود بروید\n  files_integrity: >-\n    ما نتوانستیم یکپارچگی فایل های برنامه را تأیید کنیم.\n\n    این ممکن است در صورتی اتفاق بیفتد که فایل‌ها اصلاح شده باشند، خراب شده باشند\n    یا نصب ناقص باشد.\n\n    به دلایل امنیتی، Seelen UI نمی تواند ادامه دهد.\n\n    لطفاً آن را مجدداً از وب سایت رسمی نصب کنید.\n  files_integrity_title: هشدار امنیتی - بررسی یکپارچگی فایل ها انجام نشد\n  not_found: WebView2 Runtime یافت نشد\n  not_found_description: رابط کاربری Seelen به Webview2 Runtime نیاز دارد. لطفا آن را نصب کنید.\n  outdated: WebView2 Runtime قدیمی شده است\n  outdated_description: >-\n    رابط کاربری Seelen به Webview2 Runtime %{min_version} یا بالاتر نیاز دارد.\n    لطفا آن را به روز کنید.\nservice:\n  not_running: سرویس رابط کاربری Seelen اجرا نمی شود\n  not_running_description: >-\n    سرویس UI Seelen (سرویس slu) یک وابستگی / کمک کننده برای عملکرد صحیح برنامه\n    است.\n  not_running_ok: سرویس را شروع کنید\nshortcut:\n  register:\n    placeholder: هر ترکیب کلیدی را فشار دهید\n    title: ثبت میانبر جدید\nwidget_liveness:\n  failed_description: |-\n    ویجت \"%{widget_name}\" بارها پاسخ نداد.\n\n    می توانید برنامه را دوباره راه اندازی کنید.\n  failed_title: خطای ویجت\nwidget_permissions:\n  perm_open_file: باز کردن فایل ها\n  perm_run: برنامه ها را اجرا کنید\n  request_description: |-\n    ویجت '%{widget_name}' در حال درخواست مجوز برای %{command} است.\n\n    آیا می خواهید این اجازه را بدهید؟\n  request_title: درخواست مجوز ویجت\n"
  },
  {
    "path": "src/background/i18n/fi.yml",
    "content": "cancel: Peruuttaa\ndone: Tehty\nelevated:\n  description: >-\n    Seelen-käyttöliittymä toimii järjestelmänvalvojan oikeuksilla.\n\n    Tämä ei lisää tai paranna ominaisuuksia, ja se voi aiheuttaa\n    turvallisuusriskin.\n\n    Turvallisuuden parantamiseksi käynnistä se uudelleen ilman\n    järjestelmänvalvojan oikeuksia.\n  title: Toimii järjestelmänvalvojana\nfile_explorer: Tiedostonhallinta\nresource:\n  added: Uusi resurssi on lisätty\n  download_failed_body: >-\n    Jotain meni pieleen resurssin lataamisen aikana, ilmoita siitä Seelenin\n    kehittäjätiimille.\n  download_failed_title: Resurssien lataus epäonnistui\n  downloading: Varojen lataaminen...\n  downloading_body: Tämä saattaa kestää muutaman sekunnin, älä sulje ikkunaa.\n  enable: Ota käyttöön\nruntime:\n  corrupted_data: Vioittuneet tiedot\n  corrupted_file: Määritystiedosto on vioittunut ja käyttää sen sijaan oletusarvoja.\n  corrupted_file_path: Vioittunut tiedostopolku\n  download: Siirry lataussivulle\n  files_integrity: >-\n    Emme voineet varmistaa sovellustiedostojen eheyttä.\n\n    Tämä voi tapahtua, jos tiedostoja on muokattu, vioittunut tai asennus on\n    epätäydellinen.\n\n    Turvallisuussyistä Seelen UI ei voi jatkaa.\n\n    Asenna se uudelleen viralliselta verkkosivustolta.\n  files_integrity_title: Suojaushälytys – Tiedostojen eheyden tarkistus epäonnistui\n  not_found: WebView2:n suoritusaikaa ei löydy\n  not_found_description: Seelen-käyttöliittymä vaatii Webview2 Runtime -sovelluksen. Asenna se.\n  outdated: WebView2 Runtime vanhentunut\n  outdated_description: >-\n    Seelen-käyttöliittymä vaatii Webview2 Runtime %{min_version} tai uudemman.\n    Päivitä se.\nservice:\n  not_running: Seelen UI -palvelu ei ole käynnissä\n  not_running_description: >-\n    Seelen UI Service (slu-service) on riippuvuus/apuohjelma, jonka avulla\n    sovellus toimii oikein.\n  not_running_ok: Aloita palvelu\nshortcut:\n  register:\n    placeholder: Paina mitä tahansa näppäinyhdistelmää\n    title: Uuden pikakuvakkeen rekisteröinti\nwidget_liveness:\n  failed_description: |-\n    Widget '%{widget_name}' lakkasi vastaamasta liian monta kertaa.\n\n    Voit yrittää käynnistää sovelluksen uudelleen.\n  failed_title: Widget-virhe\nwidget_permissions:\n  perm_open_file: avaa tiedostoja\n  perm_run: ajaa ohjelmia\n  request_description: |-\n    Widget '%{widget_name}' pyytää lupaa komentoon %{command}.\n\n    Haluatko sallia tämän?\n  request_title: Widgetin lupapyyntö\n"
  },
  {
    "path": "src/background/i18n/fr.yml",
    "content": "cancel: Annuler\ndone: Fait\nelevated:\n  description: >-\n    Seelen UI fonctionne avec des privilèges d'administrateur.\n\n    Cela n’ajoute ni n’améliore aucune fonctionnalité et peut présenter un\n    risque de sécurité.\n\n    Pour une meilleure sécurité, veuillez le redémarrer sans autorisations\n    d'administrateur.\n  title: Exécuter en tant qu'administrateur\nfile_explorer: Explorateur de fichiers\nresource:\n  added: Une nouvelle ressource a été ajoutée\n  download_failed_body: >-\n    Un problème s'est produit lors du téléchargement de la ressource, veuillez\n    le signaler à l'équipe de développeurs de Seelen.\n  download_failed_title: Le téléchargement des ressources a échoué\n  downloading: Téléchargement des actifs...\n  downloading_body: Cela peut prendre quelques secondes, ne fermez pas la fenêtre.\n  enable: Activer\nruntime:\n  corrupted_data: Données corrompues\n  corrupted_file: Un fichier de configuration est corrompu et utilise des valeurs par défaut.\n  corrupted_file_path: Chemin d'accès au fichier corrompu\n  download: Aller à la page de téléchargement\n  files_integrity: >-\n    Nous n'avons pas pu vérifier l'intégrité des dossiers de candidature.\n\n    Cela peut se produire si les fichiers ont été modifiés, corrompus ou si\n    l'installation est incomplète.\n\n    Pour des raisons de sécurité, Seelen UI ne peut pas continuer.\n\n    Veuillez le réinstaller depuis le site officiel.\n  files_integrity_title: Alerte de sécurité - Échec de la vérification de l'intégrité des fichiers\n  not_found: Le moteur d'exécution WebView2 est introuvable\n  not_found_description: >-\n    L'interface utilisateur de Seelen nécessite le runtime Webview2. Veuillez\n    l'installer.\n  outdated: WebView2 Runtime obsolète\n  outdated_description: >-\n    L'interface utilisateur de Seelen nécessite Webview2 Runtime %{min_version}\n    ou supérieur. Veuillez le mettre à jour.\nservice:\n  not_running: Le service d'interface utilisateur Seelen ne fonctionne pas\n  not_running_description: >-\n    Seelen UI Service (slu-service) est une dépendance/une aide permettant à\n    l'application de fonctionner correctement.\n  not_running_ok: Démarrer le service\nshortcut:\n  register:\n    placeholder: Appuyez sur n'importe quelle combinaison de touches\n    title: Enregistrement de nouveaux raccourcis\nwidget_liveness:\n  failed_description: |-\n    Le widget '%{widget_name}' a cessé de répondre trop souvent.\n\n    Vous pouvez essayer de redémarrer l'application.\n  failed_title: Erreur de widget\nwidget_permissions:\n  perm_open_file: ouvrir des fichiers\n  perm_run: exécuter des programmes\n  request_description: \"Le widget '%{widget_name}' demande l'autorisation de %{command}. \\n\\nVoulez-vous autoriser cela\\_?\"\n  request_title: Demande d'autorisation de widget\n"
  },
  {
    "path": "src/background/i18n/gu.yml",
    "content": "cancel: રદ કરવું\ndone: કામ\nelevated:\n  description: >-\n    સીલેન UI એડમિનિસ્ટ્રેટર વિશેષાધિકારો સાથે ચાલી રહ્યું છે.\n\n    આ કોઈપણ સુવિધાઓ ઉમેરતું નથી અથવા સુધારતું નથી, અને તે સુરક્ષા જોખમ ઊભું કરી\n    શકે છે.\n\n    બહેતર સુરક્ષા માટે, કૃપા કરીને તેને એડમિનિસ્ટ્રેટરની પરવાનગી વિના\n    પુનઃપ્રારંભ કરો.\n  title: એડમિનિસ્ટ્રેટર તરીકે ચાલી રહ્યા છે\nfile_explorer: ફાઇલ એક્સપ્લોરર\nresource:\n  added: એક નવો સ્રોત ઉમેરવામાં આવ્યો છે\n  download_failed_body: >-\n    સંસાધન ડાઉનલોડ કરતી વખતે કંઈક ખોટું થયું, કૃપા કરીને તેને સીલેન ડેવલપર ટીમને\n    જાણ કરો.\n  download_failed_title: સંસાધન ડાઉનલોડ નિષ્ફળ\n  downloading: અસ્કયામતો ડાઉનલોડ કરી રહ્યા છીએ ...\n  downloading_body: આને થોડી સેકંડ લાગી શકે છે, કૃપા કરીને વિંડો બંધ ન કરો.\n  enable: સક્ષમ કરવું\nruntime:\n  corrupted_data: ભ્રષ્ટ આંકડા\n  corrupted_file: તેના બદલે ડિફ default લ્ટ મૂલ્યોનો ઉપયોગ કરીને, એક રૂપરેખાંકન ફાઇલ દૂષિત છે.\n  corrupted_file_path: ભ્રષ્ટ ફાઇલ માર્ગ\n  download: ડાઉનલોડ પૃષ્ઠ પર જાઓ\n  files_integrity: |-\n    અમે એપ્લિકેશન ફાઇલોની અખંડિતતા ચકાસી શક્યાં નથી.\n    જો ફાઇલો સંશોધિત, દૂષિત અથવા ઇન્સ્ટોલેશન અધૂરી હોય તો આવું થઈ શકે છે.\n    સુરક્ષા કારણોસર, Seelen UI ચાલુ રાખી શકાતું નથી.\n    કૃપા કરીને તેને સત્તાવાર વેબસાઇટ પરથી ફરીથી ઇન્સ્ટોલ કરો.\n  files_integrity_title: સુરક્ષા ચેતવણી - ફાઇલોની અખંડિતતા તપાસ નિષ્ફળ\n  not_found: WebView2 રનટાઇમ મળ્યો નથી\n  not_found_description: Seelen UI ને Webview2 રનટાઇમની જરૂર છે. કૃપા કરીને તેને ઇન્સ્ટોલ કરો.\n  outdated: WebView2 રનટાઇમ જૂનો\n  outdated_description: >-\n    સીલેન UI ને Webview2 રનટાઇમ %{min_version} અથવા ઉચ્ચની જરૂર છે. કૃપા કરીને\n    તેને અપડેટ કરો.\nservice:\n  not_running: સીલેન UI સેવા ચાલી રહી નથી\n  not_running_description: >-\n    સીલેન UI સેવા (સ્લુ-સર્વિસ) એ એપ્લિકેશનને યોગ્ય રીતે કાર્ય કરવા માટે એક\n    નિર્ભરતા/સહાયક છે.\n  not_running_ok: સેવા શરૂ કરો\nshortcut:\n  register:\n    placeholder: કોઈપણ કી સંયોજન દબાવો\n    title: નવી શોર્ટકટ નોંધણી\nwidget_liveness:\n  failed_description: |-\n    વિજેટ '%{widget_name}' એ ઘણી વખત પ્રતિસાદ આપવાનું બંધ કર્યું.\n\n    તમે એપ્લિકેશનને ફરીથી પ્રારંભ કરવાનો પ્રયાસ કરી શકો છો.\n  failed_title: વિજેટ ભૂલ\nwidget_permissions:\n  perm_open_file: ફાઇલો ખોલો\n  perm_run: કાર્યક્રમો ચલાવો\n  request_description: |-\n    વિજેટ '%{widget_name}' %{command} ને પરવાનગીની વિનંતી કરી રહ્યું છે.\n\n    શું તમે આને મંજૂરી આપવા માંગો છો?\n  request_title: વિજેટ પરવાનગી વિનંતી\n"
  },
  {
    "path": "src/background/i18n/he.yml",
    "content": "cancel: לְבַטֵל\ndone: נַעֲשָׂה\nelevated:\n  description: |-\n    ממשק המשתמש של Seelen פועל עם הרשאות מנהל.\n    זה לא מוסיף או משפר תכונות כלשהן, וזה עלול להוות סיכון אבטחה.\n    לאבטחה טובה יותר, אנא הפעל אותו מחדש ללא הרשאות מנהל.\n  title: פועל כמנהל\nfile_explorer: סייר הקבצים\nresource:\n  added: נוסף משאב חדש\n  download_failed_body: משהו השתבש בזמן הורדת המשאב, אנא דווח עליו לצוות המפתחים של Seelen.\n  download_failed_title: הורדת משאבים נכשלה\n  downloading: הורדת נכסים ...\n  downloading_body: זה עשוי לקחת מספר שניות, אנא אל תסגור את החלון.\n  enable: אפשר\nruntime:\n  corrupted_data: נתונים פגומים\n  corrupted_file: קובץ תצורה פגום, באמצעות ערכי ברירת מחדל במקום זאת.\n  corrupted_file_path: נתיב קובץ פגום\n  download: הורדות\n  files_integrity: |-\n    לא הצלחנו לאמת את תקינות קבצי היישום.\n    זה יכול לקרות אם הקבצים שונו, נפגמו או שההתקנה לא הושלמה.\n    מסיבות אבטחה, ממשק המשתמש של Seelen לא יכול להמשיך.\n    נא להתקין אותו מחדש מהאתר הרשמי.\n  files_integrity_title: התראת אבטחה - בדיקת תקינות הקבצים נכשלה\n  not_found: זמן ריצה של WebView2 לא נמצא\n  not_found_description: ממשק המשתמש של Seelen דורש זמן ריצה של Webview2. נא להתקין אותו.\n  outdated: זמן ריצה של WebView2 מיושן\n  outdated_description: >-\n    ממשק המשתמש של Seelen דורש זמן ריצה של Webview2 %{min_version} ומעלה. אנא\n    עדכן אותו.\nservice:\n  not_running: שירות ממשק המשתמש של Seelen לא פועל\n  not_running_description: Seelen UI Service (slu-service) הוא תלות/עוזר כדי שהאפליקציה תפעל כראוי.\n  not_running_ok: התחל שירות\nshortcut:\n  register:\n    placeholder: לחץ על כל שילוב מקשים\n    title: רישום קיצור דרך חדש\nwidget_liveness:\n  failed_description: |-\n    הווידג'ט '%{widget_name}' הפסיק להגיב יותר מדי פעמים.\n\n    אתה יכול לנסות להפעיל מחדש את האפליקציה.\n  failed_title: שגיאת ווידג'ט\nwidget_permissions:\n  perm_open_file: לפתוח קבצים\n  perm_run: להפעיל תוכניות\n  request_description: |-\n    הווידג'ט '%{widget_name}' מבקש הרשאה ל-%{command}.\n\n    האם אתה רוצה לאפשר זאת?\n  request_title: בקשת הרשאת יישומון\n"
  },
  {
    "path": "src/background/i18n/hi.yml",
    "content": "cancel: रद्द करना\ndone: हो गया\nelevated:\n  description: >-\n    सीलेन यूआई प्रशासकीय विशेषाधिकारों के साथ चल रहा है।\n\n    यह किसी भी सुविधा को जोड़ता या सुधारता नहीं है, और इससे सुरक्षा जोखिम पैदा\n    हो सकता है।\n\n    बेहतर सुरक्षा के लिए, कृपया व्यवस्थापक की अनुमति के बिना इसे पुनः आरंभ करें।\n  title: प्रशासक के रूप में चल रहा है\nfile_explorer: फाइल ढूँढने वाला\nresource:\n  added: एक नया संसाधन जोड़ा गया है\n  download_failed_body: >-\n    संसाधन डाउनलोड करते समय कुछ गलत हो गया, कृपया इसे सेलेन डेवलपर टीम को\n    रिपोर्ट करें।\n  download_failed_title: संसाधन डाउनलोड विफल\n  downloading: संपत्ति डाउनलोड करना ...\n  downloading_body: इसमें कुछ सेकंड लग सकते हैं, कृपया खिड़की बंद न करें।\n  enable: सक्षम\nruntime:\n  corrupted_data: दूषित आंकड़ा\n  corrupted_file: एक कॉन्फ़िगरेशन फ़ाइल दूषित है, इसके बजाय डिफ़ॉल्ट मानों का उपयोग करके।\n  corrupted_file_path: दूषित फ़ाइल पथ\n  download: डाउनलोड पेज पर जाएं\n  files_integrity: >-\n    हम एप्लिकेशन फ़ाइलों की अखंडता को सत्यापित नहीं कर सके।\n\n    ऐसा तब हो सकता है जब फ़ाइलें संशोधित की गई हों, दूषित हों, या इंस्टॉलेशन\n    अधूरा हो।\n\n    सुरक्षा कारणों से, सीलेन यूआई जारी नहीं रह सकता।\n\n    कृपया इसे आधिकारिक वेबसाइट से पुनः इंस्टॉल करें।\n  files_integrity_title: सुरक्षा चेतावनी - फ़ाइलें सत्यनिष्ठा जाँच विफल\n  not_found: WebView2 रनटाइम नहीं मिला\n  not_found_description: सीलेन यूआई को वेबव्यू2 रनटाइम की आवश्यकता है। कृपया इसे इंस्टॉल करें.\n  outdated: WebView2 रनटाइम पुराना हो गया\n  outdated_description: >-\n    सीलेन यूआई को वेबव्यू2 रनटाइम %{min_version} या उच्चतर की आवश्यकता है। कृपया\n    इसे अपडेट करें.\nservice:\n  not_running: सीलेन यूआई सेवा नहीं चल रही है\n  not_running_description: >-\n    सीलेन यूआई सेवा (स्लू-सर्विस) ऐप के ठीक से काम करने के लिए एक निर्भरता/सहायक\n    है।\n  not_running_ok: सेवा प्रारंभ करें\nshortcut:\n  register:\n    placeholder: किसी भी प्रमुख संयोजन को दबाएं\n    title: नया शॉर्टकट पंजीकृत करना\nwidget_liveness:\n  failed_description: |-\n    विजेट '%{widget_name}' ने कई बार प्रत्युत्तर देना बंद कर दिया।\n\n    आप ऐप को पुनः प्रारंभ करने का प्रयास कर सकते हैं.\n  failed_title: विजेट त्रुटि\nwidget_permissions:\n  perm_open_file: खुली फ़ाइलें\n  perm_run: प्रोग्राम चलाएँ\n  request_description: |-\n    विजेट '%{widget_name}' %{command} की अनुमति का अनुरोध कर रहा है।\n\n    क्या आप इसकी अनुमति देंगे?\n  request_title: विजेट अनुमति अनुरोध\n"
  },
  {
    "path": "src/background/i18n/hr.yml",
    "content": "cancel: Otkazati\ndone: Završen\nelevated:\n  description: >-\n    Seelen UI radi s administratorskim ovlastima.\n\n    Ovo ne dodaje niti poboljšava nikakve značajke i može predstavljati\n    sigurnosni rizik.\n\n    Radi bolje sigurnosti, ponovno ga pokrenite bez administratorskih\n    dopuštenja.\n  title: Pokreće se kao administrator\nfile_explorer: File Explorer\nresource:\n  added: Dodan je novi resurs\n  download_failed_body: >-\n    Nešto je pošlo po zlu prilikom preuzimanja resursa, molimo da ga prijavite\n    timu za razvojne programere Seelen.\n  download_failed_title: Preuzimanje resursa nije uspjelo\n  downloading: Preuzimanje imovine ...\n  downloading_body: >-\n    Ovo bi moglo potrajati nekoliko sekundi, molim vas, nemojte zatvoriti\n    prozor.\n  enable: Omogućiti\nruntime:\n  corrupted_data: Oštećeni podaci\n  corrupted_file: Konfiguracijska datoteka je oštećena, koristeći zadane vrijednosti.\n  corrupted_file_path: Ispravljena staza datoteke\n  download: Idi na stranicu za preuzimanje\n  files_integrity: >-\n    Nismo mogli provjeriti integritet aplikacijskih datoteka.\n\n    To se može dogoditi ako su datoteke izmijenjene, oštećene ili instalacija\n    nije dovršena.\n\n    Iz sigurnosnih razloga, Seelen UI ne može nastaviti.\n\n    Ponovno ga instalirajte sa službene web stranice.\n  files_integrity_title: Sigurnosno upozorenje - Provjera integriteta datoteka nije uspjela\n  not_found: WebView2 Runtime nije pronađen\n  not_found_description: Seelen UI zahtijeva Webview2 Runtime. Molimo instalirajte ga.\n  outdated: WebView2 Runtime je zastario\n  outdated_description: >-\n    Seelen UI zahtijeva Webview2 Runtime %{min_version} ili noviji. Molimo\n    ažurirajte ga.\nservice:\n  not_running: Usluga Seelen UI nije pokrenuta\n  not_running_description: >-\n    Seelen UI Service (slu-service) je ovisnost/pomoć za ispravan rad\n    aplikacije.\n  not_running_ok: Pokrenite uslugu\nshortcut:\n  register:\n    placeholder: Pritisnite bilo koju kombinaciju tipki\n    title: Registriranje novog prečaca\nwidget_liveness:\n  failed_description: |-\n    Widget '%{widget_name}' prestao je reagirati previše puta.\n\n    Možete pokušati ponovno pokrenuti aplikaciju.\n  failed_title: Pogreška widgeta\nwidget_permissions:\n  perm_open_file: otvorene datoteke\n  perm_run: pokretati programe\n  request_description: |-\n    Widget '%{widget_name}' traži dozvolu za %{command}.\n\n    Želite li to dopustiti?\n  request_title: Zahtjev za dopuštenje widgeta\n"
  },
  {
    "path": "src/background/i18n/hu.yml",
    "content": "cancel: Töröl\ndone: Kész\nelevated:\n  description: |-\n    A Seelen UI rendszergazdai jogosultságokkal fut.\n    Ez nem bővíti vagy javítja a funkciókat, és biztonsági kockázatot jelenthet.\n    A nagyobb biztonság érdekében indítsa újra rendszergazdai engedélyek nélkül.\n  title: Rendszergazdaként fut\nfile_explorer: Fájlböngésző\nresource:\n  added: Egy új erőforrás került hozzáadásra\n  download_failed_body: >-\n    Az erőforrás letöltése közben valami rosszul ment, kérjük, jelezze a Seelen\n    fejlesztői csapatának.\n  download_failed_title: Az erőforrás letöltése sikertelen\n  downloading: Eszközök letöltése...\n  downloading_body: Ez eltarthat néhány másodpercig, kérjük, ne zárja be az ablakot.\n  enable: Engedélyezze a\nruntime:\n  corrupted_data: Hibás adatok\n  corrupted_file: Egy konfigurációs fájl sérült, helyette alapértelmezett értékeket használ.\n  corrupted_file_path: Megrongált fájl elérési útvonal\n  download: Menjen a letöltési oldalra\n  files_integrity: >-\n    Nem tudtuk ellenőrizni az alkalmazásfájlok sértetlenségét.\n\n    Ez akkor fordulhat elő, ha a fájlok módosultak, sérültek, vagy a telepítés\n    nem fejeződött be.\n\n    Biztonsági okokból a Seelen UI nem folytatható.\n\n    Kérjük, telepítse újra a hivatalos webhelyről.\n  files_integrity_title: Biztonsági figyelmeztetés – A fájlok integritásának ellenőrzése sikertelen\n  not_found: A WebView2 futási környezet nem található\n  not_found_description: >-\n    A Seelen felhasználói felületéhez Webview2 Runtime szükséges. Kérjük,\n    telepítse.\n  outdated: WebView2 Runtime elavult\n  outdated_description: >-\n    A Seelen felhasználói felületéhez Webview2 Runtime %{min_version} vagy újabb\n    szükséges. Kérjük, frissítse.\nservice:\n  not_running: Seelen UI szolgáltatás nem fut\n  not_running_description: >-\n    A Seelen UI szolgáltatás (slu-szolgáltatás) egy függőség/segítő az\n    alkalmazás megfelelő működéséhez.\n  not_running_ok: Indítsa el a szolgáltatást\nshortcut:\n  register:\n    placeholder: Nyomja meg a gomb kombinációját\n    title: Új parancsikon regisztrálása\nwidget_liveness:\n  failed_description: |-\n    A(z) '%{widget_name}' widget túl sokszor leállt.\n\n    Megpróbálhatja újraindítani az alkalmazást.\n  failed_title: Widget hiba\nwidget_permissions:\n  perm_open_file: nyissa meg a fájlokat\n  perm_run: programokat futtatni\n  request_description: |-\n    A(z) '%{widget_name}' widget engedélyt kér a következőhöz: %{command}.\n\n    Szeretnéd ezt engedélyezni?\n  request_title: Widget engedélykérés\n"
  },
  {
    "path": "src/background/i18n/hy.yml",
    "content": "cancel: Չեղարկել\ndone: Արված\nelevated:\n  description: >-\n    Seelen UI-ն աշխատում է ադմինիստրատորի արտոնություններով:\n\n    Սա չի ավելացնում կամ բարելավում որևէ գործառույթ, և դա կարող է անվտանգության\n    վտանգ ներկայացնել:\n\n    Ավելի լավ անվտանգության համար վերագործարկեք այն առանց ադմինիստրատորի\n    թույլտվությունների:\n  title: Աշխատում է որպես ադմինիստրատոր\nfile_explorer: Ֆայլերի Explorer\nresource:\n  added: Ավելացվել է նոր ռեսուրս\n  download_failed_body: >-\n    Ինչ-որ բան սխալ է գործել ռեսուրսը ներբեռնելիս, խնդրում ենք այն հաղորդել\n    Սելեն ծրագրավորողի թիմին:\n  download_failed_title: Ռեսուրսների ներբեռնումը ձախողվեց\n  downloading: Ակտիվների ներբեռնում ...\n  downloading_body: \"Սա կարող է տեւել մի քանի վայրկյան, խնդրում եմ մի փակեք պատուհանը:\"\n  enable: Միացնել\nruntime:\n  corrupted_data: Կոռումպացված տվյալներ\n  corrupted_file: \"Կազմաձեւման ֆայլը կոռումպացված է, փոխարենը օգտագործելով լռելյայն արժեքներ:\"\n  corrupted_file_path: Կոռուպցիոն ֆայլի ուղին\n  download: Գնացեք ներբեռնման էջ\n  files_integrity: >-\n    Մենք չկարողացանք ստուգել հավելվածի ֆայլերի ամբողջականությունը:\n\n    Դա կարող է տեղի ունենալ, եթե ֆայլերը փոփոխված են, վնասված են կամ տեղադրումն\n    ավարտված չէ:\n\n    Անվտանգության նկատառումներից ելնելով, Seelen UI-ն չի կարող շարունակել:\n\n    Խնդրում ենք նորից տեղադրել այն պաշտոնական կայքից:\n  files_integrity_title: Անվտանգության զգուշացում – Ֆայլերի ամբողջականության ստուգումը ձախողվեց\n  not_found: WebView2 Runtime-ը չի գտնվել\n  not_found_description: \"Seelen UI-ն պահանջում է Webview2 Runtime: Խնդրում ենք տեղադրել այն:\"\n  outdated: WebView2 Runtime-ը հնացել է\n  outdated_description: >-\n    Seelen UI-ն պահանջում է Webview2 Runtime %{min_version} կամ ավելի բարձր:\n    Խնդրում ենք թարմացնել այն:\nservice:\n  not_running: Seelen UI ծառայությունը չի աշխատում\n  not_running_description: >-\n    Seelen UI Service-ը (slu-service) կախվածություն/օգնական է հավելվածի ճիշտ\n    աշխատանքի համար:\n  not_running_ok: Սկսեք ծառայությունը\nshortcut:\n  register:\n    placeholder: Սեղմեք ցանկացած ստեղնաշարի համադրություն\n    title: Նոր դյուրանցում գրանցելը\nwidget_liveness:\n  failed_description: |-\n    «%{widget_name}» վիջեթը դադարել է շատ անգամ պատասխանել:\n\n    Կարող եք փորձել վերագործարկել հավելվածը:\n  failed_title: Վիջեթի սխալ\nwidget_permissions:\n  perm_open_file: բաց ֆայլեր\n  perm_run: գործարկել ծրագրեր\n  request_description: |-\n    «%{widget_name}» վիջեթը թույլտվություն է խնդրում %{command}-ին:\n\n    Ցանկանու՞մ եք սա թույլ տալ:\n  request_title: Վիջեթի թույլտվության հարցում\n"
  },
  {
    "path": "src/background/i18n/id.yml",
    "content": "cancel: Membatalkan\ndone: Selesai\nelevated:\n  description: >-\n    Seelen UI berjalan dengan hak administrator.\n\n    Ini tidak menambah atau meningkatkan fitur apa pun, dan mungkin menimbulkan\n    risiko keamanan.\n\n    Untuk keamanan yang lebih baik, silakan restart tanpa izin administrator.\n  title: Berjalan sebagai Administrator\nfile_explorer: Penjelajah Berkas\nresource:\n  added: Sumber daya baru telah ditambahkan\n  download_failed_body: >-\n    Terjadi kesalahan saat mengunduh sumber daya, silakan laporkan ke tim\n    pengembang Seelen.\n  download_failed_title: Pengunduhan sumber daya gagal\n  downloading: Mengunduh aset...\n  downloading_body: Ini mungkin memerlukan waktu beberapa detik, mohon jangan menutup jendela.\n  enable: Aktifkan\nruntime:\n  corrupted_data: Data yang rusak\n  corrupted_file: File konfigurasi rusak, menggunakan nilai default sebagai gantinya.\n  corrupted_file_path: Jalur file yang rusak\n  download: Buka halaman unduh\n  files_integrity: >-\n    Kami tidak dapat memverifikasi integritas file aplikasi.\n\n    Hal ini dapat terjadi jika file telah diubah, rusak, atau instalasi tidak\n    lengkap.\n\n    Demi alasan keamanan, Seelen UI tidak dapat melanjutkan.\n\n    Silakan instal ulang dari situs resminya.\n  files_integrity_title: Peringatan Keamanan - Pemeriksaan Integritas File Gagal\n  not_found: Waktu Proses WebView2 tidak ditemukan\n  not_found_description: Seelen UI memerlukan Runtime Webview2. Silakan menginstalnya.\n  outdated: Waktu Proses WebView2 sudah ketinggalan jaman\n  outdated_description: >-\n    Seelen UI memerlukan Webview2 Runtime %{min_version} atau lebih tinggi.\n    Harap perbarui.\nservice:\n  not_running: Layanan Seelen UI tidak berjalan\n  not_running_description: >-\n    Seelen UI Service (slu-service) adalah ketergantungan/pembantu agar aplikasi\n    berfungsi dengan baik.\n  not_running_ok: Mulai layanan\nshortcut:\n  register:\n    placeholder: Tekan Kombinasi Kunci apa pun\n    title: Mendaftarkan jalan pintas baru\nwidget_liveness:\n  failed_description: |-\n    Widget '%{widget_name}' terlalu sering berhenti merespons.\n\n    Anda dapat mencoba memulai ulang aplikasi.\n  failed_title: Kesalahan Widget\nwidget_permissions:\n  perm_open_file: membuka file\n  perm_run: menjalankan program\n  request_description: |-\n    Widget '%{widget_name}' meminta izin ke %{command}.\n\n    Apakah Anda ingin mengizinkan ini?\n  request_title: Permintaan Izin Widget\n"
  },
  {
    "path": "src/background/i18n/is.yml",
    "content": "cancel: Hætta við\ndone: Gert\nelevated:\n  description: >-\n    Seelen UI er í gangi með stjórnandaréttindi.\n\n    Þetta bætir ekki við eða bætir neina eiginleika og það getur valdið\n    öryggisáhættu.\n\n    Til að bæta öryggi, vinsamlegast endurræstu það án leyfis stjórnanda.\n  title: Keyrir sem stjórnandi\nfile_explorer: Skráarkönnuður\nresource:\n  added: Nýjum auðlind hefur verið bætt við\n  download_failed_body: >-\n    Eitthvað fór úrskeiðis þegar hún halaði niður auðlindinni, vinsamlegast\n    tilkynntu það til Seelen Developer teymisins.\n  download_failed_title: Auðlind niðurhal mistókst\n  downloading: Að hlaða niður eignum ...\n  downloading_body: Þetta gæti tekið nokkrar sekúndur, vinsamlegast ekki loka glugganum.\n  enable: Virkja\nruntime:\n  corrupted_data: Skemmd gögn\n  corrupted_file: Stillingarskrá er skemmd og notar sjálfgefin gildi í staðinn.\n  corrupted_file_path: Skemmd skráarleið\n  download: Farðu á niðurhalssíðuna\n  files_integrity: >-\n    Við gátum ekki staðfest heilleika forritaskránna.\n\n    Þetta getur gerst ef skrár voru breyttar, skemmdar eða uppsetningunni er\n    ófullnægjandi.\n\n    Af öryggisástæðum getur Seelen UI ekki haldið áfram.\n\n    Vinsamlegast settu það upp aftur frá opinberu vefsíðunni.\n  files_integrity_title: Öryggisviðvörun - Athugun á heiðarleika skráa mistókst\n  not_found: WebView2 Runtime fannst ekki\n  not_found_description: Seelen UI krefst Webview2 Runtime. Vinsamlegast settu það upp.\n  outdated: WebView2 Runtime úreltur\n  outdated_description: >-\n    Seelen notendaviðmót krefst Webview2 Runtime %{min_version} eða hærri.\n    Vinsamlegast uppfærðu það.\nservice:\n  not_running: Seelen UI þjónusta er ekki í gangi\n  not_running_description: Seelen UI Service (slu-þjónusta) er háð/hjálp til að appið virki rétt.\n  not_running_ok: Byrjaðu þjónustu\nshortcut:\n  register:\n    placeholder: Ýttu á hvaða lykilsamsetningu sem er\n    title: Að skrá nýja flýtileið\nwidget_liveness:\n  failed_description: |-\n    Græjan '%{widget_name}' hætti að svara of oft.\n\n    Þú getur prófað að endurræsa forritið.\n  failed_title: Búnaður Villa\nwidget_permissions:\n  perm_open_file: opna skrár\n  perm_run: keyra forrit\n  request_description: |-\n    Græjan '%{widget_name}' biður um leyfi til að %{command}.\n\n    Viltu leyfa þetta?\n  request_title: Beiðni um leyfi fyrir græju\n"
  },
  {
    "path": "src/background/i18n/it.yml",
    "content": "cancel: Cancellare\ndone: Fatto\nelevated:\n  description: >-\n    L'interfaccia utente di Seelen è in esecuzione con privilegi di\n    amministratore.\n\n    Ciò non aggiunge né migliora alcuna funzionalità e potrebbe rappresentare un\n    rischio per la sicurezza.\n\n    Per una maggiore sicurezza, riavvialo senza autorizzazioni di\n    amministratore.\n  title: In esecuzione come amministratore\nfile_explorer: Esplora file\nresource:\n  added: È stata aggiunta una nuova risorsa\n  download_failed_body: >-\n    Qualcosa è andato storto durante il download della risorsa, segnalatelo al\n    team di sviluppatori di Seelen.\n  download_failed_title: Download di risorse non riuscito\n  downloading: Scaricare le risorse...\n  downloading_body: L'operazione potrebbe richiedere alcuni secondi; non chiudere la finestra.\n  enable: Abilitazione\nruntime:\n  corrupted_data: Dati corrotti\n  corrupted_file: Un file di configurazione è danneggiato e utilizza valori predefiniti.\n  corrupted_file_path: Percorso del file corrotto\n  download: Vai alla pagina di download\n  files_integrity: >-\n    Non è stato possibile verificare l'integrità dei file dell'applicazione.\n\n    Ciò può verificarsi se i file sono stati modificati, danneggiati o se\n    l'installazione è incompleta.\n\n    Per motivi di sicurezza, Seelen UI non può continuare.\n\n    Si prega di reinstallarlo dal sito Web ufficiale.\n  files_integrity_title: 'Avviso di sicurezza: controllo dell''integrità dei file non riuscito'\n  not_found: Runtime WebView2 non trovato\n  not_found_description: >-\n    L'interfaccia utente di Seelen richiede Webview2 Runtime. Per favore\n    installalo.\n  outdated: WebView2 Runtime obsoleto\n  outdated_description: >-\n    L'interfaccia utente di Seelen richiede Webview2 Runtime %{min_version} o\n    versione successiva. Per favore aggiornalo.\nservice:\n  not_running: Il servizio UI Seelen non è in esecuzione\n  not_running_description: >-\n    Seelen UI Service (slu-service) è una dipendenza/un aiuto per il corretto\n    funzionamento dell'app.\n  not_running_ok: Avvia il servizio\nshortcut:\n  register:\n    placeholder: Premere qualsiasi combinazione di tasti\n    title: Registrazione di nuovi collegamenti\nwidget_liveness:\n  failed_description: |-\n    Il widget '%{widget_name}' ha smesso di rispondere troppe volte.\n\n    Puoi provare a riavviare l'app.\n  failed_title: Errore del widget\nwidget_permissions:\n  perm_open_file: aprire file\n  perm_run: eseguire programmi\n  request_description: |-\n    Il widget '%{widget_name}' sta richiedendo l'autorizzazione a %{command}.\n\n    Vuoi consentire questo?\n  request_title: Richiesta di autorizzazione del widget\n"
  },
  {
    "path": "src/background/i18n/ja.yml",
    "content": "cancel: キャンセル\ndone: 完了\nelevated:\n  description: |-\n    Seelen UI は管理者権限で実行されています。\n    これにより機能が追加または改善されることはなく、セキュリティ上のリスクが生じる可能性があります。\n    セキュリティを強化するため、管理者権限なしで再起動してください。\n  title: 管理者として実行\nfile_explorer: ファイルエクスプローラー\nresource:\n  added: 新しいリソースが追加されました\n  download_failed_body: リソースのダウンロード中に問題が発生しました。\n  download_failed_title: リソースのダウンロードに失敗しました\n  downloading: アセットのダウンロード...\n  downloading_body: ウィンドウを閉じないでください。\n  enable: 有効にする\nruntime:\n  corrupted_data: データが破損しています。\n  corrupted_file: 設定ファイルが破損しているため、代わりにデフォルト値が使用されています。\n  corrupted_file_path: ファイルパスが破損しています。\n  download: ダウンロードページを開く\n  files_integrity: |-\n    アプリケーション ファイルの整合性を確認できませんでした。\n    これは、ファイルが変更されたり破損したり、インストールが不完全な場合に発生する可能性があります。\n    セキュリティ上の理由により、Seelen UI は続行できません。\n    公式サイトから再インストールしてください。\n  files_integrity_title: セキュリティ警告 - ファイルの整合性チェックに失敗しました\n  not_found: WebView2 ランタイムが見つかりません\n  not_found_description: Seelen UI には Webview2 ランタイムが必要です。インストールしてください。\n  outdated: WebView2 ランタイムが古いです\n  outdated_description: Seelen UI には、Webview2 ランタイム %{min_version} 以降が必要です。更新してください。\nservice:\n  not_running: Seelen UI サービスが実行されていません\n  not_running_description: Seelen UI サービス (slu-service) は、アプリが適切に動作するための依存関係/ヘルパーです。\n  not_running_ok: サービスを開始する\nshortcut:\n  register:\n    placeholder: キーを押してください...\n    title: ショートカットキーの登録\nwidget_liveness:\n  failed_description: |-\n    ウィジェット '%{widget_name}' が何度も応答を停止しました。\n\n    アプリを再起動してみてください。\n  failed_title: ウィジェットエラー\nwidget_permissions:\n  perm_open_file: ファイルを開く\n  perm_run: プログラムを実行する\n  request_description: |-\n    ウィジェット '%{widget_name}' は %{command} への権限を要求しています。\n\n    これを許可しますか?\n  request_title: ウィジェットの許可リクエスト\n"
  },
  {
    "path": "src/background/i18n/ka.yml",
    "content": "cancel: გაუქმება\ndone: გაკეთებული\nelevated:\n  description: >-\n    Seelen UI მუშაობს ადმინისტრატორის პრივილეგიებით.\n\n    ეს არ ამატებს ან არ აუმჯობესებს რაიმე ფუნქციას და შეიძლება საფრთხე შეუქმნას\n    უსაფრთხოებას.\n\n    უკეთესი უსაფრთხოებისთვის, გთხოვთ, გადატვირთოთ იგი ადმინისტრატორის ნებართვის\n    გარეშე.\n  title: მუშაობს როგორც ადმინისტრატორი\nfile_explorer: ფაილების მკვლევარი\nresource:\n  added: დაემატა ახალი რესურსი\n  download_failed_body: >-\n    რესურსის ჩამოტვირთვისას რამე არასწორედ დასრულდა, გთხოვთ, შეატყობინოთ მას\n    Seelen Developer- ის გუნდს.\n  download_failed_title: რესურსის ჩამოტვირთვა ვერ მოხერხდა\n  downloading: აქტივების ჩამოტვირთვა ...\n  downloading_body: ამას შეიძლება რამდენიმე წამი დასჭირდეს, გთხოვთ, არ დახუროთ ფანჯარა.\n  enable: შესაძლებლობის მიცემა\nruntime:\n  corrupted_data: კორუმპირებული მონაცემები\n  corrupted_file: >-\n    კონფიგურაციის ფაილი კორუმპირებულია, ამის ნაცვლად ნაგულისხმევი მნიშვნელობების\n    გამოყენებით.\n  corrupted_file_path: კორუმპირებული ფაილის გზა\n  download: გადადით ჩამოტვირთვის გვერდზე\n  files_integrity: >-\n    ჩვენ ვერ დავადასტურეთ აპლიკაციის ფაილების მთლიანობა.\n\n    ეს შეიძლება მოხდეს, თუ ფაილები შეცვლილია, დაზიანებულია ან ინსტალაცია\n    არასრულია.\n\n    უსაფრთხოების მიზეზების გამო, Seelen UI ვერ გააგრძელებს.\n\n    გთხოვთ ხელახლა დააინსტალიროთ ოფიციალური საიტიდან.\n  files_integrity_title: უსაფრთხოების გაფრთხილება - ფაილების მთლიანობის შემოწმება ვერ მოხერხდა\n  not_found: WebView2 Runtime ვერ მოიძებნა\n  not_found_description: Seelen UI მოითხოვს Webview2 Runtime-ს. გთხოვთ დააინსტალიროთ.\n  outdated: WebView2 Runtime მოძველებულია\n  outdated_description: >-\n    Seelen UI მოითხოვს Webview2 Runtime %{min_version} ან უფრო მაღალს. გთხოვთ\n    განაახლოთ იგი.\nservice:\n  not_running: Seelen UI სერვისი არ მუშაობს\n  not_running_description: >-\n    Seelen UI Service (slu-service) არის დამოკიდებულება/დამხმარე აპის გამართულად\n    მუშაობისთვის.\n  not_running_ok: სერვისის დაწყება\nshortcut:\n  register:\n    placeholder: დააჭირეთ ნებისმიერ საკვანძო კომბინაციას\n    title: ახალი მალსახმობის რეგისტრაცია\nwidget_liveness:\n  failed_description: |-\n    ვიჯეტმა „%{widget_name}“ ძალიან ბევრჯერ შეწყვიტა პასუხი.\n\n    შეგიძლიათ სცადოთ აპის გადატვირთვა.\n  failed_title: ვიჯეტის შეცდომა\nwidget_permissions:\n  perm_open_file: ფაილების გახსნა\n  perm_run: პროგრამების გაშვება\n  request_description: |-\n    ვიჯეტი '%{widget_name}' ითხოვს %{command}-ის ნებართვას.\n\n    გსურთ ამის დაშვება?\n  request_title: ვიჯეტის ნებართვის მოთხოვნა\n"
  },
  {
    "path": "src/background/i18n/km.yml",
    "content": "cancel: លប់ចោល\ndone: រ្យេឱដេនោ\nelevated:\n  description: >-\n    Seelen UI កំពុងដំណើរការដោយមានសិទ្ធិជាអ្នកគ្រប់គ្រង។\n\n    នេះមិនបន្ថែម ឬកែលម្អមុខងារណាមួយឡើយ ហើយវាអាចបង្កហានិភ័យសុវត្ថិភាព។\n\n    ដើម្បីសុវត្ថិភាពកាន់តែប្រសើរ\n    សូមចាប់ផ្តើមវាឡើងវិញដោយគ្មានការអនុញ្ញាតពីអ្នកគ្រប់គ្រង។\n  title: ដំណើរការជាអ្នកគ្រប់គ្រង\nfile_explorer: កម្មវិធីរុករកឯកសារ\nresource:\n  added: ធនធានថ្មីត្រូវបានបន្ថែម\n  download_failed_body: មានអ្វីមួយមិនប្រក្រតីនៅពេលទាញយកធនធានសូមរាយការណ៍ទៅក្រុមអ្នកអភិវឌ្ឍន៍ Seeen ។\n  download_failed_title: ការទាញយកធនធានបានបរាជ័យ\n  downloading: ទាញយកទ្រព្យសម្បត្តិ ...\n  downloading_body: វាអាចចំណាយពេលពីរបីវិនាទីសូមកុំបិទបង្អួច។\n  enable: បើកដៃ\nruntime:\n  corrupted_data: ទិន្នន័យខូច\n  corrupted_file: ឯកសារកំណត់រចនាសម្ព័ន្ធត្រូវបានខូចដោយប្រើតម្លៃលំនាំដើមជំនួសវិញ។\n  corrupted_file_path: ផ្លូវឯកសារខូចខូច\n  download: ចូលទៅកាន់ទំព័រទាញយក\n  files_integrity: |-\n    យើងមិនអាចផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវនៃឯកសារកម្មវិធីបានទេ។\n    វាអាចកើតឡើងប្រសិនបើឯកសារត្រូវបានកែប្រែ ខូច ឬការដំឡើងមិនពេញលេញ។\n    សម្រាប់ហេតុផលសុវត្ថិភាព Seelen UI មិនអាចបន្តបានទេ។\n    សូមដំឡើងវាឡើងវិញពីគេហទំព័រផ្លូវការ។\n  files_integrity_title: ការដាស់តឿនសុវត្ថិភាព - ការត្រួតពិនិត្យភាពត្រឹមត្រូវនៃឯកសារបានបរាជ័យ\n  not_found: WebView2 Runtime រកមិនឃើញទេ។\n  not_found_description: Seelen UI ទាមទារ Webview2 Runtime ។ សូមដំឡើងវា។\n  outdated: WebView2 Runtime ហួសសម័យ\n  outdated_description: Seelen UI ទាមទារ Webview2 Runtime %{min_version} ឬខ្ពស់ជាងនេះ។ សូមអាប់ដេតវា។\nservice:\n  not_running: សេវា Seelen UI មិនដំណើរការទេ។\n  not_running_description: >-\n    Seelen UI Service (slu-service)\n    គឺជាការពឹងផ្អែក/ជំនួយសម្រាប់កម្មវិធីឱ្យដំណើរការបានត្រឹមត្រូវ។\n  not_running_ok: ចាប់ផ្តើមសេវាកម្ម\nshortcut:\n  register:\n    placeholder: ចុចបន្សំគ្រាប់ចុចណាមួយ\n    title: ការចុះឈ្មោះផ្លូវកាត់ថ្មី\nwidget_liveness:\n  failed_description: |-\n    ធាតុក្រាហ្វិក '%{widget_name}' បានឈប់ឆ្លើយតបច្រើនដងពេកហើយ។\n\n    អ្នកអាចព្យាយាមចាប់ផ្តើមកម្មវិធីឡើងវិញ។\n  failed_title: កំហុសធាតុក្រាហ្វិក\nwidget_permissions:\n  perm_open_file: បើកឯកសារ\n  perm_run: ដំណើរការកម្មវិធី\n  request_description: |-\n    ធាតុក្រាហ្វិក '%{widget_name}' កំពុងស្នើសុំការអនុញ្ញាតទៅ %{command} ។\n\n    តើអ្នកចង់អនុញ្ញាតវាទេ?\n  request_title: សំណើការអនុញ្ញាតធាតុក្រាហ្វិក\n"
  },
  {
    "path": "src/background/i18n/ko.yml",
    "content": "cancel: 취소\ndone: 완료\nelevated:\n  description: |-\n    Seelen UI가 관리자 권한으로 실행되고 있습니다.\n    이는 기능을 추가하거나 개선하지 않으며 보안 위험을 초래할 수 있습니다.\n    보안 강화를 위해 관리자 권한 없이 다시 시작하시기 바랍니다.\n  title: 관리자로 실행\nfile_explorer: 파일 탐색기\nresource:\n  added: 새로운 리소스가 추가되었습니다.\n  download_failed_body: 리소스를 다운로드하는 동안 문제가 발생했습니다. Seelen 개발자 팀에 신고해 주세요.\n  download_failed_title: 리소스 다운로드 실패\n  downloading: 에셋 다운로드...\n  downloading_body: 몇 초 정도 소요될 수 있으니 창을 닫지 마세요.\n  enable: 사용\nruntime:\n  corrupted_data: 손상된 데이터\n  corrupted_file: 구성 파일이 손상되어 기본값 대신 기본값을 사용합니다.\n  corrupted_file_path: 손상된 파일 경로\n  download: 다운로드 페이지로 이동\n  files_integrity: |-\n    애플리케이션 파일의 무결성을 확인할 수 없습니다.\n    파일이 수정, 손상되었거나 설치가 불완전한 경우 이런 일이 발생할 수 있습니다.\n    보안상의 이유로 Seelen UI를 계속할 수 없습니다.\n    공식 홈페이지에서 다시 설치해주세요.\n  files_integrity_title: 보안 경고 - 파일 무결성 검사 실패\n  not_found: WebView2 런타임을 찾을 수 없습니다.\n  not_found_description: Seelen UI에는 Webview2 런타임이 필요합니다. 설치해 주세요.\n  outdated: WebView2 런타임이 오래됨\n  outdated_description: Seelen UI에는 Webview2 런타임 %{min_version} 이상이 필요합니다. 업데이트해주세요.\nservice:\n  not_running: Seelen UI 서비스가 실행되고 있지 않습니다.\n  not_running_description: Seelen UI 서비스(slu-service)는 앱이 제대로 작동하기 위한 종속성/도우미입니다.\n  not_running_ok: 서비스 시작\nshortcut:\n  register:\n    placeholder: 주요 조합을 누릅니다\n    title: 새로운 바로 가기 등록\nwidget_liveness:\n  failed_description: |-\n    위젯 '%{widget_name}'이(가) 너무 자주 응답을 중지했습니다.\n\n    앱을 다시 시작해 보세요.\n  failed_title: 위젯 오류\nwidget_permissions:\n  perm_open_file: 열린 파일\n  perm_run: 프로그램 실행\n  request_description: |-\n    '%{widget_name}' 위젯이 %{command}에 대한 권한을 요청하고 있습니다.\n\n    이것을 허용하시겠습니까?\n  request_title: 위젯 권한 요청\n"
  },
  {
    "path": "src/background/i18n/ku.yml",
    "content": "cancel: Bişûndekirin\ndone: Dank\nelevated:\n  description: >-\n    Seelen UI bi îmtiyazên rêvebirê dimeşe.\n\n    Ev tu taybetmendiyan zêde nake an baştir dike, û dibe ku xeterek ewlehiyê\n    çêbike.\n\n    Ji bo ewlehiya çêtir, ji kerema xwe wê bêyî destûrên rêvebir ji nû ve bidin\n    destpêkirin.\n  title: Wekî Rêveber dimeşe\nfile_explorer: Explorer Explorer\nresource:\n  added: Resavkaniyek nû hatiye zêdekirin\n  download_failed_body: >-\n    Di dema dakêşandina çavkaniyê de tiştek çewt derbas bû, ji kerema xwe ew ji\n    tîmê pêşdebirên Seelen re ragihînin.\n  download_failed_title: Daxistina çavkaniyê têk çû\n  downloading: Daxistina Assets ...\n  downloading_body: Ev dibe ku çend hûrdeman bigire, ji kerema xwe pencereyê bigire.\n  enable: Bikêrkirin\nruntime:\n  corrupted_data: Daneyên hilweşandî\n  corrupted_file: Pelek mîhengê hilweşandî ye, li şûna nirxên xwerû bikar tîne.\n  corrupted_file_path: Rêya pelê hilweşandî\n  download: Biçe rûpela daxistinê\n  files_integrity: |-\n    Me nekarî yekitiya pelên serîlêdanê verast bikin.\n    Heke pel hatine guheztin, xerakirin, an sazkirin ne temam be ev dikare bibe.\n    Ji ber sedemên ewlehiyê, Seelen UI nikare berdewam bike.\n    Ji kerema xwe wê ji malpera fermî ji nû ve saz bikin.\n  files_integrity_title: Hişyariya Ewlekariyê - Kontrolkirina Yekbûna Pelan têk çû\n  not_found: WebView2 Runtime nehat dîtin\n  not_found_description: Seelen UI hewceyê Webview2 Runtime hewce dike. Ji kerema xwe saz bikin.\n  outdated: WebView2 Runtime kevn bûye\n  outdated_description: >-\n    Seelen UI ji Webview2 Runtime %{min_version} an mezintir hewce dike. Ji\n    kerema xwe wê nûve bikin.\nservice:\n  not_running: Karûbarê UI ya Seelen nayê xebitandin\n  not_running_description: >-\n    Xizmeta UI ya Seelen (slu-xizmet) ji bo ku sepan bi rêkûpêk bixebite\n    pêwendiyek / arîkarek e.\n  not_running_ok: Xizmetê dest pê bikin\nshortcut:\n  register:\n    placeholder: Her kombînasyona kilîtiyê çap bikin\n    title: Kurteya nû qeyd bikin\nwidget_liveness:\n  failed_description: |-\n    Widgeta '%{widget_name}' gelek caran bersiv nedaye.\n\n    Hûn dikarin biceribînin ku sepanê ji nû ve bidin destpêkirin.\n  failed_title: Çewtiya Widgetê\nwidget_permissions:\n  perm_open_file: pelan vekin\n  perm_run: bernameyan bimeşînin\n  request_description: |-\n    Widgeta '%{widget_name}' ji %{command} re destûr dixwaze.\n\n    Ma hûn dixwazin destûrê bidin vê yekê?\n  request_title: Daxwaza Destûra Widgetê\n"
  },
  {
    "path": "src/background/i18n/lb.yml",
    "content": "cancel: Ofbriechen\ndone: Verdataktioun\nelevated:\n  description: >-\n    Seelen UI leeft mat Administrator Privilegien.\n\n    Dëst addt oder verbessert keng Features, an et kann e Sécherheetsrisiko\n    duerstellen.\n\n    Fir besser Sécherheet, w.e.g. nei starten ouni Administrator Permissiounen.\n  title: Lafen als Administrateur\nfile_explorer: Datei Explorer\nresource:\n  added: Eng nei Ressource gouf bäigefüügt\n  download_failed_body: >-\n    Eppes ass falsch gaang wärend Dir d'Ressource eroflueden, mellt Iech w.e.g.\n    an de Seeper Entwéckler Team.\n  download_failed_title: Ressource Download ass net gescheitert\n  downloading: Eruppes-Opstellung erofgeluedenen ...\n  downloading_body: Dëst kann e puer Sekonnen da daach, wann ech net zou d'Found sinn.\n  enable: Erméiglecher zréck\nruntime:\n  corrupted_data: Korrupt Daten\n  corrupted_file: Eng Konfiguratiounsdatei ass beschiedegt, déi Standard Wäerter amplaz.\n  corrupted_file_path: Korrupt Dateiwee\n  download: Gitt op d'Download Säit\n  files_integrity: >-\n    Mir konnten d'Integritéit vun den Applikatiounsdateien net verifizéieren.\n\n    Dëst ka geschéien wann Dateien geännert goufen, korrupt sinn oder\n    d'Installatioun onkomplett ass.\n\n    Aus Sécherheetsgrënn kann Seelen UI net weidergoen.\n\n    Installéiert w.e.g. et vun der offizieller Websäit.\n  files_integrity_title: Sécherheetsalarm - Dateien Integritéitscheck ausgefall\n  not_found: WebView2 Runtime net fonnt\n  not_found_description: Seelen UI erfuerdert Webview2 Runtime. Weg installéieren et.\n  outdated: WebView2 Runtime veroudert\n  outdated_description: >-\n    Seelen UI erfuerdert Webview2 Runtime %{min_version} oder méi héich. Weg\n    update et.\nservice:\n  not_running: Seelen UI Service net lafen\n  not_running_description: >-\n    Seelen UI Service (slu-Service) ass eng Ofhängegkeet / Helfer fir d'App\n    richteg ze schaffen.\n  not_running_ok: Start Service\nshortcut:\n  register:\n    placeholder: Press all Tastekombinatioun\n    title: Registréiert nei Ofkiirzung\nwidget_liveness:\n  failed_description: |-\n    De Widget '%{widget_name}' huet opgehalen ze oft ze reagéieren.\n\n    Dir kënnt probéieren d'App nei ze starten.\n  failed_title: Widget Feeler\nwidget_permissions:\n  perm_open_file: opmaachen Dateien\n  perm_run: lafen Programmer\n  request_description: |-\n    De Widget '%{widget_name}' freet d'Erlaabnes fir %{command}.\n\n    Wëllt Dir dëst erlaben?\n  request_title: Widget Erlaabnis Ufro\n"
  },
  {
    "path": "src/background/i18n/lo.yml",
    "content": "cancel: ຍົກເລີກ\ndone: ແລ້ວ? ແລ້ວ\nelevated:\n  description: >-\n    Seelen UI ກໍາລັງເຮັດວຽກດ້ວຍສິດທິຂອງຜູ້ເບິ່ງແຍງລະບົບ.\n\n    ອັນນີ້ບໍ່ໄດ້ເພີ່ມ ຫຼືປັບປຸງຄຸນສົມບັດໃດໆ,\n    ແລະມັນອາດຈະສ້າງຄວາມສ່ຽງດ້ານຄວາມປອດໄພ.\n\n    ເພື່ອຄວາມປອດໄພທີ່ດີກວ່າ,\n    ກະລຸນາປິດເປີດມັນຄືນໃໝ່ໂດຍບໍ່ມີການອະນຸຍາດຈາກຜູ້ເບິ່ງແຍງລະບົບ.\n  title: ແລ່ນເປັນ Administrator\nfile_explorer: File Explorer\nresource:\n  added: ຊັບພະຍາກອນໃຫມ່ໄດ້ຖືກເພີ່ມເຂົ້າມາແລ້ວ\n  download_failed_body: >-\n    ມີບາງສິ່ງບາງຢ່າງຜິດພາດໃນຂະນະທີ່ດາວໂຫລດຊັບພະຍາກອນ,\n    ກະລຸນາລາຍງານໃຫ້ທີມງານນັກພັດທະນາຂອງນັກພັດທະນາ.\n  download_failed_title: ການດາວໂຫລດຊັບພະຍາກອນລົ້ມເຫລວ\n  downloading: ການດາວໂຫລດຊັບສິນ ...\n  downloading_body: ມັນອາດໃຊ້ເວລາສອງສາມວິນາທີ, ກະລຸນາຢ່າປິດປ່ອງຢ້ຽມ.\n  enable: ເປີດໃຊ້\nruntime:\n  corrupted_data: ຂໍ້ມູນທີ່ເສີຍຫາຍ\n  corrupted_file: ເອກະສານການຕັ້ງຄ່າແມ່ນເສຍຫາຍ, ໂດຍໃຊ້ຄຸນຄ່າເລີ່ມຕົ້ນແທນ.\n  corrupted_file_path: ເສັ້ນທາງເອກະສານທີ່ເສີຍຫາຍ\n  download: ໄປທີ່ໜ້າດາວໂຫຼດ\n  files_integrity: |-\n    ພວກເຮົາບໍ່ສາມາດຢືນຢັນຄວາມສົມບູນຂອງໄຟລ໌ແອັບພລິເຄຊັນໄດ້.\n    ອັນນີ້ສາມາດເກີດຂຶ້ນໄດ້ຖ້າໄຟລ໌ຖືກແກ້ໄຂ, ເສຍຫາຍ, ຫຼືການຕິດຕັ້ງບໍ່ສົມບູນ.\n    ສໍາລັບເຫດຜົນດ້ານຄວາມປອດໄພ, Seelen UI ບໍ່ສາມາດສືບຕໍ່ໄດ້.\n    ກະລຸນາຕິດຕັ້ງມັນຄືນໃໝ່ຈາກເວັບໄຊທ໌ທາງການ.\n  files_integrity_title: ແຈ້ງເຕືອນຄວາມປອດໄພ - ການກວດສອບຄວາມສົມບູນຂອງໄຟລ໌ລົ້ມເຫລວ\n  not_found: ບໍ່ພົບ WebView2 Runtime\n  not_found_description: Seelen UI ຕ້ອງການ Webview2 Runtime. ກະລຸນາຕິດຕັ້ງມັນ.\n  outdated: WebView2 Runtime ລ້າສະໄຫມ\n  outdated_description: >-\n    Seelen UI ຕ້ອງການ Webview2 Runtime %{min_version} ຫຼືສູງກວ່າ.\n    ກະລຸນາອັບເດດມັນ.\nservice:\n  not_running: ການບໍລິການ Seelen UI ບໍ່ເຮັດວຽກ\n  not_running_description: >-\n    Seelen UI Service (slu-service)\n    ເປັນການເພິ່ງພາອາໄສ/ຜູ້ຊ່ວຍເພື່ອໃຫ້ແອັບເຮັດວຽກໄດ້ຢ່າງຖືກຕ້ອງ.\n  not_running_ok: ເລີ່ມການບໍລິການ\nshortcut:\n  register:\n    placeholder: ກົດປຸ່ມປະສົມປະສານໃດໆ\n    title: ລົງທະບຽນລັດໃຫມ່\nwidget_liveness:\n  failed_description: |-\n    ວິດເຈັດ '%{widget_name}' ຢຸດຕອບສະໜອງຫຼາຍເທື່ອເກີນໄປ.\n\n    ທ່ານສາມາດລອງຣີສະຕາດແອັບໄດ້.\n  failed_title: Widget ຜິດພາດ\nwidget_permissions:\n  perm_open_file: ເປີດໄຟລ໌\n  perm_run: ດໍາເນີນໂຄງການ\n  request_description: |-\n    ວິດເຈັດ '%{widget_name}' ກໍາລັງຮ້ອງຂໍການອະນຸຍາດ %{command}.\n\n    ທ່ານຕ້ອງການອະນຸຍາດໃຫ້ສິ່ງນີ້ບໍ?\n  request_title: ການຮ້ອງຂໍການອະນຸຍາດ Widget\n"
  },
  {
    "path": "src/background/i18n/lt.yml",
    "content": "cancel: Atšaukti\ndone: Padaryta\nelevated:\n  description: >-\n    Seelen vartotojo sąsaja veikia su administratoriaus teisėmis.\n\n    Tai neprideda ir nepatobulina jokių funkcijų ir gali kelti pavojų saugumui.\n\n    Norėdami užtikrinti didesnį saugumą, paleiskite jį iš naujo be\n    administratoriaus leidimo.\n  title: Veikia kaip administratorius\nfile_explorer: Failų naršyklė\nresource:\n  added: Pridėtas naujas išteklius\n  download_failed_body: >-\n    Atsisiunčiant išteklių kažkas nepavyko, praneškite apie tai \"Seelen\" kūrėjų\n    komandai.\n  download_failed_title: Išteklių atsisiuntimas nepavyko\n  downloading: Turto atsisiuntimas...\n  downloading_body: Tai gali užtrukti kelias sekundes, todėl neuždarykite lango.\n  enable: Įjungti\nruntime:\n  corrupted_data: Sugadinti duomenys\n  corrupted_file: >-\n    Konfigūracijos failas sugadintas, vietoj jo naudojamos numatytojo nustatymo\n    reikšmės.\n  corrupted_file_path: Sugadintas failo kelias\n  download: Eikite į atsisiuntimo puslapį\n  files_integrity: >-\n    Nepavyko patikrinti programos failų vientisumo.\n\n    Taip gali nutikti, jei failai buvo modifikuoti, sugadinti arba diegimas\n    nebaigtas.\n\n    Saugumo sumetimais Seelen vartotojo sąsaja negali tęstis.\n\n    Įdiekite jį iš naujo iš oficialios svetainės.\n  files_integrity_title: Saugos įspėjimas – nepavyko patikrinti failų vientisumo\n  not_found: WebView2 vykdymo laikas nerastas\n  not_found_description: „Seelen“ vartotojo sąsajai reikalinga „Webview2 Runtime“. Įdiekite jį.\n  outdated: „WebView2“ vykdymo laikas pasenęs\n  outdated_description: >-\n    „Seelen“ vartotojo sąsajai reikalinga „Webview2 Runtime“ %{min_version} arba\n    naujesnė versija. Prašome atnaujinti.\nservice:\n  not_running: Seelen UI paslauga neveikia\n  not_running_description: >-\n    Seelen UI paslauga (slu-service) yra priklausomybė / pagalbininkas, kad\n    programa tinkamai veiktų.\n  not_running_ok: Pradėti paslaugą\nshortcut:\n  register:\n    placeholder: Paspauskite bet kurį raktų derinį\n    title: Registruojant naują nuorodą\nwidget_liveness:\n  failed_description: |-\n    Valdiklis '%{widget_name}' nustojo reaguoti per daug kartų.\n\n    Galite pabandyti iš naujo paleisti programą.\n  failed_title: Valdiklio klaida\nwidget_permissions:\n  perm_open_file: atidaryti failus\n  perm_run: paleisti programas\n  request_description: |-\n    Valdiklis '%{widget_name}' prašo leidimo %{command}.\n\n    Ar norite tai leisti?\n  request_title: Valdiklio leidimo užklausa\n"
  },
  {
    "path": "src/background/i18n/lv.yml",
    "content": "cancel: Atcelt\ndone: Darīts\nelevated:\n  description: >-\n    Seelen UI darbojas ar administratora privilēģijām.\n\n    Tas nepievieno vai neuzlabo nekādas funkcijas, un tas var radīt drošības\n    risku.\n\n    Lai nodrošinātu labāku drošību, lūdzu, restartējiet to bez administratora\n    atļaujām.\n  title: Darbojas kā administrators\nfile_explorer: Failu pārlūks\nresource:\n  added: Ir pievienots jauns resurss\n  download_failed_body: >-\n    Lejupielādējot resursu, kaut kas notika nepareizi, lūdzu, ziņojiet par to\n    Seelen izstrādātāju komandai.\n  download_failed_title: Resursu lejupielāde neizdevās\n  downloading: Aktīvu lejupielāde...\n  downloading_body: Tas var aizņemt dažas sekundes, lūdzu, neaizveriet logu.\n  enable: Ieslēgt\nruntime:\n  corrupted_data: Bojāti dati\n  corrupted_file: >-\n    Konfigurācijas fails ir bojāts, tā vietā tiek izmantotas noklusējuma\n    vērtības.\n  corrupted_file_path: Bojāts faila ceļš\n  download: Dodieties uz lejupielādes lapu\n  files_integrity: |-\n    Mēs nevarējām pārbaudīt lietojumprogrammu failu integritāti.\n    Tas var notikt, ja faili ir modificēti, bojāti vai instalēšana ir nepilnīga.\n    Drošības apsvērumu dēļ Seelen UI nevar turpināt.\n    Lūdzu, atkārtoti instalējiet to no oficiālās vietnes.\n  files_integrity_title: Drošības brīdinājums — failu integritātes pārbaude neizdevās\n  not_found: WebView2 izpildlaiks nav atrasts\n  not_found_description: Seelen UI ir nepieciešams Webview2 Runtime. Lūdzu, instalējiet to.\n  outdated: WebView2 izpildlaiks ir novecojis\n  outdated_description: >-\n    Seelen lietotāja interfeisam ir nepieciešama Webview2 Runtime %{min_version}\n    vai jaunāka versija. Lūdzu, atjauniniet to.\nservice:\n  not_running: Seelen UI pakalpojums nedarbojas\n  not_running_description: >-\n    Seelen UI pakalpojums (slu-service) ir atkarība/palīgs, lai lietotne\n    darbotos pareizi.\n  not_running_ok: Sāciet pakalpojumu\nshortcut:\n  register:\n    placeholder: Nospiediet jebkuru atslēgu kombināciju\n    title: Reģistrējot jaunu saīsni\nwidget_liveness:\n  failed_description: |-\n    Logrīks '%{widget_name}' pārstāja reaģēt pārāk daudz reižu.\n\n    Varat mēģināt restartēt lietotni.\n  failed_title: Logrīka kļūda\nwidget_permissions:\n  perm_open_file: atvērt failus\n  perm_run: palaist programmas\n  request_description: |-\n    Logrīks '%{widget_name}' pieprasa atļauju %{command}.\n\n    Vai vēlaties to atļaut?\n  request_title: Logrīka atļaujas pieprasījums\n"
  },
  {
    "path": "src/background/i18n/mk.yml",
    "content": "cancel: Откажи\ndone: Готово\nelevated:\n  description: >-\n    Seelen UI работи со администраторски привилегии.\n\n    Ова не додава или подобрува никакви функции и може да претставува\n    безбедносен ризик.\n\n    За подобра безбедност, рестартирајте го без администраторски дозволи.\n  title: Работи како администратор\nfile_explorer: Истражувач на датотеки\nresource:\n  added: Додаден е нов ресурс\n  download_failed_body: >-\n    Нешто тргна наопаку додека го преземате ресурсот, ве молиме пријавете го во\n    тимот за развивачи на Селен.\n  download_failed_title: Преземањето на ресурсите не успеа\n  downloading: Преземање средства ...\n  downloading_body: Ова може да потрае неколку секунди, ве молиме, не затворајте го прозорецот.\n  enable: Овозможи\nruntime:\n  corrupted_data: Расипани податоци\n  corrupted_file: >-\n    Датотеката за конфигурација е расипана, наместо тоа, користејќи стандардни\n    вредности.\n  corrupted_file_path: Расипана патека на датотеки\n  download: Одете на страницата за преземање\n  files_integrity: >-\n    Не можевме да го потврдиме интегритетот на датотеките на апликацијата.\n\n    Ова може да се случи ако датотеките се изменети, оштетени или инсталацијата\n    е нецелосна.\n\n    Од безбедносни причини, Seelen UI не може да продолжи.\n\n    Ве молиме повторно инсталирајте го од официјалната веб-страница.\n  files_integrity_title: >-\n    Безбедносно предупредување - Проверката на интегритетот на датотеките не\n    успеа\n  not_found: WebView2 Runtime не е пронајдено\n  not_found_description: Seelen UI бара Webview2 Runtime. Ве молиме инсталирајте го.\n  outdated: WebView2 Runtime е застарено\n  outdated_description: >-\n    Seelen UI бара Webview2 Runtime %{min_version} или повисоко. Ве молиме\n    ажурирајте го.\nservice:\n  not_running: Селенската UI услуга не работи\n  not_running_description: >-\n    Seelen UI Service (slu-service) е зависност/помошник за апликацијата да\n    работи правилно.\n  not_running_ok: Започнете ја услугата\nshortcut:\n  register:\n    placeholder: Притиснете која било комбинација на копче\n    title: Регистрирање на нова кратенка\nwidget_liveness:\n  failed_description: |-\n    Виџетот „%{widget_name}“ престана да реагира премногу пати.\n\n    Може да се обидете да ја рестартирате апликацијата.\n  failed_title: Грешка во виџетот\nwidget_permissions:\n  perm_open_file: отворени датотеки\n  perm_run: стартувајте програми\n  request_description: |-\n    Виџетот „%{widget_name}“ бара дозвола за %{command}.\n\n    Дали сакате да го дозволите ова?\n  request_title: Барање за дозвола за виџети\n"
  },
  {
    "path": "src/background/i18n/mn.yml",
    "content": "cancel: Цуаах\ndone: Хийх\nelevated:\n  description: >-\n    Seelen UI нь администраторын эрхтэй ажиллаж байна.\n\n    Энэ нь ямар ч функцийг нэмж, сайжруулахгүй бөгөөд аюулгүй байдалд эрсдэл\n    учруулж болзошгүй юм.\n\n    Аюулгүй байдлыг сайжруулахын тулд администраторын зөвшөөрөлгүйгээр дахин\n    эхлүүлнэ үү.\n  title: Администратороор ажиллаж байна\nfile_explorer: File Explorer\nresource:\n  added: Шинэ нөөц нэмэгдсэн\n  download_failed_body: >-\n    Нөөцийг татаж авч байхдаа ямар нэг зүйл буруу болсон бол Seelen\n    хөгжүүлэгчийн баг руу мэдээлнэ үү.\n  download_failed_title: Нөөцийг татаж авах амжилтгүй боллоо\n  downloading: Хөрөнгө татаж авах ...\n  downloading_body: Энэ нь хэдэн секунд болж магадгүй бөгөөд цонхыг хааж болохгүй.\n  enable: Болоцоо огох\nruntime:\n  corrupted_data: Гэмтсэн өгөгдөл\n  corrupted_file: Тохируулгын файл эвдэрсэн, оронд нь үндсэн утгыг ашиглана.\n  corrupted_file_path: Авлисан файлын зам\n  download: Татаж авах хуудас руу очно уу\n  files_integrity: >-\n    Бид програмын файлуудын бүрэн бүтэн байдлыг шалгаж чадсангүй.\n\n    Энэ нь файлуудыг өөрчилсөн, эвдэрсэн эсвэл суулгац дутуу хийгдсэн тохиолдолд\n    тохиолдож болно.\n\n    Аюулгүй байдлын үүднээс Seelen UI-г үргэлжлүүлэх боломжгүй.\n\n    Үүнийг албан ёсны вэбсайтаас дахин суулгана уу.\n  files_integrity_title: Аюулгүй байдлын дохиолол - Файлын бүрэн бүтэн байдлыг шалгаж чадсангүй\n  not_found: WebView2 Ажиллах цаг олдсонгүй\n  not_found_description: Seelen UI нь Webview2 Runtime шаарддаг. Үүнийг суулгана уу.\n  outdated: WebView2 Ажиллах хугацаа хуучирсан\n  outdated_description: >-\n    Seelen UI нь Webview2 Runtime %{min_version} буюу түүнээс дээш байхыг\n    шаарддаг. Үүнийг шинэчилнэ үү.\nservice:\n  not_running: Seelen UI үйлчилгээ ажиллахгүй байна\n  not_running_description: >-\n    Seelen UI үйлчилгээ (slu-service) нь програмыг зөв ажиллуулахад\n    хамааралтай/туслагч юм.\n  not_running_ok: Үйлчилгээг эхлүүлэх\nshortcut:\n  register:\n    placeholder: Аливаа товчлуурын хослолыг дарна уу\n    title: Шинэ товчлол бүртгэж байна\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' виджет хэт олон удаа хариу өгөхөө больсон.\n\n    Та програмыг дахин эхлүүлэхийг оролдож болно.\n  failed_title: Виджетийн алдаа\nwidget_permissions:\n  perm_open_file: файлуудыг нээх\n  perm_run: програмуудыг ажиллуулах\n  request_description: |-\n    '%{widget_name}' виджет нь %{command}-д зөвшөөрөл хүсэж байна.\n\n    Та үүнийг зөвшөөрөхийг хүсч байна уу?\n  request_title: Виджетийн зөвшөөрлийн хүсэлт\n"
  },
  {
    "path": "src/background/i18n/ms.yml",
    "content": "cancel: Batalkan\ndone: Selesai\nelevated:\n  description: >-\n    UI Seelen berjalan dengan keistimewaan pentadbir.\n\n    Ini tidak menambah atau menambah baik sebarang ciri, dan ia mungkin\n    menimbulkan risiko keselamatan.\n\n    Untuk keselamatan yang lebih baik, sila mulakan semula tanpa kebenaran\n    pentadbir.\n  title: Berjalan sebagai Pentadbir\nfile_explorer: Penjelajah Fail\nresource:\n  added: Sumber baru telah ditambah\n  download_failed_body: >-\n    Sesuatu yang tidak kena semasa memuat turun sumber, sila laporkan kepada\n    pasukan pemaju Seelen.\n  download_failed_title: Muat turun sumber gagal\n  downloading: Memuat turun aset ...\n  downloading_body: Ini mungkin mengambil masa beberapa saat, jangan menutup tetingkap.\n  enable: Membolehkan\nruntime:\n  corrupted_data: Data yang rosak\n  corrupted_file: Fail konfigurasi rosak, menggunakan nilai lalai.\n  corrupted_file_path: Laluan fail yang rosak\n  download: Pergi ke halaman muat turun\n  files_integrity: >-\n    Kami tidak dapat mengesahkan integriti fail aplikasi.\n\n    Ini boleh berlaku jika fail diubah suai, rosak atau pemasangan tidak\n    lengkap.\n\n    Atas sebab keselamatan, UI Seelen tidak boleh diteruskan.\n\n    Sila pasang semula dari laman web rasmi.\n  files_integrity_title: Makluman Keselamatan - Semakan Integriti Fail Gagal\n  not_found: WebView2 Runtime tidak ditemui\n  not_found_description: UI Seelen memerlukan Webview2 Runtime. Sila pasang.\n  outdated: WebView2 Runtime sudah lapuk\n  outdated_description: >-\n    UI Seelen memerlukan Webview2 Runtime %{min_version} atau lebih tinggi. Sila\n    kemas kini.\nservice:\n  not_running: Perkhidmatan UI Seelen tidak berjalan\n  not_running_description: >-\n    Perkhidmatan UI Seelen (perkhidmatan slu) ialah kebergantungan/pembantu\n    untuk apl berfungsi dengan betul.\n  not_running_ok: Mulakan perkhidmatan\nshortcut:\n  register:\n    placeholder: Tekan sebarang gabungan kekunci\n    title: Mendaftarkan pintasan baru\nwidget_liveness:\n  failed_description: |-\n    Widget '%{widget_name}' berhenti bertindak balas terlalu banyak kali.\n\n    Anda boleh cuba memulakan semula apl.\n  failed_title: Ralat Widget\nwidget_permissions:\n  perm_open_file: buka fail\n  perm_run: menjalankan program\n  request_description: |-\n    Widget '%{widget_name}' meminta kebenaran untuk %{command}.\n\n    Adakah anda mahu membenarkan ini?\n  request_title: Permintaan Kebenaran Widget\n"
  },
  {
    "path": "src/background/i18n/mt.yml",
    "content": "cancel: Ikkanċella\ndone: Magħmul\nelevated:\n  description: >-\n    Seelen UI qed taħdem bi privileġġi ta' amministratur.\n\n    Dan ma jżid jew itejjeb l-ebda karatteristika, u jista' joħloq riskju\n    għas-sigurtà.\n\n    Għal sigurtà aħjar, jekk jogħġbok ibda mill-ġdid mingħajr permessi ta'\n    amministratur.\n  title: Tmexxi bħala Amministratur\nfile_explorer: File Explorer\nresource:\n  added: Ġiet miżjuda riżorsa ġdida\n  download_failed_body: >-\n    Xi ħaġa marret ħażin waqt li tniżżel ir-riżorsa, jekk jogħġbok irrapportaha\n    lit-tim tal-iżviluppatur Seelen.\n  download_failed_title: It-tniżżil tar-riżorsi falla\n  downloading: Tniżżil ta 'assi ...\n  downloading_body: Dan jista 'jieħu ftit sekondi, jekk jogħġbok ma tagħlaqx it-tieqa.\n  enable: Jippermetti\nruntime:\n  corrupted_data: Dejta korrotta\n  corrupted_file: Fajl ta 'konfigurazzjoni huwa korrotta, billi tuża valuri default minflok.\n  corrupted_file_path: Triq tal-fajl korrotta\n  download: Mur fil-paġna tat-tniżżil\n  files_integrity: >-\n    Ma stajniex nivverifikaw l-integrità tal-fajls tal-applikazzjoni.\n\n    Dan jista 'jiġri jekk il-fajls ġew modifikati, korrotta, jew\n    l-installazzjoni ma tkunx kompluta.\n\n    Għal raġunijiet ta' sigurtà, Seelen UI ma tistax tkompli.\n\n    Jekk jogħġbok installa mill-ġdid mill-websajt uffiċjali.\n  files_integrity_title: Twissija tas-Sigurtà - Iċċekkjar tal-Integrità tal-Fajls Fallit\n  not_found: WebView2 Runtime ma nstabx\n  not_found_description: Seelen UI teħtieġ Webview2 Runtime. Jekk jogħġbok installah.\n  outdated: WebView2 Runtime skadut\n  outdated_description: >-\n    Seelen UI teħtieġ Webview2 Runtime %{min_version} jew ogħla. Jekk jogħġbok\n    aġġornaha.\nservice:\n  not_running: Is-Servizz ta' Seelen UI mhux qed jaħdem\n  not_running_description: >-\n    Seelen UI Service (slu-service) huwa dipendenza/helper biex l-app taħdem\n    sew.\n  not_running_ok: Ibda s-servizz\nshortcut:\n  register:\n    placeholder: Agħfas kwalunkwe kombinazzjoni taċ-ċavetta\n    title: Tirreġistra shortcut ġdid\nwidget_liveness:\n  failed_description: |-\n    Il-widget '%{widget_name}' waqaf jirrispondi wisq drabi.\n\n    Tista 'tipprova terġa' tibda l-app.\n  failed_title: Widget Żball\nwidget_permissions:\n  perm_open_file: fajls miftuħa\n  perm_run: imexxu programmi\n  request_description: |-\n    Il-widget '%{widget_name}' qed jitlob permess lil %{kmand}.\n\n    Trid tippermetti dan?\n  request_title: Talba ta' Permess għal Widget\n"
  },
  {
    "path": "src/background/i18n/ne.yml",
    "content": "cancel: रद्द गर्नु\ndone: पूरा\nelevated:\n  description: >-\n    Seelen UI प्रशासक विशेषाधिकार संग चलिरहेको छ।\n\n    यसले कुनै पनि सुविधाहरू थप्ने वा सुधार गर्दैन, र यसले सुरक्षा जोखिम\n    निम्त्याउन सक्छ।\n\n    राम्रो सुरक्षाको लागि, कृपया प्रशासक अनुमति बिना यसलाई पुन: सुरु गर्नुहोस्।\n  title: प्रशासकको रूपमा चलिरहेको छ\nfile_explorer: फाइल एक्सप्लोरर\nresource:\n  added: एक नयाँ स्रोत थपियो\n  download_failed_body: >-\n    संसाधन डाउनलोड गर्दा केहि गलत भयो, प्रयोगकर्तालाई देखिने विकासकर्ता टोलीमा\n    रिपोर्ट गर्नुहोस्।\n  download_failed_title: स्रोत डाउनलोड असफल भयो\n  downloading: सम्पत्ति डाउनलोड गर्दै ...\n  downloading_body: यसले केहि सेकेन्ड लिन सक्दछ, कृपया विन्डो बन्द नगर्नुहोस्।\n  enable: समर्थ बनाउनु\nruntime:\n  corrupted_data: भ्रष्ट डाटा\n  corrupted_file: एक कन्फिगरेसन फाईल बिग्रेको छ, यसको सट्टामा पूर्वनिर्धारित मान प्रयोग गर्दै।\n  corrupted_file_path: भ्रष्ट फाइल मार्ग\n  download: डाउनलोड पृष्ठमा जानुहोस्\n  files_integrity: |-\n    हामीले अनुप्रयोग फाइलहरूको अखण्डता प्रमाणित गर्न सकेनौं।\n    यदि फाइलहरू परिमार्जन, भ्रष्ट वा स्थापना अपूर्ण भएमा यो हुन सक्छ।\n    सुरक्षा कारणहरूका लागि, Seelen UI जारी राख्न सक्दैन।\n    कृपया यसलाई आधिकारिक वेबसाइटबाट पुन: स्थापना गर्नुहोस्।\n  files_integrity_title: सुरक्षा सतर्कता - फाइलहरूको अखण्डता जाँच असफल भयो\n  not_found: WebView2 रनटाइम फेला परेन\n  not_found_description: Seelen UI लाई Webview2 रनटाइम चाहिन्छ। कृपया यसलाई स्थापना गर्नुहोस्।\n  outdated: WebView2 रनटाइम पुरानो\n  outdated_description: >-\n    Seelen UI लाई Webview2 रनटाइम %{min_version} वा उच्च चाहिन्छ। कृपया यसलाई\n    अपडेट गर्नुहोस्।\nservice:\n  not_running: Seelen UI सेवा चलिरहेको छैन\n  not_running_description: >-\n    Seelen UI सेवा (slu-service) एपलाई राम्रोसँग काम गर्नको लागि निर्भरता/सहायक\n    हो।\n  not_running_ok: सेवा सुरु गर्नुहोस्\nshortcut:\n  register:\n    placeholder: कुनै कुञ्जी संयोजन थिच्नुहोस्\n    title: नयाँ सर्टकट दर्ता गर्दै\nwidget_liveness:\n  failed_description: |-\n    विजेट '%{widget_name}' धेरै पटक प्रतिक्रिया दिन रोकियो।\n\n    तपाईं एप पुन: सुरु गर्ने प्रयास गर्न सक्नुहुन्छ।\n  failed_title: विजेट त्रुटि\nwidget_permissions:\n  perm_open_file: फाइलहरू खोल्नुहोस्\n  perm_run: कार्यक्रमहरू सञ्चालन गर्ने\n  request_description: |-\n    विजेट '%{widget_name}' ले %{command} लाई अनुमति अनुरोध गरिरहेको छ।\n\n    के तपाईं यसलाई अनुमति दिन चाहनुहुन्छ?\n  request_title: विजेट अनुमति अनुरोध\n"
  },
  {
    "path": "src/background/i18n/nl.yml",
    "content": "cancel: Annuleren\ndone: Klaar\nelevated:\n  description: >-\n    Seelen UI draait met beheerdersrechten.\n\n    Dit voegt geen functies toe of verbetert deze niet, en het kan een\n    veiligheidsrisico vormen.\n\n    Voor een betere veiligheid dient u het programma opnieuw op te starten\n    zonder beheerdersrechten.\n  title: Uitvoeren als beheerder\nfile_explorer: Bestandsverkenner\nresource:\n  added: Er is een nieuwe bron toegevoegd\n  download_failed_body: >-\n    Er is iets misgegaan tijdens het downloaden van de bron, meld dit\n    alstublieft aan het ontwikkelteam van Seelen.\n  download_failed_title: Downloaden van bronnen mislukt\n  downloading: Bedrijfsmiddelen downloaden...\n  downloading_body: Dit kan enkele seconden duren, sluit het venster niet.\n  enable: inschakelen\nruntime:\n  corrupted_data: Beschadigde gegevens\n  corrupted_file: >-\n    Een configuratiebestand is beschadigd, waardoor standaardwaarden worden\n    gebruikt.\n  corrupted_file_path: Corrupt bestandspad\n  download: Ga naar de downloadpagina\n  files_integrity: >-\n    We konden de integriteit van de applicatiebestanden niet verifiëren.\n\n    Dit kan gebeuren als bestanden zijn gewijzigd of beschadigd of als de\n    installatie onvolledig is.\n\n    Om veiligheidsredenen kan de Seelen UI niet doorgaan.\n\n    Installeer het opnieuw vanaf de officiële website.\n  files_integrity_title: >-\n    Beveiligingswaarschuwing - Controle van de integriteit van bestanden is\n    mislukt\n  not_found: WebView2 Runtime niet gevonden\n  not_found_description: >-\n    De Seelen-gebruikersinterface vereist Webview2 Runtime. Installeer het\n    alstublieft.\n  outdated: WebView2 Runtime verouderd\n  outdated_description: >-\n    Seelen UI vereist Webview2 Runtime %{min_version} of hoger. Update het\n    alstublieft.\nservice:\n  not_running: Seelen UI-service is niet actief\n  not_running_description: >-\n    Seelen UI Service (slu-service) is een afhankelijkheid/helper om de app\n    correct te laten werken.\n  not_running_ok: Dienst starten\nshortcut:\n  register:\n    placeholder: Druk op een sleutelcombinatie\n    title: Nieuwe snelkoppeling registreren\nwidget_liveness:\n  failed_description: |-\n    De widget '%{widget_name}' reageerde te vaak niet meer.\n\n    U kunt proberen de app opnieuw te starten.\n  failed_title: Widgetfout\nwidget_permissions:\n  perm_open_file: bestanden openen\n  perm_run: programma's uitvoeren\n  request_description: |-\n    De widget '%{widget_name}' vraagt ​​toestemming voor %{command}.\n\n    Wilt u dit toestaan?\n  request_title: Verzoek om widgettoestemming\n"
  },
  {
    "path": "src/background/i18n/no.yml",
    "content": "cancel: Kansellere\ndone: Ferdig\nelevated:\n  description: >-\n    Seelen UI kjører med administratorrettigheter.\n\n    Dette legger ikke til eller forbedrer noen funksjoner, og det kan utgjøre en\n    sikkerhetsrisiko.\n\n    For bedre sikkerhet, start den på nytt uten administratortillatelser.\n  title: Kjører som administrator\nfile_explorer: Filutforsker\nresource:\n  added: En ny ressurs er lagt til\n  download_failed_body: >-\n    Noe gikk galt mens du lastet ned ressursen, vennligst rapporter den til\n    Seelen -utviklerteamet.\n  download_failed_title: Ressursnedlasting mislyktes\n  downloading: Last ned eiendeler ...\n  downloading_body: Dette kan ta noen sekunder, ikke lukk vinduet.\n  enable: Aktiver\nruntime:\n  corrupted_data: Ødelagte data\n  corrupted_file: En konfigurasjonsfil er ødelagt ved å bruke standardverdier i stedet.\n  corrupted_file_path: Ødelagt filsti\n  download: Gå til nedlastingssiden\n  files_integrity: >-\n    Vi kunne ikke bekrefte integriteten til applikasjonsfilene.\n\n    Dette kan skje hvis filene ble endret, ødelagt eller installasjonen er\n    ufullstendig.\n\n    Av sikkerhetsgrunner kan ikke Seelen UI fortsette.\n\n    Installer den på nytt fra den offisielle nettsiden.\n  files_integrity_title: Sikkerhetsvarsel - Kontroll av filers integritet mislyktes\n  not_found: WebView2 Runtime ikke funnet\n  not_found_description: Seelen UI krever Webview2 Runtime. Vennligst installer den.\n  outdated: WebView2 Runtime utdatert\n  outdated_description: >-\n    Seelen UI krever Webview2 Runtime %{min_version} eller høyere. Vennligst\n    oppdater den.\nservice:\n  not_running: Seelen UI Service kjører ikke\n  not_running_description: >-\n    Seelen UI Service (slu-service) er en avhengighet/hjelper for at appen skal\n    fungere skikkelig.\n  not_running_ok: Start tjenesten\nshortcut:\n  register:\n    placeholder: Trykk på enhver tastekombinasjon\n    title: Registrering av ny snarvei\nwidget_liveness:\n  failed_description: |-\n    Modulen «%{widget_name}» sluttet å svare for mange ganger.\n\n    Du kan prøve å starte appen på nytt.\n  failed_title: Widget-feil\nwidget_permissions:\n  perm_open_file: åpne filer\n  perm_run: kjøre programmer\n  request_description: |-\n    Modulen «%{widget_name}» ber om tillatelse til %{command}.\n\n    Vil du tillate dette?\n  request_title: Forespørsel om widgettillatelse\n"
  },
  {
    "path": "src/background/i18n/pa.yml",
    "content": "cancel: ਰੱਦ ਕਰੋ\ndone: ਕੀਤਾ\nelevated:\n  description: >-\n    ਸੀਲਨ UI ਪ੍ਰਸ਼ਾਸਕ ਦੇ ਅਧਿਕਾਰਾਂ ਨਾਲ ਚੱਲ ਰਿਹਾ ਹੈ।\n\n    ਇਹ ਕਿਸੇ ਵੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਜੋੜਦਾ ਜਾਂ ਸੁਧਾਰਦਾ ਨਹੀਂ ਹੈ, ਅਤੇ ਇਹ ਇੱਕ ਸੁਰੱਖਿਆ\n    ਜੋਖਮ ਪੈਦਾ ਕਰ ਸਕਦਾ ਹੈ।\n\n    ਬਿਹਤਰ ਸੁਰੱਖਿਆ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਪ੍ਰਸ਼ਾਸਕ ਦੀ ਇਜਾਜ਼ਤ ਤੋਂ ਬਿਨਾਂ ਇਸਨੂੰ ਮੁੜ ਚਾਲੂ\n    ਕਰੋ।\n  title: ਪ੍ਰਸ਼ਾਸਕ ਵਜੋਂ ਚੱਲ ਰਿਹਾ ਹੈ\nfile_explorer: ਫਾਈਲ ਐਕਸਪਲੋਰਰ\nresource:\n  added: ਇੱਕ ਨਵਾਂ ਸਰੋਤ ਜੋੜਿਆ ਗਿਆ ਹੈ\n  download_failed_body: >-\n    ਸਰੋਤ ਨੂੰ ਡਾ ing ਨਲੋਡ ਕਰਨ ਵੇਲੇ ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ, ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਨੂੰ ਸਹਿਤ ਡਿਵੈਲਪਰ\n    ਟੀਮ ਵਿੱਚ ਰਿਪੋਰਟ ਕਰੋ.\n  download_failed_title: ਸਰੋਤ ਡਾਉਨਲੋਡ ਅਸਫਲ\n  downloading: ਸੰਪਤੀ ਨੂੰ ਡਾ ing ਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ...\n  downloading_body: ਇਹ ਕੁਝ ਸਕਿੰਟ ਲੈ ਸਕਦਾ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਵਿੰਡੋ ਨੂੰ ਬੰਦ ਨਾ ਕਰੋ.\n  enable: ਯੋਗ\nruntime:\n  corrupted_data: ਖਰਾਬ ਡੇਟਾ\n  corrupted_file: ਇਸ ਦੀ ਬਜਾਏ ਡਿਫਾਲਟ ਮੁੱਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਇੱਕ ਸੰਰਚਨਾ ਫਾਇਲ ਖਰਾਬ ਹੋ ਗਈ ਹੈ.\n  corrupted_file_path: ਖਰਾਬ ਫਾਈਲ ਮਾਰਗ\n  download: ਡਾਊਨਲੋਡ ਪੰਨੇ 'ਤੇ ਜਾਓ\n  files_integrity: >-\n    ਅਸੀਂ ਐਪਲੀਕੇਸ਼ਨ ਫਾਈਲਾਂ ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕਰ ਸਕੇ।\n\n    ਇਹ ਉਦੋਂ ਹੋ ਸਕਦਾ ਹੈ ਜੇਕਰ ਫਾਈਲਾਂ ਨੂੰ ਸੋਧਿਆ ਗਿਆ ਸੀ, ਖਰਾਬ ਕੀਤਾ ਗਿਆ ਸੀ, ਜਾਂ\n    ਇੰਸਟਾਲੇਸ਼ਨ ਅਧੂਰੀ ਹੈ।\n\n    ਸੁਰੱਖਿਆ ਕਾਰਨਾਂ ਕਰਕੇ, Seelen UI ਜਾਰੀ ਨਹੀਂ ਰਹਿ ਸਕਦਾ ਹੈ।\n\n    ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਅਧਿਕਾਰਤ ਵੈੱਬਸਾਈਟ ਤੋਂ ਮੁੜ ਸਥਾਪਿਤ ਕਰੋ।\n  files_integrity_title: ਸੁਰੱਖਿਆ ਚੇਤਾਵਨੀ - ਫਾਈਲਾਂ ਦੀ ਇਕਸਾਰਤਾ ਜਾਂਚ ਅਸਫਲ ਰਹੀ\n  not_found: WebView2 ਰਨਟਾਈਮ ਨਹੀਂ ਮਿਲਿਆ\n  not_found_description: Seelen UI ਨੂੰ Webview2 ਰਨਟਾਈਮ ਦੀ ਲੋੜ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਸਥਾਪਿਤ ਕਰੋ।\n  outdated: WebView2 ਰਨਟਾਈਮ ਪੁਰਾਣਾ\n  outdated_description: >-\n    Seelen UI ਲਈ Webview2 ਰਨਟਾਈਮ %{min_version} ਜਾਂ ਇਸ ਤੋਂ ਵੱਧ ਦੀ ਲੋੜ ਹੈ। ਕਿਰਪਾ\n    ਕਰਕੇ ਇਸਨੂੰ ਅੱਪਡੇਟ ਕਰੋ।\nservice:\n  not_running: ਸੀਲਨ UI ਸੇਵਾ ਨਹੀਂ ਚੱਲ ਰਹੀ\n  not_running_description: ਸੀਲੇਨ UI ਸੇਵਾ (ਸਲੂ-ਸੇਵਾ) ਐਪ ਦੇ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਕਰਨ ਲਈ ਇੱਕ ਨਿਰਭਰਤਾ/ਸਹਾਇਕ ਹੈ।\n  not_running_ok: ਸੇਵਾ ਸ਼ੁਰੂ ਕਰੋ\nshortcut:\n  register:\n    placeholder: ਕੋਈ ਵੀ ਕੁੰਜੀ ਸੰਜੋਗ ਦਬਾਓ\n    title: ਨਵਾਂ ਸ਼ੌਰਟਕਟ ਰਜਿਸਟਰ ਕਰਨਾ\nwidget_liveness:\n  failed_description: |-\n    ਵਿਜੇਟ '%{widget_name}' ਨੇ ਬਹੁਤ ਵਾਰ ਜਵਾਬ ਦੇਣਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ।\n\n    ਤੁਸੀਂ ਐਪ ਨੂੰ ਰੀਸਟਾਰਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਸਕਦੇ ਹੋ।\n  failed_title: ਵਿਜੇਟ ਗਲਤੀ\nwidget_permissions:\n  perm_open_file: ਫਾਈਲਾਂ ਖੋਲ੍ਹੋ\n  perm_run: ਪ੍ਰੋਗਰਾਮ ਚਲਾਓ\n  request_description: |-\n    ਵਿਜੇਟ '%{widget_name}' %{command} ਨੂੰ ਇਜਾਜ਼ਤ ਮੰਗ ਰਿਹਾ ਹੈ।\n\n    ਕੀ ਤੁਸੀਂ ਇਸਦੀ ਇਜਾਜ਼ਤ ਦੇਣਾ ਚਾਹੁੰਦੇ ਹੋ?\n  request_title: ਵਿਜੇਟ ਅਨੁਮਤੀ ਦੀ ਬੇਨਤੀ\n"
  },
  {
    "path": "src/background/i18n/pl.yml",
    "content": "cancel: Anulować\ndone: Zrobione\nelevated:\n  description: >-\n    Seelen UI działa z uprawnieniami administratora.\n\n    Nie dodaje to ani nie ulepsza żadnych funkcji i może stanowić zagrożenie\n    bezpieczeństwa.\n\n    Dla większego bezpieczeństwa uruchom go ponownie bez uprawnień\n    administratora.\n  title: Uruchamianie jako Administrator\nfile_explorer: Eksplorator plików\nresource:\n  added: Dodano nowy zasób\n  download_failed_body: >-\n    Coś poszło nie tak podczas pobierania zasobu, zgłoś to zespołowi deweloperów\n    Seelen.\n  download_failed_title: Pobieranie zasobów nie powiodło się\n  downloading: Pobieranie zasobów...\n  downloading_body: Może to potrwać kilka sekund, nie zamykaj okna.\n  enable: Włącz\nruntime:\n  corrupted_data: Uszkodzone dane\n  corrupted_file: >-\n    Plik konfiguracyjny jest uszkodzony i zamiast niego używane są wartości\n    domyślne.\n  corrupted_file_path: Uszkodzona ścieżka pliku\n  download: Przejdź do strony pobierania\n  files_integrity: >-\n    Nie mogliśmy zweryfikować integralności plików aplikacji.\n\n    Może się to zdarzyć, jeśli pliki zostały zmodyfikowane, uszkodzone lub\n    instalacja jest niekompletna.\n\n    Ze względów bezpieczeństwa interfejs użytkownika Seelen nie może być\n    kontynuowany.\n\n    Zainstaluj go ponownie z oficjalnej strony internetowej.\n  files_integrity_title: Alert zabezpieczeń — sprawdzenie integralności plików nie powiodło się\n  not_found: Nie znaleziono środowiska uruchomieniowego WebView2\n  not_found_description: >-\n    Interfejs użytkownika Seelen wymaga środowiska uruchomieniowego Webview2.\n    Zainstaluj go.\n  outdated: Środowisko wykonawcze WebView2 jest nieaktualne\n  outdated_description: >-\n    Interfejs użytkownika Seelen wymaga środowiska uruchomieniowego Webview2\n    %{min_version} lub nowszego. Zaktualizuj je i spróbuj ponownie.\nservice:\n  not_running: Usługa Seelen UI nie działa prawidłowo\n  not_running_description: >-\n    Usługa Seelen UI Service (slu-service) jest zależnością umożliwiającą\n    prawidłowe działanie aplikacji.\n  not_running_ok: Uruchom usługę\nshortcut:\n  register:\n    placeholder: Naciśnij dowolną kombinację klawiszy\n    title: Rejestracja nowego skrótu\nwidget_liveness:\n  failed_description: |-\n    Widżet „%{widget_name}” przestał odpowiadać zbyt wiele razy.\n\n    Możesz spróbować ponownie uruchomić aplikację.\n  failed_title: Błąd widgetu\nwidget_permissions:\n  perm_open_file: otwarte pliki\n  perm_run: uruchamiać programy\n  request_description: |-\n    Widżet „%{widget_name}” prosi o pozwolenie na %{command}.\n\n    Czy chcesz na to pozwolić?\n  request_title: Żądanie pozwolenia na widget\n"
  },
  {
    "path": "src/background/i18n/ps.yml",
    "content": "cancel: لغوه کول\ndone: شوی\nelevated:\n  description: >-\n    سیلین UI د مدیر امتیازاتو سره پرمخ ځي.\n\n    دا هیڅ ځانګړتیاوې نه اضافه کوي یا وده نه کوي، او دا ممکن امنیتي خطر رامنځته\n    کړي.\n\n    د ښه امنیت لپاره، مهرباني وکړئ دا د مدیر اجازې پرته بیا پیل کړئ.\n  title: د مدیر په توګه چلول\nfile_explorer: د فایل سپړونکی\nresource:\n  added: یو نوی سرچینه اضافه شوې\n  download_failed_body: >-\n    یو څه د سرچینې ډاونلوډ پر مهال غلط شوي، مهرباني وکړئ دا د اصلي پراختیا کونکي\n    ټیم ته راپور کړئ.\n  download_failed_title: د سرچینې ډاونلوډ ونشو\n  downloading: د شتمنیو ډاونلوډ کول ...\n  downloading_body: دا ممکن یو څو ثانیې ونیسي، مهرباني وکړئ کړکۍ بند کړئ.\n  enable: وړ\nruntime:\n  corrupted_data: فاسد ډاټا\n  corrupted_file: د سازونې فایل فاسد شوی، پرځای یې د ډیفالټ ارزښتونو په کارولو سره.\n  corrupted_file_path: فاسد فایل لاره\n  download: د ډاونلوډ پاڼې ته لاړ شئ\n  files_integrity: |-\n    موږ نشو کولی د غوښتنلیک فایلونو بشپړتیا تایید کړو.\n    دا واقع کیدی شي که فایلونه تعدیل شوي، فاسد شوي، یا نصب کول نیمګړي وي.\n    د امنیت دلایلو لپاره ، سیلین UI نشي دوام کولی.\n    مهرباني وکړئ دا د رسمي ویب پاڼې څخه بیا نصب کړئ.\n  files_integrity_title: د امنیت خبرتیا - د فایلونو بشپړتیا چک ناکام شو\n  not_found: WebView2 د چلولو وخت ونه موندل شو\n  not_found_description: Seelen UI د Webview2 Runtime ته اړتیا لري. مهرباني وکړئ دا نصب کړئ.\n  outdated: WebView2 د چلولو وخت زوړ شوی\n  outdated_description: >-\n    Seelen UI د Webview2 Runtime %{min_version} یا لوړ ته اړتیا لري. مهرباني\n    وکړئ تازه یې کړئ.\nservice:\n  not_running: د سیلین UI خدمت نه روان دی\n  not_running_description: >-\n    د سیلین UI خدمت (slu-service) د ایپ لپاره د سم کار کولو لپاره انحصار / مرسته\n    کونکی دی.\n  not_running_ok: خدمت پیل کړئ\nshortcut:\n  register:\n    placeholder: په هر کلي ترکیب فشار ورکړئ\n    title: د نوي لنډلارې ثبت کول\nwidget_liveness:\n  failed_description: |-\n    ویجیټ '%{widget_name}' ډیر ځله ځواب ورکول بند کړل.\n\n    تاسو کولی شئ د اپلیکیشن بیا پیلولو هڅه وکړئ.\n  failed_title: د ویجټ تېروتنه\nwidget_permissions:\n  perm_open_file: فایلونه خلاص کړئ\n  perm_run: پروګرامونه چلوي\n  request_description: |-\n    ویجټ '%{widget_name}' %{command} ته د اجازې غوښتنه کوي.\n\n    ایا تاسو غواړئ دا اجازه ورکړئ؟\n  request_title: د ویجټ اجازې غوښتنه\n"
  },
  {
    "path": "src/background/i18n/pt-BR.yml",
    "content": "cancel: Cancelar\ndone: Feita\nelevated:\n  description: >-\n    Seelen UI está sendo executado com privilégios de administrador.\n\n    Isso não adiciona nem melhora nenhum recurso e pode representar um risco à\n    segurança.\n\n    Para melhor segurança, reinicie-o sem permissões de administrador.\n  title: Executando como Administrador\nfile_explorer: Explorador de arquivos\nresource:\n  added: Um novo recurso foi adicionado\n  download_failed_body: >-\n    Algo deu errado durante o download do recurso. Informe à equipe de\n    desenvolvedores da Seelen.\n  download_failed_title: Falha no download do recurso\n  downloading: Download de ativos...\n  downloading_body: Isso pode levar alguns segundos, mas não feche a janela.\n  enable: Habilitar\nruntime:\n  corrupted_data: Dados corrompidos\n  corrupted_file: >-\n    Um arquivo de configuração está corrompido, usando valores padrão em seu\n    lugar.\n  corrupted_file_path: Caminho de arquivo corrompido\n  download: Vá para a página de download\n  files_integrity: >-\n    Não foi possível verificar a integridade dos arquivos do aplicativo.\n\n    Isso pode acontecer se os arquivos forem modificados, corrompidos ou se a\n    instalação estiver incompleta.\n\n    Por motivos de segurança, o Seelen UI não pode continuar.\n\n    Por favor, reinstale-o no site oficial.\n  files_integrity_title: Alerta de segurança - Falha na verificação de integridade dos arquivos\n  not_found: WebView2 Runtime não encontrado\n  not_found_description: A Seelen UI requer o WebView2 Runtime. Por favor, instale-o.\n  outdated: WebView2 Runtime desatualizado\n  outdated_description: >-\n    A UI Seelen requer Webview2 Runtime %{min_version} ou superior. Por favor\n    atualize-o.\nservice:\n  not_running: Seelen UI Service não está em execução\n  not_running_description: >-\n    Seelen UI Service (slu-service) é uma dependência/ajudante para o aplicativo\n    funcionar corretamente.\n  not_running_ok: Iniciar serviço\nshortcut:\n  register:\n    placeholder: Pressione qualquer combinação de tecla\n    title: Registrando um novo atalho\nwidget_liveness:\n  failed_description: |-\n    O widget '%{widget_name}' parou de responder muitas vezes.\n\n    Você pode tentar reiniciar o aplicativo.\n  failed_title: Erro de widget\nwidget_permissions:\n  perm_open_file: abrir arquivos\n  perm_run: executar programas\n  request_description: |-\n    O widget '%{widget_name}' está solicitando permissão para %{command}.\n\n    Você quer permitir isso?\n  request_title: Solicitação de permissão de widget\n"
  },
  {
    "path": "src/background/i18n/pt-PT.yml",
    "content": "cancel: Cancelar\ndone: Concluída\nelevated:\n  description: >-\n    O Seelen UI está a ser executado com privilégios de administrador.\n\n    Isto não acrescenta nem melhora nenhum recurso e pode representar um risco\n    de segurança.\n\n    Para melhor segurança, reinicie-o sem permissões de administrador.\n  title: Executando como Administrador\nfile_explorer: Explorador de ficheiros\nresource:\n  added: Um novo recurso foi adicionado\n  download_failed_body: >-\n    Algo deu errado ao baixar o recurso, informe -o à equipe de desenvolvedores\n    da Seelen.\n  download_failed_title: Download de recursos falhou\n  downloading: Download de ativos ...\n  downloading_body: Isso pode levar alguns segundos, por favor, não feche a janela.\n  enable: Ativar\nruntime:\n  corrupted_data: Dados corrompidos\n  corrupted_file: Um arquivo de configuração está corrompido, usando valores padrão.\n  corrupted_file_path: Caminho de arquivo corrompido\n  download: Vá para a página de download\n  files_integrity: >-\n    Não foi possível verificar a integridade dos arquivos do aplicativo.\n\n    Isso pode acontecer se os arquivos forem modificados, corrompidos ou se a\n    instalação estiver incompleta.\n\n    Por motivos de segurança, o Seelen UI não pode continuar.\n\n    Por favor, reinstale-o no site oficial.\n  files_integrity_title: Alerta de segurança - Falha na verificação de integridade dos arquivos\n  not_found: WebView2 Tempo de execução não encontrado\n  not_found_description: O SEELEN UI requer o tempo de execução do WebView2. Por favor, instale.\n  outdated: WebView2 Tempo de execução desatualizado\n  outdated_description: >-\n    O SEELEN UI requer WebView2 Runtime %{min_version} ou superior. Por favor,\n    atualize -o.\nservice:\n  not_running: Serviço de interface do usuário de Seelen não está sendo executado\n  not_running_description: >-\n    O Serviço Seelen UI (SLU-Service) é uma dependência/auxiliar para o\n    aplicativo funcionar corretamente.\n  not_running_ok: Start Service\nshortcut:\n  register:\n    placeholder: Pressione qualquer combinação de tecla\n    title: Registrando um novo atalho\nwidget_liveness:\n  failed_description: |-\n    O widget '%{widget_name}' deixou de responder muitas vezes.\n\n    Pode tentar reiniciar o aplicativo.\n  failed_title: Erro de widget\nwidget_permissions:\n  perm_open_file: abrir ficheiros\n  perm_run: executar programas\n  request_description: |-\n    O widget '%{widget_name}' está a solicitar permissão para %{command}.\n\n    Quer permitir isso?\n  request_title: Pedido de permissão de widget\n"
  },
  {
    "path": "src/background/i18n/ro.yml",
    "content": "cancel: Anula\ndone: Făcut\nelevated:\n  description: >-\n    Seelen UI rulează cu privilegii de administrator.\n\n    Acest lucru nu adaugă sau îmbunătățește nicio caracteristică și poate\n    prezenta un risc de securitate.\n\n    Pentru o mai bună securitate, reporniți-l fără permisiuni de administrator.\n  title: Funcționează ca administrator\nfile_explorer: Explorator de fișiere\nresource:\n  added: O nouă resursă a fost adăugată\n  download_failed_body: >-\n    Ceva nu a mers bine în timpul descărcării resursei, vă rugăm să raportați\n    acest lucru echipei de dezvoltatori Seelen.\n  download_failed_title: Descărcarea resurselor a eșuat\n  downloading: Descărcarea activelor...\n  downloading_body: Aceasta poate dura câteva secunde, vă rugăm să nu închideți fereastra.\n  enable: Activare\nruntime:\n  corrupted_data: Date corupte\n  corrupted_file: >-\n    Un fișier de configurare este corupt, utilizând în schimb valorile\n    implicite.\n  corrupted_file_path: Calea fișierului coruptă\n  download: Accesați pagina de descărcare\n  files_integrity: >-\n    Nu am putut verifica integritatea fișierelor aplicației.\n\n    Acest lucru se poate întâmpla dacă fișierele au fost modificate, corupte sau\n    instalarea este incompletă.\n\n    Din motive de securitate, Seelen UI nu poate continua.\n\n    Vă rugăm să-l reinstalați de pe site-ul oficial.\n  files_integrity_title: Alertă de securitate - Verificarea integrității fișierelor a eșuat\n  not_found: WebView2 Runtime nu a fost găsit\n  not_found_description: Seelen UI necesită Webview2 Runtime. Vă rugăm să o instalați.\n  outdated: WebView2 Runtime depășit\n  outdated_description: >-\n    Seelen UI necesită Webview2 Runtime %{min_version} sau mai mare. Vă rugăm să\n    o actualizați.\nservice:\n  not_running: Serviciul Seelen UI nu rulează\n  not_running_description: >-\n    Seelen UI Service (serviciul slu) este o dependență/ajutor pentru ca\n    aplicația să funcționeze corect.\n  not_running_ok: Începeți serviciul\nshortcut:\n  register:\n    placeholder: Apăsați orice combinație de taste\n    title: Înregistrarea unei noi comenzi rapide\nwidget_liveness:\n  failed_description: |-\n    Widgetul „%{widget_name}” nu mai răspunde de prea multe ori.\n\n    Puteți încerca să reporniți aplicația.\n  failed_title: Eroare widget\nwidget_permissions:\n  perm_open_file: deschide fișiere\n  perm_run: rulați programe\n  request_description: |-\n    Widgetul „%{widget_name}” solicită permisiunea %{command}.\n\n    Doriți să permiteți acest lucru?\n  request_title: Solicitare de permisiune pentru widgeturi\n"
  },
  {
    "path": "src/background/i18n/ru.yml",
    "content": "cancel: Отмена\ndone: Сделанный\nelevated:\n  description: >-\n    Seelen UI работает с правами администратора.\n\n    Это не добавляет и не улучшает какие-либо функции и может представлять\n    угрозу безопасности.\n\n    Для большей безопасности перезапустите его без прав администратора.\n  title: Запуск от имени администратора\nfile_explorer: Проводник файлов\nresource:\n  added: Добавлен новый ресурс\n  download_failed_body: >-\n    Что-то пошло не так при загрузке ресурса, пожалуйста, сообщите об этом\n    команде разработчиков Seelen.\n  download_failed_title: Загрузка ресурса не удалась\n  downloading: Загрузка активов...\n  downloading_body: Это может занять несколько секунд, пожалуйста, не закрывайте окно.\n  enable: Включить\nruntime:\n  corrupted_data: Поврежденные данные\n  corrupted_file: Файл конфигурации поврежден, вместо него используются значения по умолчанию.\n  corrupted_file_path: Поврежденный путь к файлу\n  download: Перейти на страницу загрузки\n  files_integrity: >-\n    Нам не удалось проверить целостность файлов приложения.\n\n    Это может произойти, если файлы были изменены, повреждены или установка не\n    завершена.\n\n    По соображениям безопасности работа Seelen UI не может продолжаться.\n\n    Пожалуйста, переустановите его с официального сайта.\n  files_integrity_title: \"Оповещение системы безопасности: не удалось проверить целостность файлов\"\n  not_found: Среда выполнения WebView2 не найдена\n  not_found_description: >-\n    Для пользовательского интерфейса Seelen требуется среда выполнения Webview2.\n    Пожалуйста, установите его.\n  outdated: Среда выполнения WebView2 устарела\n  outdated_description: >-\n    Для пользовательского интерфейса Seelen требуется среда выполнения Webview2\n    %{min_version} или выше. Пожалуйста, обновите его.\nservice:\n  not_running: Служба пользовательского интерфейса Seelen не работает\n  not_running_description: >-\n    Служба пользовательского интерфейса Seelen (slu-service) — это\n    зависимость/помощник для правильной работы приложения.\n  not_running_ok: Запустить службу\nshortcut:\n  register:\n    placeholder: Нажмите любую комбинацию клавиш\n    title: Зарегистрирование нового ярлыка\nwidget_liveness:\n  failed_description: |-\n    Виджет «%{widget_name}» слишком много раз переставал отвечать.\n\n    Вы можете попробовать перезапустить приложение.\n  failed_title: Ошибка виджета\nwidget_permissions:\n  perm_open_file: открывать файлы\n  perm_run: запускать программы\n  request_description: |-\n    Виджет «%{widget_name}» запрашивает разрешение на %{command}.\n\n    Вы хотите это разрешить?\n  request_title: Запрос разрешения виджета\n"
  },
  {
    "path": "src/background/i18n/si.yml",
    "content": "cancel: අවලංගු කරන්න\ndone: කළා\nelevated:\n  description: >-\n    Seelen UI පරිපාලක වරප්‍රසාද සහිතව ක්‍රියාත්මක වේ.\n\n    මෙය කිසිදු විශේෂාංගයක් එකතු කිරීම හෝ වැඩිදියුණු කිරීම සිදු නොකරන අතර, එය\n    ආරක්ෂක අවදානමක් ඇති කළ හැකිය.\n\n    වඩා හොඳ ආරක්ෂාවක් සඳහා, කරුණාකර පරිපාලක අවසරයකින් තොරව එය නැවත ආරම්භ කරන්න.\n  title: පරිපාලක ලෙස ක්රියාත්මක වේ\nfile_explorer: ගොනු ගවේෂකය\nresource:\n  added: නව සම්පතක් එකතු කර ඇත\n  download_failed_body: >-\n    සම්පත බාගත කිරීමේදී යම් දෙයක් වැරදී ඇති අතර කරුණාකර එය සීලෙන්ග් සංවර්ධක\n    කණ්ඩායමට වාර්තා කරන්න.\n  download_failed_title: සම්පත් බාගත කිරීම අසාර්ථක විය\n  downloading: වත්කම් බාගත කිරීම ...\n  downloading_body: මේ සඳහා තත්පර කිහිපයක් ගතවනු ඇත, කරුණාකර කවුළුව වසා නොගන්න.\n  enable: සක්රීය කරන්න\nruntime:\n  corrupted_data: දූෂිත දත්ත\n  corrupted_file: ඒ වෙනුවට පෙරනිමි අගයන් භාවිතා කරමින් වින්යාස ගොනුවක් දූෂිත වේ.\n  corrupted_file_path: දූෂිත ගොනු මාර්ගය\n  download: බාගැනීම් පිටුවට යන්න\n  files_integrity: |-\n    අපට යෙදුම් ගොනුවල අඛණ්ඩතාව සත්‍යාපනය කළ නොහැකි විය.\n    ගොනු වෙනස් කිරීම, දූෂිත වීම හෝ ස්ථාපනය අසම්පූර්ණ නම් මෙය සිදු විය හැක.\n    ආරක්ෂක හේතූන් මත, Seelen UI දිගටම කරගෙන යා නොහැක.\n    කරුණාකර එය නිල වෙබ් අඩවියෙන් නැවත ස්ථාපනය කරන්න.\n  files_integrity_title: ආරක්ෂක ඇඟවීම - ගොනු අඛණ්ඩතාව පරීක්ෂා කිරීම අසාර්ථක විය\n  not_found: WebView2 ධාවන කාලය හමු නොවීය\n  not_found_description: Seelen UI සඳහා Webview2 ධාවන කාලය අවශ්‍යයි. කරුණාකර එය ස්ථාපනය කරන්න.\n  outdated: WebView2 ධාවන කාලය යල් පැන ඇත\n  outdated_description: >-\n    Seelen UI සඳහා Webview2 ධාවන කාලය %{min_version} හෝ ඉහළ අවශ්‍ය වේ. කරුණාකර\n    එය යාවත්කාලීන කරන්න.\nservice:\n  not_running: Seelen UI සේවාව ක්‍රියාත්මක නොවේ\n  not_running_description: >-\n    Seelen UI සේවාව (slu-service) යනු යෙදුම නිසියාකාරව ක්‍රියා කිරීමට\n    යැපුම්/උපකාරකයකි.\n  not_running_ok: සේවාව ආරම්භ කරන්න\nshortcut:\n  register:\n    placeholder: ඕනෑම යතුරු සංයෝජනයක් ඔබන්න\n    title: නව කෙටිමඟ ලියාපදිංචි කිරීම\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' විජට්ටුව බොහෝ වාර ගණනක් ප්‍රතිචාර දැක්වීම නැවැත්වීය.\n\n    ඔබට යෙදුම නැවත ආරම්භ කිරීමට උත්සාහ කළ හැකිය.\n  failed_title: විජට් දෝෂය\nwidget_permissions:\n  perm_open_file: විවෘත ගොනු\n  perm_run: වැඩසටහන් ක්රියාත්මක කරන්න\n  request_description: |-\n    '%{widget_name}' විජට් එක %{command} වෙත අවසර ඉල්ලයි.\n\n    ඔබට මෙයට ඉඩ දීමට අවශ්‍යද?\n  request_title: විජට් අවසර ඉල්ලීම\n"
  },
  {
    "path": "src/background/i18n/sk.yml",
    "content": "cancel: Zrušiť\ndone: Vykonaný\nelevated:\n  description: >-\n    Používateľské rozhranie Seelen je spustené s oprávneniami správcu.\n\n    Toto nepridáva ani nevylepšuje žiadne funkcie a môže predstavovať\n    bezpečnostné riziko.\n\n    Pre lepšiu bezpečnosť ho reštartujte bez oprávnení správcu.\n  title: Beží ako správca\nfile_explorer: Prieskumník súborov\nresource:\n  added: Bol pridaný nový zdroj\n  download_failed_body: >-\n    Pri sťahovaní zdroja sa niečo pokazilo, nahláste to prosím vývojárskemu tímu\n    Seelen.\n  download_failed_title: Stiahnutie zdroja sa nepodarilo\n  downloading: Sťahovanie aktív...\n  downloading_body: Môže to trvať niekoľko sekúnd, nezatvárajte okno.\n  enable: Povolenie stránky\nruntime:\n  corrupted_data: Poškodené údaje\n  corrupted_file: >-\n    Konfiguračný súbor je poškodený a namiesto neho sa používajú predvolené\n    hodnoty.\n  corrupted_file_path: Poškodená cesta k súboru\n  download: Prejdite na stránku sťahovania\n  files_integrity: >-\n    Nepodarilo sa nám overiť integritu súborov aplikácie.\n\n    To sa môže stať, ak boli súbory upravené, poškodené alebo inštalácia nie je\n    dokončená.\n\n    Z bezpečnostných dôvodov nemôže používateľské rozhranie Seelen pokračovať.\n\n    Preinštalujte ho z oficiálnej webovej stránky.\n  files_integrity_title: Bezpečnostné upozornenie – kontrola integrity súborov zlyhala\n  not_found: WebView2 Runtime sa nenašlo\n  not_found_description: >-\n    Používateľské rozhranie Seelen vyžaduje Webview2 Runtime. Nainštalujte si\n    ho.\n  outdated: WebView2 Runtime je zastarané\n  outdated_description: >-\n    Používateľské rozhranie Seelen vyžaduje Webview2 Runtime %{min_version}\n    alebo vyššie. Aktualizujte ho.\nservice:\n  not_running: Služba používateľského rozhrania Seelen nie je spustená\n  not_running_description: >-\n    Seelen UI Service (slu-service) je závislosť/pomocník pre správne fungovanie\n    aplikácie.\n  not_running_ok: Spustite službu\nshortcut:\n  register:\n    placeholder: Stlačte ľubovoľnú kombináciu klávesov\n    title: Registrácia novej skratky\nwidget_liveness:\n  failed_description: |-\n    Miniaplikácia '%{widget_name}' prestala reagovať príliš veľakrát.\n\n    Môžete skúsiť reštartovať aplikáciu.\n  failed_title: Chyba miniaplikácie\nwidget_permissions:\n  perm_open_file: otvorené súbory\n  perm_run: spúšťať programy\n  request_description: |-\n    Miniaplikácia '%{widget_name}' požaduje povolenie pre %{command}.\n\n    Chcete to povoliť?\n  request_title: Žiadosť o povolenie miniaplikácie\n"
  },
  {
    "path": "src/background/i18n/so.yml",
    "content": "cancel: Burin\ndone: Sameeyey\nelevated:\n  description: >-\n    Seelen UI waxa ay la socotaa mudnaanta maamulaha\n\n    Tani ma soo kordhinayso ama ma wanaajinayso wax sifooyin ah, waxaana laga\n    yaabaa inay keento khatar amni.\n\n    Si loo helo nabad-gelyo wanaagsan, fadlan dib u billow la'aanteed maamulaha\n    fasaxa\n  title: U ordaya sidii Maamule\nfile_explorer: File Explorer\nresource:\n  added: Kheyraad Cusub ayaa lagu daray\n  download_failed_body: >-\n    Waxbaa khaldamay iyagoo soo dejinaya kheyraadka, fadlan u sheeg kooxda\n    horumarinta Selelen ee Selen.\n  download_failed_title: Soo-dejiyeyaasha Kheyraadka ayaa ku guuldareystay\n  downloading: Soo dejisashada hantida ...\n  downloading_body: >-\n    Tani waxay qaadan kartaa dhowr ilbidhiqsi, fadlan ha xiri ka dhigin\n    daaqadda.\n  enable: Karsiin\nruntime:\n  corrupted_data: Xogta musuqmaasuqa\n  corrupted_file: >-\n    Faylka qaabeynta ayaa la musuqmaasuqay, iyadoo la adeegsanayo qiimayaasha\n    caadiga ah halkii.\n  corrupted_file_path: Jidka faylka ee musuqmaasuqa\n  download: Tag bogga soo dejinta\n  files_integrity: >-\n    Waanu xaqiijin kari waynay daacadnimada faylasha codsiga\n\n    Tani waxay dhici kartaa haddii faylasha wax laga beddelay, la kharribmay,\n    ama rakibiddu aanay dhammaystirnayn.\n\n    Sababaha amniga, Seelen UI ma sii socon karo.\n\n    Fadlan dib uga soo dejiso degelka rasmiga ah\n  files_integrity_title: Digniin Amni - Hubinta daacadnimada faylalka waa guuldareystay\n  not_found: WebView2 Runtime lama helin\n  not_found_description: Seelen UI wuxuu u baahan yahay Webview2 Runtime. Fadlan rakib\n  outdated: WebView2 Runtime waa dhacay\n  outdated_description: >-\n    Seelen UI wuxuu u baahan yahay Webview2 Runtime %{min_version} ama ka\n    sareeya. Fadlan u cusboonaysii\nservice:\n  not_running: Seelen UI Service ma socdo\n  not_running_description: >-\n    Seelen UI Service (slu-service) waa ku-tiirsanaan/caawiye abka inuu si sax\n    ah u shaqeeyo.\n  not_running_ok: Bilow adeegga\nshortcut:\n  register:\n    placeholder: Riix wixii isku darka furaha ah\n    title: Diiwaangelinta gaagaaban cusub\nwidget_liveness:\n  failed_description: |-\n    Qalabka '%{widget_name}' wuxuu joojiyay jawaab celinta marar badan.\n\n    Waxaad isku dayi kartaa inaad dib u bilowdo abka.\n  failed_title: Khaladka Widget\nwidget_permissions:\n  perm_open_file: faylasha furan\n  perm_run: socodsii barnaamijyada\n  request_description: |-\n    Qalabka '%{widget_name}' wuxuu codsanayaa ogolaanshaha %{command}.\n\n    Ma doonaysaa inaad tan ogolaato?\n  request_title: Codsiga Ogolaanshaha Widget\n"
  },
  {
    "path": "src/background/i18n/sr.yml",
    "content": "cancel: Отказати\ndone: Доношен\nelevated:\n  description: >-\n    Сеелен УИ ради са администраторским привилегијама.\n\n    Ово не додаје нити побољшава ниједну функцију и може представљати\n    безбедносни ризик.\n\n    Ради боље безбедности, поново га покрените без администраторских дозвола.\n  title: Покреће се као администратор\nfile_explorer: Филе Екплорер\nresource:\n  added: Додат је нови ресурс\n  download_failed_body: >-\n    Нешто је пошло по злу док преузимате ресурс, молим вас да га пријавите у\n    тиму за програмере СЕЕЛЕН-а.\n  download_failed_title: Преузимање ресурса није успело\n  downloading: Преузимање имовине ...\n  downloading_body: Ово би могло потрајати неколико секунди, молим вас, не затворите прозор.\n  enable: Омогућити\nruntime:\n  corrupted_data: Оштећени подаци\n  corrupted_file: Конфигурациона датотека је оштећена, користећи задане вредности уместо тога.\n  corrupted_file_path: Оштећени пут датотека\n  download: Идите на страницу за преузимање\n  files_integrity: >-\n    Нисмо могли да проверимо интегритет датотека апликације.\n\n    Ово се може десити ако су датотеке измењене, оштећене или инсталација није\n    довршена.\n\n    Из безбедносних разлога, Сеелен УИ не може да настави.\n\n    Поново га инсталирајте са званичне веб странице.\n  files_integrity_title: Безбедносно упозорење – провера интегритета датотека није успела\n  not_found: ВебВиев2 Рунтиме није пронађено\n  not_found_description: Сеелен УИ захтева Вебвиев2 Рунтиме. Молимо вас да га инсталирате.\n  outdated: ВебВиев2 Рунтиме је застарео\n  outdated_description: Сеелен УИ захтева Вебвиев2 Рунтиме %{мин_версион} или новију. Ажурирајте га.\nservice:\n  not_running: Сеелен УИ Сервице не ради\n  not_running_description: >-\n    Сеелен УИ Сервице (слу-сервис) је зависност/помоћник за правилно\n    функционисање апликације.\n  not_running_ok: Започните услугу\nshortcut:\n  register:\n    placeholder: Притисните било коју комбинацију тастера\n    title: Регистровање нове пречице\nwidget_liveness:\n  failed_description: |-\n    Виџет „%{видгет_наме}“ је престао да одговара превише пута.\n\n    Можете покушати да поново покренете апликацију.\n  failed_title: Грешка у виџету\nwidget_permissions:\n  perm_open_file: отворите датотеке\n  perm_run: покренути програме\n  request_description: |-\n    Виџет '%{видгет_наме}' захтева дозволу за %{цомманд}.\n\n    Да ли желите да дозволите ово?\n  request_title: Захтев за дозволу за виџет\n"
  },
  {
    "path": "src/background/i18n/sv.yml",
    "content": "cancel: Avboka\ndone: Gjort\nelevated:\n  description: >-\n    Seelen UI körs med administratörsbehörighet.\n\n    Detta lägger inte till eller förbättrar några funktioner, och det kan utgöra\n    en säkerhetsrisk.\n\n    För bättre säkerhet, starta om den utan administratörsbehörighet.\n  title: Kör som administratör\nfile_explorer: Filutforskaren\nresource:\n  added: En ny resurs har lagts till\n  download_failed_body: >-\n    Något gick fel när du laddade ner resursen, vänligen rapportera det till\n    Seelen-utvecklarteamet.\n  download_failed_title: Nedladdning av resurs misslyckades\n  downloading: Nedladdning av tillgångar...\n  downloading_body: Det kan ta några sekunder, men stäng inte fönstret.\n  enable: Aktivera\nruntime:\n  corrupted_data: Korrupta data\n  corrupted_file: En konfigurationsfil är skadad och standardvärden används i stället.\n  corrupted_file_path: Felaktig filsökväg\n  download: Gå till nedladdningssidan\n  files_integrity: >-\n    Vi kunde inte verifiera programfilernas integritet.\n\n    Detta kan hända om filer har ändrats, skadats eller installationen är\n    ofullständig.\n\n    Av säkerhetsskäl kan Seelen UI inte fortsätta.\n\n    Installera om det från den officiella webbplatsen.\n  files_integrity_title: Säkerhetsvarning - Filintegritetskontrollen misslyckades\n  not_found: WebView2 Runtime hittades inte\n  not_found_description: Seelen UI kräver Webview2 Runtime. Installera den.\n  outdated: WebView2 Runtime föråldrad\n  outdated_description: Seelen UI kräver Webview2 Runtime %{min_version} eller högre. Uppdatera den.\nservice:\n  not_running: Seelen UI Service körs inte\n  not_running_description: >-\n    Seelen UI Service (slu-service) är ett beroende/hjälpare för att appen ska\n    fungera korrekt.\n  not_running_ok: Starta service\nshortcut:\n  register:\n    placeholder: Tryck på alla tangentkombinationer\n    title: Registrera ny genväg\nwidget_liveness:\n  failed_description: |-\n    Widgeten '%{widget_name}' slutade svara för många gånger.\n\n    Du kan prova att starta om appen.\n  failed_title: Widgetfel\nwidget_permissions:\n  perm_open_file: öppna filer\n  perm_run: köra program\n  request_description: |-\n    Widgeten '%{widget_name}' begär tillstånd till %{command}.\n\n    Vill du tillåta detta?\n  request_title: Begäran om widgettillstånd\n"
  },
  {
    "path": "src/background/i18n/sw.yml",
    "content": "cancel: Ghairi\ndone: Imekamilika\nelevated:\n  description: >-\n    Seelen UI inaendeshwa na haki za msimamizi.\n\n    Hii haiongezi au kuboresha vipengele vyovyote, na inaweza kuleta hatari ya\n    usalama.\n\n    Kwa usalama bora, tafadhali iwashe upya bila ruhusa za msimamizi.\n  title: Kuendesha kama Msimamizi\nfile_explorer: Kichunguzi cha Faili\nresource:\n  added: Rasilimali mpya imeongezwa\n  download_failed_body: >-\n    Kitu kilienda vibaya wakati wa kupakua rasilimali, tafadhali ripoti kwa timu\n    ya wasanidi programu wa Seelen.\n  download_failed_title: Upakuaji wa rasilimali umeshindwa\n  downloading: Kupakua mali ...\n  downloading_body: Hii inaweza kuchukua sekunde chache, tafadhali usifunge dirisha.\n  enable: Wezesha\nruntime:\n  corrupted_data: Data iliyoharibiwa\n  corrupted_file: Faili ya usanidi imeharibiwa, kwa kutumia maadili ya msingi badala yake.\n  corrupted_file_path: Njia ya faili iliyoharibiwa\n  download: Nenda kwenye ukurasa wa kupakua\n  files_integrity: >-\n    Hatukuweza kuthibitisha uadilifu wa faili za programu.\n\n    Hii inaweza kutokea ikiwa faili zilirekebishwa, kupotoshwa, au usakinishaji\n    haujakamilika.\n\n    Kwa sababu za usalama, Seelen UI haiwezi kuendelea.\n\n    Tafadhali isakinishe upya kutoka kwa tovuti rasmi.\n  files_integrity_title: Tahadhari ya Usalama - Ukaguzi wa Uadilifu wa Faili Umeshindwa\n  not_found: Muda wa utekelezaji wa WebView2 haujapatikana\n  not_found_description: Seelen UI inahitaji Webview2 Runtime. Tafadhali isakinishe.\n  outdated: WebView2 Runtime imepitwa na wakati\n  outdated_description: >-\n    Seelen UI inahitaji Webview2 Runtime %{min_version} au toleo jipya zaidi.\n    Tafadhali isasishe.\nservice:\n  not_running: Huduma ya Seelen UI haifanyi kazi\n  not_running_description: >-\n    Seelen UI Service (slu-service) ni tegemezi/msaidizi kwa programu kufanya\n    kazi vizuri.\n  not_running_ok: Anza huduma\nshortcut:\n  register:\n    placeholder: Bonyeza mchanganyiko wowote muhimu\n    title: Kusajili njia ya mkato mpya\nwidget_liveness:\n  failed_description: |-\n    Wijeti '%{widget_name}' iliacha kujibu mara nyingi sana.\n\n    Unaweza kujaribu kuanzisha upya programu.\n  failed_title: Hitilafu ya Wijeti\nwidget_permissions:\n  perm_open_file: fungua faili\n  perm_run: endesha programu\n  request_description: |-\n    Wijeti '%{widget_name}' inaomba ruhusa kwa %{command}.\n\n    Je, ungependa kuruhusu hili?\n  request_title: Ombi la Ruhusa ya Wijeti\n"
  },
  {
    "path": "src/background/i18n/ta.yml",
    "content": "cancel: ரத்துசெய்\ndone: முடிந்தது\nelevated:\n  description: >-\n    சீலன் UI நிர்வாகி சிறப்புரிமைகளுடன் இயங்குகிறது.\n\n    இது எந்த அம்சங்களையும் சேர்க்காது அல்லது மேம்படுத்தாது, மேலும் இது\n    பாதுகாப்பு ஆபத்தை ஏற்படுத்தலாம்.\n\n    சிறந்த பாதுகாப்பிற்காக, நிர்வாகி அனுமதியின்றி மீண்டும் தொடங்கவும்.\n  title: நிர்வாகியாக இயங்குகிறது\nfile_explorer: கோப்பு எக்ஸ்ப்ளோரர்\nresource:\n  added: ஒரு புதிய ஆதாரம் சேர்க்கப்பட்டுள்ளது\n  download_failed_body: >-\n    வளத்தைப் பதிவிறக்கும் போது ஏதோ தவறு ஏற்பட்டது, தயவுசெய்து அதை சீலன்\n    டெவலப்பர் குழுவிடம் புகாரளிக்கவும்.\n  download_failed_title: வள பதிவிறக்கம் தோல்வியடைந்தது\n  downloading: சொத்துக்களைப் பதிவிறக்குவது ...\n  downloading_body: இதற்கு சில வினாடிகள் ஆகலாம், தயவுசெய்து சாளரத்தை மூட வேண்டாம்.\n  enable: இயக்கு\nruntime:\n  corrupted_data: சிதைந்த தரவு\n  corrupted_file: >-\n    அதற்கு பதிலாக இயல்புநிலை மதிப்புகளைப் பயன்படுத்தி ஒரு உள்ளமைவு கோப்பு\n    சிதைந்துள்ளது.\n  corrupted_file_path: சிதைந்த கோப்பு பாதை\n  download: பதிவிறக்கப் பக்கத்திற்குச் செல்லவும்\n  files_integrity: >-\n    பயன்பாட்டுக் கோப்புகளின் நேர்மையை எங்களால் சரிபார்க்க முடியவில்லை.\n\n    கோப்புகள் மாற்றப்பட்டாலோ, சிதைக்கப்பட்டாலோ அல்லது நிறுவல் முழுமையடையாமல்\n    இருந்தாலோ இது நிகழலாம்.\n\n    பாதுகாப்பு காரணங்களுக்காக, சீலன் UI தொடர முடியாது.\n\n    அதிகாரப்பூர்வ இணையதளத்தில் இருந்து அதை மீண்டும் நிறுவவும்.\n  files_integrity_title: பாதுகாப்பு எச்சரிக்கை - கோப்புகளின் ஒருமைப்பாடு சோதனை தோல்வியடைந்தது\n  not_found: WebView2 இயக்க நேரம் கிடைக்கவில்லை\n  not_found_description: Seelen UIக்கு Webview2 இயக்க நேரம் தேவை. தயவுசெய்து அதை நிறுவவும்.\n  outdated: WebView2 இயக்க நேரம் காலாவதியானது\n  outdated_description: >-\n    Seelen UI க்கு Webview2 இயக்க நேரம் %{min_version} அல்லது அதற்கு மேல் தேவை.\n    தயவுசெய்து அதைப் புதுப்பிக்கவும்.\nservice:\n  not_running: சீலன் UI சேவை இயங்கவில்லை\n  not_running_description: >-\n    சீலன் UI சேவை (slu-service) என்பது ஆப்ஸ் சரியாக வேலை செய்வதற்கான சார்பு/உதவி\n    ஆகும்.\n  not_running_ok: சேவையைத் தொடங்கவும்\nshortcut:\n  register:\n    placeholder: எந்த முக்கிய கலவையையும் அழுத்தவும்\n    title: புதிய குறுக்குவழியை பதிவு செய்தல்\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' விட்ஜெட் பலமுறை பதிலளிப்பதை நிறுத்தியது.\n\n    நீங்கள் பயன்பாட்டை மறுதொடக்கம் செய்ய முயற்சி செய்யலாம்.\n  failed_title: விட்ஜெட் பிழை\nwidget_permissions:\n  perm_open_file: கோப்புகளைத் திறக்கவும்\n  perm_run: நிரல்களை இயக்கவும்\n  request_description: |-\n    விட்ஜெட் '%{widget_name}' %{command}க்கு அனுமதி கோருகிறது.\n\n    இதை அனுமதிக்க விரும்புகிறீர்களா?\n  request_title: விட்ஜெட் அனுமதி கோரிக்கை\n"
  },
  {
    "path": "src/background/i18n/te.yml",
    "content": "cancel: రద్దు చేయండి\ndone: పూర్తయింది\nelevated:\n  description: >-\n    సీలెన్ UI అడ్మినిస్ట్రేటర్ అధికారాలతో అమలవుతోంది.\n\n    ఇది ఏ లక్షణాలను జోడించదు లేదా మెరుగుపరచదు మరియు ఇది భద్రతా ప్రమాదాన్ని\n    కలిగిస్తుంది.\n\n    మెరుగైన భద్రత కోసం, దయచేసి నిర్వాహక అనుమతులు లేకుండా దీన్ని\n    పునఃప్రారంభించండి.\n  title: అడ్మినిస్ట్రేటర్‌గా రన్ అవుతున్నారు\nfile_explorer: ఫైల్ ఎక్స్‌ప్లోరర్\nresource:\n  added: కొత్త వనరు జోడించబడింది\n  download_failed_body: >-\n    వనరును డౌన్‌లోడ్ చేసేటప్పుడు ఏదో తప్పు జరిగింది, దయచేసి దీన్ని సీలెన్\n    డెవలపర్ బృందానికి నివేదించండి.\n  download_failed_title: వనరుల డౌన్‌లోడ్ విఫలమైంది\n  downloading: ఆస్తులను డౌన్‌లోడ్ చేస్తోంది ...\n  downloading_body: దీనికి కొన్ని సెకన్లు పట్టవచ్చు, దయచేసి విండోను మూసివేయవద్దు.\n  enable: ఎనేబుల్\nruntime:\n  corrupted_data: పాడైన డేటా\n  corrupted_file: బదులుగా డిఫాల్ట్ విలువలను ఉపయోగించి కాన్ఫిగరేషన్ ఫైల్ పాడైంది.\n  corrupted_file_path: పాడైన ఫైల్ మార్గం\n  download: డౌన్‌లోడ్ పేజీకి వెళ్లండి\n  files_integrity: >-\n    మేము అప్లికేషన్ ఫైల్‌ల సమగ్రతను ధృవీకరించలేకపోయాము.\n\n    ఫైల్‌లు సవరించబడినా, పాడైపోయినా లేదా ఇన్‌స్టాలేషన్ అసంపూర్ణమైనా ఇది\n    జరగవచ్చు.\n\n    భద్రతా కారణాల దృష్ట్యా, సీలెన్ UI కొనసాగదు.\n\n    దయచేసి అధికారిక వెబ్‌సైట్ నుండి దీన్ని మళ్లీ ఇన్‌స్టాల్ చేయండి.\n  files_integrity_title: భద్రతా హెచ్చరిక - ఫైల్‌ల సమగ్రత తనిఖీ విఫలమైంది\n  not_found: WebView2 రన్‌టైమ్ కనుగొనబడలేదు\n  not_found_description: Seelen UIకి Webview2 రన్‌టైమ్ అవసరం. దయచేసి దీన్ని ఇన్‌స్టాల్ చేయండి.\n  outdated: WebView2 రన్‌టైమ్ పాతది\n  outdated_description: >-\n    Seelen UIకి Webview2 రన్‌టైమ్ %{min_version} లేదా అంతకంటే ఎక్కువ అవసరం.\n    దయచేసి దీన్ని నవీకరించండి.\nservice:\n  not_running: సీలెన్ UI సర్వీస్ అమలులో లేదు\n  not_running_description: >-\n    సీలెన్ UI సర్వీస్ (slu-service) అనేది యాప్ సరిగ్గా పని చేయడానికి\n    డిపెండెన్సీ/సహాయకం.\n  not_running_ok: సేవ ప్రారంభించండి\nshortcut:\n  register:\n    placeholder: ఏదైనా కీ కలయిక నొక్కండి\n    title: కొత్త సత్వరమార్గాన్ని నమోదు చేస్తోంది\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' విడ్జెట్ చాలా సార్లు ప్రతిస్పందించడం ఆగిపోయింది.\n\n    మీరు యాప్‌ని పునఃప్రారంభించి ప్రయత్నించవచ్చు.\n  failed_title: విడ్జెట్ లోపం\nwidget_permissions:\n  perm_open_file: ఫైళ్లను తెరవండి\n  perm_run: కార్యక్రమాలను అమలు చేయండి\n  request_description: |-\n    '%{widget_name}' విడ్జెట్ %{command}కి అనుమతిని అభ్యర్థిస్తోంది.\n\n    మీరు దీన్ని అనుమతించాలనుకుంటున్నారా?\n  request_title: విడ్జెట్ అనుమతి అభ్యర్థన\n"
  },
  {
    "path": "src/background/i18n/tg.yml",
    "content": "cancel: Манъ кардан\ndone: Анҷом\nelevated:\n  description: >-\n    Seelen UI бо имтиёзҳои администратор кор мекунад.\n\n    Ин ягон хусусиятро илова ё такмил намедиҳад ва он метавонад ба амният хатар\n    эҷод кунад.\n\n    Барои бехатарии беҳтар, лутфан онро бе иҷозати администратор бозоғоз намоед.\n  title: Ҳамчун администратор иҷро мешавад\nfile_explorer: Explorer File\nresource:\n  added: Як манбаи нав илова карда шуд\n  download_failed_body: >-\n    Ҳангоми зеркашии манбаъ хато рафт, лутфан онро ба дастаи таҳиякунандаи\n    SELEDENTER хабар диҳед.\n  download_failed_title: Захира зеркашӣ ноком шуд\n  downloading: Зеркашии дороиҳо ...\n  downloading_body: Ин метавонад якчанд сонияро талаб кунад, лутфан тирезаро пӯшед.\n  enable: Имконият додан\nruntime:\n  corrupted_data: Маълумоти вайроншуда\n  corrupted_file: Файли танзимот вайрон шудааст, бо истифода аз арзишҳои пешфарз ба ҷои.\n  corrupted_file_path: Роҳи фасли файл\n  download: Ба саҳифаи зеркашӣ гузаред\n  files_integrity: >-\n    Мо натавонистем якпорчагии файлҳои барномаро тафтиш кунем.\n\n    Ин метавонад рӯй диҳад, агар файлҳо тағир дода шаванд, вайрон карда шаванд ё\n    насбкунӣ нопурра бошад.\n\n    Бо сабабҳои амниятӣ, Seelen UI наметавонад идома ёбад.\n\n    Лутфан онро аз вебсайти расмӣ аз нав насб кунед.\n  files_integrity_title: Огоҳии амниятӣ - Санҷиши якпорчагии файлҳо ноком шуд\n  not_found: Вақти иҷрои WebView2 ёфт нашуд\n  not_found_description: Seelen UI вақти Webview2-ро талаб мекунад. Лутфан онро насб кунед.\n  outdated: WebView2 Runtime кӯҳна шудааст\n  outdated_description: >-\n    UI Seelen Webview2 Runtime %{min_version} ё навтарро талаб мекунад. Лутфан\n    онро навсозӣ кунед.\nservice:\n  not_running: Seelen UI Service кор намекунад\n  not_running_description: >-\n    Seelen UI Service (slu-service) як вобастагӣ/ёрирасон барои дуруст кор\n    кардани барнома мебошад.\n  not_running_ok: Хидматро оғоз кунед\nshortcut:\n  register:\n    placeholder: Ягон комбинатсияи тугмаро пахш кунед\n    title: Сабти миёнабурҳои нав\nwidget_liveness:\n  failed_description: |-\n    Виҷети '%{widget_name}' чанд маротиба посух надод.\n\n    Шумо метавонед кӯшиш кунед, ки барномаро бозоғоз намоед.\n  failed_title: Хатогии виджет\nwidget_permissions:\n  perm_open_file: файлҳоро кушоед\n  perm_run: барномаҳоро иҷро кунед\n  request_description: |-\n    Виҷети '%{widget_name}' барои %{command} иҷозат талаб мекунад.\n\n    Оё шумо мехоҳед ба ин иҷозат диҳед?\n  request_title: Дархости иҷозати виджет\n"
  },
  {
    "path": "src/background/i18n/th.yml",
    "content": "cancel: ยกเลิก\ndone: เสร็จแล้ว\nelevated:\n  description: >-\n    Seelen UI กำลังทำงานด้วยสิทธิ์ของผู้ดูแลระบบ\n\n    การดำเนินการนี้ไม่ได้เพิ่มหรือปรับปรุงคุณสมบัติใดๆ\n    และอาจก่อให้เกิดความเสี่ยงด้านความปลอดภัย\n\n    เพื่อความปลอดภัยที่ดีขึ้น โปรดรีสตาร์ทโดยไม่มีสิทธิ์ของผู้ดูแลระบบ\n  title: ทำงานเป็นผู้ดูแลระบบ\nfile_explorer: ไฟล์เอ็กซ์พลอเรอร์\nresource:\n  added: มีการเพิ่มทรัพยากรใหม่\n  download_failed_body: มีบางอย่างผิดปกติในขณะที่ดาวน์โหลดทรัพยากรโปรดรายงานไปยังทีมนักพัฒนา Seelen\n  download_failed_title: การดาวน์โหลดทรัพยากรล้มเหลว\n  downloading: ดาวน์โหลดสินทรัพย์ ...\n  downloading_body: อาจใช้เวลาสองสามวินาทีโปรดอย่าปิดหน้าต่าง\n  enable: เปิดใช้งาน\nruntime:\n  corrupted_data: ข้อมูลที่เสียหาย\n  corrupted_file: ไฟล์การกำหนดค่าเสียหายโดยใช้ค่าเริ่มต้นแทน\n  corrupted_file_path: เส้นทางไฟล์ที่เสียหาย\n  download: ไปที่หน้าดาวน์โหลด\n  files_integrity: |-\n    เราไม่สามารถตรวจสอบความสมบูรณ์ของไฟล์แอปพลิเคชันได้\n    กรณีนี้อาจเกิดขึ้นได้หากไฟล์ได้รับการแก้ไข เสียหาย หรือการติดตั้งไม่สมบูรณ์\n    ด้วยเหตุผลด้านความปลอดภัย Seelen UI ไม่สามารถดำเนินการต่อได้\n    โปรดติดตั้งใหม่จากเว็บไซต์อย่างเป็นทางการ\n  files_integrity_title: การแจ้งเตือนความปลอดภัย - การตรวจสอบความสมบูรณ์ของไฟล์ล้มเหลว\n  not_found: ไม่พบรันไทม์ WebView2\n  not_found_description: Seelen UI ต้องการรันไทม์ Webview2 กรุณาติดตั้ง.\n  outdated: รันไทม์ WebView2 ล้าสมัย\n  outdated_description: Seelen UI ต้องใช้รันไทม์ Webview2 %{min_version} หรือสูงกว่า กรุณาอัปเดตมัน.\nservice:\n  not_running: บริการ Seelen UI ไม่ทำงาน\n  not_running_description: >-\n    Seelen UI Service (slu-service)\n    เป็นตัวพึ่งพา/ตัวช่วยเพื่อให้แอปทำงานได้อย่างถูกต้อง\n  not_running_ok: เริ่มให้บริการ\nshortcut:\n  register:\n    placeholder: กดชุดค่าผสมคีย์ใด ๆ\n    title: การลงทะเบียนทางลัดใหม่\nwidget_liveness:\n  failed_description: |-\n    วิดเจ็ต '%{widget_name}' หยุดตอบสนองหลายครั้งเกินไป\n\n    คุณสามารถลองรีสตาร์ทแอปได้\n  failed_title: ข้อผิดพลาดของวิดเจ็ต\nwidget_permissions:\n  perm_open_file: เปิดไฟล์\n  perm_run: รันโปรแกรม\n  request_description: |-\n    วิดเจ็ต '%{widget_name}' กำลังขออนุญาตไปยัง %{command}\n\n    คุณต้องการอนุญาตสิ่งนี้หรือไม่?\n  request_title: คำขออนุญาตวิดเจ็ต\n"
  },
  {
    "path": "src/background/i18n/tl.yml",
    "content": "cancel: Kanselahin\ndone: Tapos na\nelevated:\n  description: >-\n    Gumagana ang Seelen UI na may mga pribilehiyo ng administrator.\n\n    Hindi ito nagdaragdag o nagpapahusay ng anumang mga tampok, at maaari itong\n    magdulot ng panganib sa seguridad.\n\n    Para sa mas mahusay na seguridad, mangyaring i-restart ito nang walang mga\n    pahintulot ng administrator.\n  title: Tumatakbo bilang Administrator\nfile_explorer: File Explorer\nresource:\n  added: Ang isang bagong mapagkukunan ay naidagdag\n  download_failed_body: >-\n    May mali habang nag -download ng mapagkukunan, mangyaring iulat ito sa\n    koponan ng developer ng Seelen.\n  download_failed_title: Nabigo ang pag -download ng mapagkukunan\n  downloading: Pag -download ng mga assets ...\n  downloading_body: Maaaring tumagal ito ng ilang segundo, mangyaring huwag isara ang window.\n  enable: Paganahin\nruntime:\n  corrupted_data: Nasira data\n  corrupted_file: >-\n    Ang isang file ng pagsasaayos ay nasira, gamit ang mga default na halaga sa\n    halip.\n  corrupted_file_path: Nasira na landas ng file\n  download: Pumunta sa pahina ng pag-download\n  files_integrity: >-\n    Hindi namin ma-verify ang integridad ng mga file ng application.\n\n    Ito ay maaaring mangyari kung ang mga file ay binago, nasira, o ang\n    pag-install ay hindi kumpleto.\n\n    Para sa mga kadahilanang panseguridad, hindi maaaring magpatuloy ang Seelen\n    UI.\n\n    Mangyaring muling i-install ito mula sa opisyal na website.\n  files_integrity_title: Alerto sa Seguridad - Nabigo ang Pagsusuri ng Integridad ng mga File\n  not_found: Hindi nahanap ang WebView2 Runtime\n  not_found_description: >-\n    Ang Seelen UI ay nangangailangan ng Webview2 Runtime. Mangyaring i-install\n    ito.\n  outdated: Luma na ang Runtime ng WebView2\n  outdated_description: >-\n    Ang Seelen UI ay nangangailangan ng Webview2 Runtime %{min_version} o mas\n    mataas. Mangyaring i-update ito.\nservice:\n  not_running: Hindi tumatakbo ang Seelen UI Service\n  not_running_description: >-\n    Ang Seelen UI Service (slu-service) ay isang dependency/helper para gumana\n    nang maayos ang app.\n  not_running_ok: Simulan ang serbisyo\nshortcut:\n  register:\n    placeholder: Pindutin ang anumang key na kumbinasyon\n    title: Pagrehistro ng Bagong Shortcut\nwidget_liveness:\n  failed_description: >-\n    Ang widget na '%{widget_name}' ay huminto sa pagtugon ng masyadong maraming\n    beses.\n\n\n    Maaari mong subukang i-restart ang app.\n  failed_title: Error sa Widget\nwidget_permissions:\n  perm_open_file: buksan ang mga file\n  perm_run: magpatakbo ng mga programa\n  request_description: |-\n    Ang widget na '%{widget_name}' ay humihiling ng pahintulot na %{command}.\n\n    Gusto mo bang payagan ito?\n  request_title: Kahilingan ng Pahintulot sa Widget\n"
  },
  {
    "path": "src/background/i18n/tr.yml",
    "content": "cancel: İptal etmek\ndone: Tamamlamak\nelevated:\n  description: >-\n    Seelen UI yönetici ayrıcalıklarıyla çalışıyor.\n\n    Bu, herhangi bir özelliği eklemez veya iyileştirmez ve güvenlik riski\n    oluşturabilir.\n\n    Daha iyi güvenlik için lütfen yönetici izinleri olmadan yeniden başlatın.\n  title: Yönetici olarak çalıştırma\nfile_explorer: Dosya Gezgini\nresource:\n  added: Yeni bir kaynak eklendi\n  download_failed_body: >-\n    Kaynak indirilirken bir şeyler ters gitti, lütfen bunu Seelen geliştirici\n    ekibine bildirin.\n  download_failed_title: Kaynak indirme başarısız oldu\n  downloading: Varlıklar indiriliyor...\n  downloading_body: Bu işlem birkaç saniye sürebilir, lütfen pencereyi kapatmayın.\n  enable: Etkinleştir\nruntime:\n  corrupted_data: Bozuk veriler\n  corrupted_file: >-\n    Bir yapılandırma dosyası bozulmuş, bunun yerine varsayılan değerler\n    kullanılıyor.\n  corrupted_file_path: Bozuk dosya yolu\n  download: İndirme sayfasına git\n  files_integrity: >-\n    Uygulama dosyalarının bütünlüğünü doğrulayamadık.\n\n    Dosyalar değiştirilmiş, bozulmuşsa veya yükleme tamamlanmamışsa bu durum\n    meydana gelebilir.\n\n    Güvenlik nedeniyle Seelen kullanıcı arayüzü devam edemiyor.\n\n    Lütfen resmi web sitesinden yeniden yükleyin.\n  files_integrity_title: Güvenlik Uyarısı - Dosya Bütünlüğü Denetimi Başarısız\n  not_found: WebView2 Çalışma Zamanı bulunamadı\n  not_found_description: >-\n    Seelen kullanıcı arayüzü Webview2 Çalışma Zamanı gerektirir. Lütfen\n    yükleyin.\n  outdated: WebView2 Çalışma Zamanı güncel değil\n  outdated_description: >-\n    Seelen kullanıcı arayüzü, Webview2 Çalışma Zamanı %{min_version} veya üstünü\n    gerektirir. Lütfen güncelleyin.\nservice:\n  not_running: Seelen UI Hizmeti çalışmıyor\n  not_running_description: >-\n    Seelen UI Hizmeti (slu-service), uygulamanın düzgün çalışması için bir\n    bağımlılık/yardımcıdır.\n  not_running_ok: Hizmeti başlat\nshortcut:\n  register:\n    placeholder: Herhangi bir tuş kombinasyonuna basın\n    title: Yeni kısayol kaydetme\nwidget_liveness:\n  failed_description: |-\n    '%{widget_name}' widget'ı birçok kez yanıt vermeyi durdurdu.\n\n    Uygulamayı yeniden başlatmayı deneyebilirsiniz.\n  failed_title: Widget Hatası\nwidget_permissions:\n  perm_open_file: dosyaları aç\n  perm_run: programları çalıştır\n  request_description: |-\n    '%{widget_name}' widget'ı %{command} için izin istiyor.\n\n    Buna izin vermek istiyor musun?\n  request_title: Widget İzin Talebi\n"
  },
  {
    "path": "src/background/i18n/uk.yml",
    "content": "cancel: Скасувати\ndone: Виконаний\nelevated:\n  description: |-\n    Seelen UI працює з правами адміністратора.\n    Це не додає та не покращує жодних функцій і може становити загрозу безпеці.\n    Для кращої безпеки перезапустіть його без прав адміністратора.\n  title: Запуск від імені адміністратора\nfile_explorer: Провідник файлів\nresource:\n  added: Додано новий ресурс\n  download_failed_body: >-\n    Під час завантаження ресурсу сталася помилка, будь ласка, повідомте про неї\n    команді розробників Seelen.\n  download_failed_title: Не вдалося завантажити ресурс\n  downloading: Завантаження активів...\n  downloading_body: Це може зайняти кілька секунд, будь ласка, не закривайте вікно.\n  enable: Увімкнути\nruntime:\n  corrupted_data: Пошкоджені дані\n  corrupted_file: >-\n    Файл конфігурації пошкоджено, замість нього використовуються значення за\n    замовчуванням.\n  corrupted_file_path: Неправильний шлях до файлу\n  download: Перейдіть на сторінку завантаження\n  files_integrity: >-\n    Нам не вдалося перевірити цілісність файлів програми.\n\n    Це може статися, якщо файли були змінені, пошкоджені або інсталяція не\n    завершена.\n\n    З міркувань безпеки Seelen UI не може продовжувати роботу.\n\n    Перевстановіть його з офіційного сайту.\n  files_integrity_title: Сповіщення системи безпеки – не вдалося перевірити цілісність файлів\n  not_found: WebView2 Runtime не знайдено\n  not_found_description: >-\n    Інтерфейс користувача Seelen потребує Webview2 Runtime. Будь ласка,\n    встановіть його.\n  outdated: WebView2 Runtime застаріла\n  outdated_description: >-\n    Інтерфейс користувача Seelen вимагає Webview2 Runtime %{min_version} або\n    новішої версії. Будь ласка, оновіть його.\nservice:\n  not_running: Служба інтерфейсу користувача Seelen не працює\n  not_running_description: >-\n    Seelen UI Service (slu-service) — це залежність/помічник для належної роботи\n    програми.\n  not_running_ok: Запустити службу\nshortcut:\n  register:\n    placeholder: Натисніть будь -яку комбінацію клавіш\n    title: Реєстрація нового ярлика\nwidget_liveness:\n  failed_description: |-\n    Віджет \"%{widget_name}\" переставав відповідати занадто багато разів.\n\n    Ви можете спробувати перезапустити програму.\n  failed_title: Помилка віджета\nwidget_permissions:\n  perm_open_file: відкритих файлів\n  perm_run: запускати програми\n  request_description: |-\n    Віджет «%{widget_name}» запитує дозвіл на %{command}.\n\n    Ви хочете це дозволити?\n  request_title: Запит на дозвіл віджета\n"
  },
  {
    "path": "src/background/i18n/ur.yml",
    "content": "cancel: منسوخ کریں\ndone: کیا\nelevated:\n  description: >-\n    Seelen UI منتظم کے استحقاق کے ساتھ چل رہا ہے۔\n\n    یہ کسی بھی خصوصیت کو شامل یا بہتر نہیں کرتا ہے، اور یہ سیکورٹی کے خطرے کا\n    باعث بن سکتا ہے۔\n\n    بہتر سیکیورٹی کے لیے، براہ کرم اسے منتظم کی اجازت کے بغیر دوبارہ شروع کریں۔\n  title: ایڈمنسٹریٹر کے طور پر چل رہا ہے۔\nfile_explorer: فائل ایکسپلورر\nresource:\n  added: ایک نیا وسیلہ شامل کیا گیا ہے\n  download_failed_body: >-\n    وسائل کو ڈاؤن لوڈ کرتے وقت کچھ غلط ہو گیا ، براہ کرم اسے سیلن ڈویلپر ٹیم کو\n    رپورٹ کریں۔\n  download_failed_title: وسائل ڈاؤن لوڈ ناکام\n  downloading: اثاثوں کو ڈاؤن لوڈ کرنا ...\n  downloading_body: اس میں کچھ سیکنڈ لگ سکتے ہیں ، براہ کرم ونڈو کو بند نہ کریں۔\n  enable: قابل بنائیں\nruntime:\n  corrupted_data: خراب ڈیٹا\n  corrupted_file: >-\n    اس کے بجائے پہلے سے طے شدہ اقدار کا استعمال کرتے ہوئے ، کنفیگریشن فائل خراب\n    ہوگئی ہے۔\n  corrupted_file_path: خراب فائل کا راستہ\n  download: ڈاؤن لوڈ صفحہ پر جائیں۔\n  files_integrity: |-\n    ہم درخواست فائلوں کی سالمیت کی تصدیق نہیں کرسکتے ہیں۔\n    یہ ہوسکتا ہے اگر فائلوں میں ترمیم کی گئی ، خراب ہو ، یا تنصیب نامکمل ہے۔\n    سیکیورٹی وجوہات کی بناء پر ، سیلین UI جاری نہیں رکھ سکتا۔\n    براہ کرم اسے سرکاری ویب سائٹ سے دوبارہ انسٹال کریں۔\n  files_integrity_title: سیکیورٹی الرٹ - فائلوں کی سالمیت چیک ناکام ہوگئی\n  not_found: WebView2 رن ٹائم نہیں ملا\n  not_found_description: Seelen UI کو Webview2 رن ٹائم کی ضرورت ہے۔ براہ کرم اسے انسٹال کریں۔\n  outdated: WebView2 رن ٹائم پرانا ہے۔\n  outdated_description: >-\n    Seelen UI کو Webview2 رن ٹائم %{min_version} یا اس سے زیادہ درکار ہے۔ براہ\n    کرم اسے اپ ڈیٹ کریں۔\nservice:\n  not_running: Seelen UI سروس نہیں چل رہی ہے۔\n  not_running_description: >-\n    Seelen UI سروس (slu-service) ایپ کے صحیح طریقے سے کام کرنے کے لیے ایک\n    انحصار/مددگار ہے۔\n  not_running_ok: سروس شروع کریں۔\nshortcut:\n  register:\n    placeholder: کوئی بھی کلیدی مجموعہ دبائیں\n    title: نیا شارٹ کٹ رجسٹر کرنا\nwidget_liveness:\n  failed_description: |-\n    ویجیٹ '%{widget_name}' نے کئی بار جواب دینا بند کر دیا۔\n\n    آپ ایپ کو دوبارہ شروع کرنے کی کوشش کر سکتے ہیں۔\n  failed_title: ویجیٹ کی خرابی۔\nwidget_permissions:\n  perm_open_file: فائلیں کھولیں\n  perm_run: پروگرام چلائیں\n  request_description: |-\n    ویجیٹ '%{widget_name}' %{command} سے اجازت طلب کر رہا ہے۔\n\n    کیا آپ اس کی اجازت دینا چاہتے ہیں؟\n  request_title: ویجیٹ کی اجازت کی درخواست\n"
  },
  {
    "path": "src/background/i18n/uz.yml",
    "content": "cancel: Bekor qilmoq\ndone: Bajarildi\nelevated:\n  description: >-\n    Seelen UI administrator imtiyozlari bilan ishlaydi.\n\n    Bu hech qanday xususiyatni qo'shmaydi yoki yaxshilamaydi va xavfsizlikka\n    xavf tug'dirishi mumkin.\n\n    Yaxshiroq xavfsizlik uchun administrator ruxsatisiz uni qayta ishga\n    tushiring.\n  title: Administrator sifatida ishga tushirish\nfile_explorer: Fayl Explorer\nresource:\n  added: Yangi manba qo'shildi\n  download_failed_body: >-\n    Resursni yuklayotganda biron bir narsa noto'g'ri ketdi, iltimos, Seelen\n    ishlab chiqaruvchi jamoasiga xabar bering.\n  download_failed_title: Resurslarni yuklab olish amalga oshmadi\n  downloading: Aktivlarni yuklab olish ...\n  downloading_body: Bu bir necha soniya davom etishi mumkin, iltimos derazani yopmang.\n  enable: Yoqib yubormoq\nruntime:\n  corrupted_data: Buzilgan ma'lumotlar\n  corrupted_file: >-\n    Buning o'rniga standart qiymatlardan foydalangan holda konfiguratsiya fayli\n    buzilgan.\n  corrupted_file_path: Buzilgan fayl yo'li\n  download: Yuklab olish sahifasiga o'ting\n  files_integrity: >-\n    Ilova fayllari yaxlitligini tekshira olmadik.\n\n    Bu fayllar o'zgartirilgan, buzilgan yoki o'rnatish tugallanmagan bo'lsa\n    sodir bo'lishi mumkin.\n\n    Xavfsizlik nuqtai nazaridan Seelen UI davom eta olmaydi.\n\n    Iltimos, uni rasmiy veb-saytdan qayta o'rnating.\n  files_integrity_title: Xavfsizlik ogohlantirishi - Fayllar yaxlitligini tekshirish amalga oshmadi\n  not_found: WebView2 ish vaqti topilmadi\n  not_found_description: Seelen UI uchun Webview2 Runtime kerak. Iltimos, uni oʻrnating.\n  outdated: WebView2 ish vaqti eskirgan\n  outdated_description: >-\n    Seelen UI uchun Webview2 Runtime %{min_version} yoki undan yuqoriroq talab\n    qilinadi. Iltimos, uni yangilang.\nservice:\n  not_running: Seelen UI xizmati ishlamayapti\n  not_running_description: >-\n    Seelen UI xizmati (slu-xizmati) ilovaning to'g'ri ishlashi uchun\n    qaramlik/yordamchi hisoblanadi.\n  not_running_ok: Xizmatni ishga tushirish\nshortcut:\n  register:\n    placeholder: Har qanday tugmachani bosing\n    title: Yangi yorliqni ro'yxatdan o'tkazish\nwidget_liveness:\n  failed_description: |-\n    “%{widget_name}” vidjeti juda ko‘p marta javob berishni to‘xtatdi.\n\n    Ilovani qayta ishga tushirishga urinib ko'rishingiz mumkin.\n  failed_title: Vidjet xatosi\nwidget_permissions:\n  perm_open_file: fayllarni oching\n  perm_run: dasturlarni ishga tushirish\n  request_description: |-\n    “%{widget_name}” vidjeti %{command} uchun ruxsat so‘ramoqda.\n\n    Bunga ruxsat bermoqchimisiz?\n  request_title: Vidjetga ruxsat soʻrovi\n"
  },
  {
    "path": "src/background/i18n/vi.yml",
    "content": "cancel: Hủy bỏ\ndone: Xong\nelevated:\n  description: >-\n    Giao diện người dùng Seelen đang chạy với đặc quyền của quản trị viên.\n\n    Điều này không bổ sung hoặc cải thiện bất kỳ tính năng nào và có thể gây ra\n    rủi ro bảo mật.\n\n    Để bảo mật tốt hơn, vui lòng khởi động lại nó mà không có sự cho phép của\n    quản trị viên.\n  title: Chạy với tư cách quản trị viên\nfile_explorer: Trình khám phá tệp\nresource:\n  added: Một tài nguyên mới đã được thêm vào\n  download_failed_body: >-\n    Có điều gì đó không ổn trong khi tải xuống tài nguyên, vui lòng báo cáo cho\n    nhóm Nhà phát triển Seelen.\n  download_failed_title: Tải xuống tài nguyên không thành công\n  downloading: Tải xuống tài sản ...\n  downloading_body: Điều này có thể mất vài giây, xin vui lòng không đóng cửa sổ.\n  enable: Cho phép\nruntime:\n  corrupted_data: Dữ liệu bị hỏng\n  corrupted_file: Một tệp cấu hình bị hỏng, sử dụng các giá trị mặc định thay thế.\n  corrupted_file_path: Đường dẫn tệp bị hỏng\n  download: Đi tới trang tải xuống\n  files_integrity: >-\n    Chúng tôi không thể xác minh tính toàn vẹn của các tệp ứng dụng.\n\n    Điều này có thể xảy ra nếu tệp bị sửa đổi, bị hỏng hoặc quá trình cài đặt\n    chưa hoàn tất.\n\n    Vì lý do bảo mật, giao diện người dùng Seelen không thể tiếp tục.\n\n    Vui lòng cài đặt lại nó từ trang web chính thức.\n  files_integrity_title: Cảnh báo bảo mật - Kiểm tra tính toàn vẹn của tệp không thành công\n  not_found: Không tìm thấy thời gian chạy WebView2\n  not_found_description: Giao diện người dùng Seelen yêu cầu Thời gian chạy Webview2. Hãy cài đặt nó.\n  outdated: Thời gian chạy WebView2 đã lỗi thời\n  outdated_description: >-\n    Giao diện người dùng Seelen yêu cầu Webview2 Runtime %{min_version} trở lên.\n    Hãy cập nhật nó.\nservice:\n  not_running: Dịch vụ giao diện người dùng Seelen không chạy\n  not_running_description: >-\n    Dịch vụ giao diện người dùng Seelen (slu-service) là một công cụ phụ\n    thuộc/trợ giúp để ứng dụng hoạt động bình thường.\n  not_running_ok: Bắt đầu dịch vụ\nshortcut:\n  register:\n    placeholder: Nhấn bất kỳ kết hợp khóa nào\n    title: Đăng ký phím tắt mới\nwidget_liveness:\n  failed_description: |-\n    Tiện ích '%{widget_name}' đã ngừng phản hồi quá nhiều lần.\n\n    Bạn có thể thử khởi động lại ứng dụng.\n  failed_title: Lỗi tiện ích\nwidget_permissions:\n  perm_open_file: mở tập tin\n  perm_run: chạy chương trình\n  request_description: |-\n    Tiện ích '%{widget_name}' đang yêu cầu quyền đối với %{command}.\n\n    Bạn có muốn cho phép điều này?\n  request_title: Yêu cầu cấp phép tiện ích\n"
  },
  {
    "path": "src/background/i18n/yo.yml",
    "content": "cancel: Fagilee\ndone: Ṣe\nelevated:\n  description: |-\n    Seelen UI nṣiṣẹ pẹlu awọn anfani alakoso.\n    Eyi ko ṣafikun tabi mu awọn ẹya eyikeyi dara, ati pe o le jẹ eewu aabo.\n    Fun aabo to dara julọ, jọwọ tun bẹrẹ laisi awọn igbanilaaye alakoso.\n  title: Nṣiṣẹ bi Alakoso\nfile_explorer: Explorer faili\nresource:\n  added: A ti ṣafikun awọn orisun titun\n  download_failed_body: >-\n    Ohunkan ti o jẹ aṣiṣe lakoko gbigba awọn orisun, jọwọ jabo o si ẹgbẹ oludalu\n    ti o yẹ fun Silen.\n  download_failed_title: Igbasilẹ Igbasilẹ kuna\n  downloading: Gbigba awọn ohun-ini ...\n  downloading_body: Eyi le gba iṣẹju diẹ, jọwọ maṣe pa window naa.\n  enable: Mu ṣiṣẹ\nruntime:\n  corrupted_data: Awọn data ti o bajẹ\n  corrupted_file: Faili iṣeto kan jẹ ibajẹ, lilo awọn iye aifọwọyi dipo.\n  corrupted_file_path: Ọna faili ibajẹ\n  download: Lọ si oju-iwe igbasilẹ\n  files_integrity: |-\n    A ko le mọ daju otitọ ti awọn faili ohun elo naa.\n    Eyi le ṣẹlẹ ti awọn faili ba yipada, bajẹ, tabi fifi sori ẹrọ ko pe.\n    Fun awọn idi aabo, Seelen UI ko le tẹsiwaju.\n    Jọwọ tun fi sii lati oju opo wẹẹbu osise.\n  files_integrity_title: Itaniji Aabo - Ṣayẹwo Iduroṣinṣin Awọn faili kuna\n  not_found: WebView2 asiko isise ko ri\n  not_found_description: Seelen UI nilo Webview2 Runtime. Jọwọ fi sii.\n  outdated: WebView2 Runtime ti igba atijọ\n  outdated_description: >-\n    Seelen UI nilo Webview2 Runtime %{min_version} tabi ga julọ. Jọwọ ṣe\n    imudojuiwọn rẹ.\nservice:\n  not_running: Seelen UI Service ko nṣiṣẹ\n  not_running_description: >-\n    Iṣẹ Seelen UI (iṣẹ slu-slu) jẹ igbẹkẹle/oluranlọwọ fun ohun elo lati ṣiṣẹ\n    daradara.\n  not_running_ok: Bẹrẹ iṣẹ\nshortcut:\n  register:\n    placeholder: Tẹ eyikeyi apapo bọtini\n    title: Sisọ ọrọ abuja tuntun\nwidget_liveness:\n  failed_description: |-\n    Ẹrọ ailorukọ '%{widget_name}' dẹkun idahun ni ọpọlọpọ igba.\n\n    O le gbiyanju lati tun app naa bẹrẹ.\n  failed_title: Aṣiṣe ẹrọ ailorukọ\nwidget_permissions:\n  perm_open_file: ṣii awọn faili\n  perm_run: ṣiṣe awọn eto\n  request_description: |-\n    Ẹrọ ailorukọ '%{widget_name}' n beere fun igbanilaaye lati %{command}.\n\n    Ṣe o fẹ gba eyi laaye?\n  request_title: Ibeere Gbigbanilaaye ailorukọ\n"
  },
  {
    "path": "src/background/i18n/zh-CN.yml",
    "content": "cancel: 取消\ndone: 完毕\nelevated:\n  description: |-\n    Seelen UI 正在以管理员权限运行。\n    这不会添加或改进任何功能，并且可能会带来安全风险。\n    为了更好的安全性，请在没有管理员权限的情况下重新启动它。\n  title: 以管理员身份运行\nfile_explorer: 文件浏览器\nresource:\n  added: 已添加新资源\n  download_failed_body: 下载资源时出现问题，请向 Seelen 开发团队报告。\n  download_failed_title: 资源下载失败\n  downloading: 正在下载资源...\n  downloading_body: 这可能需要几秒钟，请不要关闭窗口。\n  enable: 启用\nruntime:\n  corrupted_data: 数据损坏\n  corrupted_file: 配置文件已损坏，正在使用默认值。\n  corrupted_file_path: 损坏的文件路径\n  download: 前往下载页面\n  files_integrity: |-\n    我们无法验证应用程序文件的完整性。\n    如果文件被修改、损坏或安装不完整，则可能会发生这种情况。\n    出于安全原因，Seelen UI 无法继续。\n    请从官方网站重新安装。\n  files_integrity_title: 安全警报 - 文件完整性检查失败\n  not_found: 未找到 WebView2 运行时\n  not_found_description: Seelen UI 需要 Webview2 运行时。请安装它。\n  outdated: WebView2 运行时版本过旧\n  outdated_description: Seelen UI 需要 Webview2 运行时 %{min_version} 或更高版本。请更新它。\nservice:\n  not_running: Seelen UI 服务未运行\n  not_running_description: Seelen UI 服务（slu-service）是应用程序正常工作的依赖项/帮助程序。\n  not_running_ok: 启动服务\nshortcut:\n  register:\n    placeholder: 按下任意组合键\n    title: 注册新快捷键\nwidget_liveness:\n  failed_description: |-\n    小组件“%{widget_name}”停止响应的次数过多。\n\n    您可以尝试重新启动应用程序。\n  failed_title: 小部件错误\nwidget_permissions:\n  perm_open_file: 打开文件\n  perm_run: 运行程序\n  request_description: |-\n    小组件“%{widget_name}”正在请求 %{command} 的权限。\n\n    您想允许这样做吗？\n  request_title: 小部件权限请求\n"
  },
  {
    "path": "src/background/i18n/zh-TW.yml",
    "content": "cancel: 取消\ndone: 完畢\nelevated:\n  description: |-\n    Seelen UI 正在以管理員權限執行。\n    這不會增加或改進任何功能，並且可能會帶來安全風險。\n    為了更好的安全性，請在沒有管理員權限的情況下重新啟動它。\n  title: 以管理員身份執行\nfile_explorer: 文件瀏覽器\nresource:\n  added: 添加了一個新資源\n  download_failed_body: 下載資源時出現問題，請向Seelen開發人員團隊報告。\n  download_failed_title: 資源下載失敗\n  downloading: 下載資產...\n  downloading_body: 這可能需要幾秒鐘，請不要關閉窗口。\n  enable: 使能夠\nruntime:\n  corrupted_data: 損壞的數據\n  corrupted_file: 使用默認值改為損壞配置文件。\n  corrupted_file_path: 損壞的文件路徑\n  download: 轉到下載頁面\n  files_integrity: |-\n    我們無法驗證應用程序文件的完整性。\n    如果文件被修改、損壞或安裝不完整，則可能會發生這種情況。\n    出於安全原因，Seelen UI 無法繼續。\n    請從官方網站重新安裝。\n  files_integrity_title: 安全警報 - 文件完整性檢查失敗\n  not_found: WebView2運行時找不到\n  not_found_description: Seelen UI需要WebView2運行時。 請安裝。\n  outdated: WebView2運行時過時\n  outdated_description: Seelen UI需要WebView2運行時％{min_version}或更高。 請更新。\nservice:\n  not_running: Seelen UI服務不運行\n  not_running_description: SEELEN UI服務（SLU服務）是該應用程序正常工作的依賴性/助手。\n  not_running_ok: 開始服務\nshortcut:\n  register:\n    placeholder: 按任何鍵組合\n    title: 註冊新快捷方式\nwidget_liveness:\n  failed_description: |-\n    小元件「%{widget_name}」停止回應的次數過多。\n\n    您可以嘗試重新啟動應用程式。\n  failed_title: 小部件錯誤\nwidget_permissions:\n  perm_open_file: 開啟文件\n  perm_run: 運行程式\n  request_description: |-\n    小元件「%{widget_name}」正在請求 %{command} 的權限。\n\n    您想允許這樣做嗎？\n  request_title: 小部件權限請求\n"
  },
  {
    "path": "src/background/i18n/zu.yml",
    "content": "cancel: Hlikihla\ndone: Usulile\nelevated:\n  description: >-\n    I-Seelen UI isebenza ngamalungelo omlawuli.\n\n    Lokhu akungezi noma akuthuthukisi noma yiziphi izici, futhi kungase kudale\n    ingcuphe yezokuphepha.\n\n    Ukuze uthole ukuphepha okungcono, sicela uyiqale kabusha ngaphandle\n    kwezimvume zomlawuli.\n  title: Isebenza njengomlawuli\nfile_explorer: I-File Explorer\nresource:\n  added: Kufakwe insiza entsha\n  download_failed_body: >-\n    Kukhona okungahambanga kahle ngenkathi elanda insiza, sicela ubike kwiqembu\n    le-Seelen Developer.\n  download_failed_title: Ukulanda izinsiza kwehlulekile\n  downloading: Ukulanda Amafa ...\n  downloading_body: Lokhu kungathatha imizuzwana embalwa, ngicela ungavali iwindi.\n  enable: Nika amandla\nruntime:\n  corrupted_data: Idatha eyonakalisiwe\n  corrupted_file: >-\n    Ifayela lokucushwa lonakalisiwe, kusetshenziswa amanani azenzakalelayo\n    esikhundleni salokho.\n  corrupted_file_path: Indlela yefayela eyonakele\n  download: Iya ekhasini lokulanda\n  files_integrity: >-\n    Asikwazanga ukuqinisekisa ubuqotho bamafayela ohlelo lokusebenza.\n\n    Lokhu kungenzeka uma amafayela aye ashintshwa, wonakala, noma ukufakwa\n    kungaphelele.\n\n    Ngenxa yezizathu zokuphepha, i-Seelen UI ayikwazi ukuqhubeka.\n\n    Sicela uyifake kabusha kusuka kuwebhusayithi esemthethweni.\n  files_integrity_title: Isexwayiso Sokuphepha - Ukuhlola Ubuqotho Kwamafayela Kwehlulekile\n  not_found: Isikhathi sokusebenza se-WebView2 asitholakali\n  not_found_description: I-Seelen UI idinga isikhathi sokusebenza se-Webview2. Sicela uyifake.\n  outdated: I-WebView2 Runtime isiphelelwe yisikhathi\n  outdated_description: >-\n    I-Seelen UI idinga isikhathi sokusebenza se-Webview2 %{min_version} noma\n    ngaphezulu. Sicela uyibuyekeze.\nservice:\n  not_running: Isevisi ye-Seelen UI ayisebenzi\n  not_running_description: >-\n    Isevisi ye-Seelen UI (slu-service) incike/umsizi ukuze uhlelo lokusebenza\n    lusebenze kahle.\n  not_running_ok: Qala isevisi\nshortcut:\n  register:\n    placeholder: Cindezela noma iyiphi inhlanganisela yokhiye\n    title: Ukubhalisa isinqamuleli esisha\nwidget_liveness:\n  failed_description: |-\n    Iwijethi '%{widget_name}' iyeke ukuphendula izikhathi eziningi kakhulu.\n\n    Ungazama ukuqala kabusha uhlelo lokusebenza.\n  failed_title: Iphutha Lewijethi\nwidget_permissions:\n  perm_open_file: vula amafayela\n  perm_run: sebenzisa izinhlelo\n  request_description: |-\n    Iwijethi '%{widget_name}' icela imvume ku-%{command}.\n\n    Uyafuna ukuvumela lokhu?\n  request_title: Isicelo Semvume Yewijethi\n"
  },
  {
    "path": "src/background/logger.rs",
    "content": "use std::sync::LazyLock;\n\nuse owo_colors::OwoColorize;\nuse windows::Win32::UI::Shell::FOLDERID_LocalAppData;\n\nuse crate::{error::Result, windows_api::WindowsApi};\n\npub struct SeelenLogger {}\n\nfn format_now() -> String {\n    static FMT: LazyLock<Vec<time::format_description::BorrowedFormatItem>> = LazyLock::new(|| {\n        time::format_description::parse(\"[[[year]-[month]-[day]][[[hour]:[minute]:[second]]\")\n            .expect(\"valid time format\")\n    });\n\n    time::OffsetDateTime::now_local()\n        .unwrap_or_else(|_| time::OffsetDateTime::now_utc())\n        .format(&FMT)\n        .unwrap_or_else(|_| \"?time?\".to_owned())\n}\n\nimpl SeelenLogger {\n    const MAX_LOG_SIZE: u64 = 5 * 1024 * 1024; // 5MB\n\n    pub fn init() -> Result<()> {\n        let logs_folder =\n            WindowsApi::known_folder(FOLDERID_LocalAppData)?.join(\"com.seelen.seelen-ui/logs\");\n        std::fs::create_dir_all(&logs_folder)?;\n\n        let log_path = logs_folder.join(\"Seelen UI.log\");\n        if log_path.exists() {\n            let metadata = std::fs::metadata(&log_path)?;\n            if metadata.len() > Self::MAX_LOG_SIZE {\n                let bak_path = logs_folder.join(\"Seelen UI.log.bak\");\n                std::fs::rename(&log_path, &bak_path)?;\n            }\n        }\n\n        let file_dispatch = fern::Dispatch::new()\n            .format(|out, message, record| {\n                out.finish(format_args!(\n                    \"{}[{}][{}] {}\",\n                    format_now(),\n                    record.level(),\n                    record.target(),\n                    message\n                ))\n            })\n            .chain(fern::log_file(log_path)?);\n\n        let dispatch = fern::Dispatch::new()\n            .level(log::LevelFilter::Trace)\n            .level_for(\"tao\", log::LevelFilter::Off)\n            .level_for(\"os_info\", log::LevelFilter::Off)\n            .level_for(\"notify\", log::LevelFilter::Off)\n            .level_for(\"notify_debouncer_full\", log::LevelFilter::Off)\n            .level_for(\"discord_presence\", log::LevelFilter::Off)\n            .chain(file_dispatch);\n\n        #[cfg(dev)]\n        let dispatch = dispatch.chain(\n            fern::Dispatch::new()\n                .format(|out, message, record| {\n                    out.finish(format_args!(\n                        \"[{}][{}] {}\",\n                        match record.level() {\n                            log::Level::Error => \"ERROR\".red().to_string(),\n                            log::Level::Warn => \"WARN~\".yellow().to_string(),\n                            log::Level::Info => \"INFO~\".bright_blue().to_string(),\n                            log::Level::Debug => \"DEBUG\".bright_green().to_string(),\n                            log::Level::Trace => \"TRACE\".bright_black().to_string(),\n                        },\n                        if record.level() == log::Level::Error {\n                            record\n                                .file()\n                                .filter(|f| !f.contains(\"exposed.rs\"))\n                                .map(|f| {\n                                    format!(\n                                        \"{}:{}\",\n                                        f.replace(\"\\\\\", \"/\"),\n                                        record.line().unwrap_or_default()\n                                    )\n                                })\n                                .unwrap_or_else(|| record.target().to_owned())\n                                .bright_red()\n                                .to_string()\n                        } else {\n                            record.target().bright_black().to_string()\n                        },\n                        message\n                    ))\n                })\n                .chain(std::io::stdout()),\n        );\n\n        dispatch.apply()?;\n        register_panic_hook();\n        Ok(())\n    }\n}\n\npub fn register_panic_hook() {\n    let base_hook = std::panic::take_hook();\n    std::panic::set_hook(Box::new(move |info| {\n        let cause = info\n            .payload()\n            .downcast_ref::<String>()\n            .map(|s| s.to_string())\n            .unwrap_or_else(|| {\n                info.payload()\n                    .downcast_ref::<&str>()\n                    .unwrap_or(&\"<cause unknown>\")\n                    .to_string()\n            });\n\n        let mut string_location = String::from(\"<location unknown>\");\n        if let Some(location) = info.location() {\n            string_location = format!(\n                \"{}:{}:{}\",\n                location.file(),\n                location.line(),\n                location.column()\n            );\n        }\n\n        log::error!(\"A panic occurred:\\n  Cause: {cause}\\n  Location: {string_location}\");\n        base_hook(info);\n    }));\n}\n"
  },
  {
    "path": "src/background/main.rs",
    "content": "// Prevents additional console window on Windows in release\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n#![feature(never_type)]\n\nmod app;\nmod app_instance;\nmod cli;\nmod error;\nmod exposed;\nmod hook;\nmod logger;\nmod migrations;\nmod modules;\nmod resources;\nmod state;\nmod tauri_context;\nmod tauri_plugins;\nmod telemetry;\nmod utils;\nmod virtual_desktops;\nmod widgets;\nmod windows_api;\n\n#[macro_use]\nextern crate rust_i18n;\ni18n!(\"background/i18n\", fallback = \"en\");\n\nuse std::sync::{atomic::AtomicBool, OnceLock};\n\nuse app::{Seelen, SEELEN};\nuse cli::{application::handle_console_client, SelfPipe, ServicePipe};\nuse error::Result;\nuse exposed::register_invoke_handler;\nuse logger::SeelenLogger;\nuse slu_ipc::messages::SvcAction;\nuse tauri_plugins::register_plugins;\nuse utils::{\n    integrity::{is_already_running, print_initial_information, restart_as_appx, warn_if_elevated},\n    is_running_as_appx, was_installed_using_msix, PERFORMANCE_HELPER,\n};\n\nuse crate::{app::get_app_handle, error::ResultLogExt, utils::constants::SEELEN_COMMON};\n\nstatic APP_HANDLE: OnceLock<tauri::AppHandle<tauri::Wry>> = OnceLock::new();\nstatic TOKIO_RUNTIME_HANDLE: OnceLock<tokio::runtime::Handle> = OnceLock::new();\nstatic SILENT: AtomicBool = AtomicBool::new(false);\nstatic VERBOSE: AtomicBool = AtomicBool::new(false);\n\npub fn is_local_dev() -> bool {\n    cfg!(dev)\n}\n\npub fn get_tokio_handle() -> &'static tokio::runtime::Handle {\n    TOKIO_RUNTIME_HANDLE\n        .get()\n        .expect(\"Tokio runtime was not initialized\")\n}\n\n#[tokio::main]\nasync fn main() {\n    if let Err(err) = SeelenLogger::init() {\n        let fallback = std::env::temp_dir().join(\"seelen-ui-logger-error.log\");\n        let _ = std::fs::write(&fallback, format!(\"Failed to initialize logger: {err:?}\"));\n        std::process::exit(1);\n    }\n\n    if let Err(err) = handle_console_client().await {\n        log::error!(\"Failed to execute command: {err:?}\");\n        std::process::exit(1);\n    };\n\n    if is_already_running() {\n        SelfPipe::request_open_settings().await.log_error();\n        return;\n    }\n\n    if was_installed_using_msix() && !is_running_as_appx() {\n        log::info!(\"GUI was installed using MSIX, restarting as appx...\");\n        restart_as_appx().log_error();\n        return;\n    }\n\n    TOKIO_RUNTIME_HANDLE\n        .set(tokio::runtime::Handle::current())\n        .expect(\"Failed to set runtime handle\");\n\n    rust_i18n::set_locale(&seelen_core::state::Settings::get_system_language());\n    trace_lock!(PERFORMANCE_HELPER).start(\"setup\");\n    let mut app_builder = tauri::Builder::default();\n    app_builder = register_plugins(app_builder);\n    app_builder = register_invoke_handler(app_builder);\n\n    // if no custom runtime is present, the app will use the installed with the system\n    if let Some(path) = crate::utils::get_fixed_runtime_path() {\n        std::env::set_var(\"WEBVIEW2_BROWSER_EXECUTABLE_FOLDER\", path);\n    }\n\n    let app = app_builder\n        .setup(|app| {\n            APP_HANDLE.set(app.handle().to_owned()).unwrap();\n\n            tokio::spawn(async move {\n                let handle = get_app_handle();\n                if let Err(err) = setup(handle).await {\n                    log::error!(\"Error while setting up: {err:?}\");\n                    handle.exit(1);\n                }\n            });\n            Ok(())\n        })\n        .build(tauri_context::get_context())\n        .expect(\"Error while building tauri application\");\n\n    // share the current runtime with Tauri\n    tauri::async_runtime::set(tokio::runtime::Handle::current());\n    app.run(app_callback);\n}\n\nasync fn setup(app_handle: &tauri::AppHandle<tauri::Wry>) -> Result<()> {\n    print_initial_information();\n    create_main_folders()?;\n    utils::integrity::validate_webview_runtime_is_installed(app_handle)?;\n    utils::integrity::ensure_bundle_files_integrity(app_handle)?;\n\n    SelfPipe::start_listener()?;\n    if !ServicePipe::is_running() {\n        ServicePipe::start_service().await?;\n    }\n\n    utils::integrity::check_for_webview_optimal_state(app_handle)?;\n\n    trace_lock!(SEELEN).start()?;\n    trace_lock!(PERFORMANCE_HELPER).end(\"setup\");\n    warn_if_elevated(app_handle);\n    telemetry::start_telemetry();\n    Ok(())\n}\n\nfn create_main_folders() -> Result<()> {\n    std::fs::create_dir_all(SEELEN_COMMON.app_temp_dir())?;\n    std::fs::create_dir_all(SEELEN_COMMON.app_data_dir())?;\n    std::fs::create_dir_all(SEELEN_COMMON.app_cache_dir())?;\n\n    std::fs::create_dir_all(SEELEN_COMMON.user_themes_path())?;\n    std::fs::create_dir_all(SEELEN_COMMON.user_icons_path())?;\n    std::fs::create_dir_all(SEELEN_COMMON.user_wallpapers_path())?;\n    std::fs::create_dir_all(SEELEN_COMMON.user_sounds_path())?;\n    std::fs::create_dir_all(SEELEN_COMMON.user_plugins_path())?;\n    std::fs::create_dir_all(SEELEN_COMMON.user_widgets_path())?;\n    Ok(())\n}\n\nfn app_callback(_: &tauri::AppHandle<tauri::Wry>, event: tauri::RunEvent) {\n    match event {\n        tauri::RunEvent::Ready => {\n            log::info!(\"Tauri Application is ready.\");\n        }\n        tauri::RunEvent::Resumed => {\n            log::info!(\"Tauri Event Loop was resumed.\");\n        }\n        tauri::RunEvent::ExitRequested { api, code, .. } => match code {\n            Some(code) => {\n                // if exit code is 0 it means that the app was closed by the user\n                if code == 0 {\n                    log_error!(ServicePipe::request(SvcAction::Stop));\n                }\n            }\n            // prevent close background on webview windows closing\n            None => api.prevent_exit(),\n        },\n        tauri::RunEvent::Exit => {\n            log::info!(\"───────────────────── Exiting Seelen UI ─────────────────────\");\n            if Seelen::is_running() {\n                trace_lock!(SEELEN).stop();\n            }\n        }\n        _ => {}\n    }\n}\n"
  },
  {
    "path": "src/background/migrations.rs",
    "content": "use seelen_core::resource::WidgetId;\nuse tauri::{path::BaseDirectory, Manager};\n\nuse crate::{app::get_app_handle, error::Result, utils::constants::SEELEN_COMMON};\n\npub struct Migrations;\n\nimpl Migrations {\n    // migration of user settings files below v1.8.3\n    fn migration_v1_8_3() -> Result<()> {\n        let path = get_app_handle().path();\n        let data_path = path.app_data_dir()?;\n\n        let old_path = path.resolve(\".config/seelen\", BaseDirectory::Home)?;\n        if old_path.exists() {\n            log::trace!(\"Migrating user settings from {old_path:?}\");\n            for entry in std::fs::read_dir(&old_path)?.flatten() {\n                if entry.file_type()?.is_dir() {\n                    continue;\n                }\n                std::fs::copy(entry.path(), data_path.join(entry.file_name()))?;\n            }\n            std::fs::remove_dir_all(&old_path)?;\n        }\n        Ok(())\n    }\n\n    // migration of user settings files below v2.1.0\n    fn migration_v2_1_0() -> Result<()> {\n        let old_folder = SEELEN_COMMON.app_data_dir().join(\"placeholders\");\n        let old = old_folder.join(\"custom.yml\");\n        if old.exists() {\n            std::fs::copy(old, SEELEN_COMMON.app_cache_dir().join(\"toolbar_items.yml\"))?;\n            std::fs::remove_dir_all(old_folder)?;\n        }\n        Ok(())\n    }\n\n    fn migration_v2_3_9() -> Result<()> {\n        let handle = get_app_handle();\n        let data_path = handle.path().app_data_dir()?;\n\n        let old_soundpacks = data_path.join(\"sounds\");\n        if old_soundpacks.exists() {\n            std::fs::remove_dir_all(old_soundpacks)?;\n        }\n\n        let old_iconpacks = data_path.join(\"icons\");\n        if old_iconpacks.exists() {\n            std::fs::remove_dir_all(old_iconpacks)?;\n        }\n        Ok(())\n    }\n\n    // remove old generated icon pack (path changed in v2.4.10)\n    fn migration_v2_4_10() -> Result<()> {\n        let old_path = SEELEN_COMMON.user_icons_path().join(\"system\");\n        if old_path.exists() {\n            std::fs::remove_dir_all(old_path)?;\n        }\n        Ok(())\n    }\n\n    fn migration_v2_5_0() -> Result<()> {\n        let old = SEELEN_COMMON.app_data_dir().join(\"applications.yml\");\n        if old.exists() {\n            std::fs::rename(\n                old,\n                SEELEN_COMMON.app_data_dir().join(\"settings_by_app.yml\"),\n            )?;\n        }\n\n        let old_weg_save = SEELEN_COMMON.app_data_dir().join(\"seelenweg_items_v2.yml\");\n        if old_weg_save.exists() {\n            let new_weg_save = SEELEN_COMMON\n                .widget_data_dir(&WidgetId::known_weg())\n                .join(\"state.yml\");\n            std::fs::create_dir_all(new_weg_save.parent().unwrap())?;\n            std::fs::rename(old_weg_save, new_weg_save)?;\n        }\n\n        let old_toolbar_save = SEELEN_COMMON.app_data_dir().join(\"toolbar_items.yml\");\n        if old_toolbar_save.exists() {\n            let new_toolbar_save = SEELEN_COMMON\n                .widget_data_dir(&WidgetId::known_toolbar())\n                .join(\"state.yml\");\n            std::fs::create_dir_all(new_toolbar_save.parent().unwrap())?;\n            std::fs::rename(old_toolbar_save, new_toolbar_save)?;\n        }\n        Ok(())\n    }\n\n    pub fn run() -> Result<()> {\n        Self::migration_v1_8_3()?;\n        Self::migration_v2_1_0()?;\n        Self::migration_v2_3_9()?;\n        Self::migration_v2_4_10()?;\n        Self::migration_v2_5_0()?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/apps/application/mod.rs",
    "content": "pub mod msix;\npub mod msix_manifest;\npub mod previews;\nmod windows;\n\npub use windows::*;\n\nuse std::sync::LazyLock;\n\nuse seelen_core::system_state::UserAppWindow;\n\nuse crate::{event_manager, utils::lock_free::SyncVec, windows_api::window::Window};\n\npub static USER_APPS_MANAGER: LazyLock<UserAppsManager> = LazyLock::new(UserAppsManager::init);\n\npub struct UserAppsManager {\n    pub interactable_windows: SyncVec<UserAppWindow>,\n}\n\n#[allow(dead_code)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum UserAppWinEvent {\n    Added(isize),\n    Updated(isize),\n    Removed(isize),\n}\n\nevent_manager!(UserAppsManager, UserAppWinEvent);\n\nimpl UserAppsManager {\n    fn init() -> Self {\n        Self {\n            interactable_windows: SyncVec::from(Self::init_listing_app_windows()),\n        }\n    }\n\n    pub fn instance() -> &'static Self {\n        &USER_APPS_MANAGER\n    }\n\n    pub fn contains_win(&self, window: &Window) -> bool {\n        let hwnd = window.address();\n        self.interactable_windows.any(|w| w.hwnd == hwnd)\n    }\n\n    fn add_win(&self, window: &Window) {\n        log::trace!(\"Adding: {window}\");\n        self.interactable_windows.push(window.to_serializable());\n    }\n\n    fn remove_win(&self, window: &Window) {\n        log::trace!(\"Removing: {window}\");\n        let hwnd = window.address();\n        self.interactable_windows.retain(|w| w.hwnd != hwnd);\n    }\n}\n"
  },
  {
    "path": "src/background/modules/apps/application/msix.rs",
    "content": "use std::{\n    path::{Path, PathBuf},\n    sync::LazyLock,\n};\n\nuse windows::ApplicationModel::{AppInfo, Package};\n\nuse crate::{error::Result, modules::apps::application::msix_manifest::PackageManifest};\n\nstatic UWP_LIGHTUNPLATED_POSTFIX: &str = \"_altform-lightunplated\";\nstatic UWP_UNPLATED_POSTFIX: &str = \"_altform-unplated\";\nstatic UWP_TARGET_SIZE_POSTFIXES: &[&str] = &[\n    \".targetsize-256\",\n    \".targetsize-96\",\n    \".targetsize-64\",\n    \".targetsize-48\",\n    \".targetsize-32\",\n];\nstatic UWP_SCALE_POSTFIXES: &[&str] = &[\n    \".scale-400\",\n    \".scale-200\",\n    \".scale-150\",\n    \".scale-125\",\n    \".scale-100\",\n];\n\npub struct MsixAppsManager {\n    _priv: (),\n}\n\nimpl MsixAppsManager {\n    fn new() -> Self {\n        Self { _priv: () }\n    }\n\n    pub fn instance() -> &'static Self {\n        static MSIX_APPS_MANAGER: LazyLock<MsixAppsManager> = LazyLock::new(MsixAppsManager::new);\n        &MSIX_APPS_MANAGER\n    }\n\n    /// Some apps like PWA on edge can be stored as UWP apps and don't have an executable path,\n    /// so in that cases the function will return None.\n    ///\n    /// This function will fail if the umid provided is not of type msix/appx.\n    pub fn get_app_path(&self, app_umid: &str) -> Result<Option<PathBuf>> {\n        let app_info = AppInfo::GetFromAppUserModelId(&app_umid.into())?;\n        let package = app_info.Package()?;\n        let package_family_name = package.Id()?.FamilyName()?.to_string_lossy();\n\n        let manifest = PackageManifest::try_read_for(&package)?;\n\n        let apps = &manifest.applications.application;\n        for app in apps {\n            if format!(\"{package_family_name}!{}\", app.id) != app_umid {\n                continue;\n            }\n\n            if let Some(executable) = &app.executable {\n                let package_path = PathBuf::from(package.InstalledPath()?.to_os_string());\n                return Ok(Some(package_path.join(executable)));\n            }\n        }\n\n        // println!(\"Manifest {manifest:#?}, package_family_name {package_family_name}\");\n        Ok(None)\n    }\n\n    /// ### Returns:\n    /// light and dark icons\n    pub fn get_app_icon_path(&self, app_umid: &str) -> Result<(PathBuf, PathBuf)> {\n        let app_info = AppInfo::GetFromAppUserModelId(&app_umid.into())?;\n\n        let package = app_info.Package()?;\n\n        let manifest = PackageManifest::try_read_for(&package)?;\n\n        let package_path = PathBuf::from(package.InstalledPath()?.to_os_string());\n        let store_logo = package_path.join(&manifest.properties.logo);\n\n        // if package does't have the app but it is still part of the package then use the package logo\n        let app_manifest = match manifest.get_app(&app_info.Id()?.to_string_lossy()) {\n            Some(app) => app,\n            None => {\n                return get_hightest_quality_posible_for_uwp_image(&store_logo)\n                    .or_else(|| get_icon_via_display_info(&app_info, app_umid))\n                    .ok_or(\"Could not find package logo path\".into())\n            }\n        };\n\n        let app_logo_44 = package_path.join(&app_manifest.visual_elements.logo_44);\n        let app_logo_150 = package_path.join(&app_manifest.visual_elements.logo_150);\n\n        get_hightest_quality_posible_for_uwp_image(&app_logo_44)\n            .or_else(|| get_hightest_quality_posible_for_uwp_image(&app_logo_150))\n            .or_else(|| get_hightest_quality_posible_for_uwp_image(&store_logo))\n            .or_else(|| get_icon_via_display_info(&app_info, app_umid))\n            .ok_or_else(|| format!(\"App icon not found for {app_umid} in {package_path:?}\").into())\n    }\n}\n\nimpl PackageManifest {\n    fn try_read_for(package: &Package) -> Result<Self> {\n        let package_path = PathBuf::from(package.InstalledPath()?.to_os_string());\n        let manifest_path = package_path.join(\"AppxManifest.xml\");\n\n        let file = std::fs::File::open(&manifest_path)?;\n        let mut reader = std::io::BufReader::new(file);\n\n        Ok(quick_xml::de::from_reader(&mut reader)?)\n    }\n}\n\n/// Fallback icon extraction using `AppDisplayInfo::GetLogo`.\n///\n/// When the manifest-based file search fails (e.g. paths remapped by resources.pri,\n/// `ms-resource:` URIs, locale subdirectories), the WinRT `AppInfo.DisplayInfo.GetLogo`\n/// API resolves the correct icon for the current context and returns its raw bytes.\n/// Those bytes are written to a temporary file so the rest of the pipeline can use\n/// `image::open()` on a normal path.\n///\n/// Returns `(path, path)` — a single file used for both light and dark since\n/// `GetLogo` does not distinguish themes.\nfn get_icon_via_display_info(app_info: &AppInfo, app_umid: &str) -> Option<(PathBuf, PathBuf)> {\n    use std::hash::{DefaultHasher, Hash, Hasher};\n    use windows::Foundation::Size;\n    use windows::Storage::Streams::{Buffer, DataReader, InputStreamOptions};\n\n    let display_info = app_info.DisplayInfo().ok()?;\n    let logo_ref = display_info\n        .GetLogo(Size {\n            Width: 256.0,\n            Height: 256.0,\n        })\n        .ok()?;\n\n    let stream = logo_ref.OpenReadAsync().ok()?.join().ok()?;\n\n    let size = stream.Size().ok()? as u32;\n    if size == 0 {\n        return None;\n    }\n\n    let buffer = Buffer::Create(size).ok()?;\n    let filled = stream\n        .ReadAsync(&buffer, size, InputStreamOptions::None)\n        .ok()?\n        .join()\n        .ok()?;\n\n    let len = filled.Length().ok()? as usize;\n    let reader = DataReader::FromBuffer(&filled).ok()?;\n    let mut bytes = vec![0u8; len];\n    reader.ReadBytes(&mut bytes).ok()?;\n\n    let mut hasher = DefaultHasher::new();\n    app_umid.hash(&mut hasher);\n    let path = std::env::temp_dir().join(format!(\"seelen_logo_{:x}.png\", hasher.finish()));\n    std::fs::write(&path, &bytes).ok()?;\n\n    Some((path.clone(), path))\n}\n\n// returns light and dark icons\npub fn get_hightest_quality_posible_for_uwp_image(icon_path: &Path) -> Option<(PathBuf, PathBuf)> {\n    let filename = icon_path.file_stem()?.to_str()?;\n    let extension = icon_path.extension()?.to_str()?;\n\n    let size_postfixes = (*UWP_TARGET_SIZE_POSTFIXES)\n        .iter()\n        .chain((*UWP_SCALE_POSTFIXES).iter());\n\n    for size_postfix in size_postfixes {\n        let light_icon = icon_path.with_file_name(format!(\n            \"{filename}{size_postfix}{UWP_LIGHTUNPLATED_POSTFIX}.{extension}\"\n        ));\n\n        let dark_icon = icon_path.with_file_name(format!(\n            \"{filename}{size_postfix}{UWP_UNPLATED_POSTFIX}.{extension}\"\n        ));\n\n        let unthemed_icon =\n            icon_path.with_file_name(format!(\"{filename}{size_postfix}.{extension}\"));\n\n        match (\n            light_icon.exists(),\n            dark_icon.exists(),\n            unthemed_icon.exists(),\n        ) {\n            (true, true, _) => return Some((light_icon, dark_icon)),\n            (true, false, _) => return Some((light_icon.clone(), light_icon)),\n            (false, true, true) => return Some((unthemed_icon, dark_icon)),\n            (false, false, true) => return Some((unthemed_icon.clone(), unthemed_icon)),\n            _ => {}\n        }\n    }\n\n    // Some apps only adds one icon and without any postfix\n    // but we prefer the light/dark specific icon\n    if icon_path.exists() {\n        return Some((icon_path.to_path_buf(), icon_path.to_path_buf()));\n    }\n\n    None\n}\n"
  },
  {
    "path": "src/background/modules/apps/application/msix_manifest.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct PackageManifest {\n    pub identity: ManifestIdentity,\n    pub properties: ManifestProperties,\n    #[serde(default)]\n    pub applications: ManifestApplications,\n}\n\nimpl PackageManifest {\n    pub fn get_app(&self, id: &str) -> Option<&ManifestApplication> {\n        self.applications\n            .application\n            .iter()\n            .find(|app| app.id == id)\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ManifestIdentity {\n    #[serde(rename = \"@Name\")]\n    pub name: String,\n    #[serde(rename = \"@Version\")]\n    pub version: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct ManifestProperties {\n    pub display_name: String,\n    pub publisher_display_name: String,\n    pub logo: String,\n    pub description: Option<String>,\n}\n\n/// This struct makes reference to:\n/// https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-applications\n#[derive(Debug, Default, Clone, Serialize, Deserialize)]\n#[serde(default, rename_all = \"PascalCase\")]\npub struct ManifestApplications {\n    pub application: Vec<ManifestApplication>,\n}\n\n/// This struct makes reference to:\n/// https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-application\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct ManifestApplication {\n    #[serde(rename = \"@Id\")]\n    pub id: String,\n    #[serde(rename = \"@Executable\")]\n    pub executable: Option<String>,\n    pub visual_elements: ManifestApplicationVisualElements,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct ManifestApplicationVisualElements {\n    #[serde(rename = \"@DisplayName\")]\n    pub display_name: String,\n    #[serde(rename = \"@Description\")]\n    pub description: String,\n    #[serde(rename = \"@BackgroundColor\")]\n    pub background_color: String,\n    #[serde(rename = \"@Square150x150Logo\")]\n    pub logo_150: String,\n    #[serde(rename = \"@Square44x44Logo\")]\n    pub logo_44: String,\n}\n"
  },
  {
    "path": "src/background/modules/apps/application/previews.rs",
    "content": "use std::sync::LazyLock;\n\nuse image::{DynamicImage, RgbaImage};\nuse seelen_core::system_state::UserAppWindowPreview;\nuse win_screenshot::prelude::capture_window;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    hook::HookManager,\n    modules::apps::application::{UserAppWinEvent, UserAppsManager, USER_APPS_MANAGER},\n    utils::{constants::SEELEN_COMMON, lock_free::SyncHashMap},\n    windows_api::{\n        window::{event::WinEvent, Window},\n        WindowsApi,\n    },\n};\n\nstatic WINDOWS_PREVIEWS: LazyLock<WinPreviewManager> = LazyLock::new(WinPreviewManager::create);\n\npub struct WinPreviewManager {\n    pub previews: SyncHashMap<isize, UserAppWindowPreview>,\n}\n\n#[derive(Debug, Clone)]\n#[allow(dead_code)]\npub enum WinPreviewEvent {\n    Captured(isize),\n    Cleaned(isize),\n}\n\nevent_manager!(WinPreviewManager, WinPreviewEvent);\n\nimpl WinPreviewManager {\n    pub fn instance() -> &'static Self {\n        &WINDOWS_PREVIEWS\n    }\n\n    fn create() -> Self {\n        let manager = Self {\n            previews: SyncHashMap::new(),\n        };\n        manager.init().log_error();\n        manager\n    }\n\n    fn init(&self) -> Result<()> {\n        let windows = UserAppsManager::instance()\n            .interactable_windows\n            .map(|w| w.hwnd);\n\n        std::thread::spawn(move || {\n            for hwnd in windows {\n                let window = Window::from(hwnd);\n                WINDOWS_PREVIEWS.capture_window(&window).log_error();\n            }\n        });\n\n        UserAppsManager::subscribe(|e| match e {\n            UserAppWinEvent::Added(addr) => {\n                let window = Window::from(addr);\n                WINDOWS_PREVIEWS.capture_window(&window).log_error();\n            }\n            UserAppWinEvent::Updated(_) => {}\n            UserAppWinEvent::Removed(addr) => {\n                if let Some(preview) = WINDOWS_PREVIEWS.previews.remove(&addr) {\n                    if preview.path.exists() {\n                        std::fs::remove_file(&preview.path).log_error();\n                    }\n                    Self::send(WinPreviewEvent::Cleaned(addr));\n                }\n            }\n        });\n\n        HookManager::subscribe(|(event, window)| {\n            if !USER_APPS_MANAGER.contains_win(&window) {\n                return;\n            }\n\n            match event {\n                WinEvent::ObjectNameChange | WinEvent::SynDebouncedForegroundRectChange => {\n                    WINDOWS_PREVIEWS.capture_window(&window).log_error();\n                }\n                WinEvent::SystemMinimizeEnd => {\n                    let addr = window.address();\n                    std::thread::spawn(move || {\n                        std::thread::sleep(std::time::Duration::from_millis(300));\n                        let window = Window::from(addr);\n                        WINDOWS_PREVIEWS.capture_window(&window).log_error();\n                    });\n                }\n                _ => {}\n            }\n        });\n\n        Ok(())\n    }\n\n    fn capture_window(&self, window: &Window) -> Result<()> {\n        if window.is_minimized() {\n            return Ok(());\n        }\n\n        let addr = window.address();\n        log::trace!(\"capturing window ({addr:x})\");\n\n        let buf = capture_window(window.address()).map_err(|_| \"Failed to capture window\")?;\n        let image = RgbaImage::from_raw(buf.width, buf.height, buf.pixels)\n            .ok_or(\"Failed to create image\")?;\n\n        let image_hash = image_to_hash(&image);\n        let image = DynamicImage::ImageRgba8(image);\n\n        let box_shadow = WindowsApi::shadow_rect(window.hwnd())?;\n        let image = image.crop_imm(\n            box_shadow.left.unsigned_abs(),\n            box_shadow.top.unsigned_abs(),\n            buf.width - (box_shadow.left + box_shadow.right).unsigned_abs(),\n            buf.height - (box_shadow.top + box_shadow.bottom).unsigned_abs(),\n        );\n\n        let path_to_save = SEELEN_COMMON.app_temp_dir().join(format!(\"{addr}.webp\"));\n        image.save_with_format(&path_to_save, image::ImageFormat::WebP)?;\n\n        self.previews.upsert(\n            addr,\n            UserAppWindowPreview {\n                hash: image_hash,\n                path: path_to_save,\n                width: image.width(),\n                height: image.height(),\n            },\n        );\n        Self::send(WinPreviewEvent::Captured(addr));\n        Ok(())\n    }\n}\n\nfn image_to_hash(icon_image: &image::RgbaImage) -> String {\n    use std::hash::{Hash, Hasher};\n    let mut hasher = std::hash::DefaultHasher::new();\n    icon_image.as_raw().hash(&mut hasher);\n    format!(\"{:x}\", hasher.finish())\n}\n"
  },
  {
    "path": "src/background/modules/apps/application/windows.rs",
    "content": "use std::sync::atomic::Ordering;\n\nuse seelen_core::{state::AppExtraFlag, system_state::UserAppWindow};\nuse windows::Win32::UI::WindowsAndMessaging::{\n    WS_CHILD, WS_EX_APPWINDOW, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_MINIMIZEBOX,\n};\n\nuse crate::{\n    hook::HookManager,\n    modules::apps::application::{UserAppWinEvent, UserAppsManager, USER_APPS_MANAGER},\n    state::application::FULL_STATE,\n    utils::spawn_named_thread,\n    windows_api::{\n        event_window::IS_INTERACTIVE_SESSION,\n        window::{event::WinEvent, Window},\n        WindowEnumerator, WindowsApi,\n    },\n};\n\nimpl UserAppsManager {\n    pub(super) fn init_listing_app_windows() -> Vec<UserAppWindow> {\n        let mut initial = Vec::new();\n        let _ = WindowEnumerator::new().for_each(|window| {\n            if is_interactable_window(&window) {\n                initial.push(window.to_serializable());\n            }\n        });\n\n        HookManager::subscribe(|(event, window)| Self::on_win_event(event, window));\n\n        spawn_named_thread(\"InteractableWindowsRevalidator\", || loop {\n            std::thread::sleep(std::time::Duration::from_millis(2000));\n\n            // Pause when session is not interactive to reduce CPU usage\n            if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                continue;\n            }\n\n            Self::instance().interactable_windows.retain(|w| {\n                let window = Window::from(w.hwnd);\n                if window.is_interactable_and_not_hidden() {\n                    true\n                } else {\n                    Self::send(UserAppWinEvent::Removed(window.address()));\n                    false\n                }\n            });\n        });\n\n        initial\n    }\n\n    fn on_win_event(event: WinEvent, window: Window) {\n        let mut is_interactable = USER_APPS_MANAGER.contains_win(&window);\n\n        match event {\n            WinEvent::SystemForeground => {\n                if is_interactable {\n                    let hwnd = window.address();\n                    USER_APPS_MANAGER.interactable_windows.sort_by(|a, b| {\n                        if a.hwnd == hwnd {\n                            std::cmp::Ordering::Less\n                        } else if b.hwnd == hwnd {\n                            std::cmp::Ordering::Greater\n                        } else {\n                            std::cmp::Ordering::Equal\n                        }\n                    });\n                    Self::send(UserAppWinEvent::Updated(window.address()));\n                }\n            }\n            WinEvent::ObjectCreate | WinEvent::ObjectShow => {\n                if !is_interactable && is_interactable_window(&window) {\n                    USER_APPS_MANAGER.add_win(&window);\n                    Self::send(UserAppWinEvent::Added(window.address()));\n                }\n            }\n            WinEvent::ObjectNameChange | WinEvent::ObjectParentChange => {\n                let was_interactable = is_interactable;\n                is_interactable = is_interactable_window(&window);\n                match (was_interactable, is_interactable) {\n                    (false, true) => {\n                        USER_APPS_MANAGER.add_win(&window);\n                        Self::send(UserAppWinEvent::Added(window.address()));\n                    }\n                    (true, false) => {\n                        USER_APPS_MANAGER.remove_win(&window);\n                        Self::send(UserAppWinEvent::Removed(window.address()));\n                    }\n                    _ => {}\n                }\n\n                // re-check for UWP apps that on creation starts without a parent\n                if event == WinEvent::ObjectParentChange {\n                    if let Some(parent) = window.parent() {\n                        if !USER_APPS_MANAGER.contains_win(&parent)\n                            && parent.is_interactable_and_not_hidden()\n                        {\n                            USER_APPS_MANAGER.add_win(&parent);\n                            Self::send(UserAppWinEvent::Added(parent.address()));\n                        }\n                    }\n                }\n            }\n            WinEvent::ObjectHide => {\n                // UWP ApplicationFrameHosts are always hidden on minimize\n                if is_interactable && !window.is_frame().unwrap_or(false) {\n                    USER_APPS_MANAGER.remove_win(&window);\n                    Self::send(UserAppWinEvent::Removed(window.address()));\n                }\n            }\n            WinEvent::ObjectDestroy => {\n                if is_interactable {\n                    USER_APPS_MANAGER.remove_win(&window);\n                    Self::send(UserAppWinEvent::Removed(window.address()));\n                }\n            }\n            _ => {}\n        }\n\n        // update cases on UserAppWindow\n        if is_interactable\n            && matches!(\n                event,\n                WinEvent::ObjectNameChange\n                    | WinEvent::SystemMinimizeStart\n                    | WinEvent::SystemMinimizeEnd\n                    | WinEvent::SynDebouncedForegroundRectChange\n                    | WinEvent::SyntheticFullscreenStart\n                    | WinEvent::SyntheticFullscreenEnd\n                    | WinEvent::SyntheticMonitorChanged\n            )\n        {\n            USER_APPS_MANAGER.interactable_windows.for_each(|w| {\n                if w.hwnd == window.address() {\n                    *w = window.to_serializable();\n                }\n            });\n            Self::send(UserAppWinEvent::Updated(window.address()));\n        }\n    }\n}\n\n/// The idea with this module is contain all the logic under the filteriong of windows\n/// that can be considered as applications windows, it means windows that are interactable\n/// for the users.\n///\n/// As windows properties can change, this should be reevaluated on every change.\npub fn is_interactable_window(window: &Window) -> bool {\n    // It must be a visible Window and not cloaked\n    if !window.is_window() || !window.is_visible() || window.is_cloaked() {\n        return false;\n    }\n\n    // ignore windows without a title, these are not intended to be shown to users (comonly are invisible windows)\n    let title = window.title();\n    if title.is_empty() {\n        return false;\n    }\n\n    // this class is used for edge tabs to be shown as independent windows on alt + tab\n    // this only applies when the new tab is created it is binded to explorer.exe for some reason\n    // maybe we can search/learn more about edge tabs later.\n    // fix: https://github.com/eythaann/Seelen-UI/issues/83\n    if window.class() == \"Windows.Internal.Shell.TabProxyWindow\" {\n        return false;\n    }\n\n    let style = WindowsApi::get_styles(window.hwnd());\n    let ex_style = WindowsApi::get_ex_styles(window.hwnd());\n\n    if !ex_style.contains(WS_EX_APPWINDOW) {\n        // It must not be owned by another window\n        if style.contains(WS_CHILD) || window.owner().is_some() {\n            return false;\n        }\n\n        // Discard tool windows without WS_EX_APPWINDOW\n        if ex_style.contains(WS_EX_TOOLWINDOW) || ex_style.contains(WS_EX_NOACTIVATE) {\n            return false;\n        }\n    }\n\n    let process = window.process();\n    // unmanageable window, these probably are system processes\n    if process.open_limited_handle().is_err() {\n        return false;\n    }\n\n    // Internal behaviour for seelen ui widgets:\n    // Discard unminimizable windows (they have no caption/title bar)\n    if !style.contains(WS_MINIMIZEBOX) && process.is_seelen() {\n        return false;\n    }\n\n    if process.is_frozen().unwrap_or(false) {\n        return false;\n    }\n\n    let to_validate = match window.get_frame_creator() {\n        Ok(None) => return false, // not found\n        Ok(Some(creator)) => creator,\n        Err(_) => *window, // window is not a frame\n    };\n\n    let guard = FULL_STATE.load();\n    match guard.get_app_config_by_window(to_validate.hwnd()) {\n        Ok(Some(config)) => {\n            if config.options.contains(&AppExtraFlag::NoInteractive) {\n                return false;\n            }\n        }\n        Ok(_) => {}\n        Err(err) => {\n            log::error!(\"Error getting app config: {err}\");\n            return false;\n        }\n    }\n\n    true\n}\n"
  },
  {
    "path": "src/background/modules/apps/infrastructure.rs",
    "content": "use std::{collections::HashMap, sync::Once};\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{FocusedApp, UserAppWindow, UserAppWindowPreview},\n};\nuse windows::Win32::UI::Shell::{IShellDispatch6, Shell};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::Result,\n    modules::apps::application::{previews::WinPreviewManager, UserAppsManager},\n    windows_api::{input::Mouse, window::Window, Com},\n};\n\n/// Lazy initialization wrapper that registers Tauri events on first access\n/// This keeps Tauri logic separate from system logic while ensuring lazy initialization\nfn get_apps_manager() -> &'static UserAppsManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        UserAppsManager::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::UserAppWindowsChanged,\n                UserAppsManager::instance().interactable_windows.to_vec(),\n            );\n        });\n\n        WinPreviewManager::subscribe(|_| {\n            emit_to_webviews(\n                SeelenEvent::UserAppWindowsPreviewsChanged,\n                WinPreviewManager::instance().previews.to_hash_map(),\n            );\n        });\n    });\n    UserAppsManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_focused_app() -> FocusedApp {\n    Window::get_foregrounded().as_focused_app_information()\n}\n\n#[tauri::command(async)]\npub fn get_mouse_position() -> [i32; 2] {\n    let point = Mouse::get_cursor_pos().unwrap_or_default();\n    [point.x, point.y]\n}\n\n#[tauri::command(async)]\npub fn get_key_state(key: win_hotkeys::VKey) -> bool {\n    use win_hotkeys::state::KeyboardState;\n    use win_hotkeys::VKey;\n\n    if key == VKey::Menu {\n        return KeyboardState::async_is_key_down(VKey::Menu.to_vk_code())\n            || KeyboardState::async_is_key_down(VKey::LMenu.to_vk_code())\n            || KeyboardState::async_is_key_down(VKey::RMenu.to_vk_code());\n    }\n\n    KeyboardState::async_is_key_down(key.to_vk_code())\n}\n\n#[tauri::command(async)]\npub fn get_user_app_windows() -> Vec<UserAppWindow> {\n    get_apps_manager().interactable_windows.to_vec()\n}\n\n#[tauri::command(async)]\npub fn get_user_app_windows_previews() -> HashMap<isize, UserAppWindowPreview> {\n    get_apps_manager();\n    WinPreviewManager::instance().previews.to_hash_map()\n}\n\n/// This function is called show_desktop but acts more like minimize_all\n#[tauri::command(async)]\npub fn show_desktop() -> Result<()> {\n    Com::run_with_context(|| {\n        let shell: IShellDispatch6 = Com::create_instance(&Shell)?;\n        unsafe { shell.ToggleDesktop()? };\n        Ok(())\n    })\n}\n"
  },
  {
    "path": "src/background/modules/apps/mod.rs",
    "content": "pub mod application;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/media/devices/application.rs",
    "content": "use std::sync::LazyLock;\n\nuse windows::Win32::{\n    Devices::FunctionDiscovery::PKEY_Device_FriendlyName,\n    Foundation::PROPERTYKEY,\n    Media::Audio::{\n        eAll, eCapture, eCommunications, eMultimedia, eRender, EDataFlow, ERole,\n        Endpoints::{\n            IAudioEndpointVolume, IAudioEndpointVolumeCallback, IAudioEndpointVolumeCallback_Impl,\n        },\n        IAudioSessionControl, IAudioSessionControl2, IAudioSessionEvents, IAudioSessionEvents_Impl,\n        IAudioSessionManager2, IAudioSessionNotification, IAudioSessionNotification_Impl,\n        IMMDevice, IMMDeviceEnumerator, IMMEndpoint, IMMNotificationClient,\n        IMMNotificationClient_Impl, ISimpleAudioVolume, MMDeviceEnumerator, DEVICE_STATE_ACTIVE,\n    },\n    System::Com::{CLSCTX_ALL, STGM_READ},\n};\nuse windows_core::Interface;\n\nuse crate::{\n    error::{ErrorMap, Result, ResultLogExt},\n    event_manager,\n    utils::{lock_free::SyncHashMap, pcwstr},\n    windows_api::{process::Process, Com},\n};\n\nuse super::domain::{MediaDevice, MediaDeviceSession, MediaDeviceType};\n\npub struct DevicesManager {\n    inputs: SyncHashMap<String, MediaDevice>,\n    outputs: SyncHashMap<String, MediaDevice>,\n\n    device_enumerator: IMMDeviceEnumerator,\n    mm_notification_client: IMMNotificationClient,\n}\n\n#[derive(Debug, Clone)]\npub enum DevicesEvent {\n    DeviceAdded(String),\n    DeviceRemoved(String),\n    DefaultDeviceChanged {\n        flow: EDataFlow,\n        role: ERole,\n        device_id: String,\n    },\n    DeviceVolumeChanged {\n        device_id: String,\n        volume: f32,\n        muted: bool,\n    },\n    SessionAdded {\n        device_id: String,\n        session: MediaDeviceSession,\n    },\n    SessionRemoved {\n        device_id: String,\n        session_id: String,\n    },\n    SessionVolumeChanged {\n        device_id: String,\n        session_id: String,\n        volume: f32,\n        muted: bool,\n    },\n}\n\nunsafe impl Send for DevicesEvent {}\n\nunsafe impl Send for DevicesManager {}\nunsafe impl Sync for DevicesManager {}\n\nevent_manager!(DevicesManager, DevicesEvent);\n\nimpl DevicesManager {\n    fn new() -> Result<Self> {\n        Ok(Self {\n            inputs: SyncHashMap::new(),\n            outputs: SyncHashMap::new(),\n            device_enumerator: Com::create_instance(&MMDeviceEnumerator)?,\n            mm_notification_client: DevicesManagerEvents.into(),\n        })\n    }\n\n    fn init(&mut self) -> Result<()> {\n        unsafe {\n            let collection = self\n                .device_enumerator\n                .EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE)?;\n\n            for idx in 0..collection.GetCount()? {\n                // log instead propagate error to avoid panic if just some device fail to load\n                self.load_device(&collection.Item(idx)?).log_error();\n            }\n\n            self.device_enumerator\n                .RegisterEndpointNotificationCallback(&self.mm_notification_client)?;\n        }\n\n        let eid = Self::subscribe(|event| {\n            DevicesManager::instance().process_event(event).log_error();\n        });\n        Self::set_event_handler_priority(&eid, 1);\n\n        Ok(())\n    }\n\n    pub fn instance() -> &'static Self {\n        static MANAGER: LazyLock<DevicesManager> = LazyLock::new(|| {\n            let mut manager = DevicesManager::new().expect(\"Failed to create devices manager\");\n            manager.init().log_error();\n            manager\n        });\n        &MANAGER\n    }\n\n    pub fn get_inputs(&self) -> Vec<MediaDevice> {\n        self.inputs.values()\n    }\n\n    pub fn get_outputs(&self) -> Vec<MediaDevice> {\n        self.outputs.values()\n    }\n\n    pub fn get_raw_device(&self, device_id: &str) -> Option<IMMDevice> {\n        unsafe { self.device_enumerator.GetDevice(pcwstr(device_id)) }.ok()\n    }\n\n    pub fn set_volume_level(\n        &self,\n        device_id: String,\n        session_id: Option<String>,\n        mut level: f32,\n    ) -> Result<()> {\n        level = level.clamp(0.0, 1.0); // ensure valid value\n\n        let cb = |device: &mut MediaDevice| {\n            match &session_id {\n                Some(session_id) => {\n                    if let Some(session) = device.session(session_id) {\n                        unsafe {\n                            let volume: ISimpleAudioVolume = session.controls.cast()?;\n                            volume.SetMasterVolume(level, &windows::core::GUID::zeroed())?;\n                        }\n                    }\n                }\n                None => unsafe {\n                    device\n                        .volume_endpoint\n                        .SetMasterVolumeLevelScalar(level, &windows::core::GUID::zeroed())?;\n                },\n            }\n            Ok(())\n        };\n\n        if let Some(result) = self.inputs.get(&device_id, cb) {\n            return result;\n        }\n        if let Some(result) = self.outputs.get(&device_id, cb) {\n            return result;\n        }\n        Ok(())\n    }\n\n    pub fn toggle_mute(&self, device_id: String, session_id: Option<String>) -> Result<()> {\n        let cb = |device: &mut MediaDevice| {\n            match &session_id {\n                Some(session_id) => {\n                    if let Some(session) = device.session(session_id) {\n                        unsafe {\n                            let volume: ISimpleAudioVolume = session.controls.cast()?;\n                            volume.SetMute(\n                                !volume.GetMute()?.as_bool(),\n                                &windows::core::GUID::zeroed(),\n                            )?;\n                        }\n                    }\n                }\n                None => unsafe {\n                    device.volume_endpoint.SetMute(\n                        !device.volume_endpoint.GetMute()?.as_bool(),\n                        &windows::core::GUID::zeroed(),\n                    )?;\n                },\n            }\n            Ok(())\n        };\n\n        if let Some(result) = self.inputs.get(&device_id, cb) {\n            return result;\n        }\n        if let Some(result) = self.outputs.get(&device_id, cb) {\n            return result;\n        }\n        Ok(())\n    }\n\n    fn load_device(&self, device: &IMMDevice) -> Result<()> {\n        let mut device = unsafe { MediaDevice::load(device)? };\n        device.is_default_multimedia = self.is_default_device(&device, eMultimedia);\n        device.is_default_communications = self.is_default_device(&device, eCommunications);\n        match device.r#type {\n            MediaDeviceType::Input => self.inputs.upsert(device.id.clone(), device),\n            MediaDeviceType::Output => self.outputs.upsert(device.id.clone(), device),\n        };\n        Ok(())\n    }\n\n    fn remove_device(&self, device_id: &str) {\n        if let Some(device) = self.inputs.remove(device_id) {\n            device.release();\n        };\n        if let Some(device) = self.outputs.remove(device_id) {\n            device.release();\n        };\n    }\n\n    fn is_default_device(&self, device: &MediaDevice, role: ERole) -> bool {\n        let dataflow = match device.r#type {\n            MediaDeviceType::Input => eCapture,\n            MediaDeviceType::Output => eRender,\n        };\n        unsafe {\n            self.device_enumerator\n                .GetDefaultAudioEndpoint(dataflow, role)\n                .and_then(|d| d.GetId())\n                .map(|id| id.to_hstring() == device.id)\n                .unwrap_or(false)\n        }\n    }\n\n    fn process_event(&self, event: DevicesEvent) -> Result<()> {\n        match &event {\n            DevicesEvent::DeviceAdded(device_id) => {\n                if let Some(device) = self.get_raw_device(device_id) {\n                    self.load_device(&device)?;\n                }\n            }\n            DevicesEvent::DeviceRemoved(device_id) => {\n                self.remove_device(device_id);\n            }\n            DevicesEvent::DefaultDeviceChanged {\n                flow,\n                role,\n                device_id,\n            } => {\n                let devices = if *flow == eCapture {\n                    &self.inputs\n                } else {\n                    &self.outputs\n                };\n\n                devices.for_each(|(_, device)| {\n                    if *role == eMultimedia {\n                        device.is_default_multimedia = device.id == *device_id;\n                    } else if *role == eCommunications {\n                        device.is_default_communications = device.id == *device_id;\n                    }\n                });\n            }\n            DevicesEvent::DeviceVolumeChanged {\n                device_id,\n                volume,\n                muted,\n            } => {\n                let cb = |device: &mut MediaDevice| {\n                    device.volume = *volume;\n                    device.muted = *muted;\n                };\n                self.inputs.get(device_id, cb);\n                self.outputs.get(device_id, cb);\n            }\n            DevicesEvent::SessionAdded { device_id, session } => {\n                let cb = |device: &mut MediaDevice| {\n                    device.sessions.push(session.clone());\n                };\n                self.inputs.get(device_id, cb);\n                self.outputs.get(device_id, cb);\n            }\n            DevicesEvent::SessionRemoved {\n                device_id,\n                session_id,\n            } => {\n                let cb = |device: &mut MediaDevice| {\n                    device.remove_session(session_id);\n                };\n                self.inputs.get(device_id, cb);\n                self.outputs.get(device_id, cb);\n            }\n            DevicesEvent::SessionVolumeChanged {\n                device_id,\n                session_id,\n                volume,\n                muted,\n            } => {\n                let cb = |device: &mut MediaDevice| {\n                    if let Some(session) = device.session_mut(session_id) {\n                        session.volume = *volume;\n                        session.muted = *muted;\n                    }\n                };\n                self.inputs.get(device_id, cb);\n                self.outputs.get(device_id, cb);\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl MediaDevice {\n    pub unsafe fn load(raw_device: &IMMDevice) -> Result<Self> {\n        let device_id = raw_device.GetId()?.to_string()?;\n        let volume_endpoint: IAudioEndpointVolume = raw_device.Activate(CLSCTX_ALL, None)?;\n        let session_manager: IAudioSessionManager2 = raw_device.Activate(CLSCTX_ALL, None)?;\n\n        let mut sessions = Vec::new();\n        let enumerator = session_manager.GetSessionEnumerator()?;\n        for session_idx in 0..enumerator.GetCount()? {\n            let session: IAudioSessionControl2 = enumerator.GetSession(session_idx)?.cast()?;\n            match MediaDeviceSession::load(session, &device_id) {\n                Ok(session) => sessions.push(session),\n                Err(e) => log::error!(\"Failed to load session: {e:?}\"),\n            }\n        }\n\n        let properties = raw_device.OpenPropertyStore(STGM_READ)?;\n        let data_flow = if raw_device.cast::<IMMEndpoint>()?.GetDataFlow()? == eCapture {\n            MediaDeviceType::Input\n        } else {\n            MediaDeviceType::Output\n        };\n\n        let volume_callback = IAudioEndpointVolumeCallback::from(MediaDeviceEventHandler {\n            device_id: device_id.clone(),\n        });\n\n        let session_created_callback = IAudioSessionNotification::from(MediaDeviceEventHandler {\n            device_id: device_id.clone(),\n        });\n\n        let device = MediaDevice {\n            id: device_id.clone(),\n            name: properties.GetValue(&PKEY_Device_FriendlyName)?.to_string(),\n            r#type: data_flow,\n            is_default_multimedia: false, // unset, parent should set this\n            is_default_communications: false, // unset, parent should set this\n            sessions,\n            volume: volume_endpoint.GetMasterVolumeLevelScalar()?,\n            muted: volume_endpoint.GetMute()?.as_bool(),\n            volume_endpoint,\n            volume_callback,\n            session_manager,\n            session_created_callback,\n        };\n\n        device\n            .volume_endpoint\n            .RegisterControlChangeNotify(&device.volume_callback)?;\n        device\n            .session_manager\n            .RegisterSessionNotification(&device.session_created_callback)?;\n        Ok(device)\n    }\n}\n\nimpl MediaDeviceSession {\n    pub unsafe fn load(session: IAudioSessionControl2, device_id: &str) -> Result<Self> {\n        let session_id = session.GetSessionIdentifier()?.to_string()?;\n        let volume: ISimpleAudioVolume = session.cast()?;\n        let proccess = Process::from_id(session.GetProcessId()?);\n\n        let events_callback = IAudioSessionEvents::from(MediaSessionEventHandler {\n            device_id: device_id.to_owned(),\n            session_id: session_id.clone(),\n        });\n\n        let session = MediaDeviceSession {\n            id: session_id,\n            instance_id: session.GetSessionInstanceIdentifier()?.to_string()?,\n            process_id: proccess.id(),\n            name: proccess\n                .program_display_name()\n                .unwrap_or_else(|_| \"???\".to_string()),\n            icon_path: proccess.program_path().ok(),\n            is_system: session.IsSystemSoundsSession().0 == 0,\n            volume: volume.GetMasterVolume()?,\n            muted: volume.GetMute()?.as_bool(),\n            controls: session,\n            events_callback,\n        };\n\n        session\n            .controls\n            .RegisterAudioSessionNotification(&session.events_callback)?;\n        Ok(session)\n    }\n}\n\nimpl Drop for DevicesManager {\n    fn drop(&mut self) {\n        self.inputs.clear();\n        self.outputs.clear();\n        unsafe {\n            self.device_enumerator\n                .UnregisterEndpointNotificationCallback(&self.mm_notification_client)\n                .log_error();\n        }\n    }\n}\n\n#[windows_core::implement(IMMNotificationClient)]\nstruct DevicesManagerEvents;\n\nimpl IMMNotificationClient_Impl for DevicesManagerEvents_Impl {\n    fn OnDefaultDeviceChanged(\n        &self,\n        flow: EDataFlow,\n        role: ERole,\n        device_id: &windows_core::PCWSTR,\n    ) -> windows_core::Result<()> {\n        DevicesManager::send(DevicesEvent::DefaultDeviceChanged {\n            flow,\n            role,\n            device_id: unsafe { device_id.to_string()? },\n        });\n        Ok(())\n    }\n\n    fn OnDeviceAdded(&self, device_id: &windows_core::PCWSTR) -> windows_core::Result<()> {\n        DevicesManager::send(DevicesEvent::DeviceAdded(unsafe { device_id.to_string()? }));\n        Ok(())\n    }\n\n    fn OnDeviceRemoved(&self, device_id: &windows_core::PCWSTR) -> windows_core::Result<()> {\n        DevicesManager::send(DevicesEvent::DeviceRemoved(unsafe {\n            device_id.to_string()?\n        }));\n        Ok(())\n    }\n\n    fn OnDeviceStateChanged(\n        &self,\n        device_id: &windows_core::PCWSTR,\n        new_device_state: windows::Win32::Media::Audio::DEVICE_STATE,\n    ) -> windows_core::Result<()> {\n        let device_id = unsafe { device_id.to_string()? };\n        let tx = DevicesManager::event_tx();\n        match new_device_state {\n            DEVICE_STATE_ACTIVE => tx.send(DevicesEvent::DeviceAdded(device_id)),\n            _ => tx.send(DevicesEvent::DeviceRemoved(device_id)),\n        }\n        .wrap_error()\n        .log_error();\n        Ok(())\n    }\n\n    fn OnPropertyValueChanged(\n        &self,\n        _device_id: &windows_core::PCWSTR,\n        _key: &PROPERTYKEY,\n    ) -> windows_core::Result<()> {\n        Ok(())\n    }\n}\n\n#[windows::core::implement(IAudioEndpointVolumeCallback, IAudioSessionNotification)]\npub struct MediaDeviceEventHandler {\n    device_id: String,\n}\n\nimpl IAudioEndpointVolumeCallback_Impl for MediaDeviceEventHandler_Impl {\n    fn OnNotify(\n        &self,\n        data: *mut windows::Win32::Media::Audio::AUDIO_VOLUME_NOTIFICATION_DATA,\n    ) -> windows_core::Result<()> {\n        if let Some(data) = unsafe { data.as_ref() } {\n            let tx = DevicesManager::event_tx();\n            let result = tx.send(DevicesEvent::DeviceVolumeChanged {\n                device_id: self.device_id.clone(),\n                volume: data.fMasterVolume,\n                muted: data.bMuted.as_bool(),\n            });\n            result.log_error();\n        }\n        Ok(())\n    }\n}\n\nimpl IAudioSessionNotification_Impl for MediaDeviceEventHandler_Impl {\n    fn OnSessionCreated(\n        &self,\n        new_session: windows_core::Ref<'_, IAudioSessionControl>,\n    ) -> windows_core::Result<()> {\n        if let Some(new_session) = new_session.as_ref() {\n            let new_session: IAudioSessionControl2 = new_session.cast()?;\n            match unsafe { MediaDeviceSession::load(new_session, &self.device_id) } {\n                Ok(session) => {\n                    let tx = DevicesManager::event_tx();\n                    tx.send(DevicesEvent::SessionAdded {\n                        device_id: self.device_id.clone(),\n                        session,\n                    })\n                    .log_error();\n                }\n                Err(e) => log::error!(\"Failed to load session: {e:?}\"),\n            }\n        }\n        Ok(())\n    }\n}\n\n#[windows_core::implement(IAudioSessionEvents)]\npub struct MediaSessionEventHandler {\n    device_id: String,\n    session_id: String,\n}\n\nimpl IAudioSessionEvents_Impl for MediaSessionEventHandler_Impl {\n    fn OnChannelVolumeChanged(\n        &self,\n        _channel_count: u32,\n        _new_channel_volume_array: *const f32,\n        _changed_channel: u32,\n        _event_context: *const windows::core::GUID,\n    ) -> windows::core::Result<()> {\n        Ok(())\n    }\n\n    fn OnDisplayNameChanged(\n        &self,\n        _new_display_name: &windows::core::PCWSTR,\n        _event_context: *const windows::core::GUID,\n    ) -> windows::core::Result<()> {\n        Ok(())\n    }\n\n    fn OnGroupingParamChanged(\n        &self,\n        _new_grouping_param: *const windows::core::GUID,\n        _event_context: *const windows::core::GUID,\n    ) -> windows::core::Result<()> {\n        Ok(())\n    }\n\n    fn OnIconPathChanged(\n        &self,\n        _new_icon_path: &windows::core::PCWSTR,\n        _event_context: *const windows::core::GUID,\n    ) -> windows::core::Result<()> {\n        Ok(())\n    }\n\n    fn OnSessionDisconnected(\n        &self,\n        _disconnect_reason: windows::Win32::Media::Audio::AudioSessionDisconnectReason,\n    ) -> windows::core::Result<()> {\n        let tx = DevicesManager::event_tx();\n        let result = tx.send(DevicesEvent::SessionRemoved {\n            device_id: self.device_id.clone(),\n            session_id: self.session_id.clone(),\n        });\n        result.log_error();\n        Ok(())\n    }\n\n    fn OnSimpleVolumeChanged(\n        &self,\n        new_volume: f32,\n        new_mute: windows::core::BOOL,\n        _event_context: *const windows::core::GUID,\n    ) -> windows::core::Result<()> {\n        let tx = DevicesManager::event_tx();\n        let result = tx.send(DevicesEvent::SessionVolumeChanged {\n            device_id: self.device_id.clone(),\n            session_id: self.session_id.clone(),\n            volume: new_volume,\n            muted: new_mute.as_bool(),\n        });\n        result.log_error();\n        Ok(())\n    }\n\n    fn OnStateChanged(\n        &self,\n        _new_state: windows::Win32::Media::Audio::AudioSessionState,\n    ) -> windows::core::Result<()> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/media/devices/domain.rs",
    "content": "use std::path::PathBuf;\n\nuse serde::Serialize;\nuse windows::Win32::Media::Audio::{\n    Endpoints::{IAudioEndpointVolume, IAudioEndpointVolumeCallback},\n    IAudioSessionControl2, IAudioSessionEvents, IAudioSessionManager2, IAudioSessionNotification,\n};\n\n#[derive(Debug, Clone, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaDeviceSession {\n    #[serde(skip)]\n    pub controls: IAudioSessionControl2,\n    #[serde(skip)]\n    pub events_callback: IAudioSessionEvents,\n    // ---\n    pub id: String,\n    pub instance_id: String,\n    pub process_id: u32,\n    pub name: String,\n    pub icon_path: Option<PathBuf>,\n    pub is_system: bool,\n    pub volume: f32,\n    pub muted: bool,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum MediaDeviceType {\n    Input,\n    Output,\n}\n\n#[derive(Debug, Clone, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct MediaDevice {\n    #[serde(skip)]\n    pub volume_endpoint: IAudioEndpointVolume,\n    #[serde(skip)]\n    pub volume_callback: IAudioEndpointVolumeCallback,\n    #[serde(skip)]\n    pub session_manager: IAudioSessionManager2,\n    #[serde(skip)]\n    pub session_created_callback: IAudioSessionNotification,\n    // ---\n    pub id: String,\n    pub name: String,\n    pub r#type: MediaDeviceType,\n    pub is_default_multimedia: bool,\n    pub is_default_communications: bool,\n    pub sessions: Vec<MediaDeviceSession>,\n    pub volume: f32,\n    pub muted: bool,\n}\n\nimpl MediaDevice {\n    pub fn session(&self, session_id: &str) -> Option<&MediaDeviceSession> {\n        self.sessions.iter().find(|s| s.id == session_id)\n    }\n\n    pub fn session_mut(&mut self, session_id: &str) -> Option<&mut MediaDeviceSession> {\n        self.sessions.iter_mut().find(|s| s.id == session_id)\n    }\n\n    pub fn remove_session(&mut self, session_id: &str) {\n        for session in std::mem::take(&mut self.sessions) {\n            if session.id == session_id {\n                session.release();\n                continue;\n            }\n            self.sessions.push(session);\n        }\n    }\n\n    pub fn release(&self) {\n        unsafe {\n            use crate::error::ResultLogExt;\n            self.volume_endpoint\n                .UnregisterControlChangeNotify(&self.volume_callback)\n                .log_error();\n            self.session_manager\n                .UnregisterSessionNotification(&self.session_created_callback)\n                .log_error();\n        };\n    }\n}\n\nimpl MediaDeviceSession {\n    pub fn release(self) {\n        unsafe {\n            use crate::error::ResultLogExt;\n            self.controls\n                .UnregisterAudioSessionNotification(&self.events_callback)\n                .log_error();\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/modules/media/devices/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::handlers::SeelenEvent;\n\nuse crate::{app::emit_to_webviews, error::Result, windows_api::WindowsApi};\n\nuse super::DevicesManager;\n\nfn get_devices_manager() -> &'static DevicesManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        DevicesManager::subscribe(|_event| {\n            let manager = DevicesManager::instance();\n            let inputs = manager.get_inputs();\n            let outputs = manager.get_outputs();\n\n            emit_to_webviews(SeelenEvent::MediaDevices, (&inputs, &outputs));\n            emit_to_webviews(SeelenEvent::MediaInputs, &inputs);\n            emit_to_webviews(SeelenEvent::MediaOutputs, &outputs);\n        });\n    });\n    DevicesManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_media_devices() -> Result<(serde_json::Value, serde_json::Value)> {\n    let manager = get_devices_manager();\n    let inputs = serde_json::to_value(manager.get_inputs())?;\n    let outputs = serde_json::to_value(manager.get_outputs())?;\n    Ok((inputs, outputs))\n}\n\n#[tauri::command(async)]\npub async fn media_set_default_device(id: String, role: String) -> Result<()> {\n    get_devices_manager();\n    WindowsApi::set_default_audio_device(&id, &role)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn media_toggle_mute(device_id: String, session_id: Option<String>) -> Result<()> {\n    let manager = get_devices_manager();\n    manager.toggle_mute(device_id, session_id)\n}\n\n#[tauri::command(async)]\npub fn set_volume_level(device_id: String, session_id: Option<String>, level: f32) -> Result<()> {\n    let manager = get_devices_manager();\n    manager.set_volume_level(device_id, session_id, level)\n}\n"
  },
  {
    "path": "src/background/modules/media/devices/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n\npub use application::DevicesManager;\n"
  },
  {
    "path": "src/background/modules/media/mod.rs",
    "content": "pub mod devices;\npub mod players;\n"
  },
  {
    "path": "src/background/modules/media/players/application.rs",
    "content": "use std::{ffi::OsStr, path::PathBuf, sync::LazyLock, time::Duration};\n\nuse seelen_core::system_state::{MediaPlayerOwner, MediaPlayerTimeline};\nuse windows::{\n    Foundation::TypedEventHandler,\n    Media::Control::{\n        GlobalSystemMediaTransportControlsSession,\n        GlobalSystemMediaTransportControlsSessionManager,\n        GlobalSystemMediaTransportControlsSessionPlaybackStatus,\n        GlobalSystemMediaTransportControlsSessionTimelineProperties,\n        MediaPropertiesChangedEventArgs, PlaybackInfoChangedEventArgs, SessionsChangedEventArgs,\n        TimelinePropertiesChangedEventArgs,\n    },\n};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    modules::start::application::StartMenuManager,\n    utils::{icon_extractor::request_icon_extraction_from_umid, lock_free::SyncHashMap},\n    windows_api::{types::AppUserModelId, WindowsApi},\n};\n\nuse super::domain::{MediaPlayer, MediaPlayerSession};\n\n/// Grace period (ms) a player must have been absent before it is released.\nconst REMOVAL_GRACE_MS: u128 = 1500;\n/// Delay (ms) before sending CleanRequested after marking a player for removal.\n/// Must be strictly greater than REMOVAL_GRACE_MS so elapsed > threshold when processed.\nconst REMOVAL_SCHEDULE_MS: u64 = 2000;\n\nfn timeline_from_raw(\n    raw: GlobalSystemMediaTransportControlsSessionTimelineProperties,\n) -> windows_core::Result<MediaPlayerTimeline> {\n    // TimeSpan is in ticks of 100ns\n    Ok(MediaPlayerTimeline {\n        start: raw.StartTime()?.Duration.saturating_mul(100),\n        end: raw.EndTime()?.Duration.saturating_mul(100),\n        position: raw.Position()?.Duration.saturating_mul(100),\n        min_seek: raw.MinSeekTime()?.Duration.saturating_mul(100),\n        max_seek: raw.MaxSeekTime()?.Duration.saturating_mul(100),\n        last_updated_time: raw.LastUpdatedTime()?.UniversalTime,\n    })\n}\n\n#[derive(Debug, Clone)]\npub enum PlayersEvent {\n    PlayerAdded(GlobalSystemMediaTransportControlsSession),\n    PlayerRemoved(String),\n    CleanRequested,\n    PropertiesChanged {\n        id: String,\n        title: String,\n        author: String,\n        thumbnail: Option<PathBuf>,\n    },\n    PlaybackStatusChanged {\n        id: String,\n        playing: bool,\n    },\n    TimelineChanged {\n        id: String,\n        timeline: MediaPlayerTimeline,\n    },\n}\n\nunsafe impl Send for PlayersEvent {}\n\nevent_manager!(PlayersManager, PlayersEvent);\n\npub struct PlayersManager {\n    playing: SyncHashMap<String, MediaPlayer>,\n    sessions: SyncHashMap<String, MediaPlayerSession>,\n    manager: GlobalSystemMediaTransportControlsSessionManager,\n}\n\nunsafe impl Send for PlayersManager {}\nunsafe impl Sync for PlayersManager {}\n\nimpl PlayersManager {\n    fn new() -> Result<Self> {\n        let manager = GlobalSystemMediaTransportControlsSessionManager::RequestAsync()?.join()?;\n\n        Ok(Self {\n            playing: SyncHashMap::new(),\n            sessions: SyncHashMap::new(),\n            manager,\n        })\n    }\n\n    pub fn instance() -> &'static Self {\n        static MANAGER: LazyLock<PlayersManager> = LazyLock::new(|| {\n            let mut manager = PlayersManager::new().expect(\"Failed to create players manager\");\n            manager.init().log_error();\n            manager\n        });\n        &MANAGER\n    }\n\n    fn init(&mut self) -> Result<()> {\n        for session in self.manager.GetSessions()? {\n            self.load_session(session)?;\n        }\n        self.update_recommended_player();\n\n        self.manager\n            .SessionsChanged(&TypedEventHandler::new(Self::on_media_players_changed))?;\n\n        let eid = Self::subscribe(|event| {\n            PlayersManager::instance().process_event(&event).log_error();\n            // Only re-evaluate the recommended player when the session list or\n            // playback status changes. Timeline/property events fire very frequently\n            // (every position tick) and do not affect which player is \"current\".\n            match event {\n                PlayersEvent::PlayerAdded(_)\n                | PlayersEvent::PlayerRemoved(_)\n                | PlayersEvent::CleanRequested\n                | PlayersEvent::PlaybackStatusChanged { .. } => {\n                    PlayersManager::instance().update_recommended_player();\n                }\n                _ => {}\n            }\n        });\n        Self::set_event_handler_priority(&eid, 1);\n\n        Ok(())\n    }\n\n    pub fn get_media_player(\n        &self,\n        umid: &str,\n    ) -> Option<GlobalSystemMediaTransportControlsSession> {\n        self.sessions.get(umid, |session| session.session.clone())\n    }\n\n    pub fn get_recommended_player_id(&self) -> Result<String> {\n        Ok(self\n            .manager\n            .GetCurrentSession()?\n            .SourceAppUserModelId()?\n            .to_string_lossy())\n    }\n\n    pub fn get_playing_sessions(&self) -> Vec<MediaPlayer> {\n        self.playing.values()\n    }\n\n    fn process_event(&self, event: &PlayersEvent) -> Result<()> {\n        match event {\n            PlayersEvent::PlayerAdded(session) => {\n                let id = session.SourceAppUserModelId()?.to_string_lossy();\n                let already_exists = self\n                    .playing\n                    .get(&id, |player| {\n                        player.removed_at = None;\n                    })\n                    .is_some();\n\n                if already_exists {\n                    return Ok(());\n                }\n\n                // load_session could fail with 0x80070015 \"The device is not ready.\"\n                // when trying to load a recently added player so we retry a few times\n                let mut attempts = 0;\n                while session.TryGetMediaPropertiesAsync()?.join().is_err() && attempts < 15 {\n                    attempts += 1;\n                    std::thread::sleep(Duration::from_millis(10));\n                }\n                self.load_session(session.clone())?;\n            }\n            PlayersEvent::PlayerRemoved(id) => {\n                self.playing.get(id, |player| {\n                    // Only start the removal timer if not already pending removal.\n                    // Avoids resetting the timer on repeated SessionsChanged events.\n                    if player.removed_at.is_none() {\n                        player.removed_at = Some(std::time::Instant::now());\n                        std::thread::spawn(move || {\n                            // Must sleep longer than the REMOVAL_GRACE_MS threshold so that\n                            // by the time CleanRequested is processed, elapsed > threshold.\n                            std::thread::sleep(Duration::from_millis(REMOVAL_SCHEDULE_MS));\n                            PlayersManager::send(PlayersEvent::CleanRequested);\n                        });\n                    }\n                });\n            }\n            PlayersEvent::CleanRequested => {\n                self.release_pending_players()?;\n            }\n            PlayersEvent::PropertiesChanged {\n                id,\n                title,\n                author,\n                thumbnail,\n            } => {\n                self.playing.get(id, |player| {\n                    player.base.title = title.clone();\n                    player.base.author = author.clone();\n                    player.base.thumbnail = thumbnail.clone();\n                });\n            }\n            PlayersEvent::PlaybackStatusChanged { id, playing } => {\n                self.playing.get(id, |player| {\n                    player.base.playing = *playing;\n                });\n            }\n            PlayersEvent::TimelineChanged { id, timeline } => {\n                self.playing.get(id, |player| {\n                    player.base.timeline = timeline.clone();\n                });\n            }\n        }\n        Ok(())\n    }\n\n    fn update_recommended_player(&self) {\n        if let Ok(recommended) = self.get_recommended_player_id() {\n            self.playing.for_each(|(_, player)| {\n                player.base.default = player.base.umid == recommended;\n            });\n        }\n    }\n\n    fn load_session(&self, session: GlobalSystemMediaTransportControlsSession) -> Result<()> {\n        let source_app_umid: AppUserModelId =\n            session.SourceAppUserModelId()?.to_string_lossy().into();\n        let properties = session.TryGetMediaPropertiesAsync()?.join()?;\n\n        let timeline = session.GetTimelineProperties()?;\n        let playback_info = session.GetPlaybackInfo()?;\n        let status = playback_info.PlaybackStatus()?;\n\n        let display_name = match &source_app_umid {\n            AppUserModelId::Appx(umid) => WindowsApi::get_uwp_app_info(umid)?\n                .DisplayInfo()?\n                .DisplayName()?\n                .to_string_lossy(),\n            AppUserModelId::PropertyStore(umid) => {\n                let start = StartMenuManager::instance();\n                let shortcut = start.get_by_file_umid(umid);\n                match shortcut {\n                    Some(shortcut) => shortcut\n                        .path\n                        .file_stem()\n                        .unwrap_or_else(|| OsStr::new(\"Unknown\"))\n                        .to_string_lossy()\n                        .to_string(),\n                    None => \"Unknown\".to_string(),\n                }\n            }\n        };\n\n        // pre-extraction to avoid flickering on the ui\n        request_icon_extraction_from_umid(&source_app_umid);\n        self.playing.upsert(\n            source_app_umid.to_string(),\n            MediaPlayer {\n                base: seelen_core::system_state::MediaPlayer {\n                    umid: source_app_umid.to_string(),\n                    title: properties.Title().unwrap_or_default().to_string_lossy(),\n                    author: properties.Artist().unwrap_or_default().to_string_lossy(),\n                    owner: MediaPlayerOwner { name: display_name },\n                    thumbnail: properties\n                        .Thumbnail()\n                        .ok()\n                        .and_then(|stream| WindowsApi::extract_thumbnail_from_ref(stream).ok()),\n                    playing: status\n                        == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Playing,\n                    timeline: timeline_from_raw(timeline)?,\n                    default: false,\n                },\n                removed_at: None,\n            },\n        );\n\n        let wrapped_session = MediaPlayerSession::create(\n            session,\n            &TypedEventHandler::new(Self::on_media_player_properties_changed),\n            &TypedEventHandler::new(Self::on_media_player_playback_changed),\n            &TypedEventHandler::new(Self::on_media_player_timeline_changed),\n        )?;\n\n        self.sessions\n            .upsert(source_app_umid.to_string(), wrapped_session);\n        Ok(())\n    }\n\n    fn release_pending_players(&self) -> Result<()> {\n        let mut ids_to_release = Vec::new();\n        let mut needs_reschedule = false;\n        self.playing.for_each(|(id, player)| {\n            if let Some(removed_at) = player.removed_at {\n                if removed_at.elapsed().as_millis() > REMOVAL_GRACE_MS {\n                    ids_to_release.push(id.clone());\n                } else {\n                    // Timer fired early (e.g. another event triggered CleanRequested);\n                    // reschedule so this player is eventually cleaned up.\n                    needs_reschedule = true;\n                }\n            }\n        });\n        for id in ids_to_release {\n            self.release_session(&id)?;\n        }\n        if needs_reschedule {\n            std::thread::spawn(|| {\n                std::thread::sleep(Duration::from_millis(REMOVAL_SCHEDULE_MS));\n                PlayersManager::send(PlayersEvent::CleanRequested);\n            });\n        }\n        Ok(())\n    }\n\n    fn release_session(&self, player_id: &str) -> Result<()> {\n        // Remove session from map - Drop trait will handle event unregistration\n        self.sessions.remove(player_id);\n        self.playing.remove(player_id);\n        Ok(())\n    }\n\n    fn on_media_players_changed(\n        session_manager: windows_core::Ref<GlobalSystemMediaTransportControlsSessionManager>,\n        _args: windows_core::Ref<SessionsChangedEventArgs>,\n    ) -> windows_core::Result<()> {\n        if let Some(session_manager) = session_manager.as_ref() {\n            let mut current_list = Vec::new();\n            PlayersManager::instance().playing.for_each(|(_, session)| {\n                if session.removed_at.is_none() {\n                    current_list.push(session.base.umid.clone());\n                }\n            });\n\n            let tx = PlayersManager::event_tx();\n            for session in session_manager.GetSessions()? {\n                let id = session.SourceAppUserModelId()?.to_string();\n                if !current_list.contains(&id) {\n                    let _ = tx.send(PlayersEvent::PlayerAdded(session));\n                }\n                current_list.retain(|x| *x != id);\n            }\n\n            for id in current_list {\n                let _ = tx.send(PlayersEvent::PlayerRemoved(id));\n            }\n        }\n        Ok(())\n    }\n\n    fn on_media_player_properties_changed(\n        session: windows_core::Ref<GlobalSystemMediaTransportControlsSession>,\n        _args: windows_core::Ref<MediaPropertiesChangedEventArgs>,\n    ) -> windows_core::Result<()> {\n        if let Some(session) = session.as_ref() {\n            let id = session.SourceAppUserModelId()?.to_string();\n            let properties = session.TryGetMediaPropertiesAsync()?.join()?;\n            let tx = PlayersManager::event_tx();\n            // Use unwrap_or_default / .ok() to match load_session's resilience:\n            // some players (e.g. system sounds) do not expose title, artist, or thumbnail.\n            let result = tx.send(PlayersEvent::PropertiesChanged {\n                id,\n                title: properties.Title().unwrap_or_default().to_string_lossy(),\n                author: properties.Artist().unwrap_or_default().to_string_lossy(),\n                thumbnail: properties\n                    .Thumbnail()\n                    .ok()\n                    .and_then(|s| WindowsApi::extract_thumbnail_from_ref(s).ok()),\n            });\n            result.log_error();\n        }\n        Ok(())\n    }\n\n    fn on_media_player_playback_changed(\n        session: windows_core::Ref<GlobalSystemMediaTransportControlsSession>,\n        _args: windows_core::Ref<PlaybackInfoChangedEventArgs>,\n    ) -> windows_core::Result<()> {\n        if let Some(session) = session.as_ref() {\n            let playback = session.GetPlaybackInfo()?;\n            let player_id = session.SourceAppUserModelId()?;\n            let tx = PlayersManager::event_tx();\n            let event = PlayersEvent::PlaybackStatusChanged {\n                id: player_id.to_string(),\n                playing: playback.PlaybackStatus()?\n                    == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Playing,\n            };\n            tx.send(event).log_error();\n        }\n        Ok(())\n    }\n\n    fn on_media_player_timeline_changed(\n        session: windows_core::Ref<GlobalSystemMediaTransportControlsSession>,\n        _args: windows_core::Ref<TimelinePropertiesChangedEventArgs>,\n    ) -> windows_core::Result<()> {\n        if let Some(session) = session.as_ref() {\n            let tx = PlayersManager::event_tx();\n            let event = PlayersEvent::TimelineChanged {\n                id: session.SourceAppUserModelId()?.to_string(),\n                timeline: timeline_from_raw(session.GetTimelineProperties()?)?,\n            };\n            tx.send(event).log_error();\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/media/players/domain.rs",
    "content": "use std::time::Instant;\n\nuse windows::{\n    Foundation::TypedEventHandler,\n    Media::Control::{\n        GlobalSystemMediaTransportControlsSession, MediaPropertiesChangedEventArgs,\n        PlaybackInfoChangedEventArgs, TimelinePropertiesChangedEventArgs,\n    },\n};\n\nuse crate::error::{Result, ResultLogExt};\n\n/// Wrapper for GlobalSystemMediaTransportControlsSession that automatically\n/// registers and unregisters event handlers on creation and drop\npub struct MediaPlayerSession {\n    pub session: GlobalSystemMediaTransportControlsSession,\n    properties_token: i64,\n    playback_token: i64,\n    timeline_token: i64,\n}\n\nimpl MediaPlayerSession {\n    pub fn create(\n        session: GlobalSystemMediaTransportControlsSession,\n        properties_handler: &TypedEventHandler<\n            GlobalSystemMediaTransportControlsSession,\n            MediaPropertiesChangedEventArgs,\n        >,\n        playback_handler: &TypedEventHandler<\n            GlobalSystemMediaTransportControlsSession,\n            PlaybackInfoChangedEventArgs,\n        >,\n        timeline_handler: &TypedEventHandler<\n            GlobalSystemMediaTransportControlsSession,\n            TimelinePropertiesChangedEventArgs,\n        >,\n    ) -> Result<Self> {\n        // WinRT event tokens are i64, not EventRegistrationToken\n        let properties_token = session.MediaPropertiesChanged(properties_handler)?;\n        let playback_token = session.PlaybackInfoChanged(playback_handler)?;\n        let timeline_token = session.TimelinePropertiesChanged(timeline_handler)?;\n\n        Ok(Self {\n            session,\n            properties_token,\n            playback_token,\n            timeline_token,\n        })\n    }\n}\n\nimpl Drop for MediaPlayerSession {\n    fn drop(&mut self) {\n        self.session\n            .RemoveMediaPropertiesChanged(self.properties_token)\n            .log_error();\n        self.session\n            .RemovePlaybackInfoChanged(self.playback_token)\n            .log_error();\n        self.session\n            .RemoveTimelinePropertiesChanged(self.timeline_token)\n            .log_error();\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct MediaPlayer {\n    pub base: seelen_core::system_state::MediaPlayer,\n    pub removed_at: Option<Instant>,\n}\n\nimpl serde::Serialize for MediaPlayer {\n    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.base.serialize(serializer)\n    }\n}\n"
  },
  {
    "path": "src/background/modules/media/players/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::handlers::SeelenEvent;\n\nuse crate::{app::emit_to_webviews, error::Result};\n\nuse super::{domain::MediaPlayer, PlayersManager};\n\nfn get_players_manager() -> &'static PlayersManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        PlayersManager::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::MediaSessions,\n                PlayersManager::instance().get_playing_sessions(),\n            );\n        });\n    });\n    PlayersManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_media_sessions() -> Result<Vec<MediaPlayer>> {\n    let manager = get_players_manager();\n    Ok(manager.get_playing_sessions())\n}\n\n#[tauri::command(async)]\npub fn media_next(id: String) -> Result<()> {\n    let manager = get_players_manager();\n    if let Some(session) = manager.get_media_player(&id) {\n        let success = session.TrySkipNextAsync()?.join()?;\n        if !success {\n            return Err(\"failed to skip next\".into());\n        }\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn media_prev(id: String) -> Result<()> {\n    let manager = get_players_manager();\n    if let Some(session) = manager.get_media_player(&id) {\n        let success = session.TrySkipPreviousAsync()?.join()?;\n        if !success {\n            return Err(\"failed to skip previous\".into());\n        }\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn media_toggle_play_pause(id: String) -> Result<()> {\n    let manager = get_players_manager();\n    if let Some(session) = manager.get_media_player(&id) {\n        let success = session.TryTogglePlayPauseAsync()?.join()?;\n        if !success {\n            return Err(\"failed to toggle play\".into());\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/modules/media/players/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n\npub use application::PlayersManager;\n"
  },
  {
    "path": "src/background/modules/media/readme.md",
    "content": "# Understanding this hell\n\n- Media Device can be input device (microphone) or output (speaker)\n- Media Device has sessions that are the apps using the device\n  - sessions can have one or more streams.\n  - sessions can implement transport protocol to indicate what is playing, we call this Media Player.\n- Media Device has channels as example 2 channels (left and right), or 5.1, 7.1 etc.\n\nnow each device have master volume > volume per session > volume per stream > volume per channel\n"
  },
  {
    "path": "src/background/modules/mod.rs",
    "content": "pub mod apps;\npub mod media;\npub mod monitors;\npub mod network;\npub mod notifications;\npub mod power;\npub mod radios;\npub mod start;\npub mod system;\npub mod system_settings;\npub mod system_tray;\npub mod trash_bin;\npub mod user;\n\n#[macro_export]\nmacro_rules! event_manager {\n    ($name:ident, $event:ty) => {\n        static CHANNEL: std::sync::LazyLock<(\n            crossbeam_channel::Sender<$event>,\n            crossbeam_channel::Receiver<$event>,\n        )> = std::sync::LazyLock::new(crossbeam_channel::unbounded);\n\n        static SUBSCRIBERS: std::sync::LazyLock<\n            parking_lot::RwLock<Vec<(String, Box<dyn Fn($event) + Sync + Send + 'static>, u32)>>,\n        > = std::sync::LazyLock::new(|| parking_lot::RwLock::new(Vec::new()));\n\n        static THREAD_INIT: std::sync::Once = std::sync::Once::new();\n\n        #[allow(dead_code)]\n        impl $name {\n            fn _init_thread() {\n                THREAD_INIT.call_once(|| {\n                    let rx = CHANNEL.1.clone();\n                    std::thread::spawn(move || {\n                        for event in rx {\n                            let subscribers = SUBSCRIBERS.read();\n                            for (_id, callback, _) in subscribers.iter() {\n                                callback(event.clone());\n                            }\n                        }\n                    });\n                });\n            }\n\n            pub fn event_tx() -> crossbeam_channel::Sender<$event> {\n                Self::_init_thread();\n                CHANNEL.0.clone()\n            }\n\n            pub fn send(event: $event) {\n                Self::_init_thread();\n                if let Err(e) = CHANNEL.0.send(event) {\n                    log::error!(\"Failed to send event: {e}\");\n                }\n            }\n\n            pub fn subscribe<F>(callback: F) -> String\n            where\n                F: Fn($event) + Sync + Send + 'static,\n            {\n                let id = uuid::Uuid::new_v4().to_string();\n                SUBSCRIBERS\n                    .write()\n                    .push((id.clone(), Box::new(callback), 0));\n                id\n            }\n\n            pub fn set_event_handler_priority(id: &str, priority: u32) {\n                let mut subscribers = SUBSCRIBERS.write();\n                for s in subscribers.iter_mut() {\n                    if s.0 == id {\n                        s.2 = priority;\n                        break;\n                    }\n                }\n                // Higher priority subscribers will be called first\n                subscribers.sort_by(|a, b| b.2.cmp(&a.2));\n            }\n\n            pub fn unsubscribe(id: &str) {\n                let mut subscribers = SUBSCRIBERS.write();\n                subscribers.retain(|(i, _, _)| i != id);\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "src/background/modules/monitors/application.rs",
    "content": "use std::{collections::HashMap, sync::LazyLock};\n\nuse seelen_core::system_state::MonitorId;\nuse windows::{\n    Devices::Display::Core::{\n        DisplayManager, DisplayManagerChangedEventArgs, DisplayManagerDisabledEventArgs,\n        DisplayManagerEnabledEventArgs, DisplayManagerOptions,\n        DisplayManagerPathsFailedOrInvalidatedEventArgs,\n    },\n    Foundation::TypedEventHandler,\n    Win32::UI::WindowsAndMessaging::WM_DISPLAYCHANGE,\n};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    utils::lock_free::SyncHashMap,\n    windows_api::{event_window::subscribe_to_background_window, monitor::DisplayView},\n};\n\npub struct MonitorManager {\n    state_views: SyncHashMap<MonitorId, DisplayView>,\n    /// DisplayManager manages critical hardware so be sure to be correctly used, or will make the app crash.\n    /// https://learn.microsoft.com/en-us/uwp/api/windows.devices.display.core.displaymanager\n    display_manager: DisplayManager,\n    enabled_token: Option<i64>,\n    disabled_token: Option<i64>,\n    changed_token: Option<i64>,\n    paths_failed_or_invalidated_token: Option<i64>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum MonitorManagerEvent {\n    /// the id used is the view primary target id\n    ViewAdded(MonitorId),\n    /// the id used is the view primary target id\n    ViewRemoved(MonitorId),\n    ViewsChanged,\n}\n\nevent_manager!(MonitorManager, MonitorManagerEvent);\n\nimpl MonitorManager {\n    fn create() -> Result<MonitorManager> {\n        let display_manager = DisplayManager::Create(DisplayManagerOptions::None)?;\n        let state = display_manager\n            .TryReadCurrentStateForAllTargets()?\n            .State()?;\n\n        let mut state_views = HashMap::new();\n        for view in state.Views()? {\n            let view = DisplayView::from(view);\n            state_views.insert(view.primary_target()?.stable_id()?, view);\n        }\n\n        Ok(MonitorManager {\n            display_manager,\n            state_views: SyncHashMap::from(state_views),\n            enabled_token: None,\n            disabled_token: None,\n            changed_token: None,\n            paths_failed_or_invalidated_token: None,\n        })\n    }\n\n    pub fn instance() -> &'static MonitorManager {\n        static MONITOR_MANAGER: LazyLock<MonitorManager> = LazyLock::new(|| {\n            let mut m = MonitorManager::create().expect(\"Failed to create monitor manager\");\n            m.initialize().log_error();\n            m\n        });\n        &MONITOR_MANAGER\n    }\n\n    fn initialize(&mut self) -> Result<()> {\n        // DisplayManager.Start() requires subscribing to all events first\n        // See: https://learn.microsoft.com/en-us/uwp/api/windows.devices.display.core.displaymanager.start\n        self.enabled_token = self\n            .display_manager\n            .Enabled(&TypedEventHandler::new(Self::on_enabled))\n            .ok();\n\n        self.disabled_token = self\n            .display_manager\n            .Disabled(&TypedEventHandler::new(Self::on_disabled))\n            .ok();\n\n        self.changed_token = self\n            .display_manager\n            .Changed(&TypedEventHandler::new(Self::on_changed))\n            .ok();\n\n        self.paths_failed_or_invalidated_token = self\n            .display_manager\n            .PathsFailedOrInvalidated(&TypedEventHandler::new(\n                Self::on_paths_failed_or_invalidated,\n            ))\n            .ok();\n\n        self.display_manager.Start()?;\n        subscribe_to_background_window(|event, _w_param, _l_param| {\n            if event == WM_DISPLAYCHANGE {\n                log::debug!(\"Displays changed\");\n                Self::send(MonitorManagerEvent::ViewsChanged);\n                Self::check_for_display_changes().log_error();\n            }\n            Ok(())\n        });\n        Ok(())\n    }\n\n    // Is recommended that subscribers re-enumerate all targets and state in this call,\n    // since the system display stack could be left in any state before this event is raised.\n    fn on_enabled(\n        _sender: windows_core::Ref<DisplayManager>,\n        args: windows_core::Ref<DisplayManagerEnabledEventArgs>,\n    ) -> windows_core::Result<()> {\n        log::trace!(\"DisplayManager enabled\");\n\n        // Critical!: app will crash if this is not set\n        if let Some(args) = args.as_ref() {\n            args.SetHandled(true)?;\n        }\n        Ok(())\n    }\n\n    // Is recommended that subscribers attempt to clean up when Disabled is invoked.\n    // Most display APIs will fail while the session display stack is disabled.\n    fn on_disabled(\n        _sender: windows_core::Ref<DisplayManager>,\n        args: windows_core::Ref<DisplayManagerDisabledEventArgs>,\n    ) -> windows_core::Result<()> {\n        log::trace!(\"DisplayManager disabled\");\n\n        // Critical!: app will crash if this is not set\n        if let Some(args) = args.as_ref() {\n            args.SetHandled(true)?;\n        }\n        Ok(())\n    }\n\n    // this only detects changes on the display adapters like connect/disconnect of displays\n    fn on_changed(\n        _sender: windows_core::Ref<DisplayManager>,\n        args: windows_core::Ref<DisplayManagerChangedEventArgs>,\n    ) -> windows_core::Result<()> {\n        log::trace!(\"DisplayManager changed\");\n        Self::check_for_display_changes().log_error();\n\n        // Critical!: app will crash if this is not set\n        if let Some(args) = args.as_ref() {\n            args.SetHandled(true)?;\n        }\n        Ok(())\n    }\n\n    fn on_paths_failed_or_invalidated(\n        _sender: windows_core::Ref<DisplayManager>,\n        args: windows_core::Ref<DisplayManagerPathsFailedOrInvalidatedEventArgs>,\n    ) -> windows_core::Result<()> {\n        log::trace!(\"DisplayManager paths failed or invalidated\");\n        // Treat this as a change event\n        Self::check_for_display_changes().log_error();\n\n        // Critical!: app will crash if this is not set\n        if let Some(args) = args.as_ref() {\n            args.SetHandled(true)?;\n        }\n        Ok(())\n    }\n\n    fn check_for_display_changes() -> windows_core::Result<()> {\n        let current_state = Self::instance()\n            .display_manager\n            .TryReadCurrentStateForAllTargets()?\n            .State()?;\n\n        let mut current_views = HashMap::new();\n        for view in current_state.Views()? {\n            let view = DisplayView::from(view);\n            let id = match view.primary_target().and_then(|t| t.stable_id()) {\n                Ok(id) => id,\n                Err(_) => continue,\n            };\n            current_views.insert(id, view);\n        }\n\n        let mut old_views = Self::instance().state_views.to_hash_map();\n\n        // new monitors were added\n        for id in current_views.keys() {\n            if old_views.remove(id).is_none() {\n                Self::send(MonitorManagerEvent::ViewAdded(id.clone()));\n            }\n        }\n\n        // residuals were removed/disconnected\n        for (id, _) in old_views {\n            Self::send(MonitorManagerEvent::ViewRemoved(id));\n        }\n\n        Self::instance().state_views.replace(current_views);\n        Ok(())\n    }\n\n    pub fn get_display_view_for_target(&self, target_id: &MonitorId) -> Result<DisplayView> {\n        let state = self.display_manager.TryReadCurrentStateForAllTargets()?;\n        let state = state.State()?;\n\n        for target in self.display_manager.GetCurrentTargets()? {\n            if target.StableMonitorId()?.to_string_lossy() == target_id.0 {\n                return Ok(state.GetViewForTarget(&target)?.into());\n            }\n        }\n        Err(\"Can not find display view for target\".into())\n    }\n\n    pub fn read_all_views(&self) -> Result<Vec<DisplayView>> {\n        let state = self.display_manager.TryReadCurrentStateForAllTargets()?;\n        let state = state.State()?;\n        Ok(state.Views()?.into_iter().map(DisplayView::from).collect())\n    }\n}\n\nimpl Drop for MonitorManager {\n    fn drop(&mut self) {\n        self.display_manager.Stop().log_error();\n\n        if let Some(enabled_token) = self.enabled_token {\n            self.display_manager\n                .RemoveEnabled(enabled_token)\n                .log_error();\n        }\n\n        if let Some(disabled_token) = self.disabled_token {\n            self.display_manager\n                .RemoveDisabled(disabled_token)\n                .log_error();\n        }\n\n        if let Some(changed_token) = self.changed_token {\n            self.display_manager\n                .RemoveChanged(changed_token)\n                .log_error();\n        }\n\n        if let Some(paths_failed_or_invalidated_token) = self.paths_failed_or_invalidated_token {\n            self.display_manager\n                .RemovePathsFailedOrInvalidated(paths_failed_or_invalidated_token)\n                .log_error();\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/modules/monitors/brightness/application.rs",
    "content": "use std::sync::LazyLock;\n\nuse wmi::WMIConnection;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    modules::monitors::brightness::domain::{\n        WmiMonitorBrightness, WmiMonitorBrightnessEvent, WmiMonitorBrightnessMethods,\n        WmiSetBrightnessPayload,\n    },\n    utils::lock_free::SyncVec,\n};\n\n#[derive(Debug, Clone)]\npub enum BrightnessManagerEvent {\n    Changed(Vec<WmiMonitorBrightness>),\n}\n\nevent_manager!(BrightnessManager, BrightnessManagerEvent);\n\npub struct BrightnessManager {\n    brightness: SyncVec<WmiMonitorBrightness>,\n}\n\nimpl BrightnessManager {\n    pub fn instance() -> &'static Self {\n        static INSTANCE: LazyLock<BrightnessManager> = LazyLock::new(|| {\n            let mut m = BrightnessManager::new();\n            m.init().log_error();\n            m\n        });\n        &INSTANCE\n    }\n\n    fn new() -> Self {\n        Self {\n            brightness: SyncVec::new(),\n        }\n    }\n\n    fn init(&mut self) -> Result<()> {\n        let wmi = WMIConnection::with_namespace_path(\"ROOT\\\\WMI\")?;\n\n        std::thread::spawn(move || {\n            let wmi = WMIConnection::with_namespace_path(\"ROOT\\\\WMI\")?;\n            for event in wmi.notification::<WmiMonitorBrightnessEvent>()? {\n                let Ok(_event) = event else {\n                    continue;\n                };\n\n                let brightness: Vec<WmiMonitorBrightness> = wmi.query()?;\n                BrightnessManager::instance()\n                    .brightness\n                    .replace(brightness.clone());\n                BrightnessManager::send(BrightnessManagerEvent::Changed(brightness));\n            }\n            Result::Ok(())\n        });\n\n        self.brightness = wmi.query::<WmiMonitorBrightness>()?.into();\n        Ok(())\n    }\n\n    pub fn get_all_brightness(&self) -> Vec<WmiMonitorBrightness> {\n        self.brightness.to_vec()\n    }\n\n    pub fn set_brightness(&self, instance_name: &str, level: u8) -> Result<()> {\n        let wmi = WMIConnection::with_namespace_path(\"ROOT\\\\WMI\")?;\n\n        let instances = wmi.query::<WmiMonitorBrightnessMethods>()?;\n\n        let obj = instances\n            .into_iter()\n            .find(|v| v.instance_name == instance_name)\n            .ok_or(\"Instance not found\")?;\n\n        wmi.exec_instance_method::<WmiMonitorBrightnessMethods, ()>(\n            obj.__path,\n            \"WmiSetBrightness\",\n            WmiSetBrightnessPayload {\n                timeout: 0,\n                brightness: level,\n            },\n        )?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/monitors/brightness/domain.rs",
    "content": "#![allow(dead_code, non_snake_case)]\n\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct WmiMonitorBrightness {\n    pub active: bool,\n    pub current_brightness: u8,\n    pub instance_name: String,\n    pub level: Vec<u8>,\n    pub levels: u32,\n}\n\n#[derive(Debug, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct WmiMonitorBrightnessEvent {\n    pub active: bool,\n    pub brightness: u8,\n    pub instance_name: String,\n}\n\n#[derive(Debug, Deserialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct WmiMonitorBrightnessMethods {\n    #[serde(rename = \"__Path\")]\n    pub __path: String,\n    pub active: bool,\n    pub instance_name: String,\n}\n\n#[derive(Debug, Serialize)]\n#[serde(rename_all = \"PascalCase\")]\npub struct WmiSetBrightnessPayload {\n    pub timeout: u32,\n    pub brightness: u8,\n}\n"
  },
  {
    "path": "src/background/modules/monitors/brightness/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{handlers::SeelenEvent, system_state::MonitorBrightness};\n\nuse crate::{app::emit_to_webviews, error::Result};\n\nuse super::application::{BrightnessManager, BrightnessManagerEvent};\n\nfn get_brightness_manager() -> &'static BrightnessManager {\n    static TAURI_BRIGHTNESS_REGISTRATION: Once = Once::new();\n    TAURI_BRIGHTNESS_REGISTRATION.call_once(|| {\n        BrightnessManager::subscribe(|event| match event {\n            BrightnessManagerEvent::Changed(brightness) => {\n                let brightness_data: Vec<MonitorBrightness> = brightness\n                    .into_iter()\n                    .map(|b| MonitorBrightness {\n                        instance_name: b.instance_name,\n                        current_brightness: b.current_brightness,\n                        levels: b.levels,\n                        available_levels: b.level,\n                        active: b.active,\n                    })\n                    .collect();\n\n                emit_to_webviews(\n                    SeelenEvent::SystemMonitorsBrightnessChanged,\n                    brightness_data,\n                );\n            }\n        });\n    });\n    BrightnessManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_all_monitors_brightness() -> Result<Vec<MonitorBrightness>> {\n    let manager = get_brightness_manager();\n    let brightness = manager.get_all_brightness();\n\n    Ok(brightness\n        .into_iter()\n        .map(|b| MonitorBrightness {\n            instance_name: b.instance_name,\n            current_brightness: b.current_brightness,\n            levels: b.levels,\n            available_levels: b.level,\n            active: b.active,\n        })\n        .collect())\n}\n\n#[tauri::command(async)]\npub fn set_monitor_brightness(instance_name: String, level: u8) -> Result<()> {\n    let manager = get_brightness_manager();\n    manager.set_brightness(&instance_name, level)\n}\n"
  },
  {
    "path": "src/background/modules/monitors/brightness/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/monitors/domain.rs",
    "content": "use seelen_core::system_state::PhysicalMonitor;\n\nuse crate::{error::AppError, windows_api::monitor::Monitor};\n\nimpl TryFrom<Monitor> for PhysicalMonitor {\n    type Error = AppError;\n    fn try_from(m: Monitor) -> Result<Self, Self::Error> {\n        let (id, name) = m.get_stable_info()?;\n        Ok(Self {\n            id,\n            name,\n            rect: m.rect()?,\n            scale_factor: m.scale_factor()?,\n            is_primary: m.is_primary(),\n        })\n    }\n}\n"
  },
  {
    "path": "src/background/modules/monitors/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{handlers::SeelenEvent, system_state::PhysicalMonitor};\n\nuse crate::{\n    app::emit_to_webviews, error::Result, modules::monitors::MonitorManager,\n    windows_api::MonitorEnumerator,\n};\n\nfn get_monitor_manager() -> &'static MonitorManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        let initial = _get_connected_monitors();\n        if let Ok(monitors) = initial {\n            log::debug!(\"Initial monitors: {monitors:#?}\");\n        }\n\n        MonitorManager::subscribe(|_event| {\n            if let Ok(monitors) = _get_connected_monitors() {\n                log::debug!(\"Monitors changed: {monitors:#?}\");\n                emit_to_webviews(SeelenEvent::SystemMonitorsChanged, monitors);\n            }\n        });\n    });\n    MonitorManager::instance()\n}\n\npub fn _get_connected_monitors() -> Result<Vec<PhysicalMonitor>> {\n    let mut monitors = Vec::new();\n    for m in MonitorEnumerator::enumerate_win32()? {\n        monitors.push(m.try_into()?);\n    }\n    Ok(monitors)\n}\n\n#[tauri::command(async)]\npub fn get_connected_monitors() -> Result<Vec<PhysicalMonitor>> {\n    get_monitor_manager();\n    _get_connected_monitors()\n}\n"
  },
  {
    "path": "src/background/modules/monitors/mod.rs",
    "content": "mod application;\npub mod brightness;\nmod domain;\npub mod infrastructure;\n\npub use application::*;\n"
  },
  {
    "path": "src/background/modules/network/application/mod.rs",
    "content": "pub mod scanner;\npub mod v2;\n\nuse std::{\n    env::temp_dir,\n    net::{IpAddr, UdpSocket},\n    sync::{atomic::Ordering, LazyLock},\n};\n\nuse seelen_core::system_state::{NetworkAdapter, WlanProfile};\nuse tauri_plugin_shell::ShellExt;\nuse windows::Win32::{\n    NetworkManagement::{\n        IpHelper::{\n            GetAdaptersAddresses, GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_INCLUDE_PREFIX,\n            IP_ADAPTER_ADDRESSES_LH,\n        },\n        WiFi::WlanDisconnect,\n    },\n    Networking::{\n        NetworkListManager::{\n            INetworkListManager, NetworkListManager, NLM_CONNECTIVITY,\n            NLM_CONNECTIVITY_IPV4_INTERNET, NLM_CONNECTIVITY_IPV6_INTERNET,\n        },\n        WinSock::AF_UNSPEC,\n    },\n};\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    event_manager,\n    utils::{pwsh::PwshScript, spawn_named_thread},\n    windows_api::{event_window::IS_INTERACTIVE_SESSION, Com},\n};\n\nuse super::domain::adapter_to_slu_net_adapter;\n\n#[derive(Debug, Clone)]\npub enum NetworkManagerEvent {\n    ConnectivityChanged {\n        connectivity: NLM_CONNECTIVITY,\n        ip: String,\n    },\n}\n\nevent_manager!(NetworkManager, NetworkManagerEvent);\n\ntrait IterFromRaw {\n    unsafe fn iter_from_raw(raw: *const IP_ADAPTER_ADDRESSES_LH) -> Result<Vec<NetworkAdapter>>;\n}\n\nimpl IterFromRaw for NetworkAdapter {\n    unsafe fn iter_from_raw(raw: *const IP_ADAPTER_ADDRESSES_LH) -> Result<Vec<NetworkAdapter>> {\n        let mut adapters = Vec::new();\n\n        let mut raw_adapter = raw;\n        while !raw_adapter.is_null() {\n            let adapter = &*raw_adapter;\n            adapters.push(adapter_to_slu_net_adapter(adapter)?);\n            raw_adapter = adapter.Next;\n        }\n\n        Ok(adapters)\n    }\n}\n\npub struct NetworkManager {}\n\nimpl NetworkManager {\n    pub fn instance() -> &'static Self {\n        static NETWORK_MANAGER: LazyLock<NetworkManager> = LazyLock::new(|| {\n            let manager = NetworkManager {};\n            NetworkManager::start_monitoring();\n            manager\n        });\n        &NETWORK_MANAGER\n    }\n\n    /* fn dot11_ssid_from_string(ssid: &str) -> Result<DOT11_SSID> {\n        if ssid.len() > 32 {\n            return Err(\"SSID too long (max 32 bytes)\".into());\n        }\n        // Convert the &str to a byte array\n        let mut ssid_bytes = [0u8; 32];\n        let ssid_slice = ssid.as_bytes();\n        let len = ssid_slice.len();\n        ssid_bytes[..len].copy_from_slice(ssid_slice);\n        Ok(DOT11_SSID {\n            uSSIDLength: len as u32,\n            ucSSID: ssid_bytes,\n        })\n    } */\n\n    /* pub fn connect(entry: &WlanBssEntry) -> Result<()> {\n        let profile = WindowsString::from(entry.ssid.unwrap_or_default());\n        let mut ssid = Self::dot11_ssid_from_string(&entry.ssid.unwrap_or_default())?;\n\n        let client_handle = Self::open_wlan()?;\n\n        let attributes = WLAN_CONNECTION_PARAMETERS {\n            wlanConnectionMode: wlan_connection_mode_auto,\n            strProfile: profile.as_pcwstr(),\n            pDot11Ssid: &mut ssid,\n\n            ..Default::default()\n        };\n\n        for interface in Self::get_wlan_interfaces(client_handle)? {\n            unsafe {\n                let result =\n                    WlanConnect(client_handle, &interface.InterfaceGuid, &attributes, None);\n                if result == 0 {\n                    break;\n                }\n            }\n        }\n\n        Ok(())\n    } */\n\n    pub fn disconnect_all() -> Result<()> {\n        let client_handle = Self::open_wlan()?;\n        for interface in Self::get_wlan_interfaces(*client_handle)? {\n            unsafe {\n                WlanDisconnect(*client_handle, &interface.InterfaceGuid, None);\n            }\n        }\n        Ok(())\n    }\n\n    pub fn get_adapters() -> Result<Vec<NetworkAdapter>> {\n        let adapters = unsafe {\n            let family = AF_UNSPEC.0 as u32;\n            let flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS;\n            let mut buffer_length = 0_u32;\n\n            // first call to get the buffer size\n            GetAdaptersAddresses(family, flags, None, None, &mut buffer_length);\n\n            let mut adapters_addresses: Vec<u8> = vec![0; buffer_length as usize];\n            GetAdaptersAddresses(\n                family,\n                flags,\n                None,\n                Some(adapters_addresses.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH),\n                &mut buffer_length,\n            );\n\n            let raw_adapter = adapters_addresses.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH;\n            NetworkAdapter::iter_from_raw(raw_adapter)?\n        };\n\n        Ok(adapters)\n    }\n\n    /// emit connectivity changes, always will emit the current state on registration\n    fn start_monitoring() {\n        spawn_named_thread(\"Network Manager\", move || {\n            let result: Result<()> = Com::run_with_context(|| {\n                let list_manager: INetworkListManager = Com::create_instance(&NetworkListManager)?;\n                let mut last_state = None;\n                let mut last_ip = None;\n\n                loop {\n                    std::thread::sleep(std::time::Duration::from_millis(5000));\n\n                    // Pause when session is not interactive to reduce CPU usage\n                    if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                        continue;\n                    }\n\n                    let current_state = unsafe { list_manager.GetConnectivity() }.ok();\n                    if let (Some(current_state), Some(last_state)) = (current_state, last_state) {\n                        if current_state != last_state {\n                            if let Ok(ip) = get_local_ip_address_base() {\n                                last_ip = Some(ip);\n                                NetworkManager::send(NetworkManagerEvent::ConnectivityChanged {\n                                    connectivity: current_state,\n                                    ip: ip.to_string(),\n                                });\n                            }\n                        } else if current_state.0 & NLM_CONNECTIVITY_IPV4_INTERNET.0\n                            == NLM_CONNECTIVITY_IPV4_INTERNET.0\n                            || current_state.0 & NLM_CONNECTIVITY_IPV6_INTERNET.0\n                                == NLM_CONNECTIVITY_IPV6_INTERNET.0\n                        {\n                            let current_ip = get_local_ip_address_base().ok();\n                            if let (Some(current_ip), Some(last_ip)) = (current_ip, last_ip) {\n                                if current_ip != last_ip {\n                                    NetworkManager::send(\n                                        NetworkManagerEvent::ConnectivityChanged {\n                                            connectivity: current_state,\n                                            ip: current_ip.to_string(),\n                                        },\n                                    );\n                                }\n                            }\n\n                            last_ip = current_ip;\n                        }\n                    }\n                    last_state = current_state;\n                }\n            });\n\n            log::warn!(\"Network loop finished: {result:?}\");\n        });\n    }\n\n    pub async fn get_wifi_profiles() -> Result<Vec<WlanProfile>> {\n        let path = PwshScript::new(include_str!(\"profiles.ps1\"))\n            .execute()\n            .await?;\n        let contents = std::fs::read_to_string(path)?;\n        let profiles: Vec<WlanProfile> = serde_json::from_str(&contents)?;\n        Ok(profiles)\n    }\n\n    pub async fn add_profile(ssid: &str, password: &str, hidden: bool) -> Result<()> {\n        log::trace!(\"Adding profile {ssid}\");\n        let profile_xml = if password.is_empty() {\n            // Be sure that xml is using tabs instead of spaces for indentation\n            include_str!(\"passwordless_profile.template.xml\")\n                .replace(\"{ssid}\", ssid)\n                .replace(\"{hidden}\", if hidden { \"true\" } else { \"false\" })\n        } else {\n            // Be sure that xml is using tabs instead of spaces for indentation\n            include_str!(\"profile.template.xml\")\n                .replace(\"{ssid}\", ssid)\n                .replace(\"{password}\", password)\n                .replace(\"{hidden}\", if hidden { \"true\" } else { \"false\" })\n        };\n\n        let profile_path = temp_dir().join(format!(\"slu-{ssid}-profile.xml\"));\n\n        std::fs::write(&profile_path, profile_xml)?;\n\n        let handle = get_app_handle();\n        let output = handle\n            .shell()\n            .command(\"netsh\")\n            .args([\n                \"wlan\",\n                \"add\",\n                \"profile\",\n                &format!(\"filename={}\", &profile_path.to_string_lossy()),\n            ])\n            .output()\n            .await?;\n\n        let result = if output.status.success() {\n            Ok(())\n        } else {\n            Err(output.into())\n        };\n\n        std::fs::remove_file(&profile_path)?;\n        result\n    }\n\n    pub async fn remove_profile(ssid: &str) -> Result<()> {\n        log::trace!(\"Removing profile {ssid}\");\n\n        let handle = get_app_handle();\n        let output = handle\n            .shell()\n            .command(\"netsh\")\n            .args([\"wlan\", \"delete\", \"profile\", &format!(\"name={ssid}\")])\n            .output()\n            .await?;\n\n        if output.status.success() {\n            Ok(())\n        } else {\n            Err(output.into())\n        }\n    }\n}\n\npub fn get_local_ip_address() -> Result<String> {\n    Ok(get_local_ip_address_base()?.to_string())\n}\nfn get_local_ip_address_base() -> Result<IpAddr> {\n    let socket = UdpSocket::bind(\"0.0.0.0:0\")?;\n    socket.connect(\"8.8.8.8:80\")?;\n    let local_addr = socket.local_addr()?;\n    Ok(local_addr.ip())\n}\n"
  },
  {
    "path": "src/background/modules/network/application/passwordless_profile.template.xml",
    "content": "<?xml version=\"1.0\"?>\n<WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">\n  <name>{ssid}</name>\n  <SSIDConfig>\n    <SSID>\n      <name>{ssid}</name>\n    </SSID>\n    <nonBroadcast>{hidden}</nonBroadcast>\n  </SSIDConfig>\n  <connectionType>ESS</connectionType>\n  <connectionMode>auto</connectionMode>\n  <MSM>\n    <security>\n      <authEncryption>\n        <authentication>open</authentication>\n        <encryption>none</encryption>\n        <useOneX>false</useOneX>\n      </authEncryption>\n    </security>\n  </MSM>\n</WLANProfile>\n"
  },
  {
    "path": "src/background/modules/network/application/profile.template.xml",
    "content": "<?xml version=\"1.0\"?>\n<WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">\n  <name>{ssid}</name>\n  <SSIDConfig>\n    <SSID>\n      <name>{ssid}</name>\n    </SSID>\n    <nonBroadcast>{hidden}</nonBroadcast>\n  </SSIDConfig>\n  <connectionType>ESS</connectionType>\n  <connectionMode>auto</connectionMode>\n  <MSM>\n    <security>\n      <authEncryption>\n        <authentication>WPA2PSK</authentication>\n        <encryption>AES</encryption>\n        <useOneX>false</useOneX>\n      </authEncryption>\n      <sharedKey>\n        <keyType>passPhrase</keyType>\n        <protected>false</protected>\n        <keyMaterial>{password}</keyMaterial>\n      </sharedKey>\n    </security>\n  </MSM>\n</WLANProfile>\n"
  },
  {
    "path": "src/background/modules/network/application/profiles.ps1",
    "content": "$tempFolder = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.Guid]::NewGuid().ToString())\nNew-Item -ItemType Directory -Path $tempFolder > $null\n\nnetsh wlan export profile folder=$tempFolder key=clear > $null\n\n$jsonOutputFile = [System.IO.Path]::Combine($tempFolder, \"wifi_profiles.json\")\n\n$wifiProfiles = Get-ChildItem -Path $tempFolder -Filter *.xml | ForEach-Object {\n    [xml]$xmlContent = Get-Content -Path $_.FullName\n    [PSCustomObject]@{\n        ProfileName = $xmlContent.WLANProfile.name\n        SSID = $xmlContent.WLANProfile.SSIDConfig.SSID.name\n        Authentication = $xmlContent.WLANProfile.MSM.Security.authEncryption.authentication\n        Encryption = $xmlContent.WLANProfile.MSM.Security.authEncryption.encryption\n        Password = $xmlContent.WLANProfile.MSM.Security.sharedKey.keyMaterial\n    }\n}\n\n$wifiProfiles | ConvertTo-Json | Set-Content -Path $jsonOutputFile\n\nWrite-Host $jsonOutputFile -NoNewline"
  },
  {
    "path": "src/background/modules/network/application/scanner.rs",
    "content": "use std::{\n    sync::atomic::{AtomicBool, Ordering},\n    time::Duration,\n};\n\nuse itertools::Itertools;\nuse seelen_core::system_state::WlanBssEntry;\nuse windows::{\n    core::GUID,\n    Win32::{\n        Foundation::HANDLE,\n        NetworkManagement::WiFi::{\n            dot11_BSS_type_any, wlan_interface_state_connected,\n            wlan_intf_opcode_current_connection, WlanCloseHandle, WlanEnumInterfaces,\n            WlanGetAvailableNetworkList2, WlanGetNetworkBssList, WlanGetProfile,\n            WlanGetProfileList, WlanOpenHandle, WlanQueryInterface, WlanScan,\n            DOT11_CAPABILITY_INFO_PRIVACY, WLAN_API_VERSION_2_0,\n            WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES,\n            WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,\n            WLAN_AVAILABLE_NETWORK_LIST_V2, WLAN_AVAILABLE_NETWORK_V2, WLAN_BSS_ENTRY,\n            WLAN_BSS_LIST, WLAN_CONNECTION_ATTRIBUTES, WLAN_INTERFACE_INFO,\n            WLAN_INTERFACE_INFO_LIST, WLAN_PROFILE_INFO_LIST,\n        },\n    },\n};\nuse windows_core::{PCWSTR, PWSTR};\n\nuse crate::error::Result;\n\nuse super::NetworkManager;\n\npub struct WlanHandle(HANDLE);\n\nimpl Drop for WlanHandle {\n    fn drop(&mut self) {\n        unsafe { WlanCloseHandle(self.0, None) };\n    }\n}\n\nimpl std::ops::Deref for WlanHandle {\n    type Target = HANDLE;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nfn from_raw_entry(entry: &WLAN_BSS_ENTRY) -> WlanBssEntry {\n    let ssid = String::from_utf8_lossy(&entry.dot11Ssid.ucSSID)\n        .replace(\"\\0\", \"\")\n        .to_string();\n\n    let ssid = if ssid.is_empty() { None } else { Some(ssid) };\n\n    let bssid = entry\n        .dot11Bssid\n        .iter()\n        .map(|b| format!(\"{b:02x}\"))\n        .join(\":\");\n\n    WlanBssEntry {\n        ssid,\n        bssid,\n        channel_frequency: entry.ulChCenterFrequency,\n        signal: entry.uLinkQuality,\n        connected: false,\n        connected_channel: false,\n        secured: entry.usCapabilityInformation as u32 & DOT11_CAPABILITY_INFO_PRIVACY\n            == DOT11_CAPABILITY_INFO_PRIVACY,\n        known: false,\n    }\n}\nstatic SCANNING: AtomicBool = AtomicBool::new(false);\n\nimpl NetworkManager {\n    pub fn open_wlan() -> Result<WlanHandle> {\n        let mut client_handle = HANDLE::default();\n        let mut negotiated_version = 0;\n\n        let result = unsafe {\n            WlanOpenHandle(\n                WLAN_API_VERSION_2_0,\n                None,\n                &mut negotiated_version,\n                &mut client_handle,\n            )\n        };\n\n        if result != 0 {\n            return Err(format!(\"Failed to open Wlan, error code: {result}\").into());\n        }\n\n        Ok(WlanHandle(client_handle))\n    }\n\n    fn get_connected_wlan<'a>(\n        handle: HANDLE,\n        guid: &GUID,\n    ) -> Option<&'a WLAN_CONNECTION_ATTRIBUTES> {\n        let mut connection_ptr = std::ptr::null_mut::<WLAN_CONNECTION_ATTRIBUTES>();\n        let mut data_size = 0;\n        unsafe {\n            WlanQueryInterface(\n                handle,\n                guid,\n                wlan_intf_opcode_current_connection,\n                None,\n                &mut data_size,\n                &mut connection_ptr as *mut _ as _,\n                None,\n            );\n\n            if connection_ptr.is_null() {\n                None\n            } else {\n                Some(&*connection_ptr)\n            }\n        }\n    }\n\n    pub fn is_connected_to(ssid: &str) -> Result<bool> {\n        let client_handle = Self::open_wlan()?;\n        for interface in Self::get_wlan_interfaces(*client_handle)? {\n            let connection = Self::get_connected_wlan(*client_handle, &interface.InterfaceGuid);\n            if let Some(connection) = connection {\n                let connected_ssid =\n                    String::from_utf8_lossy(&connection.wlanAssociationAttributes.dot11Ssid.ucSSID)\n                        .replace(\"\\0\", \"\")\n                        .to_string();\n\n                if connected_ssid == ssid {\n                    return Ok(connection.isState.0 & wlan_interface_state_connected.0\n                        == connection.isState.0);\n                }\n            }\n        }\n        Ok(false)\n    }\n\n    #[allow(dead_code)]\n    fn get_profiles(client_handle: HANDLE, interface_guid: &GUID) -> Result<()> {\n        unsafe {\n            let mut profile_list_ptr = std::ptr::null_mut::<WLAN_PROFILE_INFO_LIST>();\n            let result =\n                WlanGetProfileList(client_handle, interface_guid, None, &mut profile_list_ptr);\n            if result != 0 || profile_list_ptr.is_null() {\n                return Err(format!(\"Failed to get profile list, error code: {result}\").into());\n            }\n\n            let profile_list = &*profile_list_ptr;\n            let entries = std::slice::from_raw_parts(\n                profile_list.ProfileInfo.as_ptr(),\n                profile_list.dwNumberOfItems as usize,\n            );\n\n            for entry in entries {\n                let profile_name = PCWSTR(entry.strProfileName.as_ptr());\n                let mut profile_xml = PWSTR::null();\n                let result = WlanGetProfile(\n                    client_handle,\n                    interface_guid,\n                    profile_name,\n                    None,\n                    &mut profile_xml,\n                    None,\n                    None,\n                );\n\n                if result != 0 {\n                    return Err(format!(\"Failed to get profile, error code: {result}\").into());\n                }\n\n                if !profile_xml.is_null() {\n                    let profile: serde_json::Value =\n                        quick_xml::de::from_str(&profile_xml.to_string()?)?;\n                    println!(\"{profile:#?}\")\n                }\n            }\n        }\n        Ok(())\n    }\n\n    pub fn get_wlan_interfaces(client_handle: HANDLE) -> Result<Vec<WLAN_INTERFACE_INFO>> {\n        unsafe {\n            let mut interface_list_ptr: *mut WLAN_INTERFACE_INFO_LIST = std::ptr::null_mut();\n            let result = WlanEnumInterfaces(client_handle, None, &mut interface_list_ptr);\n\n            if result != 0 || interface_list_ptr.is_null() {\n                return Err(format!(\"Failed to get interface list, error code: {result}\").into());\n            }\n\n            let interface_list = &*interface_list_ptr;\n            let interfaces = std::slice::from_raw_parts(\n                interface_list.InterfaceInfo.as_ptr(),\n                interface_list.dwNumberOfItems as usize,\n            );\n            Ok(interfaces.to_vec())\n        }\n    }\n\n    fn get_available_networks(\n        client_handle: HANDLE,\n        interface_guid: &GUID,\n    ) -> Result<Vec<WLAN_AVAILABLE_NETWORK_V2>> {\n        unsafe {\n            let mut network_list_ptr = std::ptr::null_mut::<WLAN_AVAILABLE_NETWORK_LIST_V2>();\n            let result = WlanGetAvailableNetworkList2(\n                client_handle,\n                interface_guid,\n                WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES\n                    & WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,\n                None,\n                &mut network_list_ptr,\n            );\n\n            if result != 0 || network_list_ptr.is_null() {\n                return Err(format!(\"Failed to get network list, error code: {result}\").into());\n            }\n\n            let network_list = &*network_list_ptr;\n            let entries = std::slice::from_raw_parts(\n                network_list.Network.as_ptr(),\n                network_list.dwNumberOfItems as usize,\n            );\n            Ok(entries.to_vec())\n        }\n    }\n\n    fn get_bss_entries(\n        client_handle: HANDLE,\n        interface_guid: &GUID,\n    ) -> Result<Vec<WLAN_BSS_ENTRY>> {\n        unsafe {\n            let mut bss_list_ptr = std::ptr::null_mut::<WLAN_BSS_LIST>();\n            let result = WlanGetNetworkBssList(\n                client_handle,\n                interface_guid,\n                None,\n                dot11_BSS_type_any,\n                true,\n                None,\n                &mut bss_list_ptr,\n            );\n\n            if result != 0 || bss_list_ptr.is_null() {\n                return Err(format!(\"Failed to get bss list, error code: {result}\").into());\n            }\n\n            let bss_list = &*bss_list_ptr;\n            let entries = std::slice::from_raw_parts(\n                bss_list.wlanBssEntries.as_ptr(),\n                bss_list.dwNumberOfItems as usize,\n            );\n            Ok(entries.to_vec())\n        }\n    }\n\n    pub fn scan_networks() -> Result<Vec<WlanBssEntry>> {\n        let client_handle = Self::open_wlan()?;\n        let mut wlan_entries = Vec::new();\n\n        unsafe {\n            for interface in Self::get_wlan_interfaces(*client_handle)? {\n                let interface_guid = interface.InterfaceGuid;\n                let result = WlanScan(*client_handle, &interface_guid, None, None, None);\n\n                if result != 0 {\n                    return Err(format!(\"Failed to scan, error code: {result}\").into());\n                }\n\n                let available_networks =\n                    Self::get_available_networks(*client_handle, &interface_guid)?;\n                if available_networks.is_empty() {\n                    continue;\n                }\n\n                let bss_entries = Self::get_bss_entries(*client_handle, &interface_guid)?;\n                if bss_entries.is_empty() {\n                    continue;\n                }\n\n                let connection = Self::get_connected_wlan(*client_handle, &interface_guid);\n                for entry in bss_entries {\n                    let mut wrapped_entry = from_raw_entry(&entry);\n\n                    if let Some(connection) = connection {\n                        if connection.wlanAssociationAttributes.dot11Ssid.ucSSID\n                            == entry.dot11Ssid.ucSSID\n                        {\n                            wrapped_entry.connected = connection.isState.0\n                                & wlan_interface_state_connected.0\n                                == connection.isState.0;\n                            wrapped_entry.connected_channel = wrapped_entry.connected\n                                && connection.wlanAssociationAttributes.dot11Bssid\n                                    == entry.dot11Bssid;\n                        }\n                    }\n\n                    if let Some(network) = available_networks\n                        .iter()\n                        .find(|n| n.dot11Ssid.ucSSID == entry.dot11Ssid.ucSSID)\n                    {\n                        let profile = PCWSTR::from_raw(network.strProfileName.as_ptr());\n                        wrapped_entry.known = !profile.is_null() && !profile.is_empty();\n                    }\n\n                    wlan_entries.push(wrapped_entry);\n                }\n            }\n        }\n\n        Ok(wlan_entries)\n    }\n\n    pub fn start_scanning<F>(cb: F)\n    where\n        F: Fn(Vec<WlanBssEntry>) + Send + 'static,\n    {\n        SCANNING.store(true, Ordering::SeqCst);\n        std::thread::spawn(move || {\n            let mut attempts = 0;\n            loop {\n                if !SCANNING.load(Ordering::SeqCst) {\n                    break;\n                }\n\n                match Self::scan_networks() {\n                    Ok(entries) => {\n                        // sometimes we get an empty list, because the wlan list is updating after the scan\n                        // so we will wait ~10 seconds before show empty list\n                        if !entries.is_empty() || attempts > 3 {\n                            cb(entries);\n                        } else {\n                            attempts += 1;\n                        }\n                    }\n                    Err(err) => {\n                        log::error!(\"{err}\");\n                    }\n                }\n\n                std::thread::sleep(Duration::from_secs(3));\n            }\n        });\n    }\n\n    pub fn stop_scanning() {\n        SCANNING.store(false, Ordering::SeqCst);\n    }\n}\n"
  },
  {
    "path": "src/background/modules/network/application/v2.rs",
    "content": "use windows::Networking::{\n    Connectivity::NetworkInformation,\n    NetworkOperators::{\n        NetworkOperatorTetheringManager, TetheringOperationStatus, TetheringWiFiAuthenticationKind,\n        TetheringWiFiBand,\n    },\n};\n\nuse crate::{error::Result, modules::network::domain::Hotspot};\n\n#[derive(Debug)]\n#[allow(dead_code)]\npub struct NetworkManagerV2 {}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[allow(dead_code)]\npub enum NetworkManagerEventV2 {}\n\n// event_manager!(NetworkManagerV2, NetworkManagerEventV2);\n\n#[allow(dead_code)]\nimpl NetworkManagerV2 {\n    pub fn new() -> Self {\n        Self {}\n    }\n\n    pub fn hotspot() -> Result<Option<Hotspot>> {\n        if let Ok(profile) = NetworkInformation::GetInternetConnectionProfile() {\n            let hotspot = NetworkOperatorTetheringManager::CreateFromConnectionProfile(&profile)?;\n\n            let config = hotspot.GetCurrentAccessPointConfiguration()?;\n            let band = match config.Band()? {\n                TetheringWiFiBand::Auto => \"Auto\",\n                TetheringWiFiBand::TwoPointFourGigahertz => \"2.4GHz\",\n                TetheringWiFiBand::FiveGigahertz => \"5GHz\",\n                TetheringWiFiBand::SixGigahertz => \"6GHz\",\n                _ => \"???\",\n            }\n            .to_string();\n\n            let encryption = match config.AuthenticationKind()? {\n                TetheringWiFiAuthenticationKind::Wpa2 => \"WPA2\",\n                TetheringWiFiAuthenticationKind::Wpa3 => \"WPA3\",\n                TetheringWiFiAuthenticationKind::Wpa3TransitionMode => \"WPA2/WPA3\",\n                _ => \"???\",\n            }\n            .to_string();\n\n            let state = Hotspot {\n                clients: hotspot.ClientCount()?,\n                max_clients: hotspot.MaxClientCount()?,\n                state: hotspot.TetheringOperationalState()?.into(),\n                ssid: config.Ssid().ok().map(|s| s.to_string()),\n                passphrase: config.Passphrase().ok().map(|s| s.to_string()),\n                band,\n                encryption,\n            };\n\n            return Ok(Some(state));\n        }\n        Ok(None)\n    }\n\n    pub fn toggle_hotspot(enabled: bool) -> Result<()> {\n        let hotspot = NetworkOperatorTetheringManager::CreateFromConnectionProfile(\n            &NetworkInformation::GetInternetConnectionProfile()?,\n        )?;\n        let result = if enabled {\n            hotspot.StartTetheringAsync()?.join()?\n        } else {\n            hotspot.StopTetheringAsync()?.join()?\n        };\n        let status = result.Status()?;\n        if status != TetheringOperationStatus::Success {\n            return Err(format!(\n                \"Failed to toggle hotspot, error code: {:?} - {:?}\",\n                status,\n                result.AdditionalErrorMessage()\n            )\n            .into());\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/network/domain/mod.rs",
    "content": "pub mod types;\n\nuse itertools::Itertools;\nuse seelen_core::system_state::{AdapterStatus, NetworkAdapter};\nuse serde::Serialize;\nuse types::InterfaceType;\nuse windows::{\n    Networking::NetworkOperators::TetheringOperationalState,\n    Win32::{\n        NetworkManagement::{IpHelper::IP_ADAPTER_ADDRESSES_LH, Ndis::IfOperStatusUp},\n        Networking::WinSock::{inet_ntop, AF_INET, AF_INET6, SOCKADDR_IN, SOCKADDR_IN6},\n    },\n};\n\nuse crate::error::Result;\n\n#[derive(PartialEq, Eq)]\nenum Address {\n    Ipv4,\n    Ipv6,\n    Gateway,\n}\n\nunsafe fn get_gateway(adapter: &IP_ADAPTER_ADDRESSES_LH) -> Option<String> {\n    let mut gateway_ptr = adapter.FirstGatewayAddress;\n    while !gateway_ptr.is_null() {\n        let gateway = &*gateway_ptr;\n\n        if gateway.Address.lpSockaddr.is_null() {\n            gateway_ptr = gateway.Next;\n            continue;\n        }\n\n        let sockaddr = &*(gateway.Address.lpSockaddr as *const SOCKADDR_IN);\n        if sockaddr.sin_family == AF_INET {\n            let mut string_buffer = [0u8; 16];\n            return inet_ntop(\n                AF_INET.0 as i32,\n                &sockaddr.sin_addr as *const _ as _,\n                &mut string_buffer,\n            )\n            .to_string()\n            .ok();\n        }\n\n        gateway_ptr = gateway.Next;\n    }\n    None\n}\n\nunsafe fn get_address(adapter: &IP_ADAPTER_ADDRESSES_LH, address: Address) -> Option<String> {\n    if address == Address::Gateway {\n        return get_gateway(adapter);\n    }\n\n    let mut unicast_ptr = adapter.FirstUnicastAddress;\n\n    while !unicast_ptr.is_null() {\n        let unicast = &*unicast_ptr;\n\n        if unicast.Address.lpSockaddr.is_null() {\n            unicast_ptr = unicast.Next;\n            continue;\n        }\n\n        let sockaddr = &*(unicast.Address.lpSockaddr as *const SOCKADDR_IN);\n        if address == Address::Ipv4 && sockaddr.sin_family == AF_INET {\n            let mut string_buffer = [0u8; 16];\n            return inet_ntop(\n                AF_INET.0 as i32,\n                &sockaddr.sin_addr as *const _ as _,\n                &mut string_buffer,\n            )\n            .to_string()\n            .ok();\n        }\n\n        let sockaddr = &*(unicast.Address.lpSockaddr as *const SOCKADDR_IN6);\n        if address == Address::Ipv6 && sockaddr.sin6_family == AF_INET6 {\n            let mut string_buffer = [0u8; 46];\n            return inet_ntop(\n                AF_INET6.0 as i32,\n                &sockaddr.sin6_addr as *const _ as _,\n                &mut string_buffer,\n            )\n            .to_string()\n            .ok();\n        }\n\n        unicast_ptr = unicast.Next;\n    }\n\n    None\n}\n\npub fn adapter_to_slu_net_adapter(adapter: &IP_ADAPTER_ADDRESSES_LH) -> Result<NetworkAdapter> {\n    unsafe {\n        let mac_address = adapter\n            .PhysicalAddress\n            .iter()\n            .map(|b| format!(\"{b:02x}\"))\n            .join(\":\");\n\n        let status = if adapter.OperStatus == IfOperStatusUp {\n            AdapterStatus::Up\n        } else {\n            AdapterStatus::Down\n        };\n\n        Ok(NetworkAdapter {\n            dns_suffix: adapter.DnsSuffix.to_string()?,\n            name: adapter.FriendlyName.to_string()?,\n            description: adapter.Description.to_string()?,\n            mac: mac_address,\n            status,\n            ipv4: get_address(adapter, Address::Ipv4),\n            gateway: get_address(adapter, Address::Gateway),\n            ipv6: get_address(adapter, Address::Ipv6),\n            interface_type: InterfaceType::from(adapter.IfType)\n                .to_string()\n                .replace(\"IF_TYPE_\", \"\"),\n        })\n    }\n}\n\n#[derive(Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct Hotspot {\n    pub clients: u32,\n    pub max_clients: u32,\n    pub state: HotspotState,\n    pub ssid: Option<String>,\n    pub passphrase: Option<String>,\n    pub band: String,\n    pub encryption: String,\n}\n\n#[derive(Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum HotspotState {\n    Unknown,\n    On,\n    Off,\n    InTransition,\n}\n\nimpl From<TetheringOperationalState> for HotspotState {\n    fn from(state: TetheringOperationalState) -> Self {\n        match state {\n            TetheringOperationalState::On => HotspotState::On,\n            TetheringOperationalState::Off => HotspotState::Off,\n            TetheringOperationalState::InTransition => HotspotState::InTransition,\n            _ => HotspotState::Unknown,\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/modules/network/domain/types.rs",
    "content": "use windows::Win32::NetworkManagement::IpHelper::*;\n\nmacro_rules! create_enum {\n    ($($variant:ident),*) => {\n        #[allow(non_camel_case_types)]\n        #[derive(Debug, serde::Serialize)]\n        pub enum InterfaceType {\n            $(\n                $variant = $variant as isize,\n            )*\n            Unknown\n        }\n\n        impl From<u32> for InterfaceType {\n            fn from(value: u32) -> Self {\n                match value {\n                    $(\n                        $variant => InterfaceType::$variant,\n                    )*\n                    _ => InterfaceType::Unknown\n                }\n            }\n        }\n    };\n}\n\ncreate_enum![\n    IF_TYPE_A12MPPSWITCH,\n    IF_TYPE_AAL2,\n    IF_TYPE_AAL5,\n    IF_TYPE_ADSL,\n    IF_TYPE_AFLANE_8023,\n    IF_TYPE_AFLANE_8025,\n    IF_TYPE_ARAP,\n    IF_TYPE_ARCNET,\n    IF_TYPE_ARCNET_PLUS,\n    IF_TYPE_ASYNC,\n    IF_TYPE_ATM,\n    IF_TYPE_ATM_DXI,\n    IF_TYPE_ATM_FUNI,\n    IF_TYPE_ATM_IMA,\n    IF_TYPE_ATM_LOGICAL,\n    IF_TYPE_ATM_RADIO,\n    IF_TYPE_ATM_SUBINTERFACE,\n    IF_TYPE_ATM_VCI_ENDPT,\n    IF_TYPE_ATM_VIRTUAL,\n    IF_TYPE_BASIC_ISDN,\n    IF_TYPE_BGP_POLICY_ACCOUNTING,\n    IF_TYPE_BSC,\n    IF_TYPE_CCTEMUL,\n    IF_TYPE_CES,\n    IF_TYPE_CHANNEL,\n    IF_TYPE_CNR,\n    IF_TYPE_COFFEE,\n    IF_TYPE_COMPOSITELINK,\n    IF_TYPE_DCN,\n    IF_TYPE_DDN_X25,\n    IF_TYPE_DIGITALPOWERLINE,\n    IF_TYPE_DIGITAL_WRAPPER_OVERHEAD_CHANNEL,\n    IF_TYPE_DLSW,\n    IF_TYPE_DOCSCABLE_DOWNSTREAM,\n    IF_TYPE_DOCSCABLE_MACLAYER,\n    IF_TYPE_DOCSCABLE_UPSTREAM,\n    IF_TYPE_DS0,\n    IF_TYPE_DS0_BUNDLE,\n    IF_TYPE_DS1,\n    IF_TYPE_DS1_FDL,\n    IF_TYPE_DS3,\n    IF_TYPE_DTM,\n    IF_TYPE_DVBRCC_DOWNSTREAM,\n    IF_TYPE_DVBRCC_MACLAYER,\n    IF_TYPE_DVBRCC_UPSTREAM,\n    IF_TYPE_DVB_ASI_IN,\n    IF_TYPE_DVB_ASI_OUT,\n    IF_TYPE_E1,\n    IF_TYPE_EON,\n    IF_TYPE_EPLRS,\n    IF_TYPE_ESCON,\n    IF_TYPE_ETHERNET_3MBIT,\n    IF_TYPE_ETHERNET_CSMACD,\n    IF_TYPE_FAST,\n    IF_TYPE_FASTETHER,\n    IF_TYPE_FASTETHER_FX,\n    IF_TYPE_FDDI,\n    IF_TYPE_FIBRECHANNEL,\n    IF_TYPE_FRAMERELAY,\n    IF_TYPE_FRAMERELAY_INTERCONNECT,\n    IF_TYPE_FRAMERELAY_MPI,\n    IF_TYPE_FRAMERELAY_SERVICE,\n    IF_TYPE_FRF16_MFR_BUNDLE,\n    IF_TYPE_FR_DLCI_ENDPT,\n    IF_TYPE_FR_FORWARD,\n    IF_TYPE_G703_2MB,\n    IF_TYPE_G703_64K,\n    IF_TYPE_GIGABITETHERNET,\n    IF_TYPE_GR303_IDT,\n    IF_TYPE_GR303_RDT,\n    IF_TYPE_H323_GATEKEEPER,\n    IF_TYPE_H323_PROXY,\n    IF_TYPE_HDH_1822,\n    IF_TYPE_HDLC,\n    IF_TYPE_HDSL2,\n    IF_TYPE_HIPERLAN2,\n    IF_TYPE_HIPPI,\n    IF_TYPE_HIPPIINTERFACE,\n    IF_TYPE_HOSTPAD,\n    IF_TYPE_HSSI,\n    IF_TYPE_HYPERCHANNEL,\n    IF_TYPE_IBM370PARCHAN,\n    IF_TYPE_IDSL,\n    IF_TYPE_IEEE1394,\n    IF_TYPE_IEEE80211,\n    IF_TYPE_IEEE80212,\n    IF_TYPE_IEEE802154,\n    IF_TYPE_IEEE80216_WMAN,\n    IF_TYPE_IEEE8023AD_LAG,\n    IF_TYPE_IF_GSN,\n    IF_TYPE_IMT,\n    IF_TYPE_INTERLEAVE,\n    IF_TYPE_IP,\n    IF_TYPE_IPFORWARD,\n    IF_TYPE_IPOVER_ATM,\n    IF_TYPE_IPOVER_CDLC,\n    IF_TYPE_IPOVER_CLAW,\n    IF_TYPE_IPSWITCH,\n    IF_TYPE_IS088023_CSMACD,\n    IF_TYPE_ISDN,\n    IF_TYPE_ISDN_S,\n    IF_TYPE_ISDN_U,\n    IF_TYPE_ISO88022_LLC,\n    IF_TYPE_ISO88024_TOKENBUS,\n    IF_TYPE_ISO88025R_DTR,\n    IF_TYPE_ISO88025_CRFPRINT,\n    IF_TYPE_ISO88025_FIBER,\n    IF_TYPE_ISO88025_TOKENRING,\n    IF_TYPE_ISO88026_MAN,\n    IF_TYPE_ISUP,\n    IF_TYPE_L2_VLAN,\n    IF_TYPE_L3_IPVLAN,\n    IF_TYPE_L3_IPXVLAN,\n    IF_TYPE_LAP_B,\n    IF_TYPE_LAP_D,\n    IF_TYPE_LAP_F,\n    IF_TYPE_LOCALTALK,\n    IF_TYPE_MEDIAMAILOVERIP,\n    IF_TYPE_MF_SIGLINK,\n    IF_TYPE_MIO_X25,\n    IF_TYPE_MODEM,\n    IF_TYPE_MPC,\n    IF_TYPE_MPLS,\n    IF_TYPE_MPLS_TUNNEL,\n    IF_TYPE_MSDSL,\n    IF_TYPE_MVL,\n    IF_TYPE_MYRINET,\n    IF_TYPE_NFAS,\n    IF_TYPE_NSIP,\n    IF_TYPE_OPTICAL_CHANNEL,\n    IF_TYPE_OPTICAL_TRANSPORT,\n    IF_TYPE_OTHER,\n    IF_TYPE_PARA,\n    IF_TYPE_PLC,\n    IF_TYPE_POS,\n    IF_TYPE_PPP,\n    IF_TYPE_PPPMULTILINKBUNDLE,\n    IF_TYPE_PRIMARY_ISDN,\n    IF_TYPE_PROP_BWA_P2MP,\n    IF_TYPE_PROP_CNLS,\n    IF_TYPE_PROP_DOCS_WIRELESS_DOWNSTREAM,\n    IF_TYPE_PROP_DOCS_WIRELESS_MACLAYER,\n    IF_TYPE_PROP_DOCS_WIRELESS_UPSTREAM,\n    IF_TYPE_PROP_MULTIPLEXOR,\n    IF_TYPE_PROP_POINT2POINT_SERIAL,\n    IF_TYPE_PROP_VIRTUAL,\n    IF_TYPE_PROP_WIRELESS_P2P,\n    IF_TYPE_PROTEON_10MBIT,\n    IF_TYPE_PROTEON_80MBIT,\n    IF_TYPE_QLLC,\n    IF_TYPE_RADIO_MAC,\n    IF_TYPE_RADSL,\n    IF_TYPE_REACH_DSL,\n    IF_TYPE_REGULAR_1822,\n    IF_TYPE_RFC1483,\n    IF_TYPE_RFC877_X25,\n    IF_TYPE_RS232,\n    IF_TYPE_RSRB,\n    IF_TYPE_SDLC,\n    IF_TYPE_SDSL,\n    IF_TYPE_SHDSL,\n    IF_TYPE_SIP,\n    IF_TYPE_SLIP,\n    IF_TYPE_SMDS_DXI,\n    IF_TYPE_SMDS_ICIP,\n    IF_TYPE_SOFTWARE_LOOPBACK,\n    IF_TYPE_SONET,\n    IF_TYPE_SONET_OVERHEAD_CHANNEL,\n    IF_TYPE_SONET_PATH,\n    IF_TYPE_SONET_VT,\n    IF_TYPE_SRP,\n    IF_TYPE_SS7_SIGLINK,\n    IF_TYPE_STACKTOSTACK,\n    IF_TYPE_STARLAN,\n    IF_TYPE_TDLC,\n    IF_TYPE_TERMPAD,\n    IF_TYPE_TR008,\n    IF_TYPE_TRANSPHDLC,\n    IF_TYPE_TUNNEL,\n    IF_TYPE_ULTRA,\n    IF_TYPE_USB,\n    IF_TYPE_V11,\n    IF_TYPE_V35,\n    IF_TYPE_V36,\n    IF_TYPE_V37,\n    IF_TYPE_VDSL,\n    IF_TYPE_VIRTUALIPADDRESS,\n    IF_TYPE_VOICEOVERATM,\n    IF_TYPE_VOICEOVERFRAMERELAY,\n    IF_TYPE_VOICE_EM,\n    IF_TYPE_VOICE_ENCAP,\n    IF_TYPE_VOICE_FXO,\n    IF_TYPE_VOICE_FXS,\n    IF_TYPE_VOICE_OVERIP,\n    IF_TYPE_WWANPP,\n    IF_TYPE_WWANPP2,\n    IF_TYPE_X213,\n    IF_TYPE_X25_HUNTGROUP,\n    IF_TYPE_X25_MLP,\n    IF_TYPE_X25_PLE,\n    IF_TYPE_XBOX_WIRELESS\n];\n\nimpl std::fmt::Display for InterfaceType {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{self:?}\")\n    }\n}\n"
  },
  {
    "path": "src/background/modules/network/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{handlers::SeelenEvent, system_state::WlanProfile};\nuse tauri_plugin_shell::ShellExt;\nuse windows::Win32::Networking::NetworkListManager::{\n    INetworkListManager, NetworkListManager, NLM_CONNECTIVITY_IPV4_INTERNET,\n    NLM_CONNECTIVITY_IPV6_INTERNET,\n};\n\nuse crate::{\n    app::{emit_to_webviews, get_app_handle},\n    error::Result,\n    utils::sleep_millis,\n    windows_api::Com,\n};\n\nuse super::application::{get_local_ip_address, NetworkManager, NetworkManagerEvent};\n\nfn get_network_manager() -> &'static NetworkManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        NetworkManager::subscribe(|event| match event {\n            NetworkManagerEvent::ConnectivityChanged { connectivity, ip } => {\n                log::trace!(target: \"network\", \"Connectivity changed: {connectivity:?}\");\n                if let Ok(adapters) = NetworkManager::get_adapters() {\n                    let has_internet_ipv4 = connectivity.0 & NLM_CONNECTIVITY_IPV4_INTERNET.0\n                        == NLM_CONNECTIVITY_IPV4_INTERNET.0;\n                    let has_internet_ipv6 = connectivity.0 & NLM_CONNECTIVITY_IPV6_INTERNET.0\n                        == NLM_CONNECTIVITY_IPV6_INTERNET.0;\n\n                    emit_to_webviews(SeelenEvent::NetworkDefaultLocalIp, ip);\n                    emit_to_webviews(SeelenEvent::NetworkAdapters, adapters);\n                    emit_to_webviews(\n                        SeelenEvent::NetworkInternetConnection,\n                        has_internet_ipv4 || has_internet_ipv6,\n                    );\n                }\n            }\n        });\n    });\n    NetworkManager::instance()\n}\n\nasync fn try_connect_to_profile(ssid: &str) -> Result<bool> {\n    let handle = get_app_handle();\n    let output = handle\n        .shell()\n        .command(\"netsh\")\n        .args([\"wlan\", \"connect\", &format!(\"name={ssid}\")])\n        .output()\n        .await?;\n\n    if output.status.success() {\n        // wait to ensure connection\n        let mut attempts = 0;\n        while !NetworkManager::is_connected_to(ssid)? && attempts < 10 {\n            attempts += 1;\n            sleep_millis(1000);\n        }\n        Ok(attempts < 10)\n    } else {\n        Err(output.into())\n    }\n}\n\n#[tauri::command(async)]\npub fn wlan_start_scanning() {\n    get_network_manager();\n    log::trace!(\"Start scanning networks\");\n    NetworkManager::start_scanning(|list| {\n        emit_to_webviews(SeelenEvent::NetworkWlanScanned, &list);\n    });\n}\n\n#[tauri::command(async)]\npub fn wlan_stop_scanning() {\n    get_network_manager();\n    log::trace!(\"Stop scanning networks\");\n    NetworkManager::stop_scanning();\n}\n\n#[tauri::command(async)]\npub async fn wlan_get_profiles() -> Result<Vec<WlanProfile>> {\n    get_network_manager();\n    NetworkManager::get_wifi_profiles().await\n}\n\n#[tauri::command(async)]\npub async fn wlan_connect(ssid: String, password: Option<String>, hidden: bool) -> Result<bool> {\n    get_network_manager();\n    if let Some(passphrase) = password {\n        NetworkManager::add_profile(&ssid, &passphrase, hidden).await?;\n    } else {\n        let passphrase = String::new();\n        NetworkManager::add_profile(&ssid, &passphrase, hidden).await?;\n    }\n\n    match try_connect_to_profile(&ssid).await {\n        Ok(true) => Ok(true),\n        Ok(false) => {\n            NetworkManager::remove_profile(&ssid).await?;\n            Ok(false)\n        }\n        Err(err) => {\n            NetworkManager::remove_profile(&ssid).await?;\n            Err(err)\n        }\n    }\n}\n\n#[tauri::command(async)]\npub async fn wlan_disconnect() -> Result<()> {\n    get_network_manager();\n    NetworkManager::disconnect_all()\n}\n\n#[tauri::command(async)]\npub fn get_network_default_local_ip() -> Result<String> {\n    get_network_manager();\n    get_local_ip_address()\n}\n\n#[tauri::command(async)]\npub fn get_network_adapters() -> Result<Vec<seelen_core::system_state::NetworkAdapter>> {\n    get_network_manager();\n    NetworkManager::get_adapters()\n}\n\n#[tauri::command(async)]\npub fn get_network_internet_connection() -> Result<bool> {\n    get_network_manager();\n    Com::run_with_context(|| {\n        let list_manager: INetworkListManager = Com::create_instance(&NetworkListManager)?;\n        let connectivity = unsafe { list_manager.GetConnectivity()? };\n\n        let has_internet_ipv4 =\n            connectivity.0 & NLM_CONNECTIVITY_IPV4_INTERNET.0 == NLM_CONNECTIVITY_IPV4_INTERNET.0;\n        let has_internet_ipv6 =\n            connectivity.0 & NLM_CONNECTIVITY_IPV6_INTERNET.0 == NLM_CONNECTIVITY_IPV6_INTERNET.0;\n\n        Ok(has_internet_ipv4 || has_internet_ipv6)\n    })\n}\n"
  },
  {
    "path": "src/background/modules/network/mod.rs",
    "content": "pub mod application;\npub mod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/notifications/application.rs",
    "content": "use parking_lot::Mutex as ParkingLotMutex;\nuse seelen_core::system_state::{\n    AppNotification, Toast, ToastActionActivationType, ToastBindingChild, ToastText,\n};\nuse std::{\n    collections::HashSet,\n    path::PathBuf,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        LazyLock,\n    },\n};\nuse tauri::Manager;\nuse windows::{\n    ApplicationModel::AppInfo,\n    Foundation::{TypedEventHandler, Uri},\n    UI::Notifications::{\n        KnownNotificationBindings,\n        Management::{UserNotificationListener, UserNotificationListenerAccessStatus},\n        NotificationKinds, ToastNotificationManager, ToastNotificationManagerForUser,\n        UserNotification, UserNotificationChangedEventArgs, UserNotificationChangedKind,\n    },\n};\n\nuse crate::{\n    app::get_app_handle,\n    error::{Result, ResultLogExt},\n    event_manager, log_error,\n    modules::{\n        apps::application::msix::get_hightest_quality_posible_for_uwp_image,\n        start::application::StartMenuManager,\n    },\n    utils::{\n        convert_file_to_src, icon_extractor::request_icon_extraction_from_umid,\n        lock_free::SyncHashMap, spawn_named_thread,\n    },\n    windows_api::{event_window::IS_INTERACTIVE_SESSION, types::AppUserModelId, WindowsApi},\n};\n\nstatic LOADED_NOTIFICATIONS: LazyLock<ParkingLotMutex<HashSet<u32>>> =\n    LazyLock::new(|| ParkingLotMutex::new(HashSet::new()));\nstatic RELEASED: AtomicBool = AtomicBool::new(true);\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum NotificationEvent {\n    Added(u32),\n    Removed(u32),\n    Cleared,\n}\n\npub struct NotificationManager {\n    notifications: SyncHashMap<u32, AppNotification>,\n    manager: ToastNotificationManagerForUser,\n    listener: UserNotificationListener,\n    event_token: Option<i64>,\n}\n\nunsafe impl Send for NotificationManager {}\nunsafe impl Sync for NotificationManager {}\n\nevent_manager!(NotificationManager, NotificationEvent);\n\nimpl NotificationManager {\n    fn new() -> Result<Self> {\n        Ok(Self {\n            notifications: SyncHashMap::new(),\n            manager: ToastNotificationManager::GetDefault()?,\n            listener: UserNotificationListener::Current()?,\n            event_token: None,\n        })\n    }\n\n    pub fn instance() -> &'static Self {\n        static MANAGER: LazyLock<NotificationManager> = LazyLock::new(|| {\n            let mut manager =\n                NotificationManager::new().expect(\"Failed to create notification manager\");\n            manager.init().log_error();\n            manager\n        });\n        &MANAGER\n    }\n\n    pub fn notifications(&self) -> Vec<AppNotification> {\n        let mut notifications = self.notifications.values();\n        notifications.sort_by(|a, b| b.date.cmp(&a.date));\n        notifications\n    }\n\n    pub fn remove_notification(&self, id: u32) -> Result<()> {\n        self.listener.RemoveNotification(id)?;\n        Self::send(NotificationEvent::Removed(id));\n        Ok(())\n    }\n\n    pub fn clear_notifications(&self) -> Result<()> {\n        let mut umids = HashSet::new();\n        self.notifications.for_each(|(_, n)| {\n            umids.insert(n.app_umid.clone());\n        });\n        for umid in umids {\n            let history = self.manager.History()?;\n            history.ClearWithId(&umid.into())?;\n        }\n        Self::send(NotificationEvent::Cleared);\n        Ok(())\n    }\n\n    fn init(&mut self) -> Result<()> {\n        let access = self.listener.RequestAccessAsync()?.join()?;\n        if access != UserNotificationListenerAccessStatus::Allowed {\n            return Err(\"Failed to get notification access\".into());\n        }\n\n        let u_notifications = self\n            .listener\n            .GetNotificationsAsync(NotificationKinds::Toast)?\n            .join()?;\n        for u_notification in u_notifications {\n            log_error!(self.load_notification(u_notification));\n        }\n\n        // TODO: this only works on MSIX/APPX/UWP builds so idk how to make it work on win32 apps\n        match self\n            .listener\n            .NotificationChanged(&TypedEventHandler::new(Self::on_notifications_change))\n        {\n            Ok(token) => self.event_token = Some(token),\n            Err(error) => {\n                log::warn!(\"Failed to register winrt notification change handler: {error}\");\n                // intead we use a thread\n                spawn_named_thread(\"Notification Manager\", || -> Result<()> {\n                    RELEASED.store(false, Ordering::SeqCst);\n                    while !RELEASED.load(Ordering::Acquire) {\n                        std::thread::sleep(std::time::Duration::from_secs(1));\n                        // Pause when session is not interactive to reduce CPU usage\n                        if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                            continue;\n                        }\n                        Self::manual_notifications_loop().log_error();\n                    }\n                    Ok(())\n                });\n            }\n        }\n\n        let eid = Self::subscribe(|e| log_error!(Self::process_event(e)));\n        Self::set_event_handler_priority(&eid, 1);\n        Ok(())\n    }\n\n    fn on_notifications_change(\n        listener: windows_core::Ref<UserNotificationListener>,\n        args: windows_core::Ref<UserNotificationChangedEventArgs>,\n    ) -> windows_core::Result<()> {\n        let (Some(_listener), Some(args)) = (listener.as_ref(), args.as_ref()) else {\n            return Ok(());\n        };\n\n        let change_kind = args.ChangeKind()?;\n        let changed_id = args.UserNotificationId()?;\n\n        match change_kind {\n            UserNotificationChangedKind::Added => {\n                Self::send(NotificationEvent::Added(changed_id));\n            }\n            UserNotificationChangedKind::Removed => {\n                Self::send(NotificationEvent::Removed(changed_id));\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    fn manual_notifications_loop() -> Result<()> {\n        let listener = { UserNotificationListener::Current()? };\n        let mut old_toasts = LOADED_NOTIFICATIONS.lock().clone();\n\n        for u_notification in listener\n            .GetNotificationsAsync(NotificationKinds::Toast)?\n            .join()?\n        {\n            let id: u32 = u_notification.Id()?;\n            if !old_toasts.contains(&id) {\n                Self::send(NotificationEvent::Added(id))\n            }\n            old_toasts.remove(&id);\n        }\n\n        for id in old_toasts {\n            Self::send(NotificationEvent::Removed(id))\n        }\n        Ok(())\n    }\n\n    fn process_event(event: NotificationEvent) -> Result<()> {\n        let manager = NotificationManager::instance();\n        match event {\n            NotificationEvent::Added(id) => {\n                let u_notification = UserNotificationListener::Current()?.GetNotification(id)?;\n                manager.load_notification(u_notification)?;\n            }\n            NotificationEvent::Removed(id) => {\n                manager.notifications.remove(&id);\n                LOADED_NOTIFICATIONS.lock().remove(&id);\n            }\n            NotificationEvent::Cleared => {\n                manager.notifications.clear();\n                LOADED_NOTIFICATIONS.lock().clear();\n            }\n        }\n        Ok(())\n    }\n\n    fn clean_toast(toast: &mut Toast, umid: &str) -> Result<()> {\n        if toast.launch.is_none() {\n            toast.launch = Some(format!(\"shell:AppsFolder\\\\{umid}\"));\n            toast.activation_type = ToastActionActivationType::Protocol;\n        }\n\n        let package_path = AppInfo::GetFromAppUserModelId(&umid.into())\n            .and_then(|info| info.Package())\n            .and_then(|package| package.InstalledPath())\n            .map(|path| PathBuf::from(path.to_os_string()));\n\n        for entry in &mut toast.visual.binding.children {\n            let ToastBindingChild::Image(image) = entry else {\n                continue;\n            };\n\n            if image.src.is_empty() {\n                continue;\n            }\n\n            log::trace!(\"Resolving image src:e \\\"{}\\\"\", image.src);\n            let uri = Uri::CreateUri(&image.src.clone().into())?;\n            let scheme = uri.SchemeName()?.to_string_lossy();\n            let uri_path = PathBuf::from(\n                Uri::UnescapeComponent(&uri.Path()?)?\n                    .to_string_lossy()\n                    .trim_start_matches('/'),\n            );\n\n            // https://learn.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes\n            // https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-image\n            match scheme.as_str() {\n                \"http\" | \"https\" => {}\n                \"ms-appx\" | \"ms-appx-web\" => {\n                    let path = package_path.clone()?.join(uri_path);\n                    if let Some((path, _)) = get_hightest_quality_posible_for_uwp_image(&path) {\n                        log::debug!(\"  Resolved path: {}\", path.display());\n                        image.src = convert_file_to_src(&path);\n                    } else {\n                        log::warn!(\"  Unable to resolve path {}\", path.display());\n                    }\n                }\n                \"ms-appdata\" => {\n                    let parent = if uri_path.starts_with(\"local\") {\n                        \"LocalState\"\n                    } else if uri_path.starts_with(\"roaming\") {\n                        \"LocalCache\"\n                    } else {\n                        continue;\n                    };\n\n                    let uri_path = PathBuf::from(\n                        Uri::UnescapeComponent(&uri.Path()?)?\n                            .to_string_lossy()\n                            .to_lowercase()\n                            .trim_start_matches('/')\n                            .trim_start_matches(\"local/\")\n                            .trim_start_matches(\"roaming/\"),\n                    );\n\n                    let package_family_name = AppInfo::GetFromAppUserModelId(&umid.into())?\n                        .PackageFamilyName()?\n                        .to_string_lossy();\n\n                    let path = get_app_handle()\n                        .path()\n                        .local_data_dir()?\n                        .join(\"Packages\")\n                        .join(package_family_name)\n                        .join(parent)\n                        .join(uri_path);\n\n                    log::debug!(\"  Resolved path: {}\", path.display());\n                    image.src = convert_file_to_src(&path);\n                }\n                \"file\" => {\n                    image.src = if uri_path.exists() {\n                        convert_file_to_src(&uri_path)\n                    } else {\n                        // telegram desktop from ms store uses file intead of ms-appdata\n                        // so this causes the image to be missing, windows doesn't show image as well\n                        // so we decide to follow same behavior.\n                        \"\".to_string()\n                    }\n                }\n                _ => {}\n            }\n        }\n        Ok(())\n    }\n\n    // this function an in general all the notification system can still be improved on usability and performance\n    fn load_notification(&self, u_notification: UserNotification) -> Result<()> {\n        let notification_id = u_notification.Id()?;\n        LOADED_NOTIFICATIONS.lock().insert(notification_id);\n        let notification = u_notification.Notification()?;\n\n        let app_info = match u_notification.AppInfo() {\n            Ok(info) => info,\n            Err(_) => {\n                // will fail if the notification was added by an uninstalled app\n                // log::error!(\"Unable to get app info: {}\", error);\n                return Ok(());\n            }\n        };\n\n        let display_info = app_info.DisplayInfo()?;\n        let app_umid = app_info.AppUserModelId()?;\n\n        let visuals = notification.Visual()?;\n        let binding = visuals.GetBinding(&KnownNotificationBindings::ToastGeneric()?)?;\n        let text_sequence = binding.GetTextElements()?;\n\n        let mut notification_text = String::new();\n        for text in &text_sequence {\n            let text = text.Text()?.to_string_lossy().trim().replace(\"\\r\\n\", \"\\n\");\n            notification_text.push_str(&text);\n        }\n\n        let history = self.manager.History()?;\n        let toast_notifications = history.GetHistoryWithId(&app_umid)?;\n\n        log::trace!(\n            \"Loading notification, ID: {}, AppID: {}\",\n            u_notification.Id()?,\n            app_umid\n        );\n\n        let mut notification_content = None;\n        for toast_notification in toast_notifications {\n            // this can be null when the notification count is bigger than the max allowed by default 20\n            if let Ok(content) = toast_notification.Content() {\n                let toast_xml = content.GetXml()?.to_string();\n                let mut toast: Toast = quick_xml::de::from_str(&toast_xml)?;\n                let toast_text = get_text_from_toast(&toast);\n\n                if notification_text == toast_text {\n                    Self::clean_toast(&mut toast, &app_umid.to_string())?;\n                    notification_content = Some(toast);\n                    break;\n                }\n            }\n        }\n\n        let notification_content = match notification_content {\n            Some(content) => content,\n            None => {\n                log::debug!(\"Toast content not found, generating one from plain text\");\n                let mut toast = Toast::default();\n                let content = &mut toast.visual.binding.children;\n                for text in text_sequence {\n                    let text = text\n                        .Text()?\n                        .to_string_lossy()\n                        .replace(\"\\r\\n\", \"\\n\")\n                        .trim()\n                        .to_owned();\n                    content.push(ToastBindingChild::Text(ToastText {\n                        id: None,\n                        content: text,\n                    }));\n                }\n                Self::clean_toast(&mut toast, &app_umid.to_string())?;\n                toast\n            }\n        };\n\n        // pre-extraction to avoid flickering on the ui\n        request_icon_extraction_from_umid(&app_umid.to_string().into());\n\n        self.notifications.upsert(\n            notification_id,\n            AppNotification {\n                id: notification_id,\n                app_umid: app_umid.to_string(),\n                app_name: display_info.DisplayName()?.to_string(),\n                app_description: display_info.Description()?.to_string(),\n                date: u_notification.CreationTime()?.UniversalTime,\n                content: notification_content,\n            },\n        );\n        Ok(())\n    }\n}\n\nimpl Drop for NotificationManager {\n    fn drop(&mut self) {\n        if let Some(token) = self.event_token.take() {\n            self.listener.RemoveNotificationChanged(token).log_error();\n        }\n        RELEASED.store(true, Ordering::Release);\n    }\n}\n\nfn get_text_from_toast(toast: &Toast) -> String {\n    let mut text = String::new();\n    for entry in &toast.visual.binding.children {\n        // text inside groups are intended to be ignored for the comparison\n        if let ToastBindingChild::Text(entry) = entry {\n            text.push_str(entry.content.replace(\"\\r\\n\", \"\\n\").trim());\n        }\n    }\n    text\n}\n\npub fn get_toast_activator_clsid(app_umid: &AppUserModelId) -> Result<String> {\n    match app_umid {\n        AppUserModelId::PropertyStore(umid) => {\n            let guard = StartMenuManager::instance();\n            if let Some(item) = guard.get_by_file_umid(umid) {\n                let clsid = WindowsApi::get_file_toast_activator(&item.path)?;\n                return Ok(clsid);\n            }\n        }\n        _ => {\n            // todo search for the clsid in the AppManifest\n        }\n    };\n    Err(format!(\"Unable to get toast activator clsid for: {app_umid:?}\").into())\n}\n"
  },
  {
    "path": "src/background/modules/notifications/domain.rs",
    "content": "\n"
  },
  {
    "path": "src/background/modules/notifications/infrastructure.rs",
    "content": "use std::{collections::HashMap, sync::Once};\n\nuse seelen_core::{handlers::SeelenEvent, system_state::AppNotification};\nuse windows::{\n    core::GUID,\n    Win32::UI::Notifications::{INotificationActivationCallback, NOTIFICATION_USER_INPUT_DATA},\n};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::Result,\n    modules::notifications::application::{get_toast_activator_clsid, NotificationManager},\n    windows_api::{string_utils::WindowsString, types::AppUserModelId, Com},\n};\n\nfn get_notification_manager() -> &'static NotificationManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        NotificationManager::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::Notifications,\n                NotificationManager::instance().notifications(),\n            );\n        });\n    });\n    NotificationManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_notifications() -> Vec<AppNotification> {\n    get_notification_manager().notifications()\n}\n\n// https://learn.microsoft.com/en-us/windows/win32/api/notificationactivationcallback/nf-notificationactivationcallback-inotificationactivationcallback-activate\n// https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/c8f76d072df53d3622fb5440d63afb06cb9e7a10/Microsoft.Toolkit.Uwp.Notifications/Toasts/Compat/Desktop/DesktopNotificationManagerCompat.cs#L19\n#[tauri::command(async)]\npub fn activate_notification(\n    umid: String,\n    args: String,\n    input_data: HashMap<String, String>,\n) -> Result<()> {\n    log::trace!(\"Activating notification \\'{umid}\\' with args \\'{args}\\'\");\n\n    let app_umid = AppUserModelId::from(umid);\n\n    if let Ok(activator_clsid) = get_toast_activator_clsid(&app_umid) {\n        log::trace!(\"Activating with clsid: {activator_clsid}\");\n\n        let mut data = Vec::new();\n        for (key, value) in input_data {\n            let key = WindowsString::from_str(&key);\n            let value = WindowsString::from_str(&value);\n            data.push(NOTIFICATION_USER_INPUT_DATA {\n                Key: key.as_pcwstr(),\n                Value: value.as_pcwstr(),\n            });\n        }\n\n        return Com::run_with_context(|| unsafe {\n            let clsid_activator = GUID::try_from(activator_clsid.as_str())?;\n            let toast_activator: INotificationActivationCallback =\n                Com::create_instance(&clsid_activator)?;\n\n            let app_umid = WindowsString::from_str(&app_umid);\n            let args = WindowsString::from_str(&args);\n            toast_activator.Activate(app_umid.as_pcwstr(), args.as_pcwstr(), &data)?;\n            Ok(())\n        });\n    }\n\n    // as fallback in case of not being able to use the toast activator, just open the app.\n    log::trace!(\"Using activation fallback (open app - no arguments)\");\n    crate::exposed::open_file_inner(format!(\"shell:AppsFolder\\\\{app_umid}\"))?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn notifications_close(id: u32) -> Result<()> {\n    get_notification_manager().remove_notification(id)\n}\n\n#[tauri::command(async)]\npub fn notifications_close_all() -> Result<()> {\n    get_notification_manager().clear_notifications()\n}\n"
  },
  {
    "path": "src/background/modules/notifications/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/power/application.rs",
    "content": "use std::sync::LazyLock;\n\nuse seelen_core::system_state::{Battery, PowerMode, PowerStatus};\nuse windows::Win32::{\n    Foundation::HANDLE,\n    System::{\n        Power::{\n            PowerRegisterForEffectivePowerModeNotifications, PowerSettingRegisterNotification,\n            PowerUnregisterFromEffectivePowerModeNotifications, DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS,\n            EFFECTIVE_POWER_MODE, EFFECTIVE_POWER_MODE_V2, HPOWERNOTIFY, SYSTEM_POWER_STATUS,\n        },\n        SystemServices::GUID_BATTERY_PERCENTAGE_REMAINING,\n    },\n    UI::WindowsAndMessaging::{\n        DEVICE_NOTIFY_CALLBACK, PBT_APMPOWERSTATUSCHANGE, PBT_APMRESUMEAUTOMATIC,\n        PBT_APMRESUMESUSPEND, WM_POWERBROADCAST,\n    },\n};\n\nuse windows_core::Owned;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    modules::power::domain::power_mode_to_serializable,\n    utils::lock_free::TracedMutex,\n    windows_api::{event_window::subscribe_to_background_window, WindowsApi},\n};\n\nuse super::domain::{battery_to_slu_battery, power_status_to_serializable};\n\n#[derive(Debug)]\npub struct PowerManager {\n    pub power_status: PowerStatus,\n    pub power_mode: PowerMode,\n    pub batteries: Vec<Battery>,\n\n    power_setting_battery_percent_token: Option<Owned<HPOWERNOTIFY>>,\n    power_mode_event_token: Option<isize>,\n}\n\nevent_manager!(PowerManager, PowerManagerEvent);\n\nimpl PowerManager {\n    pub fn instance() -> &'static TracedMutex<Self> {\n        static POWER_MANAGER: LazyLock<TracedMutex<PowerManager>> = LazyLock::new(|| {\n            let mut pm = PowerManager::new();\n            pm.init().log_error();\n            TracedMutex::new(pm)\n        });\n        &POWER_MANAGER\n    }\n\n    fn new() -> Self {\n        Self::default()\n    }\n\n    fn init(&mut self) -> Result<()> {\n        // Get initial power status and batteries\n        self.power_status = Self::get_power_status()?;\n        self.batteries = Self::get_batteries()?;\n\n        let eid = Self::subscribe(|event| {\n            let mut guard = Self::instance().lock();\n            match event {\n                PowerManagerEvent::BatteriesChanged(batteries) => {\n                    guard.batteries = batteries;\n                }\n                PowerManagerEvent::PowerStatusChanged(status) => {\n                    guard.power_status = status;\n                }\n                PowerManagerEvent::PowerModeChanged(mode) => {\n                    guard.power_mode = mode;\n                }\n            }\n        });\n        Self::set_event_handler_priority(&eid, 1);\n\n        // https://learn.microsoft.com/en-us/windows/win32/api/powersetting/nf-powersetting-powerregisterforeffectivepowermodenotifications#remarks\n        unsafe {\n            let mut unregister_token_ptr = std::ptr::null_mut();\n            // Immediately after registration, the callback will be invoked with the current value of the power setting.\n            PowerRegisterForEffectivePowerModeNotifications(\n                EFFECTIVE_POWER_MODE_V2,\n                Some(Self::on_effective_power_mode_change),\n                None,\n                &mut unregister_token_ptr,\n            )?;\n            self.power_mode_event_token = Some(unregister_token_ptr as isize);\n        }\n\n        // https://learn.microsoft.com/en-us/windows/win32/api/powersetting/nf-powersetting-powersettingregisternotification#remarks\n        unsafe {\n            let params = DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {\n                Callback: Some(Self::on_battery_percent_change),\n                ..Default::default()\n            };\n\n            let mut unregister_token_ptr = std::ptr::null_mut();\n            // Immediately after registration, the callback will be invoked with the current value of the power setting.\n            PowerSettingRegisterNotification(\n                &GUID_BATTERY_PERCENTAGE_REMAINING,\n                DEVICE_NOTIFY_CALLBACK,\n                HANDLE(&params as *const _ as _),\n                &mut unregister_token_ptr,\n            )\n            .ok()?;\n            self.power_setting_battery_percent_token =\n                Some(Owned::new(HPOWERNOTIFY(unregister_token_ptr as isize)));\n        }\n\n        subscribe_to_background_window(Self::on_bg_window_proc);\n        Ok(())\n    }\n\n    unsafe extern \"system\" fn on_effective_power_mode_change(\n        mode: EFFECTIVE_POWER_MODE,\n        _ctx: *const std::ffi::c_void,\n    ) {\n        let mut guard = Self::instance().lock();\n        let mode: PowerMode = power_mode_to_serializable(mode);\n        if guard.power_mode != mode {\n            log::trace!(\"Power mode changed to {mode:?}\");\n            guard.power_mode = mode;\n            Self::send(PowerManagerEvent::PowerModeChanged(mode));\n        }\n    }\n\n    unsafe extern \"system\" fn on_battery_percent_change(\n        _context: *const std::ffi::c_void,\n        _type: u32,\n        _setting: *const std::ffi::c_void,\n    ) -> u32 {\n        if let Ok(batteries) = Self::get_batteries() {\n            Self::send(PowerManagerEvent::BatteriesChanged(batteries));\n        }\n        0\n    }\n\n    pub fn get_power_status() -> Result<PowerStatus> {\n        Ok(power_status_to_serializable(\n            WindowsApi::get_system_power_status()?,\n        ))\n    }\n\n    pub fn get_batteries() -> Result<Vec<Battery>> {\n        let mut batteries: Vec<Battery> = Vec::new();\n        let manager = battery::Manager::new()?;\n        for battery in manager.batteries()?.flatten() {\n            batteries.push(battery_to_slu_battery(battery)?);\n        }\n        Ok(batteries)\n    }\n\n    fn on_bg_window_proc(msg: u32, w_param: usize, _l_param: isize) -> Result<()> {\n        if msg != WM_POWERBROADCAST {\n            return Ok(());\n        }\n\n        match w_param as u32 {\n            PBT_APMPOWERSTATUSCHANGE => {\n                let new_status = Self::get_power_status()?;\n                if Self::instance().lock().power_status.ac_line_status != new_status.ac_line_status\n                {\n                    Self::send(PowerManagerEvent::BatteriesChanged(Self::get_batteries()?));\n                }\n\n                log::trace!(\"Power status changed to {new_status:?}\");\n                Self::send(PowerManagerEvent::PowerStatusChanged(new_status));\n            }\n            PBT_APMRESUMESUSPEND | PBT_APMRESUMEAUTOMATIC => {\n                log::trace!(\"System resuming from sleep, scheduling state refresh in 2 seconds\");\n                // Spawn a task to refresh state after 2 seconds\n                // This is necessary because the power state may be stale immediately after wake up\n                tokio::spawn(async {\n                    tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n\n                    log::trace!(\"Refreshing power state after wake up\");\n                    if let Ok(new_status) = Self::get_power_status() {\n                        Self::send(PowerManagerEvent::PowerStatusChanged(new_status));\n                    }\n\n                    if let Ok(batteries) = Self::get_batteries() {\n                        Self::send(PowerManagerEvent::BatteriesChanged(batteries));\n                    }\n                });\n            }\n            _ => {}\n        }\n\n        Ok(())\n    }\n\n    #[allow(dead_code)]\n    pub fn release(&mut self) {\n        if let Some(token) = self.power_mode_event_token.take() {\n            let _ = unsafe { PowerUnregisterFromEffectivePowerModeNotifications(token as _) };\n        }\n        self.power_setting_battery_percent_token = None;\n    }\n}\n\nimpl Default for PowerManager {\n    fn default() -> Self {\n        Self {\n            power_status: power_status_to_serializable(SYSTEM_POWER_STATUS::default()),\n            power_mode: PowerMode::Unknown,\n            batteries: Vec::new(),\n            power_mode_event_token: None,\n            power_setting_battery_percent_token: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\n#[allow(clippy::enum_variant_names)]\npub enum PowerManagerEvent {\n    PowerStatusChanged(PowerStatus),\n    BatteriesChanged(Vec<Battery>),\n    PowerModeChanged(PowerMode),\n}\n"
  },
  {
    "path": "src/background/modules/power/domain.rs",
    "content": "use seelen_core::system_state::{Battery, PowerMode, PowerStatus};\nuse windows::Win32::System::Power::{EFFECTIVE_POWER_MODE, SYSTEM_POWER_STATUS};\n\nuse crate::error::Result;\n\npub fn power_status_to_serializable(power_status: SYSTEM_POWER_STATUS) -> PowerStatus {\n    PowerStatus {\n        ac_line_status: power_status.ACLineStatus,\n        battery_flag: power_status.BatteryFlag,\n        battery_life_percent: power_status.BatteryLifePercent,\n        system_status_flag: power_status.SystemStatusFlag,\n        battery_life_time: power_status.BatteryLifeTime,\n        battery_full_life_time: power_status.BatteryFullLifeTime,\n    }\n}\n\npub fn power_mode_to_serializable(mode: EFFECTIVE_POWER_MODE) -> PowerMode {\n    match mode.0 {\n        0 => PowerMode::BatterySaver,\n        1 => PowerMode::BetterBattery,\n        2 => PowerMode::Balanced,\n        3 => PowerMode::HighPerformance,\n        4 => PowerMode::MaxPerformance,\n        5 => PowerMode::GameMode,\n        6 => PowerMode::MixedReality,\n        _ => PowerMode::Unknown,\n    }\n}\n\npub fn battery_to_slu_battery(battery: battery::Battery) -> Result<Battery> {\n    let percentage = (battery.state_of_charge().value * 100.0).round();\n\n    Ok(Battery {\n        vendor: battery.vendor().map(|v| v.to_string()),\n        model: battery.model().map(|v| v.to_string()),\n        serial_number: battery.serial_number().map(|v| v.to_string()),\n        technology: battery.technology().to_string(),\n\n        state: battery.state().to_string(),\n        capacity: battery.state_of_health().value,\n        temperature: battery.temperature().map(|t| t.value),\n        percentage,\n        cycle_count: battery.cycle_count(),\n        // smart charging means that battery wont be fully charged.\n        smart_charging: battery.state() == battery::State::Full && percentage < 100.0,\n\n        energy: battery.energy().value,\n        energy_full: battery.energy_full().value,\n        energy_full_design: battery.energy_full_design().value,\n        energy_rate: battery.energy_rate().value,\n        voltage: battery.voltage().value,\n\n        time_to_full: battery.time_to_full().map(|t| t.value),\n        time_to_empty: battery.time_to_empty().map(|t| t.value),\n    })\n}\n"
  },
  {
    "path": "src/background/modules/power/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{Battery, PowerMode, PowerStatus},\n};\nuse windows::Win32::System::Shutdown::{EWX_LOGOFF, EWX_REBOOT, EWX_SHUTDOWN, SHTDN_REASON_NONE};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::Result,\n    log_error,\n    modules::power::application::{PowerManager, PowerManagerEvent},\n    utils::lock_free::TracedMutex,\n    windows_api::WindowsApi,\n};\n\n/// Lazy initialization wrapper that registers Tauri events on first access\n/// This keeps Tauri logic separate from system logic while ensuring lazy initialization\nfn get_power_manager() -> &'static TracedMutex<PowerManager> {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        PowerManager::subscribe(|event| match event {\n            PowerManagerEvent::PowerStatusChanged(status) => {\n                emit_to_webviews(SeelenEvent::PowerStatus, status);\n            }\n            PowerManagerEvent::BatteriesChanged(batteries) => {\n                emit_to_webviews(SeelenEvent::BatteriesStatus, batteries);\n            }\n            PowerManagerEvent::PowerModeChanged(mode) => {\n                emit_to_webviews(SeelenEvent::PowerMode, mode);\n            }\n        });\n    });\n    PowerManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_power_status() -> PowerStatus {\n    get_power_manager().lock().power_status.clone()\n}\n\n#[tauri::command(async)]\npub fn get_power_mode() -> PowerMode {\n    get_power_manager().lock().power_mode\n}\n\n#[tauri::command(async)]\npub fn get_batteries() -> Vec<Battery> {\n    get_power_manager().lock().batteries.clone()\n}\n\n#[tauri::command(async)]\npub fn log_out() {\n    log_error!(WindowsApi::exit_windows(EWX_LOGOFF, SHTDN_REASON_NONE));\n}\n\n#[tauri::command(async)]\npub fn suspend() {\n    log_error!(WindowsApi::set_suspend_state(false));\n}\n\n#[tauri::command(async)]\npub fn hibernate() {\n    log_error!(WindowsApi::set_suspend_state(true));\n}\n\n#[tauri::command(async)]\npub fn restart() -> Result<()> {\n    WindowsApi::exit_windows(EWX_REBOOT, SHTDN_REASON_NONE)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn shutdown() -> Result<()> {\n    WindowsApi::exit_windows(EWX_SHUTDOWN, SHTDN_REASON_NONE)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn lock() -> Result<()> {\n    WindowsApi::lock_machine()?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/modules/power/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/radios/bluetooth/classic.rs",
    "content": "use seelen_core::system_state::BluetoothDevice as SerializableBluetoothDevice;\nuse windows::{Devices::Bluetooth::BluetoothDevice, Foundation::TypedEventHandler};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    modules::radios::bluetooth::{\n        manager::BluetoothManager, BluetoothDeviceType, BluetoothManagerEvent,\n    },\n};\n\npub struct BluetoothDeviceWrapper {\n    pub(super) id: String,\n    pub(super) raw: BluetoothDevice,\n    pub(super) state: SerializableBluetoothDevice,\n\n    name_changed_token: i64,\n    connection_status_changed_token: i64,\n}\n\nimpl BluetoothDeviceWrapper {\n    pub fn create(device_id: &str) -> Result<Self> {\n        let device = BluetoothDevice::FromIdAsync(&device_id.into())?.join()?;\n\n        let id = device_id.to_string();\n        let name_changed_token =\n            device.NameChanged(&TypedEventHandler::new(move |_src, _args| {\n                BluetoothManager::send(BluetoothManagerEvent::DeviceUpdated(\n                    id.clone(),\n                    BluetoothDeviceType::Classic,\n                ));\n                Ok(())\n            }))?;\n\n        let id = device_id.to_string();\n        let connection_status_changed_token =\n            device.ConnectionStatusChanged(&TypedEventHandler::new(move |_src, _args| {\n                BluetoothManager::send(BluetoothManagerEvent::DeviceUpdated(\n                    id.clone(),\n                    BluetoothDeviceType::Classic,\n                ));\n                Ok(())\n            }))?;\n\n        Ok(Self {\n            id: device_id.to_string(),\n            state: Self::to_serializable(device_id, &device)?,\n            raw: device,\n            name_changed_token,\n            connection_status_changed_token,\n        })\n    }\n\n    pub fn refresh_state(&mut self) -> Result<()> {\n        self.state = Self::to_serializable(&self.id, &self.raw)?;\n        Ok(())\n    }\n\n    pub fn disconnect(&self) -> Result<()> {\n        // For Classic Bluetooth devices, Windows doesn't provide a direct disconnect API\n        // The recommended approach is to close the device and let Windows handle the timeout\n        // According to Microsoft docs, Windows will disconnect after 1 second if no references exist\n\n        // Close the device object - Windows will disconnect when there are no active references\n        self.raw.Close()?;\n\n        // Note: For Classic Bluetooth, if this doesn't work, the only reliable way\n        // is to unpair the device using DeviceInformation.Pairing().UnpairAsync()\n        Ok(())\n    }\n\n    pub fn to_serializable(\n        id: &str,\n        device: &BluetoothDevice,\n    ) -> Result<SerializableBluetoothDevice> {\n        use windows::Devices::Bluetooth::BluetoothConnectionStatus;\n\n        let class = device.ClassOfDevice()?;\n        let pairing_state = device.DeviceInformation()?.Pairing()?;\n        let class_value = class.RawValue()?;\n        let (major_service_classes, major_class, minor_class) =\n            SerializableBluetoothDevice::get_parts_of_class(class_value);\n\n        let is_connected = device.ConnectionStatus()? == BluetoothConnectionStatus::Connected;\n        let is_paired = pairing_state.IsPaired()?;\n\n        Ok(SerializableBluetoothDevice {\n            id: id.to_owned(),\n            name: device.Name()?.to_string(),\n            address: device.BluetoothAddress()?,\n            major_service_classes,\n            major_class,\n            minor_class,\n            appearance: None,\n            connected: is_connected,\n            paired: is_paired,\n            can_pair: pairing_state.CanPair()?,\n            can_disconnect: false, // TODO: this will be false until get a way to realize the disconnection without unpairing.\n            is_low_energy: false,\n        })\n    }\n}\n\nimpl Drop for BluetoothDeviceWrapper {\n    fn drop(&mut self) {\n        self.raw\n            .RemoveNameChanged(self.name_changed_token)\n            .log_error();\n        self.raw\n            .RemoveConnectionStatusChanged(self.connection_status_changed_token)\n            .log_error();\n    }\n}\n"
  },
  {
    "path": "src/background/modules/radios/bluetooth/handlers.rs",
    "content": "use seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{BluetoothDevice, DevicePairingAnswer, DevicePairingNeededAction},\n};\nuse windows::Devices::Enumeration::{DevicePairingResultStatus, DeviceUnpairingResultStatus};\n\nuse crate::{\n    app::emit_to_webviews, error::Result, modules::radios::bluetooth::manager::BluetoothManager,\n};\n\nfn get_bluetooth_manager() -> &'static BluetoothManager {\n    static INIT: std::sync::Once = std::sync::Once::new();\n    INIT.call_once(|| {\n        BluetoothManager::subscribe(|_e| {\n            emit_to_webviews(\n                SeelenEvent::BluetoothDevicesChanged,\n                BluetoothManager::instance().get_all_devices(),\n            );\n        });\n    });\n    BluetoothManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_bluetooth_devices() -> Vec<BluetoothDevice> {\n    get_bluetooth_manager().get_all_devices()\n}\n\n#[tauri::command(async)]\npub fn start_bluetooth_scanning() -> Result<()> {\n    get_bluetooth_manager().start_scanning()\n}\n\n#[tauri::command(async)]\npub fn stop_bluetooth_scanning() -> Result<()> {\n    get_bluetooth_manager().stop_scanning()\n}\n\n#[tauri::command(async)]\npub async fn request_pair_bluetooth_device(id: String) -> Result<DevicePairingNeededAction> {\n    let manager = get_bluetooth_manager();\n    manager.request_pair_device(&id).await\n}\n\n#[tauri::command(async)]\npub async fn confirm_bluetooth_device_pairing(\n    id: String,\n    answer: DevicePairingAnswer,\n) -> Result<()> {\n    let expected_status = if answer.accept {\n        DevicePairingResultStatus::Paired\n    } else {\n        DevicePairingResultStatus::RejectedByHandler\n    };\n\n    let manager = get_bluetooth_manager();\n    let status = manager.confirm_device_pairing(&id, answer).await?;\n\n    if status != expected_status {\n        return Err(\n            format!(\"Pairing action was not successful! Current status: {status:?}\").into(),\n        );\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn disconnect_bluetooth_device(id: String) -> Result<()> {\n    let manager = get_bluetooth_manager();\n    manager.disconnect_device(&id)\n}\n\n#[tauri::command(async)]\npub async fn forget_bluetooth_device(id: String) -> Result<()> {\n    let manager = get_bluetooth_manager();\n    let device = if let Some(classic) = manager.devices.get(&id, |d| d.raw.clone()) {\n        classic.DeviceInformation()?\n    } else if let Some(le) = manager.le_devices.get(&id, |d| d.raw.clone()) {\n        le.DeviceInformation()?\n    } else {\n        return Ok(());\n    };\n\n    let status = device.Pairing()?.UnpairAsync()?.await?.Status()?;\n    if status == DeviceUnpairingResultStatus::AccessDenied\n        || status == DeviceUnpairingResultStatus::Failed\n    {\n        return Err(\"Unpair was not succesfull!\".into());\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/modules/radios/bluetooth/low_energy.rs",
    "content": "use seelen_core::system_state::BluetoothDevice as SerializableBluetoothDevice;\nuse windows::{Devices::Bluetooth::BluetoothLEDevice, Foundation::TypedEventHandler};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    modules::radios::bluetooth::{\n        manager::BluetoothManager, BluetoothDeviceType, BluetoothManagerEvent,\n    },\n};\n\npub struct BluetoothLEDeviceWrapper {\n    pub(super) id: String,\n    pub(super) raw: BluetoothLEDevice,\n    pub(super) state: SerializableBluetoothDevice,\n\n    name_changed_token: i64,\n    connection_status_changed_token: i64,\n}\n\nimpl BluetoothLEDeviceWrapper {\n    pub fn create(device_id: &str) -> Result<Self> {\n        let device = BluetoothLEDevice::FromIdAsync(&device_id.into())?.join()?;\n\n        let id = device_id.to_string();\n        let name_changed_token =\n            device.NameChanged(&TypedEventHandler::new(move |_src, _args| {\n                BluetoothManager::send(BluetoothManagerEvent::DeviceUpdated(\n                    id.clone(),\n                    BluetoothDeviceType::LowEnergy,\n                ));\n                Ok(())\n            }))?;\n\n        let id = device_id.to_string();\n        let connection_status_changed_token =\n            device.ConnectionStatusChanged(&TypedEventHandler::new(move |_src, _args| {\n                BluetoothManager::send(BluetoothManagerEvent::DeviceUpdated(\n                    id.clone(),\n                    BluetoothDeviceType::LowEnergy,\n                ));\n                Ok(())\n            }))?;\n\n        Ok(Self {\n            id: device_id.to_string(),\n            state: Self::to_serializable(device_id, &device)?,\n            raw: device,\n            name_changed_token,\n            connection_status_changed_token,\n        })\n    }\n\n    pub fn refresh_state(&mut self) -> Result<()> {\n        self.state = Self::to_serializable(&self.id, &self.raw)?;\n        Ok(())\n    }\n\n    pub fn close(self) -> Result<()> {\n        self.raw.RemoveNameChanged(self.name_changed_token)?;\n        self.raw\n            .RemoveConnectionStatusChanged(self.connection_status_changed_token)?;\n\n        // For BLE devices, we need to close all GATT sessions first, then services\n        // According to Microsoft docs, closing sessions is what actually triggers disconnect\n        let services = self.raw.GetGattServicesAsync()?.join()?.Services()?;\n        for service in services {\n            if let Ok(session) = service.Session() {\n                session.Close()?;\n            }\n            service.Close()?;\n        }\n\n        // Finally close the device object itself\n        self.raw.Close()?;\n        Ok(())\n    }\n\n    pub fn to_serializable(\n        id: &str,\n        device: &BluetoothLEDevice,\n    ) -> Result<SerializableBluetoothDevice> {\n        use seelen_core::system_state::enums::{BluetoothMajorClass, BluetoothMinorClass};\n        use windows::Devices::Bluetooth::BluetoothConnectionStatus;\n\n        let pairing_state = device.DeviceInformation()?.Pairing()?;\n\n        let is_connected = device.ConnectionStatus()? == BluetoothConnectionStatus::Connected;\n        let is_paired = pairing_state.IsPaired()?;\n\n        Ok(SerializableBluetoothDevice {\n            id: id.to_owned(),\n            name: device.Name()?.to_string(),\n            address: device.BluetoothAddress()?,\n            major_service_classes: Vec::new(),\n            major_class: BluetoothMajorClass::Uncategorized,\n            minor_class: BluetoothMinorClass::Uncategorized { unused: 0 },\n            appearance: Some(device.Appearance()?.RawValue()?.into()),\n            connected: is_connected,\n            paired: is_paired,\n            can_pair: pairing_state.CanPair()?,\n            can_disconnect: false, // TODO: this will be false until get a way to realize the disconnection without unpairing.\n            is_low_energy: true,\n        })\n    }\n}\n\nimpl Drop for BluetoothLEDeviceWrapper {\n    fn drop(&mut self) {\n        self.raw\n            .RemoveNameChanged(self.name_changed_token)\n            .log_error();\n        self.raw\n            .RemoveConnectionStatusChanged(self.connection_status_changed_token)\n            .log_error();\n    }\n}\n"
  },
  {
    "path": "src/background/modules/radios/bluetooth/manager.rs",
    "content": "use std::sync::{Arc, LazyLock};\n\nuse arc_swap::ArcSwapOption;\nuse seelen_core::system_state::{\n    BluetoothDevice as SerializableBluetoothDevice, DevicePairingAnswer, DevicePairingNeededAction,\n};\nuse tokio::sync::mpsc;\nuse windows::{\n    Devices::{\n        Bluetooth::{BluetoothDevice, BluetoothLEDevice},\n        Enumeration::{\n            DeviceInformation, DeviceInformationCustomPairing, DevicePairingKinds,\n            DevicePairingProtectionLevel, DevicePairingRequestedEventArgs, DevicePairingResult,\n            DevicePairingResultStatus,\n        },\n    },\n    Foundation::{Deferral, TypedEventHandler},\n    Security::Credentials::PasswordCredential,\n};\nuse windows_future::IAsyncOperation;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager, get_tokio_handle, log_error,\n    utils::lock_free::SyncHashMap,\n    windows_api::{DeviceEnumerator, DeviceEvent, DeviceId},\n};\n\nuse super::{\n    classic::BluetoothDeviceWrapper, low_energy::BluetoothLEDeviceWrapper, BluetoothDeviceType,\n    BluetoothManagerEvent,\n};\n\n// Pairing configuration constants\nconst PAIRING_REQUEST_TIMEOUT_SECS: u64 = 10;\nconst PAIRING_CONFIRMATION_MAX_RETRIES: u32 = 20;\nconst PAIRING_CONFIRMATION_RETRY_INTERVAL_MS: u64 = 500;\n\nstatic BLUETOOTH_MANAGER_INSTANCE: LazyLock<BluetoothManager> = LazyLock::new(|| {\n    let mut m = BluetoothManager::create();\n    log_error!(m.initialize());\n    m\n});\n\npub struct BluetoothManager {\n    pub devices: SyncHashMap<DeviceId, BluetoothDeviceWrapper>,\n    pub le_devices: SyncHashMap<DeviceId, BluetoothLEDeviceWrapper>,\n\n    classic_enumerator: Option<DeviceEnumerator>,\n    le_enumerator: Option<DeviceEnumerator>,\n\n    // Discovery/scanning enumerators (unpaired devices)\n    discovery_classic_enumerator: ArcSwapOption<DeviceEnumerator>,\n    discovery_le_enumerator: ArcSwapOption<DeviceEnumerator>,\n\n    // Pairing state\n    pending_pair_requests: SyncHashMap<DeviceId, PendingPairRequest>,\n}\n\nunsafe impl Send for BluetoothManager {}\nunsafe impl Sync for BluetoothManager {}\n\nevent_manager!(BluetoothManager, BluetoothManagerEvent);\n\n/// Helper function to map DeviceEvent to BluetoothManagerEvent and handle it\nfn handle_device_event(event: DeviceEvent, device_type: BluetoothDeviceType) {\n    let bt_event = match event {\n        DeviceEvent::Added(id) => BluetoothManagerEvent::DeviceAdded(id, device_type),\n        DeviceEvent::Updated(id) => BluetoothManagerEvent::DeviceUpdated(id, device_type),\n        DeviceEvent::Removed(id) => BluetoothManagerEvent::DeviceRemoved(id, device_type),\n    };\n    BluetoothManager::send(bt_event);\n}\n\n#[allow(dead_code)]\nimpl BluetoothManager {\n    fn create() -> Self {\n        Self {\n            devices: SyncHashMap::new(),\n            le_devices: SyncHashMap::new(),\n            classic_enumerator: None,\n            le_enumerator: None,\n            discovery_classic_enumerator: ArcSwapOption::from(None),\n            discovery_le_enumerator: ArcSwapOption::from(None),\n            pending_pair_requests: SyncHashMap::new(),\n        }\n    }\n\n    fn initialize(&mut self) -> Result<()> {\n        // Self subscription\n        let eid = Self::subscribe(|event| Self::instance().on_event(&event).log_error());\n        Self::set_event_handler_priority(&eid, 1);\n\n        // Initialize Bluetooth classic devices enumerator\n        let classic_enumerator =\n            DeviceEnumerator::new(BluetoothDevice::GetDeviceSelector()?.to_string(), |event| {\n                handle_device_event(event, BluetoothDeviceType::Classic)\n            })?;\n\n        // Start enumeration and get initial Bluetooth classic devices\n        let classic_devices = classic_enumerator.start_blocking()?;\n        let devices: Result<Vec<BluetoothDeviceWrapper>> = classic_devices\n            .iter()\n            .map(|device| {\n                let id = device.Id()?.to_string();\n                BluetoothDeviceWrapper::create(&id)\n            })\n            .collect();\n\n        let devices_map: std::collections::HashMap<DeviceId, BluetoothDeviceWrapper> = devices?\n            .into_iter()\n            .map(|wrapper| (wrapper.id.clone(), wrapper))\n            .collect();\n        self.devices = SyncHashMap::from(devices_map);\n        self.classic_enumerator = Some(classic_enumerator);\n\n        // Initialize Bluetooth LE devices enumerator\n        let le_enumerator = DeviceEnumerator::new(\n            BluetoothLEDevice::GetDeviceSelector()?.to_string(),\n            |event| handle_device_event(event, BluetoothDeviceType::LowEnergy),\n        )?;\n\n        // Start enumeration and get initial Bluetooth LE devices\n        let le_devices = le_enumerator.start_blocking()?;\n        let devices_le: Result<Vec<BluetoothLEDeviceWrapper>> = le_devices\n            .iter()\n            .map(|device| {\n                let id = device.Id()?.to_string();\n                BluetoothLEDeviceWrapper::create(&id)\n            })\n            .collect();\n\n        let devices_le_map: std::collections::HashMap<DeviceId, BluetoothLEDeviceWrapper> =\n            devices_le?\n                .into_iter()\n                .map(|wrapper| (wrapper.id.clone(), wrapper))\n                .collect();\n        self.le_devices = SyncHashMap::from(devices_le_map);\n        self.le_enumerator = Some(le_enumerator);\n\n        Ok(())\n    }\n\n    pub fn instance() -> &'static Self {\n        &BLUETOOTH_MANAGER_INSTANCE\n    }\n\n    fn on_event(&self, event: &BluetoothManagerEvent) -> Result<()> {\n        match event {\n            BluetoothManagerEvent::DeviceAdded(id, device_type) => match device_type {\n                BluetoothDeviceType::Classic => {\n                    let wrapper = BluetoothDeviceWrapper::create(id)?;\n                    self.devices.upsert(id.clone(), wrapper);\n                }\n                BluetoothDeviceType::LowEnergy => {\n                    let wrapper = BluetoothLEDeviceWrapper::create(id)?;\n                    self.le_devices.upsert(id.clone(), wrapper);\n                }\n            },\n            BluetoothManagerEvent::DeviceUpdated(id, device_type) => match device_type {\n                BluetoothDeviceType::Classic => {\n                    self.devices.get(id, |device| {\n                        device.refresh_state().log_error();\n                    });\n                }\n                BluetoothDeviceType::LowEnergy => {\n                    self.le_devices.get(id, |device| {\n                        device.refresh_state().log_error();\n                    });\n                }\n            },\n            BluetoothManagerEvent::DeviceRemoved(id, device_type) => {\n                self.pending_pair_requests.remove(id);\n                match device_type {\n                    BluetoothDeviceType::Classic => {\n                        self.devices.remove(id);\n                    }\n                    BluetoothDeviceType::LowEnergy => {\n                        self.le_devices.remove(id);\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n\n    pub fn get_classic_devices(&self) -> Vec<SerializableBluetoothDevice> {\n        let mut devices = Vec::new();\n        self.devices.for_each(|(_id, wrapper)| {\n            devices.push(wrapper.state.clone());\n        });\n        devices\n    }\n\n    pub fn get_le_devices(&self) -> Vec<SerializableBluetoothDevice> {\n        let mut devices = Vec::new();\n        self.le_devices.for_each(|(_id, wrapper)| {\n            devices.push(wrapper.state.clone());\n        });\n        devices\n    }\n\n    pub fn get_all_devices(&self) -> Vec<SerializableBluetoothDevice> {\n        let mut devices = self.get_classic_devices();\n        devices.extend(self.get_le_devices());\n        devices\n    }\n\n    /// Start scanning for unpaired Bluetooth devices\n    /// Uses GetDeviceSelectorFromPairingState(false) which automatically turns on system scanning\n    pub fn start_scanning(&self) -> Result<()> {\n        // If already scanning, do nothing\n        if self.discovery_classic_enumerator.load().is_some()\n            || self.discovery_le_enumerator.load().is_some()\n        {\n            return Ok(());\n        }\n\n        // Start scanning for unpaired classic Bluetooth devices\n        let classic_selector = BluetoothDevice::GetDeviceSelectorFromPairingState(false)?;\n        let discovery_classic_enumerator =\n            DeviceEnumerator::new(classic_selector.to_string(), |event| {\n                handle_device_event(event, BluetoothDeviceType::Classic)\n            })?;\n\n        discovery_classic_enumerator.start()?;\n        self.discovery_classic_enumerator\n            .store(Some(Arc::new(discovery_classic_enumerator)));\n\n        // Start scanning for unpaired Bluetooth LE devices\n        let le_selector = BluetoothLEDevice::GetDeviceSelectorFromPairingState(false)?;\n        let discovery_le_enumerator = DeviceEnumerator::new(le_selector.to_string(), |event| {\n            handle_device_event(event, BluetoothDeviceType::LowEnergy)\n        })?;\n\n        discovery_le_enumerator.start()?;\n        self.discovery_le_enumerator\n            .store(Some(Arc::new(discovery_le_enumerator)));\n\n        Ok(())\n    }\n\n    /// Stop scanning for unpaired Bluetooth devices\n    pub fn stop_scanning(&self) -> Result<()> {\n        // Discovery enumerators are automatically stopped via Drop trait\n        self.discovery_classic_enumerator.store(None);\n        self.discovery_le_enumerator.store(None);\n        Ok(())\n    }\n\n    /// Prepares a device for pairing by setting up event handlers and creating the pending request.\n    /// Returns the mpsc receiver, pair handler, and protection level needed for pairing.\n    fn prepare_pair_device(\n        &self,\n        device_id: &str,\n    ) -> Result<(\n        mpsc::Receiver<Result<DevicePairingNeededAction>>,\n        DeviceInformationCustomPairing,\n        DevicePairingProtectionLevel,\n    )> {\n        let device = DeviceInformation::CreateFromIdAsync(&device_id.into())?.join()?;\n        let pairing = device.Pairing()?;\n\n        if pairing.IsPaired()? {\n            return Err(\"Device is already paired\".into());\n        }\n\n        if !pairing.CanPair()? {\n            return Err(\"Device cannot be paired\".into());\n        }\n\n        let protection_level = pairing.ProtectionLevel()?;\n        let pair_handler = pairing.Custom()?;\n\n        let (tx, rx) = mpsc::channel::<Result<DevicePairingNeededAction>>(1);\n\n        // Setup the pairing requested event handler\n        let device_id_clone = device_id.to_string();\n        let event_token =\n            pair_handler.PairingRequested(&TypedEventHandler::new(move |_sender, args| {\n                log::trace!(\"Pairing requested for device {}\", device_id_clone);\n                let result = Self::on_pair_request(args, device_id_clone.clone());\n                let tx = tx.clone();\n                get_tokio_handle().spawn(async move {\n                    if let Err(e) = tx.send(result).await {\n                        log::error!(\"Failed to send pairing result: {:?}\", e);\n                    }\n                });\n                Ok(())\n            }))?;\n\n        // Create initial pending request (will be updated by callback and after pairing)\n        self.pending_pair_requests.upsert(\n            device_id.to_string(),\n            PendingPairRequest {\n                handler: pair_handler.clone(),\n                event_token,\n                action: DevicePairingNeededAction::None,\n                async_operation: None,\n                request: None,\n                deferral: None,\n            },\n        );\n\n        Ok((rx, pair_handler, protection_level))\n    }\n\n    /// Starts the pairing process with the given device and waits for the action required.\n    /// Returns the action needed from the user.\n    async fn start_pairing(\n        &self,\n        device_id: &str,\n        mut rx: mpsc::Receiver<Result<DevicePairingNeededAction>>,\n        pair_handler: DeviceInformationCustomPairing,\n        protection_level: DevicePairingProtectionLevel,\n    ) -> Result<DevicePairingNeededAction> {\n        log::trace!(\"Starting pairing for device {}\", device_id);\n\n        // Start the pairing async operation (but don't await it yet to avoid deadlock)\n        // The operation will call the PairingRequested callback, which will send us the action\n        let pair_async_op = pair_handler.PairWithProtectionLevelAsync(\n            DevicePairingKinds::ConfirmOnly\n                | DevicePairingKinds::DisplayPin\n                | DevicePairingKinds::ProvidePin\n                | DevicePairingKinds::ConfirmPinMatch\n                | DevicePairingKinds::ProvidePasswordCredential\n                | DevicePairingKinds::ProvideAddress,\n            protection_level,\n        )?;\n\n        // Wait for the callback to determine what action is needed\n        // This must happen BEFORE we await the pairing result to avoid deadlock\n        // (the callback creates a Deferral that pauses the pairing operation)\n        let action = tokio::time::timeout(\n            std::time::Duration::from_secs(PAIRING_REQUEST_TIMEOUT_SECS),\n            rx.recv(),\n        )\n        .await\n        .map_err(|_| {\n            format!(\n                \"Pairing request timed out after {} seconds\",\n                PAIRING_REQUEST_TIMEOUT_SECS\n            )\n        })?\n        .ok_or(\"Pairing channel closed unexpectedly\")??;\n\n        // If no valid action is needed, pairing cannot proceed\n        if action == DevicePairingNeededAction::None {\n            return Err(\"Device pairing requires unsupported action\".into());\n        }\n\n        // Store the async operation for later (will be awaited after user confirmation)\n        self.pending_pair_requests.get(device_id, |pending| {\n            pending.async_operation = Some(pair_async_op);\n        });\n\n        Ok(action)\n    }\n\n    /// Initiates pairing with a device and returns the action required from the user.\n    /// The pending pairing state is stored internally until confirmed or cancelled.\n    pub async fn request_pair_device(&self, device_id: &str) -> Result<DevicePairingNeededAction> {\n        // Prepare the device for pairing\n        let (rx, pair_handler, protection_level) = self.prepare_pair_device(device_id)?;\n\n        // Start pairing and handle cleanup on failure\n        match self\n            .start_pairing(device_id, rx, pair_handler, protection_level)\n            .await\n        {\n            Ok(action) => Ok(action),\n            Err(e) => {\n                // Clean up pending request if pairing failed\n                self.pending_pair_requests.remove(device_id);\n                Err(e)\n            }\n        }\n    }\n\n    /// Confirms or rejects a pending pairing request with user input.\n    /// Completes the pairing process and returns the final status.\n    pub async fn confirm_device_pairing(\n        &self,\n        device_id: &str,\n        answer: DevicePairingAnswer,\n    ) -> Result<DevicePairingResultStatus> {\n        let Some(mut pending) = self.pending_pair_requests.remove(device_id) else {\n            return Err(format!(\"No pending pairing request for device {device_id}\").into());\n        };\n\n        let event_args = pending.request.as_ref().ok_or(\"Pairing args are null\")?;\n\n        // Extract the async operation before dropping pending\n        let async_op = pending\n            .async_operation\n            .take()\n            .ok_or(\"Pairing async operation is null\")?;\n\n        // Apply the user's answer to the pairing request\n        if answer.accept {\n            if let Some(pin) = answer.pin {\n                event_args.AcceptWithPin(&pin.into())?;\n            } else if let (Some(username), Some(password)) = (answer.username, answer.password) {\n                let credential = PasswordCredential::CreatePasswordCredential(\n                    &\"\".into(),\n                    &username.into(),\n                    &password.into(),\n                )?;\n                event_args.AcceptWithPasswordCredential(&credential)?;\n            } else if let Some(address) = answer.address {\n                event_args.AcceptWithAddress(&address.into())?;\n            } else {\n                event_args.Accept()?;\n            }\n        }\n\n        // Drop the pending request, which completes the deferral and allows pairing to proceed\n        drop(pending);\n\n        // NOW we can await the pairing result (after deferral.Complete() was called in Drop)\n        let result = async_op.await?;\n        log::trace!(\"Pairing result for device {}: {:?}\", device_id, result);\n\n        // Wait for pairing to complete (for DisplayPin/ConfirmPinMatch, the other device must confirm too)\n        if answer.accept {\n            let mut paired = false;\n            for i in 0..PAIRING_CONFIRMATION_MAX_RETRIES {\n                if result.Status()? == DevicePairingResultStatus::Paired {\n                    log::info!(\n                        \"Pairing confirmed for device {} after {} attempts\",\n                        device_id,\n                        i + 1\n                    );\n                    paired = true;\n                    break;\n                }\n\n                tokio::time::sleep(std::time::Duration::from_millis(\n                    PAIRING_CONFIRMATION_RETRY_INTERVAL_MS,\n                ))\n                .await;\n            }\n\n            if !paired {\n                log::warn!(\n                    \"Pairing polling completed for device {} without confirmation. Cancelling pairing.\",\n                    device_id\n                );\n            }\n        }\n\n        Ok(result.Status()?)\n    }\n\n    /// Handles the pairing requested callback from Windows.\n    /// Determines what action is needed and updates the pending request.\n    fn on_pair_request(\n        request: windows_core::Ref<DevicePairingRequestedEventArgs>,\n        device_id: String,\n    ) -> Result<DevicePairingNeededAction> {\n        let Some(request) = request.as_ref() else {\n            return Err(format!(\"Pairing args are null for device {}\", device_id).into());\n        };\n\n        let kind = request.PairingKind()?;\n        log::trace!(\"Pairing kind for device {}: {:?}\", device_id, kind);\n\n        // Determine what action is needed from the user based on pairing kind\n        let action = match kind {\n            DevicePairingKinds::None => DevicePairingNeededAction::None,\n            DevicePairingKinds::ConfirmOnly => DevicePairingNeededAction::ConfirmOnly,\n            DevicePairingKinds::DisplayPin => {\n                let pin = request.Pin()?.to_string();\n                DevicePairingNeededAction::DisplayPin { pin }\n            }\n            DevicePairingKinds::ProvidePin => DevicePairingNeededAction::ProvidePin,\n            DevicePairingKinds::ConfirmPinMatch => {\n                let pin = request.Pin()?.to_string();\n                DevicePairingNeededAction::ConfirmPinMatch { pin }\n            }\n            DevicePairingKinds::ProvidePasswordCredential => {\n                DevicePairingNeededAction::ProvidePasswordCredential\n            }\n            DevicePairingKinds::ProvideAddress => DevicePairingNeededAction::ProvideAddress,\n            _ => {\n                log::warn!(\"Unsupported pairing kind for device {device_id}: {kind:?}\");\n                DevicePairingNeededAction::None\n            }\n        };\n\n        if action != DevicePairingNeededAction::None {\n            Self::instance()\n                .pending_pair_requests\n                .get(&device_id, |pending| {\n                    pending.action = action.clone();\n                    pending.request = Some(request.clone());\n                    // The deferral makes the pairing operation wait until user confirmation\n                    pending.deferral = request.GetDeferral().ok();\n                });\n        }\n\n        Ok(action)\n    }\n\n    /// Disconnects a paired device without unpairing it.\n    ///\n    /// Todo: this is not working.\n    pub fn disconnect_device(&self, device_id: &str) -> Result<()> {\n        if self\n            .devices\n            .get(device_id, |device| {\n                device.disconnect().log_error();\n            })\n            .is_some()\n        {\n            return Ok(());\n        }\n\n        if let Some(device) = self.le_devices.remove(device_id) {\n            device.close()?;\n            return Ok(());\n        }\n\n        Err(format!(\"Device not found: {}\", device_id).into())\n    }\n\n    pub fn release(&mut self) {\n        // Device enumerators are automatically stopped via Drop trait\n        self.devices.clear();\n        self.le_devices.clear();\n        log_error!(self.stop_scanning());\n    }\n}\n\npub struct PendingPairRequest {\n    /// Custom pairing interface for the device\n    pub handler: DeviceInformationCustomPairing,\n    /// Event handler token to remove on cleanup\n    pub event_token: i64,\n    /// Action required from the user\n    pub action: DevicePairingNeededAction,\n    /// Pairing async operation (to be awaited after user confirmation)\n    pub async_operation: Option<IAsyncOperation<DevicePairingResult>>,\n    /// Event arguments from the pairing callback\n    pub request: Option<DevicePairingRequestedEventArgs>,\n    /// Deferral to control async pairing flow (present if user input is needed)\n    pub deferral: Option<Deferral>,\n}\n\nimpl Drop for PendingPairRequest {\n    fn drop(&mut self) {\n        if let Some(deferral) = &self.deferral {\n            let _ = deferral.Complete();\n        }\n        let _ = self.handler.RemovePairingRequested(self.event_token);\n    }\n}\n"
  },
  {
    "path": "src/background/modules/radios/bluetooth/mod.rs",
    "content": "mod classic;\npub mod handlers;\nmod low_energy;\nmod manager;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum BluetoothDeviceType {\n    Classic,\n    LowEnergy,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[allow(clippy::enum_variant_names)]\npub enum BluetoothManagerEvent {\n    DeviceAdded(String, BluetoothDeviceType),\n    DeviceUpdated(String, BluetoothDeviceType),\n    DeviceRemoved(String, BluetoothDeviceType),\n}\n"
  },
  {
    "path": "src/background/modules/radios/device.rs",
    "content": "use seelen_core::system_state::{RadioDevice, RadioDeviceKind};\nuse windows::{\n    Devices::Radios::{Radio, RadioKind, RadioState},\n    Foundation::TypedEventHandler,\n};\n\nuse crate::{error::Result, modules::radios::manager::RadioManager, windows_api::DeviceEvent};\n\npub struct SluRadioDevice {\n    pub id: String,\n    pub raw: Radio,\n    pub cache: RadioDevice,\n    state_changed_token: i64,\n}\n\nimpl SluRadioDevice {\n    pub fn create(device_id: &str) -> Result<SluRadioDevice> {\n        let radio = Radio::FromIdAsync(&device_id.into())?.join()?;\n\n        let id = device_id.to_string();\n        let state_changed_token = radio.StateChanged(&TypedEventHandler::new(\n            move |sender: windows_core::Ref<Radio>,\n                  _args: windows_core::Ref<windows_core::IInspectable>| {\n                // Get the state OUTSIDE the lock to avoid deadlock\n                // The Windows API call (State()) can trigger re-entrant events\n                if let Some(sender) = sender.as_ref() {\n                    let is_enabled = sender.State().is_ok_and(|s| s == RadioState::On);\n                    // Now update the cache with the lock (fast operation)\n                    RadioManager::instance().radios.get(&id, |r| {\n                        r.cache.is_enabled = is_enabled;\n                    });\n                    RadioManager::send(DeviceEvent::Updated(id.clone()));\n                }\n                Ok(())\n            },\n        ))?;\n\n        Ok(SluRadioDevice {\n            id: device_id.to_string(),\n            cache: Self::to_serializable(device_id, &radio)?,\n            raw: radio,\n            state_changed_token,\n        })\n    }\n\n    pub fn to_serializable(id: &str, radio: &Radio) -> Result<RadioDevice> {\n        let kind = match radio.Kind()? {\n            RadioKind::WiFi => RadioDeviceKind::WiFi,\n            RadioKind::MobileBroadband => RadioDeviceKind::MobileBroadband,\n            RadioKind::Bluetooth => RadioDeviceKind::Bluetooth,\n            RadioKind::FM => RadioDeviceKind::FM,\n            _ => RadioDeviceKind::Other,\n        };\n\n        Ok(RadioDevice {\n            id: id.to_owned(),\n            name: radio.Name()?.to_string(),\n            kind,\n            is_enabled: radio.State().is_ok_and(|s| s == RadioState::On),\n        })\n    }\n}\n\nimpl Drop for SluRadioDevice {\n    fn drop(&mut self) {\n        let _ = self.raw.RemoveStateChanged(self.state_changed_token);\n    }\n}\n"
  },
  {
    "path": "src/background/modules/radios/handlers.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{RadioDevice, RadioDeviceKind},\n};\n\nuse crate::{app::emit_to_webviews, error::Result, modules::radios::manager::RadioManager};\n\n/// Wrapper for RadioManager that automatically registers Tauri events on first access\n/// This keeps Tauri logic separate from system logic while ensuring lazy initialization\nfn get_radio_manager() -> &'static RadioManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        RadioManager::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::RadiosChanged,\n                RadioManager::instance().get_radios(),\n            );\n        });\n    });\n\n    RadioManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_radios() -> Vec<RadioDevice> {\n    get_radio_manager().get_radios()\n}\n\n#[tauri::command(async)]\npub fn set_radios_state(kind: RadioDeviceKind, enabled: bool) -> Result<()> {\n    get_radio_manager().set_radios_state(kind, enabled)\n}\n"
  },
  {
    "path": "src/background/modules/radios/manager.rs",
    "content": "use std::sync::LazyLock;\n\nuse seelen_core::system_state::{RadioDevice, RadioDeviceKind};\nuse windows::Devices::Radios::{Radio, RadioAccessStatus, RadioState};\n\nuse crate::{\n    error::Result,\n    event_manager, log_error,\n    modules::radios::device::SluRadioDevice,\n    utils::lock_free::SyncHashMap,\n    windows_api::{DeviceEnumerator, DeviceEvent, DeviceId},\n};\n\npub struct RadioManager {\n    pub radios: SyncHashMap<DeviceId, SluRadioDevice>,\n    device_enumerator: Option<DeviceEnumerator>,\n}\n\nunsafe impl Send for RadioManager {}\nunsafe impl Sync for RadioManager {}\n\nevent_manager!(RadioManager, DeviceEvent);\n\n#[allow(dead_code)]\nimpl RadioManager {\n    fn create() -> Self {\n        Self {\n            radios: SyncHashMap::new(),\n            device_enumerator: None,\n        }\n    }\n\n    fn initialize(&mut self) -> Result<()> {\n        // Create device enumerator with callback\n        let enumerator = DeviceEnumerator::new(Radio::GetDeviceSelector()?.to_string(), |event| {\n            log_error!(RadioManager::instance().on_event(&event));\n            RadioManager::send(event);\n        })?;\n\n        // Start enumeration (blocks until initial enumeration completes)\n        let devices = enumerator.start_blocking()?;\n\n        // Map DeviceInformation to (Radio, i64) with state change handler\n        let radios: Result<Vec<SluRadioDevice>> = devices\n            .iter()\n            .map(|device| {\n                let id = device.Id()?.to_string();\n                SluRadioDevice::create(&id)\n            })\n            .collect();\n\n        let radios_map: std::collections::HashMap<DeviceId, SluRadioDevice> = radios?\n            .into_iter()\n            .map(|radio| (radio.id.clone(), radio))\n            .collect();\n        self.radios = SyncHashMap::from(radios_map);\n        self.device_enumerator = Some(enumerator);\n        Ok(())\n    }\n\n    pub fn instance() -> &'static Self {\n        static RADIO_MANAGER_INSTANCE: LazyLock<RadioManager> = LazyLock::new(|| {\n            let mut m = RadioManager::create();\n            log_error!(m.initialize());\n            m\n        });\n        &RADIO_MANAGER_INSTANCE\n    }\n\n    pub fn is_enabled(&self, kind: RadioDeviceKind) -> bool {\n        self.radios\n            .any(|(_id, radio)| radio.cache.kind == kind && radio.cache.is_enabled)\n    }\n\n    fn on_event(&self, event: &DeviceEvent) -> Result<()> {\n        match event {\n            DeviceEvent::Added(id) => {\n                let radio = SluRadioDevice::create(id)?;\n                self.radios.upsert(id.clone(), radio);\n            }\n            DeviceEvent::Updated(_id) => {}\n            DeviceEvent::Removed(id) => {\n                self.radios.remove(id);\n            }\n        }\n        Ok(())\n    }\n\n    pub fn get_radios(&self) -> Vec<RadioDevice> {\n        let mut radios = Vec::new();\n        self.radios\n            .for_each(|(_id, radio)| radios.push(radio.cache.clone()));\n        radios\n    }\n\n    pub fn set_radios_state(&self, kind: RadioDeviceKind, enabled: bool) -> Result<()> {\n        if Radio::RequestAccessAsync()?.join()? != RadioAccessStatus::Allowed {\n            // todo handle this via UI error.\n            return Ok(());\n        }\n\n        let mut to_update = Vec::new();\n        self.radios.for_each(|(_id, radio)| {\n            if radio.cache.kind == kind {\n                to_update.push(radio.raw.clone());\n            }\n        });\n\n        let state = if enabled {\n            RadioState::On\n        } else {\n            RadioState::Off\n        };\n        for radio in to_update {\n            radio.SetStateAsync(state)?.join()?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/radios/mod.rs",
    "content": "pub mod bluetooth;\npub mod device;\npub mod handlers;\npub mod manager;\n"
  },
  {
    "path": "src/background/modules/radios/wifi/mod.rs",
    "content": ""
  },
  {
    "path": "src/background/modules/start/application.rs",
    "content": "use std::path::{Path, PathBuf};\nuse std::sync::{Arc, LazyLock};\nuse std::time::Duration;\n\nuse notify_debouncer_full::{\n    new_debouncer,\n    notify::{ReadDirectoryChangesWatcher, RecursiveMode, Watcher},\n    DebounceEventResult, DebouncedEvent, Debouncer, FileIdMap,\n};\nuse seelen_core::system_state::StartMenuItem;\nuse windows::Win32::UI::Shell::{FOLDERID_CommonStartMenu, FOLDERID_StartMenu};\nuse windows::{\n    ApplicationModel::PackageCatalog, Foundation::TypedEventHandler,\n    Management::Deployment::PackageManager, UI::StartScreen::StartScreenManager,\n};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    utils::{constants::SEELEN_COMMON, lock_free::SyncVec},\n    windows_api::WindowsApi,\n};\n\npub struct StartMenuManager {\n    pub list: SyncVec<Arc<StartMenuItem>>,\n    cache_path: PathBuf,\n    _file_watcher: Option<Arc<Debouncer<ReadDirectoryChangesWatcher, FileIdMap>>>,\n    _package_catalog: Option<PackageCatalog>,\n}\n\n#[derive(Debug, Clone)]\n#[allow(dead_code)]\npub enum StartMenuEvent {\n    ItemsRefreshed,\n}\n\nevent_manager!(StartMenuManager, StartMenuEvent);\n\nunsafe impl Send for StartMenuManager {}\nunsafe impl Sync for StartMenuManager {}\n\nimpl StartMenuManager {\n    /// programs shared by all users\n    pub fn common_items_path() -> PathBuf {\n        WindowsApi::known_folder(FOLDERID_CommonStartMenu)\n            .expect(\"Failed to get FOLDERID_CommonStartMenu folder path\")\n    }\n\n    /// programs specific to the current user\n    pub fn user_items_path() -> PathBuf {\n        WindowsApi::known_folder(FOLDERID_StartMenu)\n            .expect(\"Failed to get FOLDERID_StartMenu folder path\")\n    }\n\n    fn new() -> StartMenuManager {\n        StartMenuManager {\n            list: SyncVec::new(),\n            cache_path: SEELEN_COMMON.app_cache_dir().join(\"start_menu_v2.json\"),\n            _file_watcher: None,\n            _package_catalog: None,\n        }\n    }\n\n    pub fn instance() -> &'static Self {\n        static START_MENU_MANAGER: LazyLock<StartMenuManager> = LazyLock::new(|| {\n            let mut manager = StartMenuManager::new();\n            manager.init().log_error();\n            manager\n        });\n        &START_MENU_MANAGER\n    }\n\n    fn init(&mut self) -> Result<()> {\n        if self.cache_path.exists() {\n            match self.load_cache() {\n                Ok(_) => {\n                    // refresh without blocking\n                    std::thread::spawn(|| {\n                        Self::reload().log_error();\n                    });\n                    // Setup listeners after loading cache\n                    self.setup_listeners().log_error();\n                    return Ok(());\n                }\n                Err(e) => {\n                    log::error!(\"Failed to load start menu cache: {e}\");\n                }\n            }\n        }\n\n        self.list.replace(Self::load_start_menu_items()?);\n        self.store_cache()?;\n        // Setup listeners after initial load\n        self.setup_listeners().log_error();\n        Ok(())\n    }\n\n    pub fn get_by_target(&self, target: &Path) -> Option<Arc<StartMenuItem>> {\n        self.list\n            .find(|item| item.target.as_ref().is_some_and(|t| t == target))\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchiconresource\n    pub fn get_by_file_umid(&self, umid: &str) -> Option<Arc<StartMenuItem>> {\n        self.list.find(|item| {\n            if let Some(item_umid) = &item.umid {\n                return item_umid == umid;\n            }\n            if let Some(target) = &item.target {\n                // some apps registered as media player as example use the process name as umid\n                return target.ends_with(umid);\n            }\n            false\n        })\n    }\n\n    pub fn store_cache(&self) -> Result<()> {\n        let file = std::fs::File::create(&self.cache_path)?;\n        let writer = std::io::BufWriter::new(file);\n        serde_json::to_writer_pretty(writer, &self.list.to_vec())?;\n        Ok(())\n    }\n\n    pub fn load_cache(&mut self) -> Result<()> {\n        let file = std::fs::File::open(&self.cache_path)?;\n        let reader = std::io::BufReader::new(file);\n        let items: Vec<StartMenuItem> = serde_json::from_reader(reader)?;\n        self.list.replace(items.into_iter().map(Arc::new).collect());\n        Ok(())\n    }\n\n    fn _get_items(dir: &Path) -> Result<Vec<Arc<StartMenuItem>>> {\n        let mut items = Vec::new();\n        for entry in std::fs::read_dir(dir)?.flatten() {\n            let path = entry.path();\n            let file_type = entry.file_type()?;\n\n            if file_type.is_dir() {\n                items.extend(Self::_get_items(&path)?);\n                continue;\n            }\n\n            if file_type.is_file() {\n                let target = WindowsApi::resolve_lnk_target(&path).ok().map(|(t, _)| t);\n                // Get display name from filename without extension\n                let display_name = path\n                    .file_stem()\n                    .and_then(|s| s.to_str())\n                    .unwrap_or(\"Unknown\")\n                    .to_string();\n\n                items.push(Arc::new(StartMenuItem {\n                    umid: WindowsApi::get_file_umid(&path).ok(),\n                    toast_activator: WindowsApi::get_file_toast_activator(&path).ok(),\n                    path,\n                    target,\n                    display_name,\n                }))\n            }\n        }\n        Ok(items)\n    }\n\n    pub fn load_start_menu_items() -> Result<Vec<Arc<StartMenuItem>>> {\n        log::trace!(\"Loading start menu items\");\n        let mut items = Vec::new();\n\n        // win32 unpackaged\n        items.extend(Self::_get_items(&Self::common_items_path())?);\n        items.extend(Self::_get_items(&Self::user_items_path())?);\n\n        // win32/uwp packaged\n        let pkg_manager = PackageManager::new()?;\n        let start_screen = StartScreenManager::GetDefault()?;\n\n        let packages = pkg_manager.FindPackagesByUserSecurityId(&\"\".into())?;\n        for package in packages {\n            let apps = package.GetAppListEntries()?;\n\n            for app in apps {\n                // https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-visualelements\n                let is_on_start_screen = start_screen.SupportsAppListEntry(&app)?;\n\n                if is_on_start_screen {\n                    let umid = app.AppUserModelId()?.to_string_lossy();\n\n                    // Get display name from DisplayInfo\n                    let display_name = app\n                        .DisplayInfo()?\n                        .DisplayName()?\n                        .to_string_lossy()\n                        .to_string();\n\n                    items.push(Arc::new(StartMenuItem {\n                        umid: Some(umid),\n                        toast_activator: None,\n                        path: PathBuf::new(),\n                        target: None,\n                        display_name,\n                    }))\n                }\n            }\n        }\n\n        log::trace!(\"Loaded {} start menu items\", items.len());\n        Ok(items)\n    }\n\n    fn create_file_watcher(\n        &self,\n    ) -> Result<Arc<Debouncer<ReadDirectoryChangesWatcher, FileIdMap>>> {\n        let mut debouncer = new_debouncer(\n            Duration::from_millis(500),\n            None,\n            |result: DebounceEventResult| match result {\n                Ok(events) => {\n                    log::debug!(\n                        \"Start menu file watcher detected changes: {} events\",\n                        events.len()\n                    );\n                    Self::on_files_changed(events).log_error();\n                }\n                Err(errors) => {\n                    log::error!(\"Start menu file watcher error: {errors:?}\");\n                }\n            },\n        )?;\n\n        let watcher = debouncer.watcher();\n        watcher.watch(&Self::common_items_path(), RecursiveMode::Recursive)?;\n        watcher.watch(&Self::user_items_path(), RecursiveMode::Recursive)?;\n\n        Ok(Arc::new(debouncer))\n    }\n\n    fn reload() -> Result<()> {\n        let manager = Self::instance();\n        let new_items = Self::load_start_menu_items()?;\n        manager.list.replace(new_items);\n        manager.store_cache().log_error();\n        Self::send(StartMenuEvent::ItemsRefreshed);\n        Ok(())\n    }\n\n    fn on_files_changed(_events: Vec<DebouncedEvent>) -> Result<()> {\n        Self::reload()\n    }\n\n    fn setup_package_catalog_listener(&mut self) -> Result<()> {\n        let catalog = PackageCatalog::OpenForCurrentUser()?;\n\n        let handler_installing = TypedEventHandler::new(|_catalog, _args| {\n            log::debug!(\"Package installing event detected\");\n            std::thread::spawn(|| {\n                std::thread::sleep(Duration::from_millis(1000));\n                Self::reload().log_error();\n            });\n            Ok(())\n        });\n\n        catalog.PackageInstalling(&handler_installing)?;\n\n        let handler_uninstalling = TypedEventHandler::new(|_catalog, _args| {\n            log::debug!(\"Package uninstalling event detected\");\n            std::thread::spawn(|| {\n                std::thread::sleep(Duration::from_millis(1000));\n                Self::reload().log_error();\n            });\n            Ok(())\n        });\n\n        catalog.PackageUninstalling(&handler_uninstalling)?;\n\n        self._package_catalog = Some(catalog);\n\n        Ok(())\n    }\n\n    pub fn setup_listeners(&mut self) -> Result<()> {\n        // Setup file system watcher\n        let watcher = self.create_file_watcher()?;\n        self._file_watcher = Some(watcher);\n\n        // Setup package catalog listener\n        self.setup_package_catalog_listener().log_error();\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/start/domain.rs",
    "content": "\n"
  },
  {
    "path": "src/background/modules/start/infrastructure.rs",
    "content": "use std::sync::{Arc, Once};\n\nuse seelen_core::{handlers::SeelenEvent, system_state::StartMenuItem};\n\nuse crate::app::emit_to_webviews;\n\nuse super::application::StartMenuManager;\n\nfn get_start_menu_manager() -> &'static StartMenuManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        StartMenuManager::subscribe(|_event| {\n            emit_to_webviews(SeelenEvent::StartMenuItemsChanged, get_start_menu_items());\n        });\n    });\n    StartMenuManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_start_menu_items() -> Vec<Arc<StartMenuItem>> {\n    let manager = get_start_menu_manager();\n    manager.list.to_vec()\n}\n"
  },
  {
    "path": "src/background/modules/start/mod.rs",
    "content": "pub mod application;\npub mod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/system/mod.rs",
    "content": "pub mod tauri;\n\nuse std::sync::LazyLock;\n\nuse seelen_core::system_state::{Core, Disk, Memory, NetworkStatistics};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    state::application::FULL_STATE,\n    utils::lock_free::TracedMutex,\n};\n\npub struct SystemInfo {\n    disks: TracedMutex<sysinfo::Disks>,\n    networks: TracedMutex<sysinfo::Networks>,\n    system: TracedMutex<sysinfo::System>,\n    // Cache for change detection\n    pub last_disks: TracedMutex<Vec<Disk>>,\n    pub last_networks: TracedMutex<Vec<NetworkStatistics>>,\n    pub last_memory: TracedMutex<Memory>,\n    pub last_cores: TracedMutex<Vec<Core>>,\n}\n\n#[derive(Debug, Clone)]\n#[allow(clippy::enum_variant_names)]\npub enum SystemInfoEvent {\n    DisksChanged,\n    NetworkChanged,\n    MemoryChanged,\n    CoresChanged,\n}\n\nevent_manager!(SystemInfo, SystemInfoEvent);\n\nimpl SystemInfo {\n    pub fn instance() -> &'static SystemInfo {\n        static INSTANCE: LazyLock<SystemInfo> = LazyLock::new(|| {\n            let mut instance = SystemInfo::new();\n            instance.init().log_error();\n            instance\n        });\n        &INSTANCE\n    }\n\n    fn new() -> Self {\n        Self {\n            disks: TracedMutex::new(sysinfo::Disks::new()),\n            networks: TracedMutex::new(sysinfo::Networks::new()),\n            system: TracedMutex::new(sysinfo::System::new()),\n            // Cache for change detection\n            last_disks: TracedMutex::new(Vec::new()),\n            last_networks: TracedMutex::new(Vec::new()),\n            last_memory: TracedMutex::new(Memory::default()),\n            last_cores: TracedMutex::new(Vec::new()),\n        }\n    }\n\n    fn init(&mut self) -> Result<()> {\n        // Initialize first data\n        *self.last_disks.lock() = self.disks();\n        *self.last_networks.lock() = self.network();\n        *self.last_memory.lock() = self.memory();\n        *self.last_cores.lock() = self.cores();\n\n        // Spawn monitoring thread\n        std::thread::spawn(|| loop {\n            let interval = FULL_STATE.load().settings.polling_interval;\n            std::thread::sleep(std::time::Duration::from_secs(interval));\n            SystemInfo::instance().check_and_emit_changes();\n        });\n\n        Ok(())\n    }\n\n    fn check_and_emit_changes(&self) {\n        // Check disks (compare available_space)\n        {\n            let current_disks = self.disks();\n            let mut last_disks = self.last_disks.lock();\n            if Self::disks_changed(&last_disks, &current_disks) {\n                *last_disks = current_disks;\n                Self::send(SystemInfoEvent::DisksChanged);\n            }\n        }\n\n        // Check network (compare received and transmitted)\n        {\n            let current_network = self.network();\n            let mut last_network = self.last_networks.lock();\n            if Self::network_changed(&last_network, &current_network) {\n                *last_network = current_network;\n                Self::send(SystemInfoEvent::NetworkChanged);\n            }\n        }\n\n        {\n            let current_memory = self.memory();\n            let mut last_memory = self.last_memory.lock();\n            if Self::memory_changed(&last_memory, &current_memory) {\n                *last_memory = current_memory;\n                Self::send(SystemInfoEvent::MemoryChanged);\n            }\n        }\n\n        {\n            let current_cores = self.cores();\n            let mut last_cores = self.last_cores.lock();\n            if Self::cores_changed(&last_cores, &current_cores) {\n                *last_cores = current_cores;\n                Self::send(SystemInfoEvent::CoresChanged);\n            }\n        }\n    }\n\n    fn disks_changed(old: &[Disk], new: &[Disk]) -> bool {\n        if old.len() != new.len() {\n            return true;\n        }\n        for (old_disk, new_disk) in old.iter().zip(new.iter()) {\n            if old_disk.available_space != new_disk.available_space\n                || old_disk.read_bytes != new_disk.read_bytes\n                || old_disk.written_bytes != new_disk.written_bytes\n            {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn network_changed(old: &[NetworkStatistics], new: &[NetworkStatistics]) -> bool {\n        if old.len() != new.len() {\n            return true;\n        }\n        for (old_net, new_net) in old.iter().zip(new.iter()) {\n            if old_net.received != new_net.received || old_net.transmitted != new_net.transmitted {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn memory_changed(old: &Memory, new: &Memory) -> bool {\n        old.free != new.free || old.swap_free != new.swap_free\n    }\n\n    fn cores_changed(old: &[Core], new: &[Core]) -> bool {\n        if old.len() != new.len() {\n            return true;\n        }\n        for (old_core, new_core) in old.iter().zip(new.iter()) {\n            if old_core.usage != new_core.usage {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn disks(&self) -> Vec<Disk> {\n        let interval = FULL_STATE.load().settings.polling_interval;\n        let mut guard = self.disks.lock();\n        guard.refresh(true);\n        guard\n            .iter()\n            .map(|disk| {\n                let usage = disk.usage();\n                Disk {\n                    name: disk.name().to_string_lossy().to_string(),\n                    file_system: disk.file_system().to_string_lossy().to_string(),\n                    mount_point: disk.mount_point().to_path_buf(),\n                    total_space: disk.total_space(),\n                    available_space: disk.available_space(),\n                    is_removable: disk.is_removable(),\n                    read_bytes: usage.read_bytes / interval,\n                    written_bytes: usage.written_bytes / interval,\n                }\n            })\n            .collect()\n    }\n\n    fn network(&self) -> Vec<NetworkStatistics> {\n        let interval = FULL_STATE.load().settings.polling_interval;\n        let mut guard = self.networks.lock();\n        guard.refresh(true);\n        guard\n            .iter()\n            .map(|(name, network)| NetworkStatistics {\n                name: name.clone(),\n                received: network.received() / interval,\n                transmitted: network.transmitted() / interval,\n                packets_received: network.packets_received() / interval,\n                packets_transmitted: network.packets_transmitted() / interval,\n            })\n            .collect()\n    }\n\n    fn memory(&self) -> Memory {\n        let mut system = self.system.lock();\n        system.refresh_memory();\n\n        Memory {\n            total: system.total_memory(),\n            free: system.free_memory(),\n            swap_total: system.total_swap(),\n            swap_free: system.free_swap(),\n        }\n    }\n\n    fn cores(&self) -> Vec<Core> {\n        let mut system = self.system.lock();\n        system.refresh_cpu_all();\n        system\n            .cpus()\n            .iter()\n            .map(|cpu| Core {\n                name: cpu.name().to_owned(),\n                brand: cpu.brand().to_owned(),\n                usage: cpu.cpu_usage(),\n                frequency: cpu.frequency(),\n            })\n            .collect()\n    }\n}\n"
  },
  {
    "path": "src/background/modules/system/tauri.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{Core, Disk, Memory, NetworkStatistics},\n};\n\nuse crate::{\n    app::emit_to_webviews,\n    modules::system::{SystemInfo, SystemInfoEvent},\n};\n\n/// Wrapper for SystemInfo that automatically registers Tauri events on first access\n/// This keeps Tauri logic separate from system logic while ensuring lazy initialization\nfn get_system_info() -> &'static SystemInfo {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        SystemInfo::subscribe(|event| match event {\n            SystemInfoEvent::DisksChanged => {\n                emit_to_webviews(\n                    SeelenEvent::SystemDisksChanged,\n                    &*SystemInfo::instance().last_disks.lock(),\n                );\n            }\n            SystemInfoEvent::NetworkChanged => {\n                emit_to_webviews(\n                    SeelenEvent::SystemNetworkChanged,\n                    &*SystemInfo::instance().last_networks.lock(),\n                );\n            }\n            SystemInfoEvent::MemoryChanged => {\n                emit_to_webviews(\n                    SeelenEvent::SystemMemoryChanged,\n                    &*SystemInfo::instance().last_memory.lock(),\n                );\n            }\n            SystemInfoEvent::CoresChanged => {\n                emit_to_webviews(\n                    SeelenEvent::SystemCoresChanged,\n                    &*SystemInfo::instance().last_cores.lock(),\n                );\n            }\n        });\n    });\n\n    SystemInfo::instance()\n}\n\n#[tauri::command(async)]\npub fn get_system_disks() -> Vec<Disk> {\n    get_system_info().last_disks.lock().clone()\n}\n\n#[tauri::command(async)]\npub fn get_system_network() -> Vec<NetworkStatistics> {\n    get_system_info().last_networks.lock().clone()\n}\n\n#[tauri::command(async)]\npub fn get_system_memory() -> Memory {\n    get_system_info().last_memory.lock().clone()\n}\n\n#[tauri::command(async)]\npub fn get_system_cores() -> Vec<Core> {\n    get_system_info().last_cores.lock().clone()\n}\n"
  },
  {
    "path": "src/background/modules/system_settings/application.rs",
    "content": "use std::sync::LazyLock;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n};\nuse seelen_core::system_state::UIColors;\nuse windows::{\n    Foundation::TypedEventHandler,\n    UI::ViewManagement::{UIColorType, UISettings},\n};\nuse windows_core::IInspectable;\n\nfn color_to_string(color: windows::UI::Color) -> String {\n    format!(\n        \"#{:02X}{:02X}{:02X}{:02X}\",\n        color.R, color.G, color.B, color.A\n    )\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum SystemSettingsEvent {\n    ColorChanged,\n    TextScaleChanged,\n}\n\npub struct SystemSettings {\n    settings: UISettings,\n    color_event_token: Option<i64>,\n    text_scale_event_token: Option<i64>,\n}\n\nunsafe impl Send for SystemSettings {}\n\nevent_manager!(SystemSettings, SystemSettingsEvent);\n\nimpl SystemSettings {\n    pub fn instance() -> &'static Self {\n        static SYSTEM_SETTINGS: LazyLock<SystemSettings> = LazyLock::new(|| {\n            let mut settings = SystemSettings::new();\n            settings.init().log_error();\n            settings\n        });\n        &SYSTEM_SETTINGS\n    }\n\n    fn new() -> Self {\n        Self {\n            settings: UISettings::new().expect(\"Failed to create UISettings\"),\n            color_event_token: None,\n            text_scale_event_token: None,\n        }\n    }\n\n    fn init(&mut self) -> Result<()> {\n        // Register color change event\n        // Windows-rs clones the handler internally, so we don't need to store it\n        let color_token = self\n            .settings\n            .ColorValuesChanged(&TypedEventHandler::new(Self::internal_on_colors_change))?;\n        self.color_event_token = Some(color_token);\n\n        // Register text scale change event\n        let text_scale_token = self\n            .settings\n            .TextScaleFactorChanged(&TypedEventHandler::new(Self::internal_on_text_scale_change))?;\n        self.text_scale_event_token = Some(text_scale_token);\n\n        Ok(())\n    }\n\n    fn internal_on_colors_change(\n        _listener: windows_core::Ref<UISettings>,\n        _args: windows_core::Ref<IInspectable>,\n    ) -> windows_core::Result<()> {\n        let _ = Self::event_tx().send(SystemSettingsEvent::ColorChanged);\n        Ok(())\n    }\n\n    fn internal_on_text_scale_change(\n        _listener: windows_core::Ref<UISettings>,\n        _args: windows_core::Ref<IInspectable>,\n    ) -> windows_core::Result<()> {\n        let _ = Self::event_tx().send(SystemSettingsEvent::TextScaleChanged);\n        Ok(())\n    }\n\n    pub fn get_colors(&self) -> Result<UIColors> {\n        let settings = &self.settings;\n        Ok(UIColors {\n            background: color_to_string(settings.GetColorValue(UIColorType::Background)?),\n            foreground: color_to_string(settings.GetColorValue(UIColorType::Foreground)?),\n            accent_darkest: color_to_string(settings.GetColorValue(UIColorType::AccentDark3)?),\n            accent_darker: color_to_string(settings.GetColorValue(UIColorType::AccentDark2)?),\n            accent_dark: color_to_string(settings.GetColorValue(UIColorType::AccentDark1)?),\n            accent: color_to_string(settings.GetColorValue(UIColorType::Accent)?),\n            accent_light: color_to_string(settings.GetColorValue(UIColorType::AccentLight1)?),\n            accent_lighter: color_to_string(settings.GetColorValue(UIColorType::AccentLight2)?),\n            accent_lightest: color_to_string(settings.GetColorValue(UIColorType::AccentLight3)?),\n            // https://learn.microsoft.com/is-is/uwp/api/windows.ui.viewmanagement.uisettings.getcolorvalue?view=winrt-19041#remarks\n            complement: None,\n        })\n    }\n}\n\nimpl Drop for SystemSettings {\n    fn drop(&mut self) {\n        if let Some(token) = self.color_event_token.take() {\n            self.settings.RemoveColorValuesChanged(token).log_error();\n        }\n\n        if let Some(deferral) = self.text_scale_event_token.take() {\n            self.settings\n                .RemoveTextScaleFactorChanged(deferral)\n                .log_error();\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/modules/system_settings/domain.rs",
    "content": "\n"
  },
  {
    "path": "src/background/modules/system_settings/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{handlers::SeelenEvent, system_state::UIColors};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::Result,\n    modules::system_settings::application::{SystemSettings, SystemSettingsEvent},\n};\n\n/// Lazy initialization wrapper that registers Tauri events on first access\n/// This keeps Tauri logic separate from system logic while ensuring lazy initialization\nfn get_system_settings() -> &'static SystemSettings {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        SystemSettings::subscribe(|event| {\n            if event == SystemSettingsEvent::ColorChanged {\n                if let Ok(colors) = SystemSettings::instance().get_colors() {\n                    emit_to_webviews(SeelenEvent::ColorsChanged, &colors);\n                }\n            }\n        });\n    });\n    SystemSettings::instance()\n}\n\n#[tauri::command(async)]\npub fn get_system_colors() -> Result<UIColors> {\n    get_system_settings().get_colors()\n}\n"
  },
  {
    "path": "src/background/modules/system_settings/language/application.rs",
    "content": "use std::{\n    collections::{hash_map::Entry, HashMap},\n    sync::{atomic::Ordering, LazyLock},\n};\n\nuse itertools::Itertools;\nuse seelen_core::system_state::{ImeStatus, KeyboardLayout, SystemLanguage};\nuse windows::Win32::{\n    Globalization::{\n        GetLocaleInfoEx, LCIDToLocaleName, LOCALE_SLOCALIZEDDISPLAYNAME, LOCALE_SNATIVELANGUAGENAME,\n    },\n    UI::{\n        Input::{\n            Ime::{\n                ImmGetContext, ImmGetConversionStatus, ImmGetDefaultIMEWnd, ImmGetDescriptionW,\n                IME_CONVERSION_MODE, IME_SENTENCE_MODE,\n            },\n            KeyboardAndMouse::{\n                ActivateKeyboardLayout, GetKeyboardLayout, GetKeyboardLayoutList,\n                LoadKeyboardLayoutW, HKL, KLF_SETFORPROCESS,\n            },\n        },\n        WindowsAndMessaging::WM_INPUTLANGCHANGEREQUEST,\n    },\n};\nuse winreg::{enums::HKEY_LOCAL_MACHINE, RegKey};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    utils::{lock_free::SyncVec, spawn_named_thread},\n    windows_api::{event_window::IS_INTERACTIVE_SESSION, string_utils::WindowsString, WindowsApi},\n};\n\nstatic LANGUAGE_MANAGER: LazyLock<LanguageManager> = LazyLock::new(|| {\n    let mut manager = LanguageManager::new();\n    manager.init().log_error();\n    manager\n});\n\nevent_manager!(LanguageManager, LanguageEvent);\n\n#[derive(Debug, Clone)]\n#[allow(dead_code)]\npub enum LanguageEvent {\n    KeyboardLayoutChanged(u32),\n    IMEStatusChanged(ImeStatus),\n}\n\n#[derive(Debug)]\npub struct LanguageManager {\n    languages: SyncVec<SystemLanguage>,\n}\n\nimpl LanguageManager {\n    pub fn instance() -> &'static Self {\n        &LANGUAGE_MANAGER\n    }\n\n    fn new() -> Self {\n        Self {\n            languages: SyncVec::new(),\n        }\n    }\n\n    fn init(&mut self) -> Result<()> {\n        self.languages.replace(Self::enum_langs()?);\n\n        let eid = Self::subscribe(|event| {\n            match event {\n                LanguageEvent::IMEStatusChanged(status) => {\n                    log::info!(\"IME status changed: {status:?}\");\n                }\n                LanguageEvent::KeyboardLayoutChanged(hkl) => {\n                    log::info!(\"Keyboard layout changed: {hkl:08X?}\");\n                }\n            }\n\n            if let Ok(langs) = Self::enum_langs() {\n                Self::instance().languages.replace(langs);\n            }\n        });\n        Self::set_event_handler_priority(&eid, 1);\n\n        spawn_named_thread(\"Keyboard Layout Monitor\", || {\n            let mut hkl = Self::get_active_hkl();\n            // let mut ime = Self::get_active_ime();\n\n            loop {\n                std::thread::sleep(std::time::Duration::from_secs(1));\n\n                // Pause when session is not interactive to reduce CPU usage\n                if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                    continue;\n                }\n\n                let current = Self::get_active_hkl();\n                if hkl != current {\n                    hkl = current;\n                    Self::send(LanguageEvent::KeyboardLayoutChanged(current.0 as _));\n                }\n\n                /* let current = Self::get_active_ime();\n                if ime != current {\n                    ime = current;\n                    Self::send(LanguageEvent::IMEStatusChanged(current));\n                } */\n            }\n        });\n        Ok(())\n    }\n\n    fn get_input_locale_list() -> Vec<HKL> {\n        unsafe {\n            let len = GetKeyboardLayoutList(None) as usize;\n            let mut list = vec![HKL::default(); len];\n            GetKeyboardLayoutList(Some(&mut list));\n            list\n        }\n    }\n\n    // active keyboard layout is set per thread, so we show the active layout for the foregrounded one.\n    fn get_active_hkl() -> HKL {\n        let (_, focused_thread) =\n            WindowsApi::window_thread_process_id(WindowsApi::get_foreground_window());\n        unsafe { GetKeyboardLayout(focused_thread) }\n    }\n\n    // same as keyboard layout, IME status is set per process.\n    #[allow(dead_code)]\n    fn get_active_ime() -> ImeStatus {\n        unsafe {\n            let hime = ImmGetDefaultIMEWnd(WindowsApi::get_foreground_window());\n            let himc = ImmGetContext(hime);\n\n            let mut conversion_mode = IME_CONVERSION_MODE::default();\n            let mut sentence_mode = IME_SENTENCE_MODE::default();\n            let _ = ImmGetConversionStatus(\n                himc,\n                Some(&mut conversion_mode as _),\n                Some(&mut sentence_mode as _),\n            );\n\n            ImeStatus {\n                conversion_mode: conversion_mode.0,\n                sentence_mode: sentence_mode.0,\n            }\n        }\n    }\n\n    fn enum_langs() -> Result<Vec<SystemLanguage>> {\n        let mut languages: HashMap<u32, SystemLanguage> = HashMap::new();\n        let active_hkl = Self::get_active_hkl();\n\n        for hkl in Self::get_input_locale_list() {\n            let lang_id = hkl.0 as u32 & 0xFFFF; // low word\n\n            let lang = match languages.entry(lang_id) {\n                Entry::Occupied(entry) => entry.into_mut(),\n                Entry::Vacant(entry) => {\n                    let lang = Self::get_language(lang_id)?;\n                    entry.insert(lang)\n                }\n            };\n\n            let layout: KeyboardLayout = Self::get_keyboard_layout(hkl, hkl == active_hkl)?;\n            lang.keyboard_layouts.push(layout);\n        }\n\n        Ok(languages.into_values().collect())\n    }\n\n    fn get_language(lang_id: u32) -> Result<SystemLanguage> {\n        let mut lang_code = WindowsString::new_to_fill(256);\n        let mut display_name = WindowsString::new_to_fill(256);\n        let mut native_name = WindowsString::new_to_fill(256);\n\n        unsafe {\n            LCIDToLocaleName(lang_id, Some(lang_code.as_mut_slice()), 0);\n            GetLocaleInfoEx(\n                lang_code.as_pcwstr(),\n                LOCALE_SLOCALIZEDDISPLAYNAME,\n                Some(display_name.as_mut_slice()),\n            );\n            GetLocaleInfoEx(\n                lang_code.as_pcwstr(),\n                LOCALE_SNATIVELANGUAGENAME,\n                Some(native_name.as_mut_slice()),\n            );\n        };\n\n        Ok(SystemLanguage {\n            id: format!(\"{lang_id:04X}\"),\n            code: lang_code.to_string(),\n            name: display_name.to_string(),\n            native_name: native_name.to_string(),\n            keyboard_layouts: Vec::new(),\n        })\n    }\n\n    fn get_keyboard_layout(hkl: HKL, active: bool) -> Result<KeyboardLayout> {\n        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);\n        let reg_layouts = hklm.open_subkey(r\"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\")?;\n\n        let layout_id = Self::get_reg_layout_id(hkl.0 as _)?;\n\n        let reg_layout = reg_layouts.open_subkey(&layout_id)?;\n        let display_name =\n            if let Ok(path) = reg_layout.get_value::<String, _>(\"Layout Display Name\") {\n                WindowsApi::resolve_indirect_string(&path)?\n            } else {\n                reg_layout\n                    .get_value(\"Layout Text\")\n                    .unwrap_or_else(|_| String::from(\"Unknown\"))\n            };\n\n        Self::get_ime_family(hkl).log_error();\n\n        Ok(KeyboardLayout {\n            id: layout_id,\n            handle: format!(\"{:08X}\", hkl.0 as usize),\n            display_name,\n            active,\n        })\n    }\n\n    fn get_ime_family(hkl: HKL) -> Result<()> {\n        unsafe {\n            let len = ImmGetDescriptionW(hkl, None);\n            let mut description = WindowsString::new_to_fill((len + 1) as usize);\n            ImmGetDescriptionW(hkl, Some(description.as_mut_slice()));\n\n            // log::debug!(\"({:?}) IME description: {}\", hkl, description.to_string());\n        }\n        Ok(())\n    }\n\n    /// this function is used to get the real layout id.\n    /// Examples:\n    /// - 0409080A = spanish lang english keyboard => layout id is 0409\n    /// - F0020409 = english lang dvorak keyboard => layout id is 0002 but mapped to 00010409\n    fn get_reg_layout_id(hkl: u32) -> Result<String> {\n        // https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values\n        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);\n        let reg_layouts = hklm.open_subkey(r\"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\")?;\n        let available_klids = reg_layouts.enum_keys().flatten().collect_vec();\n\n        // https://learn.microsoft.com/en-us/windows/win32/intl/locale-identifiers\n        // https://learn.microsoft.com/en-us/windows/win32/intl/language-identifiers\n        let language_id = hkl & 0xFFFF; // low word\n        let mut device_id = hkl >> 16; // high word\n\n        // `Device Handle` contains `Layout ID`\n        if device_id & 0xF000 == 0xF000 {\n            let layout_id_to_search = format!(\"{:04X}\", device_id & 0x0FFF);\n\n            for current_klid in &available_klids {\n                let reg_layout = reg_layouts.open_subkey(current_klid)?;\n                if let Ok(layout_id) = reg_layout.get_value::<String, _>(\"Layout Id\") {\n                    // Layout Id stored using case insensitive\n                    if layout_id_to_search == layout_id.to_uppercase() {\n                        return Ok(current_klid.clone());\n                    }\n                }\n            }\n        } else {\n            // Use language id only if keyboard layout language is not available. This\n            // is crucial in cases when keyboard is installed more than once or under\n            // different languages. For example when French keyboard is installed under US\n            // input language we need to return French keyboard identifier.\n            if device_id == 0 {\n                device_id = language_id;\n            }\n            return Ok(format!(\"{device_id:08X}\"));\n        }\n\n        Err(format!(\"klid not found for {hkl:?}\").into())\n    }\n\n    pub fn set_keyboard_layout(klid: &str, hkl: &str) -> Result<()> {\n        let klid = WindowsString::from_str(klid);\n        let hkl = usize::from_str_radix(hkl, 16)?;\n\n        unsafe { LoadKeyboardLayoutW(klid.as_pcwstr(), Default::default())? };\n\n        let foreground = WindowsApi::get_foreground_window();\n        WindowsApi::post_message(foreground, WM_INPUTLANGCHANGEREQUEST, 0, hkl as _)?;\n\n        unsafe { ActivateKeyboardLayout(HKL(hkl as _), KLF_SETFORPROCESS)? };\n\n        Self::send(LanguageEvent::KeyboardLayoutChanged(hkl as u32));\n        Ok(())\n    }\n\n    pub fn get_languages(&self) -> Vec<SystemLanguage> {\n        self.languages.to_vec()\n    }\n}\n"
  },
  {
    "path": "src/background/modules/system_settings/language/domain.rs",
    "content": "\n"
  },
  {
    "path": "src/background/modules/system_settings/language/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{handlers::SeelenEvent, system_state::SystemLanguage};\n\nuse crate::{app::emit_to_webviews, error::Result};\n\nuse super::application::LanguageManager;\n\n/// Lazy initialization wrapper that registers Tauri events on first access\n/// This keeps Tauri logic separate from system logic while ensuring lazy initialization\nfn get_language_manager() -> &'static LanguageManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        LanguageManager::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::SystemLanguagesChanged,\n                &LanguageManager::instance().get_languages(),\n            );\n        });\n    });\n    LanguageManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_system_languages() -> Vec<SystemLanguage> {\n    get_language_manager().get_languages()\n}\n\n#[tauri::command(async)]\npub fn set_system_keyboard_layout(id: String, handle: String) -> Result<()> {\n    get_language_manager();\n    LanguageManager::set_keyboard_layout(&id, &handle)?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/modules/system_settings/language/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/system_settings/mod.rs",
    "content": "pub mod application;\nmod domain;\npub mod infrastructure;\npub mod language;\n"
  },
  {
    "path": "src/background/modules/system_tray/application/mod.rs",
    "content": "pub mod tray_hook_loader;\npub mod tray_icon;\nmod util;\n\nuse std::sync::LazyLock;\n\nuse seelen_core::system_state::{SysTrayIcon, SysTrayIconId};\nuse slu_ipc::messages::Win32TrayEvent;\n\nuse crate::{\n    event_manager, modules::system_tray::application::tray_hook_loader::TrayHookLoader,\n    utils::lock_free::SyncHashMap,\n};\n\npub struct SystemTrayManager {\n    icons: SyncHashMap<SysTrayIconId, SysTrayIcon>,\n    _loader: Option<TrayHookLoader>,\n}\n\n#[derive(Debug, Clone)]\npub enum SystemTrayEvent {\n    Changed,\n}\n\nevent_manager!(SystemTrayManager, SystemTrayEvent);\n\nimpl SystemTrayManager {\n    fn create() -> Self {\n        log::trace!(\"Creating system tray manager\");\n\n        let loader = match TrayHookLoader::new() {\n            Ok(loader) => Some(loader),\n            Err(err) => {\n                log::error!(\"Failed to create tray hook loader: {:?}\", err);\n                None\n            }\n        };\n\n        Self {\n            icons: SyncHashMap::new(),\n            _loader: loader,\n        }\n    }\n\n    pub fn instance() -> &'static Self {\n        static SYSTEM_TRAY_MANAGER: LazyLock<SystemTrayManager> =\n            LazyLock::new(SystemTrayManager::create);\n        &SYSTEM_TRAY_MANAGER\n    }\n\n    /// Handles a tray event received via IPC\n    /// This method should be called from the AppIpc handler\n    pub fn handle_tray_event(event: Win32TrayEvent) {\n        if let Some(_event) = Self::instance().process_event(event) {\n            Self::send(SystemTrayEvent::Changed);\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/modules/system_tray/application/tray_hook_loader.rs",
    "content": "use std::path::{Path, PathBuf};\n\nuse windows::{\n    core::PCWSTR,\n    Win32::{\n        Foundation::{HMODULE, LPARAM, LRESULT, WPARAM},\n        System::LibraryLoader::{GetProcAddress, LoadLibraryW},\n        UI::WindowsAndMessaging::{SetWindowsHookExW, HHOOK, WH_CALLWNDPROC},\n    },\n};\nuse windows_core::Owned;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    modules::system_tray::application::util::Util,\n    utils::constants::SEELEN_COMMON,\n    windows_api::WindowsApi,\n};\n\n// ============================================================================\n// DLL Function Types\n// ============================================================================\n\n/// Type for the GetMsgProc hook procedure exported by the DLL\ntype GetMsgProcFn = unsafe extern \"system\" fn(i32, WPARAM, LPARAM) -> LRESULT;\n\n// ============================================================================\n// Hook Loader Implementation\n// ============================================================================\n\n// SAFETY: HHOOK is a Windows handle that is safe to share between threads\n// even though it contains a raw pointer internally\nunsafe impl Send for TrayHookLoader {}\nunsafe impl Sync for TrayHookLoader {}\n\npub struct TrayHookLoader {\n    // Drop order matches declaration order: hook is uninstalled before DLL is freed\n    _hook_handle: Option<Owned<HHOOK>>,\n    _dll_handle: Option<Owned<HMODULE>>,\n}\n\nimpl TrayHookLoader {\n    /// Creates a new loader, loads the DLL and installs the hook\n    /// Events will be sent automatically through AppIpc\n    pub fn new() -> Result<Self> {\n        let dll_path = Self::get_dll_path()?;\n        let dll_handle = Self::load_dll(&dll_path)?;\n        let call_msg_proc = Self::get_proc_address::<GetMsgProcFn>(*dll_handle, \"CallWndProc\")?;\n\n        let shell_tray = WindowsApi::find_window(None, None, None, Some(\"Shell_TrayWnd\"))?;\n        let (_pid, thread_id) = WindowsApi::window_thread_process_id(shell_tray);\n\n        let hook_handle = unsafe {\n            SetWindowsHookExW(\n                WH_CALLWNDPROC,\n                Some(call_msg_proc),\n                Some((*dll_handle).into()),\n                thread_id, // 0 = global hook for all threads\n            )\n            .map_err(|e| format!(\"Failed to install hook: {:?}\", e))?\n        };\n\n        log::info!(\"Tray hook DLL loaded and installed successfully\");\n\n        Util::refresh_icons().log_error();\n\n        Ok(Self {\n            _hook_handle: Some(unsafe { Owned::new(hook_handle) }),\n            _dll_handle: Some(dll_handle),\n        })\n    }\n\n    /// Gets the DLL path, copying it to SEELEN_COMMON temp directory to avoid\n    /// permission issues with MSIX installations (WindowsApps folder is restricted)\n    fn get_dll_path() -> Result<PathBuf> {\n        let exe_path = std::env::current_exe()?;\n        let exe_dir = exe_path\n            .parent()\n            .ok_or(\"Failed to get executable directory\")?;\n\n        // The DLL should be in the same directory as the executable\n        let source_dll_path = exe_dir.join(\"sluhk.dll\");\n        if !source_dll_path.exists() {\n            return Err(format!(\"DLL not found at: {}\", source_dll_path.display()).into());\n        }\n\n        // Copy to temp directory to ensure accessibility for hooks\n        // (MSIX installations in WindowsApps have restricted permissions)\n        let temp_dir = SEELEN_COMMON.app_temp_dir();\n        std::fs::create_dir_all(temp_dir)?;\n        let target_dll_path = temp_dir.join(\"sluhk.dll\");\n\n        // Use read & write instead of copy to avoid encryption issues on MSIX/APPX\n        // when copying across different volumes (e.g., S: to C:)\n        let content = std::fs::read(&source_dll_path)?;\n        std::fs::write(&target_dll_path, content)?;\n        log::info!(\n            \"Tray hook DLL copied from {} to {}\",\n            source_dll_path.display(),\n            target_dll_path.display()\n        );\n\n        Ok(target_dll_path)\n    }\n\n    /// Loads the DLL into memory\n    fn load_dll(path: &Path) -> Result<Owned<HMODULE>> {\n        let path_wide: Vec<u16> = path\n            .to_string_lossy()\n            .encode_utf16()\n            .chain(std::iter::once(0))\n            .collect();\n\n        unsafe {\n            let handle = LoadLibraryW(PCWSTR(path_wide.as_ptr()))?;\n            Ok(Owned::new(handle))\n        }\n    }\n\n    /// Gets the address of an exported function\n    fn get_proc_address<F>(dll_handle: HMODULE, name: &str) -> Result<F> {\n        let name_cstr = std::ffi::CString::new(name)\n            .map_err(|e| format!(\"Invalid function name '{}': {}\", name, e))?;\n\n        unsafe {\n            let proc_addr =\n                GetProcAddress(dll_handle, windows::core::PCSTR(name_cstr.as_ptr() as _));\n\n            match proc_addr {\n                Some(addr) => Ok(std::mem::transmute_copy(&addr)),\n                None => Err(format!(\"Failed to get proc address for: {}\", name).into()),\n            }\n        }\n    }\n}\n\nimpl Drop for TrayHookLoader {\n    fn drop(&mut self) {\n        // Owned<HHOOK> calls UnhookWindowsHookEx, Owned<HMODULE> calls FreeLibrary automatically.\n        // Fields drop in declaration order: hook_handle first, then _dll_handle.\n        log::info!(\"Tray hook unloaded\");\n    }\n}\n"
  },
  {
    "path": "src/background/modules/system_tray/application/tray_icon.rs",
    "content": "use std::hash::{DefaultHasher, Hash, Hasher};\n\nuse seelen_core::system_state::{SysTrayIcon, SysTrayIconId, SystrayIconAction};\nuse windows::Win32::{\n    Foundation::{HWND, LPARAM, WPARAM},\n    UI::{\n        Controls::{WM_MOUSEHOVER, WM_MOUSELEAVE},\n        Shell::{NIN_POPUPCLOSE, NIN_POPUPOPEN, NIN_SELECT},\n        WindowsAndMessaging::{\n            AllowSetForegroundWindow, GetWindowThreadProcessId, SendNotifyMessageW, HICON,\n            WM_CONTEXTMENU, WM_LBUTTONDBLCLK, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,\n            WM_MBUTTONUP, WM_MOUSEMOVE, WM_RBUTTONDOWN, WM_RBUTTONUP,\n        },\n    },\n};\n\nuse crate::{\n    modules::system_tray::application::{util::Util, SystemTrayManager},\n    utils::{constants::SEELEN_COMMON, icon_extractor::convert_hicon_to_rgba_image},\n    windows_api::{window::Window, WindowsApi},\n};\nuse slu_ipc::messages::{IconEventData, Win32TrayEvent};\n\n/// Events that can be emitted by `Systray`.\n#[derive(Clone, Debug, Eq, PartialEq)]\n#[allow(clippy::enum_variant_names)]\npub enum SystrayEvent {\n    IconAdd(SysTrayIcon),\n    IconUpdate(SysTrayIcon),\n    IconRemove(SysTrayIconId),\n}\n\nimpl SystemTrayManager {\n    /// Returns all icons managed by the `Systray`.\n    pub fn icons(&self) -> Vec<SysTrayIcon> {\n        self.icons.values()\n    }\n\n    /// Returns the icon with the given handle and uid.\n    pub fn icon_by_handle(&self, handle: isize, uid: u32) -> Option<SysTrayIcon> {\n        self.icons\n            .get(&SysTrayIconId::HandleUid(handle, uid), |v| v.clone())\n    }\n\n    /// Returns the icon with the given guid.\n    pub fn icon_by_guid(&self, guid: uuid::Uuid) -> Option<SysTrayIcon> {\n        self.icons.get(&SysTrayIconId::Guid(guid), |v| v.clone())\n    }\n\n    fn find_icon(&self, icon_data: &IconEventData) -> Option<SysTrayIcon> {\n        icon_data\n            .guid\n            .and_then(|guid| self.icon_by_guid(guid))\n            .or_else(|| match (icon_data.window_handle, icon_data.uid) {\n                (Some(handle), Some(uid)) => self.icon_by_handle(handle, uid),\n                _ => None,\n            })\n    }\n\n    /// Handles an event from the `Systray`.\n    ///\n    /// Returns `None` if the event should be ignored (e.g. if an icon that\n    /// doesn't exist was removed).\n    pub(super) fn process_event(&self, mut event: Win32TrayEvent) -> Option<SystrayEvent> {\n        // set application name if not tooltip is set\n        match &mut event {\n            Win32TrayEvent::IconAdd { data: icon_data }\n            | Win32TrayEvent::IconUpdate { data: icon_data } => {\n                if icon_data.tooltip.as_ref().is_none() {\n                    if let Some(window_handle) = icon_data.window_handle {\n                        let window = Window::from(window_handle);\n                        if let Ok(name) = window.app_display_name() {\n                            icon_data.tooltip = Some(name);\n                        }\n                    }\n                }\n            }\n            _ => {}\n        }\n\n        match &event {\n            Win32TrayEvent::IconAdd { data: icon_data }\n            | Win32TrayEvent::IconUpdate { data: icon_data } => {\n                let found_icon_id = self.find_icon(icon_data).map(|icon| icon.stable_id.clone());\n\n                let found_icon = match found_icon_id {\n                    Some(id) => self.icons.get(&id, |v| v.clone()),\n                    None => None,\n                };\n\n                // Update the icon in-place if found.\n                if let Some(found_icon) = found_icon {\n                    // Avoid emitting update events for no-op changes.\n                    if !has_change(&found_icon, icon_data) {\n                        return None;\n                    }\n\n                    let mut to_update = found_icon.clone();\n\n                    if let Some(uid) = icon_data.uid {\n                        to_update.uid = Some(uid);\n                    }\n\n                    if let Some(window_handle) = icon_data.window_handle {\n                        to_update.window_handle = Some(window_handle);\n                    }\n\n                    if let Some(guid) = icon_data.guid {\n                        to_update.guid = Some(guid);\n                    }\n\n                    if let Some(tooltip) = &icon_data.tooltip {\n                        to_update.tooltip = tooltip.clone();\n                    }\n\n                    if let Some(icon_handle) = icon_data.icon_handle {\n                        // Avoid re-reading the icon image if it's the same as the existing icon.\n                        if to_update.icon_handle != Some(icon_handle) {\n                            if let Ok(img) = convert_hicon_to_rgba_image(&HICON(icon_handle as _)) {\n                                to_update.icon_handle = Some(icon_handle);\n                                to_update.icon_image_hash = Some(image_to_hash(&img));\n\n                                let path = SEELEN_COMMON\n                                    .app_temp_dir()\n                                    .join(format!(\"{}.png\", to_update.stable_id));\n                                img.save(&path).unwrap();\n                                to_update.icon_path = Some(path);\n                            }\n                        }\n                    }\n\n                    if let Some(callback_message) = icon_data.callback_message {\n                        to_update.callback_message = Some(callback_message);\n                    }\n\n                    if let Some(version) = icon_data.version {\n                        to_update.version = Some(version);\n                    }\n\n                    to_update.is_visible = icon_data.is_visible;\n\n                    self.icons\n                        .upsert(to_update.stable_id.clone(), to_update.clone());\n                    Some(SystrayEvent::IconUpdate(to_update.clone()))\n                } else {\n                    // Icon doesn't exist yet, so add new icon. Skip icons that\n                    // cannot be identified.\n                    let stable_id = icon_data.guid.map(SysTrayIconId::Guid).or({\n                        match (icon_data.window_handle, icon_data.uid) {\n                            (Some(handle), Some(uid)) => {\n                                Some(SysTrayIconId::HandleUid(handle, uid))\n                            }\n                            _ => None,\n                        }\n                    })?;\n\n                    log::trace!(\"Tray icon added: {}\", stable_id);\n\n                    let mut icon_image_hash = None;\n                    let mut icon_path = None;\n\n                    if let Some(icon_handle) = icon_data.icon_handle {\n                        if let Ok(img) = convert_hicon_to_rgba_image(&HICON(icon_handle as _)) {\n                            icon_image_hash = Some(image_to_hash(&img));\n                            let path = SEELEN_COMMON\n                                .app_temp_dir()\n                                .join(format!(\"{}.png\", stable_id));\n                            img.save(&path).unwrap();\n                            icon_path = Some(path);\n                        }\n                    }\n\n                    let icon = SysTrayIcon {\n                        stable_id,\n                        uid: icon_data.uid,\n                        window_handle: icon_data.window_handle,\n                        guid: icon_data.guid,\n                        tooltip: icon_data.tooltip.clone().unwrap_or_default(),\n                        icon_handle: icon_data.icon_handle,\n                        icon_path,\n                        icon_image_hash,\n                        callback_message: icon_data.callback_message,\n                        version: icon_data.version,\n                        is_visible: icon_data.is_visible,\n                    };\n\n                    self.icons.upsert(icon.stable_id.clone(), icon.clone());\n                    Some(SystrayEvent::IconAdd(icon))\n                }\n            }\n            Win32TrayEvent::IconRemove { data: icon_data } => {\n                let icon_id = self.find_icon(icon_data).map(|icon| icon.stable_id.clone());\n                if let Some(icon_id) = icon_id {\n                    log::trace!(\"Tray icon removed: {}\", icon_id);\n                    self.icons.remove(&icon_id);\n                    Some(SystrayEvent::IconRemove(icon_id))\n                } else {\n                    None\n                }\n            }\n        }\n    }\n\n    /// Sends an action to the systray icon.\n    pub fn send_action(\n        &self,\n        icon_id: &SysTrayIconId,\n        action: &SystrayIconAction,\n    ) -> crate::Result<()> {\n        log::trace!(\"Sending icon action: {:?} to: {:?}\", action, icon_id);\n        let icon = self\n            .icons\n            .get(icon_id, |v| v.clone())\n            .ok_or(\"Icon not found\")?;\n\n        // Early return if we don't have the required fields.\n        let window_handle = icon\n            .window_handle\n            .ok_or(\"Inoperable icon, missing window handle\")?;\n        let uid = icon.uid.ok_or(\"Inoperable icon, missing uid\")?;\n        let callback = icon\n            .callback_message\n            .ok_or(\"Inoperable icon, missing callback\")?;\n\n        if !WindowsApi::is_window(HWND(window_handle as _)) {\n            return Err(\"Window handle is invalid\".into());\n        }\n\n        let is_mouse_click = matches!(\n            action,\n            SystrayIconAction::LeftClick\n                | SystrayIconAction::RightClick\n                | SystrayIconAction::MiddleClick\n        );\n\n        // For mouse clicks, there is often a menu that appears after the\n        // click. Allow the notify icon to gain focus so that the menu can be\n        // dismissed after clicking outside.\n        if is_mouse_click {\n            let mut proc_id = u32::default();\n            unsafe { GetWindowThreadProcessId(HWND(window_handle as _), Some(&mut proc_id)) };\n            let _ = unsafe { AllowSetForegroundWindow(proc_id) };\n        }\n\n        let wm_messages = match action {\n            SystrayIconAction::LeftClick => vec![WM_LBUTTONDOWN, WM_LBUTTONUP],\n            SystrayIconAction::LeftDoubleClick => vec![WM_LBUTTONDBLCLK, WM_LBUTTONUP],\n            SystrayIconAction::RightClick => {\n                vec![WM_RBUTTONDOWN, WM_RBUTTONUP]\n            }\n            SystrayIconAction::MiddleClick => {\n                vec![WM_MBUTTONDOWN, WM_MBUTTONUP]\n            }\n            SystrayIconAction::HoverEnter => vec![WM_MOUSEHOVER],\n            SystrayIconAction::HoverLeave => vec![WM_MOUSELEAVE],\n            SystrayIconAction::HoverMove => vec![WM_MOUSEMOVE],\n        };\n\n        for wm_message in wm_messages {\n            Self::notify_icon(window_handle, callback, uid, icon.version, wm_message)?;\n        }\n\n        // Additional messages are sent for version 4 and above. Explorer sends\n        // these for version 3 as well though, so we do the same.\n        // Ref: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyicona#remarks\n        if icon.version.is_some_and(|version| version >= 3) {\n            let v3_message = match action {\n                SystrayIconAction::HoverEnter => NIN_POPUPOPEN,\n                SystrayIconAction::HoverLeave => NIN_POPUPCLOSE,\n                SystrayIconAction::LeftClick => NIN_SELECT,\n                SystrayIconAction::RightClick => WM_CONTEXTMENU,\n                _ => return Ok(()),\n            };\n\n            Self::notify_icon(window_handle, callback, uid, icon.version, v3_message)?;\n        }\n\n        Ok(())\n    }\n\n    /// Sends a message to the systray icon window.\n    fn notify_icon(\n        window_handle: isize,\n        callback: u32,\n        uid: u32,\n        version: Option<u32>,\n        message: u32,\n    ) -> crate::Result<()> {\n        // The wparam is the mouse position for version > 3 (with the low and\n        // high word being the x and y-coordinates respectively), and the UID\n        // for version <= 3.\n        let wparam = if version.is_some_and(|version| version > 3) {\n            let cursor_pos = Util::cursor_position()?;\n            Util::pack_i32(cursor_pos.0 as i16, cursor_pos.1 as i16) as u32\n        } else {\n            uid\n        };\n\n        // The high word for the lparam is the UID for version > 3, and 0 for\n        // version <= 3. The low word is always the message.\n        let lparam = if version.is_some_and(|version| version > 3) {\n            Util::pack_i32(message as i16, uid as i16)\n        } else {\n            Util::pack_i32(message as i16, 0)\n        };\n\n        unsafe {\n            SendNotifyMessageW(\n                HWND(window_handle as _),\n                callback,\n                WPARAM(wparam as _),\n                LPARAM(lparam as _),\n            )\n        }?;\n\n        Ok(())\n    }\n}\n\n/// Computes a hash of the icon image.\nfn image_to_hash(icon_image: &image::RgbaImage) -> String {\n    let mut hasher = DefaultHasher::new();\n    icon_image.as_raw().hash(&mut hasher);\n    format!(\"{:x}\", hasher.finish())\n}\n\n/// Checks if the icon would change from the given icon data.\nfn has_change(icon: &SysTrayIcon, data: &IconEventData) -> bool {\n    data.uid.is_some_and(|uid| icon.uid != Some(uid))\n        || data\n            .window_handle\n            .is_some_and(|handle| icon.window_handle != Some(handle))\n        || data.guid.is_some_and(|guid| icon.guid != Some(guid))\n        || data.tooltip.as_ref().is_some_and(|t| &icon.tooltip != t)\n        || data\n            .icon_handle\n            .is_some_and(|handle| icon.icon_handle != Some(handle))\n        || data\n            .callback_message\n            .is_some_and(|msg| icon.callback_message != Some(msg))\n        || data.version.is_some_and(|ver| icon.version != Some(ver))\n        || icon.is_visible != data.is_visible\n}\n"
  },
  {
    "path": "src/background/modules/system_tray/application/util.rs",
    "content": "use windows::Win32::{\n    Foundation::{LPARAM, POINT, WPARAM},\n    UI::WindowsAndMessaging::{\n        GetCursorPos, RegisterWindowMessageW, SendNotifyMessageW, HWND_BROADCAST,\n    },\n};\n\nuse windows_core::w;\n\npub struct Util;\nimpl Util {\n    /// Packs two 16-bit values into a 32-bit value. This is commonly used\n    /// for `WPARAM` and `LPARAM` values.\n    ///\n    /// Equivalent to the Win32 `MAKELPARAM` and `MAKEWPARAM` macros.\n    pub fn pack_i32(low: i16, high: i16) -> i32 {\n        low as i32 | ((high as i32) << 16)\n    }\n\n    /// Gets the mouse position in screen coordinates.\n    pub fn cursor_position() -> crate::Result<(i32, i32)> {\n        let mut point = POINT { x: 0, y: 0 };\n        unsafe { GetCursorPos(&mut point) }?;\n        Ok((point.x, point.y))\n    }\n\n    /// Refreshes the icons of the tray.\n    ///\n    /// Simulates the Windows taskbar being re-created. Some windows fail to\n    /// re-add their icons, in which case it's an implementation error on\n    /// their side. These windows that fail also do not re-add their icons\n    /// to the Windows taskbar when `explorer.exe` is restarted ordinarily.\n    pub fn refresh_icons() -> crate::Result<()> {\n        log::info!(\"Refreshing icons by sending `TaskbarCreated` message.\");\n        let msg = unsafe { RegisterWindowMessageW(w!(\"TaskbarCreated\")) };\n        if msg == 0 {\n            return Err(\"Failed to register message\".into());\n        }\n        unsafe { SendNotifyMessageW(HWND_BROADCAST, msg, WPARAM::default(), LPARAM::default()) }?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/system_tray/domain.rs",
    "content": "\n"
  },
  {
    "path": "src/background/modules/system_tray/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{SysTrayIcon, SysTrayIconId, SystrayIconAction},\n};\n\nuse crate::{\n    app::emit_to_webviews, error::Result, modules::system_tray::application::SystemTrayManager,\n};\n\nfn get_system_tray_manager() -> &'static SystemTrayManager {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        SystemTrayManager::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::SystemTrayChanged,\n                SystemTrayManager::instance().icons(),\n            );\n        });\n    });\n    SystemTrayManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_system_tray_icons() -> Vec<SysTrayIcon> {\n    get_system_tray_manager().icons()\n}\n\n#[tauri::command(async)]\npub fn send_system_tray_icon_action(id: SysTrayIconId, action: SystrayIconAction) -> Result<()> {\n    get_system_tray_manager().send_action(&id, &action)?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/modules/system_tray/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n\npub use application::SystemTrayManager;\n"
  },
  {
    "path": "src/background/modules/trash_bin/application.rs",
    "content": "use std::sync::LazyLock;\n\nuse seelen_core::system_state::TrashBinInfo;\nuse windows::Win32::UI::Shell::{\n    SHEmptyRecycleBinW, SHQueryRecycleBinW, SHERB_NOSOUND, SHQUERYRBINFO,\n};\nuse windows_core::PCWSTR;\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    utils::lock_free::TracedMutex,\n    windows_api::event_window::{subscribe_to_background_window, WM_TRASH_BIN_NOTIFY},\n};\n\n#[derive(Debug)]\npub struct TrashBinManager {\n    pub info: TrashBinInfo,\n}\n\n#[derive(Debug, Clone)]\npub enum TrashBinManagerEvent {\n    InfoChanged(TrashBinInfo),\n}\n\nevent_manager!(TrashBinManager, TrashBinManagerEvent);\n\nimpl TrashBinManager {\n    pub fn instance() -> &'static TracedMutex<Self> {\n        static MANAGER: LazyLock<TracedMutex<TrashBinManager>> = LazyLock::new(|| {\n            let mut m = TrashBinManager::new();\n            m.init().log_error();\n            TracedMutex::new(m)\n        });\n        &MANAGER\n    }\n\n    fn new() -> Self {\n        Self {\n            info: TrashBinInfo::default(),\n        }\n    }\n\n    fn init(&mut self) -> Result<()> {\n        self.info = Self::query()?;\n\n        let eid = Self::subscribe(|event| {\n            let TrashBinManagerEvent::InfoChanged(info) = event;\n            Self::instance().lock().info = info;\n        });\n        Self::set_event_handler_priority(&eid, 1);\n\n        subscribe_to_background_window(Self::on_bg_window_proc);\n        Ok(())\n    }\n\n    pub fn query() -> Result<TrashBinInfo> {\n        let mut rb_info = SHQUERYRBINFO {\n            cbSize: std::mem::size_of::<SHQUERYRBINFO>() as u32,\n            ..Default::default()\n        };\n        unsafe { SHQueryRecycleBinW(PCWSTR::null(), &mut rb_info)? };\n        Ok(TrashBinInfo {\n            item_count: rb_info.i64NumItems,\n            size_in_bytes: rb_info.i64Size,\n        })\n    }\n\n    pub fn empty() -> Result<()> {\n        unsafe {\n            SHEmptyRecycleBinW(None, PCWSTR::null(), SHERB_NOSOUND)?;\n        }\n        Ok(())\n    }\n\n    fn on_bg_window_proc(msg: u32, _w_param: usize, _l_param: isize) -> Result<()> {\n        if msg != WM_TRASH_BIN_NOTIFY {\n            return Ok(());\n        }\n\n        let new_info = Self::query()?;\n        let current = Self::instance().lock().info.clone();\n\n        if new_info.item_count != current.item_count\n            || new_info.size_in_bytes != current.size_in_bytes\n        {\n            Self::send(TrashBinManagerEvent::InfoChanged(new_info));\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/modules/trash_bin/infrastructure.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{handlers::SeelenEvent, system_state::TrashBinInfo};\n\nuse crate::{app::emit_to_webviews, error::Result, utils::lock_free::TracedMutex};\n\nuse super::application::{TrashBinManager, TrashBinManagerEvent};\n\nfn get_trash_bin_manager() -> &'static TracedMutex<TrashBinManager> {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        TrashBinManager::subscribe(|event| {\n            let TrashBinManagerEvent::InfoChanged(info) = event;\n            emit_to_webviews(SeelenEvent::TrashBinChanged, info);\n        });\n    });\n    TrashBinManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_trash_bin_info() -> TrashBinInfo {\n    get_trash_bin_manager().lock().info.clone()\n}\n\n#[tauri::command(async)]\npub fn trash_bin_empty() -> Result<()> {\n    get_trash_bin_manager();\n    TrashBinManager::empty()\n}\n"
  },
  {
    "path": "src/background/modules/trash_bin/mod.rs",
    "content": "mod application;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/modules/user/application.rs",
    "content": "use notify_debouncer_full::{\n    new_debouncer,\n    notify::{ReadDirectoryChangesWatcher, RecursiveMode, Watcher},\n    DebounceEventResult, Debouncer, FileIdMap,\n};\nuse parking_lot::Mutex;\nuse seelen_core::system_state::{FolderType, User};\nuse std::{\n    collections::HashMap,\n    path::PathBuf,\n    sync::{Arc, LazyLock},\n    time::Duration,\n};\nuse tauri::Manager;\nuse windows::Win32::{\n    Security::Authentication::Identity::{NameDisplay, NameSamCompatible},\n    System::SystemInformation::ComputerNameDnsDomain,\n};\nuse winreg::{\n    enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE},\n    RegKey,\n};\n\nuse crate::{\n    app::get_app_handle, error::Result, event_manager, log_error, trace_lock,\n    windows_api::WindowsApi,\n};\n\nuse super::domain::PictureQuality;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum UserManagerEvent {\n    #[allow(dead_code)]\n    UserUpdated,\n    FolderChanged(FolderType),\n}\n\n#[derive(Debug)]\npub struct FolderDetails {\n    pub path: PathBuf,\n    pub content: Vec<PathBuf>,\n    _watcher: Debouncer<ReadDirectoryChangesWatcher, FileIdMap>,\n}\n\n#[derive(Debug)]\npub struct UserManager {\n    pub user: User,\n    pub folders: HashMap<FolderType, FolderDetails>,\n}\n\nunsafe impl Send for UserManager {}\nunsafe impl Send for UserManagerEvent {}\n\nevent_manager!(UserManager, UserManagerEvent);\n\nimpl UserManager {\n    pub fn instance() -> &'static Arc<Mutex<Self>> {\n        static USER_MANAGER: LazyLock<Arc<Mutex<UserManager>>> = LazyLock::new(|| {\n            Arc::new(Mutex::new(\n                UserManager::new().expect(\"Failed to create user manager\"),\n            ))\n        });\n        &USER_MANAGER\n    }\n\n    fn get_path_from_folder(folder_type: &FolderType) -> Option<PathBuf> {\n        let resolver = get_app_handle().path();\n        match folder_type {\n            FolderType::Recent => {\n                Some(resolver.data_dir().ok()?.join(\"Microsoft\\\\Windows\\\\Recent\"))\n            }\n            FolderType::Desktop => resolver.desktop_dir().ok(),\n            FolderType::Downloads => resolver.download_dir().ok(),\n            FolderType::Documents => resolver.document_dir().ok(),\n            FolderType::Pictures => resolver.picture_dir().ok(),\n            FolderType::Videos => resolver.video_dir().ok(),\n            FolderType::Music => resolver.audio_dir().ok(),\n        }\n    }\n\n    fn get_logged_on_user_sid() -> Result<String> {\n        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);\n        let settings = hklm\n            .open_subkey(\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Authentication\\\\LogonUI\")?;\n        Ok(settings.get_value(\"LastLoggedOnUserSID\")?)\n    }\n\n    fn get_folder_content(base_path: PathBuf, folder_type: FolderType) -> Result<Vec<PathBuf>> {\n        // Recent is a flat folder of .lnk shortcuts; others are scanned up to 5 levels deep\n        // to avoid exploding on large/deep folder trees.\n        let max_depth = match folder_type {\n            FolderType::Recent => 1,\n            _ => 5,\n        };\n\n        let mut list = Vec::new();\n\n        for entry in walkdir::WalkDir::new(&base_path)\n            .follow_links(false)\n            .max_depth(max_depth)\n            .into_iter()\n            .filter_entry(|e| !is_ignored_entry(e.path()))\n            .filter_map(|e| e.ok())\n        {\n            let path = entry.path();\n            if !path.is_file() || is_ignored_file(path) {\n                continue;\n            }\n\n            list.push(path.to_path_buf());\n        }\n\n        Ok(list)\n    }\n\n    fn get_user_profile_picture_path(sid: &str, quality: PictureQuality) -> Result<PathBuf> {\n        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);\n        let settings = hklm.open_subkey(\n            format!(\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\AccountPicture\\\\Users\\\\{sid}\")\n                .as_str(),\n        )?;\n        let path: String = settings.get_value(quality.as_str())?;\n        Ok(path.into())\n    }\n\n    fn get_one_drive_attributes() -> Result<(String, PathBuf)> {\n        let hkcu = RegKey::predef(HKEY_CURRENT_USER);\n        let settings = hkcu.open_subkey(\"SOFTWARE\\\\Microsoft\\\\OneDrive\\\\Accounts\\\\Personal\")?;\n        let email: String = settings.get_value(\"UserEmail\")?;\n        let path: String = settings.get_value(\"UserFolder\")?;\n        Ok((email, PathBuf::from(path)))\n    }\n\n    fn get_xbox_gamertag() -> Result<String> {\n        let hkcu = RegKey::predef(HKEY_CURRENT_USER);\n        let settings = hkcu.open_subkey(\"SOFTWARE\\\\Microsoft\\\\XboxLive\")?;\n        let gamertag: String = settings.get_value(\"Gamertag\")?;\n        Ok(gamertag)\n    }\n\n    fn get_logged_user() -> User {\n        let domain = WindowsApi::get_computer_name(ComputerNameDnsDomain).unwrap_or_default();\n        let name = WindowsApi::get_username(NameDisplay)\n            .or_else(|_| -> Result<String> {\n                // A legacy account name (for example, Engineering\\JSmith).\n                // The domain-only version includes trailing backslashes (\\).\n                let name = WindowsApi::get_username(NameSamCompatible)?;\n                let name = name.split(\"\\\\\").last().unwrap_or_default();\n                match name.is_empty() {\n                    true => Err(\"Empty username\".into()),\n                    false => Ok(name.to_string()),\n                }\n            })\n            .unwrap_or_else(|_| \"???\".to_string()); // no username\n\n        let mut user = User {\n            name,\n            domain,\n            profile_home_path: PathBuf::new(), // deprecated, remove this is unnecessary\n            email: None,\n            one_drive_path: None,\n            profile_picture_path: None,\n            xbox_gamertag: None,\n        };\n\n        if let Ok(sid) = Self::get_logged_on_user_sid() {\n            user.profile_picture_path =\n                Self::get_user_profile_picture_path(&sid, PictureQuality::Quality1080).ok();\n            if let Ok((user_mail, one_drive_path)) = Self::get_one_drive_attributes() {\n                user.email = Some(user_mail);\n                user.one_drive_path = Some(one_drive_path);\n            }\n        }\n\n        user.xbox_gamertag = Self::get_xbox_gamertag().ok();\n\n        user\n    }\n\n    fn create_folder_watcher(\n        folder_type: FolderType,\n    ) -> Result<Debouncer<ReadDirectoryChangesWatcher, FileIdMap>> {\n        let debouncer = new_debouncer(\n            Duration::from_millis(1000),\n            None,\n            move |result: DebounceEventResult| match result {\n                Ok(_events) => {\n                    log_error!(Self::reload_folder_content(folder_type));\n                }\n                Err(errors) => {\n                    log::error!(\"Folder Watcher Error for {:?}: {errors:?}\", folder_type);\n                }\n            },\n        )?;\n        Ok(debouncer)\n    }\n\n    fn reload_folder_content(folder_type: FolderType) -> Result<()> {\n        let mut manager = trace_lock!(Self::instance());\n\n        if let Some(folder_details) = manager.folders.get_mut(&folder_type) {\n            folder_details.content =\n                Self::get_folder_content(folder_details.path.clone(), folder_type)?;\n            drop(manager);\n            let _ = Self::event_tx().send(UserManagerEvent::FolderChanged(folder_type));\n        }\n\n        Ok(())\n    }\n\n    pub fn new() -> Result<Self> {\n        let mut folders = HashMap::new();\n\n        for &folder_type in FolderType::values() {\n            if let Some(path) = Self::get_path_from_folder(&folder_type) {\n                let content =\n                    Self::get_folder_content(path.clone(), folder_type).unwrap_or_default();\n                let mut watcher = Self::create_folder_watcher(folder_type)?;\n                watcher.watcher().watch(&path, RecursiveMode::Recursive)?;\n\n                folders.insert(\n                    folder_type,\n                    FolderDetails {\n                        path,\n                        content,\n                        _watcher: watcher,\n                    },\n                );\n            }\n        }\n\n        Ok(Self {\n            user: Self::get_logged_user(),\n            folders,\n        })\n    }\n}\n\n/// Returns true if the entry should be excluded from the file list.\n/// When applied via `filter_entry`, returning true for a directory prunes its entire subtree.\nfn is_ignored_entry(path: &std::path::Path) -> bool {\n    let Some(name) = path.file_name().and_then(|n| n.to_str()) else {\n        return false;\n    };\n\n    matches!(\n        name,\n        // Dev artifact directories\n        \"node_modules\"\n            | \"target\"\n            | \"dist\"\n            | \"build\"\n            | \"out\"\n            | \"__pycache__\"\n            | \"venv\"\n            | \".venv\"\n            | \"env\"\n            | \"vendor\"\n            // VCS\n            | \".git\"\n            | \".svn\"\n            | \".hg\"\n            // IDE/editor\n            | \".idea\"\n            | \".vscode\"\n            | \".next\"\n            // Cache\n            | \".cache\"\n            | \"cache\"\n            | \"Cache\"\n            // Windows system\n            | \"$RECYCLE.BIN\"\n            | \"System Volume Information\"\n            | \"WindowsApps\"\n            | \"MicrosoftEdgeBackups\"\n    )\n}\n\nfn is_ignored_file(path: &std::path::Path) -> bool {\n    let Some(extension) = path.extension() else {\n        return true; // filter files without extension\n    };\n\n    let ext = extension.to_string_lossy().to_lowercase();\n    [\n        // Temp / backup\n        \"ini\",\n        \"dat\",\n        \"bak\",\n        \"tmp\",\n        \"temp\",\n        \"old\",\n        \"swp\",\n        // In-progress downloads\n        \"download\",\n        \"crdownload\",\n        \"part\",\n        // Lock / pid files\n        \"lock\",\n        \"pid\",\n        // Log files\n        \"log\",\n        // Database / cache files\n        \"db\",\n        \"sqlite\",\n        \"sqlite3\",\n        \"ldb\",\n        // Build artifacts\n        \"obj\",\n        \"pdb\",\n        \"ilk\",\n        \"exp\",\n        \"iobj\",\n        \"ipdb\",\n        // Windows shortcut noise (internet shortcuts, not file shortcuts)\n        \"url\",\n    ]\n    .contains(&ext.as_str())\n}\n"
  },
  {
    "path": "src/background/modules/user/domain.rs",
    "content": "#[allow(dead_code)]\npub enum PictureQuality {\n    Quality1080,\n    Quality448,\n    Quality424,\n    Quality240,\n    Quality208,\n    Quality192,\n    Quality96,\n    Quality64,\n    Quality48,\n    Quality40,\n    Quality32,\n}\n\nimpl PictureQuality {\n    pub fn as_str(&self) -> &'static str {\n        match self {\n            PictureQuality::Quality1080 => \"Image1080\",\n            PictureQuality::Quality192 => \"Image192\",\n            PictureQuality::Quality208 => \"Image208\",\n            PictureQuality::Quality240 => \"Image240\",\n            PictureQuality::Quality32 => \"Image32\",\n            PictureQuality::Quality40 => \"Image40\",\n            PictureQuality::Quality424 => \"Image424\",\n            PictureQuality::Quality448 => \"Image448\",\n            PictureQuality::Quality48 => \"Image48\",\n            PictureQuality::Quality64 => \"Image64\",\n            PictureQuality::Quality96 => \"Image96\",\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/modules/user/infrastructure.rs",
    "content": "use std::{\n    path::PathBuf,\n    sync::{Arc, Once},\n};\n\nuse parking_lot::Mutex;\nuse seelen_core::{\n    handlers::SeelenEvent,\n    system_state::{FolderChangedArgs, FolderType, User},\n};\n\nuse crate::{app::emit_to_webviews, trace_lock};\n\nuse super::application::{UserManager, UserManagerEvent};\n\nfn get_user_manager() -> &'static Arc<Mutex<UserManager>> {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        UserManager::subscribe(|event| match event {\n            UserManagerEvent::UserUpdated => {\n                let guard = trace_lock!(UserManager::instance());\n                emit_to_webviews(SeelenEvent::UserChanged, &guard.user);\n            }\n            UserManagerEvent::FolderChanged(folder) => {\n                emit_to_webviews(\n                    SeelenEvent::UserFolderChanged,\n                    FolderChangedArgs {\n                        of_folder: folder,\n                        content: get_user_folder_content(folder),\n                    },\n                );\n            }\n        });\n    });\n    UserManager::instance()\n}\n\n#[tauri::command(async)]\npub fn get_user() -> User {\n    trace_lock!(get_user_manager()).user.clone()\n}\n\n#[tauri::command(async)]\npub fn get_user_folder_content(folder_type: FolderType) -> Vec<PathBuf> {\n    let manager = trace_lock!(get_user_manager());\n    match manager.folders.get(&folder_type) {\n        Some(details) => details.content.clone(),\n        None => Vec::new(),\n    }\n}\n"
  },
  {
    "path": "src/background/modules/user/mod.rs",
    "content": "mod application;\nmod domain;\npub mod infrastructure;\n"
  },
  {
    "path": "src/background/resources/cli.rs",
    "content": "use std::path::PathBuf;\n\nuse seelen_core::{\n    resource::{ResourceKind, SluResource},\n    state::{IconPack, Plugin, Theme, Wallpaper, Widget},\n};\nuse serde::{Deserialize, Serialize};\n\nuse crate::{\n    cli::application::{CommandExecutionMode, SluCliCommand},\n    error::Result,\n    exposed::translate_file,\n    resources::RESOURCES,\n};\n\n/// Manage the Seelen Resources.\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\npub struct ResourceManagerCli {\n    #[command(subcommand)]\n    subcommand: SubCommand,\n}\n\n#[derive(Debug, Serialize, Deserialize, clap::Subcommand)]\nenum SubCommand {\n    /// loads a widget into the internal registry\n    Load {\n        kind: ClapResourceKind,\n        path: PathBuf,\n    },\n    /// deletes the widget from internal registry\n    Unload {\n        kind: ClapResourceKind,\n        path: PathBuf,\n    },\n    /// Bundles a widget into a single file to be shared.\n    ///\n    /// Exported file will be at the same location as the passed path\n    /// with a filename `export_{date}.yml`.\n    Bundle {\n        kind: ClapResourceKind,\n        path: PathBuf,\n    },\n    /// Translates a resource text file to all the supported languages by Seelen UI\n    /// this file should contain the source language key and value in order to be translated.\n    ///\n    /// Example:\n    /// ```yaml\n    /// # The file will be completed with the rest of the supported languages\n    /// en: Some text to be translated\n    /// ```\n    ///\n    Translate {\n        /// The file to be translated\n        path: PathBuf,\n        /// The source language of the file, by default `en`\n        source_lang: Option<String>,\n    },\n}\n\nimpl SluCliCommand for SubCommand {\n    fn execution_mode(&self) -> CommandExecutionMode {\n        match self {\n            // Commands that execute directly (don't need main instance running)\n            SubCommand::Bundle { .. } => CommandExecutionMode::Direct,\n            SubCommand::Translate { .. } => CommandExecutionMode::Direct,\n            // Commands that need main instance (use default)\n            SubCommand::Load { .. } => CommandExecutionMode::MainInstance,\n            SubCommand::Unload { .. } => CommandExecutionMode::MainInstance,\n        }\n    }\n}\n\nimpl SluCliCommand for ResourceManagerCli {\n    fn execution_mode(&self) -> CommandExecutionMode {\n        self.subcommand.execution_mode()\n    }\n}\n\nimpl ResourceManagerCli {\n    /// Processes commands that need to run in the main Seelen UI instance\n    pub fn process(self) -> Result<()> {\n        match self.subcommand {\n            SubCommand::Load { kind, path } => {\n                let kind = kind.into();\n                RESOURCES.load(&kind, &path)?;\n                let _ = RESOURCES.manual.insert(path);\n                RESOURCES.emit_kind_changed(&kind)?;\n            }\n            SubCommand::Unload { kind, path } => {\n                let kind = kind.into();\n                RESOURCES.unload(&kind, &path);\n                RESOURCES.manual.remove(&path);\n                RESOURCES.emit_kind_changed(&kind)?;\n            }\n            // Direct commands should not reach here\n            _ => {\n                return Err(\"This command should be executed directly in console\".into());\n            }\n        }\n        Ok(())\n    }\n\n    /// Processes commands that execute directly in the console\n    pub async fn process_direct(self) -> Result<()> {\n        match self.subcommand {\n            SubCommand::Bundle { kind, path } => {\n                let mut to_store_path = path.clone();\n\n                let format = time::macros::format_description!(\n                    \"[year]-[month]-[day] [hour]-[minute]-[second]\"\n                );\n                let date =\n                    time::OffsetDateTime::now_local().map_err(time::Error::IndeterminateOffset)?;\n                let date_str = date.format(&format).map_err(time::Error::Format)?;\n                let filename = format!(\"bundle {date_str}.yml\");\n\n                if to_store_path.is_dir() {\n                    to_store_path.push(filename);\n                } else {\n                    to_store_path.set_file_name(filename);\n                }\n\n                match kind {\n                    ClapResourceKind::Theme => {\n                        let mut theme = Theme::load(&path)?;\n                        theme.metadata.internal.path = to_store_path.clone();\n                        theme.save()?\n                    }\n                    ClapResourceKind::Plugin => {\n                        let mut plugin = Plugin::load(&path)?;\n                        plugin.metadata.internal.path = to_store_path.clone();\n                        plugin.save()?\n                    }\n                    ClapResourceKind::Widget => {\n                        let mut widget = Widget::load(&path)?;\n                        widget.metadata.internal.path = to_store_path.clone();\n                        widget.save()?\n                    }\n                    ClapResourceKind::IconPack => {\n                        let mut icon_pack = IconPack::load(&path)?;\n                        icon_pack.metadata.internal.path = to_store_path.clone();\n                        icon_pack.save()?\n                    }\n                    ClapResourceKind::Wallpaper => {\n                        let mut wallpaper = Wallpaper::load(&path)?;\n                        wallpaper.metadata.internal.path = to_store_path.clone();\n                        wallpaper.save()?\n                    }\n                    _ => {\n                        return Err(\"Not implemented\".into());\n                    }\n                }\n                println!(\n                    \"Bundle created successfully at: {}\",\n                    to_store_path.display()\n                );\n            }\n            SubCommand::Translate { path, source_lang } => {\n                translate_file(path, source_lang).await?\n            }\n            // MainInstance commands should not reach here\n            _ => {\n                return Err(\"This command needs Seelen UI to be running\".into());\n            }\n        }\n\n        Ok(())\n    }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, clap::ValueEnum)]\nenum ClapResourceKind {\n    Theme,\n    Widget,\n    Plugin,\n    IconPack,\n    Wallpaper,\n    SoundPack,\n}\n\nimpl From<ClapResourceKind> for ResourceKind {\n    fn from(value: ClapResourceKind) -> Self {\n        match value {\n            ClapResourceKind::Theme => ResourceKind::Theme,\n            ClapResourceKind::IconPack => ResourceKind::IconPack,\n            ClapResourceKind::Widget => ResourceKind::Widget,\n            ClapResourceKind::Plugin => ResourceKind::Plugin,\n            ClapResourceKind::Wallpaper => ResourceKind::Wallpaper,\n            ClapResourceKind::SoundPack => ResourceKind::SoundPack,\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/resources/commands.rs",
    "content": "use seelen_core::{\n    resource::{ResourceId, ResourceKind, SluResource},\n    state::{IconPack, Plugin, Theme, Wallpaper, Widget},\n};\n\nuse crate::{\n    error::Result, log_error, resources::RESOURCES, utils::icon_extractor::queue::IconExtractor,\n};\nuse std::sync::Arc;\n\n#[tauri::command(async)]\npub fn remove_resource(kind: ResourceKind, id: ResourceId) -> Result<()> {\n    match kind {\n        ResourceKind::Theme => {\n            RESOURCES.themes.retain(|_, v| {\n                if *v.id == id && !v.metadata.internal.bundled {\n                    log_error!(v.delete());\n                    return false;\n                }\n                true\n            });\n        }\n        ResourceKind::Plugin => {\n            RESOURCES.plugins.retain(|_, v| {\n                if *v.id == id && !v.metadata.internal.bundled {\n                    log_error!(v.delete());\n                    return false;\n                }\n                true\n            });\n        }\n        ResourceKind::Widget => {\n            RESOURCES.widgets.retain(|_, v| {\n                if *v.id == id && !v.metadata.internal.bundled {\n                    log_error!(v.delete());\n                    return false;\n                }\n                true\n            });\n        }\n        ResourceKind::IconPack => {\n            RESOURCES.icon_packs.retain(|_, v| {\n                if *v.id == id && !v.metadata.internal.bundled {\n                    log_error!(v.delete());\n                    return false;\n                }\n                true\n            });\n        }\n        ResourceKind::Wallpaper => {\n            RESOURCES.wallpapers.retain(|_, v| {\n                if *v.id == id && !v.metadata.internal.bundled {\n                    log_error!(v.delete());\n                    return false;\n                }\n                true\n            });\n        }\n        ResourceKind::SoundPack => {\n            // feature not implemented\n        }\n    }\n    RESOURCES.emit_kind_changed(&kind)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn state_get_themes() -> Vec<Arc<Theme>> {\n    let mut themes = Vec::new();\n    RESOURCES.themes.scan(|_, v| {\n        themes.push(v.clone());\n    });\n    themes\n}\n\n#[tauri::command(async)]\npub fn state_get_plugins() -> Vec<Arc<Plugin>> {\n    let mut plugins = Vec::new();\n    RESOURCES.plugins.scan(|_, v| {\n        plugins.push(v.clone());\n    });\n    plugins\n}\n\n#[tauri::command(async)]\npub fn state_get_widgets() -> Vec<Arc<Widget>> {\n    let mut widgets = Vec::new();\n    RESOURCES.widgets.scan(|_, v| {\n        widgets.push(v.clone());\n    });\n    widgets\n}\n\n#[tauri::command(async)]\npub fn state_get_wallpapers() -> Vec<Arc<Wallpaper>> {\n    let mut wallpapers = Vec::new();\n    RESOURCES.wallpapers.scan(|_, v| {\n        wallpapers.push(v.clone());\n    });\n    wallpapers\n}\n\n#[tauri::command(async)]\npub fn state_get_icon_packs() -> Vec<Arc<IconPack>> {\n    let mut icon_packs = Vec::new();\n\n    // Add system icon pack if it exists\n    if let Some(system_pack) = RESOURCES.system_icon_pack.lock().as_ref() {\n        icon_packs.push(Arc::new(system_pack.clone()));\n    }\n\n    // Add user icon packs\n    RESOURCES.icon_packs.scan(|_, v| {\n        icon_packs.push(v.clone());\n    });\n\n    icon_packs\n}\n\n#[tauri::command(async)]\npub fn state_delete_cached_icons() -> Result<()> {\n    if let Some(pack) = RESOURCES.system_icon_pack.lock().take() {\n        pack.delete()?;\n    }\n    IconExtractor::instance().clear_failures();\n    RESOURCES.ensure_system_icon_pack()?;\n    RESOURCES.emit_icon_packs()?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/resources/emitters.rs",
    "content": "use seelen_core::{handlers::SeelenEvent, resource::ResourceKind};\n\nuse crate::{app::emit_to_webviews, error::Result, widgets::manager::WIDGET_MANAGER};\n\nuse super::ResourceManager;\n\nimpl ResourceManager {\n    pub fn emit_widgets(&self) -> Result<()> {\n        let mut widgets = Vec::new();\n        self.widgets.scan(|_, v| {\n            widgets.push(v.clone());\n        });\n        emit_to_webviews(SeelenEvent::StateWidgetsChanged, widgets);\n        WIDGET_MANAGER.refresh()?;\n        Ok(())\n    }\n\n    pub fn emit_themes(&self) -> Result<()> {\n        let mut themes = Vec::new();\n        self.themes.scan(|_, v| {\n            themes.push(v.clone());\n        });\n        emit_to_webviews(SeelenEvent::StateThemesChanged, themes);\n        Ok(())\n    }\n\n    pub fn emit_plugins(&self) -> Result<()> {\n        let mut plugins = Vec::new();\n        self.plugins.scan(|_, v| {\n            plugins.push(v.clone());\n        });\n        emit_to_webviews(SeelenEvent::StatePluginsChanged, plugins);\n        Ok(())\n    }\n\n    pub fn emit_icon_packs(&self) -> Result<()> {\n        let mut icon_packs = Vec::new();\n\n        // Add system icon pack if it exists\n        if let Some(system_pack) = self.system_icon_pack.lock().as_ref() {\n            icon_packs.push(std::sync::Arc::new(system_pack.clone()));\n        }\n\n        // Add user icon packs\n        self.icon_packs.scan(|_, v| {\n            icon_packs.push(v.clone());\n        });\n\n        emit_to_webviews(SeelenEvent::StateIconPacksChanged, icon_packs);\n        Ok(())\n    }\n\n    pub fn emit_wallpapers(&self) -> Result<()> {\n        let mut wallpaper = Vec::new();\n        self.wallpapers.scan(|_, v| {\n            wallpaper.push(v.clone());\n        });\n        emit_to_webviews(SeelenEvent::StateWallpapersChanged, wallpaper);\n        Ok(())\n    }\n\n    pub fn emit_kind_changed(&self, kind: &ResourceKind) -> Result<()> {\n        match kind {\n            ResourceKind::Theme => self.emit_themes()?,\n            ResourceKind::Widget => self.emit_widgets()?,\n            ResourceKind::Plugin => self.emit_plugins()?,\n            ResourceKind::IconPack => self.emit_icon_packs()?,\n            ResourceKind::Wallpaper => self.emit_wallpapers()?,\n            ResourceKind::SoundPack => {\n                // feature not implemented\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/resources/mod.rs",
    "content": "pub mod cli;\npub mod commands;\nmod emitters;\nmod system_icon_pack;\n\nuse std::{\n    path::{Path, PathBuf},\n    sync::{Arc, LazyLock},\n};\n\nuse seelen_core::{\n    resource::{IconPackId, PluginId, ResourceKind, SluResource, ThemeId, WallpaperId, WidgetId},\n    state::{IconPack, Plugin, Theme, Wallpaper, Widget},\n};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    utils::{constants::SEELEN_COMMON, date_based_hex_id, lock_free::TracedMutex},\n};\n\npub static RESOURCES: LazyLock<Arc<ResourceManager>> = LazyLock::new(|| {\n    let resources = ResourceManager::default();\n    resources.initialize();\n    Arc::new(resources)\n});\n\n#[derive(Default)]\npub struct ResourceManager {\n    pub themes: scc::HashMap<ThemeId, Arc<Theme>>,\n    pub plugins: scc::HashMap<PluginId, Arc<Plugin>>,\n    pub widgets: scc::HashMap<WidgetId, Arc<Widget>>,\n    pub wallpapers: scc::HashMap<WallpaperId, Arc<Wallpaper>>,\n    /// this list doesn't include the system icon pack, this is managed by the system_icon_pack field.\n    pub icon_packs: scc::HashMap<IconPackId, Arc<IconPack>>,\n    /// current system icon pack\n    pub system_icon_pack: Arc<TracedMutex<Option<IconPack>>>,\n    /// list of manual loaded resources\n    pub manual: scc::HashSet<PathBuf>,\n}\n\nimpl ResourceManager {\n    fn initialize(&self) {\n        self.load_all_of_type(ResourceKind::Theme).log_error();\n        self.load_all_of_type(ResourceKind::Plugin).log_error();\n        self.load_all_of_type(ResourceKind::Widget).log_error();\n        self.load_all_of_type(ResourceKind::Wallpaper).log_error();\n        self.load_all_of_type(ResourceKind::IconPack).log_error();\n    }\n\n    pub fn load(&self, kind: &ResourceKind, path: &Path) -> Result<()> {\n        match kind {\n            ResourceKind::Theme => {\n                let mut theme = Theme::load(path)?;\n                if theme.id.starts_with(\"@deprecated\") {\n                    return Ok(());\n                }\n                theme.metadata.internal.bundled =\n                    path.starts_with(SEELEN_COMMON.bundled_themes_path());\n                self.themes.upsert(theme.id.clone(), Arc::new(theme));\n            }\n            ResourceKind::Widget => {\n                let mut widget = Widget::load(path)?;\n                widget.metadata.internal.bundled =\n                    path.starts_with(SEELEN_COMMON.bundled_widgets_path());\n\n                widget\n                    .plugins\n                    .retain(|plugin| !plugin.metadata.internal.path.starts_with(path));\n\n                for mut plugin in widget.plugins.clone() {\n                    plugin.metadata.internal = widget.metadata.internal.clone();\n                    self.plugins.upsert(plugin.id.clone(), Arc::new(plugin));\n                }\n\n                self.widgets.upsert(widget.id.clone(), Arc::new(widget));\n            }\n            ResourceKind::Plugin => {\n                let mut plugin = Plugin::load(path)?;\n                plugin.metadata.internal.bundled =\n                    path.starts_with(SEELEN_COMMON.bundled_plugins_path());\n                self.plugins.upsert(plugin.id.clone(), Arc::new(plugin));\n            }\n            ResourceKind::Wallpaper => {\n                if path.is_file() {\n                    let Some(extension) = path.extension() else {\n                        return Err(\"Wallpaper has no extension\".into());\n                    };\n\n                    let extension = extension.to_string_lossy().to_lowercase();\n                    if Wallpaper::SUPPORTED_IMAGES.contains(&extension.as_ref())\n                        || Wallpaper::SUPPORTED_VIDEOS.contains(&extension.as_ref())\n                    {\n                        let wallpaper = Wallpaper::create_from_file(\n                            path,\n                            &SEELEN_COMMON\n                                .user_wallpapers_path()\n                                .join(date_based_hex_id()),\n                            // copy if file is outside of user wallpapers (ex: Desktop)\n                            !path.starts_with(SEELEN_COMMON.user_wallpapers_path()),\n                        )?;\n                        self.wallpapers\n                            .upsert(wallpaper.id.clone(), Arc::new(wallpaper));\n                    }\n                    return Ok(());\n                }\n\n                let wallpaper = Wallpaper::load(path)?;\n                self.wallpapers\n                    .upsert(wallpaper.id.clone(), Arc::new(wallpaper));\n            }\n            ResourceKind::IconPack => {\n                let is_system = path == SEELEN_COMMON.system_icon_pack_path();\n                if is_system {\n                    let mut system_pack = self.system_icon_pack.lock();\n                    if system_pack.is_none() {\n                        let mut icon_pack = IconPack::load(path)?;\n                        icon_pack.metadata.internal.bundled = true;\n                        // we only read the system icon pack once, after that it is entirely runtime managed\n                        *system_pack = Some(icon_pack);\n                    }\n                } else {\n                    let icon_pack = IconPack::load(path)?;\n                    self.icon_packs\n                        .upsert(icon_pack.id.clone(), Arc::new(icon_pack));\n                }\n            }\n            ResourceKind::SoundPack => {\n                // feature not implemented\n            }\n        }\n        Ok(())\n    }\n\n    pub fn unload(&self, kind: &ResourceKind, path: &Path) {\n        match kind {\n            ResourceKind::Theme => {\n                self.themes.retain(|_, v| v.metadata.internal.path != path);\n            }\n            ResourceKind::Widget => {\n                self.widgets.retain(|_, v| v.metadata.internal.path != path);\n            }\n            ResourceKind::Plugin => {\n                self.plugins.retain(|_, v| v.metadata.internal.path != path);\n            }\n            ResourceKind::Wallpaper => {\n                self.wallpapers\n                    .retain(|_, v| v.metadata.internal.path != path);\n            }\n            ResourceKind::IconPack => {\n                self.icon_packs\n                    .retain(|_, v| v.metadata.internal.path != path);\n            }\n            ResourceKind::SoundPack => {\n                // feature not implemented\n            }\n        }\n    }\n\n    pub fn unload_all(&self, kind: &ResourceKind) {\n        match kind {\n            ResourceKind::Theme => self\n                .themes\n                .retain(|_, v| !self.manual.contains(&v.metadata.internal.path)),\n            ResourceKind::Plugin => self\n                .plugins\n                .retain(|_, v| !self.manual.contains(&v.metadata.internal.path)),\n            ResourceKind::Widget => self\n                .widgets\n                .retain(|_, v| !self.manual.contains(&v.metadata.internal.path)),\n            ResourceKind::IconPack => self\n                .icon_packs\n                .retain(|_, v| !self.manual.contains(&v.metadata.internal.path)),\n            ResourceKind::Wallpaper => self\n                .wallpapers\n                .retain(|_, v| !self.manual.contains(&v.metadata.internal.path)),\n            ResourceKind::SoundPack => {\n                // feature not implemented\n            }\n        }\n    }\n\n    /// returns a list of dirs to be read by this kind\n    fn get_entries_for_type(kind: &ResourceKind) -> Result<Vec<std::fs::ReadDir>> {\n        let list = match kind {\n            ResourceKind::Theme => {\n                let user_path = SEELEN_COMMON.user_themes_path();\n                let bundled_path = SEELEN_COMMON.bundled_themes_path();\n                vec![\n                    std::fs::read_dir(bundled_path)?,\n                    std::fs::read_dir(user_path)?,\n                ]\n            }\n            ResourceKind::Widget => {\n                let user_path = SEELEN_COMMON.user_widgets_path();\n                let bundled_path = SEELEN_COMMON.bundled_widgets_path();\n                vec![\n                    std::fs::read_dir(bundled_path)?,\n                    std::fs::read_dir(user_path)?,\n                ]\n            }\n            ResourceKind::Plugin => {\n                let user_path = SEELEN_COMMON.user_plugins_path();\n                let bundled_path = SEELEN_COMMON.bundled_plugins_path();\n                vec![\n                    std::fs::read_dir(bundled_path)?,\n                    std::fs::read_dir(user_path)?,\n                ]\n            }\n            ResourceKind::Wallpaper => {\n                let user_path = SEELEN_COMMON.user_wallpapers_path();\n                vec![std::fs::read_dir(user_path)?]\n            }\n            ResourceKind::IconPack => {\n                let user_path = SEELEN_COMMON.user_icons_path();\n                vec![std::fs::read_dir(user_path)?]\n            }\n            ResourceKind::SoundPack => {\n                let user_path = SEELEN_COMMON.user_sounds_path();\n                vec![std::fs::read_dir(user_path)?]\n            }\n        };\n        Ok(list)\n    }\n\n    pub fn load_all_of_type(&self, kind: ResourceKind) -> Result<()> {\n        log::trace!(\"Loading {kind:?}s\");\n\n        let entries = Self::get_entries_for_type(&kind)?;\n        self.unload_all(&kind);\n\n        for entry in entries.into_iter().flatten().flatten() {\n            match self.load(&kind, &entry.path()) {\n                Ok(_) => {}\n                Err(e) => {\n                    log::error!(\"Failed to load {kind:?}, error: {e}\");\n                }\n            }\n        }\n\n        if kind == ResourceKind::IconPack {\n            // try load system icon pack\n            let _ = self.load(&kind, SEELEN_COMMON.system_icon_pack_path());\n            // creates the system icon pack if not loaded\n            self.ensure_system_icon_pack()?;\n        }\n        Ok(())\n    }\n}\n\nunsafe impl Send for ResourceManager {}\nunsafe impl Sync for ResourceManager {}\n"
  },
  {
    "path": "src/background/resources/system_icon_pack.rs",
    "content": "use std::{\n    path::{Path, PathBuf},\n    sync::LazyLock,\n};\n\nuse seelen_core::chrono::{DateTime, Utc};\nuse seelen_core::{\n    resource::{ResourceText, SluResource},\n    state::{\n        CustomIconPackEntry, Icon, IconPack, IconPackEntry, SharedIconPackEntry,\n        UniqueIconPackEntry,\n    },\n};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    resources::{ResourceManager, RESOURCES},\n    utils::constants::SEELEN_COMMON,\n};\n\nstatic SAVE_SYSTEM_ICON_PACK: LazyLock<slu_utils::Throttle<()>> = LazyLock::new(|| {\n    slu_utils::throttle(\n        |()| {\n            RESOURCES.with_system_pack(|pack| pack.save()).log_error();\n        },\n        std::time::Duration::from_secs(1),\n    )\n});\n\nimpl ResourceManager {\n    fn with_system_pack<F, T>(&self, cb: F) -> T\n    where\n        F: FnOnce(&mut IconPack) -> T,\n    {\n        let mut guard = self.system_icon_pack.lock();\n        cb(guard\n            .as_mut()\n            .expect(\"System icon pack should always exist.\"))\n    }\n\n    fn request_save_system_icon_pack(&self) {\n        SAVE_SYSTEM_ICON_PACK.call(());\n    }\n\n    /// Ensures default icons exist in the system icon pack directory\n    fn sanitize_default_icons(sys_icons_path: &Path) -> Result<()> {\n        std::fs::create_dir_all(sys_icons_path)?;\n\n        let ensure_icon = |filename: &str| {\n            let icon_path = sys_icons_path.join(filename);\n            if !icon_path.exists() {\n                std::fs::copy(\n                    SEELEN_COMMON\n                        .app_resource_dir()\n                        .join(\"static/icons\")\n                        .join(filename),\n                    icon_path,\n                )?;\n            }\n            Result::Ok(())\n        };\n\n        ensure_icon(\"missing.png\")?;\n        ensure_icon(\"music_thumbnail.jpg\")?;\n        ensure_icon(\"url.png\")?;\n        ensure_icon(\"start-menu.svg\")?;\n        ensure_icon(\"folder.svg\")?;\n        ensure_icon(\"desktop.svg\")?;\n\n        ensure_icon(\"trash_bin_empty.png\")?;\n        ensure_icon(\"trash_bin_full.png\")?;\n        ensure_icon(\"trash_bin_mask.png\")?;\n        Ok(())\n    }\n\n    /// Ensures default icon entries exist in the icon pack\n    fn sanitize_default_entries(system_pack: &mut IconPack) {\n        // Ensure missing icon is set\n        system_pack.missing = Some(Icon {\n            base: Some(\"missing.png\".to_owned()),\n            ..Default::default()\n        });\n\n        // add_entry will override if exists, or create if not\n        system_pack.add_entry(IconPackEntry::Shared(SharedIconPackEntry {\n            extension: \"url\".to_string(),\n            icon: Icon {\n                base: Some(\"url.png\".to_owned()),\n                ..Default::default()\n            },\n        }));\n\n        system_pack.add_entry(IconPackEntry::Custom(CustomIconPackEntry {\n            key: \"@seelen/weg::start-menu\".to_owned(),\n            icon: Icon {\n                base: Some(\"start-menu.svg\".to_owned()),\n                mask: Some(\"start-menu.svg\".to_owned()),\n                ..Default::default()\n            },\n        }));\n\n        system_pack.add_entry(IconPackEntry::Custom(CustomIconPackEntry {\n            key: \"@seelen/weg::folder\".to_owned(),\n            icon: Icon {\n                base: Some(\"folder.svg\".to_owned()),\n                mask: Some(\"folder.svg\".to_owned()),\n                ..Default::default()\n            },\n        }));\n\n        system_pack.add_entry(IconPackEntry::Custom(CustomIconPackEntry {\n            key: \"@seelen/weg::show-desktop\".to_owned(),\n            icon: Icon {\n                base: Some(\"desktop.svg\".to_owned()),\n                ..Default::default()\n            },\n        }));\n\n        system_pack.add_entry(IconPackEntry::Custom(CustomIconPackEntry {\n            key: \"defaultPlayerThumbnail\".to_owned(),\n            icon: Icon {\n                base: Some(\"music_thumbnail.jpg\".to_owned()),\n                ..Default::default()\n            },\n        }));\n\n        system_pack.add_entry(IconPackEntry::Custom(CustomIconPackEntry {\n            key: \"bin::empty\".to_owned(),\n            icon: Icon {\n                base: Some(\"trash_bin_empty.png\".to_owned()),\n                mask: Some(\"trash_bin_mask.png\".to_owned()),\n                ..Default::default()\n            },\n        }));\n\n        system_pack.add_entry(IconPackEntry::Custom(CustomIconPackEntry {\n            key: \"bin::full\".to_owned(),\n            icon: Icon {\n                base: Some(\"trash_bin_full.png\".to_owned()),\n                mask: Some(\"trash_bin_mask.png\".to_owned()),\n                ..Default::default()\n            },\n        }));\n    }\n\n    pub fn ensure_system_icon_pack(&self) -> Result<()> {\n        let sys_icons_path = SEELEN_COMMON.system_icon_pack_path();\n\n        let mut guard = self.system_icon_pack.lock();\n        // Create new pack if it doesn't exist\n        if guard.is_none() {\n            let mut system_pack = IconPack {\n                id: \"@system/icon-pack\".into(),\n                ..Default::default()\n            };\n            system_pack.metadata.display_name = ResourceText::En(\"System\".to_string());\n            system_pack.metadata.description =\n                ResourceText::En(\"Icons from Windows and Program Files\".to_string());\n            system_pack.metadata.internal.path = sys_icons_path.to_path_buf();\n\n            *guard = Some(system_pack);\n        }\n\n        // Always sanitize default icon entries and files\n        let system_pack = guard.as_mut().expect(\"System icon pack should exist\");\n        Self::sanitize_default_entries(system_pack);\n        Self::sanitize_default_icons(sys_icons_path)?;\n\n        self.request_save_system_icon_pack();\n        Ok(())\n    }\n\n    pub fn add_system_app_icon(&self, umid: Option<&str>, path: Option<&Path>, icon: Icon) {\n        if umid.is_none() && path.is_none() {\n            return;\n        }\n\n        // Strip any `path,index` suffix before stat-ing the file so mtime is always valid.\n        let source_mtime = path.map(strip_icon_index).as_deref().and_then(last_edit_at);\n        self.with_system_pack(|system_pack| {\n            system_pack.add_entry(IconPackEntry::Unique(UniqueIconPackEntry {\n                umid: umid.map(|s| s.to_string()),\n                path: path.map(|p| p.to_path_buf()),\n                redirect: None,\n                icon: Some(icon),\n                source_mtime,\n            }));\n        });\n        self.request_save_system_icon_pack();\n        self.emit_icon_packs().log_error();\n    }\n\n    pub fn add_system_icon_redirect(&self, umid: Option<String>, origin: &Path, redirect: &Path) {\n        let source_mtime = last_edit_at(origin);\n        self.with_system_pack(|system_pack| {\n            system_pack.add_entry(IconPackEntry::Unique(UniqueIconPackEntry {\n                umid,\n                path: Some(origin.to_path_buf()),\n                redirect: Some(redirect.to_path_buf()),\n                icon: None,\n                source_mtime,\n            }));\n        });\n        self.request_save_system_icon_pack();\n        self.emit_icon_packs().log_error();\n    }\n\n    pub fn add_system_file_icon(&self, origin_extension: &str, icon: Icon) {\n        self.with_system_pack(|system_pack| {\n            system_pack.add_entry(IconPackEntry::Shared(SharedIconPackEntry {\n                extension: origin_extension.to_string(),\n                icon,\n            }));\n        });\n        self.request_save_system_icon_pack();\n        self.emit_icon_packs().log_error();\n    }\n\n    fn icon_exists(icon: &Icon) -> bool {\n        let root_path = SEELEN_COMMON.system_icon_pack_path();\n        icon.base\n            .as_ref()\n            .is_some_and(|sub| root_path.join(sub).exists())\n            || (icon\n                .light\n                .as_ref()\n                .is_some_and(|sub| root_path.join(sub).exists())\n                && icon\n                    .dark\n                    .as_ref()\n                    .is_some_and(|sub| root_path.join(sub).exists()))\n    }\n\n    /// Internal recursive function that checks for app icon without acquiring locks\n    fn _has_app_icon(system_pack: &IconPack, umid: Option<&str>, path: Option<&Path>) -> bool {\n        let lower_path = path.map(|p| p.to_string_lossy().to_lowercase());\n\n        for entry in &system_pack.entries {\n            let IconPackEntry::Unique(entry) = entry else {\n                continue;\n            };\n\n            let mut found = None;\n            if let (Some(entry_umid), Some(umid)) = (&entry.umid, umid) {\n                if entry_umid == umid {\n                    found = Some(entry);\n                }\n            }\n\n            if found.is_none()\n                && lower_path.is_some()\n                && entry\n                    .path\n                    .as_ref()\n                    .map(|p| p.to_string_lossy().to_lowercase())\n                    == lower_path\n            {\n                found = Some(entry);\n            }\n\n            if let Some(entry) = found {\n                // Check if the entry's own source file was modified since the icon was cached.\n                // We always compare against entry.path (not the caller-provided path) because an\n                // entry can be found by UMID even when its stored path differs from the provided\n                // path (e.g. the exe entry is found by UMID but the caller passes the lnk path).\n                //\n                // Use `continue` instead of `return false` so that a stale entry (e.g. a\n                // leftover from a previous app version whose path has since changed) does not\n                // short-circuit the search and prevent a later, valid entry for the same UMID\n                // from being found.\n                let entry_current_mtime = entry\n                    .path\n                    .as_deref()\n                    .map(strip_icon_index)\n                    .as_deref()\n                    .and_then(last_edit_at);\n                if let (Some(cached), Some(current)) = (entry.source_mtime, entry_current_mtime) {\n                    if cached != current {\n                        continue;\n                    }\n                }\n\n                if let Some(redirect) = &entry.redirect {\n                    return Self::_has_app_icon(system_pack, None, Some(redirect))\n                        || Self::_has_shared_file_icon(system_pack, redirect);\n                }\n\n                if let Some(icon) = &entry.icon {\n                    if Self::icon_exists(icon) {\n                        return true;\n                    }\n                }\n            };\n        }\n\n        false\n    }\n\n    fn _has_shared_file_icon(system_pack: &IconPack, path: &Path) -> bool {\n        let Some(ext) = path.extension() else {\n            return false;\n        };\n        let extension = ext.to_string_lossy().to_lowercase();\n        system_pack.entries.iter().any(|e| match e {\n            IconPackEntry::Shared(s) => {\n                s.extension.to_lowercase() == extension && Self::icon_exists(&s.icon)\n            }\n            _ => false,\n        })\n    }\n\n    /// Get icon pack by app user model id, filename or path\n    pub fn has_app_icon(&self, umid: Option<&str>, path: Option<&Path>) -> bool {\n        self.with_system_pack(|system_pack| Self::_has_app_icon(system_pack, umid, path))\n    }\n\n    pub fn has_shared_file_icon(&self, path: &Path) -> bool {\n        self.with_system_pack(|system_pack| Self::_has_shared_file_icon(system_pack, path))\n    }\n}\n\nfn last_edit_at(path: &Path) -> Option<DateTime<Utc>> {\n    let meta = std::fs::metadata(path).ok()?;\n    let date = meta.modified().ok()?;\n    Some(date.into())\n}\n\n/// Strips the Windows `path,index` icon notation suffix if present, returning the real file path.\n/// For example `\"C:\\foo\\bar.ico,0\"` → `PathBuf(\"C:\\foo\\bar.ico\")`.\n/// Paths without a `,<integer>` suffix are returned as-is.\nfn strip_icon_index(path: &Path) -> std::borrow::Cow<'_, Path> {\n    if let Some(s) = path.to_str() {\n        if let Some(comma) = s.rfind(',') {\n            if s[comma + 1..].trim().parse::<i32>().is_ok() {\n                return std::borrow::Cow::Owned(PathBuf::from(&s[..comma]));\n            }\n        }\n    }\n    std::borrow::Cow::Borrowed(path)\n}\n"
  },
  {
    "path": "src/background/state/application/apps_config.rs",
    "content": "use windows::Win32::Foundation::HWND;\n\nuse crate::{\n    error::Result, state::domain::AppConfig, utils::constants::SEELEN_COMMON,\n    windows_api::window::Window,\n};\n\nuse super::FullState;\n\nimpl FullState {\n    pub fn get_app_config_by_window(&self, hwnd: HWND) -> Result<Option<&AppConfig>> {\n        let window = Window::from(hwnd);\n\n        let path = window.process().program_path()?;\n\n        let exe = path.file_name().ok_or(\"Invalid path\")?.to_string_lossy();\n        let path = path.to_string_lossy();\n        let title = window.title();\n        let class = window.class();\n\n        if let Some(app) = self.settings.by_app.search(&title, &class, &exe, &path) {\n            return Ok(Some(app));\n        }\n\n        Ok(self.settings_by_app.search(&title, &class, &exe, &path))\n    }\n\n    fn _load_bundled_settings_by_app(&mut self) -> Result<()> {\n        let apps_templates_path = SEELEN_COMMON.bundled_app_configs_path();\n\n        self.settings_by_app.clear();\n\n        for entry in apps_templates_path.read_dir()?.flatten() {\n            let content = std::fs::read_to_string(entry.path())?;\n            let mut apps: Vec<AppConfig> = serde_yaml::from_str(&content)?;\n            for app in apps.iter_mut() {\n                app.is_bundled = true;\n            }\n            self.settings_by_app.extend(apps);\n        }\n\n        self.settings_by_app.prepare();\n        Ok(())\n    }\n\n    pub(super) fn load_bundled_settings_by_app(&mut self) {\n        if let Err(e) = self._load_bundled_settings_by_app() {\n            log::error!(\"Error loading settings by app: {e}\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/state/application/icons.rs",
    "content": "use std::path::{Path, PathBuf};\n\nuse seelen_core::{\n    resource::SluResource,\n    state::{Icon, IconPack, IconPackEntry},\n};\n\nuse crate::{error::Result, utils::date_based_hex_id};\n\npub async fn download_remote_icons(pack: &mut IconPack) -> Result<()> {\n    if pack.remote_entries.is_empty() || pack.downloaded {\n        return Ok(());\n    }\n\n    let folder_to_store = pack.metadata.directory()?;\n    let mut entries = Vec::new();\n\n    for entry in &pack.remote_entries {\n        let mut new_entry = entry.clone();\n\n        match &mut new_entry {\n            IconPackEntry::Unique(entry) => {\n                if let Some(icon) = &mut entry.icon {\n                    *icon = download_entry_icons(icon, &folder_to_store).await?;\n                }\n            }\n            IconPackEntry::Shared(entry) => {\n                entry.icon = download_entry_icons(&entry.icon, &folder_to_store).await?;\n            }\n            IconPackEntry::Custom(entry) => {\n                entry.icon = download_entry_icons(&entry.icon, &folder_to_store).await?;\n            }\n        }\n\n        entries.push(new_entry);\n    }\n\n    pack.entries = entries;\n    pack.downloaded = true;\n    pack.save()?;\n    Ok(())\n}\n\n// download remote icon url and save it on the parent path + random hash.\nasync fn download_entry_icons(icon: &Icon, folder_to_store: &Path) -> Result<Icon> {\n    let mut resolved = icon.clone();\n\n    let download_filename = async |url: &str| -> Result<String> {\n        Ok(download_remote_icon_and_validate_it(url, folder_to_store)\n            .await?\n            .file_name()\n            .ok_or(\"Could not get file name\")?\n            .to_string_lossy()\n            .to_string())\n    };\n\n    if let Some(url) = &icon.base {\n        resolved.base = download_filename(url).await.ok();\n    }\n\n    if let Some(url) = &icon.light {\n        resolved.light = download_filename(url).await.ok();\n    }\n\n    if let Some(url) = &icon.dark {\n        resolved.dark = download_filename(url).await.ok()\n    }\n\n    if let Some(url) = &icon.mask {\n        resolved.mask = download_filename(url).await.ok();\n    }\n\n    Ok(resolved)\n}\n\n/// returns a path to the downloaded icon\nasync fn download_remote_icon_and_validate_it(\n    url: &str,\n    folder_to_store: &Path,\n) -> Result<PathBuf> {\n    if !folder_to_store.is_dir() {\n        return Err(\"Folder to store is not a directory\".into());\n    }\n\n    let res = reqwest::get(url).await?;\n    let bytes = res.bytes().await?;\n\n    let format = image::guess_format(&bytes)?;\n    let icon = image::load_from_memory_with_format(&bytes, format)?;\n    let extension = format\n        .extensions_str()\n        .first()\n        .ok_or(\"Could not get extension\")?;\n\n    let icon_path = folder_to_store.join(format!(\"{}.{}\", date_based_hex_id(), extension));\n    icon.save(&icon_path)?;\n    Ok(icon_path)\n}\n"
  },
  {
    "path": "src/background/state/application/mod.rs",
    "content": "mod apps_config;\nmod icons;\npub mod performance;\nmod settings;\nmod toolbar_items;\nmod weg_items;\n\npub use icons::download_remote_icons;\n\nuse arc_swap::ArcSwap;\nuse lazy_static::lazy_static;\nuse notify_debouncer_full::{\n    new_debouncer,\n    notify::{ReadDirectoryChangesWatcher, RecursiveMode, Watcher},\n    DebounceEventResult, DebouncedEvent, Debouncer, FileIdMap,\n};\nuse seelen_core::{\n    resource::ResourceKind,\n    state::{AppsConfigurationList, CssStyles, SluPopupConfig, SluPopupContent, WegItems},\n};\nuse std::{\n    collections::HashSet,\n    path::{Path, PathBuf},\n    sync::Arc,\n    time::Duration,\n};\n\nuse crate::{\n    error::Result, log_error, resources::RESOURCES, utils::constants::SEELEN_COMMON,\n    widgets::popups::POPUPS_MANAGER,\n};\n\nuse super::domain::{Settings, ToolbarState};\n\nlazy_static! {\n    pub static ref FULL_STATE: Arc<ArcSwap<FullState>> = Arc::new(ArcSwap::from_pointee({\n        log::trace!(\"Creating new State Manager\");\n        FullState::new().expect(\"Failed to create State Manager\")\n    }));\n}\n\n#[derive(Debug, Clone)]\npub struct FullState {\n    watcher: Arc<Option<Debouncer<ReadDirectoryChangesWatcher, FileIdMap>>>,\n    // ======== data ========\n    pub settings: Settings,\n    pub settings_by_app: AppsConfigurationList,\n    pub weg_items: WegItems,\n    pub toolbar_items: ToolbarState,\n}\n\nunsafe impl Sync for FullState {}\n\nimpl FullState {\n    fn new() -> Result<Self> {\n        let mut manager = Self {\n            watcher: Arc::new(None),\n            // ======== data ========\n            settings: Settings::default(),\n            settings_by_app: AppsConfigurationList::default(),\n            weg_items: Self::initial_weg_items(),\n            toolbar_items: Self::initial_toolbar_items(),\n        };\n        manager.load_all();\n        manager.start_listeners()?;\n        Ok(manager)\n    }\n\n    /// Shorthand of `FullState::clone` on Arc reference\n    ///\n    /// Intended to be used with `ArcSwap::rcu` to mofify the state\n    pub fn cloned(&self) -> Self {\n        self.clone()\n    }\n\n    fn join_and_filter_debounced_changes(events: Vec<DebouncedEvent>) -> HashSet<PathBuf> {\n        let mut result = HashSet::new();\n        for event in events {\n            for path in event.event.paths {\n                if !path.is_dir() {\n                    result.insert(path);\n                }\n            }\n        }\n        result\n    }\n\n    fn process_changes(&mut self, changed: &HashSet<PathBuf>) -> Result<()> {\n        let mut widgets_changed = false;\n        let mut icons_changed = false;\n        let mut themes_changed = false;\n        let mut plugins_changed = false;\n        let mut wallpapers_changed = false;\n\n        let mut settings_changed = false;\n\n        // Single iteration over the changed paths\n        for path in changed {\n            if !icons_changed && path.starts_with(SEELEN_COMMON.user_icons_path()) {\n                icons_changed = true;\n            };\n\n            if !themes_changed\n                && (path.starts_with(SEELEN_COMMON.user_themes_path())\n                    || path.starts_with(SEELEN_COMMON.bundled_themes_path()))\n            {\n                themes_changed = true;\n            }\n\n            if !plugins_changed\n                && (path.starts_with(SEELEN_COMMON.user_plugins_path())\n                    || path.starts_with(SEELEN_COMMON.bundled_plugins_path()))\n            {\n                plugins_changed = true;\n            }\n\n            if !widgets_changed\n                && (path.starts_with(SEELEN_COMMON.user_widgets_path())\n                    || path.starts_with(SEELEN_COMMON.bundled_widgets_path()))\n            {\n                widgets_changed = true;\n            }\n\n            if !settings_changed && path == SEELEN_COMMON.settings_path() {\n                settings_changed = true;\n            }\n\n            if !wallpapers_changed && path.starts_with(SEELEN_COMMON.user_wallpapers_path()) {\n                wallpapers_changed = true;\n            }\n        }\n\n        if themes_changed {\n            log::info!(\"Theme changed\");\n            RESOURCES.load_all_of_type(ResourceKind::Theme)?;\n            RESOURCES.emit_themes()?;\n        }\n\n        if icons_changed {\n            log::info!(\"Icon Packs changed\");\n            RESOURCES.load_all_of_type(ResourceKind::IconPack)?;\n            RESOURCES.emit_icon_packs()?;\n        }\n\n        if plugins_changed {\n            log::info!(\"Plugins changed\");\n            RESOURCES.load_all_of_type(ResourceKind::Plugin)?;\n            RESOURCES.emit_plugins()?;\n        }\n\n        if widgets_changed {\n            log::info!(\"Widgets changed\");\n            RESOURCES.load_all_of_type(ResourceKind::Widget)?;\n            RESOURCES.emit_widgets()?;\n        }\n\n        if wallpapers_changed {\n            log::info!(\"Wallpapers changed\");\n            RESOURCES.load_all_of_type(ResourceKind::Wallpaper)?;\n            RESOURCES.emit_wallpapers()?;\n\n            if self.sanitize_wallpaper_collections() {\n                self.emit_settings()?;\n            }\n        }\n\n        // important: settings changed should be the last one to avoid use unexisting state\n        // like new recently added theme, plugin, widget, etc\n        if settings_changed {\n            log::info!(\"Seelen Settings changed\");\n            self.read_settings();\n            self.emit_settings()?;\n        }\n\n        Ok(())\n    }\n\n    fn start_listeners(&mut self) -> Result<()> {\n        log::trace!(\"Starting Seelen UI Files Watcher\");\n        let mut debouncer = new_debouncer(\n            Duration::from_millis(100),\n            None,\n            |result: DebounceEventResult| match result {\n                Ok(events) => {\n                    // log::info!(\"Seelen UI File Watcher events: {:?}\", events);\n                    let changed = Self::join_and_filter_debounced_changes(events);\n                    FULL_STATE.rcu(move |state| {\n                        let mut state = state.cloned();\n                        log_error!(state.process_changes(&changed));\n                        state\n                    });\n                }\n                Err(errors) => errors\n                    .iter()\n                    .for_each(|e| log::error!(\"File Watcher Error: {e:?}\")),\n            },\n        )?;\n\n        let paths: Vec<&Path> = vec![\n            SEELEN_COMMON.settings_path(),\n            SEELEN_COMMON.user_icons_path(),\n            SEELEN_COMMON.user_themes_path(),\n            SEELEN_COMMON.user_plugins_path(),\n            SEELEN_COMMON.user_widgets_path(),\n            SEELEN_COMMON.user_wallpapers_path(),\n        ];\n\n        for path in paths {\n            debouncer.watcher().watch(path, RecursiveMode::Recursive)?;\n        }\n\n        self.watcher = Arc::new(Some(debouncer));\n        Ok(())\n    }\n\n    /// We log each step on this cuz for some reason a deadlock is happening somewhere.\n    fn load_all(&mut self) {\n        log::trace!(\"Initial load: settings\");\n        self.read_settings();\n\n        log::trace!(\"Initial load: bundled settings by app\");\n        self.load_bundled_settings_by_app();\n\n        log::trace!(\"Initial load: weg items\");\n        self.read_weg_items();\n\n        log::trace!(\"Initial load: toolbar items\");\n        self.read_toolbar_items();\n    }\n\n    fn show_corrupted_state_to_user(path: &Path) {\n        let path = path.to_path_buf();\n        std::thread::spawn(move || {\n            let mut manager = POPUPS_MANAGER.lock();\n            let config = SluPopupConfig {\n                title: vec![SluPopupContent::Group {\n                    items: vec![\n                        SluPopupContent::Icon {\n                            name: \"BiSolidError\".to_string(),\n                            styles: Some(\n                                CssStyles::new()\n                                    .add(\"color\", \"var(--color-red-800)\")\n                                    .add(\"height\", \"1.2rem\"),\n                            ),\n                        },\n                        SluPopupContent::Text {\n                            value: t!(\"runtime.corrupted_data\").to_string(),\n                            styles: None,\n                        },\n                    ],\n                    styles: Some(CssStyles::new().add(\"alignItems\", \"center\")),\n                }],\n                content: vec![\n                    SluPopupContent::Text {\n                        value: t!(\"runtime.corrupted_file\").to_string(),\n                        styles: None,\n                    },\n                    SluPopupContent::Text {\n                        value: format!(\"{}: {:?}\", t!(\"runtime.corrupted_file_path\"), path),\n                        styles: None,\n                    },\n                ],\n                ..Default::default()\n            };\n            log_error!(manager.create(config));\n        });\n    }\n}\n"
  },
  {
    "path": "src/background/state/application/performance.rs",
    "content": "use std::sync::LazyLock;\n\nuse arc_swap::ArcSwap;\nuse seelen_core::{handlers::SeelenEvent, state::PerformanceMode, system_state::PowerMode};\nuse tauri::Listener;\n\nuse crate::{\n    app::{emit_to_webviews, get_app_handle},\n    hook::HookManager,\n    modules::power::infrastructure::{get_batteries, get_power_mode, get_power_status},\n    state::application::FULL_STATE,\n    windows_api::window::{event::WinEvent, Window},\n};\n\npub static PERFORMANCE_MODE: LazyLock<ArcSwap<PerformanceMode>> = LazyLock::new(|| {\n    start_listeners();\n    let perf_mode = get_perf_mode();\n    log::info!(\"Performance mode: {perf_mode:?}\");\n    ArcSwap::from_pointee(perf_mode)\n});\n\nfn start_listeners() {\n    let handle = get_app_handle();\n    handle.listen(SeelenEvent::PowerMode, |_| check_for_changes());\n    handle.listen(SeelenEvent::PowerStatus, |_| check_for_changes());\n    handle.listen(SeelenEvent::StateSettingsChanged, |_| check_for_changes());\n\n    HookManager::subscribe(|(event, _origin)| {\n        if matches!(\n            event,\n            WinEvent::SystemForeground\n                | WinEvent::SyntheticFullscreenStart\n                | WinEvent::SyntheticFullscreenEnd\n        ) {\n            check_for_changes();\n        }\n    });\n}\n\nfn get_perf_mode() -> PerformanceMode {\n    let foreground = Window::get_foregrounded();\n    if foreground.is_fullscreen() && !foreground.is_seelen_overlay() {\n        return PerformanceMode::Extreme;\n    }\n\n    let guard = FULL_STATE.load();\n    let config = &guard.settings.performance_mode;\n\n    let power_mode = get_power_mode();\n    if power_mode == PowerMode::BatterySaver {\n        return config.on_energy_saver;\n    }\n\n    let power_status = get_power_status();\n    let batteries = get_batteries();\n    if !batteries.is_empty() && power_status.ac_line_status != 1 {\n        return config.on_battery;\n    }\n\n    config.default\n}\n\nfn check_for_changes() {\n    let stored = PERFORMANCE_MODE.load_full();\n    let current = get_perf_mode();\n    if current != *stored {\n        log::trace!(\"Seelen UI performance mode changed to {current:?}\");\n        PERFORMANCE_MODE.store(std::sync::Arc::new(current));\n        emit_to_webviews(SeelenEvent::StatePerformanceModeChanged, current);\n    }\n}\n"
  },
  {
    "path": "src/background/state/application/settings.rs",
    "content": "use seelen_core::{handlers::SeelenEvent, state::Settings};\n\nuse crate::{\n    app::{emit_to_webviews, SEELEN},\n    error::Result,\n    resources::RESOURCES,\n    trace_lock,\n    utils::constants::SEELEN_COMMON,\n};\n\nuse super::FullState;\n\nimpl FullState {\n    pub(super) fn emit_settings(&self) -> Result<()> {\n        emit_to_webviews(SeelenEvent::StateSettingsChanged, &self.settings);\n        trace_lock!(SEELEN).on_settings_change(self)?;\n        Ok(())\n    }\n\n    fn _read_settings(&mut self) -> Result<()> {\n        let path = SEELEN_COMMON.settings_path();\n        if path.exists() {\n            self.settings = Settings::load(path)?;\n            self.migration_v2_5_0()?;\n            self.sanitize_wallpaper_collections();\n        } else {\n            self.write_settings()?; // create initial settings file\n        }\n        Ok(())\n    }\n\n    /// Resources id changed for remote/downloaded resources.\n    fn migration_v2_5_0(&mut self) -> Result<()> {\n        RESOURCES.themes.scan(|new_id, theme| {\n            let Some(remote) = &theme.metadata.internal.remote else {\n                return;\n            };\n\n            let old_id = remote.friendly_id.clone().into();\n            if let Some(config) = self.settings.by_theme.remove(&old_id) {\n                self.settings.by_theme.insert(new_id.clone(), config);\n            };\n\n            for id in &mut self.settings.active_themes {\n                if id == &old_id {\n                    *id = new_id.clone();\n                }\n            }\n        });\n\n        RESOURCES.wallpapers.scan(|new_id, wallpaper| {\n            let Some(remote) = &wallpaper.metadata.internal.remote else {\n                return;\n            };\n\n            let old_id = remote.friendly_id.clone().into();\n            if let Some(config) = self.settings.by_wallpaper.remove(&old_id) {\n                self.settings.by_wallpaper.insert(new_id.clone(), config);\n            };\n\n            for collection in &mut self.settings.wallpaper_collections {\n                for id in &mut collection.wallpapers {\n                    if id == &old_id {\n                        *id = new_id.clone();\n                    }\n                }\n            }\n        });\n\n        RESOURCES.widgets.scan(|k, v| {\n            let Some(remote) = &v.metadata.internal.remote else {\n                return;\n            };\n\n            let old_id = remote.friendly_id.clone().into();\n            if let Some(config) = self.settings.by_widget.others.remove(&old_id) {\n                self.settings.by_widget.others.insert(k.clone(), config);\n            };\n\n            self.settings.monitors_v3.values_mut().for_each(|monitor| {\n                if let Some(config) = monitor.by_widget.remove(&old_id) {\n                    monitor.by_widget.insert(k.clone(), config);\n                };\n            })\n        });\n\n        Ok(())\n    }\n\n    /// Sanitize wallpaper collections to remove non-existent wallpaper IDs\n    pub(super) fn sanitize_wallpaper_collections(&mut self) -> bool {\n        let mut changed = false;\n        for collection in &mut self.settings.wallpaper_collections {\n            let original_len = collection.wallpapers.len();\n            collection\n                .wallpapers\n                .retain(|wallpaper_id| RESOURCES.wallpapers.contains(wallpaper_id));\n            if collection.wallpapers.len() != original_len {\n                changed = true;\n            }\n        }\n        changed\n    }\n\n    pub(super) fn read_settings(&mut self) {\n        if let Err(err) = self._read_settings() {\n            log::error!(\"Failed to read settings: {err}\");\n            Self::show_corrupted_state_to_user(SEELEN_COMMON.settings_path());\n        }\n    }\n\n    pub fn write_settings(&self) -> Result<()> {\n        self.settings.save(SEELEN_COMMON.settings_path())?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/state/application/toolbar_items.rs",
    "content": "use std::collections::HashSet;\n\nuse seelen_core::{\n    resource::WidgetId,\n    state::{ToolbarItem, ToolbarItem2, ToolbarJsScope, ToolbarState},\n};\n\nuse crate::{\n    error::Result,\n    utils::{atomic_write_file, constants::SEELEN_COMMON},\n};\n\nuse super::FullState;\n\nimpl FullState {\n    pub fn initial_toolbar_items() -> ToolbarState {\n        ToolbarState {\n            left: vec![\n                ToolbarItem2::Plugin(\"@seelen/tb-user-menu\".into()),\n                ToolbarItem2::Inline(Box::new(ToolbarItem {\n                    template: \"return \\\"|\\\"\".into(),\n                    ..Default::default()\n                })),\n                ToolbarItem2::Plugin(\"@default/focused-app\".into()),\n                ToolbarItem2::Inline(Box::new(ToolbarItem {\n                    scopes: HashSet::from([ToolbarJsScope::FocusedApp]),\n                    template: \"return focusedApp.title ? \\\"-\\\" : \\\"\\\"\".into(),\n                    ..Default::default()\n                })),\n                ToolbarItem2::Plugin(\"@default/focused-app-title\".into()),\n            ],\n            center: vec![ToolbarItem2::Plugin(\"@seelen/tb-calendar-popup\".into())],\n            right: vec![\n                ToolbarItem2::Plugin(\"@seelen/tb-system-tray\".into()),\n                ToolbarItem2::Plugin(\"@seelen/tb-keyboard-selector\".into()),\n                ToolbarItem2::Plugin(\"@seelen/tb-bluetooth-popup\".into()),\n                ToolbarItem2::Plugin(\"@seelen/tb-network-popup\".into()),\n                ToolbarItem2::Plugin(\"@seelen/tb-media-popup\".into()),\n                ToolbarItem2::Plugin(\"@default/power\".into()),\n                ToolbarItem2::Plugin(\"@seelen/tb-notifications\".into()),\n                ToolbarItem2::Plugin(\"@seelen/tb-quick-settings\".into()),\n            ],\n            ..Default::default()\n        }\n    }\n\n    fn _read_toolbar_items(&mut self) -> Result<()> {\n        let path = SEELEN_COMMON\n            .widget_data_dir(&WidgetId::known_toolbar())\n            .join(\"state.yml\");\n\n        if path.exists() {\n            self.toolbar_items = serde_yaml::from_str(&std::fs::read_to_string(path)?)?;\n            self.toolbar_items.sanitize();\n        } else {\n            self.toolbar_items = Self::initial_toolbar_items();\n            self.toolbar_items.sanitize();\n            self.write_toolbar_items(&self.toolbar_items)?;\n        }\n        Ok(())\n    }\n\n    pub(super) fn read_toolbar_items(&mut self) {\n        if let Err(err) = self._read_toolbar_items() {\n            log::error!(\"Failed to read toolbar items: {err}\");\n            Self::show_corrupted_state_to_user(\n                &SEELEN_COMMON.widget_data_dir(&WidgetId::known_toolbar()),\n            );\n        }\n    }\n\n    pub fn write_toolbar_items(&self, items: &ToolbarState) -> Result<()> {\n        let path = SEELEN_COMMON\n            .widget_data_dir(&WidgetId::known_toolbar())\n            .join(\"state.yml\");\n        atomic_write_file(&path, serde_yaml::to_string(items)?.as_bytes())\n    }\n}\n"
  },
  {
    "path": "src/background/state/application/weg_items.rs",
    "content": "use seelen_core::{\n    resource::WidgetId,\n    state::{WegItem, WegItemData, WegItems},\n};\n\nuse crate::{\n    error::Result,\n    modules::apps::application::msix::MsixAppsManager,\n    utils::{atomic_write_file, constants::SEELEN_COMMON},\n};\n\nuse super::FullState;\n\nimpl FullState {\n    pub fn initial_weg_items() -> WegItems {\n        WegItems {\n            is_reorder_disabled: false,\n            left: vec![\n                WegItem::StartMenu {\n                    id: uuid::Uuid::new_v4(),\n                },\n                WegItem::ShowDesktop {\n                    id: uuid::Uuid::new_v4(),\n                },\n            ],\n            center: vec![WegItem::AppOrFile(WegItemData {\n                id: uuid::Uuid::new_v4(),\n                umid: None,\n                path: \"C:\\\\Windows\\\\explorer.exe\".into(),\n                display_name: t!(\"file_explorer\").to_string(),\n                pinned: true,\n                prevent_pinning: false,\n                relaunch: None,\n            })],\n            right: vec![\n                WegItem::TrashBin {\n                    id: uuid::Uuid::new_v4(),\n                },\n                WegItem::Media {\n                    id: uuid::Uuid::new_v4(),\n                },\n            ],\n        }\n    }\n\n    fn update_weg_items_paths(items: &mut [WegItem]) {\n        for item in items {\n            if let WegItem::AppOrFile(data) = item {\n                if let Some(umid) = &data.umid {\n                    if let Ok(Some(app_path)) = MsixAppsManager::instance().get_app_path(umid) {\n                        data.path = app_path;\n                    }\n                }\n            }\n        }\n    }\n\n    fn _read_weg_items(&mut self) -> Result<()> {\n        let path = SEELEN_COMMON\n            .widget_data_dir(&WidgetId::known_weg())\n            .join(\"state.yml\");\n\n        if path.exists() {\n            self.weg_items = serde_yaml::from_str(&std::fs::read_to_string(path)?)?;\n            self.weg_items.sanitize();\n            Self::update_weg_items_paths(&mut self.weg_items.left);\n            Self::update_weg_items_paths(&mut self.weg_items.center);\n            Self::update_weg_items_paths(&mut self.weg_items.right);\n        } else {\n            self.weg_items = Self::initial_weg_items();\n            self.weg_items.sanitize();\n            self.write_weg_items(&self.weg_items)?;\n        }\n        Ok(())\n    }\n\n    pub(super) fn read_weg_items(&mut self) {\n        if let Err(err) = self._read_weg_items() {\n            log::error!(\"Failed to read weg items: {err}\");\n            Self::show_corrupted_state_to_user(\n                &SEELEN_COMMON.widget_data_dir(&WidgetId::known_weg()),\n            );\n        }\n    }\n\n    pub fn write_weg_items(&self, items: &WegItems) -> Result<()> {\n        let path = SEELEN_COMMON\n            .widget_data_dir(&WidgetId::known_weg())\n            .join(\"state.yml\");\n        atomic_write_file(&path, serde_yaml::to_string(items)?.as_bytes())\n    }\n}\n"
  },
  {
    "path": "src/background/state/domain/mod.rs",
    "content": "pub use seelen_core::state::*;\n"
  },
  {
    "path": "src/background/state/infrastructure.rs",
    "content": "use std::path::PathBuf;\n\nuse itertools::Itertools;\nuse seelen_core::state::{\n    by_monitor::MonitorConfiguration, by_wallpaper::WallpaperInstanceSettings, IconPackEntry,\n    PerformanceMode, Wallpaper, WegItems,\n};\nuse tauri_plugin_dialog::DialogExt;\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    log_error,\n    state::application::performance::PERFORMANCE_MODE,\n    utils::{constants::SEELEN_COMMON, date_based_hex_id},\n    windows_api::WindowsApi,\n};\n\nuse super::{\n    application::FULL_STATE,\n    domain::{AppConfig, Settings, ToolbarState},\n};\n\n#[tauri::command(async)]\npub fn state_get_toolbar_items() -> ToolbarState {\n    FULL_STATE.load().toolbar_items.clone()\n}\n\n#[tauri::command(async)]\npub fn state_write_toolbar_items(mut items: ToolbarState) -> Result<()> {\n    items.sanitize();\n    FULL_STATE.rcu(|state| {\n        let mut state = state.cloned();\n        state.toolbar_items = items.clone();\n        state\n    });\n    FULL_STATE.load().write_toolbar_items(&items)\n}\n\n#[tauri::command(async)]\npub fn state_get_weg_items() -> WegItems {\n    FULL_STATE.load().weg_items.clone()\n}\n\n#[tauri::command(async)]\npub fn state_write_weg_items(mut items: WegItems) -> Result<()> {\n    items.sanitize();\n    FULL_STATE.rcu(|state| {\n        let mut state = state.cloned();\n        state.weg_items = items.clone();\n        state\n    });\n    FULL_STATE.load().write_weg_items(&items)\n}\n\n#[tauri::command(async)]\npub fn state_get_settings(path: Option<PathBuf>) -> Result<Settings> {\n    if let Some(path) = path {\n        Ok(Settings::load(path)?)\n    } else {\n        Ok(FULL_STATE.load().settings.clone())\n    }\n}\n\n#[tauri::command(async)]\npub fn state_get_default_settings() -> Result<Settings> {\n    let mut settings = Settings::default();\n    settings.sanitize()?;\n    Ok(settings)\n}\n\n#[tauri::command(async)]\npub fn state_get_default_monitor_settings() -> MonitorConfiguration {\n    MonitorConfiguration::default()\n}\n\n#[tauri::command(async)]\npub fn state_get_default_wallpaper_settings() -> WallpaperInstanceSettings {\n    WallpaperInstanceSettings::default()\n}\n\n#[tauri::command(async)]\npub fn state_write_settings(mut settings: Settings) -> Result<()> {\n    settings.sanitize()?;\n    FULL_STATE.rcu(move |state| {\n        let mut state = state.cloned();\n        state.settings = settings.clone();\n        state\n    });\n    FULL_STATE.load().write_settings()\n}\n\n#[tauri::command(async)]\npub fn state_get_settings_by_app() -> Vec<AppConfig> {\n    FULL_STATE\n        .load()\n        .settings_by_app\n        .iter()\n        .cloned()\n        .collect_vec()\n}\n\n#[tauri::command(async)]\npub fn get_native_shell_wallpaper() -> Result<PathBuf> {\n    WindowsApi::get_wallpaper()\n}\n\n#[tauri::command(async)]\npub fn set_native_shell_wallpaper(path: String) -> Result<()> {\n    WindowsApi::set_wallpaper(path)\n}\n\n#[tauri::command(async)]\npub fn state_request_wallpaper_addition() -> Result<()> {\n    get_app_handle()\n        .dialog()\n        .file()\n        .set_title(\"Pick Wallpapers\")\n        .add_filter(\"video\", &Wallpaper::SUPPORTED_VIDEOS)\n        .add_filter(\"image\", &Wallpaper::SUPPORTED_IMAGES)\n        .pick_files(|picked| {\n            for path in picked.unwrap_or_default() {\n                if let Ok(path) = path.simplified().into_path() {\n                    let folder_to_store = SEELEN_COMMON\n                        .user_wallpapers_path()\n                        .join(date_based_hex_id());\n                    log_error!(Wallpaper::create_from_file(&path, &folder_to_store, true));\n                }\n            }\n        });\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn state_add_icon_to_custom_icon_pack(_icon: IconPackEntry) -> Result<()> {\n    todo!()\n}\n\n#[tauri::command(async)]\npub fn state_get_performance_mode() -> PerformanceMode {\n    **PERFORMANCE_MODE.load()\n}\n"
  },
  {
    "path": "src/background/state/mod.rs",
    "content": "pub mod application;\npub mod domain;\npub mod infrastructure;\n\nuse application::FullState;\nuse seelen_core::{\n    resource::{PluginId, WidgetId},\n    state::{\n        value::{KnownPlugin, PluginValue},\n        WindowManagerLayout, WorkspaceId,\n    },\n    system_state::MonitorId,\n};\nuse uuid::Uuid;\n\nuse crate::resources::RESOURCES;\n\nimpl FullState {\n    pub fn is_widget_enabled(&self, widget_id: &WidgetId) -> bool {\n        self.settings.is_widget_enabled(widget_id)\n    }\n\n    pub fn is_widget_enable_on_monitor(\n        &self,\n        widget_id: &WidgetId,\n        monitor_id: &MonitorId,\n    ) -> bool {\n        self.settings\n            .is_widget_enabled_on_monitor(widget_id, monitor_id)\n    }\n\n    pub fn is_weg_enabled(&self) -> bool {\n        self.is_widget_enabled(&WidgetId::known_weg())\n    }\n\n    #[allow(dead_code)]\n    pub fn is_bar_enabled(&self) -> bool {\n        self.is_widget_enabled(&WidgetId::known_toolbar())\n    }\n\n    pub fn is_window_manager_enabled(&self) -> bool {\n        self.is_widget_enabled(&WidgetId::known_wm())\n    }\n\n    pub fn is_weg_enabled_on_monitor(&self, monitor_id: &MonitorId) -> bool {\n        self.is_widget_enable_on_monitor(&WidgetId::known_weg(), monitor_id)\n    }\n\n    pub fn is_bar_enabled_on_monitor(&self, monitor_id: &MonitorId) -> bool {\n        self.is_widget_enable_on_monitor(&WidgetId::known_toolbar(), monitor_id)\n    }\n\n    pub fn is_window_manager_enabled_on_monitor(&self, monitor_id: &MonitorId) -> bool {\n        self.is_widget_enable_on_monitor(&WidgetId::known_wm(), monitor_id)\n    }\n\n    pub fn get_widget_instances_ids(&self, widget_id: &WidgetId) -> Vec<Uuid> {\n        let config = self.settings.by_widget.others.get(widget_id);\n        match config {\n            Some(config) => config\n                .instances\n                .as_ref()\n                .map_or_else(Default::default, |i| i.keys().cloned().collect()),\n            None => Vec::new(),\n        }\n    }\n\n    pub fn is_wall_enabled(&self) -> bool {\n        self.is_widget_enabled(&WidgetId::known_wall())\n    }\n\n    pub fn get_wm_layout(&self, workspace_id: &WorkspaceId) -> WindowManagerLayout {\n        let base = WindowManagerLayout::default();\n\n        let layout_id = self.get_wm_layout_id(workspace_id);\n\n        let mut plugin_with_layout = None;\n        RESOURCES.plugins.any(|_, p| {\n            if p.id == layout_id {\n                plugin_with_layout = Some(p.clone());\n                true\n            } else {\n                false\n            }\n        });\n\n        let Some(plugin) = plugin_with_layout else {\n            return base;\n        };\n\n        let PluginValue::Known(plugin) = &plugin.plugin else {\n            return base;\n        };\n\n        let KnownPlugin::WManager(layout) = &**plugin else {\n            return base;\n        };\n\n        layout.clone()\n    }\n\n    pub fn get_wm_layout_id(&self, _workspace_id: &WorkspaceId) -> PluginId {\n        let mut default = self.settings.by_widget.wm.default_layout.clone();\n        if !default.is_valid() {\n            default = \"@default/wm-bspwm\".into();\n        }\n\n        /* let Ok(id) = monitor.stable_id() else {\n            return default;\n        }; */\n\n        /* let config = match self.settings.monitors_v3.get(&id) {\n            Some(config) => config,\n            None => return default,\n        };\n\n        let workspace = match config.workspaces_v2.get(workspace_idx) {\n            Some(workspace) => workspace,\n            None => return default,\n        };\n\n        match &workspace.layout {\n            Some(layout_id) => {\n                let layout_id: PluginId = layout_id.as_str().into();\n                if layout_id.is_valid() {\n                    layout_id\n                } else {\n                    default\n                }\n            }\n            None => default,\n        } */\n\n        default\n    }\n\n    pub fn locale(&self) -> &String {\n        // always should be filled\n        self.settings.language.as_ref().unwrap()\n    }\n}\n"
  },
  {
    "path": "src/background/tauri_context.rs",
    "content": "/// this macro slowness the build/check on dev so we split this\n/// to this separated file to avoid slowness when editting main.rs\npub fn get_context() -> tauri::Context {\n    tauri::generate_context!()\n}\n"
  },
  {
    "path": "src/background/tauri_plugins.rs",
    "content": "use tauri::{Builder, Wry};\n\npub fn register_plugins(app_builder: Builder<Wry>) -> Builder<Wry> {\n    app_builder\n        .plugin(tauri_plugin_fs::init())\n        .plugin(tauri_plugin_shell::init())\n        .plugin(tauri_plugin_dialog::init())\n        .plugin(tauri_plugin_process::init())\n        .plugin(tauri_plugin_updater::Builder::new().build())\n        .plugin(tauri_plugin_deep_link::init())\n        .plugin(tauri_plugin_http::init())\n}\n"
  },
  {
    "path": "src/background/telemetry.rs",
    "content": "use std::time::Duration;\n\nuse serde::Serialize;\nuse uuid::Uuid;\nuse winreg::{\n    enums::{HKEY_CURRENT_USER, KEY_ALL_ACCESS, KEY_READ},\n    RegKey,\n};\n\nconst REGISTRY_SUBKEY: &str = \"Software\\\\Seelen UI\\\\Analytics\";\nconst INSTALL_ID_VALUE: &str = \"InstallId\";\n\n#[cfg(dev)]\nconst TELEMETRY_ENDPOINT: &str = \"https://telemetry.local.seelen.io/events\";\n#[cfg(not(dev))]\nconst TELEMETRY_ENDPOINT: &str = \"https://telemetry.seelen.io/events\";\n\n/// Mirrors `TelemetryEventContext` from the sl-telemetry server.\n/// Uses `tag = \"eventName\"` and `rename_all = \"camelCase\"` to match the server's serde config.\n#[derive(Debug, Serialize)]\n#[serde(rename_all_fields = \"camelCase\", tag = \"eventName\")]\npub enum TelemetryEvent {\n    SeelenUIUsage {\n        install_id: uuid::Uuid,\n        os_version: String,\n        app_version: String,\n    },\n}\n\n/// Returns the persisted install ID from the registry, creating it if absent.\n///\n/// Stored at `HKCU\\Software\\Seelen\\Analytics` → `InstallId` (REG_SZ).\n/// This key is NOT declared in the MSIX package manifest, so it survives\n/// across reinstalls and package updates.\nfn get_or_create_install_id() -> crate::error::Result<Uuid> {\n    let hkcu = RegKey::predef(HKEY_CURRENT_USER);\n\n    if let Ok(key) = hkcu.open_subkey_with_flags(REGISTRY_SUBKEY, KEY_READ) {\n        if let Ok(id_str) = key.get_value::<String, _>(INSTALL_ID_VALUE) {\n            if let Ok(id) = Uuid::parse_str(&id_str) {\n                return Ok(id);\n            }\n        }\n    }\n\n    let new_id = Uuid::new_v4();\n    let (key, _) = hkcu.create_subkey_with_flags(REGISTRY_SUBKEY, KEY_ALL_ACCESS)?;\n    key.set_value(INSTALL_ID_VALUE, &new_id.to_string())?;\n    log::debug!(\"Telemetry: generated new install_id {new_id}\");\n    Ok(new_id)\n}\n\nfn os_version_string() -> String {\n    let info = os_info::get();\n    format!(\"{} {}\", info.os_type(), info.version())\n}\n\nasync fn send_event(install_id: Uuid) {\n    let event = TelemetryEvent::SeelenUIUsage {\n        install_id,\n        os_version: os_version_string(),\n        app_version: env!(\"CARGO_PKG_VERSION\").to_string(),\n    };\n\n    let body = match serde_json::to_string(&event) {\n        Ok(b) => b,\n        Err(err) => {\n            log::debug!(\"Telemetry: failed to serialize event: {err:?}\");\n            return;\n        }\n    };\n\n    let client = reqwest::Client::new();\n    match client\n        .post(TELEMETRY_ENDPOINT)\n        .header(\"Content-Type\", \"application/json\")\n        .body(body)\n        .timeout(Duration::from_secs(10))\n        .send()\n        .await\n    {\n        Ok(resp) if resp.status().is_success() => {\n            log::debug!(\"Telemetry: event sent (status {})\", resp.status());\n        }\n        Ok(resp) => {\n            let status = resp.status();\n            let body = resp.text().await.unwrap_or_default();\n            log::debug!(\"Telemetry: server responded with {status}: {body}\");\n        }\n        Err(err) => {\n            log::debug!(\"Telemetry: request failed: {err:?}\");\n        }\n    }\n}\n\n/// Spawns a background task that sends a `SeelenUIUsage` telemetry event\n/// immediately on startup and then every 10 minutes.\n///\n/// Errors obtaining the install ID are logged but do not affect the rest of the app.\npub fn start_telemetry() {\n    let install_id = match get_or_create_install_id() {\n        Ok(id) => id,\n        Err(err) => {\n            log::warn!(\"Telemetry: could not obtain install_id, skipping: {err:?}\");\n            return;\n        }\n    };\n\n    tokio::spawn(async move {\n        let mut interval = tokio::time::interval(Duration::from_secs(10 * 60));\n        loop {\n            interval.tick().await; // first tick fires immediately\n            send_event(install_id).await;\n        }\n    });\n}\n"
  },
  {
    "path": "src/background/utils/constants.rs",
    "content": "use std::{\n    path::{Path, PathBuf},\n    sync::{Arc, LazyLock},\n};\n\nuse seelen_core::resource::{ResourceId, WidgetId};\nuse tauri::Manager;\nuse windows::Win32::UI::Shell::FOLDERID_Windows;\n\nuse crate::{app::get_app_handle, windows_api::WindowsApi};\n\npub static SEELEN_COMMON: LazyLock<Arc<SeelenCommon>> =\n    LazyLock::new(|| Arc::new(SeelenCommon::new()));\n\npub struct SeelenCommon {\n    // general\n    resource_dir: PathBuf,\n    data_dir: PathBuf,\n    cache_dir: PathBuf,\n    temp_dir: PathBuf,\n    // specifits\n    settings: PathBuf,\n    icons: PathBuf,\n    system_icon_pack: PathBuf,\n    user_themes: PathBuf,\n    bundled_themes: PathBuf,\n    user_plugins: PathBuf,\n    bundled_plugins: PathBuf,\n    bundled_app_configs: PathBuf,\n    wallpapers: PathBuf,\n    widgets: PathBuf,\n    bundled_widgets: PathBuf,\n    sounds: PathBuf,\n    // system\n    system_dir: PathBuf,\n}\n\n#[allow(dead_code)]\nimpl SeelenCommon {\n    pub fn new() -> Self {\n        let resolver = get_app_handle().path();\n\n        let resource_dir = resolver.resource_dir().expect(\"Failed to get resource dir\");\n        let data_dir = resolver.app_data_dir().expect(\"Failed to get app data dir\");\n        let cache_dir = resolver.app_cache_dir().expect(\"Failed to get cache dir\");\n        let temp_dir = resolver\n            .temp_dir()\n            .expect(\"Failed to get temp dir\")\n            .join(\"com.seelen.seelen-ui\");\n\n        let system_dir =\n            WindowsApi::known_folder(FOLDERID_Windows).expect(\"Failed to get system dir\");\n\n        Self {\n            settings: data_dir.join(\"settings.json\"),\n            icons: data_dir.join(\"iconpacks\"),\n            system_icon_pack: cache_dir.join(\"gen-icon-pack\"),\n            sounds: data_dir.join(\"soundpacks\"),\n            user_themes: data_dir.join(\"themes\"),\n            bundled_themes: resource_dir.join(\"static/themes\"),\n            user_plugins: data_dir.join(\"plugins\"),\n            bundled_plugins: resource_dir.join(\"static/plugins\"),\n            bundled_app_configs: resource_dir.join(\"static/apps_templates\"),\n            widgets: data_dir.join(\"widgets\"),\n            bundled_widgets: resource_dir.join(\"static/widgets\"),\n            wallpapers: data_dir.join(\"wallpapers\"),\n            // general\n            data_dir,\n            resource_dir,\n            cache_dir,\n            temp_dir,\n            system_dir,\n        }\n    }\n\n    pub fn app_resource_dir(&self) -> &Path {\n        &self.resource_dir\n    }\n\n    pub fn app_data_dir(&self) -> &Path {\n        &self.data_dir\n    }\n\n    pub fn app_cache_dir(&self) -> &Path {\n        &self.cache_dir\n    }\n\n    pub fn app_temp_dir(&self) -> &Path {\n        &self.temp_dir\n    }\n\n    /// Windows: `X:\\Windows`\n    pub fn system_dir(&self) -> &Path {\n        &self.system_dir\n    }\n\n    pub fn settings_path(&self) -> &Path {\n        &self.settings\n    }\n\n    pub fn system_icon_pack_path(&self) -> &Path {\n        &self.system_icon_pack\n    }\n\n    pub fn user_icons_path(&self) -> &Path {\n        &self.icons\n    }\n\n    pub fn user_sounds_path(&self) -> &Path {\n        &self.sounds\n    }\n\n    pub fn user_themes_path(&self) -> &Path {\n        &self.user_themes\n    }\n\n    pub fn bundled_themes_path(&self) -> &Path {\n        &self.bundled_themes\n    }\n\n    pub fn user_plugins_path(&self) -> &Path {\n        &self.user_plugins\n    }\n\n    pub fn bundled_plugins_path(&self) -> &Path {\n        &self.bundled_plugins\n    }\n\n    pub fn bundled_app_configs_path(&self) -> &Path {\n        &self.bundled_app_configs\n    }\n\n    pub fn user_widgets_path(&self) -> &Path {\n        &self.widgets\n    }\n\n    pub fn bundled_widgets_path(&self) -> &Path {\n        &self.bundled_widgets\n    }\n\n    pub fn user_wallpapers_path(&self) -> &Path {\n        &self.wallpapers\n    }\n\n    pub fn widget_data_dir(&self, id: &WidgetId) -> PathBuf {\n        let data_dir = SEELEN_COMMON.app_data_dir().join(\"data\");\n        let folder = match &**id {\n            ResourceId::Local(id) => id.trim_start_matches(\"@\").replace(\"/\", \"-\"),\n            ResourceId::Remote(uuid) => uuid.to_string(),\n        };\n        data_dir.join(folder)\n    }\n}\n"
  },
  {
    "path": "src/background/utils/discord.rs",
    "content": "use std::sync::{\n    atomic::{AtomicBool, Ordering},\n    LazyLock,\n};\n\nuse discord_rich_presence::{\n    activity::{Activity, Assets, Button, Party, Timestamps},\n    DiscordIpc, DiscordIpcClient,\n};\n\nuse crate::{\n    error::Result, is_local_dev, state::application::FULL_STATE, utils::now_timestamp_as_millis,\n    windows_api::event_window::IS_INTERACTIVE_SESSION,\n};\n\nuse super::spawn_named_thread;\n\nstatic DISCORD_IPC_CONNECTED: AtomicBool = AtomicBool::new(false);\nstatic START_TIME: LazyLock<i64> = LazyLock::new(|| now_timestamp_as_millis() as i64);\n\nstatic DETAILS: [&str; 22] = [\n    \"Personalizing my desktop\",\n    \"Tweaking every pixel!\",\n    \"Crafting the ultimate workspace\",\n    \"Transforming my PC into art\",\n    \"Running the smoothest UI ever\",\n    \"Designing a futuristic interface\",\n    \"Building a digital paradise\",\n    \"Redefining desktop aesthetics\",\n    \"Experimenting with UI alchemy\",\n    \"Engineering the perfect theme\",\n    \"Unleashing desktop superpowers\",\n    \"Mixing colors, shapes, and textures\",\n    \"Pushing customization limits\",\n    \"Developing visual harmony\",\n    \"Architecting the perfect layout\",\n    \"Breathing life into pixels\",\n    \"Curating icon perfection\",\n    \"Revolutionizing my desktop experience\",\n    \"Creating a digital masterpiece\",\n    \"Yeap, this is my desktop\",\n    \"Art is my passion\",\n    \"Omg I love this\",\n];\n\nstatic STATUSES: [&str; 20] = [\n    \"Brilliant\",\n    \"Smart\",\n    \"Powerful\",\n    \"Creative\",\n    \"Futuristic\",\n    \"Pro Mode\",\n    \"SLU\",\n    \"Glowy\",\n    \"Ravens\",\n    \"Unicorns\",\n    \"Seelen\",\n    \"Next-Gen\",\n    \"Ethereal\",\n    \"Cyberpunk\",\n    \"Minimalist\",\n    \"Productive\",\n    \"Magical\",\n    \"Pixels\",\n    \"Neon\",\n    \"+Aura\",\n];\n\npub fn start_discord_rpc() -> Result<()> {\n    if !FULL_STATE.load().settings.drpc {\n        return Ok(());\n    }\n\n    spawn_named_thread(\"Discord IPC\", || {\n        let retry_conection_time = if is_local_dev() {\n            std::time::Duration::from_secs(5)\n        } else {\n            std::time::Duration::from_secs(5 * 60) // 5 minutes\n        };\n\n        loop {\n            log::trace!(\"Trying to connect to Discord IPC\");\n            let mut client = DiscordIpcClient::new(\"1384275226652704928\").unwrap(); // never fails\n\n            if client.connect().is_err() {\n                log::trace!(\n                    \"Discord RPC not connected, retrying in {} seconds\",\n                    retry_conection_time.as_secs()\n                );\n                std::thread::sleep(retry_conection_time);\n                continue;\n            }\n\n            log::info!(\"Discord IPC successfully connected\");\n            DISCORD_IPC_CONNECTED.store(true, Ordering::SeqCst);\n\n            while DISCORD_IPC_CONNECTED.load(Ordering::SeqCst) {\n                // Pause when session is not interactive to reduce CPU usage\n                if !IS_INTERACTIVE_SESSION.load(Ordering::Acquire) {\n                    std::thread::sleep(std::time::Duration::from_secs(60));\n                    continue;\n                }\n\n                match client.set_activity(get_activity()) {\n                    Ok(_) => {\n                        log::trace!(\"Discord RPC activity updated\");\n                        // refresh every 10 minutes\n                        std::thread::sleep(std::time::Duration::from_secs(600));\n                    }\n                    Err(_err) => {\n                        DISCORD_IPC_CONNECTED.store(false, Ordering::SeqCst);\n                        let _ = client.close();\n                    }\n                }\n            }\n\n            log::info!(\"Discord IPC disconnected\");\n            std::thread::sleep(retry_conection_time);\n        }\n    });\n    Ok(())\n}\n\npub fn get_activity() -> Activity<'static> {\n    let random_detail = DETAILS[rand::random_range(0..DETAILS.len())];\n    let random_status = STATUSES[rand::random_range(0..STATUSES.len())];\n\n    Activity::new()\n        .state(random_status)\n        .details(random_detail)\n        .assets(\n            Assets::new()\n                .large_image(\"app_logo\")\n                .large_text(\"Seelen UI\")\n                .small_image(\"seelen_corp_logo2\")\n                .small_text(\"Made by Seelen Corp.\"),\n        )\n        .timestamps(Timestamps::new().start(*START_TIME))\n        .party(Party::new().id(\"seelen-ui-party\").size([10, 10]))\n        .buttons(vec![\n            Button::new(\"🚀 Download Now!\", \"https://seelen.io/apps/seelen-ui\"),\n            Button::new(\n                \"🐦‍⬛ Seelen Network\",\n                \"https://discord.com/invite/seelen-network-751144791596597561\",\n            ),\n        ])\n}\n"
  },
  {
    "path": "src/background/utils/icon_extractor/mod.rs",
    "content": "pub mod queue;\n\n#[cfg(target_arch = \"x86_64\")]\nuse std::arch::x86_64::{\n    __m128i, _mm_loadu_si128, _mm_setr_epi8, _mm_shuffle_epi8, _mm_storeu_si128,\n};\n\n#[cfg(target_arch = \"aarch64\")]\nuse std::arch::aarch64::{uint8x16_t, vld1q_u8, vqtbl1q_u8, vst1q_u8};\n\nuse std::io::BufRead;\nuse std::path::{Path, PathBuf};\n\nuse image::{GenericImageView, ImageBuffer, RgbaImage};\nuse queue::{IconExtractor, IconExtractorRequest};\nuse windows::Win32::Storage::FileSystem::FILE_FLAGS_AND_ATTRIBUTES;\nuse windows::Win32::UI::Controls::{IImageList, ILD_TRANSPARENT};\nuse windows::Win32::UI::Shell::{\n    SHGetFileInfoW, SHGetImageList, SHFILEINFOW, SHGFI_SYSICONINDEX, SHIL_JUMBO,\n};\nuse windows::Win32::{\n    Graphics::Gdi::{\n        CreateCompatibleDC, DeleteDC, GetDIBits, GetObjectW, SelectObject, BITMAP, BITMAPINFO,\n        BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, HBITMAP,\n    },\n    UI::{\n        Shell::ExtractIconExW,\n        WindowsAndMessaging::{GetIconInfoExW, HICON, ICONINFOEXW},\n    },\n};\n\nuse seelen_core::state::Icon;\nuse windows_core::Owned;\n\nuse crate::{\n    error::Result,\n    modules::{apps::application::msix::MsixAppsManager, start::application::StartMenuManager},\n    resources::RESOURCES,\n    utils::{constants::SEELEN_COMMON, date_based_hex_id},\n    windows_api::{string_utils::WindowsString, types::AppUserModelId, WindowsApi},\n};\n\n/// Convert BGRA to RGBA\n///\n/// Uses SIMD to go fast\n#[cfg(target_arch = \"x86_64\")]\npub fn bgra_to_rgba(data: &mut [u8]) {\n    // The shuffle mask for converting BGRA -> RGBA\n    let mask: __m128i = unsafe {\n        _mm_setr_epi8(\n            2, 1, 0, 3, // First pixel\n            6, 5, 4, 7, // Second pixel\n            10, 9, 8, 11, // Third pixel\n            14, 13, 12, 15, // Fourth pixel\n        )\n    };\n    // For each 16-byte chunk in your data\n    for chunk in data.chunks_exact_mut(16) {\n        let mut vector = unsafe { _mm_loadu_si128(chunk.as_ptr() as *const __m128i) };\n        vector = unsafe { _mm_shuffle_epi8(vector, mask) };\n        unsafe { _mm_storeu_si128(chunk.as_mut_ptr() as *mut __m128i, vector) };\n    }\n}\n\n// Uses NEON intrinsics to go fast\n#[cfg(target_arch = \"aarch64\")]\npub fn bgra_to_rgba(data: &mut [u8]) {\n    // The shuffle mask for converting BGRA -> RGBA\n    let maskplain: [u8; 16] = [\n        2, 1, 0, 3, // First pixel\n        6, 5, 4, 7, // Second pixel\n        10, 9, 8, 11, // Third pixel\n        14, 13, 12, 15, // Fourth pixel\n    ];\n    // The shuffle mask for the conversion in NEON intrinsics\n    let mask: uint8x16_t = unsafe { vld1q_u8(maskplain.as_ptr()) };\n    // For each 16-byte chunk in your data\n    for chunk in data.chunks_exact_mut(16) {\n        let mut vector: uint8x16_t = unsafe { vld1q_u8(chunk.as_ptr()) };\n        vector = unsafe { vqtbl1q_u8(vector, mask) };\n        unsafe { vst1q_u8(chunk.as_mut_ptr(), vector) };\n    }\n}\n\npub fn convert_hicon_to_rgba_image(hicon: &HICON) -> Result<RgbaImage> {\n    unsafe {\n        let mut icon_info = ICONINFOEXW {\n            cbSize: std::mem::size_of::<ICONINFOEXW>() as u32,\n            ..Default::default()\n        };\n\n        if !GetIconInfoExW(*hicon, &mut icon_info).as_bool() {\n            return Err(\"Failed to get icon info\".into());\n        }\n\n        let _hbm_mask = Owned::new(icon_info.hbmMask);\n        let _hbm_color = Owned::new(icon_info.hbmColor);\n        convert_hbitmap_to_rgba_image(icon_info.hbmColor)\n    }\n}\n\npub fn convert_hbitmap_to_rgba_image(hbitmap: HBITMAP) -> Result<RgbaImage> {\n    unsafe {\n        let mut bitmap = BITMAP::default();\n        if GetObjectW(\n            hbitmap.into(),\n            std::mem::size_of::<BITMAP>() as i32,\n            Some(&mut bitmap as *mut _ as *mut _),\n        ) == 0\n        {\n            return Err(\"Failed to get bitmap info\".into());\n        }\n\n        let width = bitmap.bmWidth;\n        let height = bitmap.bmHeight.abs();\n\n        let hdc_screen = CreateCompatibleDC(None);\n        let hdc_mem = CreateCompatibleDC(Some(hdc_screen));\n        let hbm_old = SelectObject(hdc_mem, hbitmap.into());\n\n        let mut bmp_info = BITMAPINFO {\n            bmiHeader: BITMAPINFOHEADER {\n                biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,\n                biWidth: width,\n                biHeight: -(height), // Negative for top-down bitmap\n                biPlanes: 1,\n                biBitCount: 32, // 4 bytes per pixel\n                biCompression: BI_RGB.0,\n                ..Default::default()\n            },\n            ..Default::default()\n        };\n\n        let mut buffer = vec![0u8; (width * height * 4) as usize];\n\n        let dibits_result = GetDIBits(\n            hdc_mem,\n            hbitmap,\n            0,\n            height as u32,\n            Some(buffer.as_mut_ptr() as *mut _),\n            &mut bmp_info,\n            DIB_RGB_COLORS,\n        );\n\n        // Cleanup GDI always, even on error\n        SelectObject(hdc_mem, hbm_old);\n        let _ = DeleteDC(hdc_mem);\n        let _ = DeleteDC(hdc_screen);\n\n        if dibits_result == 0 {\n            return Err(\"Failed to get dibits\".into());\n        }\n\n        // Alpha fix (important!)\n        fix_alpha_channel(buffer.as_mut_slice());\n        bgra_to_rgba(buffer.as_mut_slice());\n\n        let image = ImageBuffer::from_raw(width as u32, height as u32, buffer)\n            .ok_or(\"Failed to create image buffer\")?;\n        Ok(image)\n    }\n}\n\nfn fix_alpha_channel(buffer: &mut [u8]) {\n    let pixels = buffer.len() / 4;\n    let mut has_non_zero_alpha = false;\n    let mut has_varying_alpha = false;\n\n    // Verify the alpha channel\n    for i in 0..pixels {\n        let alpha = buffer[i * 4 + 3];\n        if alpha > 0 {\n            has_non_zero_alpha = true;\n        }\n        if alpha > 0 && alpha < 255 {\n            has_varying_alpha = true;\n            break;\n        }\n    }\n\n    // if all alpha values are 0, set them to 255\n    if !has_non_zero_alpha {\n        for i in 0..pixels {\n            buffer[i * 4 + 3] = 255;\n        }\n    }\n    // if there is premultiplied alpha\n    else if has_varying_alpha {\n        for i in 0..pixels {\n            let alpha = buffer[i * 4 + 3];\n            if alpha > 0 && alpha < 255 {\n                let alpha_f = alpha as f32 / 255.0;\n                let b = ((buffer[i * 4] as f32 / alpha_f).min(255.0)) as u8;\n                let g = ((buffer[i * 4 + 1] as f32 / alpha_f).min(255.0)) as u8;\n                let r = ((buffer[i * 4 + 2] as f32 / alpha_f).min(255.0)) as u8;\n                buffer[i * 4] = b;\n                buffer[i * 4 + 1] = g;\n                buffer[i * 4 + 2] = r;\n            }\n        }\n    }\n}\n\n/// this is the best solution having in consideration that a transparent image and have separated pixels\n/// with transparent gaps, so search side by side and crop them is the best approach.\npub fn crop_transparent_borders(rgba_image: &RgbaImage) -> RgbaImage {\n    let (width, height) = rgba_image.dimensions();\n    let mut top = None;\n    let mut bottom = None;\n    let mut left = None;\n    let mut right = None;\n\n    'outer: for y in 0..height {\n        for x in 0..width {\n            let pixel = rgba_image.get_pixel(x, y);\n            if pixel.0[3] != 0 {\n                top = Some(y);\n                break 'outer;\n            }\n        }\n    }\n\n    let top = match top {\n        Some(top) => top,\n        None => return RgbaImage::new(1, 1),\n    };\n\n    'outer: for y in (top..height).rev() {\n        for x in 0..width {\n            let pixel = rgba_image.get_pixel(x, y);\n            if pixel.0[3] != 0 {\n                bottom = Some(y);\n                break 'outer;\n            }\n        }\n    }\n\n    let bottom = match bottom {\n        Some(bottom) => bottom,\n        None => return RgbaImage::new(1, 1),\n    };\n\n    'outer: for x in 0..width {\n        for y in top..bottom {\n            let pixel = rgba_image.get_pixel(x, y);\n            if pixel.0[3] != 0 {\n                left = Some(x);\n                break 'outer;\n            }\n        }\n    }\n\n    let left = match left {\n        Some(left) => left,\n        None => return RgbaImage::new(1, 1),\n    };\n\n    'outer: for x in (left..width).rev() {\n        for y in top..bottom {\n            let pixel = rgba_image.get_pixel(x, y);\n            if pixel.0[3] != 0 {\n                right = Some(x);\n                break 'outer;\n            }\n        }\n    }\n\n    let right = match right {\n        Some(right) => right,\n        None => return RgbaImage::new(1, 1),\n    };\n\n    rgba_image\n        .view(left, top, right - left + 1, bottom - top + 1)\n        .to_image()\n}\n\npub fn extract_icon_from_module(path: &Path, index: i32) -> Result<RgbaImage> {\n    let path = WindowsString::from(path);\n    unsafe {\n        let mut hicon = HICON::default();\n        let extracted = ExtractIconExW(path.as_pcwstr(), index, Some(&mut hicon), None, 1);\n        if extracted == 0 || hicon.is_invalid() {\n            return Err(format!(\"Icon index {index} not found\").into());\n        }\n        let hicon = Owned::new(hicon);\n\n        let image = crop_transparent_borders(&convert_hicon_to_rgba_image(&hicon)?);\n        Ok(image)\n    }\n}\n\npub fn get_shell_icon(path: &Path) -> Result<RgbaImage> {\n    unsafe {\n        let normalized = path\n            .canonicalize()?\n            .to_string_lossy()\n            .trim_start_matches(r\"\\\\?\\\")\n            .to_owned();\n        let path = WindowsString::from(normalized);\n\n        let mut file_info = SHFILEINFOW::default();\n        let result = SHGetFileInfoW(\n            path.as_pcwstr(),\n            FILE_FLAGS_AND_ATTRIBUTES(0),\n            Some(&mut file_info),\n            std::mem::size_of::<SHFILEINFOW>() as u32,\n            SHGFI_SYSICONINDEX,\n        );\n\n        if result == 0 {\n            return Err(\"Failed to get file information\".into());\n        }\n\n        // file_info.iIcon = 0 is a valid icon but it is the default icon for files on Windows\n        // so we will handle this as no icon to avoid generate unnecessary artifacts\n        if file_info.iIcon == 0 {\n            return Err(\"Icon index is 0\".into());\n        }\n\n        let image_list: IImageList = SHGetImageList(SHIL_JUMBO as i32)?;\n        // if 256x256 icon is not available, will use the icons with the most color depth and size\n        // this is useful for some icons where color depth is less than 32,\n        // example: icon of 124x124 16bits and other 64x64 32bits this will return the 32bits icon\n        // color depth is prioritized over size\n        let icon = Owned::new(image_list.GetIcon(file_info.iIcon, ILD_TRANSPARENT.0)?);\n        let image = crop_transparent_borders(&convert_hicon_to_rgba_image(&icon)?);\n        Ok(image)\n    }\n}\n\nfn get_icon_from_url_file(path: &Path) -> Result<RgbaImage> {\n    let file = std::fs::File::open(path)?;\n    let reader = std::io::BufReader::new(file);\n\n    let mut icon_file = None;\n    let mut icon_idx: Option<i32> = None;\n\n    // in theory .url files are encoded in UTF-8 so we don't need to use OsString\n    for line in reader.lines() {\n        let line = line?;\n        if let Some(stripped) = line.strip_prefix(\"IconFile=\") {\n            icon_file = Some(PathBuf::from(stripped));\n        } else if let Some(stripped) = line.strip_prefix(\"IconIndex=\") {\n            icon_idx = Some(stripped.parse()?);\n        }\n    }\n\n    let icon_file = icon_file.ok_or(\"Url does not have IconFile\")?;\n    extract_icon_from_module(&icon_file, icon_idx.unwrap_or(0))\n}\n\n/// Parses Windows-style `path,index` icon notation (e.g. `C:\\foo\\bar.ico,0`).\n///\n/// Returns `(path, index)` only when the string ends with `,<integer>` **and** the path\n/// before the comma points to an existing file.\nfn parse_path_with_icon_index(raw: &Path) -> Option<(PathBuf, i32)> {\n    let s = raw.to_str()?;\n    let comma = s.rfind(',')?;\n    let index: i32 = s[comma + 1..].trim().parse().ok()?;\n    let path = PathBuf::from(s[..comma].trim());\n    if path.exists() && !path.is_dir() {\n        Some((path, index))\n    } else {\n        None\n    }\n}\n\n/// Extracts an icon from a file at a specific resource index and saves it.\n///\n/// `key` is the original `path,index` string (e.g. `C:\\foo\\bar.ico,0`) used as the lookup\n/// key in RESOURCES, so different indices of the same file are stored independently.\n/// `path` is the actual file path without the index suffix.\nfn _extract_and_save_icon_from_module_with_index(\n    key: &Path,\n    path: &Path,\n    index: i32,\n) -> Result<()> {\n    if RESOURCES.has_app_icon(None, Some(key)) {\n        return Ok(());\n    }\n\n    let filestem = path.file_stem().ok_or(\"Failed to get file stem\")?;\n    let gen_icon_filename = format!(\"{}_{}.png\", filestem.to_string_lossy(), date_based_hex_id());\n\n    log::trace!(\"Extracting icon (index {index}) for {:?}\", path.file_name());\n\n    let image = extract_icon_from_module(path, index)?;\n    let image = crop_transparent_borders(&image);\n\n    let gen_icon = Icon {\n        base: Some(gen_icon_filename.clone()),\n        is_aproximately_square: is_aproximately_a_square(&image),\n        ..Default::default()\n    };\n\n    image.save(\n        SEELEN_COMMON\n            .system_icon_pack_path()\n            .join(&gen_icon_filename),\n    )?;\n    RESOURCES.add_system_app_icon(None, Some(key), gen_icon);\n    Ok(())\n}\n\n/// returns the path of the icon extracted from the executable or copied if is an UWP app.\n///\n/// If the icon already exists, it returns the path instead overriding, this is needed for allow user custom icons.\n///\n/// umid on this case only applys to Property Store umid\nfn _extract_and_save_icon_from_file(origin: &Path) -> Result<()> {\n    if !origin.exists() || origin.is_dir() {\n        // Handle Windows path,index notation (e.g. \"C:\\path\\app.exe,2\" or \"file.ico,0\")\n        if let Some((real_path, index)) = parse_path_with_icon_index(origin) {\n            return _extract_and_save_icon_from_module_with_index(origin, &real_path, index);\n        }\n        return Err(format!(\"File not found: {}\", origin.display()).into());\n    }\n\n    let origin_ext = match origin.extension() {\n        Some(ext) => ext.to_string_lossy().to_lowercase(),\n        // no extension === no icon\n        None => return Ok(()),\n    };\n\n    // ico files are by itself an icon\n    if origin_ext == \"ico\" {\n        return Ok(());\n    }\n\n    let is_exe_file = origin_ext == \"exe\";\n    let is_lnk_file = origin_ext == \"lnk\";\n    let is_url_file = origin_ext == \"url\";\n\n    if is_exe_file || is_lnk_file || is_url_file {\n        if RESOURCES.has_app_icon(None, Some(origin)) {\n            return Ok(());\n        }\n    } else if RESOURCES.has_shared_file_icon(origin) {\n        return Ok(());\n    }\n\n    let file_name = origin.file_name().ok_or(\"Failed to get file name\")?;\n    let filestem = origin.file_stem().ok_or(\"Failed to get file stem\")?;\n\n    let gen_icon_filename = format!(\"{}_{}.png\", filestem.to_string_lossy(), date_based_hex_id());\n    let mut gen_icon = Icon {\n        base: Some(gen_icon_filename.clone()),\n        ..Default::default()\n    };\n\n    log::trace!(\"Extracting icon for {file_name:?}\");\n\n    if origin_ext == \"url\" {\n        let image = get_icon_from_url_file(origin)?;\n        gen_icon.is_aproximately_square = is_aproximately_a_square(&image);\n        image.save(\n            SEELEN_COMMON\n                .system_icon_pack_path()\n                .join(&gen_icon_filename),\n        )?;\n        RESOURCES.add_system_app_icon(None, Some(origin), gen_icon);\n        return Ok(());\n    }\n\n    let mut umid = None;\n    if is_lnk_file {\n        umid = WindowsApi::get_file_umid(origin).ok();\n        let has_custom_icon = WindowsApi::resolve_lnk_custom_icon_path(origin).is_ok();\n\n        if !has_custom_icon {\n            let (target, _) = WindowsApi::resolve_lnk_target(origin)?;\n            _extract_and_save_icon_from_file(&target)?;\n            RESOURCES.add_system_icon_redirect(umid, origin, &target);\n            return Ok(());\n        }\n    }\n\n    // try get the icon directly from the file\n    let icon = get_shell_icon(origin)?;\n    gen_icon.is_aproximately_square = is_aproximately_a_square(&icon);\n\n    if is_exe_file || is_lnk_file {\n        icon.save(\n            SEELEN_COMMON\n                .system_icon_pack_path()\n                .join(&gen_icon_filename),\n        )?;\n        RESOURCES.add_system_app_icon(umid.as_deref(), Some(origin), gen_icon);\n    } else {\n        let gen_icon_filename = format!(\"{}_{}.png\", origin_ext, date_based_hex_id());\n        icon.save(\n            SEELEN_COMMON\n                .system_icon_pack_path()\n                .join(&gen_icon_filename),\n        )?;\n        gen_icon.base = Some(gen_icon_filename);\n        RESOURCES.add_system_file_icon(&origin_ext, gen_icon);\n    }\n\n    Ok(())\n}\n\n/// returns the path of the icon extracted from the app with the specified package app user model id.\nfn _extract_and_save_icon_umid(aumid: &AppUserModelId) -> Result<()> {\n    match aumid {\n        AppUserModelId::Appx(app_umid) => {\n            let msix_manager = MsixAppsManager::instance();\n            let path = msix_manager.get_app_path(app_umid)?;\n            {\n                if RESOURCES.has_app_icon(Some(aumid.as_str()), path.as_deref()) {\n                    return Ok(());\n                }\n            }\n\n            log::trace!(\"Extracting icon for {app_umid:?}\");\n            let mut gen_icon = Icon::default();\n            let (light_path, dark_path) = msix_manager.get_app_icon_path(app_umid)?;\n\n            let name = date_based_hex_id();\n\n            let light_rgba = image::open(&light_path)?.to_rgba8();\n            let light_rgba = crop_transparent_borders(&light_rgba);\n\n            if light_path != dark_path {\n                let dark_rgba = image::open(&dark_path)?.to_rgba8();\n                let dark_rgba = crop_transparent_borders(&dark_rgba);\n\n                light_rgba.save(\n                    SEELEN_COMMON\n                        .system_icon_pack_path()\n                        .join(format!(\"{name}_light.png\")),\n                )?;\n                dark_rgba.save(\n                    SEELEN_COMMON\n                        .system_icon_pack_path()\n                        .join(format!(\"{name}_dark.png\")),\n                )?;\n\n                gen_icon.light = Some(format!(\"{name}_light.png\"));\n                gen_icon.dark = Some(format!(\"{name}_dark.png\"));\n            } else {\n                light_rgba.save(\n                    SEELEN_COMMON\n                        .system_icon_pack_path()\n                        .join(format!(\"{name}.png\")),\n                )?;\n                gen_icon.base = Some(format!(\"{name}.png\"));\n            }\n\n            gen_icon.is_aproximately_square = is_aproximately_a_square(&light_rgba);\n\n            RESOURCES.add_system_app_icon(Some(app_umid), path.as_deref(), gen_icon);\n            Ok(())\n        }\n        AppUserModelId::PropertyStore(app_umid) => {\n            let start = StartMenuManager::instance();\n            let lnk = start\n                .get_by_file_umid(app_umid)\n                .ok_or(format!(\"No shortcut found for umid {app_umid}\"))?;\n\n            {\n                if RESOURCES.has_app_icon(Some(aumid.as_str()), Some(&lnk.path)) {\n                    return Ok(());\n                }\n            }\n\n            _extract_and_save_icon_from_file(&lnk.path)?;\n            Ok(())\n        }\n    }\n}\n\npub fn request_icon_extraction_from_file<T: AsRef<Path>>(path: T) {\n    let path = path.as_ref();\n    if path.as_os_str().is_empty() {\n        return;\n    }\n    IconExtractor::instance().request(IconExtractorRequest::Path(path.to_path_buf()));\n}\n\npub fn request_icon_extraction_from_umid(aumid: &AppUserModelId) {\n    IconExtractor::instance().request(IconExtractorRequest::AppUMID(aumid.clone()));\n}\n\nconst SQUARE_MARGIN: f32 = 0.1;\nconst ASPECT_TOLERANCE: f32 = 0.05;\nconst OPACITY_THRESHOLD: u8 = 254;\n\npub fn is_aproximately_a_square(rgba_image: &RgbaImage) -> bool {\n    let (width, height) = rgba_image.dimensions();\n\n    // verify if the image is not empty\n    if width == 0 || height == 0 {\n        return false;\n    }\n\n    // verify if the image is a square\n    let aspect_ratio = width as f32 / height as f32;\n    if (aspect_ratio - 1.0).abs() > ASPECT_TOLERANCE {\n        return false;\n    }\n\n    // Calculate margin\n    let margin_x = (width as f32 * SQUARE_MARGIN) as u32;\n    let margin_y = (height as f32 * SQUARE_MARGIN) as u32;\n    let inner_width = width - 2 * margin_x;\n    let inner_height = height - 2 * margin_y;\n\n    // verify if the image is a square\n    for y in margin_y..margin_y + inner_height {\n        for x in margin_x..margin_x + inner_width {\n            let pixel = rgba_image.get_pixel(x, y);\n            if pixel.0[3] < OPACITY_THRESHOLD {\n                return false;\n            }\n        }\n    }\n\n    true\n}\n"
  },
  {
    "path": "src/background/utils/icon_extractor/queue.rs",
    "content": "use std::{\n    path::{Path, PathBuf},\n    sync::LazyLock,\n};\n\nuse serde::{Deserialize, Serialize};\nuse slu_utils::{debounce, Debounce};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager,\n    modules::start::application::{StartMenuEvent, StartMenuManager},\n    utils::{constants::SEELEN_COMMON, lock_free::SyncVec},\n    windows_api::types::AppUserModelId,\n};\n\nuse super::{_extract_and_save_icon_from_file, _extract_and_save_icon_umid};\n\n/// File extensions that are per-file (each has its own unique icon).\n/// Everything else is per-extension (all files of that type share the same shell icon).\nconst PER_FILE_EXTENSIONS: &[&str] = &[\"exe\", \"lnk\", \"url\"];\n\n/// Returns the real file path for Windows `path,index` icon notation (e.g. `\"file.ico,0\"` → `\"file.ico\"`).\n/// For normal paths the input is returned unchanged.\nfn real_path_for_ext(path: &Path) -> std::borrow::Cow<'_, std::path::Path> {\n    if let Some(s) = path.to_str() {\n        if let Some(comma) = s.rfind(',') {\n            if s[comma + 1..].trim().parse::<i32>().is_ok() {\n                return std::borrow::Cow::Owned(PathBuf::from(&s[..comma]));\n            }\n        }\n    }\n    std::borrow::Cow::Borrowed(path)\n}\n\npub struct IconExtractor {\n    failures: SyncVec<IconExtractorRequest>,\n    save_failures: Debounce<()>,\n}\n\n/// Represents a request to extract an icon.\n///\n/// - `AppUMID`: extract by application user model ID.\n/// - `Path`: extract from a specific file path (used for exe/lnk/url).\n/// - `Extension`: marks an entire file extension as failed (e.g. `\"js\"` covers all `*.js` files).\n///   This avoids storing hundreds of individual path failures for non-executable file types.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum IconExtractorRequest {\n    AppUMID(AppUserModelId),\n    Path(PathBuf),\n    /// Glob-like failure record: any file with this extension should be skipped.\n    Extension(String),\n}\n\nevent_manager!(IconExtractor, IconExtractorRequest);\n\nimpl IconExtractor {\n    fn create() -> Self {\n        let mut extractor = Self {\n            failures: SyncVec::new(),\n            save_failures: debounce(\n                |_| {\n                    let path = SEELEN_COMMON.app_cache_dir().join(\"icon_failures2.yml\");\n                    if let Ok(file) = std::fs::File::create(&path) {\n                        serde_yaml::to_writer(file, &Self::instance().failures.to_vec())\n                            .log_error();\n                    }\n                },\n                std::time::Duration::from_secs(2),\n            ),\n        };\n\n        extractor.init().log_error();\n\n        Self::subscribe(|request| {\n            let m = Self::instance();\n            if m.is_failed(&request) {\n                return;\n            }\n\n            if let Err(err) = Self::process(&request) {\n                log::error!(\"Failed to extract icon: {err}\");\n                m.record_failure(request);\n                m.save_failures.call(());\n            }\n        });\n\n        StartMenuManager::subscribe(|event| {\n            if matches!(event, StartMenuEvent::ItemsRefreshed) {\n                Self::instance().revalidate_property_store_failures();\n            }\n        });\n\n        extractor\n    }\n\n    pub fn init(&mut self) -> Result<()> {\n        let cached = SEELEN_COMMON.app_cache_dir().join(\"icon_failures2.yml\");\n        if cached.exists() {\n            let buff = std::fs::read(cached)?;\n            let failures = serde_yaml::from_slice::<Vec<IconExtractorRequest>>(&buff)?;\n            self.failures = failures.into();\n        }\n        Ok(())\n    }\n\n    pub fn instance() -> &'static IconExtractor {\n        static ICON_EXTRACTOR: LazyLock<IconExtractor> = LazyLock::new(IconExtractor::create);\n        &ICON_EXTRACTOR\n    }\n\n    pub fn request(&self, request: IconExtractorRequest) {\n        Self::send(request);\n    }\n\n    /// Returns true if this request is already known to fail and should be skipped.\n    fn is_failed(&self, request: &IconExtractorRequest) -> bool {\n        if self.failures.contains(request) {\n            return true;\n        }\n        // For generic file paths, also check if the whole extension has been marked as failed.\n        if let IconExtractorRequest::Path(path) = request {\n            let real = real_path_for_ext(path);\n            if let Some(ext) = real.extension() {\n                let ext_lower = ext.to_string_lossy().to_lowercase();\n                if !PER_FILE_EXTENSIONS.contains(&ext_lower.as_str()) {\n                    return self\n                        .failures\n                        .contains(&IconExtractorRequest::Extension(ext_lower));\n                }\n            }\n        }\n        false\n    }\n\n    /// Records a failure. For non-executable file types the whole extension is recorded\n    /// as a glob pattern (`Extension`), collapsing any previously stored individual paths\n    /// with that extension to keep the failure list compact.\n    fn record_failure(&self, request: IconExtractorRequest) {\n        if let IconExtractorRequest::Path(ref path) = request {\n            let real = real_path_for_ext(path);\n            if let Some(ext) = real.extension() {\n                let ext_lower = ext.to_string_lossy().to_lowercase();\n                if !PER_FILE_EXTENSIONS.contains(&ext_lower.as_str()) {\n                    // Remove any individual path entries already stored for this extension.\n                    self.failures.retain(|f| match f {\n                        IconExtractorRequest::Path(p) => {\n                            p.extension()\n                                .map(|e| e.to_string_lossy().to_lowercase())\n                                .as_deref()\n                                != Some(&ext_lower)\n                        }\n                        _ => true,\n                    });\n                    let ext_entry = IconExtractorRequest::Extension(ext_lower);\n                    if !self.failures.contains(&ext_entry) {\n                        self.failures.push(ext_entry);\n                    }\n                    return;\n                }\n            }\n        }\n        self.failures.push(request);\n    }\n\n    pub fn clear_failures(&self) {\n        self.failures.clear();\n        let path = SEELEN_COMMON.app_cache_dir().join(\"icon_failures2.yml\");\n        std::fs::remove_file(path).log_error();\n    }\n\n    /// Re-queues all `AppUMID(PropertyStore)` failures so they are retried after the\n    /// start menu has been refreshed. The entries are removed from the failures list\n    /// first so `is_failed` does not skip them immediately.\n    fn revalidate_property_store_failures(&self) {\n        let to_retry: Vec<IconExtractorRequest> = self\n            .failures\n            .to_vec()\n            .into_iter()\n            .filter(|r| matches!(r, IconExtractorRequest::AppUMID(id) if id.is_property_store()))\n            .collect();\n\n        if to_retry.is_empty() {\n            return;\n        }\n\n        log::debug!(\n            \"Revalidating {} PropertyStore UMID icon failure(s) after start menu refresh\",\n            to_retry.len()\n        );\n\n        self.failures.retain(|r| !to_retry.contains(r));\n        self.save_failures.call(());\n        for request in to_retry {\n            Self::send(request);\n        }\n    }\n\n    fn process(request: &IconExtractorRequest) -> Result<()> {\n        match request {\n            IconExtractorRequest::AppUMID(umid) => {\n                _extract_and_save_icon_umid(umid)?;\n            }\n            IconExtractorRequest::Path(path) => {\n                _extract_and_save_icon_from_file(path)?;\n            }\n            IconExtractorRequest::Extension(_) => {\n                // Extension entries are failure markers, not actionable requests.\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/utils/integrity/checksums.rs",
    "content": "use std::path::Path;\n\nuse slu_utils::checksums::CheckSums;\nuse walkdir::WalkDir;\n\nuse crate::{app::get_app_handle, error::Result};\n\nuse tauri::Manager;\nuse tauri_plugin_dialog::{DialogExt, MessageDialogKind};\n\n/// Public key for minisign verification (same as updater)\nconst MINISIGN_PUBLIC_KEY: &str = \"dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQ4QjU1RUI0NEM0NzBERUIKUldUckRVZE10RjYxU0lpaERvdklYL05DVlg0Sk9EVngvaEgzZjMvU1NNemJTZXZ1K0dNVXU3ZkQK\";\n\npub fn ensure_bundle_files_integrity(app: &tauri::AppHandle) -> Result<()> {\n    log::trace!(\"Validating bundle files integrity\");\n\n    let install_dir = app.path().resource_dir()?;\n    let static_path = install_dir.join(\"static\");\n\n    let checksums_path = install_dir.join(\"SHA256SUMS\");\n    let signature_path = install_dir.join(\"SHA256SUMS.sig\");\n\n    if !signature_path.exists() {\n        show_integrity_dialog();\n        return Err(\"Signature file not found\".into());\n    }\n\n    if !checksums_path.exists() {\n        show_integrity_dialog();\n        return Err(\"Checksums file not found\".into());\n    }\n\n    // Skip signature validation in development mode\n    if !tauri::is_dev() {\n        if let Err(err) =\n            verify_external_signature(&checksums_path, &signature_path, MINISIGN_PUBLIC_KEY)\n        {\n            show_integrity_dialog();\n            return Err(err);\n        }\n    }\n\n    if let Err(err) = validate_directory_checksums(&static_path, &checksums_path) {\n        show_integrity_dialog();\n        return Err(err);\n    }\n\n    Ok(())\n}\n\nfn validate_directory_checksums(base_path: &Path, checksums_path: &Path) -> Result<()> {\n    log::trace!(\"Validating checksums for {}\", base_path.display());\n\n    let checksums_content = std::fs::read(checksums_path)?;\n    let expected_checksums = CheckSums::parse(&checksums_content)?;\n\n    // Calculate actual checksums\n    let mut actual_checksums = CheckSums::new();\n    for entry in WalkDir::new(base_path)\n        .follow_links(false)\n        .into_iter()\n        .flatten()\n    {\n        let path = entry.path();\n        if !path.is_file() {\n            continue;\n        }\n\n        let relative_path = path\n            .strip_prefix(base_path.parent().unwrap())\n            .expect(\"Strip failed\");\n\n        // Read file using full path, but store checksum with relative path\n        let content =\n            std::fs::read(path).map_err(|e| format!(\"Failed to read {}: {}\", path.display(), e))?;\n        actual_checksums.raw_add(&content, relative_path);\n    }\n\n    let diffs = expected_checksums.compare(&actual_checksums);\n    if !diffs.is_empty() {\n        log::error!(\"Checksums mismatch: {:#?}\", diffs);\n        return Err(\"Checksums does not match\".into());\n    }\n\n    log::trace!(\"All Checksums validated successfully\");\n    Ok(())\n}\n\nfn verify_external_signature(file: &Path, signature_file: &Path, key_base64: &str) -> Result<()> {\n    let checksums_content = std::fs::read(file)?;\n    let signature_content = std::fs::read_to_string(signature_file)?;\n\n    slu_utils::signature::verify_minisign(&checksums_content, &signature_content, key_base64)?;\n    log::trace!(\"Signature verification successful for {}\", file.display());\n    Ok(())\n}\n\n/// Shows an error dialog for integrity validation failures\nfn show_integrity_dialog() {\n    get_app_handle()\n        .dialog()\n        .message(t!(\"runtime.files_integrity\"))\n        .title(t!(\"runtime.files_integrity_title\"))\n        .kind(MessageDialogKind::Error)\n        .blocking_show();\n}\n"
  },
  {
    "path": "src/background/utils/integrity/mod.rs",
    "content": "mod checksums;\nmod webview;\n\npub use checksums::*;\npub use webview::*;\n\nuse itertools::Itertools;\nuse tauri::webview_version;\nuse tauri_plugin_dialog::DialogExt;\nuse windows::Win32::{\n    Foundation::{GetLastError, ERROR_ALREADY_EXISTS},\n    System::Threading::CreateMutexW,\n};\n\nuse crate::{\n    error::Result,\n    is_local_dev,\n    utils::{has_fixed_runtime, is_running_as_appx},\n    windows_api::{string_utils::WindowsString, WindowsApi},\n};\n\nuse super::spawn_named_thread;\n\n/// Prints information about the computer runtime context to help debugging.\n#[rustfmt::skip]\npub fn print_initial_information() {\n    let version = env!(\"CARGO_PKG_VERSION\");\n    let debug = if tauri::is_dev() { \" (debug)\" } else { \"\" };\n    let local = if is_local_dev() { \" (local)\" } else { \"\" };\n\n    let os = os_info::get();\n    let sys_locale = seelen_core::state::Settings::get_locale();\n\n    log::info!(\n        \"───────────────────── Starting Seelen UI v{version}{local}{debug} ─────────────────────\"\n    );\n\n    log::info!(\"Arguments        : {:?}\", std::env::args().collect_vec());\n    log::info!(\"Working Directory: {:?}\", std::env::current_dir());\n\n    log::info!(\"Operating System : {}\", os.os_type());\n    log::info!(\"  version        : {}\", os.version());\n    log::info!(\"  edition        : {}\", os.edition().unwrap_or(\"None\"));\n    log::info!(\"  codename       : {}\", os.codename().unwrap_or(\"None\"));\n    log::info!(\"  bitness        : {}\", os.bitness());\n    log::info!(\"  architecture   : {}\", os.architecture().unwrap_or(\"Unknown\"));\n    log::info!(\"  locate         : {}\", sys_locale.unwrap_or(\"Unknown\".to_owned()));\n\n    /* let mut sys_info = sysinfo::System::new();\n    sys_info.refresh_cpu();\n    sys_info.refresh_memory();\n    log::info!(\"Specs\");\n    log::info!(\"  CPU Threads    : {}\", sys_info.cpus().len());\n    log::info!(\"  Memory         : {}GB\", sys_info.total_memory() / 1024 / 1024 / 1024); */\n\n    log::info!(\"WebView2 Runtime : {:?}\", webview_version());\n    log::info!(\"  Fixed          : {:?}\", has_fixed_runtime());\n\n    log::info!(\"Build Profile    : {}\", if cfg!(debug_assertions) { \"debug\" } else { \"release\" });\n    log::info!(\"  Bundled with   : {}\", if is_running_as_appx() { \"APPX/MSIX\" } else { \"NSIS\" });\n}\n\npub fn restart_as_appx() -> Result<!> {\n    WindowsApi::execute(\n        r\"shell:AppsFolder\\Seelen.SeelenUI_p6yyn03m1894e!App\".to_string(),\n        None,\n        None,\n        false,\n    )?;\n    std::process::exit(0);\n}\n\nfn is_uac_enabled() -> bool {\n    use winreg::{enums::HKEY_LOCAL_MACHINE, RegKey};\n    let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);\n    let Ok(key) = hklm.open_subkey(r\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\")\n    else {\n        return true; // assume enabled if we can't read\n    };\n    let enable_lua: u32 = key.get_value(\"EnableLUA\").unwrap_or(1);\n    enable_lua != 0\n}\n\npub fn warn_if_elevated(app: &tauri::AppHandle) {\n    if is_uac_enabled() && WindowsApi::is_elevated().unwrap_or(false) {\n        app.dialog()\n            .message(t!(\"elevated.description\"))\n            .title(t!(\"elevated.title\"))\n            .kind(tauri_plugin_dialog::MessageDialogKind::Warning)\n            .show(|_| {});\n    }\n}\n\n// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexw\npub fn is_already_running() -> bool {\n    unsafe {\n        let session_id = WindowsApi::current_session_id();\n        let mutex_name = format!(\"Local\\\\Seelen-UI-Instance-{}\", session_id);\n        let mutex_name_wide = WindowsString::from_str(&mutex_name);\n\n        // Try to create a named mutex specific to the current session\n        let Ok(handle) = CreateMutexW(None, true, mutex_name_wide.as_pcwstr()) else {\n            // Failed to create mutex, assume not running to be safe\n            log::warn!(\"Failed to create instance mutex, proceeding anyway\");\n            return false;\n        };\n\n        // if mutex existed before, another instance is already running for this session\n        let last_error = GetLastError();\n        if last_error == ERROR_ALREADY_EXISTS {\n            return true;\n        }\n\n        // This is the first instance for this session\n        // Keep the handle alive by leaking it (will be released when process exits)\n        Box::leak(Box::new(handle));\n        false\n    }\n}\n"
  },
  {
    "path": "src/background/utils/integrity/webview.rs",
    "content": "use std::sync::atomic::{AtomicBool, Ordering};\n\nuse base64::Engine;\nuse tauri::webview_version;\nuse tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};\nuse tauri_plugin_shell::ShellExt;\n\nuse crate::{error::Result, widgets::webview::WebviewArgs};\n\nuse super::spawn_named_thread;\n\nstatic WEBVIEW_STATE_VALIDATED: AtomicBool = AtomicBool::new(false);\n\npub fn validate_webview_runtime_is_installed(app: &tauri::AppHandle) -> Result<()> {\n    let error = match webview_version() {\n        Ok(version) => {\n            let mut version = version.split('.');\n            let major = version.next().unwrap_or(\"0\").parse().unwrap_or(0);\n            if major < 110 {\n                Some((\n                    t!(\"runtime.outdated\"),\n                    t!(\"runtime.outdated_description\", min_version = \"110\"),\n                ))\n            } else {\n                None\n            }\n        }\n        Err(_) => Some((t!(\"runtime.not_found\"), t!(\"runtime.not_found_description\"))),\n    };\n\n    if let Some((title, message)) = error {\n        let ok_pressed = app\n            .dialog()\n            .message(message)\n            .title(title)\n            .kind(MessageDialogKind::Error)\n            .buttons(MessageDialogButtons::OkCustom(\n                t!(\"runtime.download\").to_string(),\n            ))\n            .blocking_show();\n        if ok_pressed {\n            let url = \"https://developer.microsoft.com/en-us/microsoft-edge/webview2/?form=MA13LH#download\";\n            #[allow(deprecated)]\n            app.shell().open(url, None)?;\n        }\n        return Err(\"Webview runtime not installed or outdated\".into());\n    }\n    Ok(())\n}\n\n/// Try creating a webview window, tauri for some reason could panic stoping the setup hook and for some reason\n/// the panic hook is not catching this so this implementation is a workaround for that.\n///\n/// The event loop is still running after fail so we can easily restart the app just using tauri.\npub fn check_for_webview_optimal_state(app: &tauri::AppHandle) -> Result<()> {\n    log::info!(\"Testing webview optimal state...\");\n    start_integrity_thread(app.clone());\n    let label = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(\"@seelen/integrity\");\n    let args = WebviewArgs::default();\n\n    let window = tauri::WebviewWindowBuilder::new(\n        app,\n        &label,\n        tauri::WebviewUrl::App(\"vanilla/integrity/index.html\".into()),\n    )\n    .visible(false)\n    .data_directory(args.data_directory())\n    .additional_browser_args(&args.to_string())\n    .build()?;\n    window.hwnd()?; // build could not fail so we check for the handle.\n    window.destroy()?; // close the fake window\n    WEBVIEW_STATE_VALIDATED.store(true, Ordering::SeqCst);\n    Ok(())\n}\n\nfn start_integrity_thread(app: tauri::AppHandle) {\n    spawn_named_thread(\"Integrity\", move || {\n        // Maximum number of attempts to validate the webview state (max: 5 seconds)\n        let mut remaining_attempts = 50;\n        while !WEBVIEW_STATE_VALIDATED.load(Ordering::SeqCst) && remaining_attempts > 0 {\n            remaining_attempts -= 1;\n            std::thread::sleep(std::time::Duration::from_millis(100));\n        }\n        // If all attempts are exhausted, exit the application with an error code\n        if remaining_attempts == 0 {\n            app.exit(1);\n        }\n    });\n}\n"
  },
  {
    "path": "src/background/utils/lock_free/mod.rs",
    "content": "mod sync_hash_map;\nmod sync_vec;\nmod traced_mutex;\n\npub use sync_hash_map::SyncHashMap;\npub use sync_vec::SyncVec;\npub use traced_mutex::TracedMutex;\n"
  },
  {
    "path": "src/background/utils/lock_free/sync_hash_map.rs",
    "content": "use std::borrow::Borrow;\nuse std::collections::HashMap;\nuse std::hash::Hash;\n\nuse crate::utils::lock_free::TracedMutex;\n\n/// Wrapper for `Mutex<HashMap<K, V>>` with simplifies the API and prevents deadlocks\npub struct SyncHashMap<K, V>(TracedMutex<HashMap<K, V>>);\n\n#[allow(dead_code, clippy::multiple_bound_locations)]\nimpl<K, V> SyncHashMap<K, V>\nwhere\n    K: Eq + Hash,\n{\n    pub fn new() -> Self {\n        Self(TracedMutex::new(HashMap::new()))\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.lock().len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.0.lock().is_empty()\n    }\n\n    pub fn upsert(&self, key: K, value: V) -> Option<V> {\n        self.0.lock().insert(key, value)\n    }\n\n    pub fn remove<Q: ?Sized>(&self, key: &Q) -> Option<V>\n    where\n        K: Borrow<Q>,\n        Q: Eq + Hash,\n    {\n        self.0.lock().remove(key)\n    }\n\n    pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool\n    where\n        K: Borrow<Q>,\n        Q: Eq + Hash,\n    {\n        self.0.lock().contains_key(key)\n    }\n\n    pub fn get<Q: ?Sized, F, R>(&self, key: &Q, f: F) -> Option<R>\n    where\n        K: Borrow<Q>,\n        Q: Eq + Hash,\n        F: FnOnce(&mut V) -> R,\n    {\n        self.0.lock().get_mut(key).map(f)\n    }\n\n    /// If key does not exist, it will be created with default value\n    pub fn get_or_default<Q, F, R>(&self, key: Q, f: F) -> R\n    where\n        V: Default,\n        Q: Into<K>,\n        F: FnOnce(&mut V) -> R,\n    {\n        f(self.0.lock().entry(key.into()).or_default())\n    }\n\n    /// If key does not exist, it will be created using the provided constructor function\n    pub fn get_or_insert<Q, C, F, R>(&self, key: Q, constructor: C, f: F) -> R\n    where\n        Q: Into<K>,\n        C: FnOnce() -> V,\n        F: FnOnce(&mut V) -> R,\n    {\n        f(self.0.lock().entry(key.into()).or_insert_with(constructor))\n    }\n\n    pub fn for_each<F>(&self, f: F)\n    where\n        F: FnMut((&K, &mut V)),\n    {\n        self.0.lock().iter_mut().for_each(f);\n    }\n\n    pub fn retain<F>(&self, f: F)\n    where\n        F: FnMut(&K, &mut V) -> bool,\n    {\n        self.0.lock().retain(f);\n    }\n\n    pub fn clear(&self) {\n        self.0.lock().clear();\n    }\n\n    pub fn any<F>(&self, f: F) -> bool\n    where\n        F: FnMut((&K, &V)) -> bool,\n    {\n        self.0.lock().iter().any(f)\n    }\n\n    pub fn take(&self) -> HashMap<K, V> {\n        self.0.lock().drain().collect()\n    }\n\n    pub fn replace(&self, value: HashMap<K, V>) {\n        *self.0.lock() = value;\n    }\n}\n\n#[allow(dead_code)]\nimpl<K, V> SyncHashMap<K, V>\nwhere\n    K: Eq + Hash + Clone,\n    V: Clone,\n{\n    pub fn to_hash_map(&self) -> HashMap<K, V> {\n        self.0.lock().clone()\n    }\n\n    pub fn keys(&self) -> Vec<K> {\n        self.0.lock().keys().cloned().collect()\n    }\n\n    pub fn values(&self) -> Vec<V> {\n        self.0.lock().values().cloned().collect()\n    }\n}\n\nimpl<K, V> From<HashMap<K, V>> for SyncHashMap<K, V>\nwhere\n    K: Eq + Hash,\n{\n    fn from(value: HashMap<K, V>) -> Self {\n        Self(TracedMutex::new(value))\n    }\n}\n\nimpl<K, V> Default for SyncHashMap<K, V>\nwhere\n    K: Eq + Hash,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "src/background/utils/lock_free/sync_vec.rs",
    "content": "use crate::utils::lock_free::TracedMutex;\n\n/// Wrapper for `Mutex<Vec<T>>` with simplifies the API and prevents deadlocks\n#[derive(Debug)]\npub struct SyncVec<T>(TracedMutex<Vec<T>>);\n\n#[allow(dead_code)]\nimpl<T> SyncVec<T> {\n    pub fn new() -> Self {\n        Self(TracedMutex::new(Vec::new()))\n    }\n\n    pub fn contains(&self, item: &T) -> bool\n    where\n        T: PartialEq,\n    {\n        self.0.lock().contains(item)\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.lock().len()\n    }\n\n    pub fn push(&self, item: T) {\n        self.0.lock().push(item);\n    }\n\n    pub fn any(&self, f: impl FnMut(&T) -> bool) -> bool {\n        self.0.lock().iter().any(f)\n    }\n\n    pub fn for_each<F>(&self, f: F)\n    where\n        F: FnMut(&mut T),\n    {\n        self.0.lock().iter_mut().for_each(f);\n    }\n\n    pub fn map<F, R>(&self, f: F) -> Vec<R>\n    where\n        F: FnMut(&mut T) -> R,\n    {\n        self.0.lock().iter_mut().map(f).collect()\n    }\n\n    pub fn retain<F>(&self, f: F)\n    where\n        F: FnMut(&T) -> bool,\n    {\n        self.0.lock().retain(f);\n    }\n\n    pub fn clear(&self) {\n        self.0.lock().clear();\n    }\n\n    pub fn drain(&self) -> Vec<T> {\n        self.0.lock().drain(..).collect()\n    }\n\n    pub fn replace(&self, value: Vec<T>) {\n        *self.0.lock() = value;\n    }\n\n    pub fn sort_by<F>(&self, compare: F)\n    where\n        F: FnMut(&T, &T) -> std::cmp::Ordering,\n    {\n        self.0.lock().sort_by(compare);\n    }\n}\n\nimpl<T: Clone> SyncVec<T> {\n    pub fn to_vec(&self) -> Vec<T> {\n        self.0.lock().clone()\n    }\n\n    pub fn find(&self, f: impl Fn(&T) -> bool) -> Option<T> {\n        let items = self.0.lock();\n        for item in items.iter() {\n            if f(item) {\n                return Some(item.clone());\n            }\n        }\n        None\n    }\n}\n\nimpl<T> From<Vec<T>> for SyncVec<T> {\n    fn from(value: Vec<T>) -> Self {\n        Self(TracedMutex::new(value))\n    }\n}\n\nimpl<T> Default for SyncVec<T> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "src/background/utils/lock_free/traced_mutex.rs",
    "content": "use parking_lot::{Mutex, MutexGuard};\nuse rust_i18n::AtomicStr;\nuse std::panic::Location;\n\nuse crate::error::AppError;\n\n/// A wrapper around parking_lot::Mutex that tracks the last location where it was locked.\n/// This is useful for debugging deadlocks and understanding lock contention.\npub struct TracedMutex<T> {\n    inner: Mutex<T>,\n    last_lock_location: AtomicStr,\n}\n\nimpl<T> TracedMutex<T> {\n    /// Creates a new TracedMutex with the given value\n    pub fn new(value: T) -> Self {\n        Self {\n            inner: Mutex::new(value),\n            last_lock_location: AtomicStr::new(\"\"),\n        }\n    }\n\n    /// Locks the mutex and records the caller's location.\n    ///\n    /// The `#[track_caller]` attribute ensures that `Location::caller()` returns\n    /// the location where `trace_lock()` was called, not the location inside this function.\n    ///\n    /// # Panics\n    ///\n    /// This function will panic if the mutex is already locked by the current thread (deadlock).\n    /// The panic will include:\n    /// - The current caller location (where trace_lock was called)\n    /// - The last recorded lock location (if any)\n    #[track_caller]\n    pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> {\n        // let current_location = Location::caller();\n\n        // Try to acquire the lock with a timeout to detect potential deadlocks\n        match self.inner.try_lock_for(std::time::Duration::from_secs(5)) {\n            Some(guard) => {\n                let location = Location::caller();\n                self.last_lock_location.replace(format!(\n                    \"{}:{}:{}\",\n                    location.file(),\n                    location.line(),\n                    location.column()\n                ));\n                guard\n            }\n            None => {\n                // Lock is already held, gather information and panic\n                let msg = format!(\n                    \"Mutex is deadlocked, Last lock location: {}\",\n                    self.last_lock_location\n                );\n                panic!(\"{:?}\", AppError::from(msg));\n            }\n        }\n    }\n}\n\nimpl<T: Default> Default for TracedMutex<T> {\n    fn default() -> Self {\n        Self::new(T::default())\n    }\n}\n\n// Implement common traits to make TracedMutex a drop-in replacement\nimpl<T: std::fmt::Debug> std::fmt::Debug for TracedMutex<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self.inner.try_lock() {\n            Some(guard) => f\n                .debug_struct(\"TracedMutex\")\n                .field(\"data\", &*guard)\n                .finish(),\n            None => f\n                .debug_struct(\"TracedMutex\")\n                .field(\"data\", &\"<locked>\")\n                .finish(),\n        }\n    }\n}\n\n// Safety: TracedMutex can be sent between threads if T can be sent\nunsafe impl<T: Send> Send for TracedMutex<T> {}\n// Safety: TracedMutex can be shared between threads if T can be sent\nunsafe impl<T: Send> Sync for TracedMutex<T> {}\n"
  },
  {
    "path": "src/background/utils/mod.rs",
    "content": "pub mod constants;\npub mod discord;\npub mod icon_extractor;\npub mod integrity;\npub mod lock_free;\npub mod pwsh;\npub mod updater;\npub mod virtual_desktop;\nmod winver;\n\npub use winver::*;\n\nuse std::{\n    collections::HashMap,\n    fs::{create_dir_all, File},\n    io::Write,\n    path::{Path, PathBuf},\n    sync::{atomic::AtomicBool, LazyLock},\n    time::{Duration, Instant, SystemTime, UNIX_EPOCH},\n};\n\nuse itertools::Itertools;\nuse parking_lot::Mutex;\nuse windows::{\n    core::GUID,\n    Win32::UI::Shell::{SHGetKnownFolderPath, KF_FLAG_DEFAULT},\n};\n\nuse crate::error::Result;\n\n/// Writes `content` to `path` atomically: writes to a sibling `.tmp` file first,\n/// syncs to disk, then renames into place. This guarantees the target file is\n/// never left empty or partially written, even if the process is killed mid-write.\npub fn atomic_write_file(path: &Path, content: &[u8]) -> Result<()> {\n    let dir = path.parent().ok_or(\"Path has no parent directory\")?;\n    create_dir_all(dir)?;\n\n    let tmp_path = path.with_extension(\"tmp\");\n    let mut file = File::create(&tmp_path)?;\n    file.write_all(content)?;\n    file.flush()?;\n    file.sync_all()?;\n    drop(file); // must close before rename on Windows\n    std::fs::rename(&tmp_path, path)?;\n    Ok(())\n}\n\npub fn pcwstr(s: &str) -> windows::core::PCWSTR {\n    windows::core::PCWSTR::from_raw(s.encode_utf16().chain(Some(0)).collect_vec().as_ptr())\n}\n\npub fn sleep_millis(millis: u64) {\n    std::thread::sleep(Duration::from_millis(millis));\n}\n\n/// Resolve paths with folder ids in the form of \"{GUID}\\path\\to\\file\"\n///\n/// https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid\n#[allow(dead_code)]\npub fn resolve_guid_path<S: AsRef<str>>(path: S) -> Result<PathBuf> {\n    let parts = path.as_ref().split(\"\\\\\");\n    let mut path_buf = PathBuf::new();\n\n    for (idx, part) in parts.into_iter().enumerate() {\n        if part.starts_with(\"{\") && part.ends_with(\"}\") {\n            let guid = part.trim_start_matches('{').trim_end_matches('}');\n            let rfid = GUID::try_from(guid)?;\n            let string_path =\n                unsafe { SHGetKnownFolderPath(&rfid as _, KF_FLAG_DEFAULT, None)?.to_string()? };\n\n            path_buf.push(string_path);\n        } else if idx == 0 {\n            return Ok(PathBuf::from(path.as_ref()));\n        } else {\n            path_buf.push(part);\n        }\n    }\n\n    Ok(path_buf)\n}\n\npub static TRACE_LOCK_ENABLED: AtomicBool = AtomicBool::new(true);\npub static LAST_SUCCESSFUL_LOCK: LazyLock<Mutex<HashMap<String, String>>> =\n    LazyLock::new(|| Mutex::new(HashMap::new()));\n\n#[macro_export]\nmacro_rules! trace_lock {\n    ($mutex:expr) => {\n        trace_lock!($mutex, 5)\n    };\n    ($mutex:expr, $duration:expr) => {{\n        let guard = $mutex.try_lock_for(std::time::Duration::from_secs($duration));\n        let guard_name = stringify!($mutex);\n        match guard {\n            Some(guard) => {\n                if $crate::utils::TRACE_LOCK_ENABLED.load(std::sync::atomic::Ordering::Acquire) {\n                    let mut map = $crate::utils::LAST_SUCCESSFUL_LOCK\n                        .try_lock_for(std::time::Duration::from_secs(5))\n                        .unwrap();\n                    let location = format!(\"{}:{}\", file!(), line!());\n                    map.insert(guard_name.to_owned(), location);\n                }\n                guard\n            }\n            None => {\n                let mut panic_msg = format!(\n                    \"{} mutex is deadlocked at {}:{}\",\n                    guard_name,\n                    file!(),\n                    line!()\n                );\n\n                if let Some(path) = $crate::utils::LAST_SUCCESSFUL_LOCK\n                    .try_lock_for(std::time::Duration::from_secs(5))\n                    .unwrap()\n                    .get(guard_name)\n                {\n                    panic_msg = format!(\"{}, last successful aquire was at {}\", panic_msg, path);\n                }\n\n                panic!(\"{:?}\", $crate::error::AppError::from(panic_msg));\n            }\n        }\n    }};\n}\n\npub static PERFORMANCE_HELPER: LazyLock<Mutex<PerformanceHelper>> = LazyLock::new(|| {\n    Mutex::new(PerformanceHelper {\n        time: HashMap::new(),\n    })\n});\n\npub struct PerformanceHelper {\n    time: HashMap<String, Instant>,\n}\n\nimpl PerformanceHelper {\n    pub fn start(&mut self, name: &str) {\n        log::debug!(\"{name} start\");\n        self.time.insert(name.to_string(), Instant::now());\n    }\n\n    pub fn elapsed(&self, name: &str) -> Duration {\n        self.time.get(name).unwrap().elapsed()\n    }\n\n    pub fn end(&mut self, name: &str) {\n        log::debug!(\"{} end in: {:.2}s\", name, self.elapsed(name).as_secs_f64());\n        self.time.remove(name);\n    }\n}\n\n/// Useful when spawning threads that will allocate a loop or some other blocking operation\npub fn spawn_named_thread<F, T>(id: &str, cb: F) -> std::thread::JoinHandle<T>\nwhere\n    F: FnOnce() -> T,\n    F: Send + 'static,\n    T: Send + 'static,\n{\n    let thread = std::thread::Builder::new()\n        .name(format!(\"Seelen Thread - {id}\"))\n        .spawn(cb);\n    match thread {\n        Ok(handle) => handle,\n        Err(e) => panic!(\"Failed to spawn thread: {e}\"),\n    }\n}\n\n/// intended to work as converFileToSrc in JS side using tauri library\npub fn convert_file_to_src(path: &Path) -> String {\n    #[cfg(any(windows, target_os = \"android\"))]\n    let base = \"http://asset.localhost/\";\n    #[cfg(not(any(windows, target_os = \"android\")))]\n    let base = \"asset://localhost/\";\n    let path = path\n        .canonicalize()\n        .unwrap_or_else(|_| path.to_path_buf())\n        .to_string_lossy()\n        .to_string();\n    let encoded = urlencoding::encode(&path);\n    format!(\"{base}{encoded}\")\n}\n\npub fn now_timestamp_as_millis() -> u64 {\n    let start = SystemTime::now();\n    let since_the_epoch = start\n        .duration_since(UNIX_EPOCH)\n        .expect(\"Time went backwards\");\n    since_the_epoch.as_secs() * 1000 + since_the_epoch.subsec_nanos() as u64 / 1_000_000\n}\n\npub fn date_based_hex_id() -> String {\n    let since_epoch = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n    format!(\"{since_epoch:x}\")\n}\n\npub fn get_parts_of_inline_command(cmd: &str) -> (String, Option<String>) {\n    let start_double_quoted = cmd.starts_with(\"\\\"\");\n    if start_double_quoted || cmd.starts_with(\"'\") {\n        let delimiter = if start_double_quoted { '\"' } else { '\\'' };\n        let mut parts = cmd.split(['\"', '\\'']).filter(|s| !s.is_empty());\n\n        let program = parts.next().unwrap_or_default().trim().to_owned();\n        let args = cmd\n            .trim_start_matches(&format!(\"{delimiter}{program}{delimiter}\"))\n            .trim()\n            .to_owned();\n        return (program, if args.is_empty() { None } else { Some(args) });\n    }\n\n    let cmd_as_path = PathBuf::from(cmd);\n    if cmd_as_path.exists() {\n        let program = cmd_as_path.to_string_lossy().to_string();\n        return (program, None);\n    }\n\n    let mut parts = cmd.split(\" \").filter(|s| !s.is_empty());\n    let program = parts.next().unwrap_or_default().trim().to_owned();\n    let args = cmd.trim_start_matches(&program).trim().to_owned();\n    (program, if args.is_empty() { None } else { Some(args) })\n}\n"
  },
  {
    "path": "src/background/utils/pwsh.rs",
    "content": "use std::{env::temp_dir, path::PathBuf, sync::LazyLock};\n\nuse itertools::Itertools;\nuse tauri_plugin_shell::ShellExt;\nuse windows::Win32::UI::Shell::FOLDERID_System;\n\nuse crate::{app::get_app_handle, error::Result, windows_api::WindowsApi};\n\nconst PWSH_COMMON_ARGS: [&str; 7] = [\n    \"-NoLogo\",\n    \"-NoProfile\",\n    \"-NonInteractive\",\n    \"-ExecutionPolicy\",\n    \"Bypass\",\n    \"-WindowStyle\",\n    \"Hidden\",\n];\n\npub struct PwshScript {\n    mode: PwshExecutionMode,\n    inner: String,\n    elevated: bool,\n}\n\npub enum PwshExecutionMode {\n    ScriptFile(Vec<String>),\n    Command,\n}\n\nstatic POWERSHELL_PATH: LazyLock<PathBuf> = LazyLock::new(|| {\n    WindowsApi::known_folder(FOLDERID_System)\n        .expect(\"Failed to get system folder\")\n        .join(\"WindowsPowerShell\\\\v1.0\\\\powershell.exe\")\n});\n\nimpl PwshScript {\n    pub fn new<S: Into<String>>(contents: S) -> Self {\n        Self {\n            inner: contents.into(),\n            mode: PwshExecutionMode::ScriptFile(Vec::new()),\n            elevated: false,\n        }\n    }\n\n    pub fn inline_command(mut self) -> Self {\n        self.mode = PwshExecutionMode::Command;\n        self\n    }\n\n    /// ignored if `mode` is other than `PwshExecutionMode::ScriptFile`\n    pub fn elevated(mut self) -> Self {\n        self.elevated = true;\n        self\n    }\n\n    fn build_args(&self, script_path_str: &str) -> Vec<String> {\n        match &self.mode {\n            PwshExecutionMode::ScriptFile(args) => {\n                let mut args = PWSH_COMMON_ARGS\n                    .iter()\n                    .map(|s| s.to_string())\n                    .chain([\"-File\".to_string(), script_path_str.to_string()])\n                    .chain(args.clone())\n                    .collect_vec();\n                if self.elevated && !WindowsApi::is_elevated().unwrap_or(false) {\n                    args = PWSH_COMMON_ARGS\n                    .iter()\n                    .map(|s| s.to_string())\n                    .chain([\n                        \"-Command\".to_string(),\n                        format!(\"Start-Process '{}' -Verb runAs -WindowStyle Hidden -Wait -ArgumentList '{}'\", POWERSHELL_PATH.display(), args.join(\" \"))\n                    ])\n                    .collect_vec();\n                }\n                args\n            }\n            PwshExecutionMode::Command => PWSH_COMMON_ARGS\n                .iter()\n                .map(|s| s.to_string())\n                .chain([\"-Command\".to_string(), self.inner.to_string()])\n                .collect_vec(),\n        }\n    }\n\n    /// returns `Ok(stdout)` or `Err(stderr)`\n    ///\n    /// if elevated, will run as admin and always will return `Ok(\"\")`\n    pub async fn execute(&self) -> Result<String> {\n        let script_path = temp_dir().join(format!(\"slu-{}.ps1\", uuid::Uuid::new_v4()));\n        std::fs::write(&script_path, &self.inner)?;\n\n        let args = self.build_args(&script_path.to_string_lossy());\n        let shell = get_app_handle().shell();\n        let result = shell.command(&*POWERSHELL_PATH).args(args).output().await;\n        // delete script before check output\n        std::fs::remove_file(&script_path)?;\n        let output = result?;\n\n        if output.status.success() {\n            let (cow, _used, _has_errors) = encoding_rs::GBK.decode(&output.stdout);\n            Ok(cow.trim().to_string())\n        } else {\n            Err(output.into())\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/utils/updater.rs",
    "content": "use seelen_core::state::UpdateChannel;\nuse slu_ipc::messages::SvcAction;\nuse tauri_plugin_updater::{Update, UpdaterExt};\n\nuse crate::{\n    app::get_app_handle, cli::ServicePipe, error::Result, state::application::FULL_STATE,\n    utils::has_fixed_runtime,\n};\n\nuse super::is_running_as_appx;\n\npub static SIGN_PUB_KEY: &str = \"dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQ4QjU1RUI0NEM0NzBERUIKUldUckRVZE10RjYxU0lpaERvdklYL05DVlg0Sk9EVngvaEgzZjMvU1NNemJTZXZ1K0dNVXU3ZkQK\";\n\npub async fn check_for_updates() -> Result<Option<Update>> {\n    if tauri::is_dev() || has_fixed_runtime() || is_running_as_appx() {\n        return Ok(None);\n    }\n\n    let state = FULL_STATE.load();\n    let channel = state.settings.updater.channel;\n    let mut update = None;\n\n    if channel == UpdateChannel::Nightly {\n        let updater: tauri_plugin_updater::Updater = get_app_handle()\n            .updater_builder()\n            .pubkey(SIGN_PUB_KEY)\n            .endpoints(vec![\n                \"https://github.com/eythaann/Seelen-UI/releases/download/nightly/latest.json\"\n                    .try_into()\n                    .expect(\"Failed to parse url\"),\n            ])?\n            .build()?;\n        update = updater.check().await?;\n    }\n\n    // Release Channel\n    if update.is_none() {\n        let updater: tauri_plugin_updater::Updater = get_app_handle()\n            .updater_builder()\n            .pubkey(SIGN_PUB_KEY)\n            .endpoints(vec![\n                \"https://github.com/eythaann/Seelen-UI/releases/latest/download/latest.json\"\n                    .try_into()\n                    .expect(\"Failed to parse url\"),\n            ])?\n            .build()?;\n        update = updater.check().await?;\n    }\n\n    Ok(update)\n}\n\npub async fn trace_update_intallation(update: Update) -> Result<()> {\n    log::trace!(\"Update: downloading\");\n    let bytes = update\n        .download(\n            |_chunk_length, _content_length| {},\n            || log::trace!(\"Update: download finished\"),\n        )\n        .await?;\n    ServicePipe::request(SvcAction::Stop)?;\n    update.install(bytes)?;\n    log::trace!(\"Update: intallation finished\");\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/utils/virtual_desktop.rs",
    "content": "use windows::core::GUID;\nuse winreg::{enums::HKEY_CURRENT_USER, RegKey};\n\nuse crate::{error::Result, windows_api::WindowsApi};\n\nuse super::is_windows_10;\n\npub struct RegistryVirtualDesktopManager {}\n\n#[allow(dead_code)]\npub struct RegistryVirtualDesktop {\n    id: [u8; 16],\n    guid: GUID,\n    name: String,\n}\n\nimpl From<GUID> for RegistryVirtualDesktop {\n    fn from(guid: GUID) -> Self {\n        let mut id: Vec<u8> = Vec::new();\n        id.append(&mut guid.data1.to_le_bytes().to_vec());\n        id.append(&mut guid.data2.to_le_bytes().to_vec());\n        id.append(&mut guid.data3.to_le_bytes().to_vec());\n        id.append(&mut guid.data4.to_vec());\n\n        Self {\n            id: id.try_into().expect(\"Invalid id length\"),\n            guid,\n            name: String::new(),\n        }\n    }\n}\n\nimpl From<Vec<u8>> for RegistryVirtualDesktop {\n    fn from(id: Vec<u8>) -> Self {\n        Self {\n            id: id.clone().try_into().expect(\"Invalid id length\"),\n            guid: GUID {\n                data1: u32::from_le_bytes(id[0..4].try_into().unwrap()),\n                data2: u16::from_le_bytes(id[4..6].try_into().unwrap()),\n                data3: u16::from_le_bytes(id[6..8].try_into().unwrap()),\n                data4: id[8..].try_into().unwrap(),\n            },\n            name: String::new(),\n        }\n    }\n}\n\n#[allow(dead_code)]\nimpl RegistryVirtualDesktop {\n    pub fn id(&self) -> String {\n        format!(\"{:?}\", self.guid)\n    }\n\n    pub fn guid(&self) -> GUID {\n        self.guid\n    }\n}\n\n#[allow(dead_code)]\nimpl RegistryVirtualDesktopManager {\n    fn get_virtual_desktops_folder() -> Result<RegKey> {\n        let hkcu = RegKey::predef(HKEY_CURRENT_USER);\n        Ok(if is_windows_10() {\n            let session_id = WindowsApi::current_session_id();\n            hkcu.open_subkey(format!(r\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\{session_id}\\VirtualDesktops\"))?\n        } else {\n            hkcu.open_subkey(r\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops\")?\n        })\n    }\n\n    pub fn enum_virtual_desktops() -> Result<Vec<RegistryVirtualDesktop>> {\n        let desktops = Self::get_virtual_desktops_folder()?;\n        let current = desktops.get_raw_value(\"VirtualDesktopIDs\")?.bytes;\n        let mut result = Vec::new();\n        for desktop_id in current.chunks_exact(16).map(Vec::from) {\n            result.push(RegistryVirtualDesktop::from(desktop_id))\n        }\n        Ok(result)\n    }\n\n    pub fn current_virtual_desktops() -> Result<RegistryVirtualDesktop> {\n        let desktops = Self::get_virtual_desktops_folder()?;\n        let current = desktops.get_raw_value(\"CurrentVirtualDesktop\")?;\n        Ok(RegistryVirtualDesktop::from(current.bytes))\n    }\n}\n"
  },
  {
    "path": "src/background/utils/winver.rs",
    "content": "use std::path::PathBuf;\n\nuse windows::Win32::Storage::Packaging::Appx::GetCurrentPackageId;\n\npub fn is_windows_10() -> bool {\n    matches!(os_info::get().version(), os_info::Version::Semantic(_, _, x) if (&10240..&22000).contains(&x))\n}\n\n#[allow(dead_code)]\npub fn is_windows_11() -> bool {\n    matches!(os_info::get().version(), os_info::Version::Semantic(_, _, x) if x >= &22000)\n}\n\npub fn has_fixed_runtime() -> bool {\n    std::env::var_os(\"WEBVIEW2_BROWSER_EXECUTABLE_FOLDER\").is_some()\n}\n\npub fn get_fixed_runtime_path() -> Option<PathBuf> {\n    let exe = std::env::current_exe().ok()?;\n    let install_dir = exe.parent()?;\n    let read_dir = install_dir.join(\"runtime\").read_dir().ok()?;\n    let runtime = read_dir.last()?.ok()?.path();\n    if runtime.join(\"msedgewebview2.exe\").exists() {\n        Some(runtime)\n    } else {\n        None\n    }\n}\n\npub fn is_running_as_appx() -> bool {\n    static CACHE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();\n    *CACHE.get_or_init(|| unsafe {\n        let mut len = 0u32;\n        let _ = GetCurrentPackageId(&mut len, None);\n        let mut buffer = vec![0u8; len as usize];\n        GetCurrentPackageId(&mut len, Some(buffer.as_mut_ptr())).is_ok()\n    })\n}\n\npub fn was_installed_using_msix() -> bool {\n    static CACHE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();\n    *CACHE.get_or_init(|| {\n        std::env::current_exe().is_ok_and(|p| p.with_file_name(\"AppxManifest.xml\").exists())\n    })\n}\n"
  },
  {
    "path": "src/background/virtual_desktops/cli.rs",
    "content": "use seelen_core::state::WidgetTriggerPayload;\nuse serde::{Deserialize, Serialize};\n\nuse crate::{\n    error::Result, virtual_desktops::SluWorkspacesManager2, widgets::trigger_widget,\n    windows_api::window::Window,\n};\n\n/// Manage the Seelen Window Manager.\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\n#[command(alias = \"vd\")]\npub struct VirtualDesktopCli {\n    #[command(subcommand)]\n    pub subcommand: VdCommand,\n}\n\n#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, clap::Subcommand)]\npub enum VdCommand {\n    /// Send the window to the specified workspace\n    SendToWorkspace {\n        /// The index of the workspace to switch to.\n        index: usize,\n    },\n    /// Switch to the specified workspace\n    SwitchWorkspace {\n        /// The index of the workspace to switch to.\n        index: usize,\n    },\n    /// Send the window to the specified workspace and switch to it\n    MoveToWorkspace {\n        /// The index of the workspace to switch to.\n        index: usize,\n    },\n    /// Switch to the next workspace\n    SwitchNext,\n    /// Switch to the previous workspace\n    SwitchPrev,\n    /// Create a new workspace\n    CreateNewWorkspace,\n    /// Destroy the current workspace (will do nothing if there's only one workspace)\n    DestroyCurrentWorkspace,\n    /// Toggle the workspace view\n    ToggleWorkspacesView,\n}\n\nimpl VirtualDesktopCli {\n    pub fn process(self) -> Result<()> {\n        self.subcommand.process()\n    }\n}\n\nimpl VdCommand {\n    pub fn process(self) -> Result<()> {\n        let focused_win = Window::get_foregrounded();\n        let monitor_id = focused_win.monitor().stable_id()?;\n        let vd = SluWorkspacesManager2::instance();\n\n        match self {\n            VdCommand::SendToWorkspace { index } => {\n                let workspace_id = vd\n                    .monitors\n                    .get(&monitor_id, |monitor| {\n                        monitor\n                            .workspaces\n                            .get(index)\n                            .map(|w| w.id.clone())\n                            .ok_or_else(|| format!(\"Workspace index {} not found\", index))\n                    })\n                    .ok_or(\"Monitor not found\")??;\n                vd.send_to(&focused_win, &workspace_id)?;\n            }\n            VdCommand::SwitchWorkspace { index } => {\n                vd.switch_to(&monitor_id, index)?;\n            }\n            VdCommand::MoveToWorkspace { index } => {\n                let workspace_id = vd\n                    .monitors\n                    .get(&monitor_id, |monitor| {\n                        monitor\n                            .workspaces\n                            .get(index)\n                            .map(|w| w.id.clone())\n                            .ok_or_else(|| format!(\"Workspace index {} not found\", index))\n                    })\n                    .ok_or(\"Monitor not found\")??;\n                vd.send_to(&focused_win, &workspace_id)?;\n                std::thread::sleep(std::time::Duration::from_millis(20));\n                vd.switch_to(&monitor_id, index)?;\n            }\n            VdCommand::SwitchNext | VdCommand::SwitchPrev => {\n                let (active_workspace_idx, len) = vd\n                    .monitors\n                    .get(&monitor_id, |monitor| {\n                        let active_workspace_idx = monitor\n                            .workspaces\n                            .iter()\n                            .position(|w| &w.id == monitor.active_workspace_id())\n                            .ok_or(\"No active workspace\")?;\n                        Result::Ok((active_workspace_idx, monitor.workspaces.len()))\n                    })\n                    .ok_or(\"Monitor not found\")??;\n\n                let next_idx = if self == VdCommand::SwitchNext {\n                    (active_workspace_idx + 1) % len // next and cycle\n                } else {\n                    (active_workspace_idx + (len - 1)) % len // prev and cycle\n                };\n                vd.switch_to(&monitor_id, next_idx)?;\n            }\n            VdCommand::CreateNewWorkspace => {\n                let workspace_id = vd.create_desktop(&monitor_id)?;\n                vd.switch_to_id(&monitor_id, &workspace_id)?;\n            }\n            VdCommand::DestroyCurrentWorkspace => {\n                let workspace_id = vd\n                    .monitors\n                    .get(&monitor_id, |monitor| monitor.active_workspace_id().clone())\n                    .ok_or(\"Monitor not found\")?;\n                vd.destroy_desktop(&monitor_id, &workspace_id)?;\n            }\n            VdCommand::ToggleWorkspacesView => {\n                trigger_widget(WidgetTriggerPayload::new(\n                    \"@seelen/workspaces-viewer\".into(),\n                ))?;\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/virtual_desktops/events.rs",
    "content": "use seelen_core::{state::WorkspaceId, system_state::MonitorId};\n\n#[derive(Debug, Clone)]\n#[allow(dead_code)]\npub enum VirtualDesktopEvent {\n    DesktopCreated(WorkspaceId),\n    DesktopDestroyed(WorkspaceId),\n    DesktopChanged {\n        monitor: MonitorId,\n        workspace: WorkspaceId,\n    },\n    /// Emitted when the virtual desktops state changes (e.g., wallpapers updated)\n    StateChanged,\n    // DesktopNameChanged(WorkspaceId, String),\n    /* DesktopMoved {\n        desktop: WorkspaceId,\n        old_index: usize,\n        new_index: usize,\n    }, */\n    WindowAdded {\n        window: isize,\n        desktop: WorkspaceId,\n    },\n    /// Emitted when a window is or moved of virtual desktop.\n    WindowMoved {\n        window: isize,\n        desktop: WorkspaceId,\n    },\n    WindowRemoved {\n        window: isize,\n    },\n}\n"
  },
  {
    "path": "src/background/virtual_desktops/handlers.rs",
    "content": "use std::sync::Once;\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    resource::{SluResource, WallpaperId},\n    state::{VirtualDesktops, Wallpaper},\n    system_state::MonitorId,\n};\n\nuse crate::{\n    app::emit_to_webviews, error::Result, resources::RESOURCES, utils::date_based_hex_id,\n    virtual_desktops::SluWorkspacesManager2,\n};\n\nfn get_vd_manager() -> &'static SluWorkspacesManager2 {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        SluWorkspacesManager2::subscribe(|_event| {\n            let payload: VirtualDesktops = SluWorkspacesManager2::instance().into();\n            emit_to_webviews(SeelenEvent::VirtualDesktopsChanged, payload);\n        });\n    });\n\n    SluWorkspacesManager2::instance()\n}\n\n#[tauri::command(async)]\npub fn get_virtual_desktops() -> VirtualDesktops {\n    get_vd_manager().into()\n}\n\n#[tauri::command(async)]\npub fn switch_workspace(workspace_id: seelen_core::state::WorkspaceId) -> Result<()> {\n    let manager = get_vd_manager();\n    let monitor_id = manager.get_monitor_of_workspace(&workspace_id);\n    manager.switch_to_id(&monitor_id, &workspace_id)\n}\n\n#[tauri::command(async)]\npub fn create_workspace(monitor_id: MonitorId) -> Result<seelen_core::state::WorkspaceId> {\n    let vd = get_vd_manager();\n    let workspace_id = vd.create_desktop(&monitor_id)?;\n    vd.switch_to_id(&monitor_id, &workspace_id)?;\n    Ok(workspace_id)\n}\n\n#[tauri::command(async)]\npub fn destroy_workspace(workspace_id: seelen_core::state::WorkspaceId) -> Result<()> {\n    let manager = get_vd_manager();\n    let monitor_id = manager.get_monitor_of_workspace(&workspace_id);\n    manager.destroy_desktop(&monitor_id, &workspace_id)\n}\n\n#[tauri::command(async)]\npub fn rename_workspace(\n    workspace_id: seelen_core::state::WorkspaceId,\n    name: Option<String>,\n) -> Result<()> {\n    let manager = get_vd_manager();\n    let monitor_id = manager.get_monitor_of_workspace(&workspace_id);\n    manager.rename_desktop(&monitor_id, &workspace_id, name)\n}\n\n#[tauri::command(async)]\npub fn wallpaper_next() {\n    super::wallpapers::WorkspaceWallpapersManager::next();\n}\n\n#[tauri::command(async)]\npub fn wallpaper_prev() {\n    super::wallpapers::WorkspaceWallpapersManager::previous();\n}\n\n#[tauri::command(async)]\npub fn wallpaper_save_thumbnail(wallpaper_id: WallpaperId, thumbnail_bytes: Vec<u8>) -> Result<()> {\n    let Some(wallpaper) = RESOURCES.wallpapers.get(&wallpaper_id) else {\n        return Err(\"Invalid wallpaper id\".into());\n    };\n\n    let thumbnail_filename = format!(\"thumbnail_{}.jpg\", date_based_hex_id());\n    let thumbnail_path = wallpaper.metadata.directory()?.join(&thumbnail_filename);\n    std::fs::write(&thumbnail_path, &thumbnail_bytes)?;\n\n    let mut wallpaper_mut = Wallpaper::clone(&wallpaper);\n    wallpaper_mut.thumbnail_filename = Some(thumbnail_filename);\n    wallpaper_mut.save()?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/virtual_desktops/mod.rs",
    "content": "pub mod cli;\npub mod events;\npub mod handlers;\npub mod wallpapers;\n\nuse std::collections::HashMap;\nuse std::fs::File;\nuse std::io::Write;\nuse std::sync::LazyLock;\n\nuse seelen_core::state::{DesktopWorkspace, VirtualDesktopMonitor, VirtualDesktops, WorkspaceId};\nuse seelen_core::system_state::MonitorId;\nuse slu_utils::{debounce, Debounce};\nuse windows::Win32::UI::WindowsAndMessaging::{SW_FORCEMINIMIZE, SW_MINIMIZE, SW_RESTORE};\n\nuse crate::error::{Result, ResultLogExt};\nuse crate::hook::HookManager;\nuse crate::modules::apps::application::{UserAppWinEvent, UserAppsManager};\nuse crate::modules::monitors::{MonitorManager, MonitorManagerEvent};\nuse crate::utils::constants::SEELEN_COMMON;\nuse crate::utils::lock_free::{SyncHashMap, SyncVec};\nuse crate::virtual_desktops::wallpapers::WorkspaceWallpapersManager;\nuse crate::windows_api::window::event::WinEvent;\nuse crate::windows_api::window::Window;\nuse crate::{event_manager, log_error};\n\nuse events::VirtualDesktopEvent;\n\nstatic WORKSPACES_MANAGER: LazyLock<SluWorkspacesManager2> =\n    LazyLock::new(SluWorkspacesManager2::create);\n\npub static MINIMIZED_BY_WORKSPACES: LazyLock<scc::HashSet<isize>> =\n    LazyLock::new(scc::HashSet::new);\npub static RESTORED_EVENT_QUEUE: LazyLock<SyncVec<isize>> = LazyLock::new(SyncVec::new);\n\npub struct SluWorkspacesManager2 {\n    pub monitors: SyncHashMap<MonitorId, VirtualDesktopMonitor>,\n    pub workspace_index: SyncHashMap<WorkspaceId, MonitorId>,\n    pub pinned: SyncVec<isize>,\n}\n\nevent_manager!(SluWorkspacesManager2, VirtualDesktopEvent);\n\nimpl SluWorkspacesManager2 {\n    pub fn instance() -> &'static Self {\n        &WORKSPACES_MANAGER\n    }\n\n    fn load_stored() -> Result<VirtualDesktops> {\n        let path = SEELEN_COMMON.app_cache_dir().join(\"workspaces2.json\");\n        let file = File::open(path)?;\n        file.lock()?;\n        Ok(serde_json::from_reader(file)?)\n    }\n\n    fn request_save(&self) {\n        static SAVE_DEBOUNCER: LazyLock<Debounce<()>> = LazyLock::new(|| {\n            debounce(\n                |_| {\n                    let fun = || {\n                        let state: VirtualDesktops = SluWorkspacesManager2::instance().into();\n                        let path = SEELEN_COMMON.app_cache_dir().join(\"workspaces2.json\");\n                        let mut file = std::fs::File::create(path)?;\n                        file.write_all(&serde_json::to_vec(&state)?)?;\n                        file.flush()?;\n                        log::trace!(\"desktop workspaces successfully saved\");\n                        Result::Ok(())\n                    };\n                    fun().log_error();\n                },\n                std::time::Duration::from_secs(2),\n            )\n        });\n\n        SAVE_DEBOUNCER.call(());\n    }\n\n    fn create() -> Self {\n        let mut manager = Self::from(match Self::load_stored() {\n            Ok(mut state) => {\n                state.sanitize();\n                state\n            }\n            Err(_) => Default::default(),\n        });\n        manager.initialize().log_error();\n\n        // Initialize wallpaper manager and set initial wallpapers\n        wallpapers::WorkspaceWallpapersManager::init(&manager);\n        manager\n    }\n\n    /// TODO: try to move windows on others native virtual desktops to only one,\n    /// or add a warning message to users.\n    fn initialize(&mut self) -> Result<()> {\n        // ensure saved windows are still valid.\n        // todo: check if thery are on correct monitor\n        self.for_each_workspace(|workspace| {\n            workspace\n                .windows\n                .retain(|w| Window::from(*w).is_interactable_and_not_hidden());\n        });\n\n        // restore workspaces state\n        self.monitors.for_each(|(_, monitor)| {\n            for workspace in &monitor.workspaces {\n                if &workspace.id == monitor.active_workspace_id() {\n                    workspace.restore();\n                } else {\n                    // allow resume workspaces correctly on change\n                    for addr in &workspace.windows {\n                        let _ = MINIMIZED_BY_WORKSPACES.insert(*addr);\n                    }\n                    workspace.hide(true);\n                }\n            }\n        });\n\n        // create monitors\n        for view in MonitorManager::instance().read_all_views()? {\n            let id = view.primary_target()?.stable_id()?;\n            if self.monitors.contains_key(&id) {\n                continue;\n            }\n            self.monitors.upsert(id, VirtualDesktopMonitor::create());\n        }\n\n        // scan no added windows, but only add the non minimized ones to the current active workspace\n        UserAppsManager::instance()\n            .interactable_windows\n            .for_each(|data| {\n                let window = Window::from(data.hwnd);\n                if !self.contains(&window) && !window.is_minimized() {\n                    self.add_to_current_workspace(&window);\n                }\n            });\n\n        MonitorManager::subscribe(|e| match e {\n            MonitorManagerEvent::ViewAdded(monitor_id) => {\n                Self::instance()\n                    .monitors\n                    .upsert(monitor_id, VirtualDesktopMonitor::create());\n            }\n            MonitorManagerEvent::ViewRemoved(monitor_id) => {\n                // Todo: move windows to another monitor\n                Self::instance().monitors.remove(&monitor_id);\n            }\n            _ => {}\n        });\n\n        UserAppsManager::subscribe(|event| match event {\n            UserAppWinEvent::Added(addr) => {\n                Self::instance().add_to_current_workspace(&Window::from(addr));\n            }\n            UserAppWinEvent::Removed(addr) => {\n                Self::instance().remove(&Window::from(addr));\n            }\n            _ => {}\n        });\n\n        let eid = HookManager::subscribe(|(event, origin)| {\n            Self::on_win_event(event, origin).log_error();\n        });\n        HookManager::set_event_handler_priority(&eid, 2);\n        Ok(())\n    }\n\n    fn on_win_event(event: WinEvent, window: Window) -> Result<()> {\n        let window_id = window.address();\n        match event {\n            WinEvent::SystemMinimizeEnd => {\n                // Check if the window was restored by our workspace system\n                let mut found = false;\n                RESTORED_EVENT_QUEUE.retain(|w| {\n                    if !found && w == &window_id {\n                        found = true;\n                        return false;\n                    }\n                    true\n                });\n\n                // If found in the queue, it was restored by us, so ignore the event\n                if found {\n                    return Ok(());\n                }\n\n                let manager = Self::instance();\n                if let Ok(workspace_id) = window.workspace_id() {\n                    let monitor_id = manager.get_monitor_of_workspace(&workspace_id);\n                    // Restore workspace if the window was unminimized by the user via alt+tab or others\n                    manager.switch_to_id(&monitor_id, &workspace_id)?;\n                } else if !manager.is_pinned(&window_id) && window.is_interactable_and_not_hidden()\n                {\n                    // Add minimized windows during the scanning, to the current active workspace\n                    manager.add_to_current_workspace(&window);\n                }\n            }\n            WinEvent::SystemForeground | WinEvent::ObjectFocus => {\n                let manager = Self::instance();\n                let mut updated = false;\n\n                // Update z-order: move focused window to end of the list\n                manager.monitors.for_each(|(_, monitor)| {\n                    for workspace in &mut monitor.workspaces {\n                        if workspace.windows.contains(&window_id) {\n                            workspace.windows.retain(|w| w != &window_id);\n                            workspace.windows.push(window_id);\n                            updated = true;\n                        }\n                    }\n                });\n\n                if updated {\n                    manager.request_save();\n                }\n            }\n            WinEvent::SyntheticMonitorChanged => {\n                let manager = Self::instance();\n                if manager.contains(&window) && !manager.is_pinned(&window_id) {\n                    manager.remove(&window);\n                    manager.add_to_current_workspace(&window);\n                }\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    pub fn for_each_workspace<F: Fn(&mut DesktopWorkspace)>(&mut self, f: F) {\n        self.monitors.for_each(|(_, monitor)| {\n            for workspace in &mut monitor.workspaces {\n                f(workspace);\n            }\n        });\n    }\n\n    pub fn get_monitor_of_workspace(&self, workspace_id: &WorkspaceId) -> MonitorId {\n        self.workspace_index\n            .get(workspace_id, |x| x.clone())\n            .expect(\"workspace_index is broken\")\n    }\n\n    pub fn is_pinned(&self, window_id: &isize) -> bool {\n        self.pinned.contains(window_id)\n    }\n\n    fn contains(&self, window: &Window) -> bool {\n        let window_id = window.address();\n        self.is_pinned(&window_id) || {\n            self.monitors.any(|(_, monitor)| {\n                monitor\n                    .workspaces\n                    .iter()\n                    .any(|w| w.windows.contains(&window_id))\n            })\n        }\n    }\n\n    fn add_to_current_workspace(&self, window: &Window) {\n        let window_id = window.address();\n\n        // Get monitor ID with fallback to pinned list\n        let Ok(monitor_id) = window.monitor().stable_id() else {\n            // As fallback we gonna add the window to the pinned list.\n            // If getting monitor id continues to fail, this won't be able to be unpinned.\n            if !self.pinned.contains(&window_id) {\n                log::trace!(\"adding {window} to pinned list\");\n                self.pinned.push(window_id);\n            }\n            return;\n        };\n\n        // Get or create monitor and add window to active workspace\n        let result = self.monitors.get_or_insert(\n            monitor_id.clone(),\n            VirtualDesktopMonitor::create,\n            |monitor| {\n                let active_workspace = monitor.active_workspace_mut();\n                if active_workspace.windows.contains(&window_id) {\n                    return None;\n                }\n\n                log::trace!(\"adding {window} to workspace {}\", active_workspace.id);\n                active_workspace.windows.push(window_id);\n                Some(active_workspace.id.clone())\n            },\n        );\n\n        // Update workspace index and send event outside of the monitor lock\n        if let Some(workspace_id) = result {\n            self.workspace_index\n                .upsert(workspace_id.clone(), monitor_id);\n\n            Self::send(VirtualDesktopEvent::WindowAdded {\n                window: window_id,\n                desktop: workspace_id,\n            });\n            self.request_save();\n        }\n    }\n\n    fn remove(&self, window: &Window) {\n        let window_id = window.address();\n        log::trace!(\"Removing {window} from workspaces\");\n\n        // Remove from pinned list\n        self.pinned.retain(|w| w != &window_id);\n\n        // Remove from all workspaces\n        self.monitors.for_each(|(_, monitor)| {\n            for workspace in &mut monitor.workspaces {\n                workspace.windows.retain(|w| w != &window_id);\n            }\n        });\n\n        Self::send(VirtualDesktopEvent::WindowRemoved { window: window_id });\n        self.request_save();\n    }\n\n    /// Switch to a workspace by ID on a specific monitor\n    pub fn switch_to_id(&self, monitor_id: &MonitorId, workspace_id: &WorkspaceId) -> Result<()> {\n        let changed = self\n            .monitors\n            .get(monitor_id, |monitor| {\n                if monitor.active_workspace_id() == workspace_id {\n                    return Ok(false);\n                }\n\n                monitor.active_workspace().hide(false);\n                monitor.set_active_workspace(workspace_id)?;\n                monitor.active_workspace().restore();\n                Result::Ok(true)\n            })\n            .ok_or(\"Monitor not found\")??;\n\n        if changed {\n            log::trace!(\"Switched to workspace {workspace_id} on monitor {monitor_id}\");\n            Self::send(VirtualDesktopEvent::DesktopChanged {\n                monitor: monitor_id.clone(),\n                workspace: workspace_id.clone(),\n            });\n            self.request_save();\n        }\n\n        Ok(())\n    }\n\n    /// Switch to a workspace by index on a specific monitor\n    pub fn switch_to(&self, monitor_id: &MonitorId, index: usize) -> Result<()> {\n        let workspace_id = self\n            .monitors\n            .get(monitor_id, |monitor| {\n                monitor\n                    .workspaces\n                    .get(index)\n                    .map(|w| w.id.clone())\n                    .ok_or_else(|| format!(\"Workspace index {} not found\", index))\n            })\n            .ok_or(\"Monitor not found\")??;\n        self.switch_to_id(monitor_id, &workspace_id)\n    }\n\n    /// Send a window to a specific workspace\n    pub fn send_to(&self, window: &Window, workspace_id: &WorkspaceId) -> Result<()> {\n        let monitor_id = self.get_monitor_of_workspace(workspace_id);\n        let window_id = window.address();\n\n        // Remove window from current workspace\n        self.monitors.for_each(|(_, monitor)| {\n            for workspace in &mut monitor.workspaces {\n                workspace.windows.retain(|w| w != &window_id);\n            }\n        });\n\n        // Add window to target workspace\n        self.monitors\n            .get(&monitor_id, |monitor| {\n                let target_workspace = monitor\n                    .workspaces\n                    .iter_mut()\n                    .find(|w| &w.id == workspace_id)\n                    .ok_or(\"Workspace not found in monitor\")?;\n\n                target_workspace.windows.push(window_id);\n\n                // Hide window if target workspace is not active\n                if monitor.active_workspace_id() != workspace_id {\n                    window.show_window(SW_MINIMIZE).ok();\n                    let _ = MINIMIZED_BY_WORKSPACES.insert(window_id);\n                }\n\n                Self::send(VirtualDesktopEvent::WindowMoved {\n                    window: window_id,\n                    desktop: workspace_id.clone(),\n                });\n                self.request_save();\n                Ok(())\n            })\n            .ok_or(\"Monitor not found\")?\n    }\n\n    /// Create a new workspace on a specific monitor\n    pub fn create_desktop(&self, monitor_id: &MonitorId) -> Result<WorkspaceId> {\n        let workspace_id = self\n            .monitors\n            .get(monitor_id, |monitor| monitor.add_workspace())\n            .ok_or(\"Monitor not found\")?;\n        self.workspace_index\n            .upsert(workspace_id.clone(), monitor_id.clone());\n\n        // Set wallpaper to the new workspace\n        WorkspaceWallpapersManager::update_workspace_wallpapers_internal(self);\n\n        Self::send(VirtualDesktopEvent::DesktopCreated(workspace_id.clone()));\n        self.request_save();\n        Ok(workspace_id)\n    }\n\n    /// Destroy a workspace on a specific monitor\n    pub fn destroy_desktop(\n        &self,\n        monitor_id: &MonitorId,\n        workspace_id: &WorkspaceId,\n    ) -> Result<()> {\n        self.monitors\n            .get(monitor_id, |monitor| {\n                let was_active = monitor.active_workspace_id() == workspace_id;\n                // Remove the workspace (this moves windows to the previous workspace)\n                monitor.remove_workspace(workspace_id)?;\n                // If the removed workspace was active, restore the new active workspace\n                if was_active {\n                    monitor.active_workspace().restore();\n                }\n                Result::Ok(())\n            })\n            .ok_or(\"Monitor not found\")??;\n\n        // Remove from workspace index\n        self.workspace_index.remove(workspace_id);\n        Self::send(VirtualDesktopEvent::DesktopDestroyed(workspace_id.clone()));\n        self.request_save();\n        Ok(())\n    }\n\n    /// Rename a workspace on a specific monitor\n    pub fn rename_desktop(\n        &self,\n        monitor_id: &MonitorId,\n        workspace_id: &WorkspaceId,\n        name: Option<String>,\n    ) -> Result<()> {\n        self.monitors\n            .get(monitor_id, |monitor| {\n                monitor.rename_workspace(workspace_id, name)\n            })\n            .ok_or(\"Monitor not found\")??;\n        self.request_save();\n        Ok(())\n    }\n}\n\nimpl From<VirtualDesktops> for SluWorkspacesManager2 {\n    fn from(value: VirtualDesktops) -> Self {\n        let mut workspace_index = HashMap::new();\n        for (mid, m) in &value.monitors {\n            for w in &m.workspaces {\n                workspace_index.insert(w.id.clone(), mid.clone());\n            }\n        }\n\n        Self {\n            monitors: SyncHashMap::from(value.monitors),\n            workspace_index: SyncHashMap::from(workspace_index),\n            pinned: SyncVec::from(value.pinned),\n        }\n    }\n}\n\nimpl From<&SluWorkspacesManager2> for VirtualDesktops {\n    fn from(value: &SluWorkspacesManager2) -> Self {\n        Self {\n            monitors: value.monitors.to_hash_map(),\n            pinned: value.pinned.to_vec(),\n        }\n    }\n}\n\npub trait DesktopWorkspaceExt {\n    fn hide(&self, force: bool);\n    fn restore(&self);\n}\n\nimpl DesktopWorkspaceExt for DesktopWorkspace {\n    fn hide(&self, force: bool) {\n        let mode = if force { SW_FORCEMINIMIZE } else { SW_MINIMIZE };\n        for addr in &self.windows {\n            let window = Window::from(*addr);\n            if window.is_window() && !window.is_minimized() {\n                let _ = MINIMIZED_BY_WORKSPACES.insert(window.address());\n                log_error!(window.show_window(mode));\n            }\n        }\n    }\n\n    fn restore(&self) {\n        let len = self.windows.len();\n        for (idx, addr) in self.windows.iter().enumerate() {\n            let window = Window::from(*addr);\n            let is_minimized = window.is_minimized();\n\n            // avoid restore windows manually minimized by the user\n            if is_minimized && !MINIMIZED_BY_WORKSPACES.contains(addr) {\n                continue;\n            }\n\n            if is_minimized {\n                // Push before show_window to avoid a race where SystemMinimizeEnd\n                // fires on the hook thread before this thread reaches the push.\n                RESTORED_EVENT_QUEUE.push(*addr);\n                // use normal show instead async cuz it will keep the order of restoring\n                log_error!(window.show_window(SW_RESTORE));\n            }\n            MINIMIZED_BY_WORKSPACES.remove(addr);\n\n            // ensure correct focus\n            if idx == len - 1 {\n                log_error!(window.focus());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/virtual_desktops/wallpapers.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::{Arc, LazyLock, OnceLock};\nuse std::time::Duration;\n\nuse parking_lot::RwLock;\nuse rand::Rng;\nuse seelen_core::handlers::SeelenEvent;\nuse seelen_core::resource::WallpaperId;\nuse seelen_core::state::{WallpaperCollection, WorkspaceId};\nuse seelen_core::system_state::MonitorId;\nuse tauri::Listener;\nuse tokio::sync::mpsc;\nuse uuid::Uuid;\n\nuse crate::app::get_app_handle;\nuse crate::error::{Result, ResultLogExt};\nuse crate::get_tokio_handle;\nuse crate::state::application::FULL_STATE;\n\nuse super::events::VirtualDesktopEvent;\n\n/// Tracks the current wallpaper index for each collection\nstatic COLLECTION_INDICES: LazyLock<Arc<RwLock<HashMap<Uuid, usize>>>> =\n    LazyLock::new(|| Arc::new(RwLock::new(HashMap::new())));\n\nstatic MANUAL_CHANGE_SENDER: OnceLock<mpsc::UnboundedSender<ChangeDirection>> = OnceLock::new();\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum ChangeDirection {\n    Next,\n    Previous,\n}\n\npub struct WorkspaceWallpapersManager;\nimpl WorkspaceWallpapersManager {\n    pub fn init(manager: &super::SluWorkspacesManager2) {\n        log::trace!(\"Initializing Workspaces Wallpaper Manager\");\n\n        // Set initial wallpapers for all workspaces\n        Self::update_workspace_wallpapers_internal(manager);\n\n        // Start rotation loop\n        let (tx, rx) = mpsc::unbounded_channel();\n        MANUAL_CHANGE_SENDER.set(tx).ok();\n        get_tokio_handle().spawn(async move {\n            Self::rotation_loop(rx).await.log_error();\n        });\n\n        // Listen for settings changes\n        get_app_handle().listen(SeelenEvent::StateSettingsChanged, |_| {\n            Self::on_settings_changed();\n        });\n    }\n\n    /// Handle settings changes\n    fn on_settings_changed() {\n        let vd_manager = super::SluWorkspacesManager2::instance();\n        Self::update_workspace_wallpapers_internal(vd_manager);\n        super::SluWorkspacesManager2::send(VirtualDesktopEvent::StateChanged);\n    }\n\n    /// Get the wallpaper collection ID for a given workspace on a monitor\n    /// Priority: workspace collection → monitor collection → global collection → None\n    fn get_collection_id(monitor_id: &MonitorId, workspace_id: &WorkspaceId) -> Option<Uuid> {\n        let state = FULL_STATE.load();\n\n        // Try to get workspace collection\n        if let Some(monitor_config) = state.settings.monitors_v3.get(monitor_id) {\n            if let Some(workspace_config) = monitor_config.by_workspace.get(workspace_id) {\n                if let Some(collection_id) = workspace_config.wallpaper_collection {\n                    return Some(collection_id);\n                }\n            }\n\n            // Try to get monitor collection\n            if let Some(collection_id) = monitor_config.wallpaper_collection {\n                return Some(collection_id);\n            }\n        }\n\n        // Try to get global default collection\n        state.settings.by_widget.wall.default_collection\n    }\n\n    /// Get the collection by UUID\n    fn get_collection(collection_id: &Uuid) -> Option<WallpaperCollection> {\n        let state = FULL_STATE.load();\n        state\n            .settings\n            .wallpaper_collections\n            .iter()\n            .find(|c| &c.id == collection_id)\n            .cloned()\n    }\n\n    /// Get the current wallpaper for a workspace\n    pub fn get_current_wallpaper(\n        monitor_id: &MonitorId,\n        workspace_id: &WorkspaceId,\n    ) -> Option<WallpaperId> {\n        let collection_id = Self::get_collection_id(monitor_id, workspace_id)?;\n        let collection = Self::get_collection(&collection_id)?;\n\n        if collection.wallpapers.is_empty() {\n            return None;\n        }\n\n        let indices = COLLECTION_INDICES.read();\n        let index = indices.get(&collection_id).copied().unwrap_or(0);\n        let wallpaper_index = index % collection.wallpapers.len();\n\n        collection.wallpapers.get(wallpaper_index).cloned()\n    }\n\n    /// Increment the index for a collection\n    fn increment_collection_index(collection_id: &Uuid, direction: ChangeDirection) {\n        let collection = match Self::get_collection(collection_id) {\n            Some(c) => c,\n            None => return,\n        };\n\n        if collection.wallpapers.is_empty() {\n            return;\n        }\n\n        let state = FULL_STATE.load();\n        // Randomize only makes sense if there are more than 2 wallpapers\n        let randomize = state.settings.by_widget.wall.randomize && collection.wallpapers.len() > 2;\n\n        let mut indices = COLLECTION_INDICES.write();\n        let current_index = indices.get(collection_id).copied().unwrap_or(0);\n\n        let new_index = if randomize {\n            let mut rng = rand::rng();\n            loop {\n                let random_index = rng.random_range(0..collection.wallpapers.len());\n                if random_index != current_index {\n                    break random_index;\n                }\n            }\n        } else {\n            match direction {\n                ChangeDirection::Next => (current_index + 1) % collection.wallpapers.len(),\n                ChangeDirection::Previous => {\n                    if current_index == 0 {\n                        collection.wallpapers.len() - 1\n                    } else {\n                        current_index - 1\n                    }\n                }\n            }\n        };\n\n        indices.insert(*collection_id, new_index);\n    }\n\n    /// Update wallpapers for all workspaces\n    fn update_all_wallpapers(direction: ChangeDirection) {\n        let state = FULL_STATE.load();\n\n        // Collect all unique collection IDs being used\n        let mut active_collections = std::collections::HashSet::new();\n\n        for monitor in state.settings.monitors_v3.values() {\n            // Add monitor-level collection if it exists\n            if let Some(collection_id) = monitor.wallpaper_collection {\n                active_collections.insert(collection_id);\n            }\n\n            // Add workspace-level collections\n            for workspace_config in monitor.by_workspace.values() {\n                if let Some(collection_id) = workspace_config.wallpaper_collection {\n                    active_collections.insert(collection_id);\n                }\n            }\n        }\n\n        // Add global default collection if it exists\n        if let Some(collection_id) = state.settings.by_widget.wall.default_collection {\n            active_collections.insert(collection_id);\n        }\n\n        // Increment index for all active collections\n        for collection_id in active_collections {\n            Self::increment_collection_index(&collection_id, direction);\n        }\n\n        {\n            let vd_manager = super::SluWorkspacesManager2::instance();\n            Self::update_workspace_wallpapers_internal(vd_manager);\n            super::SluWorkspacesManager2::send(VirtualDesktopEvent::StateChanged);\n        }\n    }\n\n    /// Update wallpaper IDs in all workspaces (internal method with manager reference)\n    pub(super) fn update_workspace_wallpapers_internal(vd_manager: &super::SluWorkspacesManager2) {\n        vd_manager.monitors.for_each(|(monitor_id, monitor)| {\n            for workspace in &mut monitor.workspaces {\n                let wallpaper_id = Self::get_current_wallpaper(monitor_id, &workspace.id);\n                workspace.wallpaper = wallpaper_id;\n            }\n        });\n    }\n\n    /// Get the interval duration from settings (in seconds)\n    fn get_interval_duration() -> Duration {\n        let state = FULL_STATE.load();\n        let interval_seconds = (state.settings.by_widget.wall.interval as u64).max(60); // At least 1 minute\n        Duration::from_secs(interval_seconds)\n    }\n\n    /// Main rotation loop\n    async fn rotation_loop(mut rx: mpsc::UnboundedReceiver<ChangeDirection>) -> Result<()> {\n        loop {\n            let interval = Self::get_interval_duration();\n\n            // Wait for either the interval to elapse or a manual change to be triggered\n            tokio::select! {\n                _ = tokio::time::sleep(interval) => {\n                    log::trace!(\"Automatic wallpaper rotation triggered\");\n                    Self::update_all_wallpapers(ChangeDirection::Next);\n                }\n                direction = rx.recv() => {\n                    if let Some(direction) = direction {\n                        log::trace!(\"Manual wallpaper change triggered: {:?}\", direction);\n                        Self::update_all_wallpapers(direction);\n                    }\n                }\n            }\n        }\n    }\n\n    /// Manually advance to next wallpaper\n    pub fn next() {\n        if let Some(sender) = MANUAL_CHANGE_SENDER.get() {\n            if let Err(e) = sender.send(ChangeDirection::Next) {\n                log::warn!(\"Failed to send next wallpaper command: {}\", e);\n            }\n        }\n    }\n\n    /// Manually go to previous wallpaper\n    pub fn previous() {\n        if let Some(sender) = MANUAL_CHANGE_SENDER.get() {\n            if let Err(e) = sender.send(ChangeDirection::Previous) {\n                log::warn!(\"Failed to send previous wallpaper command: {}\", e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/cli.rs",
    "content": "use clap::{Args, Subcommand};\nuse seelen_core::state::WidgetTriggerPayload;\n\nuse crate::{error::Result, widgets::trigger_widget};\n\n#[derive(Debug, Args)]\npub struct WidgetCli {\n    #[command(subcommand)]\n    command: WidgetCommand,\n}\n\n#[derive(Debug, Subcommand)]\nenum WidgetCommand {\n    /// Triggers a widget\n    Trigger { widget_id: String },\n}\n\nimpl WidgetCli {\n    pub fn run(&self) -> Result<()> {\n        match &self.command {\n            WidgetCommand::Trigger { widget_id } => {\n                trigger_widget(WidgetTriggerPayload::new(widget_id.clone().into()))?;\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/loader.rs",
    "content": "use std::{\n    sync::{atomic::AtomicU8, Arc},\n    time::Duration,\n};\n\nuse seelen_core::state::{Widget, WidgetInstanceMode, WidgetStatus};\nuse tauri::{Emitter, Listener};\nuse tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};\nuse uuid::Uuid;\n\nuse crate::{\n    app::get_app_handle,\n    error::ResultLogExt,\n    get_tokio_handle,\n    resources::RESOURCES,\n    state::application::FULL_STATE,\n    utils::lock_free::SyncHashMap,\n    widgets::{manager::WIDGET_MANAGER, webview::WidgetWebview, WidgetWebviewLabel},\n};\n\nconst LIVENESS_PROVE_INTERVAL: Duration = Duration::from_secs(5);\nconst LIVENESS_PROVE_WAIT_TIMEOUT: Duration = Duration::from_secs(2);\nconst LIVENESS_PROVE_MAX_RETRIES: u8 = 5;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum InstanceType {\n    /// Instances defined in user settings\n    Static,\n    /// Instances created dynamically during runtime\n    Runtime,\n}\n\npub struct WidgetContainer {\n    pub definition: Arc<Widget>,\n    pub instances: SyncHashMap<WidgetWebviewLabel, WidgetInstance>,\n}\n\nimpl WidgetContainer {\n    pub fn create(widget: Arc<Widget>) -> Self {\n        let instances = SyncHashMap::new();\n\n        match widget.instances {\n            WidgetInstanceMode::Single => {\n                let instance = WidgetInstance::create(&widget, None, None, InstanceType::Static);\n                instances.upsert(instance.label.clone(), instance);\n            }\n            WidgetInstanceMode::Multiple => {\n                for replica_id in FULL_STATE.load().get_widget_instances_ids(&widget.id) {\n                    let instance = WidgetInstance::create(\n                        &widget,\n                        None,\n                        Some(&replica_id),\n                        InstanceType::Static,\n                    );\n                    instances.upsert(instance.label.clone(), instance);\n                }\n            }\n            WidgetInstanceMode::ReplicaByMonitor => {}\n        }\n\n        Self {\n            definition: widget,\n            instances,\n        }\n    }\n\n    pub fn start_all_webviews(&self) {\n        self.instances.for_each(|(_k, v)| {\n            v.start_webview(&self.definition);\n        });\n    }\n\n    pub fn start_webview(&self, label: &WidgetWebviewLabel) {\n        self.instances.get(label, |instance| {\n            instance.start_webview(&self.definition);\n        });\n    }\n\n    pub fn create_runtime_instance(&self, instance_id: &Uuid) {\n        let instance = WidgetInstance::create(\n            &self.definition,\n            None,\n            Some(instance_id),\n            InstanceType::Runtime,\n        );\n        self.instances.upsert(instance.label.clone(), instance);\n    }\n}\n\npub struct WidgetInstance {\n    pub label: WidgetWebviewLabel,\n    pub instance_type: InstanceType,\n    window: Option<WidgetWebview>,\n    _status: WidgetStatus,\n\n    live: Arc<tokio::sync::Notify>,\n    retries: Arc<AtomicU8>,\n}\n\nimpl WidgetInstance {\n    fn create(\n        widget: &Widget,\n        monitor_id: Option<&str>,\n        instance_id: Option<&Uuid>,\n        instance_type: InstanceType,\n    ) -> Self {\n        let label = WidgetWebviewLabel::new(&widget.id, monitor_id, instance_id);\n        log::info!(\"Starting widget instance: {label}\");\n        Self {\n            label,\n            instance_type,\n            window: None,\n            _status: WidgetStatus::Pending,\n            live: Arc::new(tokio::sync::Notify::new()),\n            retries: Arc::new(AtomicU8::new(0)),\n        }\n    }\n\n    pub fn status(&self) -> &WidgetStatus {\n        &self._status\n    }\n\n    pub fn set_status(&mut self, status: WidgetStatus) {\n        log::trace!(\"{} status changed to: {:?}\", self.label, status);\n        self._status = status;\n    }\n\n    pub fn is_ready(&self) -> bool {\n        self.window.is_some() && self.status() == &WidgetStatus::Ready\n    }\n\n    fn start_webview(&mut self, definition: &Widget) {\n        if self.status() != &WidgetStatus::Pending {\n            return;\n        }\n\n        self.set_status(WidgetStatus::Creating);\n        let window = match WidgetWebview::create(definition, &self.label) {\n            Ok(window) => window,\n            Err(err) => {\n                log::error!(\"Failed to create webview: {}\", err);\n                self.set_status(WidgetStatus::CrashedOnCreation);\n                return;\n            }\n        };\n        self.set_status(WidgetStatus::Mounting);\n\n        let live = self.live.clone();\n        let label = self.label.clone();\n        let retries = self.retries.clone();\n        let liveness_prove = get_tokio_handle().spawn(async move {\n            let app = get_app_handle();\n\n            loop {\n                tokio::time::sleep(LIVENESS_PROVE_INTERVAL).await;\n                let _ = app.emit_to(&label.raw, \"internal::liveness-ping\", ());\n\n                tokio::select! {\n                    _ = live.notified() => {\n                        // log::trace!(\"Liveness prove succeeded for {label}\");\n                    }\n                    _ = tokio::time::sleep(LIVENESS_PROVE_WAIT_TIMEOUT) => {\n                        log::warn!(\"Liveness prove failed for {label}, reloading webview.\");\n\n                        let attempt = retries.fetch_add(1, std::sync::atomic::Ordering::SeqCst);\n                        if attempt < LIVENESS_PROVE_MAX_RETRIES {\n                            WIDGET_MANAGER.groups.get(&label.widget_id, |c| {\n                                c.instances.get(&label, |w| {\n                                    w.set_status(WidgetStatus::Pending);\n                                    if let Some(window) = &w.window {\n                                        window.0.reload().log_error();\n                                    }\n                                });\n                            });\n                        } else {\n                            log::error!(\"Liveness prove failed for {label} too many times, giving up.\");\n                            let lang = rust_i18n::locale();\n                            let widget_name = RESOURCES\n                                .widgets\n                                .read(&label.widget_id, |_, w| w.metadata.display_name.get(&lang).to_string())\n                                .unwrap_or_else(|| label.widget_id.to_string());\n                            app.dialog()\n                                .message(t!(\"widget_liveness.failed_description\", widget_name = widget_name))\n                                .title(t!(\"widget_liveness.failed_title\"))\n                                .kind(MessageDialogKind::Error)\n                                .buttons(MessageDialogButtons::Ok)\n                                .show(|_| {});\n                            break;\n                        }\n                    }\n                }\n            }\n        });\n\n        let label = self.label.clone();\n        let instance_type = self.instance_type;\n        window.0.on_window_event(move |event| {\n            if let tauri::WindowEvent::Destroyed = event {\n                liveness_prove.abort();\n                WIDGET_MANAGER.groups.get(&label.widget_id, |c| {\n                    match instance_type {\n                        // Remove runtime instances on destroy\n                        InstanceType::Runtime => {\n                            c.instances.remove(&label);\n                        }\n                        // Reset static instances to pending state\n                        InstanceType::Static => {\n                            c.instances.get(&label, |w| {\n                                w.window = None;\n                                w.retries.store(0, std::sync::atomic::Ordering::SeqCst);\n                                w.set_status(WidgetStatus::Pending);\n                            });\n                        }\n                    }\n                });\n            }\n        });\n\n        let live = self.live.clone();\n        window.0.listen(\"internal::liveness-pong\", move |_event| {\n            live.notify_waiters();\n        });\n\n        self.window = Some(window);\n    }\n}\n\nimpl Drop for WidgetInstance {\n    fn drop(&mut self) {\n        log::info!(\"Dropping {}\", self.label);\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/manager.rs",
    "content": "use std::sync::LazyLock;\n\nuse seelen_core::{\n    resource::WidgetId,\n    state::{WidgetLoader, WidgetStatus},\n};\n\nuse crate::{\n    error::Result,\n    resources::RESOURCES,\n    state::application::FULL_STATE,\n    utils::lock_free::SyncHashMap,\n    widgets::{loader::WidgetContainer, WidgetWebviewLabel},\n};\n\npub static WIDGET_MANAGER: LazyLock<WidgetManager> = LazyLock::new(WidgetManager::create);\n\npub struct WidgetManager {\n    /// group of widgets instances by widget resource id\n    pub groups: SyncHashMap<WidgetId, WidgetContainer>,\n}\n\nimpl WidgetManager {\n    fn create() -> Self {\n        Self {\n            groups: SyncHashMap::new(),\n        }\n    }\n\n    pub fn is_ready(&self, label: &WidgetWebviewLabel) -> bool {\n        self.groups\n            .get(&label.widget_id, |c| {\n                c.instances.any(|(k, i)| k == label && i.is_ready())\n            })\n            .unwrap_or(false)\n    }\n\n    pub fn set_status(&self, label: &WidgetWebviewLabel, status: WidgetStatus) {\n        self.groups.get(&label.widget_id, |c| {\n            c.instances.get(label, |instance| {\n                instance.set_status(status);\n            });\n        });\n    }\n\n    pub fn refresh(&self) -> Result<()> {\n        // remove deleted resources\n        self.groups.retain(|key, _| RESOURCES.widgets.contains(key));\n\n        let mut filtered = Vec::new();\n        RESOURCES.widgets.scan(|k, w| {\n            if w.loader != WidgetLoader::Legacy {\n                filtered.push((k.clone(), w.clone()));\n            }\n        });\n\n        let state = FULL_STATE.load();\n        for (id, widget) in filtered {\n            if !state.is_widget_enabled(&id) {\n                self.groups.remove(&id);\n                continue;\n            }\n\n            if !self.groups.contains_key(&id) {\n                self.groups\n                    .upsert(id.clone(), WidgetContainer::create(widget));\n            }\n        }\n\n        // lazy creation of webviews to reduce startup time\n        std::thread::spawn(|| {\n            WIDGET_MANAGER.groups.for_each(|(_, container)| {\n                if !container.definition.lazy {\n                    container.start_all_webviews();\n                }\n            });\n        });\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/mod.rs",
    "content": "pub mod cli;\npub mod loader;\npub mod manager;\npub mod permissions;\npub mod popups;\npub mod task_switcher;\npub mod toolbar;\npub mod wallpaper_manager;\npub mod webview;\npub mod weg;\npub mod window_manager;\n\nuse std::{\n    path::{Path, PathBuf},\n    sync::LazyLock,\n};\n\nuse seelen_core::{\n    handlers::SeelenEvent,\n    resource::ResourceId,\n    state::{context_menu::ContextMenu, WidgetInstanceMode, WidgetStatus, WidgetTriggerPayload},\n    Rect,\n};\nuse tauri::Emitter;\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    resources::RESOURCES,\n    state::application::FULL_STATE,\n    utils::{atomic_write_file, constants::SEELEN_COMMON, lock_free::SyncHashMap},\n    widgets::{manager::WIDGET_MANAGER, webview::WidgetWebviewLabel},\n    windows_api::{\n        input::{Keyboard, Mouse},\n        WindowsApi,\n    },\n};\n\nstatic PENDING_TRIGGERS: LazyLock<SyncHashMap<WidgetWebviewLabel, WidgetTriggerPayload>> =\n    LazyLock::new(SyncHashMap::new);\n\n#[tauri::command(async)]\npub fn set_current_widget_status(\n    webview: tauri::WebviewWindow,\n    status: WidgetStatus,\n) -> Result<()> {\n    let label = WidgetWebviewLabel::try_from_raw(webview.label())?;\n    WIDGET_MANAGER.set_status(&label, status);\n\n    if let Some(pending) = PENDING_TRIGGERS.remove(&label) {\n        get_app_handle().emit_to(label.raw, SeelenEvent::WidgetTriggered, &pending)?;\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn trigger_widget(mut payload: WidgetTriggerPayload) -> Result<()> {\n    let state = FULL_STATE.load();\n    if !state.is_widget_enabled(&payload.id) {\n        return Err(\"Can't trigger a disabled widget\".into());\n    }\n\n    let widget = RESOURCES\n        .widgets\n        .get(&payload.id)\n        .ok_or(\"Widget not found\")?\n        .clone();\n\n    let monitor_id = payload.monitor_id.as_ref().map(|id| id.to_string());\n    let label = WidgetWebviewLabel::new(\n        &payload.id,\n        monitor_id.as_deref(),\n        payload.instance_id.as_ref(),\n    );\n\n    match widget.instances {\n        WidgetInstanceMode::Single => {}\n        WidgetInstanceMode::ReplicaByMonitor => {\n            if payload.monitor_id.is_none() {\n                return Err(\"Monitor id is required for replica by monitor widgets\".into());\n            }\n        }\n        WidgetInstanceMode::Multiple => {\n            let Some(instance_id) = &payload.instance_id else {\n                return Err(\"Instance id is required for multiple instance widgets\".into());\n            };\n\n            WIDGET_MANAGER.groups.get(&payload.id, |container| {\n                if !container.instances.contains_key(&label) {\n                    container.create_runtime_instance(instance_id);\n                }\n            });\n        }\n    }\n\n    if payload.desired_position.is_none() {\n        payload.desired_position = Some(Mouse::get_cursor_pos()?);\n    }\n\n    if !WIDGET_MANAGER.is_ready(&label) {\n        log::warn!(\"Trigger postponed, because widget instance is not ready: {label}\");\n        PENDING_TRIGGERS.upsert(label.clone(), payload);\n\n        WIDGET_MANAGER.groups.get(&label.widget_id, |c| {\n            c.start_webview(&label);\n        });\n        return Ok(());\n    }\n\n    get_app_handle().emit_to(label.raw, SeelenEvent::WidgetTriggered, &payload)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn trigger_context_menu(\n    webview: tauri::WebviewWindow,\n    menu: ContextMenu,\n    forward_to: Option<String>,\n) -> Result<()> {\n    let owner = WidgetWebviewLabel::try_from_raw(webview.label())?;\n\n    let mut payload = WidgetTriggerPayload::new(\"@seelen/context-menu\".into());\n    payload.instance_id = Some(menu.identifier);\n    payload.align_x = menu.align_x;\n    payload.align_y = menu.align_y;\n\n    payload.add_custom_arg(\"menu\", serde_json::to_value(menu)?);\n    payload.add_custom_arg(\"owner\", serde_json::to_value(&owner.raw)?);\n    payload.add_custom_arg(\n        \"forwardTo\",\n        serde_json::to_value(forward_to.unwrap_or(owner.raw))?,\n    );\n    trigger_widget(payload)\n}\n\n#[tauri::command(async)]\npub fn get_self_window_handle(webview: tauri::WebviewWindow) -> Result<isize> {\n    Ok(webview.hwnd()?.0 as isize)\n}\n\n#[tauri::command(async)]\npub fn set_self_position(webview: tauri::WebviewWindow, rect: Rect) -> Result<()> {\n    use windows::Win32::Foundation::{HWND, RECT};\n    use windows::Win32::Graphics::Gdi::*;\n    use windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;\n\n    let hwnd = HWND(webview.hwnd()?.0);\n    let rect = RECT {\n        left: rect.left,\n        top: rect.top,\n        right: rect.right,\n        bottom: rect.bottom,\n    };\n\n    // pre set position for resize in case of multiples dpi\n    WindowsApi::move_window(hwnd, &rect)?;\n    WindowsApi::set_position(hwnd, None, &rect, SWP_ASYNCWINDOWPOS)?;\n    // ensure child windows are redrawn\n    unsafe {\n        let _ = RedrawWindow(\n            Some(hwnd),\n            None,\n            None,\n            RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME | RDW_ERASE,\n        );\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn bring_self_to_top(webview: tauri::WebviewWindow) -> Result<()> {\n    use windows::Win32::Foundation::HWND;\n    let hwnd = HWND(webview.hwnd()?.0);\n    WindowsApi::bring_to_top(hwnd)\n}\n\npub fn show_settings() -> Result<()> {\n    trigger_widget(WidgetTriggerPayload::new(\"@seelen/settings\".into()))\n}\n\n#[tauri::command(async)]\npub fn show_start_menu() -> Result<()> {\n    let guard = FULL_STATE.load();\n    if guard.is_widget_enabled(&\"@seelen/apps-menu\".into()) {\n        trigger_widget(WidgetTriggerPayload::new(\"@seelen/apps-menu\".into()))?;\n        return Ok(());\n    }\n    // trick for showing the native start menu\n    Keyboard::new().send_keys(\"{win}\")\n}\n\n#[tauri::command(async)]\npub fn write_data_file(\n    webview: tauri::WebviewWindow,\n    filename: String,\n    content: String,\n) -> Result<()> {\n    let base_path = widget_data_dir(&webview)?;\n    let path = resolve_safe_path(&base_path, &filename)?;\n    atomic_write_file(&path, content.as_bytes())?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn read_data_file(webview: tauri::WebviewWindow, filename: String) -> Result<String> {\n    let base_path = widget_data_dir(&webview)?;\n    let path = resolve_safe_path(&base_path, &filename)?;\n    Ok(std::fs::read_to_string(path)?)\n}\n\nfn widget_data_dir(webview: &tauri::WebviewWindow) -> Result<PathBuf> {\n    let label = WidgetWebviewLabel::try_from_raw(webview.label())?;\n    let data_dir = SEELEN_COMMON.app_data_dir().join(\"data\");\n\n    let folder = match &*label.widget_id {\n        ResourceId::Local(id) => id.trim_start_matches(\"@\").replace(\"/\", \"-\"),\n        ResourceId::Remote(uuid) => uuid.to_string(),\n    };\n\n    let path = data_dir.join(folder);\n    std::fs::create_dir_all(&path)?;\n    Ok(path)\n}\n\nfn resolve_safe_path(base: &Path, filename: &str) -> Result<PathBuf> {\n    let filename_path = PathBuf::from(filename);\n\n    if filename_path.is_absolute() {\n        return Err(\"Absolute paths are not allowed\".into());\n    }\n\n    let joined = base.join(filename_path);\n    let base_canon = base.canonicalize()?;\n\n    let target_canon = joined.canonicalize().or_else(|_| {\n        // if file does not exist, canonicalize the parent\n        let parent = joined.parent().ok_or(\"Invalid path\")?;\n        let parent_canon = parent.canonicalize()?;\n        Result::Ok(parent_canon.join(joined.file_name().ok_or(\"Invalid filename\")?))\n    })?;\n\n    if !target_canon.starts_with(&base_canon) {\n        return Err(\"Path traversal attempt detected >:(\".into());\n    }\n\n    Ok(target_canon)\n}\n"
  },
  {
    "path": "src/background/widgets/permissions.rs",
    "content": "use std::{collections::HashMap, path::PathBuf, sync::LazyLock};\n\nuse parking_lot::Mutex;\nuse seelen_core::resource::WidgetId;\nuse serde::{Deserialize, Serialize};\nuse tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};\n\nuse crate::{\n    app::get_app_handle,\n    error::{Result, ResultLogExt},\n    resources::RESOURCES,\n    utils::constants::SEELEN_COMMON,\n    widgets::webview::WidgetWebviewLabel,\n};\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/// Commands that require explicit permission for third-party widgets.\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\npub enum WidgetPerm {\n    Run,\n    OpenFile,\n}\n\nimpl WidgetPerm {\n    fn i18n_label(&self) -> String {\n        match self {\n            WidgetPerm::Run => t!(\"widget_permissions.perm_run\"),\n            WidgetPerm::OpenFile => t!(\"widget_permissions.perm_open_file\"),\n        }\n        .to_string()\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\npub enum WidgetPermState {\n    Allowed,\n    Denied,\n}\n\n#[derive(Default, Serialize, Deserialize)]\npub struct WidgetPermissions(HashMap<WidgetId, HashMap<WidgetPerm, WidgetPermState>>);\n\n// =============================================================================\n// Manager\n// =============================================================================\n\npub struct PermissionsManager {\n    data: Mutex<WidgetPermissions>,\n    /// Serializes dialog display so only one permission dialog appears at a time.\n    dialog_lock: Mutex<()>,\n    path: PathBuf,\n}\n\npub static WIDGET_PERMISSIONS: LazyLock<PermissionsManager> = LazyLock::new(|| {\n    let path = SEELEN_COMMON.app_data_dir().join(\"permissions.json\");\n    PermissionsManager::load(path)\n});\n\nimpl PermissionsManager {\n    fn load(path: PathBuf) -> Self {\n        let data = std::fs::File::open(&path)\n            .ok()\n            .and_then(|f| serde_json::from_reader(f).ok())\n            .unwrap_or_default();\n\n        Self {\n            data: Mutex::new(data),\n            dialog_lock: Mutex::new(()),\n            path,\n        }\n    }\n\n    fn save(&self) -> Result<()> {\n        let data = self.data.lock();\n        let file = std::fs::File::create(&self.path)?;\n        let writer = std::io::BufWriter::new(file);\n        serde_json::to_writer_pretty(writer, &*data)?;\n        Ok(())\n    }\n\n    /// Returns `Some(true)` if previously allowed, `Some(false)` if denied, `None` if unknown.\n    fn is_resolved(&self, widget_id: &WidgetId, command: &WidgetPerm) -> Option<bool> {\n        let data = self.data.lock();\n        match data.0.get(widget_id).and_then(|perms| perms.get(command)) {\n            Some(WidgetPermState::Allowed) => Some(true),\n            Some(WidgetPermState::Denied) => Some(false),\n            None => None,\n        }\n    }\n\n    fn persist_decision(&self, widget_id: WidgetId, command: WidgetPerm, granted: bool) {\n        let state = if granted {\n            WidgetPermState::Allowed\n        } else {\n            WidgetPermState::Denied\n        };\n        let mut data = self.data.lock();\n        data.0.entry(widget_id).or_default().insert(command, state);\n    }\n\n    /// Main entry point. Grants permission immediately for bundled widgets.\n    /// For third-party widgets checks the stored decision or prompts the user.\n    pub fn request(&self, widget_id: &WidgetId, command: WidgetPerm) -> Result<()> {\n        // Bundled widgets always have permission.\n        if RESOURCES\n            .widgets\n            .read(widget_id, |_, w| w.metadata.internal.bundled)\n            .unwrap_or(false)\n        {\n            return Ok(());\n        }\n\n        // Fast path: decision already recorded.\n        if let Some(granted) = self.is_resolved(widget_id, &command) {\n            return Self::decision_to_result(granted, widget_id, &command);\n        }\n\n        // Slow path: show dialog (serialized so only one dialog appears at a time).\n        let _dialog_guard = self.dialog_lock.lock();\n\n        // Re-check after acquiring the lock – another thread may have resolved it.\n        if let Some(granted) = self.is_resolved(widget_id, &command) {\n            return Self::decision_to_result(granted, widget_id, &command);\n        }\n\n        let lang = rust_i18n::locale();\n        let widget_name = RESOURCES\n            .widgets\n            .read(widget_id, |_, w| {\n                w.metadata.display_name.get(&lang).to_string()\n            })\n            .unwrap_or_else(|| widget_id.to_string());\n\n        let message = t!(\n            \"widget_permissions.request_description\",\n            widget_name = widget_name,\n            command = command.i18n_label()\n        );\n\n        let granted = get_app_handle()\n            .dialog()\n            .message(message)\n            .title(t!(\"widget_permissions.request_title\"))\n            .kind(MessageDialogKind::Warning)\n            .buttons(MessageDialogButtons::YesNo)\n            .blocking_show();\n\n        self.persist_decision(widget_id.clone(), command.clone(), granted);\n        self.save().log_error();\n\n        Self::decision_to_result(granted, widget_id, &command)\n    }\n\n    fn decision_to_result(granted: bool, widget_id: &WidgetId, command: &WidgetPerm) -> Result<()> {\n        if granted {\n            Ok(())\n        } else {\n            Err(format!(\n                \"Widget '{}' does not have permission to '{}'.\",\n                widget_id,\n                command.i18n_label()\n            )\n            .into())\n        }\n    }\n}\n\n// =============================================================================\n// Public helper for Tauri command handlers\n// =============================================================================\n\n/// Resolves the calling widget from the webview label and checks (or requests)\n/// permission for `command`. Returns `Ok(())` if access is granted.\npub fn request_widget_permission(\n    webview: &tauri::WebviewWindow,\n    command: WidgetPerm,\n) -> Result<()> {\n    let label = WidgetWebviewLabel::try_from_raw(webview.label())\n        .map_err(|_| \"Permission denied: caller is not a widget webview.\")?;\n    WIDGET_PERMISSIONS.request(&label.widget_id, command)\n}\n\n/// Dev-only command: simulates a permission request for any widget ID and perm.\n/// Follows the same flow as a real request (checks cache, shows dialog, persists result).\n#[tauri::command(async)]\npub fn simulate_perm(widget_id: String, perm: WidgetPerm) -> Result<()> {\n    WIDGET_PERMISSIONS.request(&WidgetId::from(widget_id.as_str()), perm)\n}\n"
  },
  {
    "path": "src/background/widgets/popups/cli.rs",
    "content": "use serde::{Deserialize, Serialize};\n\nuse crate::{error::Result, widgets::popups::shortcut_registering::set_registering_shortcut};\n\n/// Manage the Seelen Popups.\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\npub struct PopupsCli {\n    #[command(subcommand)]\n    pub subcommand: PopupsCommand,\n}\n\n#[derive(Debug, Serialize, Deserialize, clap::Subcommand)]\npub enum PopupsCommand {\n    Create {\n        /// json config\n        config: String,\n    },\n    Update {\n        /// id\n        id: String,\n        /// json config\n        config: String,\n    },\n    Close {\n        /// id\n        id: String,\n    },\n    #[command(hide = true)]\n    InternalSetShortcut { json: String },\n}\n\nimpl PopupsCli {\n    pub fn process(self) -> Result<()> {\n        self.subcommand.process()\n    }\n}\n\nimpl PopupsCommand {\n    pub fn process(self) -> Result<()> {\n        #[allow(clippy::single_match)]\n        match self {\n            PopupsCommand::InternalSetShortcut { json } => {\n                let shortcut: Option<Vec<String>> = serde_json::from_str(&json)?;\n                set_registering_shortcut(shortcut)?;\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/popups/handlers.rs",
    "content": "use seelen_core::state::SluPopupConfig;\nuse uuid::Uuid;\n\nuse crate::error::Result;\n\nuse super::POPUPS_MANAGER;\n\n#[tauri::command(async)]\npub fn get_popup_config(instance_id: Uuid) -> Option<SluPopupConfig> {\n    POPUPS_MANAGER.lock().configs.get(&instance_id).cloned()\n}\n\n#[tauri::command(async)]\npub fn create_popup(config: SluPopupConfig) -> Result<Uuid> {\n    POPUPS_MANAGER.lock().create(config)\n}\n\n#[tauri::command(async)]\npub fn update_popup(instance_id: Uuid, config: SluPopupConfig) -> Result<()> {\n    POPUPS_MANAGER.lock().update(&instance_id, config)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn close_popup(instance_id: Uuid) -> Result<()> {\n    POPUPS_MANAGER.lock().close_popup(&instance_id)?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/widgets/popups/mod.rs",
    "content": "pub mod cli;\npub mod handlers;\npub mod shortcut_registering;\n\nuse std::{collections::HashMap, sync::LazyLock};\n\nuse parking_lot::Mutex;\nuse seelen_core::{handlers::SeelenEvent, resource::WidgetId, state::SluPopupConfig};\nuse tauri::{\n    utils::{config::WindowEffectsConfig, WindowEffect},\n    Emitter, Listener, LogicalSize, WebviewWindow, WebviewWindowBuilder, WindowEvent,\n};\nuse uuid::Uuid;\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    widgets::{webview::WebviewArgs, WidgetWebviewLabel},\n};\n\npub static POPUPS_MANAGER: LazyLock<Mutex<PopupsManager>> = LazyLock::new(|| {\n    Mutex::new(PopupsManager {\n        webviews: HashMap::new(),\n        configs: HashMap::new(),\n        listeners: HashMap::new(),\n    })\n});\n\npub struct PopupsManager {\n    webviews: HashMap<Uuid, WebviewWindow>,\n    configs: HashMap<Uuid, SluPopupConfig>,\n    pub listeners: HashMap<Uuid, Vec<u32>>,\n}\n\nimpl PopupsManager {\n    pub fn get_window_handle(&self, id: &Uuid) -> Option<&WebviewWindow> {\n        self.webviews.get(id)\n    }\n\n    pub fn create(&mut self, config: SluPopupConfig) -> Result<Uuid> {\n        let popup_id = Uuid::new_v4();\n        let label = WidgetWebviewLabel::new(&WidgetId::known_popup(), None, Some(&popup_id));\n        let args = WebviewArgs::default();\n\n        let manager = get_app_handle();\n        let window = WebviewWindowBuilder::new(\n            manager,\n            label.raw,\n            tauri::WebviewUrl::App(\"react/popup/index.html\".into()),\n        )\n        .center()\n        .minimizable(false)\n        .maximizable(false)\n        .resizable(false)\n        .closable(false)\n        .always_on_top(true)\n        .decorations(false)\n        .transparent(true)\n        .inner_size(config.width, config.height)\n        .effects(WindowEffectsConfig {\n            color: None,\n            radius: None,\n            state: None,\n            effects: vec![WindowEffect::Acrylic],\n        })\n        .visible(false)\n        .data_directory(args.data_directory())\n        .additional_browser_args(&args.to_string())\n        .build()?;\n\n        window.on_window_event(move |e| {\n            if let WindowEvent::Destroyed = e {\n                log::trace!(\"popup destroyed: {popup_id}\");\n                std::thread::spawn(move || {\n                    let mut popups = POPUPS_MANAGER.lock();\n                    popups.webviews.remove(&popup_id);\n                    popups.configs.remove(&popup_id);\n                    if let Some(tokens) = popups.listeners.remove(&popup_id) {\n                        for token in tokens {\n                            get_app_handle().unlisten(token);\n                        }\n                    }\n                });\n            }\n        });\n\n        window.center()?; // ensure centered position\n        self.configs.insert(popup_id, config);\n        self.webviews.insert(popup_id, window);\n        Ok(popup_id)\n    }\n\n    pub fn update(&mut self, id: &Uuid, config: SluPopupConfig) -> Result<()> {\n        if let Some(webview) = self.webviews.get(id) {\n            webview.emit(SeelenEvent::PopupContentChanged, &config)?;\n            webview.set_size(LogicalSize::new(config.width, config.height))?;\n            webview.center()?;\n        }\n        self.configs.insert(*id, config);\n        Ok(())\n    }\n\n    pub fn close_popup(&mut self, id: &Uuid) -> Result<()> {\n        if let Some(webview) = self.webviews.get(id) {\n            webview.close()?;\n        } else {\n            return Err(\"popup not found\".into());\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/popups/shortcut_registering.rs",
    "content": "use std::sync::LazyLock;\n\nuse parking_lot::Mutex;\nuse seelen_core::state::{CssStyles, SluPopupConfig, SluPopupContent};\nuse slu_ipc::messages::SvcAction;\nuse tauri::{Emitter, Listener, WindowEvent};\nuse uuid::Uuid;\n\nuse crate::{\n    app::get_app_handle, cli::ServicePipe, error::Result, log_error,\n    widgets::popups::POPUPS_MANAGER,\n};\n\npub static REG_SHORTCUT_DATA: LazyLock<Mutex<RegShortcutData>> =\n    LazyLock::new(|| Mutex::new(RegShortcutData::default()));\n\n#[derive(Default)]\npub struct RegShortcutData {\n    pub popup_id: Uuid,\n    pub shortcut: Option<Vec<String>>,\n    pub response_view_label: Option<String>,\n    pub response_event: Option<String>,\n}\n\nimpl RegShortcutData {\n    fn emit_state_to_requester(&self) {\n        if let (Some(label), Some(event)) = (&self.response_view_label, &self.response_event) {\n            log_error!(get_app_handle().emit_to(label, event, &self.shortcut));\n        }\n    }\n\n    fn cancel_shortcut_registering(&mut self) {\n        if !self.popup_id.is_nil() {\n            // could fail if was already closed\n            let _ = POPUPS_MANAGER.lock().close_popup(&self.popup_id);\n        }\n        *self = Default::default();\n    }\n}\n\npub fn set_registering_shortcut(shortcut: Option<Vec<String>>) -> Result<()> {\n    let mut reg = REG_SHORTCUT_DATA.lock();\n    reg.shortcut = shortcut.clone();\n\n    let Some(shortcut) = shortcut else {\n        reg.emit_state_to_requester();\n        reg.cancel_shortcut_registering();\n        return Ok(());\n    };\n\n    let popup_config = get_popup_config(&shortcut);\n\n    if !reg.popup_id.is_nil() {\n        POPUPS_MANAGER.lock().update(&reg.popup_id, popup_config)?;\n        return Ok(());\n    }\n\n    let mut popup_manager = POPUPS_MANAGER.lock();\n    reg.popup_id = popup_manager.create(popup_config)?;\n\n    let handle = popup_manager.get_window_handle(&reg.popup_id).unwrap();\n\n    // stop on close of the popup\n    handle.on_window_event(|e| {\n        if let WindowEvent::Destroyed = e {\n            log_error!(ServicePipe::request(SvcAction::StopShortcutRegistration));\n        }\n    });\n\n    handle.once(\"user_shortcut_accepted\", |_| {\n        let mut reg = REG_SHORTCUT_DATA.lock();\n        reg.emit_state_to_requester();\n        reg.cancel_shortcut_registering();\n    });\n    Ok(())\n}\n\nfn get_popup_config(shortcut: &[String]) -> SluPopupConfig {\n    let items = if shortcut.is_empty() {\n        vec![SluPopupContent::Text {\n            value: t!(\"shortcut.register.placeholder\").to_string(),\n            styles: Some(\n                CssStyles::new()\n                    .add(\"color\", \"#6c6c6c\")\n                    .add(\"fontSize\", \"1rem\")\n                    .add(\"fontStyle\", \"italic\"),\n            ),\n        }]\n    } else {\n        shortcut\n            .iter()\n            .map(|s| SluPopupContent::Text {\n                value: s.to_string(),\n                styles: Some(\n                    CssStyles::new()\n                        .add(\"backgroundColor\", \"#fefefe\")\n                        .add(\"padding\", \"4px 10px\")\n                        .add(\"borderRadius\", \"4px\"),\n                ),\n            })\n            .collect()\n    };\n\n    let keys_box = SluPopupContent::Group {\n        items,\n        styles: Some(\n            CssStyles::new()\n                .add(\"width\", \"100%\")\n                .add(\"height\", \"100%\")\n                .add(\"display\", \"flex\")\n                .add(\"gap\", \"5px\")\n                .add(\"justifyContent\", \"center\")\n                .add(\"alignItems\", \"center\")\n                .add(\"flexWrap\", \"wrap\")\n                .add(\"fontWeight\", \"bold\"),\n        ),\n    };\n\n    let actions = if shortcut.is_empty() {\n        Vec::new()\n    } else {\n        vec![\n            SluPopupContent::Button {\n                inner: vec![SluPopupContent::Text {\n                    value: t!(\"cancel\").to_string(),\n                    styles: None,\n                }],\n                on_click: \"exit\".to_string(),\n                styles: Some(\n                    CssStyles::new()\n                        .add(\"backgroundColor\", \"var(--color-red-700)\")\n                        .add(\"color\", \"var(--color-white)\"),\n                ),\n            },\n            SluPopupContent::Button {\n                inner: vec![SluPopupContent::Text {\n                    value: t!(\"done\").to_string(),\n                    styles: None,\n                }],\n                on_click: \"user_shortcut_accepted\".to_string(),\n                styles: None,\n            },\n        ]\n    };\n\n    SluPopupConfig {\n        width: 360.0,\n        height: 180.0,\n        title: vec![SluPopupContent::Text {\n            value: t!(\"shortcut.register.title\").to_string(),\n            styles: None,\n        }],\n        content: vec![keys_box],\n        footer: actions,\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/task_switcher/cli.rs",
    "content": "use seelen_core::state::WidgetTriggerPayload;\n\nuse crate::{error::Result, widgets::trigger_widget};\n\n#[derive(Debug, clap::Args)]\npub struct TaskSwitcherClient {\n    #[command(subcommand)]\n    command: TaskSwitcherCommand,\n}\n\nimpl TaskSwitcherClient {\n    pub fn process(self) -> Result<()> {\n        self.command.process()\n    }\n}\n\n#[derive(Debug, clap::Subcommand)]\npub enum TaskSwitcherCommand {\n    SelectNextTask {\n        #[clap(long)]\n        auto_confirm: bool,\n    },\n    SelectPreviousTask {\n        #[clap(long)]\n        auto_confirm: bool,\n    },\n}\n\nimpl TaskSwitcherCommand {\n    pub fn process(self) -> Result<()> {\n        match self {\n            TaskSwitcherCommand::SelectNextTask { auto_confirm } => {\n                let mut args = WidgetTriggerPayload::new(\"@seelen/task-switcher\".into());\n                args.add_custom_arg(\"direction\", \"next\");\n                args.add_custom_arg(\"autoConfirm\", auto_confirm);\n                trigger_widget(args)?;\n            }\n            TaskSwitcherCommand::SelectPreviousTask { auto_confirm } => {\n                let mut args = WidgetTriggerPayload::new(\"@seelen/task-switcher\".into());\n                args.add_custom_arg(\"direction\", \"previous\");\n                args.add_custom_arg(\"autoConfirm\", auto_confirm);\n                trigger_widget(args)?;\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/task_switcher/mod.rs",
    "content": "pub mod cli;\n"
  },
  {
    "path": "src/background/widgets/toolbar/hook.rs",
    "content": "use crate::{error::Result, windows_api::window::event::WinEvent, windows_api::window::Window};\n\nuse super::FancyToolbar;\n\nimpl FancyToolbar {\n    pub fn process_win_event(&mut self, event: WinEvent, origin: &Window) -> Result<()> {\n        if event == WinEvent::ObjectLocationChange && origin.hwnd() == self.hwnd()? {\n            self.reposition_if_needed()?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/toolbar/mod.rs",
    "content": "pub mod hook;\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    log_error,\n    state::application::FULL_STATE,\n    widgets::webview::WebviewArgs,\n    windows_api::{monitor::Monitor, AppBarData, WindowsApi},\n};\nuse base64::Engine;\nuse seelen_core::state::{FancyToolbarSide, HideMode};\nuse tauri::WebviewWindow;\nuse windows::Win32::{\n    Foundation::{HWND, RECT},\n    UI::WindowsAndMessaging::{SWP_ASYNCWINDOWPOS, SWP_NOSIZE},\n};\n\npub struct FancyToolbar {\n    window: WebviewWindow,\n    pub rect: RECT,\n}\n\nimpl Drop for FancyToolbar {\n    fn drop(&mut self) {\n        log::info!(\"Dropping {}\", self.window.label());\n        if let Ok(hwnd) = self.hwnd() {\n            AppBarData::from_handle(hwnd).unregister_bar();\n        }\n        log_error!(self.window.destroy());\n    }\n}\n\nimpl FancyToolbar {\n    pub fn hwnd(&self) -> Result<HWND> {\n        Ok(HWND(self.window.hwnd()?.0))\n    }\n\n    pub fn new(monitor_id: &str) -> Result<Self> {\n        Ok(Self {\n            window: Self::create_window(monitor_id)?,\n            rect: RECT::default(),\n        })\n    }\n}\n\n// statics\nimpl FancyToolbar {\n    pub const TITLE: &'static str = \"Seelen Fancy Toolbar\";\n    pub const TARGET: &'static str = \"@seelen/fancy-toolbar\";\n\n    pub fn decoded_label(monitor_id: &str) -> String {\n        format!(\"{}?monitorId={}\", Self::TARGET, monitor_id)\n    }\n\n    pub fn label(monitor_id: &str) -> String {\n        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(Self::decoded_label(monitor_id))\n    }\n\n    fn create_window(monitor_id: &str) -> Result<WebviewWindow> {\n        let manager = get_app_handle();\n        let args = WebviewArgs::default();\n\n        log::info!(\"Creating {}\", Self::decoded_label(monitor_id));\n\n        let window = tauri::WebviewWindowBuilder::new(\n            manager,\n            Self::label(monitor_id),\n            tauri::WebviewUrl::App(\"react/toolbar/index.html\".into()),\n        )\n        .title(Self::TITLE)\n        .minimizable(false)\n        .maximizable(false)\n        .closable(false)\n        .resizable(false)\n        .visible(false)\n        .decorations(false)\n        .transparent(true)\n        .shadow(false)\n        .skip_taskbar(true)\n        .always_on_top(true)\n        .data_directory(args.data_directory())\n        .additional_browser_args(&args.to_string())\n        .build()?;\n\n        Ok(window)\n    }\n\n    pub fn get_toolbar_height_on_monitor(monitor: &Monitor) -> Result<i32> {\n        let state = FULL_STATE.load();\n        let settings = &state.settings.by_widget.fancy_toolbar;\n        let scale_factor = monitor.scale_factor()?;\n        Ok((settings.total_size() as f64 * scale_factor) as i32)\n    }\n\n    pub fn set_position(&mut self, monitor: &Monitor) -> Result<()> {\n        let hwnd = HWND(self.hwnd()?.0);\n\n        let state = FULL_STATE.load();\n        let settings = &state.settings.by_widget.fancy_toolbar;\n\n        let monitor_info = WindowsApi::monitor_info(monitor.handle())?;\n        let rc_monitor = monitor_info.monitorInfo.rcMonitor;\n\n        let real_height = Self::get_toolbar_height_on_monitor(monitor)?;\n\n        self.rect = rc_monitor;\n        match settings.position {\n            FancyToolbarSide::Top => {\n                self.rect.bottom = rc_monitor.top + real_height;\n            }\n            FancyToolbarSide::Bottom => {\n                self.rect.top = rc_monitor.bottom - real_height;\n            }\n        }\n\n        let mut abd = AppBarData::from_handle(hwnd);\n        match settings.hide_mode {\n            HideMode::Never => {\n                abd.set_edge(settings.position.into());\n                abd.set_rect(self.rect);\n                abd.register_as_new_bar();\n            }\n            _ => abd.unregister_bar(),\n        };\n\n        // pre set position for resize in case of multiples dpi\n        WindowsApi::set_position(hwnd, None, &self.rect, SWP_NOSIZE)?;\n        WindowsApi::set_position(hwnd, None, &self.rect, SWP_ASYNCWINDOWPOS)?;\n        Ok(())\n    }\n\n    pub fn reposition_if_needed(&mut self) -> Result<()> {\n        let hwnd = self.hwnd()?;\n        if self.rect == WindowsApi::get_outer_window_rect(hwnd)? {\n            return Ok(()); // position is ok no need to reposition\n        }\n        self.set_position(&Monitor::from(WindowsApi::monitor_from_window(hwnd)))?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/wallpaper_manager/cli.rs",
    "content": "use crate::{error::Result, virtual_desktops::wallpapers::WorkspaceWallpapersManager};\n\n/// Wallpaper manager commands\n#[derive(Debug, clap::Args)]\npub struct WallpaperCli {\n    #[command(subcommand)]\n    command: WallpaperCommand,\n}\n\n#[derive(Debug, clap::Subcommand)]\nenum WallpaperCommand {\n    /// Cycle to the next wallpaper\n    Next,\n    /// Cycle to the previous wallpaper\n    Prev,\n}\n\nimpl WallpaperCli {\n    pub fn process(self) -> Result<()> {\n        match self.command {\n            WallpaperCommand::Next => WorkspaceWallpapersManager::next(),\n            WallpaperCommand::Prev => WorkspaceWallpapersManager::previous(),\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/wallpaper_manager/hook.rs",
    "content": "use crate::{error::Result, windows_api::window::event::WinEvent, windows_api::window::Window};\n\nuse super::SeelenWall;\n\nimpl SeelenWall {\n    pub fn process_win_event(&mut self, _event: WinEvent, _origin: &Window) -> Result<()> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/wallpaper_manager/mod.rs",
    "content": "pub mod cli;\nmod hook;\n\nuse base64::Engine;\nuse tauri::WebviewWindow;\nuse windows::Win32::{\n    Foundation::{HWND, LPARAM, RECT, WPARAM},\n    Graphics::Gdi::{InvalidateRect, UpdateWindow},\n    UI::WindowsAndMessaging::{\n        FindWindowA, FindWindowExA, GetParent, PostMessageW, SetParent, SetWindowLongPtrW,\n        SetWindowPos, GWL_EXSTYLE, GWL_STYLE, HWND_BOTTOM, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE,\n        SWP_NOMOVE, SWP_NOOWNERZORDER, SWP_NOSIZE, WS_CHILDWINDOW, WS_EX_ACCEPTFILES,\n        WS_EX_APPWINDOW, WS_EX_NOREDIRECTIONBITMAP, WS_EX_WINDOWEDGE,\n    },\n};\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    log_error, pcstr,\n    widgets::webview::WebviewArgs,\n    windows_api::{WindowEnumerator, WindowsApi},\n};\n\nstruct DesktopInfo {\n    progman: HWND,\n    worker_w: Option<HWND>,\n    defview: Option<HWND>,\n    is_raised_desktop: bool,\n}\n\npub struct SeelenWall {\n    window: WebviewWindow,\n}\n\nimpl Drop for SeelenWall {\n    fn drop(&mut self) {\n        log::info!(\"Dropping {}\", self.window.label());\n        log_error!(self.window.destroy());\n    }\n}\n\nimpl SeelenWall {\n    pub const TITLE: &str = \"Seelen Wall\";\n    const TARGET: &str = \"@seelen/wallpaper-manager\";\n\n    pub fn new() -> Result<Self> {\n        log::info!(\"Creating {}\", Self::TARGET);\n        Ok(Self {\n            window: Self::create_window()?,\n        })\n    }\n\n    fn create_window() -> Result<WebviewWindow> {\n        let handle = get_app_handle();\n        let label = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(Self::TARGET);\n        let args = WebviewArgs::default();\n\n        let builder = tauri::WebviewWindowBuilder::new(\n            handle,\n            label,\n            tauri::WebviewUrl::App(\"svelte/wallpaper_manager/index.html\".into()),\n        )\n        .title(Self::TITLE)\n        .minimizable(false)\n        .maximizable(false)\n        .closable(false)\n        .resizable(false)\n        .decorations(false)\n        .shadow(false)\n        .transparent(true)\n        .focusable(false)\n        .visible(false)\n        .skip_taskbar(true)\n        .drag_and_drop(false)\n        .disable_drag_drop_handler()\n        .always_on_bottom(true)\n        .data_directory(args.data_directory())\n        .additional_browser_args(&args.to_string());\n\n        // workaround for https://github.com/tauri-apps/tao/issues/1153\n        /* if let Ok(_progman) = WindowsApi::find_window(None, None, None, Some(\"Progman\")) {\n            let mut progman = Default::default();\n            builder = builder.parent_raw(progman);\n            progman.0 = _progman.0;\n            builder = builder.parent_raw(progman);\n        } */\n\n        let window = builder.build()?;\n        // window.open_devtools();\n        Ok(window)\n    }\n\n    pub fn hwnd(&self) -> Result<HWND> {\n        Ok(HWND(self.window.hwnd()?.0))\n    }\n\n    pub fn update_position(&self) -> Result<()> {\n        let rect = WindowsApi::virtual_screen_rect()?;\n        let main_hwnd = self.hwnd()?;\n\n        // Try to position the wallpaper under desktop items\n        match Self::try_set_under_desktop_items(main_hwnd) {\n            Ok(_) => {\n                // When parented to WorkerW, coordinates are relative to parent\n                // Calculate dimensions from the absolute screen rect\n                let relative_rect = RECT {\n                    left: 0,\n                    top: 0,\n                    right: rect.right - rect.left,\n                    bottom: rect.bottom - rect.top,\n                };\n\n                WindowsApi::move_window(main_hwnd, &relative_rect)?;\n                WindowsApi::set_position(main_hwnd, None, &relative_rect, SWP_ASYNCWINDOWPOS)?;\n            }\n            Err(e) => {\n                log::warn!(\n                    \"Failed to attach to desktop hierarchy: {}, using absolute positioning\",\n                    e\n                );\n                // Fallback to absolute positioning without parent\n                WindowsApi::move_window(main_hwnd, &rect)?;\n                WindowsApi::set_position(main_hwnd, None, &rect, SWP_ASYNCWINDOWPOS)?;\n                self.window.set_always_on_bottom(true)?;\n            }\n        }\n\n        log_error!(Self::refresh_desktop().map_err(|e| format!(\"Failed to refresh desktop: {e}\")));\n        Ok(())\n    }\n\n    /// Sets up the desktop layer by sending a message to Progman to spawn WorkerW\n    fn setup_desktop_layer() -> Result<()> {\n        let progman = unsafe { FindWindowA(pcstr!(\"Progman\"), None)? };\n        // Send 0x052C to Progman. This message directs Progman to spawn a WorkerW\n        // behind the desktop icons. If it is already there, nothing happens.\n        unsafe { PostMessageW(Some(progman), 0x052C, WPARAM(0xD), LPARAM(0x1))? };\n        Ok(())\n    }\n\n    /// Detects the desktop configuration and returns desktop information\n    fn detect_desktop_info() -> Result<DesktopInfo> {\n        let progman = unsafe { FindWindowA(pcstr!(\"Progman\"), None)? };\n\n        let mut worker_w = None;\n        let mut defview = None;\n        let mut is_raised_desktop = false;\n\n        // CASE 1: Standard Windows 10/11 layout\n        // WorkerW exists as a sibling to another WorkerW that contains SHELLDLL_DefView\n        // 0x00010190 \"\" WorkerW\n        //   0x000100EE \"\" SHELLDLL_DefView\n        //     0x000100F0 \"FolderView\" SysListView32\n        // 0x00100B8A \"\" WorkerW       <-- This is the WorkerW we want\n        // 0x000100EC \"Program Manager\" Progman\n        // We enumerate all Windows, until we find one, that has the SHELLDLL_DefView as a child.\n        // If we found that window, we take its next sibling and assign it to workerw.\n        WindowEnumerator::new().for_each(|current| unsafe {\n            if FindWindowExA(Some(current.hwnd()), None, pcstr!(\"SHELLDLL_DefView\"), None).is_ok() {\n                // Find next worker after the current one\n                if let Ok(_worker) =\n                    FindWindowExA(None, Some(current.hwnd()), pcstr!(\"WorkerW\"), None)\n                {\n                    worker_w = Some(_worker);\n                }\n            }\n        })?;\n\n        // CASE 2: Raised Desktop (Windows 11 with layered shell view)\n        // Progman contains SHELLDLL_DefView and WorkerW as children\n        // 0x000100EC \"Program Manager\" Progman\n        //   0x000100EE \"\" SHELLDLL_DefView\n        //     0x000100F0 \"FolderView\" SysListView32\n        //   0x00100B8A \"\" WorkerW       <-- This is the WorkerW we want\n        if worker_w.is_none() {\n            // Check if DefView is a child of Progman\n            defview = unsafe {\n                FindWindowExA(Some(progman), None, pcstr!(\"SHELLDLL_DefView\"), None).ok()\n            };\n\n            if defview.is_some() {\n                // Check if Progman has WS_EX_NOREDIRECTIONBITMAP style\n                let ex_style = WindowsApi::get_ex_styles(progman);\n                is_raised_desktop = ex_style.contains(WS_EX_NOREDIRECTIONBITMAP);\n\n                // Find WorkerW as child of Progman\n                let mut attempts = 0;\n                worker_w =\n                    unsafe { FindWindowExA(Some(progman), None, pcstr!(\"WorkerW\"), None).ok() };\n                while worker_w.is_none() && attempts < 10 {\n                    attempts += 1;\n                    std::thread::sleep(std::time::Duration::from_millis(100));\n                    worker_w =\n                        unsafe { FindWindowExA(Some(progman), None, pcstr!(\"WorkerW\"), None).ok() };\n                }\n            }\n        }\n\n        Ok(DesktopInfo {\n            progman,\n            worker_w,\n            defview,\n            is_raised_desktop,\n        })\n    }\n\n    /// Attaches the wallpaper window to the desktop using the appropriate method\n    fn attach_to_desktop(hwnd: HWND, desktop_info: &DesktopInfo) -> Result<()> {\n        if desktop_info.is_raised_desktop {\n            Self::attach_raised_desktop(hwnd, desktop_info)\n        } else {\n            Self::attach_standard_desktop(hwnd, desktop_info)\n        }\n    }\n\n    /// Attaches wallpaper to standard desktop (WorkerW parent)\n    fn attach_standard_desktop(hwnd: HWND, desktop_info: &DesktopInfo) -> Result<()> {\n        let worker_w = desktop_info\n            .worker_w\n            .ok_or(\"WorkerW not found for standard desktop\")?;\n\n        unsafe {\n            // Check if already parented to WorkerW to avoid flicker\n            let current_parent = GetParent(hwnd).ok();\n            if current_parent != Some(worker_w) {\n                SetParent(hwnd, Some(worker_w))?;\n            }\n\n            // Position at bottom of z-order\n            SetWindowPos(\n                hwnd,\n                Some(HWND_BOTTOM),\n                0,\n                0,\n                0,\n                0,\n                SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER,\n            )?;\n        }\n\n        Ok(())\n    }\n\n    /// Attaches wallpaper to raised desktop with layered shell view (Progman parent)\n    fn attach_raised_desktop(hwnd: HWND, desktop_info: &DesktopInfo) -> Result<()> {\n        let defview = desktop_info\n            .defview\n            .ok_or(\"DefView not found for raised desktop\")?;\n\n        unsafe {\n            // Check if already parented to Progman to avoid flicker\n            let current_parent = GetParent(hwnd).ok();\n            let needs_reparent = current_parent != Some(desktop_info.progman);\n\n            if needs_reparent {\n                let mut style = WindowsApi::get_styles(hwnd);\n                style |= WS_CHILDWINDOW;\n                SetWindowLongPtrW(hwnd, GWL_STYLE, style.0 as isize);\n\n                let mut ex_style = WindowsApi::get_ex_styles(hwnd);\n                ex_style &= !WS_EX_ACCEPTFILES;\n                ex_style &= !WS_EX_APPWINDOW;\n                ex_style &= !WS_EX_WINDOWEDGE; // this can be removed wait for https://github.com/tauri-apps/tao/issues/1153\n                SetWindowLongPtrW(hwnd, GWL_EXSTYLE, ex_style.0 as isize);\n\n                // Set parent to Progman\n                SetParent(hwnd, Some(desktop_info.progman))?;\n            }\n\n            // Position wallpaper below DefView in z-order\n            WindowsApi::set_position(\n                hwnd,\n                Some(defview),\n                &Default::default(),\n                SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,\n            )?;\n\n            // Ensure WorkerW z-order is correct (must be at absolute bottom)\n            if let Some(worker_w) = desktop_info.worker_w {\n                Self::ensure_workerw_z_order(worker_w)?;\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Ensures WorkerW is at the bottom of the z-order\n    /// This is critical for the correct layering: ItemsView > Wallpaper > WorkerW\n    fn ensure_workerw_z_order(worker_w: HWND) -> Result<()> {\n        unsafe {\n            SetWindowPos(\n                worker_w,\n                Some(HWND_BOTTOM),\n                0,\n                0,\n                0,\n                0,\n                SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,\n            )?;\n        }\n        Ok(())\n    }\n\n    /// Main function to set wallpaper under desktop items\n    fn try_set_under_desktop_items(hwnd: HWND) -> Result<()> {\n        // Setup desktop layer\n        Self::setup_desktop_layer()?;\n\n        // Detect desktop configuration\n        let desktop_info = Self::detect_desktop_info()?;\n\n        // Attach wallpaper to desktop\n        Self::attach_to_desktop(hwnd, &desktop_info)?;\n\n        Ok(())\n    }\n\n    /// this is only needed on the case 2 of try_set_inside_workerw\n    fn refresh_desktop() -> Result<()> {\n        unsafe {\n            let progman = FindWindowA(pcstr!(\"Progman\"), None)?;\n            if let Ok(shell_view) =\n                FindWindowExA(Some(progman), None, pcstr!(\"SHELLDLL_DefView\"), None)\n            {\n                InvalidateRect(Some(shell_view), None, true).ok()?;\n                UpdateWindow(shell_view).ok()?;\n            }\n        }\n        WindowsApi::refresh_desktop()\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/webview.rs",
    "content": "use std::path::PathBuf;\n\nuse base64::Engine;\nuse seelen_core::{\n    resource::WidgetId,\n    state::{Widget, WidgetLoader},\n};\n\nuse crate::{\n    app::get_app_handle, error::Result, state::application::FULL_STATE,\n    utils::constants::SEELEN_COMMON,\n};\n\npub struct WidgetWebview(pub tauri::WebviewWindow);\n\nimpl WidgetWebview {\n    pub fn create(widget: &Widget, label: &WidgetWebviewLabel) -> Result<Self> {\n        let state = FULL_STATE.load();\n        let title = widget.metadata.display_name.get(state.locale());\n\n        let args = WebviewArgs::default();\n\n        let url = match widget.loader {\n            WidgetLoader::Legacy => {\n                return Err(\"Legacy widgets are not supported by the new widget loader\".into());\n            }\n            WidgetLoader::InternalReact => {\n                let resource_name = widget\n                    .id\n                    .resource_name()\n                    .ok_or(\"Can't get internal resource path\")?;\n                tauri::WebviewUrl::App(format!(\"react/{resource_name}/index.html\").into())\n            }\n            WidgetLoader::Internal => {\n                let resource_name = widget\n                    .id\n                    .resource_name()\n                    .ok_or(\"Can't get internal resource path\")?;\n                tauri::WebviewUrl::App(format!(\"svelte/{resource_name}/index.html\").into())\n            }\n            WidgetLoader::ThirdParty => {\n                tauri::WebviewUrl::App(\"vanilla/third_party/index.html\".into())\n            }\n        };\n\n        let window: tauri::WebviewWindow =\n            tauri::WebviewWindowBuilder::new(get_app_handle(), &label.raw, url)\n                .title(title)\n                .transparent(true)\n                .visible(false)\n                .data_directory(args.data_directory())\n                .additional_browser_args(&args.to_string())\n                .build()?;\n\n        Ok(Self(window))\n    }\n}\n\nimpl Drop for WidgetWebview {\n    fn drop(&mut self) {\n        let _ = self.0.destroy();\n    }\n}\n\n// =============================================================================\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub struct WidgetWebviewLabel {\n    /// this should be used as the real webview label\n    pub raw: String,\n    /// this is the decoded label, useful for debugging and logging\n    pub decoded: String,\n    /// widget id from this label was created\n    pub widget_id: WidgetId,\n}\n\nimpl WidgetWebviewLabel {\n    pub fn new(\n        widget_id: &WidgetId,\n        monitor_id: Option<&str>,\n        instance_id: Option<&uuid::Uuid>,\n    ) -> Self {\n        let mut label = widget_id.to_string();\n        let with_monitor_id = monitor_id.is_some();\n        let with_instance_id = instance_id.is_some();\n        if with_monitor_id || with_instance_id {\n            label.push('?');\n        }\n\n        if let Some(monitor_id) = monitor_id {\n            label.push_str(&format!(\"monitorId={}\", urlencoding::encode(monitor_id)));\n        }\n\n        if let Some(instance_id) = instance_id {\n            if with_monitor_id {\n                label.push('&');\n            }\n            label.push_str(&format!(\n                \"instanceId={}\",\n                urlencoding::encode(&instance_id.to_string())\n            ));\n        }\n\n        Self {\n            raw: base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&label),\n            decoded: label,\n            widget_id: widget_id.clone(),\n        }\n    }\n\n    pub fn try_from_raw(raw: &str) -> Result<Self> {\n        let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(raw)?;\n        let decoded = String::from_utf8(decoded)?;\n        let widget_id = WidgetId::from(decoded.split('?').next().expect(\"Invalid label\"));\n\n        Ok(Self {\n            raw: raw.to_string(),\n            decoded,\n            widget_id,\n        })\n    }\n}\n\nimpl std::fmt::Display for WidgetWebviewLabel {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.decoded)\n    }\n}\n\n// =============================================================================\n\npub struct WebviewArgs {\n    common_args: Vec<String>,\n    extra_args: Vec<String>,\n}\n\nimpl WebviewArgs {\n    const BASE_ARGS: &[&str] = &[\n        \"--disable-features=translate,msWebOOUI,msPdfOOUI,msSmartScreenProtection,RendererAppContainer\",\n        \"--no-first-run\",\n        \"--disable-site-isolation-trials\",\n        /* \"--disable-breakpad\",\n        \"--disable-component-update\",\n        \"--disable-default-apps\",\n        \"--disable-background-timer-throttling\",\n        \"--disable-background-networking\", */\n    ];\n\n    const PERFORMANCE_ARGS: &[&str] = &[\n        // \"--enable-low-end-device-mode\",\n        // \"--in-process-gpu\",\n        \"--disable-gpu\",\n        \"--disable-software-rasterizer\",\n    ];\n\n    pub fn data_directory(&self) -> PathBuf {\n        if self.extra_args.is_empty() {\n            SEELEN_COMMON.app_cache_dir().to_path_buf()\n        } else {\n            SEELEN_COMMON\n                .app_cache_dir()\n                .join(self.extra_args.join(\"\").replace('-', \"\"))\n        }\n    }\n}\n\nimpl Default for WebviewArgs {\n    fn default() -> Self {\n        let common_args = if FULL_STATE.load().settings.hardware_acceleration {\n            Self::BASE_ARGS.iter().map(|s| s.to_string()).collect()\n        } else {\n            Self::BASE_ARGS\n                .iter()\n                .chain(Self::PERFORMANCE_ARGS)\n                .map(|s| s.to_string())\n                .collect()\n        };\n\n        Self {\n            common_args,\n            extra_args: Vec::new(),\n        }\n    }\n}\n\nimpl std::fmt::Display for WebviewArgs {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.common_args.join(\" \"))\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/weg/cli.rs",
    "content": "use seelen_core::{\n    state::{WegItem, WegItemData},\n    system_state::UserAppWindow,\n};\nuse serde::{Deserialize, Serialize};\nuse windows::Win32::UI::WindowsAndMessaging::SW_MINIMIZE;\n\nuse crate::{\n    error::Result,\n    modules::apps::application::USER_APPS_MANAGER,\n    state::application::FULL_STATE,\n    windows_api::{window::Window, WindowsApi},\n};\n\n/// Mirrors `getWindowsForItem` from the frontend (`windows.ts`).\n///\n/// Returns all interactable windows that belong to `item`, matching by:\n/// 1. UMID – when both the item and the window carry a non-empty UMID.\n/// 2. Path  – `item.relaunch.command` **or** `item.path` equals `w.process.path`\n///    (case-insensitive, both are checked independently).\n///\n/// note: on update of this function check src\\ui\\react\\weg\\modules\\shared\\state\\windows.ts both should work the same\nfn get_windows_for_item<'a>(\n    item: &WegItemData,\n    interactables: &'a [UserAppWindow],\n) -> Vec<&'a UserAppWindow> {\n    if item.umid.is_some() {\n        return interactables\n            .iter()\n            .filter(|w| w.umid.is_some() && item.umid == w.umid)\n            .collect();\n    }\n\n    let item_command = item.relaunch.as_ref().map(|r| r.command.to_lowercase());\n    let item_path = item.path.to_string_lossy().to_lowercase();\n\n    interactables\n        .iter()\n        .filter(|w| {\n            let win_path = w\n                .process\n                .path\n                .as_ref()\n                .map(|p| p.to_string_lossy().to_lowercase())\n                .unwrap_or_default();\n\n            if win_path.is_empty() {\n                return false;\n            }\n\n            item_command.as_deref() == Some(win_path.as_str()) || item_path == win_path\n        })\n        .collect()\n}\n\n/// Seelen's dock commands\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\npub struct WegCli {\n    #[command(subcommand)]\n    pub subcommand: WegCommand,\n}\n\n#[derive(Debug, Serialize, Deserialize, clap::Subcommand)]\npub enum WegCommand {\n    /// Set foreground to the application which is idx-nth on the weg. If it is not started, then starts it.\n    ForegroundOrRunApp {\n        /// Which index should be started on weg.\n        index: usize,\n    },\n}\n\nimpl WegCli {\n    pub fn process(self) -> Result<()> {\n        #[allow(irrefutable_let_patterns)]\n        if let WegCommand::ForegroundOrRunApp { index } = self.subcommand {\n            let state = FULL_STATE.load();\n            let weg_items = &state.weg_items;\n\n            let all_items: Vec<&WegItem> = weg_items\n                .left\n                .iter()\n                .chain(weg_items.center.iter())\n                .chain(weg_items.right.iter())\n                .filter(|item| matches!(item, WegItem::AppOrFile(_)))\n                .collect();\n\n            if all_items.len() <= index {\n                return Ok(());\n            }\n\n            let WegItem::AppOrFile(inner_data) = all_items[index] else {\n                return Ok(());\n            };\n\n            let interactables = USER_APPS_MANAGER.interactable_windows.to_vec();\n            let windows = get_windows_for_item(inner_data, &interactables);\n\n            if windows.is_empty() {\n                let command = inner_data\n                    .relaunch\n                    .as_ref()\n                    .map(|r| r.command.clone())\n                    .unwrap_or_else(|| inner_data.path.to_string_lossy().to_string());\n                let args = inner_data\n                    .relaunch\n                    .as_ref()\n                    .and_then(|r| r.args.as_ref())\n                    .map(|a| a.to_string());\n                let working_dir = inner_data\n                    .relaunch\n                    .as_ref()\n                    .and_then(|r| r.working_dir.clone());\n                WindowsApi::execute(command, args, working_dir, false)?;\n            } else {\n                let focused = windows.iter().find(|w| Window::from(w.hwnd).is_focused());\n                if let Some(w) = focused {\n                    Window::from(w.hwnd).show_window_async(SW_MINIMIZE)?;\n                } else if let Some(w) = windows.first() {\n                    let window = Window::from(w.hwnd);\n                    if window.is_window() {\n                        window.focus()?;\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/weg/handler.rs",
    "content": "use std::path::PathBuf;\n\nuse seelen_core::{handlers::SeelenEvent, state::WegItemData};\nuse tauri_plugin_shell::ShellExt;\n\nuse crate::{\n    app::{emit_to_webviews, get_app_handle},\n    error::Result,\n    windows_api::{window::Window, WindowsApi},\n};\nuse windows::Win32::UI::WindowsAndMessaging::{SW_SHOWMINNOACTIVE, WM_CLOSE};\n\n#[tauri::command(async)]\npub fn weg_close_app(hwnd: isize) -> Result<()> {\n    let window = Window::from(hwnd);\n    WindowsApi::post_message(window.hwnd(), WM_CLOSE, 0, 0)?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn weg_kill_app(hwnd: isize) -> Result<()> {\n    let window = Window::from(hwnd);\n    get_app_handle()\n        .shell()\n        .command(\"taskkill.exe\")\n        .args([\"/F\", \"/PID\", &window.process().id().to_string()])\n        .spawn()?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn weg_toggle_window_state(hwnd: isize, was_focused: bool) -> Result<()> {\n    let window = Window::from(hwnd);\n    // was_focused is intented to know if the window was focused before click on the dock item\n    // on click the items makes the dock being focused.\n    if was_focused {\n        // Got to prevent the activation, because the click initialed as Seelen in focus, and the\n        // activation here will make this assigned to an app, which is not properly focused, just activated.\n        window.show_window_async(SW_SHOWMINNOACTIVE)?;\n    } else {\n        window.focus()?;\n    }\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn weg_pin_item(path: PathBuf) -> Result<()> {\n    if !path.exists() || path.is_dir() {\n        return Err(\"Invalid path\".into());\n    }\n\n    let umid = WindowsApi::get_file_umid(&path).ok();\n    let display_name = WindowsApi::get_executable_display_name(&path).unwrap_or_else(|_| {\n        path.file_stem()\n            .map(|s| s.to_string_lossy().to_string())\n            .unwrap_or_else(|| \"Unknown\".to_string())\n    });\n\n    let item = WegItemData {\n        id: uuid::Uuid::new_v4(),\n        display_name,\n        umid,\n        path,\n        pinned: true,\n        prevent_pinning: false,\n        relaunch: None,\n    };\n\n    emit_to_webviews(SeelenEvent::WegAddItem, &item);\n    Ok(())\n}\n"
  },
  {
    "path": "src/background/widgets/weg/hook.rs",
    "content": "use windows::Win32::{\n    Foundation::HWND,\n    UI::WindowsAndMessaging::{\n        FindWindowExA, EVENT_OBJECT_CREATE, EVENT_OBJECT_SHOW, EVENT_OBJECT_UNCLOAKED, SW_HIDE,\n    },\n};\n\nuse crate::{\n    error::Result,\n    pcstr,\n    windows_api::window::event::WinEvent,\n    windows_api::{window::Window, WindowsApi},\n};\n\nuse super::{SeelenWeg, TASKBAR_CLASS};\n\nimpl SeelenWeg {\n    pub fn process_individual_win_event(&mut self, event: WinEvent, origin: &Window) -> Result<()> {\n        if event == WinEvent::ObjectLocationChange && origin.hwnd() == self.hwnd()? {\n            self.reposition_if_needed()?;\n        }\n        Ok(())\n    }\n\n    // move this to independent function as this should work independently if dock is enabled or not\n    pub fn process_raw_win_event(event: u32, origin_hwnd: HWND) -> Result<()> {\n        let origin = Window::from(origin_hwnd);\n        match event {\n            EVENT_OBJECT_UNCLOAKED => {\n                let class = origin.class();\n                // this will hide native notification center and notifications preview\n                if class == \"Windows.UI.Core.CoreWindow\"\n                    && origin\n                        .process()\n                        .program_exe_name()\n                        .is_ok_and(|n| n == \"ShellExperienceHost.exe\")\n                {\n                    let _ = WindowsApi::show_window_async(origin_hwnd, SW_HIDE);\n                    return Ok(());\n                }\n            }\n            EVENT_OBJECT_SHOW | EVENT_OBJECT_CREATE => {\n                let class = origin.class();\n                let parent_class = origin.parent().map(|p| p.class()).unwrap_or_default();\n                if TASKBAR_CLASS\n                    .iter()\n                    .any(|t| t == &class || t == &parent_class)\n                {\n                    Self::hide_native_taskbar();\n                    return Ok(());\n                }\n\n                if class.eq(\"XamlExplorerHostIslandWindow\") && origin.title().is_empty() {\n                    let content_hwnd = unsafe {\n                        FindWindowExA(\n                            Some(origin_hwnd),\n                            None,\n                            pcstr!(\"Windows.UI.Composition.DesktopWindowContentBridge\"),\n                            None,\n                        )\n                        .unwrap_or_default()\n                    };\n\n                    if !content_hwnd.is_invalid() {\n                        let input_hwnd = unsafe {\n                            FindWindowExA(\n                                Some(content_hwnd),\n                                None,\n                                pcstr!(\"Windows.UI.Input.InputSite.WindowClass\"),\n                                None,\n                            )\n                            .unwrap_or_default()\n                        };\n                        if !input_hwnd.is_invalid() {\n                            // can fail on volume window island\n                            let _ = WindowsApi::show_window_async(input_hwnd, SW_HIDE);\n                        }\n                        // can fail on volume window island\n                        let _ = WindowsApi::show_window_async(content_hwnd, SW_HIDE);\n                        WindowsApi::show_window_async(origin_hwnd, SW_HIDE)?;\n                    }\n                }\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/weg/instance.rs",
    "content": "use base64::Engine;\nuse seelen_core::state::{FancyToolbarSide, HideMode, SeelenWegSide};\nuse tauri::{WebviewWindow, Wry};\nuse windows::Win32::{\n    Foundation::{HWND, RECT},\n    UI::WindowsAndMessaging::{SWP_ASYNCWINDOWPOS, SWP_NOSIZE},\n};\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    log_error,\n    state::application::FULL_STATE,\n    widgets::{toolbar::FancyToolbar, webview::WebviewArgs},\n    windows_api::{monitor::Monitor, AppBarData, WindowsApi},\n};\n\npub struct SeelenWeg {\n    pub window: WebviewWindow<Wry>,\n    /// This is the GUI rect of the dock, not used as webview window rect\n    pub theoretical_rect: RECT,\n    /// This is the webview/window rect\n    pub webview_rect: RECT,\n}\n\nimpl Drop for SeelenWeg {\n    fn drop(&mut self) {\n        log::info!(\"Dropping {}\", self.window.label());\n        if let Ok(hwnd) = self.hwnd() {\n            AppBarData::from_handle(hwnd).unregister_bar();\n        }\n        log_error!(self.window.destroy());\n    }\n}\n\nimpl SeelenWeg {\n    pub const TITLE: &'static str = \"SeelenWeg\";\n    pub const TARGET: &'static str = \"@seelen/weg\";\n\n    pub fn hwnd(&self) -> Result<HWND> {\n        Ok(HWND(self.window.hwnd()?.0))\n    }\n\n    fn create_window(monitor_id: &str) -> Result<WebviewWindow> {\n        let manager = get_app_handle();\n        let label = format!(\"{}?monitorId={}\", Self::TARGET, monitor_id);\n        let args = WebviewArgs::default();\n\n        log::info!(\"Creating {label}\");\n        let label = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&label);\n\n        let window = tauri::WebviewWindowBuilder::new(\n            manager,\n            label,\n            tauri::WebviewUrl::App(\"react/weg/index.html\".into()),\n        )\n        .title(Self::TITLE)\n        .minimizable(false)\n        .maximizable(false)\n        .closable(false)\n        .resizable(false)\n        .visible(false)\n        .decorations(false)\n        .transparent(true)\n        .shadow(false)\n        .skip_taskbar(true)\n        .always_on_top(true)\n        .data_directory(args.data_directory())\n        .additional_browser_args(&args.to_string())\n        .build()?;\n\n        window.set_ignore_cursor_events(true)?;\n        Ok(window)\n    }\n\n    pub fn new(monitor_id: &str) -> Result<Self> {\n        let weg = Self {\n            window: Self::create_window(monitor_id)?,\n            theoretical_rect: RECT::default(),\n            webview_rect: RECT::default(),\n        };\n        SeelenWeg::hide_native_taskbar();\n        Ok(weg)\n    }\n\n    pub fn get_weg_size_on_monitor(monitor: &Monitor) -> Result<i32> {\n        let state = FULL_STATE.load();\n        let settings: &seelen_core::state::SeelenWegSettings = &state.settings.by_widget.weg;\n        let total_size = (settings.total_size() as f64 * monitor.scale_factor()?) as i32;\n        Ok(total_size)\n    }\n\n    pub fn set_position(&mut self, monitor: &Monitor) -> Result<()> {\n        let state = FULL_STATE.load();\n        let toolbar_config = &state.settings.by_widget.fancy_toolbar;\n        let is_toolbar_enabled = state.is_bar_enabled_on_monitor(&monitor.stable_id()?);\n\n        let settings = &state.settings.by_widget.weg;\n\n        let hwnd = HWND(self.hwnd()?.0);\n        let monitor_info = WindowsApi::monitor_info(monitor.handle())?;\n\n        let mut work_area = monitor_info.monitorInfo.rcMonitor;\n        if is_toolbar_enabled && toolbar_config.hide_mode != HideMode::Always {\n            let toolbar_size = FancyToolbar::get_toolbar_height_on_monitor(monitor)?;\n            match state.settings.by_widget.fancy_toolbar.position {\n                FancyToolbarSide::Top => {\n                    work_area.top += toolbar_size;\n                }\n                FancyToolbarSide::Bottom => {\n                    work_area.bottom -= toolbar_size;\n                }\n            }\n        }\n\n        self.theoretical_rect = work_area;\n        self.webview_rect = work_area;\n\n        // note: we reduce by 10px the webview size of the dock to avoid be matched as a fullscreen window\n        let dock_size = Self::get_weg_size_on_monitor(monitor)?;\n        match settings.position {\n            SeelenWegSide::Left => {\n                self.theoretical_rect.right = self.theoretical_rect.left + dock_size;\n                self.webview_rect.right = work_area.right - (work_area.right - work_area.left) / 2;\n            }\n            SeelenWegSide::Right => {\n                self.theoretical_rect.left = self.theoretical_rect.right - dock_size;\n                self.webview_rect.left = work_area.left + (work_area.right - work_area.left) / 2;\n            }\n            SeelenWegSide::Top => {\n                self.theoretical_rect.bottom = self.theoretical_rect.top + dock_size;\n                self.webview_rect.bottom = work_area.top + (work_area.bottom - work_area.top) / 2;\n            }\n            SeelenWegSide::Bottom => {\n                self.theoretical_rect.top = self.theoretical_rect.bottom - dock_size;\n                self.webview_rect.top = work_area.bottom - (work_area.bottom - work_area.top) / 2;\n            }\n        }\n\n        let mut abd = AppBarData::from_handle(hwnd);\n        match settings.hide_mode {\n            HideMode::Never => {\n                abd.set_edge(settings.position.into());\n                abd.set_rect(self.theoretical_rect);\n                abd.register_as_new_bar();\n            }\n            _ => abd.unregister_bar(),\n        };\n\n        // pre set position for resize in case of multiples dpi\n        WindowsApi::set_position(hwnd, None, &self.webview_rect, SWP_NOSIZE)?;\n        WindowsApi::set_position(hwnd, None, &self.webview_rect, SWP_ASYNCWINDOWPOS)?;\n        Ok(())\n    }\n\n    pub fn reposition_if_needed(&mut self) -> Result<()> {\n        let hwnd = self.hwnd()?;\n        if self.webview_rect == WindowsApi::get_outer_window_rect(hwnd)? {\n            return Ok(()); // position is ok no need to reposition\n        }\n        self.set_position(&Monitor::from(WindowsApi::monitor_from_window(hwnd)))?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/weg/mod.rs",
    "content": "pub mod cli;\npub mod handler;\npub mod hook;\npub mod instance;\n\npub use instance::SeelenWeg;\n\nuse std::thread::JoinHandle;\n\nuse windows::Win32::{\n    Foundation::HWND,\n    UI::WindowsAndMessaging::{SW_HIDE, SW_SHOWNORMAL},\n};\n\nuse crate::{\n    error::Result,\n    state::application::FULL_STATE,\n    utils::sleep_millis,\n    windows_api::{AppBarData, AppBarDataState, WindowEnumerator, WindowsApi},\n};\n\n// ====================\n// TASKBAR HIDDEN LOGIC\n// ====================\n\npub static TASKBAR_CLASS: [&str; 2] = [\"Shell_TrayWnd\", \"Shell_SecondaryTrayWnd\"];\n\npub fn get_taskbars_handles() -> Result<Vec<HWND>> {\n    let mut founds = Vec::new();\n    WindowEnumerator::new().for_each(|w| {\n        if TASKBAR_CLASS.contains(&w.class().as_str()) && w.title().is_empty() {\n            founds.push(w.hwnd());\n        }\n    })?;\n    Ok(founds)\n}\n\nimpl SeelenWeg {\n    pub fn hide_native_taskbar() -> JoinHandle<()> {\n        std::thread::spawn(move || match get_taskbars_handles() {\n            Ok(handles) => {\n                let mut attempts = 0;\n                while attempts < 10 && FULL_STATE.load().is_weg_enabled() {\n                    for handle in &handles {\n                        let app_bar = AppBarData::from_handle(*handle);\n                        app_bar.set_state(AppBarDataState::AutoHide);\n                        let _ = WindowsApi::show_window_async(*handle, SW_HIDE);\n                    }\n                    attempts += 1;\n                    sleep_millis(50);\n                }\n            }\n            Err(err) => log::error!(\"Failed to get taskbars handles: {err:?}\"),\n        })\n    }\n\n    pub fn restore_native_taskbar() -> Result<()> {\n        for hwnd in get_taskbars_handles()? {\n            AppBarData::from_handle(hwnd).set_state(AppBarDataState::AlwaysOnTop);\n            WindowsApi::show_window_async(hwnd, SW_SHOWNORMAL)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/weg/weg_items_impl.rs",
    "content": "/* use parking_lot::Mutex;\nuse seelen_core::{\n    handlers::SeelenEvent,\n    state::{\n        PinnedWegItemData, RelaunchArguments, WegItem, WegItemSubtype, WegItems,\n        WegPinnedItemsVisibility,\n    },\n    system_state::MonitorId,\n};\nuse std::{\n    collections::HashMap,\n    ffi::OsStr,\n    path::PathBuf,\n    sync::{Arc, LazyLock},\n};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::{Result, ResultLogExt},\n    modules::{\n        apps::application::{UserAppWinEvent, UserAppsManager, USER_APPS_MANAGER},\n        start::application::StartMenuManager,\n    },\n    state::application::FULL_STATE,\n    trace_lock,\n    utils::icon_extractor::{request_icon_extraction_from_file, request_icon_extraction_from_umid},\n    windows_api::{types::AppUserModelId, window::Window, MonitorEnumerator},\n};\n\npub static SEELEN_WEG_STATE: LazyLock<Arc<Mutex<SeelenWegState>>> =\n    LazyLock::new(|| Arc::new(Mutex::new(SeelenWegState::new())));\n\n#[derive(Debug, Clone)]\npub struct SeelenWegState {\n    pub items: WegItems,\n}\n\nimpl SeelenWegState {\n    pub fn new() -> Self {\n        let mut state = SeelenWegState {\n            items: FULL_STATE.load().weg_items.clone(),\n        };\n\n        UserAppsManager::subscribe(|e| {\n            if let UserAppWinEvent::Added(addr) = e {\n                let mut guard = trace_lock!(SEELEN_WEG_STATE);\n                if guard.add(&Window::from(addr)).unwrap_or(false) {\n                    guard.emit_to_webview().log_error();\n                }\n            }\n        });\n\n        USER_APPS_MANAGER.interactable_windows.for_each(|w| {\n            state.add(&Window::from(w.hwnd)).log_error();\n        });\n\n        state\n    }\n\n\n    pub fn iter_all(&self) -> impl Iterator<Item = &WegItem> {\n        self.items\n            .left\n            .iter()\n            .chain(self.items.center.iter())\n            .chain(self.items.right.iter())\n    }\n\n    /// Adds a Temporal item for the given window's app if no matching item already exists.\n    /// Returns `true` if a new item was added, `false` if one already existed.\n    pub fn add(&mut self, window: &Window) -> Result<bool> {\n        // we get the path of the creator in case of Application Frame Host, Web apps\n        let mut path = match window.get_frame_creator() {\n            Ok(None) => return Ok(false),\n            Ok(Some(creator)) => creator.process().program_path()?,\n            Err(_) => window.process().program_path()?,\n        };\n\n        let umid = window.app_user_model_id();\n        let mut display_name = window\n            .app_display_name()\n            .unwrap_or_else(|_| String::from(\"Unknown\"));\n\n        let (relaunch_program, relaunch_args) = if let Some(umid) = &umid {\n            match umid {\n                AppUserModelId::Appx(umid) => {\n                    // pre-extraction to avoid flickering on the ui\n                    request_icon_extraction_from_umid(&AppUserModelId::Appx(umid.clone()));\n                    (format!(\"shell:AppsFolder\\\\{umid}\"), None)\n                }\n                AppUserModelId::PropertyStore(umid) => {\n                    let start_menu_manager = StartMenuManager::instance();\n                    let shortcut = start_menu_manager.get_by_file_umid(umid);\n\n                    // some apps like librewolf don't have a shortcut with the same umid\n                    if let Some(shortcut) = &shortcut {\n                        // pre-extraction to avoid flickering on the ui\n                        request_icon_extraction_from_umid(&AppUserModelId::PropertyStore(\n                            umid.clone(),\n                        ));\n                        path = shortcut.path.clone();\n                        display_name = path\n                            .file_stem()\n                            .unwrap_or_else(|| OsStr::new(\"Unknown\"))\n                            .to_string_lossy()\n                            .to_string();\n                    } else {\n                        // pre-extraction to avoid flickering on the ui\n                        request_icon_extraction_from_file(&path);\n                    }\n\n                    // System.AppUserModel.RelaunchCommand and System.AppUserModel.RelaunchDisplayNameResource\n                    // must always be set together. If one of those properties is not set, then neither is used.\n                    // https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchcommand\n                    if let (Some(relaunch_command), Some(relaunch_display_name)) =\n                        (window.relaunch_command(), window.relaunch_display_name())\n                    {\n                        display_name = relaunch_display_name;\n                        get_parts_of_inline_command(&relaunch_command)\n                    } else if shortcut.is_some() {\n                        (format!(\"shell:AppsFolder\\\\{umid}\"), None)\n                    } else {\n                        // process program path\n                        (path.to_string_lossy().to_string(), None)\n                    }\n                }\n            }\n        } else {\n            // pre-extraction to avoid flickering on the ui\n            request_icon_extraction_from_file(&path);\n            (path.to_string_lossy().to_string(), None)\n        };\n\n        let umid = umid.map(|umid| umid.to_string());\n        let relaunch_args = relaunch_args.map(RelaunchArguments::String);\n\n        // groups order documented on https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-id\n        // group should be by umid, if not present then the groups are done by relaunch command\n        // and in last case the groups are done by process id/path\n        for item in self.iter_all() {\n            match item {\n                WegItem::Pinned(current) | WegItem::Temporal(current) => {\n                    if (current.umid.is_some() && current.umid == umid)\n                        || (current.relaunch_program.to_lowercase()\n                            == relaunch_program.to_lowercase()\n                            && current.relaunch_args == relaunch_args)\n                    {\n                        return Ok(false);\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        let data = PinnedWegItemData {\n            id: uuid::Uuid::new_v4().to_string(),\n            subtype: WegItemSubtype::App,\n            umid,\n            path,\n            relaunch_program,\n            relaunch_args,\n            relaunch_in: None,\n            display_name,\n            pin_disabled: window.prevent_pinning(),\n        };\n        self.items.center.push(WegItem::Temporal(data));\n        Ok(true)\n    }\n}\n */\n"
  },
  {
    "path": "src/background/widgets/window_manager/cli.rs",
    "content": "use clap::ValueEnum;\nuse serde::{Deserialize, Serialize};\n\nuse crate::error::Result;\nuse crate::state::application::FULL_STATE;\nuse crate::trace_lock;\nuse crate::virtual_desktops::SluWorkspacesManager2;\nuse crate::widgets::window_manager::state::node_ext::WmNodeExt;\nuse crate::widgets::window_manager::state::{WmState, WmStateEvent, WmWorkspaceState};\nuse crate::windows_api::window::Window;\n\nuse super::state::WM_STATE;\n\n#[derive(Debug, Clone, Serialize, Deserialize, ValueEnum)]\npub enum AllowedReservations {\n    Left,\n    Right,\n    Top,\n    Bottom,\n    Stack,\n    Float,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, ValueEnum)]\npub enum NodeSiblingSide {\n    Left,\n    Right,\n    Up,\n    Down,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]\npub enum Sizing {\n    Increase,\n    Decrease,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]\npub enum StepWay {\n    Next,\n    Prev,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]\npub enum Axis {\n    Horizontal,\n    Vertical,\n    Top,\n    Bottom,\n    Left,\n    Right,\n}\n\n/// Manage the Seelen Window Manager.\n#[derive(Debug, Serialize, Deserialize, clap::Args)]\n#[command(alias = \"wm\")]\npub struct WindowManagerCli {\n    #[command(subcommand)]\n    pub subcommand: WmCommand,\n}\n\n#[derive(Debug, Serialize, Deserialize, clap::Subcommand)]\npub enum WmCommand {\n    /// Open Dev Tools (only works if the app is running in dev mode)\n    Debug,\n    /// Toggles the Seelen Window Manager.\n    Toggle,\n    /// Reserve space for a incoming window.\n    Reserve {\n        /// The position of the new window.\n        side: AllowedReservations,\n    },\n    /// Cancels the current reservation\n    CancelReservation,\n    /// Increases or decreases the size of the window\n    Width {\n        /// What to do with the width.\n        action: Sizing,\n    },\n    /// Increases or decreases the size of the window\n    Height {\n        /// What to do with the height.\n        action: Sizing,\n    },\n    /// Resets the size of the containers in current workspace to the default size.\n    ResetWorkspaceSize,\n    /// Toggles the floating state of the window\n    ToggleFloat,\n    /// Toggles workspace layout mode to monocle (single stack)\n    ToggleMonocle,\n    /// Moves the window to the specified position\n    Move { side: NodeSiblingSide },\n    /// Cycles the foregrounf node if it is a stack\n    CycleStack { way: StepWay },\n    /// Focuses the window in the specified position.\n    Focus {\n        /// The position of the window to focus.\n        side: NodeSiblingSide,\n    },\n}\n\nimpl WindowManagerCli {\n    pub fn process(self) -> Result<()> {\n        self.subcommand.process()\n    }\n}\n\nimpl WmCommand {\n    pub fn process(self) -> Result<()> {\n        let foreground = Window::get_foregrounded();\n\n        match self {\n            WmCommand::Toggle => {\n                FULL_STATE.rcu(move |state| {\n                    let mut state = state.cloned();\n                    state.settings.by_widget.wm.enabled = !state.settings.by_widget.wm.enabled;\n                    state\n                });\n                FULL_STATE.load().write_settings()?;\n            }\n            WmCommand::Debug => {\n                #[cfg(debug_assertions)]\n                {\n                    let guard = trace_lock!(crate::app::SEELEN);\n                    for instance in &guard.widgets_per_display {\n                        if let Some(wm) = &instance.wm {\n                            wm.window.open_devtools();\n                        }\n                    }\n                }\n            }\n            WmCommand::Width { action } => {\n                let percentage = match action {\n                    Sizing::Increase => FULL_STATE.load().settings.by_widget.wm.resize_delta,\n                    Sizing::Decrease => -FULL_STATE.load().settings.by_widget.wm.resize_delta,\n                };\n\n                let state = trace_lock!(WM_STATE);\n                state.update_size(&foreground, Axis::Horizontal, percentage, false)?;\n            }\n            WmCommand::Height { action } => {\n                let percentage = match action {\n                    Sizing::Increase => FULL_STATE.load().settings.by_widget.wm.resize_delta,\n                    Sizing::Decrease => -FULL_STATE.load().settings.by_widget.wm.resize_delta,\n                };\n\n                let state = trace_lock!(WM_STATE);\n                state.update_size(&foreground, Axis::Vertical, percentage, false)?;\n            }\n            WmCommand::Reserve { .. } => {\n                // self.reserve(side)?;\n            }\n            WmCommand::CancelReservation => {\n                // self.discard_reservation()?;\n            }\n            WmCommand::ResetWorkspaceSize => {\n                let mut state = trace_lock!(WM_STATE);\n                if let Some(workspace) = state.get_workspace_state_for_window(&foreground) {\n                    if workspace.is_floating(&foreground.address()) {\n                        WmWorkspaceState::set_rect_to_float_initial_size(&foreground)?;\n                    }\n                }\n            }\n            WmCommand::ToggleFloat => {\n                let mut state = trace_lock!(WM_STATE);\n                if let Some(workspace) = state.get_workspace_state_for_window(&foreground) {\n                    if workspace.is_floating(&foreground.address()) {\n                        workspace.add_to_tiles(&foreground);\n                    } else if workspace.is_tiled(&foreground) {\n                        workspace.unmanage(&foreground);\n                        workspace.add_to_floats(&foreground)?;\n                    }\n                }\n            }\n            WmCommand::ToggleMonocle => {\n                let monitor_id = foreground.monitor_id();\n                let workspace = SluWorkspacesManager2::instance()\n                    .monitors\n                    .get(&monitor_id, |m| m.active_workspace_id().clone())\n                    .ok_or(\"Monitor not found\")?;\n\n                let mut state = trace_lock!(WM_STATE);\n                let workspace = state.get_workspace_state(&workspace);\n                workspace.toggle_monocle();\n            }\n            WmCommand::Focus { side } => {\n                let mut state = trace_lock!(WM_STATE);\n                if let Some(workspace) = state.get_workspace_state_for_window(&foreground) {\n                    let siblings = workspace\n                        .layout\n                        .structure\n                        .get_siblings_at_side(&foreground, &side);\n                    match siblings.first().and_then(|sibling| sibling.face()) {\n                        Some(sibling) => {\n                            sibling.focus()?;\n                        }\n                        None => {\n                            log::warn!(\"There is no node at {side:?} to be focused\");\n                        }\n                    }\n                }\n            }\n            WmCommand::Move { side } => {\n                let mut state = trace_lock!(WM_STATE);\n                if let Some(workspace) = state.get_workspace_state_for_window(&foreground) {\n                    let siblings = workspace\n                        .layout\n                        .structure\n                        .get_siblings_at_side(&foreground, &side);\n\n                    match siblings.first().and_then(|sibling| sibling.face()) {\n                        Some(sibling) => {\n                            workspace.swap_nodes_containing_window(&foreground, &sibling)?;\n                        }\n                        None => {\n                            log::warn!(\"There is no node at {side:?} to be swapped\");\n                        }\n                    }\n                }\n            }\n            WmCommand::CycleStack { way } => {\n                let mut state = trace_lock!(WM_STATE);\n                let Some(workspace) = state.get_workspace_state_for_window(&foreground) else {\n                    return Ok(());\n                };\n                let Some(node) = workspace.layout.structure.leaf_containing_mut(&foreground) else {\n                    return Ok(());\n                };\n\n                let active = node.active.ok_or(\"No active window\")?;\n                let idx = node\n                    .windows\n                    .iter()\n                    .position(|w| *w == active)\n                    .ok_or(\"No active window\")?;\n\n                let len = node.windows.len();\n                let next_idx = if way == StepWay::Next {\n                    (idx + 1) % len // next and cycle\n                } else {\n                    (idx + (len - 1)) % len // prev and cycle\n                };\n\n                node.active = Some(node.windows[next_idx]);\n                node.process_stacks()?;\n                WmState::send(WmStateEvent::Changed);\n            }\n        };\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/window_manager/handler.rs",
    "content": "use std::{\n    collections::HashMap,\n    sync::{LazyLock, Once},\n};\n\nuse slu_ipc::messages::SvcAction;\nuse windows::Win32::UI::WindowsAndMessaging::SW_NORMAL;\n\nuse crate::{\n    app::emit_to_webviews,\n    cli::ServicePipe,\n    error::Result,\n    state::application::{performance::PERFORMANCE_MODE, FULL_STATE},\n    widgets::window_manager::state::{WmState, WM_LAYOUT_RECTS, WM_STATE},\n    windows_api::{window::Window, WindowsApi},\n};\nuse seelen_core::{\n    handlers::SeelenEvent,\n    rect::Rect,\n    state::{PerformanceMode, WmRenderTree},\n};\n\nstatic SCHEDULED_POSITIONS: LazyLock<scc::HashMap<isize, Rect>> = LazyLock::new(scc::HashMap::new);\n\n/// will schedule the position to be sent in a batch on the next window manager update\npub fn schedule_window_position(window: isize, rect: Rect) {\n    SCHEDULED_POSITIONS.upsert(window, rect);\n}\n\n#[tauri::command(async)]\npub fn set_app_windows_positions(positions: HashMap<isize, Rect>) -> Result<()> {\n    let mut list = HashMap::new();\n\n    // map and filter step\n    for (hwnd, rect) in positions {\n        let window = Window::from(hwnd);\n        if !window.is_window() || window.is_minimized() {\n            continue;\n        }\n\n        WM_LAYOUT_RECTS.upsert(hwnd, rect.clone());\n        // avoid to move window while dragging\n        if window.is_dragging() {\n            continue;\n        }\n\n        if window.is_maximized() {\n            window.show_window(SW_NORMAL)?; // unmaximize\n        }\n\n        let shadow = WindowsApi::shadow_rect(window.hwnd())?;\n        let desired_rect = Rect {\n            top: rect.top + shadow.top,\n            left: rect.left + shadow.left,\n            right: rect.right + shadow.right,\n            bottom: rect.bottom + shadow.bottom,\n        };\n        list.insert(hwnd, desired_rect);\n    }\n\n    SCHEDULED_POSITIONS.scan(|k, v| {\n        let window = Window::from(*k);\n        if window.is_window() && !window.is_minimized() && !window.is_dragging() {\n            list.insert(*k, v.clone());\n        }\n    });\n    SCHEDULED_POSITIONS.clear();\n\n    let state = FULL_STATE.load();\n    let perf_mode = PERFORMANCE_MODE.load();\n    let place_animated =\n        state.settings.by_widget.wm.animations.enabled && **perf_mode == PerformanceMode::Disabled;\n\n    ServicePipe::request(SvcAction::DeferWindowPositions {\n        list,\n        animated: place_animated,\n        animation_duration: state.settings.by_widget.wm.animations.duration_ms,\n        easing: state.settings.by_widget.wm.animations.ease_function.clone(),\n    })?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn request_focus(hwnd: isize) -> Result<()> {\n    let window = Window::from(hwnd);\n    if !window.is_window() {\n        return Ok(());\n    }\n    window.focus()?;\n    Ok(())\n}\n\n#[tauri::command(async)]\npub fn wm_get_render_tree() -> WmRenderTree {\n    static TAURI_EVENT_REGISTRATION: Once = Once::new();\n    TAURI_EVENT_REGISTRATION.call_once(|| {\n        WmState::subscribe(|_event| {\n            emit_to_webviews(\n                SeelenEvent::WMTreeChanged,\n                &WM_STATE.lock().get_render_tree(),\n            );\n        });\n    });\n\n    WM_STATE.lock().get_render_tree()\n}\n"
  },
  {
    "path": "src/background/widgets/window_manager/hook.rs",
    "content": "use seelen_core::state::{WmDragBehavior, WmNodeKind};\n\nuse crate::{\n    error::Result,\n    state::application::FULL_STATE,\n    trace_lock,\n    virtual_desktops::MINIMIZED_BY_WORKSPACES,\n    widgets::window_manager::state::node_ext::WmNodeExt,\n    windows_api::window::{event::WinEvent, Window},\n};\n\nuse super::{\n    cli::Axis,\n    state::{WM_LAYOUT_RECTS, WM_STATE},\n    WindowManagerV2,\n};\n\nimpl WindowManagerV2 {\n    fn system_move_size_end(window: &Window) -> Result<()> {\n        let mut state = trace_lock!(WM_STATE);\n        if !state.is_tiled(window) {\n            return Ok(());\n        }\n\n        let initial_rect = window.get_rect_before_dragging()?;\n        let end_rect = window.inner_rect()?;\n\n        let initial_width = (initial_rect.right - initial_rect.left) as f32;\n        let initial_height = (initial_rect.bottom - initial_rect.top) as f32;\n\n        let new_width = (end_rect.right - end_rect.left) as f32;\n        let new_height = (end_rect.bottom - end_rect.top) as f32;\n\n        // not resized only dragged/moved\n        if initial_width == new_width && initial_height == new_height {\n            // Check drag behavior setting - only swap on drag end if set to Swap\n            let drag_behavior = FULL_STATE.load().settings.by_widget.wm.drag_behavior;\n            if drag_behavior == WmDragBehavior::Swap {\n                let Some(workspace) = state.get_workspace_state_for_window(window) else {\n                    return Ok(());\n                };\n\n                let current_rect = window.inner_rect()?;\n                if let Some(node) = workspace.get_nearest_node_to_rect(&current_rect) {\n                    if let Some(face) = node.face() {\n                        if &face != window\n                                // don't swap if nearest is not overlapped\n                                && current_rect.intersection(&face.inner_rect()?).is_some()\n                        {\n                            workspace.swap_nodes_containing_window(window, &face)?;\n                        }\n                    }\n                }\n            }\n            return Ok(());\n        }\n\n        if initial_width != new_width {\n            let percentage_diff = (new_width - initial_width) / initial_width * 100.0;\n            let axis = if end_rect.left == initial_rect.left {\n                Axis::Right\n            } else {\n                Axis::Left\n            };\n            state.update_size(window, axis, percentage_diff, true)?;\n            log::trace!(\"window width changed by: {percentage_diff}%\");\n        }\n\n        if initial_height != new_height {\n            let percentage_diff = (new_height - initial_height) / initial_height * 100.0;\n            let axis = if end_rect.top == initial_rect.top {\n                Axis::Bottom\n            } else {\n                Axis::Top\n            };\n            state.update_size(window, axis, percentage_diff, true)?;\n            log::trace!(\"window height changed by: {percentage_diff}%\");\n        }\n        Ok(())\n    }\n\n    fn synthetic_foreground_location_change(window: &Window) -> Result<()> {\n        // Only process if drag behavior is Sort and window is being dragged\n        let drag_behavior = FULL_STATE.load().settings.by_widget.wm.drag_behavior;\n        if drag_behavior != WmDragBehavior::Sort || !window.is_dragging() {\n            return Ok(());\n        }\n\n        let mut state = trace_lock!(WM_STATE);\n        if !state.is_tiled(window) {\n            return Ok(());\n        }\n\n        let Some(workspace) = state.get_workspace_state_for_window(window) else {\n            return Ok(());\n        };\n\n        let current_rect = window.inner_rect()?;\n        if let Some(node) = workspace.get_nearest_node_to_rect(&current_rect) {\n            if let Some(face) = node.face() {\n                if &face != window\n                        // don't swap if nearest is not overlapped\n                        && current_rect.intersection(&face.inner_rect()?).is_some()\n                {\n                    workspace.swap_nodes_containing_window(window, &face)?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn process_win_event(event: WinEvent, window: &Window) -> Result<()> {\n        match event {\n            WinEvent::SynThrottledForegroundRectChange => {\n                Self::synthetic_foreground_location_change(window)?;\n            }\n            WinEvent::SystemMoveSizeEnd => {\n                Self::system_move_size_end(window)?;\n                Self::force_retiling()?;\n            }\n            WinEvent::SystemMinimizeStart => {\n                if MINIMIZED_BY_WORKSPACES.contains(&window.address()) {\n                    return Ok(());\n                }\n\n                let mut should_remove = false;\n                let mut state = trace_lock!(WM_STATE);\n                if let Some(workspace) = state.get_workspace_state_for_window(window) {\n                    if let Some(node) = workspace.layout.structure.leaf_containing(window) {\n                        should_remove = node.kind != WmNodeKind::Stack;\n                    }\n                }\n\n                if should_remove {\n                    state.remove(window)?;\n                }\n            }\n            WinEvent::SystemMinimizeEnd => {\n                let mut state = trace_lock!(WM_STATE);\n                if !state.is_managed(window) && Self::should_be_managed(window.hwnd()) {\n                    state.add(window)?;\n                }\n            }\n            WinEvent::ObjectDestroy => {\n                // Clean up expected rect for destroyed window\n                WM_LAYOUT_RECTS.remove(&window.address());\n            }\n            _ => {}\n        };\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/window_manager/instance.rs",
    "content": "use base64::Engine;\nuse seelen_core::{\n    state::{FancyToolbarSide, HideMode, SeelenWegSide},\n    system_state::MonitorId,\n};\nuse tauri::WebviewWindow;\nuse windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS};\n\nuse crate::{\n    app::get_app_handle,\n    error::Result,\n    log_error,\n    state::application::FULL_STATE,\n    widgets::{toolbar::FancyToolbar, webview::WebviewArgs, weg::SeelenWeg},\n    windows_api::{monitor::Monitor, WindowsApi},\n};\n\npub struct WindowManagerV2 {\n    pub window: WebviewWindow,\n}\n\nimpl Drop for WindowManagerV2 {\n    fn drop(&mut self) {\n        log::info!(\"Dropping {}\", self.window.label());\n        log_error!(self.window.destroy());\n    }\n}\n\nimpl WindowManagerV2 {\n    pub const TITLE: &'static str = \"Seelen Window Manager\";\n    pub const TARGET: &'static str = \"@seelen/window-manager\";\n\n    pub fn new(monitor_id: &MonitorId) -> Result<Self> {\n        Ok(Self {\n            window: Self::create_window(monitor_id)?,\n        })\n    }\n\n    pub fn hwnd(&self) -> Result<HWND> {\n        Ok(HWND(self.window.hwnd()?.0))\n    }\n\n    fn create_window(monitor_id: &MonitorId) -> Result<WebviewWindow> {\n        let label = format!(\"{}?monitorId={}\", Self::TARGET, monitor_id);\n        log::info!(\"Creating {label}\");\n        let label = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&label);\n        let args = WebviewArgs::default();\n\n        let window = tauri::WebviewWindowBuilder::new(\n            get_app_handle(),\n            label,\n            tauri::WebviewUrl::App(\"svelte/window_manager/index.html\".into()),\n        )\n        .title(Self::TITLE)\n        .minimizable(false)\n        .maximizable(false)\n        .closable(false)\n        .resizable(false)\n        .visible(true)\n        .decorations(false)\n        .transparent(true)\n        .shadow(false)\n        .skip_taskbar(true)\n        .drag_and_drop(false)\n        .always_on_top(true)\n        .data_directory(args.data_directory())\n        .additional_browser_args(&args.to_string())\n        .build()?;\n\n        window.set_ignore_cursor_events(true)?;\n\n        Ok(window)\n    }\n\n    pub fn set_position(&self, monitor: &Monitor) -> Result<()> {\n        let state = FULL_STATE.load();\n        let toolbar_config = &state.settings.by_widget.fancy_toolbar;\n        let weg_config = &state.settings.by_widget.weg;\n\n        let is_toolbar_enabled = state.is_bar_enabled_on_monitor(&monitor.stable_id()?);\n        let is_weg_enabled = state.is_weg_enabled_on_monitor(&monitor.stable_id()?);\n\n        let hwnd = HWND(self.hwnd()?.0);\n        let monitor_info = WindowsApi::monitor_info(monitor.handle())?;\n\n        let mut rect = monitor_info.monitorInfo.rcMonitor;\n        if is_toolbar_enabled && toolbar_config.hide_mode == HideMode::Never {\n            let toolbar_size = FancyToolbar::get_toolbar_height_on_monitor(monitor)?;\n            match state.settings.by_widget.fancy_toolbar.position {\n                FancyToolbarSide::Top => {\n                    rect.top += toolbar_size;\n                }\n                FancyToolbarSide::Bottom => {\n                    rect.bottom -= toolbar_size;\n                }\n            }\n        }\n\n        if is_weg_enabled && weg_config.hide_mode == HideMode::Never {\n            let weg_size = SeelenWeg::get_weg_size_on_monitor(monitor)?;\n            match weg_config.position {\n                SeelenWegSide::Top => {\n                    rect.top += weg_size;\n                }\n                SeelenWegSide::Bottom => {\n                    rect.bottom -= weg_size;\n                }\n                SeelenWegSide::Left => {\n                    rect.left += weg_size;\n                }\n                SeelenWegSide::Right => {\n                    rect.right -= weg_size;\n                }\n            }\n        }\n\n        WindowsApi::move_window(hwnd, &rect)?;\n        WindowsApi::set_position(hwnd, None, &rect, SWP_ASYNCWINDOWPOS)?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/window_manager/mod.rs",
    "content": "pub mod cli;\npub mod handler;\npub mod hook;\npub mod instance;\npub mod state;\n\nuse instance::WindowManagerV2;\nuse seelen_core::{handlers::SeelenEvent, state::AppExtraFlag};\nuse windows::Win32::{\n    Foundation::HWND,\n    UI::WindowsAndMessaging::{WS_EX_TOPMOST, WS_SIZEBOX},\n};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::Result,\n    state::application::FULL_STATE,\n    windows_api::{window::Window, WindowsApi},\n};\n\nimpl WindowManagerV2 {\n    fn should_be_managed(hwnd: HWND) -> bool {\n        let window = Window::from(hwnd);\n        if !window.is_interactable_and_not_hidden() || window.is_minimized() {\n            return false;\n        }\n\n        if let Ok(Some(config)) = FULL_STATE.load().get_app_config_by_window(hwnd) {\n            if config.options.contains(&AppExtraFlag::VdPinned) {\n                return false;\n            }\n\n            if config.options.contains(&AppExtraFlag::WmForce) {\n                return true;\n            }\n\n            if config.options.contains(&AppExtraFlag::WmUnmanage) {\n                return false;\n            }\n        }\n\n        let styles = WindowsApi::get_styles(hwnd);\n        // Ignore windows that are not resizable\n        if !styles.contains(WS_SIZEBOX) {\n            return false;\n        }\n\n        let ex_styles = WindowsApi::get_ex_styles(hwnd);\n        // Top most windows normally are widgets or tools that should not be managed\n        if ex_styles.contains(WS_EX_TOPMOST) {\n            return false;\n        }\n\n        true\n    }\n\n    pub fn force_retiling() -> Result<()> {\n        emit_to_webviews(SeelenEvent::WMForceRetiling, ());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/window_manager/state/mod.rs",
    "content": "pub mod node_ext;\n\nuse std::{\n    collections::HashMap,\n    sync::{Arc, LazyLock},\n};\n\nuse itertools::Itertools;\nuse parking_lot::Mutex;\nuse seelen_core::{\n    rect::Rect,\n    state::{WindowManagerLayout, WmNode, WmNodeKind, WmRenderTree, WorkspaceId},\n    Point,\n};\n\nuse crate::{\n    error::{Result, ResultLogExt},\n    event_manager, log_error,\n    state::application::FULL_STATE,\n    trace_lock,\n    utils::lock_free::SyncHashMap,\n    virtual_desktops::{events::VirtualDesktopEvent, SluWorkspacesManager2},\n    widgets::window_manager::{\n        handler::{schedule_window_position, set_app_windows_positions},\n        instance::WindowManagerV2,\n    },\n    windows_api::window::Window,\n};\n\nuse super::cli::Axis;\nuse node_ext::WmNodeExt;\n\npub static WM_STATE: LazyLock<Arc<Mutex<WmState>>> = LazyLock::new(|| {\n    Arc::new(Mutex::new({\n        let mut state = WmState::default();\n        state.initialize();\n        state\n    }))\n});\n\npub static WM_LAYOUT_RECTS: LazyLock<SyncHashMap<isize, Rect>> = LazyLock::new(SyncHashMap::new);\n\n#[derive(Debug, Default)]\npub struct WmState {\n    pub layouts: HashMap<WorkspaceId, WmWorkspaceState>,\n}\n\n#[derive(Debug, Clone)]\npub enum WmStateEvent {\n    Changed,\n}\n\nevent_manager!(WmState, WmStateEvent);\n\nimpl WmState {\n    pub fn get_render_tree(&self) -> WmRenderTree {\n        let mut render_tree = HashMap::new();\n        let vd = SluWorkspacesManager2::instance();\n        vd.monitors.for_each(|(mid, monitor)| {\n            let active_workspace_id = monitor.active_workspace_id();\n            if let Some(w) = self.layouts.get(active_workspace_id) {\n                render_tree.insert(mid.clone(), w.layout.clone());\n            }\n        });\n        WmRenderTree(render_tree)\n    }\n\n    /// will enumarate all monitors and workspaces\n    pub fn initialize(&mut self) {\n        let vd = SluWorkspacesManager2::instance();\n        vd.monitors.for_each(|(_, monitor)| {\n            for workspace in &monitor.workspaces {\n                let mut w_state = WmWorkspaceState::new(&workspace.id);\n                for w in &workspace.windows {\n                    let window = Window::from(*w);\n                    if WindowManagerV2::should_be_managed(window.hwnd()) {\n                        w_state.add_to_tiles(&window);\n                    }\n                }\n                self.layouts.insert(workspace.id.clone(), w_state);\n            }\n        });\n\n        SluWorkspacesManager2::subscribe(|event| {\n            trace_lock!(WM_STATE).process_vd_event(&event).log_error();\n        });\n    }\n\n    pub fn process_vd_event(&mut self, event: &VirtualDesktopEvent) -> Result<()> {\n        match event {\n            VirtualDesktopEvent::DesktopChanged { .. } => {\n                // TODO: implement Self::discard_reservation()?;\n                Self::send(WmStateEvent::Changed);\n            }\n            VirtualDesktopEvent::WindowAdded { window, desktop: _ } => {\n                let window = &Window::from(*window);\n                if !self.is_managed(window) && WindowManagerV2::should_be_managed(window.hwnd()) {\n                    self.add(window)?;\n                }\n            }\n            VirtualDesktopEvent::WindowMoved { window, .. } => {\n                let window = &Window::from(*window);\n                if self.is_managed(window) {\n                    self.remove(window)?;\n                    self.add(window)?;\n                }\n            }\n            VirtualDesktopEvent::WindowRemoved { window } => {\n                let window = &Window::from(*window);\n                if self.is_managed(window) {\n                    self.remove(window)?;\n                }\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    pub fn add(&mut self, window: &Window) -> Result<()> {\n        let workspace_id = window.workspace_id()?;\n        let wm_workspace = self.get_workspace_state(&workspace_id);\n        wm_workspace.add_to_tiles(window);\n        Ok(())\n    }\n\n    pub fn remove(&mut self, window: &Window) -> Result<()> {\n        for workspace in self.layouts.values_mut() {\n            if workspace.is_managed(window) {\n                workspace.unmanage(window);\n                break;\n            }\n        }\n        Ok(())\n    }\n\n    pub fn is_managed(&self, window: &Window) -> bool {\n        self.layouts.values().any(|w| w.is_managed(window))\n    }\n\n    pub fn is_tiled(&self, window: &Window) -> bool {\n        self.layouts.values().any(|w| w.is_tiled(window))\n    }\n\n    pub fn get_workspace_state(&mut self, workspace: &WorkspaceId) -> &mut WmWorkspaceState {\n        self.layouts\n            .entry(workspace.clone())\n            .or_insert_with(|| WmWorkspaceState::new(workspace))\n    }\n\n    pub fn get_workspace_state_for_window(\n        &mut self,\n        window: &Window,\n    ) -> Option<&mut WmWorkspaceState> {\n        let window_id = window.address();\n        let vd = SluWorkspacesManager2::instance();\n\n        // Search for the workspace containing this window\n        let mut window_workspace: Option<WorkspaceId> = None;\n        vd.monitors.for_each(|(_, monitor)| {\n            for workspace in &monitor.workspaces {\n                if workspace.windows.contains(&window_id) {\n                    window_workspace = Some(workspace.id.clone());\n                    return;\n                }\n            }\n        });\n\n        let workspace_id = window_workspace?;\n        Some(self.get_workspace_state(&workspace_id))\n    }\n\n    /// # Parameters\n    ///\n    /// - `window`: A reference to the window whose size is being updated.\n    /// - `axis`: The axis along which the size update will occur (horizontal or vertical).\n    /// - `percentage`: The percentage by which the window size will be updated. Can be positive or negative.\n    /// - `relative`: Determines how the percentage is interpreted. If `true`, the percentage is relative to\n    ///   the current window size. If `false`, it's relative to the total workspace size.\n    ///\n    pub fn update_size(\n        &self,\n        window: &Window,\n        axis: Axis,\n        percentage: f32,\n        relative: bool,\n    ) -> Result<()> {\n        let monitor_id = window.monitor_id();\n        let vd = SluWorkspacesManager2::instance();\n\n        let current_workspace = vd\n            .monitors\n            .get(&monitor_id, |monitor| monitor.active_workspace_id().clone())\n            .ok_or(\"Monitor not found\")?;\n\n        let Some(w) = self.layouts.get(&current_workspace) else {\n            return Ok(());\n        };\n\n        if w.layout.floating_windows.contains(&window.address()) {\n            let mut rect = window.inner_rect()?;\n            match axis {\n                Axis::Horizontal | Axis::Left | Axis::Right => {\n                    let width = (rect.right - rect.left) as f32;\n                    let center_x = rect.left + (width / 2.0) as i32;\n                    let new_width = (width * (1.0 + percentage / 100.0)) as i32;\n                    rect.left = center_x - new_width / 2;\n                    rect.right = center_x + new_width / 2;\n                }\n                Axis::Vertical | Axis::Top | Axis::Bottom => {\n                    let height = (rect.bottom - rect.top) as f32;\n                    let center_y = rect.top + (height / 2.0) as i32;\n                    let new_height = (height * (1.0 + percentage / 100.0)) as i32;\n                    rect.top = center_y - new_height / 2;\n                    rect.bottom = center_y + new_height / 2;\n                }\n            };\n\n            let mut positions = HashMap::new();\n            positions.insert(window.address(), rect);\n            return set_app_windows_positions(positions);\n        }\n\n        let trace = w.trace(window);\n        if trace.is_empty() {\n            return Err(\"Trying to change size of an unmanaged window\".into());\n        }\n\n        let parent_to_search = match axis {\n            Axis::Horizontal | Axis::Left | Axis::Right => WmNodeKind::Horizontal,\n            Axis::Vertical | Axis::Top | Axis::Bottom => WmNodeKind::Vertical,\n        };\n\n        let first_matched_parent = trace.iter().rev().find(|n| {\n            n.kind == parent_to_search && n.children.iter().filter(|n| !n.is_empty()).count() >= 2\n        });\n\n        let Some(first_matched_parent) = first_matched_parent else {\n            log::warn!(\"Can't change size if the window is alone on axis\");\n            return Ok(());\n        };\n\n        let (node_of_window_idx, node_of_window) = first_matched_parent\n            .children\n            .iter()\n            .find_position(|n| n.contains(window))\n            .expect(\"The algorithm at the top of this function is wrong / broken\");\n\n        let siblings = first_matched_parent\n            .children\n            .iter()\n            .enumerate()\n            .filter(|(idx, n)| {\n                *idx != node_of_window_idx\n                    && match axis {\n                        Axis::Horizontal | Axis::Vertical => true,\n                        Axis::Left | Axis::Top => *idx < node_of_window_idx,\n                        Axis::Bottom | Axis::Right => *idx > node_of_window_idx,\n                    }\n                    && !n.is_empty()\n            })\n            .collect_vec();\n\n        if siblings.is_empty() {\n            log::warn!(\"Can't change size at {axis:?} if there are no other windows on that side\");\n            return Ok(());\n        }\n\n        let total_pie: f32 = siblings.iter().map(|(_, n)| n.grow_factor.get()).sum();\n        let window_portion = node_of_window.grow_factor.get();\n\n        let to_grow = if relative { window_portion } else { total_pie } * percentage / 100f32;\n        let to_reduce = to_grow / siblings.len() as f32;\n\n        node_of_window.grow_factor.set(window_portion + to_grow);\n        for (_, n) in siblings {\n            n.grow_factor.set(n.grow_factor.get() - to_reduce);\n        }\n\n        WmState::send(WmStateEvent::Changed);\n        Ok(())\n    }\n}\n\n#[derive(Debug)]\n#[allow(dead_code)]\npub struct WmWorkspaceState {\n    pub id: WorkspaceId,\n    pub layout: WindowManagerLayout,\n    pub monocle: bool,\n}\n\nimpl WmWorkspaceState {\n    pub fn new(workspace_id: &WorkspaceId) -> Self {\n        let settings = FULL_STATE.load();\n        let layout = settings.get_wm_layout(workspace_id);\n        Self {\n            id: workspace_id.clone(),\n            layout,\n            monocle: false,\n        }\n    }\n\n    pub fn set_rect_to_float_initial_size(window: &Window) -> Result<()> {\n        let guard = FULL_STATE.load();\n        let config = &guard.settings.by_widget.wm.floating;\n\n        let monitor = window.monitor();\n        let monitor_dpi = monitor.scale_factor()?;\n        let monitor_rect = monitor.rect()?;\n        let monitor_width = monitor_rect.right - monitor_rect.left;\n        let monitor_height = monitor_rect.bottom - monitor_rect.top;\n\n        let window_width = (config.width * monitor_dpi) as i32;\n        let window_height = (config.height * monitor_dpi) as i32;\n\n        let x = monitor_rect.left + (monitor_width - window_width) / 2;\n        let y = monitor_rect.top + (monitor_height - window_height) / 2;\n\n        schedule_window_position(\n            window.address(),\n            Rect {\n                left: x,\n                top: y,\n                right: x + window_width,\n                bottom: y + window_height,\n            },\n        );\n        Ok(())\n    }\n\n    pub fn add_to_floats(&mut self, window: &Window) -> Result<()> {\n        let window_id = window.address();\n        if self.is_floating(&window_id) {\n            return Ok(());\n        }\n\n        log::trace!(\"floating window ({window_id:x})\");\n        self.layout.floating_windows.push(window_id);\n        Self::set_rect_to_float_initial_size(window)?;\n\n        WmState::send(WmStateEvent::Changed);\n        Ok(())\n    }\n\n    pub fn add_to_tiles(&mut self, window: &Window) {\n        if self.is_tiled(window) {\n            return;\n        }\n        log::trace!(\"tiling window ({:x})\", window.address());\n\n        self.layout\n            .floating_windows\n            .retain(|w| w != &window.address());\n        let residual = self.layout.structure.add_window(window);\n        for w in residual {\n            log_error!(self.add_to_floats(&Window::from(w)));\n        }\n        WmState::send(WmStateEvent::Changed);\n    }\n\n    pub fn unmanage(&mut self, window: &Window) {\n        self.layout\n            .floating_windows\n            .retain(|w| w != &window.address());\n        let residual = self.layout.structure.remove_window(window);\n        for w in residual {\n            log_error!(self.add_to_floats(&Window::from(w)));\n        }\n        WmState::send(WmStateEvent::Changed);\n    }\n\n    pub fn is_floating(&self, window_id: &isize) -> bool {\n        self.layout.floating_windows.contains(window_id)\n    }\n\n    pub fn is_tiled(&self, window: &Window) -> bool {\n        self.layout.structure.contains(window)\n    }\n\n    pub fn is_managed(&self, window: &Window) -> bool {\n        self.is_floating(&window.address()) || self.is_tiled(window)\n    }\n\n    pub fn swap_nodes_containing_window(&mut self, a: &Window, b: &Window) -> Result<()> {\n        if a == b {\n            return Ok(());\n        }\n\n        log::trace!(\n            \"swapping nodes containing windows ({:x}, {:x})\",\n            a.address(),\n            b.address()\n        );\n        let ptr = &mut self.layout.structure as *mut WmNode;\n        let node_a = unsafe { &mut *ptr }.leaf_containing_mut(a);\n        let node_b = unsafe { &mut *ptr }.leaf_containing_mut(b);\n\n        if let (Some(node_a), Some(node_b)) = (node_a, node_b) {\n            std::mem::swap(&mut node_a.kind, &mut node_b.kind);\n            std::mem::swap(&mut node_a.active, &mut node_b.active);\n            std::mem::swap(&mut node_a.windows, &mut node_b.windows);\n        }\n\n        WmState::send(WmStateEvent::Changed);\n        Ok(())\n    }\n\n    pub fn trace(&self, window: &Window) -> Vec<&WmNode> {\n        self.layout.structure.trace(window)\n    }\n\n    #[allow(dead_code)]\n    pub fn get_node_at_point(&self, point: &Point) -> Option<&WmNode> {\n        self.layout.structure.get_node_at_point(point).ok()?\n    }\n\n    pub fn get_nearest_node_to_rect(&self, rect: &Rect) -> Option<&WmNode> {\n        self.layout\n            .structure\n            .get_nearest_node_to_rect(rect)\n            .ok()?\n            .map(|(n, _)| n)\n    }\n\n    pub fn toggle_monocle(&mut self) {\n        self.monocle = !self.monocle;\n\n        if self.monocle {\n            let windows = self.layout.structure.drain();\n            let active = windows.first().copied();\n\n            self.layout.structure = WmNode {\n                kind: WmNodeKind::Stack,\n                active,\n                windows,\n                max_stack_size: None,\n                ..Default::default()\n            };\n            self.layout.structure.process_stacks().log_error();\n        } else {\n            let windows = self.layout.structure.drain();\n            self.layout.structure = FULL_STATE.load().get_wm_layout(&self.id).structure;\n            for w in windows {\n                self.add_to_tiles(&Window::from(w));\n            }\n        }\n\n        WmState::send(WmStateEvent::Changed);\n    }\n}\n"
  },
  {
    "path": "src/background/widgets/window_manager/state/node_ext.rs",
    "content": "use evalexpr::{context_map, eval_with_context, HashMapContext};\nuse itertools::Itertools;\nuse seelen_core::{\n    state::{WmNode, WmNodeKind},\n    Point, Rect,\n};\nuse windows::Win32::UI::WindowsAndMessaging::{SW_FORCEMINIMIZE, SW_RESTORE};\n\nuse crate::{\n    error::Result,\n    widgets::window_manager::{cli::NodeSiblingSide, state::WM_LAYOUT_RECTS},\n    windows_api::window::Window,\n};\n\npub trait WmNodeExt {\n    /// validates the node condition and returns `true` if the node allows adding windows\n    fn is_enabled(&self, context: &HashMapContext) -> bool;\n\n    /// will drain the node and return a list of window handles\n    fn drain(&mut self) -> Vec<isize>;\n    /// trace the way to the window\n    fn trace(&self, window: &Window) -> Vec<&Self>;\n\n    fn get_node_at_point(&self, point: &Point) -> Result<Option<&Self>>;\n    fn get_nearest_node_to_rect(&self, rect: &Rect) -> Result<Option<(&Self, i32)>>;\n\n    /// trace and get the inmediate silbings of the node\n    fn get_siblings_at_side(&self, window: &Window, side: &NodeSiblingSide) -> Vec<&Self>;\n\n    /// check if the node contains the window\n    fn contains(&self, window: &Window) -> bool;\n    fn leaf_containing(&self, window: &Window) -> Option<&Self>;\n    fn leaf_containing_mut(&mut self, window: &Window) -> Option<&mut Self>;\n\n    /// will fail if the node is full\n    fn try_add_window(&mut self, window: &Window, context: &HashMapContext) -> Result<()>;\n    fn add_window(&mut self, window: &Window) -> Vec<isize>;\n    fn remove_window(&mut self, window: &Window) -> Vec<isize>;\n\n    /// gets the first leaf node having a window, follows node priority.\n    fn face(&self) -> Option<Window>;\n\n    fn process_stacks(&self) -> Result<()>;\n}\n\nfn create_context(len: usize, is_reindexing: bool) -> HashMapContext {\n    context_map! {\n        \"managed\" => len as i64,\n        \"is_reindexing\" => is_reindexing\n    }\n    .expect(\"Failed to create context\")\n}\n\nimpl WmNodeExt for WmNode {\n    fn is_enabled(&self, context: &HashMapContext) -> bool {\n        match &self.condition {\n            None => true,\n            Some(condition) => {\n                let result = eval_with_context(condition, context).and_then(|v| v.as_boolean());\n                result.is_ok_and(|is_enabled| is_enabled)\n            }\n        }\n    }\n\n    fn drain(&mut self) -> Vec<isize> {\n        let mut drained = Vec::new();\n        drained.append(&mut self.windows);\n        self.active = None;\n        for child in self.children.iter_mut() {\n            drained.append(&mut child.drain());\n        }\n        drained\n    }\n\n    fn contains(&self, window: &Window) -> bool {\n        self.leaf_containing(window).is_some()\n    }\n\n    fn leaf_containing(&self, window: &Window) -> Option<&Self> {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if self.windows.contains(&window.address()) {\n                    return Some(self);\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in self.children.iter() {\n                    if let Some(leaf) = child.leaf_containing(window) {\n                        return Some(leaf);\n                    }\n                }\n            }\n        }\n        None\n    }\n\n    fn leaf_containing_mut<'a>(&'a mut self, window: &Window) -> Option<&'a mut Self> {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if self.windows.contains(&window.address()) {\n                    return Some(self);\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in self.children.iter_mut() {\n                    if let Some(leaf) = child.leaf_containing_mut(window) {\n                        return Some(leaf);\n                    }\n                }\n            }\n        }\n        None\n    }\n\n    fn trace(&self, window: &Window) -> Vec<&Self> {\n        let mut nodes = Vec::new();\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if self.windows.contains(&window.address()) {\n                    nodes.push(self);\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in self.children.iter() {\n                    let mut sub_trace = child.trace(window);\n                    if !sub_trace.is_empty() {\n                        nodes.push(self);\n                        nodes.append(&mut sub_trace);\n                        break;\n                    }\n                }\n            }\n        }\n        nodes\n    }\n\n    fn get_siblings_at_side(&self, window: &Window, side: &NodeSiblingSide) -> Vec<&Self> {\n        let trace = self.trace(window);\n        let mut siblings = Vec::new();\n\n        let parent_to_search = match side {\n            NodeSiblingSide::Left | NodeSiblingSide::Right => WmNodeKind::Horizontal,\n            NodeSiblingSide::Up | NodeSiblingSide::Down => WmNodeKind::Vertical,\n        };\n\n        // first we search for containers of needed axis that has at least 2 children with some window on it\n        let matched_parents = trace.iter().rev().filter(|n| {\n            n.kind == parent_to_search && n.children.iter().filter(|n| !n.is_empty()).count() >= 2\n        });\n\n        for parent in matched_parents {\n            let (node_of_window_idx, _) = parent\n                .children\n                .iter()\n                .find_position(|n| n.contains(window))\n                .expect(\"The algorithm at the top of this function is wrong / broken\");\n\n            parent\n                .children\n                .iter()\n                .enumerate()\n                .filter(|(idx, n)| {\n                    *idx != node_of_window_idx\n                        && match side {\n                            NodeSiblingSide::Left | NodeSiblingSide::Up => {\n                                idx < &node_of_window_idx\n                            }\n                            NodeSiblingSide::Right | NodeSiblingSide::Down => {\n                                idx > &node_of_window_idx\n                            }\n                        }\n                        && !n.is_empty()\n                })\n                .for_each(|(_, n)| siblings.push(n));\n        }\n\n        siblings\n    }\n\n    fn try_add_window(&mut self, window: &Window, context: &HashMapContext) -> Result<()> {\n        let addr = window.address();\n\n        if !self.is_enabled(context) {\n            return Err(\"Node is disabled by condition\".into());\n        }\n\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if self.is_full() {\n                    return Err(\"FULL\".into());\n                }\n\n                if self.windows.contains(&addr) {\n                    return Ok(());\n                }\n\n                self.windows.push(addr);\n                self.active = Some(addr);\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in self\n                    .children\n                    .iter_mut()\n                    .sorted_by(|a, b| a.priority.cmp(&b.priority))\n                {\n                    if child.try_add_window(window, context).is_ok() {\n                        return Ok(());\n                    }\n                }\n                return Err(\"FULL\".into());\n            }\n        }\n\n        if self.kind == WmNodeKind::Stack {\n            self.process_stacks()?;\n        }\n        Ok(())\n    }\n\n    /// If adding the new window is successful, a reindexing will be done.\n    ///\n    /// **Note:** Reindexing can fail on add some windows so it will return failed handles as residual\n    fn add_window(&mut self, window: &Window) -> Vec<isize> {\n        let len = self.len();\n        let context = create_context(len, false);\n        if self.try_add_window(window, &context).is_err() {\n            return vec![window.address()];\n        }\n        // reindexing to handle logical condition like `managed < 4`\n        let context = create_context(len + 1, true);\n        let handles = self.drain();\n        let mut residual = Vec::new();\n        for handle in handles {\n            if self\n                .try_add_window(&Window::from(handle), &context)\n                .is_err()\n            {\n                residual.push(handle);\n            }\n        }\n        residual\n    }\n\n    /// Will make a reindexing ignoring the removed window.\n    ///\n    /// **Note:** Reindexing can fail on add some windows so it will return failed handles as residual\n    fn remove_window(&mut self, window: &Window) -> Vec<isize> {\n        let handles = self.drain();\n        let context = create_context(\n            if handles.contains(&window.address()) {\n                handles.len() - 1\n            } else {\n                handles.len()\n            },\n            true,\n        );\n\n        let mut residual = Vec::new();\n        for handle in handles {\n            if handle != window.address()\n                && self\n                    .try_add_window(&Window::from(handle), &context)\n                    .is_err()\n            {\n                residual.push(handle);\n            }\n        }\n        residual\n    }\n\n    fn face(&self) -> Option<Window> {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if let Some(handle) = self.active {\n                    return Some(Window::from(handle));\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in self\n                    .children\n                    .iter()\n                    .sorted_by(|a, b| a.priority.cmp(&b.priority))\n                {\n                    if !child.is_empty() {\n                        return child.face();\n                    }\n                }\n            }\n        }\n        None\n    }\n\n    fn get_node_at_point(&self, point: &Point) -> Result<Option<&Self>> {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if let Some(handle) = self.active {\n                    let window = Window::from(handle);\n                    // Use expected rect from WM_LAYOUT_RECTS if available, otherwise use current inner rect\n                    let window_rect = WM_LAYOUT_RECTS\n                        .get(&handle, |v| v.clone())\n                        .unwrap_or_else(|| window.inner_rect().unwrap_or_default());\n\n                    if window_rect.contains(point) {\n                        return Ok(Some(self));\n                    }\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in &self.children {\n                    if let Some(node) = child.get_node_at_point(point)? {\n                        return Ok(Some(node));\n                    }\n                }\n            }\n        }\n        Ok(None)\n    }\n\n    /// returns None if the node is empty\n    /// uses the closest corners algorithm to find the nearest node\n    fn get_nearest_node_to_rect(&self, rect: &Rect) -> Result<Option<(&Self, i32)>> {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if let Some(handle) = self.active {\n                    let window = Window::from(handle);\n                    // Use expected rect from WM_LAYOUT_RECTS if available, otherwise use current inner rect\n                    let window_rect = WM_LAYOUT_RECTS\n                        .get(&handle, |v| v.clone())\n                        .unwrap_or_else(|| window.inner_rect().unwrap_or_default());\n\n                    let window_corners = window_rect.corners();\n                    let search_corners = rect.corners();\n\n                    // Find minimum distance between corresponding corners\n                    // corners() returns: [top-left, top-right, bottom-right, bottom-left]\n                    let mut min_distance = i32::MAX;\n                    for i in 0..4 {\n                        let distance = search_corners[i].distance_squared(&window_corners[i]);\n                        if distance < min_distance {\n                            min_distance = distance;\n                        }\n                    }\n\n                    Ok(Some((self, min_distance)))\n                } else {\n                    Ok(None)\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                let mut nearest: Option<(&Self, i32)> = None;\n                for child in &self.children {\n                    if let Some((node, distance)) = child.get_nearest_node_to_rect(rect)? {\n                        if nearest.is_none() || distance < nearest.unwrap().1 {\n                            nearest = Some((node, distance));\n                        }\n                    }\n                }\n                Ok(nearest)\n            }\n        }\n    }\n\n    fn process_stacks(&self) -> Result<()> {\n        match self.kind {\n            WmNodeKind::Leaf | WmNodeKind::Stack => {\n                if let Some(active) = self.active {\n                    for addr in &self.windows {\n                        let window = Window::from(*addr);\n                        if *addr == active {\n                            if window.is_minimized() {\n                                window.show_window(SW_RESTORE)?;\n                            }\n                        } else if !window.is_minimized() {\n                            window.show_window(SW_FORCEMINIMIZE)?;\n                        }\n                    }\n                }\n            }\n            WmNodeKind::Horizontal | WmNodeKind::Vertical => {\n                for child in self.children.iter() {\n                    child.process_stacks()?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/app_bar.rs",
    "content": "use lazy_static::lazy_static;\nuse parking_lot::Mutex;\nuse seelen_core::state::{FancyToolbarSide, SeelenWegSide};\nuse windows::Win32::{\n    Foundation::{HWND, LPARAM, RECT},\n    UI::Shell::{\n        SHAppBarMessage, ABE_BOTTOM, ABE_LEFT, ABE_RIGHT, ABE_TOP, ABM_NEW, ABM_REMOVE, ABM_SETPOS,\n        ABM_SETSTATE, ABS_ALWAYSONTOP, ABS_AUTOHIDE, APPBARDATA,\n    },\n};\n\nuse crate::trace_lock;\n\nlazy_static! {\n    pub static ref RegisteredBars: Mutex<Vec<isize>> = Mutex::new(Vec::new());\n}\n\n#[allow(dead_code)]\npub enum AppBarDataEdge {\n    Left = ABE_LEFT as isize,\n    Top = ABE_TOP as isize,\n    Right = ABE_RIGHT as isize,\n    Bottom = ABE_BOTTOM as isize,\n}\n\nimpl From<SeelenWegSide> for AppBarDataEdge {\n    fn from(val: SeelenWegSide) -> Self {\n        match val {\n            SeelenWegSide::Left => AppBarDataEdge::Left,\n            SeelenWegSide::Top => AppBarDataEdge::Top,\n            SeelenWegSide::Right => AppBarDataEdge::Right,\n            SeelenWegSide::Bottom => AppBarDataEdge::Bottom,\n        }\n    }\n}\n\nimpl From<FancyToolbarSide> for AppBarDataEdge {\n    fn from(val: FancyToolbarSide) -> Self {\n        match val {\n            FancyToolbarSide::Top => AppBarDataEdge::Top,\n            FancyToolbarSide::Bottom => AppBarDataEdge::Bottom,\n        }\n    }\n}\n\n/// https://learn.microsoft.com/en-us/windows/win32/shell/abm-setstate#parameters\n#[derive(Debug, Clone, Copy)]\npub enum AppBarDataState {\n    BothOff = 0,\n    AutoHide = ABS_AUTOHIDE as isize,\n    AlwaysOnTop = ABS_ALWAYSONTOP as isize,\n    BothOn = 3,\n}\n\nimpl From<AppBarDataState> for LPARAM {\n    fn from(val: AppBarDataState) -> Self {\n        LPARAM(val as isize)\n    }\n}\n\nimpl From<u32> for AppBarDataState {\n    fn from(state: u32) -> Self {\n        match state {\n            0 => AppBarDataState::BothOff,\n            ABS_AUTOHIDE => AppBarDataState::AutoHide,\n            ABS_ALWAYSONTOP => AppBarDataState::AlwaysOnTop,\n            3 => AppBarDataState::BothOn,\n            _ => unreachable!(),\n        }\n    }\n}\n\npub struct AppBarData(pub APPBARDATA);\n\n#[allow(dead_code)]\nimpl AppBarData {\n    pub fn from_handle(hwnd: HWND) -> Self {\n        Self(APPBARDATA {\n            cbSize: std::mem::size_of::<APPBARDATA>() as u32,\n            hWnd: hwnd,\n            ..Default::default()\n        })\n    }\n\n    pub fn set_state(&self, state: AppBarDataState) {\n        let mut data = self.0;\n        data.lParam = state.into();\n        unsafe { SHAppBarMessage(ABM_SETSTATE, &mut data) };\n    }\n\n    pub fn set_edge(&mut self, edge: AppBarDataEdge) {\n        self.0.uEdge = edge as u32;\n    }\n\n    pub fn set_rect(&mut self, rect: RECT) {\n        self.0.rc = rect;\n    }\n\n    pub fn register_as_new_bar(&mut self) {\n        let mut data = self.0;\n        let mut registered = trace_lock!(RegisteredBars);\n        let addr = data.hWnd.0 as isize;\n        if !registered.contains(&addr) {\n            registered.push(addr);\n            unsafe { SHAppBarMessage(ABM_NEW, &mut data) };\n        }\n        unsafe { SHAppBarMessage(ABM_SETPOS, &mut data) };\n    }\n\n    pub fn unregister_bar(&mut self) {\n        let mut data = self.0;\n        unsafe { SHAppBarMessage(ABM_REMOVE, &mut data) };\n        trace_lock!(RegisteredBars).retain(|x| *x != data.hWnd.0 as isize);\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/com.rs",
    "content": "use crate::error::Result;\nuse windows::{\n    core::{Interface, GUID},\n    Win32::System::Com::{\n        CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_APARTMENTTHREADED,\n    },\n};\n\npub struct Com {}\nimpl Com {\n    fn initialize() -> Result<()> {\n        let result = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) };\n        if result.is_err() {\n            return Err(\"CoInitializeEx failed\".into());\n        }\n        Ok(())\n    }\n\n    pub fn create_instance<T>(class_id: &GUID) -> Result<T>\n    where\n        T: Interface,\n    {\n        unsafe { Ok(CoCreateInstance(class_id, None, CLSCTX_ALL)?) }\n    }\n\n    /// Can panic if Com instances created between init and drop are still alive (no dropped yet)\n    fn uninitialize() {\n        unsafe { CoUninitialize() };\n    }\n\n    /// Will execute init and drop in a safe way, ensuring that all instances created between init and drop are dropped\n    pub fn run_with_context<F, T>(f: F) -> Result<T>\n    where\n        F: FnOnce() -> Result<T>,\n    {\n        Self::initialize()?;\n        let result = f();\n        Self::uninitialize();\n        result\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/devices.rs",
    "content": "use crossbeam_channel::{bounded, Receiver, Sender};\nuse parking_lot::Mutex;\nuse std::sync::{\n    atomic::{AtomicBool, Ordering},\n    Arc,\n};\nuse windows::{\n    Devices::Enumeration::{DeviceInformation, DeviceInformationUpdate, DeviceWatcher},\n    Foundation::TypedEventHandler,\n};\n\nuse crate::error::Result;\n\npub type DeviceId = String;\n\n/// Events emitted by the device watcher\n#[derive(Debug, Clone)]\npub enum DeviceEvent {\n    Added(DeviceId),\n    Updated(DeviceId),\n    Removed(DeviceId),\n}\n\ntype DeviceChangeCallback = Arc<dyn Fn(DeviceEvent) + Send + Sync + 'static>;\n\n/// Thread-safe device watcher that enumerates and monitors devices using AQS (Advanced Query Syntax)\n///\n/// # Architecture\n/// - During initial enumeration (before `start()` completes), `Added` events are captured but NOT propagated\n/// - After enumeration completes, all events (Added/Updated/Removed) are propagated to the callback\n/// - This prevents duplicate notifications for devices that exist at startup\n#[allow(dead_code)]\npub struct DeviceEnumerator {\n    watcher: DeviceWatcher,\n    devices: Arc<Mutex<Vec<DeviceInformation>>>,\n    enumeration_tx: Sender<()>,\n    enumeration_rx: Receiver<()>,\n    enumeration_completed: Arc<AtomicBool>,\n    callback: DeviceChangeCallback,\n}\n\nimpl DeviceEnumerator {\n    /// Creates a new device enumerator with the specified AQS query string and callback\n    ///\n    /// # Parameters\n    /// - `query`: AQS filter string to specify which devices to monitor\n    /// - `callback`: Callback invoked for device events AFTER initial enumeration completes\n    ///\n    /// # Examples of AQS queries:\n    /// - Bluetooth: `System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\"`\n    /// - Network: `System.Devices.InterfaceClassGuid:=\"{cac88484-7515-4c03-82e6-71a87abac361}\"`\n    /// - Audio: `System.Devices.InterfaceClassGuid:=\"{2eef81be-33fa-4800-9670-1cd474972c3f}\"`\n    ///\n    /// # Example\n    /// ```no_run\n    /// let enumerator = DeviceEnumerator::new(\n    ///     \"System.Devices.Aep.ProtocolId:=\\\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\\\"\",\n    ///     |event| {\n    ///         match event {\n    ///             DeviceEvent::Added(id) => println!(\"New device: {}\", id),\n    ///             DeviceEvent::Updated(id) => println!(\"Updated: {}\", id),\n    ///             DeviceEvent::Removed(id) => println!(\"Removed: {}\", id),\n    ///         }\n    ///     }\n    /// )?;\n    /// ```\n    pub fn new<F>(query: impl Into<String>, callback: F) -> Result<Self>\n    where\n        F: Fn(DeviceEvent) + Send + Sync + 'static,\n    {\n        let query: String = query.into();\n        let devices: Arc<Mutex<Vec<DeviceInformation>>> = Arc::new(Mutex::new(Vec::new()));\n        let (enumeration_tx, enumeration_rx) = bounded(1);\n        let enumeration_completed = Arc::new(AtomicBool::new(false));\n        let callback: DeviceChangeCallback = Arc::new(callback) as DeviceChangeCallback;\n\n        // Create the device watcher with the AQS filter\n        let watcher = DeviceInformation::CreateWatcherAqsFilter(&query.into())?;\n\n        // Setup Added event handler\n        {\n            let devices = Arc::clone(&devices);\n            let callback = callback.clone();\n            let enumeration_completed = Arc::clone(&enumeration_completed);\n            let handler = TypedEventHandler::new(\n                move |_: windows_core::Ref<DeviceWatcher>,\n                      info: windows_core::Ref<DeviceInformation>| {\n                    if let Some(info) = info.as_ref() {\n                        // Always add device to our internal list\n                        devices.lock().push(info.clone());\n\n                        // Only notify callback if enumeration has completed\n                        // (to avoid notifying for devices that existed at startup)\n                        if enumeration_completed.load(Ordering::Acquire) {\n                            if let Ok(id) = info.Id() {\n                                callback(DeviceEvent::Added(id.to_string()));\n                            }\n                        }\n                    }\n                    Ok(())\n                },\n            );\n            watcher.Added(&handler)?;\n        }\n\n        // Setup Updated event handler\n        {\n            let callback = callback.clone();\n            let handler = TypedEventHandler::new(\n                move |_: windows_core::Ref<DeviceWatcher>,\n                      update: windows_core::Ref<DeviceInformationUpdate>| {\n                    if let Some(update) = update.as_ref() {\n                        if let Ok(id) = update.Id() {\n                            callback(DeviceEvent::Updated(id.to_string()));\n                        }\n                    }\n                    Ok(())\n                },\n            );\n            watcher.Updated(&handler)?;\n        }\n\n        // Setup Removed event handler\n        {\n            let devices = Arc::clone(&devices);\n            let callback = callback.clone();\n            let handler = TypedEventHandler::new(\n                move |_: windows_core::Ref<DeviceWatcher>,\n                      update: windows_core::Ref<DeviceInformationUpdate>| {\n                    if let Some(update) = update.as_ref() {\n                        if let Ok(id) = update.Id() {\n                            let id_str = id.to_string();\n\n                            // Remove device from our internal list\n                            devices.lock().retain(|dev| {\n                                dev.Id().map(|dev_id| dev_id != id_str).unwrap_or(true)\n                            });\n\n                            // Notify callback\n                            callback(DeviceEvent::Removed(id_str));\n                        }\n                    }\n                    Ok(())\n                },\n            );\n            watcher.Removed(&handler)?;\n        }\n\n        // Setup EnumerationCompleted event handler\n        {\n            let tx = enumeration_tx.clone();\n            let enumeration_completed = Arc::clone(&enumeration_completed);\n            let handler = TypedEventHandler::new(\n                move |_: windows_core::Ref<DeviceWatcher>,\n                      _: windows_core::Ref<windows_core::IInspectable>| {\n                    // Mark enumeration as completed FIRST\n                    // This allows subsequent Added events to be propagated to the callback\n                    enumeration_completed.store(true, Ordering::Release);\n\n                    // Then signal that initial enumeration is complete\n                    let _ = tx.send(());\n                    Ok(())\n                },\n            );\n            watcher.EnumerationCompleted(&handler)?;\n        }\n\n        Ok(Self {\n            watcher,\n            devices,\n            enumeration_tx,\n            enumeration_rx,\n            enumeration_completed,\n            callback,\n        })\n    }\n\n    /// Starts the device watcher and waits for the initial enumeration to complete\n    /// Returns a list of all devices found during the initial enumeration\n    ///\n    /// This method blocks until the initial enumeration is complete, ensuring that\n    /// all existing devices are discovered before returning.\n    pub fn start_blocking(&self) -> Result<Vec<DeviceInformation>> {\n        // Start the watcher\n        self.watcher.Start()?;\n\n        // Wait for the initial enumeration to complete\n        self.enumeration_rx\n            .recv()\n            .map_err(|_| windows::core::Error::from_hresult(windows::Win32::Foundation::E_FAIL))?;\n\n        // Return a clone of all discovered devices\n        Ok(self.devices.lock().clone())\n    }\n\n    pub fn start(&self) -> Result<()> {\n        self.enumeration_completed.store(true, Ordering::Release);\n        self.watcher.Start()?;\n        Ok(())\n    }\n}\n\nimpl Drop for DeviceEnumerator {\n    fn drop(&mut self) {\n        let _ = self.watcher.Stop();\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/event_window.rs",
    "content": "use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicU32, Ordering};\nuse windows::Win32::{\n    Foundation::{HWND, LPARAM, LRESULT, WPARAM},\n    System::{\n        Power::RegisterSuspendResumeNotification,\n        RemoteDesktop::{WTSRegisterSessionNotification, NOTIFY_FOR_THIS_SESSION},\n    },\n    UI::{\n        Shell::{\n            Common::ITEMIDLIST, SHChangeNotifyEntry, SHChangeNotifyRegister,\n            SHGetSpecialFolderLocation, SHCNRF_SOURCE,\n        },\n        WindowsAndMessaging::{\n            CreateWindowExW, DefWindowProcW, DispatchMessageW, GetMessageW, PostQuitMessage,\n            RegisterClassW, RegisterShellHookWindow, RegisterWindowMessageW, TranslateMessage,\n            DEVICE_NOTIFY_WINDOW_HANDLE, MSG, WINDOW_EX_STYLE, WINDOW_STYLE, WM_APP, WM_DESTROY,\n            WM_WTSSESSION_CHANGE, WNDCLASSW, WTS_SESSION_LOCK, WTS_SESSION_LOGOFF,\n            WTS_SESSION_LOGON, WTS_SESSION_UNLOCK,\n        },\n    },\n};\n\nuse crate::{\n    app::emit_to_webviews,\n    error::{Result, WindowsResultExt},\n    event_manager, log_error,\n    utils::spawn_named_thread,\n};\n\nuse super::{string_utils::WindowsString, WindowsApi};\n\npub static WM_SHELLHOOKMESSAGE: AtomicU32 = AtomicU32::new(u32::MAX);\npub const HSHELL_FULLSCREEN_ENTER: u32 = 53;\npub const HSHELL_FULLSCREEN_EXIT: u32 = 54;\n\npub static BACKGROUND_HWND: AtomicIsize = AtomicIsize::new(0);\n\n/// Window message sent to the background window when the recycle bin state changes.\npub const WM_TRASH_BIN_NOTIFY: u32 = WM_APP + 100;\n\n/// CSIDL for the Recycle Bin virtual folder.\nconst CSIDL_BITBUCKET: i32 = 0x000a;\n/// SHCNRF_ShellLevel\nconst SHCNRF_SHELL_LEVEL: SHCNRF_SOURCE = SHCNRF_SOURCE(0x0002);\n/// SHCNE_ALLEVENTS\nconst SHCNE_ALL_EVENTS: i32 = 0x7FFFFFFF_u32 as i32;\n\n/// Global flag to track if the current session is interactive (not locked/switched).\n/// Used to pause background threads and event processing when the session is not interactive.\npub static IS_INTERACTIVE_SESSION: AtomicBool = AtomicBool::new(true);\n\npub struct BgWindowProc {}\n\nevent_manager!(BgWindowProc, (u32, usize, isize));\n\nimpl BgWindowProc {\n    /// will lock until the window is closed\n    unsafe fn _create_background_window(done: &crossbeam_channel::Sender<()>) -> Result<()> {\n        let title = WindowsString::from(\"Seelen UI Background Window\");\n        let class = WindowsString::from(\"SeelenBackgroundWindow\");\n\n        let h_module = WindowsApi::module_handle_w()?;\n\n        let wnd_class = WNDCLASSW {\n            lpfnWndProc: Some(Self::window_proc),\n            hInstance: h_module.into(),\n            lpszClassName: class.as_pcwstr(),\n            ..Default::default()\n        };\n\n        RegisterClassW(&wnd_class);\n\n        let hwnd = CreateWindowExW(\n            WINDOW_EX_STYLE::default(),\n            class.as_pcwstr(),\n            title.as_pcwstr(),\n            WINDOW_STYLE::default(),\n            0,\n            0,\n            0,\n            0,\n            None,\n            None,\n            Some(wnd_class.hInstance),\n            None,\n        )?;\n\n        let handle: isize = hwnd.0 as isize;\n        BACKGROUND_HWND.store(handle, Ordering::Relaxed);\n\n        // register window to recieve shell events\n        {\n            RegisterShellHookWindow(hwnd).ok().filter_fake_error()?;\n            let msg = WindowsString::from(\"SHELLHOOK\");\n            WM_SHELLHOOKMESSAGE.store(RegisterWindowMessageW(msg.as_pcwstr()), Ordering::Relaxed);\n        }\n\n        // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registersuspendresumenotification\n        let _resume_suspend_handle =\n            RegisterSuspendResumeNotification(hwnd.into(), DEVICE_NOTIFY_WINDOW_HANDLE)?;\n\n        // Register for session change notifications (lock/unlock, user switch, etc.)\n        // This is critical for pausing background threads when session is not interactive\n        WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION)?;\n\n        // Register for shell change notifications so that WM_TRASH_BIN_NOTIFY is sent\n        // to the background window whenever the recycle bin contents change.\n        register_shell_notifications(hwnd);\n\n        done.send(())?;\n        let mut msg = MSG::default();\n\n        // GetMessageW will run until PostQuitMessage(0) is called\n        while GetMessageW(&mut msg, Some(hwnd), 0, 0).into() {\n            TranslateMessage(&msg).ok().filter_fake_error()?;\n            DispatchMessageW(&msg);\n        }\n        Ok(())\n    }\n\n    unsafe extern \"system\" fn window_proc(\n        hwnd: HWND,\n        msg: u32,\n        w_param: WPARAM,\n        l_param: LPARAM,\n    ) -> LRESULT {\n        if msg == WM_DESTROY {\n            PostQuitMessage(0);\n            return LRESULT(0);\n        }\n\n        // Handle session change notifications to pause background processing\n        // when the session is locked or user switches\n        if msg == WM_WTSSESSION_CHANGE {\n            match w_param.0 as u32 {\n                WTS_SESSION_LOCK | WTS_SESSION_LOGOFF => {\n                    log::info!(\"Session locked/logged off - pausing background event processing\");\n                    IS_INTERACTIVE_SESSION.store(false, Ordering::Release);\n                }\n                WTS_SESSION_UNLOCK | WTS_SESSION_LOGON => {\n                    log::info!(\"Session unlocked/logged on - resuming background event processing\");\n                    IS_INTERACTIVE_SESSION.store(true, Ordering::Release);\n                    emit_to_webviews(\"internal::session_resumed\", ());\n                }\n                _ => {}\n            }\n        }\n\n        Self::send((msg, w_param.0, l_param.0));\n        DefWindowProcW(hwnd, msg, w_param, l_param)\n    }\n}\n\n/// Registers shell change notifications for the Recycle Bin on the background window.\n/// When the recycle bin contents change, Windows sends `WM_TRASH_BIN_NOTIFY` to `hwnd`,\n/// which is then broadcast to all `subscribe_to_background_window` subscribers.\nunsafe fn register_shell_notifications(hwnd: HWND) {\n    let pidl: *mut ITEMIDLIST =\n        SHGetSpecialFolderLocation(None, CSIDL_BITBUCKET).unwrap_or(std::ptr::null_mut());\n\n    let entry = SHChangeNotifyEntry {\n        pidl,\n        fRecursive: true.into(),\n    };\n\n    SHChangeNotifyRegister(\n        hwnd,\n        SHCNRF_SHELL_LEVEL,\n        SHCNE_ALL_EVENTS,\n        WM_TRASH_BIN_NOTIFY,\n        1,\n        &entry,\n    );\n}\n\n/// the objective with this window is having a thread that will receive window events\n/// and propagate them across the application (common events are keyboard, power, display, etc)\npub fn create_background_window() -> Result<()> {\n    let (tx, rx) = crossbeam_channel::bounded(1);\n    spawn_named_thread(\"Background Window\", move || {\n        log::trace!(\"Creating background window...\");\n        log_error!(unsafe { BgWindowProc::_create_background_window(&tx) });\n    });\n    rx.recv()?;\n    log::trace!(\"Background window created\");\n    Ok(())\n}\n\npub fn subscribe_to_background_window<F>(callback: F)\nwhere\n    F: Fn(u32, usize, isize) -> Result<()> + Send + Sync + 'static,\n{\n    BgWindowProc::subscribe(move |arg| {\n        log_error!(callback(arg.0, arg.1, arg.2));\n    });\n}\n"
  },
  {
    "path": "src/background/windows_api/hdc.rs",
    "content": "use windows::Win32::{\n    Foundation::HWND,\n    Graphics::Gdi::{GetDC, GetPixel, ReleaseDC, CLR_INVALID, HDC},\n};\n\nuse seelen_core::system_state::Color;\n\n// Copy or Clone can't be implemented cuz this implements auto Drop/Release of the HDC\n#[derive(Debug, PartialEq, Eq)]\npub struct DeviceContext {\n    owner_hwnd: Option<HWND>,\n    handle: HDC,\n}\n\nimpl Drop for DeviceContext {\n    fn drop(&mut self) {\n        unsafe { ReleaseDC(self.owner_hwnd, self.handle) };\n    }\n}\n\nimpl DeviceContext {\n    pub fn create(window: Option<HWND>) -> DeviceContext {\n        DeviceContext {\n            owner_hwnd: window,\n            handle: unsafe { GetDC(window) },\n        }\n    }\n\n    pub fn get_pixel(&self, x: i32, y: i32) -> Color {\n        // the color is on the form 0xAABBGGRR, little endian of RGBA\n        let color = unsafe { GetPixel(self.handle, x, y) }.0;\n        if color == CLR_INVALID {\n            return Color::default();\n        }\n        let [r, g, b, _] = color.to_le_bytes();\n        Color::new(r, g, b, 0xFF)\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/input.rs",
    "content": "use std::cmp::max;\nuse std::cmp::min;\nuse std::mem;\nuse std::str::Chars;\nuse std::thread::sleep;\nuse std::time::Duration;\n\nuse phf::phf_map;\nuse phf::phf_set;\nuse seelen_core::Point;\nuse windows::Win32::UI::Input::KeyboardAndMouse::*;\nuse windows::Win32::UI::WindowsAndMessaging::GetCursorPos;\nuse windows::Win32::UI::WindowsAndMessaging::GetSystemMetrics;\nuse windows::Win32::UI::WindowsAndMessaging::SetCursorPos;\nuse windows::Win32::UI::WindowsAndMessaging::SM_CXSCREEN;\nuse windows::Win32::UI::WindowsAndMessaging::SM_CYSCREEN;\n\nuse crate::error::Result;\n\nconst VIRTUAL_KEYS: phf::Map<&'static str, VIRTUAL_KEY> = phf_map! {\n    \"CONTROL\" => VK_CONTROL, \"CTRL\" => VK_CONTROL, \"LCONTROL\" => VK_LCONTROL, \"LCTRL\" => VK_LCONTROL, \"RCONTROL\" => VK_RCONTROL, \"RCTRL\" => VK_RCONTROL,\n    \"ALT\" => VK_MENU, \"MENU\" => VK_MENU, \"LALT\" => VK_LMENU, \"LMENU\" => VK_LMENU, \"RALT\" => VK_RMENU, \"RMENU\" => VK_RMENU,\n    \"SHIFT\" => VK_SHIFT, \"LSHIFT\" => VK_LSHIFT, \"RSHIFT\" => VK_RSHIFT, \"APPS\" => VK_APPS,\n    \"WIN\" => VK_LWIN, \"WINDOWS\" => VK_LWIN, \"LWIN\" => VK_LWIN, \"LWINDOWS\" => VK_LWIN, \"RWIN\" => VK_RWIN, \"RWINDOWS\" => VK_RWIN,\n    \"LBUTTON\" => VK_LBUTTON, \"RBUTTON\" => VK_RBUTTON, \"MBUTTON\" => VK_MBUTTON, \"XBUTTON1\" => VK_XBUTTON1, \"XBUTTON2\" => VK_XBUTTON2,\n    \"CANCEL\" => VK_CANCEL, \"BACK\" => VK_BACK, \"TAB\" => VK_TAB, \"RETURN\" => VK_RETURN, \"ENTER\" => VK_RETURN, \"PAUSE\" => VK_PAUSE, \"CAPITAL\" => VK_CAPITAL,\n    \"ESCAPE\" => VK_ESCAPE, \"ESC\" => VK_ESCAPE, \"SPACE\" => VK_SPACE,\n    \"PRIOR\" => VK_PRIOR, \"PAGE_UP\" => VK_PRIOR, \"NEXT\" => VK_NEXT, \"PAGE_DOWN\" => VK_NEXT, \"HOME\" => VK_HOME, \"END\" => VK_END,\n    \"LEFT\" => VK_LEFT, \"UP\" => VK_UP, \"RIGHT\" => VK_RIGHT, \"DOWN\" => VK_DOWN, \"PRINT\" => VK_PRINT,\n    \"INSERT\" => VK_INSERT, \"DELETE\" => VK_DELETE,\n    \"F1\" => VK_F1, \"F2\" => VK_F2, \"F3\" => VK_F3, \"F4\" => VK_F4, \"F5\" => VK_F5, \"F6\" => VK_F6, \"F7\" => VK_F7, \"F8\" => VK_F8, \"F9\" => VK_F9, \"F10\" => VK_F10,\n    \"F11\" => VK_F11, \"F12\" => VK_F12, \"F13\" => VK_F13, \"F14\" => VK_F14, \"F15\" => VK_F15, \"F16\" => VK_F16, \"F17\" => VK_F17, \"F18\" => VK_F18, \"F19\" => VK_F19,\n    \"F20\" => VK_F20, \"F21\" => VK_F21, \"F22\" => VK_F22, \"F23\" => VK_F23, \"F24\" => VK_F24,\n};\n\nconst HOLD_KEYS: phf::Set<&'static str> = phf_set! {\n    \"CONTROL\", \"CTRL\", \"LCONTROL\", \"LCTRL\", \"RCONTROL\", \"RCTRL\",\n    \"ALT\", \"MENU\", \"LALT\", \"LMENU\", \"RALT\", \"RMENU\",\n    \"SHIFT\", \"LSHIFT\", \"RSHIFT\", \"APPS\",\n    \"WIN\", \"WINDOWS\", \"LWIN\", \"LWINDOWS\", \"RWIN\", \"RWINDOWS\"\n};\n\nconst KEYEVENTF_KEYDOWN: KEYBD_EVENT_FLAGS = KEYBD_EVENT_FLAGS(0);\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum InputItem {\n    HoldKey(VIRTUAL_KEY),\n    VirtualKey(VIRTUAL_KEY),\n    Character(char),\n}\n\nimpl InputItem {\n    fn is_holdkey(&self) -> bool {\n        matches!(self, Self::HoldKey(_))\n    }\n}\n\n#[derive(Debug, PartialEq, Eq)]\nstruct Input {\n    holdkeys: Vec<VIRTUAL_KEY>,\n    items: Vec<InputItem>,\n}\n\nimpl Input {\n    fn new() -> Self {\n        Self {\n            holdkeys: Vec::new(),\n            items: Vec::new(),\n        }\n    }\n\n    fn has_holdkey(&self) -> bool {\n        !self.holdkeys.is_empty()\n    }\n\n    fn has_items(&self) -> bool {\n        !self.items.is_empty()\n    }\n\n    fn is_holdkey_only(&self) -> bool {\n        !self.holdkeys.is_empty() && self.items.is_empty()\n    }\n\n    fn push(&mut self, item: InputItem) {\n        if let InputItem::HoldKey(key) = item {\n            if !self.holdkeys.contains(&key) {\n                self.holdkeys.push(key);\n            }\n        } else {\n            self.items.push(item);\n        }\n    }\n\n    fn push_all(&mut self, items: &Vec<InputItem>) {\n        for item in items {\n            self.push(*item);\n        }\n    }\n\n    fn create_inputs(&self) -> Result<Vec<INPUT>> {\n        let mut inputs: Vec<INPUT> = Vec::new();\n\n        for holdkey in &self.holdkeys {\n            let input = Self::create_virtual_key(*holdkey, KEYEVENTF_KEYDOWN);\n            inputs.push(input);\n        }\n\n        for item in &self.items {\n            match item {\n                InputItem::VirtualKey(key) => {\n                    inputs.push(Self::create_virtual_key(*key, KEYEVENTF_KEYDOWN));\n                    inputs.push(Self::create_virtual_key(*key, KEYEVENTF_KEYUP));\n                }\n                InputItem::Character(ch) => {\n                    let mut buffer = [0; 2];\n                    let chars = ch.encode_utf16(&mut buffer);\n                    for ch_u16 in chars {\n                        let keys = Self::create_char_key(*ch_u16, self.has_holdkey());\n                        inputs.extend(keys);\n                    }\n                }\n                _ => (),\n            }\n        }\n\n        for holdkey in self.holdkeys.iter().rev() {\n            let input = Self::create_virtual_key(*holdkey, KEYEVENTF_KEYUP);\n            inputs.push(input);\n        }\n\n        Ok(inputs)\n    }\n\n    fn create_virtual_key(key: VIRTUAL_KEY, flags: KEYBD_EVENT_FLAGS) -> INPUT {\n        INPUT {\n            r#type: INPUT_KEYBOARD,\n            Anonymous: INPUT_0 {\n                ki: KEYBDINPUT {\n                    wVk: key,\n                    wScan: unsafe { MapVirtualKeyW(u32::from(key.0), MAPVK_VK_TO_VSC) } as u16,\n                    dwFlags: flags,\n                    time: 0,\n                    dwExtraInfo: 0,\n                },\n            },\n        }\n    }\n\n    fn create_char_key(ch: u16, hold_mode: bool) -> Vec<INPUT> {\n        // let code = ch as i32;\n        let vk: i16 = if ch < 256 {\n            unsafe { VkKeyScanW(ch) }\n        } else {\n            -1\n        };\n\n        if vk == -1 {\n            // Unicode\n            vec![\n                INPUT {\n                    r#type: INPUT_KEYBOARD,\n                    Anonymous: INPUT_0 {\n                        ki: KEYBDINPUT {\n                            wVk: VIRTUAL_KEY(0),\n                            wScan: ch,\n                            dwFlags: KEYEVENTF_UNICODE,\n                            time: 0,\n                            dwExtraInfo: 0,\n                        },\n                    },\n                },\n                INPUT {\n                    r#type: INPUT_KEYBOARD,\n                    Anonymous: INPUT_0 {\n                        ki: KEYBDINPUT {\n                            wVk: VIRTUAL_KEY(0),\n                            wScan: ch,\n                            dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,\n                            time: 0,\n                            dwExtraInfo: 0,\n                        },\n                    },\n                },\n            ]\n        } else {\n            // ASCII\n            let key: VIRTUAL_KEY = VIRTUAL_KEY((vk & 0xFF) as _);\n            if hold_mode {\n                vec![\n                    INPUT {\n                        r#type: INPUT_KEYBOARD,\n                        Anonymous: INPUT_0 {\n                            ki: KEYBDINPUT {\n                                wVk: key,\n                                wScan: 0,\n                                dwFlags: KEYEVENTF_KEYDOWN,\n                                time: 0,\n                                dwExtraInfo: 0,\n                            },\n                        },\n                    },\n                    INPUT {\n                        r#type: INPUT_KEYBOARD,\n                        Anonymous: INPUT_0 {\n                            ki: KEYBDINPUT {\n                                wVk: key,\n                                wScan: 0,\n                                dwFlags: KEYEVENTF_KEYUP,\n                                time: 0,\n                                dwExtraInfo: 0,\n                            },\n                        },\n                    },\n                ]\n            } else {\n                let mut shift: bool = (vk >> 8 & 0x01) != 0;\n                let state = unsafe { GetKeyState(VK_CAPITAL.0 as _) };\n                if (state & 0x01) != 0\n                    && ((ch >= 'a' as u16 && ch <= 'z' as u16)\n                        || (ch >= 'A' as u16 && ch <= 'Z' as u16))\n                {\n                    shift = !shift;\n                };\n                let mut char_inputs: Vec<INPUT> = Vec::new();\n                if shift {\n                    char_inputs.push(INPUT {\n                        r#type: INPUT_KEYBOARD,\n                        Anonymous: INPUT_0 {\n                            ki: KEYBDINPUT {\n                                wVk: VK_SHIFT,\n                                wScan: 0,\n                                dwFlags: KEYEVENTF_KEYDOWN,\n                                time: 0,\n                                dwExtraInfo: 0,\n                            },\n                        },\n                    });\n                }\n                char_inputs.push(INPUT {\n                    r#type: INPUT_KEYBOARD,\n                    Anonymous: INPUT_0 {\n                        ki: KEYBDINPUT {\n                            wVk: key,\n                            wScan: 0,\n                            dwFlags: KEYEVENTF_KEYDOWN,\n                            time: 0,\n                            dwExtraInfo: 0,\n                        },\n                    },\n                });\n                char_inputs.push(INPUT {\n                    r#type: INPUT_KEYBOARD,\n                    Anonymous: INPUT_0 {\n                        ki: KEYBDINPUT {\n                            wVk: key,\n                            wScan: 0,\n                            dwFlags: KEYEVENTF_KEYUP,\n                            time: 0,\n                            dwExtraInfo: 0,\n                        },\n                    },\n                });\n                if shift {\n                    char_inputs.push(INPUT {\n                        r#type: INPUT_KEYBOARD,\n                        Anonymous: INPUT_0 {\n                            ki: KEYBDINPUT {\n                                wVk: VK_SHIFT,\n                                wScan: 0,\n                                dwFlags: KEYEVENTF_KEYUP,\n                                time: 0,\n                                dwExtraInfo: 0,\n                            },\n                        },\n                    });\n                };\n                char_inputs\n            }\n        }\n    }\n}\n\nfn parse_input(expression: &str) -> Result<Vec<Input>> {\n    let mut inputs: Vec<Input> = Vec::new();\n\n    let mut expr = expression.chars();\n    while let Some((items, is_holdkey)) = next_input(&mut expr)? {\n        if let Some(prev) = inputs.last_mut() {\n            // if !is_holdkey && (prev.is_holdkey_only() || !prev.has_holdkey()) {\n            if (is_holdkey && !prev.has_items())\n                || (!is_holdkey && (!prev.has_holdkey() || prev.is_holdkey_only()))\n            {\n                prev.push_all(&items);\n                continue;\n            }\n        }\n\n        let mut input = Input::new();\n        input.push_all(&items);\n\n        inputs.push(input);\n    }\n\n    Ok(inputs)\n}\n\nfn next_input(expr: &mut Chars<'_>) -> Result<Option<(Vec<InputItem>, bool)>> {\n    if let Some(ch) = expr.next() {\n        let next = match ch {\n            '{' => {\n                let item = read_special_item(expr)?;\n                Some((vec![item], item.is_holdkey()))\n            }\n            '(' => {\n                let items = read_group_items(expr)?;\n                Some((items, false))\n            }\n            _ => Some((vec![InputItem::Character(ch)], false)),\n        };\n        Ok(next)\n    } else {\n        Ok(None)\n    }\n}\n\nfn read_special_item(expr: &mut Chars<'_>) -> Result<InputItem> {\n    let mut token = String::new();\n    let mut matched = false;\n    for ch in expr.by_ref() {\n        if ch == '}' && !token.is_empty() {\n            matched = true;\n            break;\n        } else {\n            token.push(ch);\n        }\n    }\n\n    if matched {\n        if token == \"(\" || token == \")\" || token == \"{\" || token == \"}\" {\n            Ok(InputItem::Character(token.chars().next().unwrap()))\n        } else {\n            let token = token.to_uppercase();\n            if let Some(key) = VIRTUAL_KEYS.get(&token) {\n                if HOLD_KEYS.contains(&token) {\n                    Ok(InputItem::HoldKey(*key))\n                } else {\n                    Ok(InputItem::VirtualKey(*key))\n                }\n            } else {\n                Err(\"Error Input Format\".into())\n            }\n        }\n    } else {\n        Err(\"Error Input Format\".into())\n    }\n}\n\nfn read_group_items(expr: &mut Chars<'_>) -> Result<Vec<InputItem>> {\n    let mut items: Vec<InputItem> = Vec::new();\n    let mut matched = false;\n\n    while let Some((next, _)) = next_input(expr)? {\n        if next.len() == 1 && next[0] == InputItem::Character(')') {\n            matched = true;\n            break;\n        }\n\n        items.extend(next);\n    }\n\n    if matched {\n        Ok(items)\n    } else {\n        Err(\"Error Input Format\".into())\n    }\n}\n\n/// Simulate typing keys on keyboard.\n#[derive(Debug, Default)]\npub struct Keyboard {\n    interval: u64,\n    holdkeys: Vec<VIRTUAL_KEY>,\n}\n\n#[allow(dead_code)]\nimpl Keyboard {\n    /// Create a keyboard to simulate typing keys.\n    pub fn new() -> Self {\n        Self {\n            interval: 0,\n            holdkeys: Vec::new(),\n        }\n    }\n\n    /// Set the interval time between keys.\n    ///\n    /// `interval` is the time number of milliseconds, `0` is default value.\n    pub fn interval(mut self, interval: u64) -> Self {\n        self.interval = interval;\n        self\n    }\n\n    /// Simulates typing `keys` on keyboard.\n    ///\n    /// `{}` is used for some special keys. For example: `{ctrl}{alt}{delete}`, `{shift}{home}`.\n    ///\n    /// `()` is used for group keys. For example: `{ctrl}(AB)` types `Ctrl+A+B`.\n    ///\n    /// `{` `}` `(` `)` can be quoted by `{}`. For example: `{{}Hi,{(}rust!{)}{}}` types `{Hi,(rust)}`.\n    pub fn send_keys(&self, keys: &str) -> Result<()> {\n        let inputs = parse_input(keys)?;\n        for ref input in inputs {\n            // self.send_keyboard(input)?;\n            let input_keys = input.create_inputs()?;\n            self.send_keyboard(&input_keys)?;\n        }\n\n        Ok(())\n    }\n\n    /// Simulates starting to hold `keys` on keyboard. Only holdkeys are allowed.\n    ///\n    /// The `keys` will be released when `end_hold_keys()` is invoked.\n    pub fn begin_hold_keys(&mut self, keys: &str) -> Result<()> {\n        let mut holdkeys: Vec<VIRTUAL_KEY> = Vec::new();\n\n        let inputs = parse_input(keys)?;\n        for input in inputs {\n            if input.has_items() {\n                return Err(\"Error holdkeys\".into());\n            }\n\n            holdkeys.extend(input.holdkeys);\n        }\n\n        if holdkeys.is_empty() {\n            return Err(\"Error holdkeys\".into());\n        }\n\n        let mut holdkey_inputs: Vec<INPUT> = Vec::new();\n        for holdkey in &holdkeys {\n            holdkey_inputs.push(Input::create_virtual_key(*holdkey, KEYEVENTF_KEYDOWN));\n        }\n        // send_input(&holdkey_inputs.as_slice())?;\n        self.send_keyboard(&holdkey_inputs)?;\n\n        self.holdkeys.extend(holdkeys);\n\n        Ok(())\n    }\n\n    /// Stop holding keys on keyboard.\n    pub fn end_hold_keys(&mut self) -> Result<()> {\n        if self.holdkeys.is_empty() {\n            Ok(())\n        } else {\n            let mut holdkey_inputs = Vec::new();\n            for holdkey in self.holdkeys.iter().rev() {\n                holdkey_inputs.push(Input::create_virtual_key(*holdkey, KEYEVENTF_KEYUP));\n            }\n            self.holdkeys.clear();\n\n            // send_input(&holdkey_inputs.as_slice())\n            self.send_keyboard(&holdkey_inputs)\n        }\n    }\n\n    // fn send_keyboard(&self, input: &Input) -> Result<()> {\n    //     let input_keys = input.create_inputs()?;\n    //     if self.interval == 0 {\n    //         send_input(&input_keys.as_slice())\n    //     } else {\n    //         for input_key in &input_keys {\n    //             let input_key_slice: [INPUT; 1] = [input_key.clone()];\n    //             send_input(&input_key_slice)?;\n\n    //             self.wait();\n    //         }\n\n    //         Ok(())\n    //     }\n    // }\n\n    fn send_keyboard(&self, input_keys: &[INPUT]) -> Result<()> {\n        // let input_keys = input.create_inputs()?;\n        if self.interval == 0 {\n            send_input(input_keys)\n        } else {\n            for input_key in input_keys {\n                let input_key_slice: [INPUT; 1] = [*input_key];\n                send_input(&input_key_slice)?;\n\n                self.wait();\n            }\n\n            Ok(())\n        }\n    }\n\n    fn wait(&self) {\n        if self.interval > 0 {\n            sleep(Duration::from_millis(self.interval));\n        }\n    }\n}\n\nimpl Drop for Keyboard {\n    fn drop(&mut self) {\n        if !self.holdkeys.is_empty() {\n            let mut holdkey_inputs: Vec<INPUT> = Vec::new();\n            for holdkey in self.holdkeys.iter().rev() {\n                holdkey_inputs.push(Input::create_virtual_key(*holdkey, KEYEVENTF_KEYUP));\n            }\n\n            if send_input(holdkey_inputs.as_slice()).is_ok() {\n                self.holdkeys.clear();\n            }\n        }\n    }\n}\n\n/// Simulate mouse event.\n#[derive(Debug)]\npub struct Mouse {\n    interval: u64,\n    move_time: u64,\n    auto_move: bool,\n    holdkeys: Vec<VIRTUAL_KEY>,\n}\n\nimpl Default for Mouse {\n    fn default() -> Self {\n        Self {\n            interval: 100,\n            move_time: 500,\n            auto_move: true,\n            holdkeys: Vec::new(),\n        }\n    }\n}\n\n#[allow(dead_code)]\nimpl Mouse {\n    /// Creates a `Mouse` to simulate mouse event.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Sets the interval time between events.\n    ///\n    /// `interval` is the time number of milliseconds, `100` is default value.\n    pub fn interval(mut self, interval: u64) -> Self {\n        self.interval = interval;\n        self\n    }\n\n    /// Sets the mouse move time in millionseconds. `500` is default value.\n    pub fn move_time(mut self, move_time: u64) -> Self {\n        self.move_time = move_time;\n        self\n    }\n\n    /// Sets whether move the cursor to the click point automatically. Default is `true`.\n    pub fn auto_move(mut self, auto_move: bool) -> Self {\n        self.auto_move = auto_move;\n        self\n    }\n\n    /// Sets the holdkeys when mouse clicks.\n    ///\n    /// The holdkeys is quoted by `{}`. For example: `{Shift}`, `{Ctrl}{Alt}`.\n    pub fn holdkeys(mut self, holdkeys: &str) -> Self {\n        self.holdkeys.clear();\n\n        let mut expr = holdkeys.chars();\n        while let Some((items, is_holdkey)) = next_input(&mut expr).unwrap() {\n            if is_holdkey {\n                for item in items {\n                    if let InputItem::HoldKey(key) = item {\n                        self.holdkeys.push(key);\n                    }\n                }\n            }\n        }\n\n        self\n    }\n\n    /// Retrieves the position of the mouse cursor, in screen coordinates.\n    pub fn get_cursor_pos() -> Result<Point> {\n        let mut pos = windows::Win32::Foundation::POINT::default();\n        unsafe { GetCursorPos(&mut pos)? };\n        Ok(Point::new(pos.x, pos.y))\n    }\n\n    /// Moves the cursor to the specified screen coordinates.\n    pub fn set_cursor_pos(pos: Point) -> Result<()> {\n        unsafe { SetCursorPos(pos.x, pos.y)? };\n        Ok(())\n    }\n\n    /// Moves the cursor from current position to the `target` position.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use uiautomation::inputs::Mouse;\n    /// use uiautomation::types::Point;\n    ///\n    /// let mouse = Mouse::new().move_time(800);\n    /// mouse.move_to(Point::new(10, 20)).unwrap();\n    /// mouse.move_to(Point::new(1000,800)).unwrap();\n    /// ```\n    pub fn move_to(&self, target: &Point) -> Result<()> {\n        let (width, height) = get_screen_size()?;\n        let x = min(max(0, target.x), width);\n        let y = min(max(0, target.y), height);\n        let target = Point::new(x, y);\n\n        if self.move_time > 0 {\n            let source = Self::get_cursor_pos()?;\n            let delta_x = target.x - source.x;\n            let delta_y = target.y - source.y;\n\n            let delta = max(delta_x.abs(), delta_y.abs());\n            let steps = delta / 20;\n            if steps > 1 {\n                let step_x = delta_x / steps;\n                let step_y = delta_y / steps;\n                let interval = Duration::from_millis(self.move_time / steps as u64);\n                for i in 1..steps {\n                    let pos = Point::new(source.x + step_x * i, source.y + step_y * i);\n                    Self::set_cursor_pos(pos)?;\n                    sleep(interval);\n                }\n            }\n        }\n\n        Self::set_cursor_pos(target)\n    }\n\n    /// Simulates a mouse click event.\n    ///\n    /// # Examples\n    /// ```\n    /// use uiautomation::inputs::Mouse;\n    ///\n    /// let mouse = Mouse::new();\n    /// let pos = Mouse::get_cursor_pos().unwrap();\n    /// mouse.click(pos).unwrap();\n    /// ```\n    pub fn click(&self, pos: Point) -> Result<()> {\n        if self.auto_move {\n            self.move_to(&pos)?;\n        }\n\n        self.before_click()?;\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_LEFTDOWN)?;\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_LEFTUP)?;\n        self.after_click()?;\n\n        Ok(())\n    }\n\n    /// Simulates a mouse double click event.\n    ///\n    /// # Examples\n    /// ```\n    /// use uiautomation::inputs::Mouse;\n    ///\n    /// let mouse = Mouse::new();\n    /// let pos = Mouse::get_cursor_pos().unwrap();\n    /// mouse.double_click(pos).unwrap();\n    /// ```\n    pub fn double_click(&self, pos: Point) -> Result<()> {\n        if self.auto_move {\n            self.move_to(&pos)?;\n        }\n\n        self.before_click()?;\n\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_LEFTDOWN)?;\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_LEFTUP)?;\n\n        sleep(Duration::from_millis(max(200, self.interval)));\n\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_LEFTDOWN)?;\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_LEFTUP)?;\n\n        self.after_click()?;\n\n        Ok(())\n    }\n\n    /// Simulates a right mouse click event.\n    ///\n    /// # Examples\n    /// ```\n    /// use uiautomation::inputs::Mouse;\n    ///\n    /// let mouse = Mouse::new();\n    /// let pos = Mouse::get_cursor_pos().unwrap();\n    /// mouse.right_click(pos).unwrap();\n    /// ```\n    pub fn right_click(&self, pos: Point) -> Result<()> {\n        if self.auto_move {\n            self.move_to(&pos)?;\n        }\n\n        self.before_click()?;\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_RIGHTDOWN)?;\n        self.mouse_event(pos.x, pos.y, MOUSEEVENTF_RIGHTUP)?;\n        self.after_click()?;\n\n        Ok(())\n    }\n\n    fn before_click(&self) -> Result<()> {\n        for holdkey in &self.holdkeys {\n            let key_input = [Input::create_virtual_key(*holdkey, KEYEVENTF_KEYDOWN)];\n            send_input(&key_input)?;\n            self.wait();\n        }\n\n        Ok(())\n    }\n\n    fn after_click(&self) -> Result<()> {\n        for holdkey in &self.holdkeys {\n            let key_input = [Input::create_virtual_key(*holdkey, KEYEVENTF_KEYUP)];\n            send_input(&key_input)?;\n            self.wait();\n        }\n\n        Ok(())\n    }\n\n    fn mouse_event(&self, x: i32, y: i32, flags: MOUSE_EVENT_FLAGS) -> Result<()> {\n        let input = [INPUT {\n            r#type: INPUT_MOUSE,\n            Anonymous: INPUT_0 {\n                mi: MOUSEINPUT {\n                    dx: x,\n                    dy: y,\n                    mouseData: 0,\n                    dwFlags: flags,\n                    time: 0,\n                    dwExtraInfo: 0,\n                },\n            },\n        }];\n        send_input(&input)?;\n        self.wait();\n\n        Ok(())\n    }\n\n    fn wait(&self) {\n        if self.interval > 0 {\n            sleep(Duration::from_millis(self.interval));\n        }\n    }\n}\n\n/// Retrieves the `(width, height)` size of the primary screen.\npub fn get_screen_size() -> Result<(i32, i32)> {\n    let width = unsafe { GetSystemMetrics(SM_CXSCREEN) };\n    if width == 0 {\n        return Err(\"Failed to get screen width\".into());\n    }\n\n    let height = unsafe { GetSystemMetrics(SM_CYSCREEN) };\n    if height == 0 {\n        return Err(\"Failed to get screen height\".into());\n    }\n\n    Ok((width, height))\n}\n\nfn send_input(inputs: &[INPUT]) -> Result<()> {\n    let sent = unsafe { SendInput(inputs, mem::size_of::<INPUT>() as _) };\n\n    if sent == inputs.len() as u32 {\n        Ok(())\n    } else {\n        Err(\"Failed to send input\".into())\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/iterator.rs",
    "content": "use windows::Win32::{\n    Foundation::{HWND, LPARAM, RECT},\n    Graphics::Gdi::{HDC, HMONITOR},\n    UI::WindowsAndMessaging::{EnumChildWindows, EnumWindows},\n};\nuse windows_core::BOOL;\n\nuse crate::{error::Result, windows_api::WindowsApi};\n\nuse super::{monitor::Monitor, window::Window};\n\n#[derive(Debug, Clone)]\npub struct WindowEnumerator {\n    parent: Option<HWND>,\n}\n\nunsafe impl Send for WindowEnumerator {}\nunsafe impl Sync for WindowEnumerator {}\n\nimpl WindowEnumerator {\n    pub fn new() -> Self {\n        Self { parent: None }\n    }\n\n    pub fn with_parent(mut self, parent: HWND) -> Self {\n        self.parent = Some(parent);\n        self\n    }\n\n    fn enumerate(\n        &self,\n        enum_proc: unsafe extern \"system\" fn(HWND, LPARAM) -> BOOL,\n        ptr: LPARAM,\n    ) -> Result<()> {\n        if let Some(parent) = self.parent {\n            unsafe { EnumChildWindows(Some(parent), Some(enum_proc), ptr).ok()? };\n        } else {\n            unsafe { EnumWindows(Some(enum_proc), ptr)? };\n        }\n        Ok(())\n    }\n\n    /// Will call the callback for each window while enumerating.\n    /// If enumeration fails it will return error.\n    pub fn for_each<F>(&self, cb: F) -> Result<()>\n    where\n        F: FnMut(Window),\n    {\n        type ForEachCallback<'a> = Box<dyn FnMut(Window) + 'a>;\n        let mut callback: ForEachCallback = Box::new(cb);\n\n        unsafe extern \"system\" fn enum_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {\n            if let Some(boxed) = (lparam.0 as *mut ForEachCallback).as_mut() {\n                (*boxed)(Window::from(hwnd));\n            }\n            true.into()\n        }\n\n        self.enumerate(enum_proc, LPARAM(&mut callback as *mut _ as isize))\n    }\n\n    pub fn for_each_and_descendants<F>(&self, cb: F) -> Result<()>\n    where\n        F: FnMut(Window),\n    {\n        let mut cb = cb;\n        self.for_each_and_descendants_impl(&mut cb)\n    }\n\n    fn for_each_and_descendants_impl<F>(&self, cb: &mut F) -> Result<()>\n    where\n        F: FnMut(Window),\n    {\n        self.for_each(|window| {\n            cb(window);\n            // ignore errors on recursive children enums\n            let _ = Self::new()\n                .with_parent(window.hwnd())\n                .for_each_and_descendants_impl(cb);\n        })\n    }\n\n    /// Will call the callback for each window while enumerating.\n    /// If enumeration fails it will return error.\n    pub fn map<F, T>(&self, cb: F) -> Result<Vec<T>>\n    where\n        F: FnMut(HWND) -> T,\n    {\n        struct MapCallbackWrapper<'a, T> {\n            cb: Box<dyn FnMut(HWND) -> T + 'a>,\n            processed: Vec<T>,\n        }\n\n        unsafe extern \"system\" fn enum_proc<T>(hwnd: HWND, lparam: LPARAM) -> BOOL {\n            if let Some(wrapper) = (lparam.0 as *mut MapCallbackWrapper<T>).as_mut() {\n                wrapper.processed.push((wrapper.cb)(hwnd));\n            }\n            true.into()\n        }\n\n        let mut wrapper = MapCallbackWrapper {\n            cb: Box::new(cb),\n            processed: Vec::new(),\n        };\n\n        self.enumerate(enum_proc::<T>, LPARAM(&mut wrapper as *mut _ as isize))?;\n        Ok(wrapper.processed)\n    }\n}\n\n#[derive(Debug, Clone, Default)]\npub struct MonitorEnumerator;\n\nimpl MonitorEnumerator {\n    pub fn enumerate_win32() -> Result<Vec<Monitor>> {\n        let mut handles = Vec::new();\n\n        unsafe extern \"system\" fn get_handles_proc(\n            hmonitor: HMONITOR,\n            _hdc: HDC,\n            _rect_clip: *mut RECT,\n            lparam: LPARAM,\n        ) -> BOOL {\n            let data_ptr = lparam.0 as *mut Vec<Monitor>;\n            if let Some(data) = data_ptr.as_mut() {\n                data.push(Monitor::from(hmonitor));\n            }\n            true.into()\n        }\n\n        WindowsApi::enum_display_monitors(Some(get_handles_proc), &mut handles as *mut _ as isize)?;\n        Ok(handles)\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/mod.rs",
    "content": "mod app_bar;\nmod com;\nmod devices;\npub mod event_window;\npub mod hdc;\npub mod input;\nmod iterator;\npub mod monitor;\npub mod process;\npub mod string_utils;\npub mod types;\npub mod undocumented;\npub mod window;\n\npub use app_bar::*;\npub use com::*;\npub use devices::*;\npub use iterator::*;\n\nuse itertools::Itertools;\nuse process::ProcessInformationFlag;\nuse string_utils::WindowsString;\nuse widestring::U16CStr;\nuse windows_core::{Interface, Owned};\n\nuse std::{\n    ffi::OsString,\n    os::windows::ffi::{OsStrExt, OsStringExt},\n    path::{Path, PathBuf},\n    thread::sleep,\n    time::Duration,\n};\n\nuse windows::{\n    core::{BSTR, GUID, PCWSTR},\n    ApplicationModel::AppInfo,\n    Storage::Streams::{\n        DataReader, IRandomAccessStreamReference, IRandomAccessStreamWithContentType,\n    },\n    Wdk::System::{\n        SystemServices::PROCESS_EXTENDED_BASIC_INFORMATION,\n        Threading::{NtQueryInformationProcess, ProcessBasicInformation},\n    },\n    Win32::{\n        Devices::Display::{\n            GetNumberOfPhysicalMonitorsFromHMONITOR, GetPhysicalMonitorsFromHMONITOR,\n            PHYSICAL_MONITOR,\n        },\n        Foundation::{\n            HANDLE, HMODULE, HWND, LPARAM, LUID, MAX_PATH, POINT, RECT, STATUS_SUCCESS, WPARAM,\n        },\n        Graphics::{\n            Dwm::{\n                DwmGetWindowAttribute, DWMWA_CLOAKED, DWMWA_EXTENDED_FRAME_BOUNDS,\n                DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, DWMWINDOWATTRIBUTE, DWM_CLOAKED_APP,\n                DWM_CLOAKED_INHERITED, DWM_CLOAKED_SHELL,\n            },\n            Gdi::{\n                EnumDisplayMonitors, GetMonitorInfoW, MonitorFromPoint, MonitorFromWindow,\n                HMONITOR, MONITORENUMPROC, MONITORINFOEXW, MONITOR_DEFAULTTOPRIMARY,\n            },\n        },\n        Security::{\n            AdjustTokenPrivileges,\n            Authentication::Identity::{GetUserNameExW, EXTENDED_NAME_FORMAT},\n            GetTokenInformation, LookupPrivilegeValueW, TokenElevation, SE_PRIVILEGE_ENABLED,\n            SE_SHUTDOWN_NAME, TOKEN_ADJUST_PRIVILEGES, TOKEN_ELEVATION, TOKEN_PRIVILEGES,\n            TOKEN_QUERY,\n        },\n        Storage::{\n            EnhancedStorage::{\n                PKEY_AppUserModel_ID, PKEY_AppUserModel_PreventPinning,\n                PKEY_AppUserModel_RelaunchCommand, PKEY_AppUserModel_RelaunchDisplayNameResource,\n                PKEY_AppUserModel_RelaunchIconResource, PKEY_AppUserModel_ToastActivatorCLSID,\n                PKEY_FileDescription,\n            },\n            FileSystem::WIN32_FIND_DATAW,\n        },\n        System::{\n            Com::{IPersistFile, STGM_READ},\n            Environment::ExpandEnvironmentStringsW,\n            LibraryLoader::GetModuleHandleW,\n            Power::{GetSystemPowerStatus, SetSuspendState, SYSTEM_POWER_STATUS},\n            RemoteDesktop::ProcessIdToSessionId,\n            Shutdown::{ExitWindowsEx, LockWorkStation, EXIT_WINDOWS_FLAGS, SHUTDOWN_REASON},\n            SystemInformation::{GetComputerNameExW, COMPUTER_NAME_FORMAT},\n            Threading::{\n                AttachThreadInput, GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId,\n                OpenProcess, OpenProcessToken, QueryFullProcessImageNameW, PROCESS_ACCESS_RIGHTS,\n                PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION,\n            },\n        },\n        UI::{\n            HiDpi::{GetDpiForMonitor, MDT_EFFECTIVE_DPI},\n            Shell::{\n                BHID_EnumItems, IEnumShellItems, IShellItem2, IShellLinkW, IVirtualDesktopManager,\n                PropertiesSystem::{IPropertyStore, SHGetPropertyStoreForWindow, GPS_DEFAULT},\n                SHCreateItemFromParsingName, SHGetKnownFolderItem, SHGetKnownFolderPath,\n                SHLoadIndirectString, ShellExecuteExW, ShellLink, VirtualDesktopManager,\n                KF_FLAG_DEFAULT, SHELLEXECUTEINFOW, SIGDN_NORMALDISPLAY,\n            },\n            WindowsAndMessaging::{\n                BringWindowToTop, FindWindowExW, GetClassNameW, GetDesktopWindow,\n                GetForegroundWindow, GetParent, GetSystemMetrics, GetWindow, GetWindowLongW,\n                GetWindowRect, GetWindowTextW, GetWindowThreadProcessId, IsIconic, IsWindow,\n                IsWindowVisible, IsZoomed, PostMessageW, SetForegroundWindow, SetWindowPos,\n                ShowWindow, ShowWindowAsync, SystemParametersInfoW, GWL_EXSTYLE, GWL_STYLE,\n                GW_OWNER, SET_WINDOW_POS_FLAGS, SHOW_WINDOW_CMD, SM_CXVIRTUALSCREEN,\n                SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SPIF_SENDCHANGE,\n                SPIF_UPDATEINIFILE, SPI_GETDESKWALLPAPER, SPI_SETDESKWALLPAPER, SWP_ASYNCWINDOWPOS,\n                SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, SW_SHOWNORMAL,\n                SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WINDOW_EX_STYLE, WINDOW_STYLE, WS_SIZEBOX,\n                WS_THICKFRAME,\n            },\n        },\n    },\n    UI::ViewManagement::UISettings,\n};\n\nuse crate::{\n    error::{Result, WindowsResultExt},\n    hook::HookManager,\n    windows_api::{\n        input::Keyboard,\n        window::{event::WinEvent, Window},\n    },\n};\n\n#[macro_export]\nmacro_rules! pcstr {\n    ($s:literal) => {\n        windows::core::s!($s)\n    };\n}\n\n#[macro_export]\nmacro_rules! pcwstr {\n    ($s:literal) => {\n        windows::core::w!($s)\n    };\n}\n\n#[macro_export]\nmacro_rules! hstring {\n    ($s:literal) => {\n        windows::core::h!($s)\n    };\n}\n\npub struct WindowsApi {}\nimpl WindowsApi {\n    pub fn module_handle_w() -> Result<HMODULE> {\n        Ok(unsafe { GetModuleHandleW(None) }?)\n    }\n\n    pub fn enum_display_monitors(\n        callback: MONITORENUMPROC,\n        callback_data_address: isize,\n    ) -> Result<()> {\n        unsafe {\n            EnumDisplayMonitors(None, None, callback, LPARAM(callback_data_address))\n                .ok()\n                .filter_fake_error()?;\n        }\n        Ok(())\n    }\n\n    pub fn post_message(hwnd: HWND, message: u32, wparam: usize, lparam: isize) -> Result<()> {\n        unsafe { PostMessageW(Some(hwnd), message, WPARAM(wparam), LPARAM(lparam))? };\n        Ok(())\n    }\n\n    pub fn get_monitor_scale_factor(hmonitor: HMONITOR) -> Result<f64> {\n        let mut dpi_x: u32 = 0;\n        let mut _dpi_y: u32 = 0;\n        unsafe { GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut _dpi_y)? };\n        // 96 is the default DPI value on Windows\n        Ok(dpi_x as f64 / 96_f64)\n    }\n\n    pub fn get_text_scale_factor() -> Result<f64> {\n        Ok(UISettings::new()?.TextScaleFactor()?)\n    }\n\n    /// Behaviour is undefined if an invalid HWND is given\n    /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid\n    pub fn window_thread_process_id(hwnd: HWND) -> (u32, u32) {\n        let mut process_id: u32 = 0;\n\n        let thread_id = unsafe {\n            GetWindowThreadProcessId(hwnd, Option::from(std::ptr::addr_of_mut!(process_id)))\n        };\n\n        (process_id, thread_id)\n    }\n\n    pub fn find_window(\n        parent: Option<HWND>,\n        after: Option<HWND>,\n        title: Option<String>,\n        class: Option<impl Into<String>>,\n    ) -> Result<HWND> {\n        let title = WindowsString::from(title.unwrap_or_default());\n        let class = WindowsString::from(class.map(Into::into).unwrap_or_default());\n        let found = unsafe {\n            FindWindowExW(\n                parent,\n                after,\n                if class.is_empty() {\n                    PCWSTR::null()\n                } else {\n                    class.as_pcwstr()\n                },\n                if title.is_empty() {\n                    PCWSTR::null()\n                } else {\n                    title.as_pcwstr()\n                },\n            )\n        }?;\n        Ok(found)\n    }\n\n    pub fn current_process() -> HANDLE {\n        unsafe { GetCurrentProcess() }\n    }\n\n    pub fn current_process_id() -> u32 {\n        unsafe { GetCurrentProcessId() }\n    }\n\n    #[allow(dead_code)]\n    pub fn current_thread_id() -> u32 {\n        unsafe { GetCurrentThreadId() }\n    }\n\n    pub fn current_session_id() -> u32 {\n        let process_id = Self::current_process_id();\n        let mut session_id = 0;\n        // this should never fail for own process\n        unsafe { ProcessIdToSessionId(process_id, &mut session_id).expect(\"Can't get session id\") };\n        session_id\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getforegroundwindow\n    pub fn get_foreground_window() -> HWND {\n        let mut hwnd = unsafe { GetForegroundWindow() };\n        // based on windows doc, get foreground can return null while window is losing activation\n        // so we wait until we get a valid window\n        while hwnd.is_invalid() {\n            hwnd = unsafe { GetForegroundWindow() };\n        }\n        hwnd\n    }\n\n    pub fn is_window(hwnd: HWND) -> bool {\n        unsafe { IsWindow(Some(hwnd)) }.into()\n    }\n\n    pub fn is_window_visible(hwnd: HWND) -> bool {\n        unsafe { IsWindowVisible(hwnd) }.into()\n    }\n\n    pub fn is_iconic(hwnd: HWND) -> bool {\n        unsafe { IsIconic(hwnd) }.into()\n    }\n\n    pub fn is_zoomed(hwnd: HWND) -> bool {\n        unsafe { IsZoomed(hwnd) }.into()\n    }\n\n    pub fn is_fullscreen(hwnd: HWND) -> Result<bool> {\n        let styles = WindowsApi::get_styles(hwnd);\n        if styles.contains(WS_THICKFRAME) {\n            return Ok(false);\n        }\n\n        let rc_monitor = WindowsApi::monitor_rect(WindowsApi::monitor_from_window(hwnd))?;\n        let window_rect = WindowsApi::get_inner_window_rect(hwnd)?;\n        Ok(window_rect.left <= rc_monitor.left\n            && window_rect.top <= rc_monitor.top\n            && window_rect.right >= rc_monitor.right\n            && window_rect.bottom >= rc_monitor.bottom)\n    }\n\n    pub fn is_cloaked(hwnd: HWND) -> Result<bool> {\n        let mut cloaked: u32 = 0;\n        Self::dwm_get_window_attribute(hwnd, DWMWA_CLOAKED, &mut cloaked)?;\n        Ok(matches!(\n            cloaked,\n            DWM_CLOAKED_APP | DWM_CLOAKED_SHELL | DWM_CLOAKED_INHERITED\n        ))\n    }\n\n    /// Sets the visibility state of a window created by the calling thread (could cause a deadlock)\n    ///\n    /// The deadlock occurs if show_window is called for a window created on a different thread but in same process.\n    /// Is safe to use for windows created by other processes\n    ///\n    /// Use this only if you need wait for the window to be visible, otherwise use show_window_async\n    ///\n    /// https://stackoverflow.com/questions/16881820/win32-api-deadlocks-while-using-different-threads\n    /// https://stackoverflow.com/questions/15637124/whats-the-difference-between-showwindow-and-showwindowasync\n    pub fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) -> Result<()> {\n        // BOOL is returned but does not signify whether or not the operation was succesful\n        // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow\n        unsafe { ShowWindow(hwnd, command) }\n            .ok()\n            .filter_fake_error()?;\n        Ok(())\n    }\n\n    pub fn show_window_async(hwnd: HWND, command: SHOW_WINDOW_CMD) -> Result<()> {\n        // BOOL is returned but does not signify whether or not the operation was succesful\n        // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindowasync\n        unsafe { ShowWindowAsync(hwnd, command) }\n            .ok()\n            .filter_fake_error()?;\n        Ok(())\n    }\n\n    pub fn get_styles(hwnd: HWND) -> WINDOW_STYLE {\n        WINDOW_STYLE(unsafe { GetWindowLongW(hwnd, GWL_STYLE) } as u32)\n    }\n\n    pub fn get_ex_styles(hwnd: HWND) -> WINDOW_EX_STYLE {\n        WINDOW_EX_STYLE(unsafe { GetWindowLongW(hwnd, GWL_EXSTYLE) } as u32)\n    }\n\n    fn _set_position(\n        hwnd: HWND,\n        order: Option<HWND>,\n        rect: RECT,\n        flags: SET_WINDOW_POS_FLAGS,\n    ) -> Result<()> {\n        unsafe {\n            SetWindowPos(\n                hwnd,\n                order,\n                rect.left,\n                rect.top,\n                (rect.right - rect.left).abs(),\n                (rect.bottom - rect.top).abs(),\n                flags,\n            )\n            .filter_fake_error()?;\n        }\n        Ok(())\n    }\n\n    /// Similar to ShowWindow could cause a deadlock if the window is created on a different thread.\n    ///\n    /// Add the flag `SWP_ASYNCWINDOWPOS` to avoid that of if you don't need to wait for the window position to be set\n    pub fn set_position(\n        hwnd: HWND,\n        order: Option<HWND>,\n        rect: &RECT,\n        flags: SET_WINDOW_POS_FLAGS,\n    ) -> Result<()> {\n        let flags = match order {\n            Some(_) => flags,\n            None => SWP_NOZORDER | flags,\n        } | SWP_NOACTIVATE;\n        Self::_set_position(hwnd, order, *rect, flags)\n    }\n\n    pub fn move_window(hwnd: HWND, rect: &RECT) -> Result<()> {\n        Self::set_position(hwnd, None, rect, SWP_NOSIZE | SWP_ASYNCWINDOWPOS)\n    }\n\n    pub fn bring_to_top(hwnd: HWND) -> Result<()> {\n        unsafe { BringWindowToTop(hwnd)? };\n        Ok(())\n    }\n\n    #[allow(dead_code)]\n    pub fn attach_thread_input(thread_id: u32, attach_to: u32, attach: bool) -> Result<()> {\n        unsafe { AttachThreadInput(thread_id, attach_to, attach).ok()? };\n        Ok(())\n    }\n\n    pub fn set_foreground(hwnd: HWND) -> Result<()> {\n        let window = Window::from(hwnd);\n\n        if !unsafe { SetForegroundWindow(hwnd).as_bool() } {\n            // https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open\n            let keyboard = Keyboard::new();\n            keyboard.send_keys(\"{alt}\")?;\n            // this can fail but still be successful.\n            let _ = unsafe { SetForegroundWindow(hwnd) };\n        }\n\n        // extra validation\n        if Window::get_foregrounded() != window {\n            return Err(\"Failed to set foreground window\".into());\n        }\n\n        // event sometimes is not emitted, so we manually emit it, this will cause 2 foreground events\n        // if original was recieved, btw having it twice is better than nothing\n        HookManager::event_tx().send((WinEvent::SystemForeground, window))?;\n        Ok(())\n    }\n\n    fn open_process(\n        access_rights: PROCESS_ACCESS_RIGHTS,\n        inherit_handle: bool,\n        process_id: u32,\n    ) -> Result<Owned<HANDLE>> {\n        unsafe {\n            let handled = OpenProcess(access_rights, inherit_handle, process_id)?;\n            Ok(Owned::new(handled))\n        }\n    }\n\n    pub fn open_current_process_token() -> Result<Owned<HANDLE>> {\n        let mut token_handle = HANDLE::default();\n        unsafe {\n            OpenProcessToken(\n                Self::current_process(),\n                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,\n                &mut token_handle,\n            )?;\n\n            if token_handle.is_invalid() {\n                return Err(\"OpenProcessToken failed\".into());\n            }\n            Ok(Owned::new(token_handle))\n        }\n    }\n\n    pub fn get_luid(system: PCWSTR, name: PCWSTR) -> Result<LUID> {\n        let mut luid = LUID::default();\n        unsafe { LookupPrivilegeValueW(system, name, &mut luid)? };\n        Ok(luid)\n    }\n\n    pub fn enable_privilege(name: PCWSTR) -> Result<()> {\n        let token_handle = Self::open_current_process_token()?;\n        let mut tkp = TOKEN_PRIVILEGES {\n            PrivilegeCount: 1,\n            ..Default::default()\n        };\n\n        tkp.Privileges[0].Luid = Self::get_luid(PCWSTR::null(), name)?;\n        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n        unsafe { AdjustTokenPrivileges(*token_handle, false, Some(&tkp), 0, None, None)? };\n        Ok(())\n    }\n\n    pub fn get_parent(hwnd: HWND) -> Result<HWND> {\n        Ok(unsafe { GetParent(hwnd)? })\n    }\n\n    pub fn get_owner(hwnd: HWND) -> Result<HWND> {\n        Ok(unsafe { GetWindow(hwnd, GW_OWNER)? })\n    }\n\n    pub fn get_desktop_window() -> HWND {\n        unsafe { GetDesktopWindow() }\n    }\n\n    pub fn is_process_frozen(process_id: u32) -> Result<bool> {\n        let handle = Self::open_process(PROCESS_QUERY_LIMITED_INFORMATION, false, process_id)?;\n        let is_frozen = unsafe {\n            let mut buffer: [PROCESS_EXTENDED_BASIC_INFORMATION; 1] = std::mem::zeroed();\n            let status = NtQueryInformationProcess(\n                *handle,\n                ProcessBasicInformation,\n                buffer.as_mut_ptr() as _,\n                std::mem::size_of::<PROCESS_EXTENDED_BASIC_INFORMATION>() as _,\n                0u32 as _,\n            );\n\n            if status != STATUS_SUCCESS {\n                return Err(format!(\n                    \"NtQueryInformationProcess failed with status: {:x}\",\n                    status.0\n                )\n                .into());\n            }\n\n            let data = buffer[0];\n            data.Anonymous.Flags & ProcessInformationFlag::IsFrozen as u32 != 0\n        };\n        Ok(is_frozen)\n    }\n\n    pub fn exe_path_by_process(process_id: u32) -> Result<OsString> {\n        let mut path = WindowsString::new_to_fill(1024);\n        let handle = Self::open_process(PROCESS_QUERY_LIMITED_INFORMATION, false, process_id)?;\n        unsafe {\n            QueryFullProcessImageNameW(*handle, PROCESS_NAME_WIN32, path.as_pwstr(), &mut 1024)?;\n        }\n        Ok(path.to_os_string())\n    }\n\n    pub fn get_class(hwnd: HWND) -> Result<String> {\n        let mut text: [u16; 512] = [0; 512];\n        let len = unsafe { GetClassNameW(hwnd, &mut text) };\n        let length = usize::try_from(len).unwrap_or(0);\n        Ok(String::from_utf16(&text[..length])?)\n    }\n\n    pub fn get_shell_item(path: &Path) -> Result<IShellItem2> {\n        let wide_path: Vec<u16> = path.as_os_str().encode_wide().chain(Some(0)).collect();\n        let item = unsafe { SHCreateItemFromParsingName(PCWSTR(wide_path.as_ptr()), None)? };\n        Ok(item)\n    }\n\n    pub fn get_property_store_for_window(hwnd: HWND) -> Result<IPropertyStore> {\n        Ok(unsafe { SHGetPropertyStoreForWindow(hwnd)? })\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-id\n    pub fn get_window_app_user_model_id(hwnd: HWND) -> Result<String> {\n        let store = Self::get_property_store_for_window(hwnd)?;\n        let value = unsafe { store.GetValue(&PKEY_AppUserModel_ID)? };\n        if value.is_empty() {\n            return Err(\"No AppUserModel_ID\".into());\n        }\n        Ok(BSTR::try_from(&value)?.to_string())\n    }\n\n    pub fn get_window_prevent_pinning(hwnd: HWND) -> Result<bool> {\n        let store = Self::get_property_store_for_window(hwnd)?;\n        let value = unsafe { store.GetValue(&PKEY_AppUserModel_PreventPinning)? };\n        if value.is_empty() {\n            return Err(\"No AppUserModel_PreventPinning\".into());\n        }\n        Ok(bool::try_from(&value)?)\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchcommand\n    pub fn get_window_relaunch_command(hwnd: HWND) -> Result<String> {\n        let store = Self::get_property_store_for_window(hwnd)?;\n        let value = unsafe { store.GetValue(&PKEY_AppUserModel_RelaunchCommand)? };\n        if value.is_empty() {\n            return Err(\"No AppUserModel_RelaunchCommand\".into());\n        }\n        Ok(BSTR::try_from(&value)?.to_string())\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchdisplaynameresource\n    pub fn get_window_relaunch_display_name(hwnd: HWND) -> Result<String> {\n        let store = Self::get_property_store_for_window(hwnd)?;\n        let value = unsafe { store.GetValue(&PKEY_AppUserModel_RelaunchDisplayNameResource)? };\n        if value.is_empty() {\n            return Err(\"No AppUserModel_RelaunchDisplayName\".into());\n        }\n        Ok(BSTR::try_from(&value)?.to_string())\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchiconresource\n    pub fn get_window_relaunch_icon_resource(hwnd: HWND) -> Result<String> {\n        let store = Self::get_property_store_for_window(hwnd)?;\n        let value = unsafe { store.GetValue(&PKEY_AppUserModel_RelaunchIconResource)? };\n        if value.is_empty() {\n            return Err(\"No AppUserModel_RelaunchIconResource\".into());\n        }\n        Ok(BSTR::try_from(&value)?.to_string())\n    }\n\n    pub fn is_uwp_package_id(package_id: &str) -> bool {\n        Self::get_uwp_app_info(package_id).is_ok()\n    }\n\n    pub fn get_uwp_app_info(umid: &str) -> Result<AppInfo> {\n        let app_info = AppInfo::GetFromAppUserModelId(&umid.into())?;\n        Ok(app_info)\n    }\n\n    /// return the program and arguments\n    pub fn resolve_lnk_target(lnk_path: &Path) -> Result<(PathBuf, OsString)> {\n        Com::run_with_context(|| {\n            let shell_link: IShellLinkW = Com::create_instance(&ShellLink)?;\n            let lnk_wide = lnk_path\n                .as_os_str()\n                .encode_wide()\n                .chain(Some(0))\n                .collect_vec();\n\n            let persist_file: IPersistFile = shell_link.cast()?;\n            unsafe { persist_file.Load(PCWSTR(lnk_wide.as_ptr()), STGM_READ)? };\n\n            let mut target_path = WindowsString::new_to_fill(1024);\n            let mut idk = WIN32_FIND_DATAW::default();\n            unsafe { shell_link.GetPath(target_path.as_mut_slice(), &mut idk, 0)? };\n            target_path = Self::resolve_environment_variables(&target_path)?;\n\n            let mut arguments = WindowsString::new_to_fill(1024);\n            unsafe { shell_link.GetArguments(arguments.as_mut_slice())? };\n\n            Ok((target_path.to_os_string().into(), arguments.to_os_string()))\n        })\n    }\n\n    pub fn resolve_lnk_custom_icon_path(lnk_path: &Path) -> Result<(PathBuf, i32)> {\n        Com::run_with_context(|| {\n            let shell_link: IShellLinkW = Com::create_instance(&ShellLink)?;\n            let lnk_wide = lnk_path\n                .as_os_str()\n                .encode_wide()\n                .chain(Some(0))\n                .collect_vec();\n\n            let persist_file: IPersistFile = shell_link.cast()?;\n            unsafe { persist_file.Load(PCWSTR(lnk_wide.as_ptr()), STGM_READ)? };\n\n            let mut icon_path = WindowsString::new_to_fill(1024);\n            let mut icon_idx = 0;\n            unsafe { shell_link.GetIconLocation(icon_path.as_mut_slice(), &mut icon_idx)? };\n\n            if icon_path.is_empty() {\n                return Err(\"There is no custom icon for this link file\".into());\n            }\n\n            icon_path = Self::resolve_environment_variables(&icon_path)?;\n            Ok((PathBuf::from(icon_path.to_os_string()), icon_idx))\n        })\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shloadindirectstring\n    /// Extracts a specified text resource when given that resource in the form of an indirect string\n    /// (a string that begins with the '@' symbol).\n    pub fn resolve_indirect_string(text: &str) -> Result<String> {\n        let source = WindowsString::from_str(text);\n        let mut out = WindowsString::new_to_fill(1024);\n        unsafe { SHLoadIndirectString(source.as_pcwstr(), out.as_mut_slice(), None)? };\n        Ok(out.to_string())\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-expandenvironmentstringsw\n    /// Expands all environment variables in a string (for example, %PATH%).\n    pub fn resolve_environment_variables(source: &WindowsString) -> Result<WindowsString> {\n        let len = unsafe { ExpandEnvironmentStringsW(source.as_pcwstr(), None) };\n        let mut out = WindowsString::new_to_fill(len as usize);\n        unsafe { ExpandEnvironmentStringsW(source.as_pcwstr(), Some(out.as_mut_slice())) };\n        Ok(out)\n    }\n\n    pub fn get_executable_display_name(path: &Path) -> Result<String> {\n        Com::run_with_context(|| unsafe {\n            let shell_item = Self::get_shell_item(path)?;\n            let text = shell_item\n                .GetString(&PKEY_FileDescription)\n                .or_else(|_| shell_item.GetDisplayName(SIGDN_NORMALDISPLAY))?;\n            Ok(text.to_string()?)\n        })\n    }\n\n    pub fn get_file_umid(path: &Path) -> Result<String> {\n        Com::run_with_context(|| unsafe {\n            let shell_item = Self::get_shell_item(path)?;\n            let store: IPropertyStore = shell_item.GetPropertyStore(GPS_DEFAULT)?;\n            let value = store.GetValue(&PKEY_AppUserModel_ID)?;\n            if value.is_empty() {\n                return Err(\"No AppUserModel_ID\".into());\n            }\n            Ok(value.to_string())\n        })\n    }\n\n    pub fn get_file_toast_activator(path: &Path) -> Result<String> {\n        Com::run_with_context(|| unsafe {\n            let shell_item = Self::get_shell_item(path)?;\n            let store: IPropertyStore = shell_item.GetPropertyStore(GPS_DEFAULT)?;\n            let value = store.GetValue(&PKEY_AppUserModel_ToastActivatorCLSID)?;\n            if value.is_empty() {\n                return Err(\"No AppUserModel ToastActivator CLSID\".into());\n            }\n            Ok(value\n                .to_string()\n                .trim_start_matches(\"{\")\n                .trim_end_matches(\"}\")\n                .to_owned())\n        })\n    }\n\n    pub fn get_window_text(hwnd: HWND) -> String {\n        let mut text: [u16; 512] = [0; 512];\n        let len = unsafe { GetWindowTextW(hwnd, &mut text) };\n        let length = usize::try_from(len).unwrap_or(0);\n        String::from_utf16(&text[..length]).unwrap_or(\"\".to_owned())\n    }\n\n    pub fn dwm_get_window_attribute<T>(\n        hwnd: HWND,\n        attribute: DWMWINDOWATTRIBUTE,\n        value: &mut T,\n    ) -> Result<()> {\n        unsafe {\n            DwmGetWindowAttribute(\n                hwnd,\n                attribute,\n                (value as *mut T).cast(),\n                u32::try_from(std::mem::size_of::<T>())?,\n            )?;\n        }\n        Ok(())\n    }\n\n    /// Get the window rect including drop shadow\n    pub fn get_outer_window_rect(hwnd: HWND) -> Result<RECT> {\n        let mut rect = RECT::default();\n        unsafe { GetWindowRect(hwnd, &mut rect)? };\n        Ok(rect)\n    }\n\n    fn get_window_thickness(hwnd: HWND) -> u32 {\n        let mut thickness = 0u32;\n        let _ = Self::dwm_get_window_attribute(\n            hwnd,\n            DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,\n            &mut thickness,\n        );\n        thickness\n    }\n\n    /// return the window rect excluding drop shadow & thick border\n    /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowrect#remarks\n    pub fn get_inner_window_rect(hwnd: HWND) -> Result<RECT> {\n        let mut rect = RECT::default();\n        if Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect).is_err() {\n            rect = Self::get_outer_window_rect(hwnd)?;\n        }\n\n        let styles = Self::get_styles(hwnd);\n        if styles.contains(WS_THICKFRAME) || styles.contains(WS_SIZEBOX) {\n            let thickness = Self::get_window_thickness(hwnd) as i32;\n            rect.left += thickness;\n            rect.top += thickness;\n            rect.right -= thickness;\n            rect.bottom -= thickness;\n        }\n\n        Ok(rect)\n    }\n\n    pub fn monitor_from_window(hwnd: HWND) -> HMONITOR {\n        unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY) }\n    }\n\n    pub fn monitor_from_point(point: &seelen_core::Point) -> HMONITOR {\n        unsafe {\n            MonitorFromPoint(\n                POINT {\n                    x: point.x,\n                    y: point.y,\n                },\n                MONITOR_DEFAULTTOPRIMARY,\n            )\n        }\n    }\n\n    pub fn primary_monitor() -> HMONITOR {\n        unsafe { MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY) }\n    }\n\n    pub fn get_physical_monitors(monitor: HMONITOR) -> Result<Vec<PHYSICAL_MONITOR>> {\n        let mut c_physical_monitors: u32 = 0;\n        let mut p_physical_monitors: Vec<PHYSICAL_MONITOR> = Vec::new();\n        unsafe {\n            GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, &mut c_physical_monitors)?;\n            p_physical_monitors.resize(c_physical_monitors as usize, std::mem::zeroed());\n            GetPhysicalMonitorsFromHMONITOR(monitor, p_physical_monitors.as_mut())?;\n        };\n        Ok(p_physical_monitors)\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen\n    pub fn virtual_screen_rect() -> Result<RECT> {\n        let mut rect = RECT::default();\n        unsafe {\n            rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\n            rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\n            rect.right = rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);\n            rect.bottom = rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);\n        }\n        Ok(rect)\n    }\n\n    pub fn monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFOEXW> {\n        let mut ex_info = MONITORINFOEXW::default();\n        ex_info.monitorInfo.cbSize = u32::try_from(std::mem::size_of::<MONITORINFOEXW>())?;\n        unsafe { GetMonitorInfoW(hmonitor, &mut ex_info.monitorInfo).ok() }?;\n        Ok(ex_info)\n    }\n\n    pub fn monitor_rect(hmonitor: HMONITOR) -> Result<RECT> {\n        Ok(Self::monitor_info(hmonitor)?.monitorInfo.rcMonitor)\n    }\n\n    /// returns the difference between outer window rect and inner window rect\n    pub fn shadow_rect(hwnd: HWND) -> Result<RECT> {\n        let outer_rect = Self::get_outer_window_rect(hwnd)?;\n        let inner_rect = Self::get_inner_window_rect(hwnd)?;\n        Ok(RECT {\n            left: outer_rect.left - inner_rect.left,\n            top: outer_rect.top - inner_rect.top,\n            right: outer_rect.right - inner_rect.right,\n            bottom: outer_rect.bottom - inner_rect.bottom,\n        })\n    }\n\n    pub fn _get_virtual_desktop_manager() -> Result<IVirtualDesktopManager> {\n        Com::create_instance(&VirtualDesktopManager)\n    }\n\n    pub fn _get_virtual_desktop_id(hwnd: HWND) -> Result<GUID> {\n        let manager = Self::_get_virtual_desktop_manager()?;\n        let mut desktop_id = GUID::zeroed();\n        let mut attempt = 0;\n        while desktop_id.to_u128() == 0 && attempt < 10 {\n            attempt += 1;\n            sleep(Duration::from_millis(30));\n            if let Ok(desktop) = unsafe { manager.GetWindowDesktopId(hwnd) } {\n                desktop_id = desktop\n            }\n        }\n        if desktop_id.to_u128() == 0 {\n            return Err(format!(\"Failed to get desktop id for: {hwnd:?}\").into());\n        }\n        Ok(desktop_id)\n    }\n\n    pub fn get_wallpaper() -> Result<PathBuf> {\n        let mut path = [0_u16; MAX_PATH as usize];\n        unsafe {\n            SystemParametersInfoW(\n                SPI_GETDESKWALLPAPER,\n                MAX_PATH,\n                Some(path.as_mut_ptr() as _),\n                SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),\n            )?;\n        }\n        Ok(PathBuf::from(\n            U16CStr::from_slice_truncate(&path)?\n                .to_ustring()\n                .to_string_lossy(),\n        ))\n    }\n\n    pub fn refresh_desktop() -> Result<()> {\n        unsafe {\n            SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, None, SPIF_UPDATEINIFILE)?;\n        }\n        Ok(())\n    }\n\n    pub fn set_wallpaper(path: String) -> Result<()> {\n        if !PathBuf::from(&path).exists() {\n            return Err(\"File not found\".into());\n        }\n\n        let mut path = path.encode_utf16().chain(Some(0)).collect_vec();\n        unsafe {\n            SystemParametersInfoW(\n                SPI_SETDESKWALLPAPER,\n                MAX_PATH,\n                Some(path.as_mut_ptr() as _),\n                SPIF_SENDCHANGE | SPIF_UPDATEINIFILE,\n            )?;\n        }\n        Ok(())\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-exitwindowsex\n    pub fn exit_windows(flags: EXIT_WINDOWS_FLAGS, reason: SHUTDOWN_REASON) -> Result<()> {\n        WindowsApi::enable_privilege(SE_SHUTDOWN_NAME)?;\n        unsafe { ExitWindowsEx(flags, reason) }?;\n        Ok(())\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/api/powrprof/nf-powrprof-setsuspendstate\n    pub fn set_suspend_state(hibernate: bool) -> Result<()> {\n        WindowsApi::enable_privilege(SE_SHUTDOWN_NAME)?;\n        let success = unsafe { SetSuspendState(hibernate, false, false) };\n        if !success {\n            return Err(\"Failed to set suspend state\".into());\n        }\n        Ok(())\n    }\n\n    pub fn is_elevated() -> Result<bool> {\n        unsafe {\n            let mut elevation = TOKEN_ELEVATION::default();\n            let mut ret_len = 0;\n\n            let token_handle = Self::open_current_process_token()?;\n            GetTokenInformation(\n                *token_handle,\n                TokenElevation,\n                Some(&mut elevation as *mut _ as *mut _),\n                std::mem::size_of::<TOKEN_ELEVATION>() as u32,\n                &mut ret_len,\n            )?;\n            Ok(elevation.TokenIsElevated != 0)\n        }\n    }\n\n    pub fn get_system_power_status() -> Result<SYSTEM_POWER_STATUS> {\n        let mut power_status = SYSTEM_POWER_STATUS::default();\n        unsafe {\n            GetSystemPowerStatus(&mut power_status as _)?;\n        }\n        Ok(power_status)\n    }\n\n    pub fn stream_to_dynamic_image(\n        stream: IRandomAccessStreamWithContentType,\n    ) -> Result<image::DynamicImage> {\n        let size = stream.Size()?;\n        let mut buffer = vec![0u8; size as usize];\n\n        let input_stream = stream.GetInputStreamAt(0)?;\n        let data_reader = DataReader::CreateDataReader(&input_stream)?;\n\n        data_reader.LoadAsync(size as u32)?.join()?;\n        data_reader.ReadBytes(&mut buffer)?;\n\n        let image = image::load_from_memory_with_format(&buffer, image::ImageFormat::Png)?;\n        Ok(image)\n    }\n\n    pub fn extract_thumbnail_from_stream(\n        stream: IRandomAccessStreamWithContentType,\n    ) -> Result<PathBuf> {\n        let image = Self::stream_to_dynamic_image(stream)?;\n        let image_path = std::env::temp_dir().join(format!(\"{}.png\", uuid::Uuid::new_v4()));\n        image.save(&image_path)?;\n        Ok(image_path)\n    }\n\n    pub fn extract_thumbnail_from_ref(stream: IRandomAccessStreamReference) -> Result<PathBuf> {\n        Self::extract_thumbnail_from_stream(stream.OpenReadAsync()?.join()?)\n    }\n\n    pub fn lock_machine() -> Result<()> {\n        unsafe { Ok(LockWorkStation()?) }\n    }\n\n    // get current thread owner username\n    pub fn get_username(format: EXTENDED_NAME_FORMAT) -> Result<String> {\n        let mut size = 0;\n        unsafe { GetUserNameExW(format, None, &mut size) };\n        let mut name = WindowsString::new_to_fill(size as usize);\n        let sucess = unsafe { GetUserNameExW(format, Some(name.as_pwstr()), &mut size) };\n        if !sucess {\n            return Err(\"Failed to get username\".into());\n        }\n        Ok(name.to_string())\n    }\n\n    pub fn get_computer_name(format: COMPUTER_NAME_FORMAT) -> Result<String> {\n        let mut name = WindowsString::new_to_fill(1024);\n        unsafe { GetComputerNameExW(format, Some(name.as_pwstr()), &mut 1024)? };\n        Ok(name.to_string())\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid\n    pub fn known_folder(folder_id: windows::core::GUID) -> Result<PathBuf> {\n        let path = unsafe { SHGetKnownFolderPath(&folder_id, KF_FLAG_DEFAULT, None)? };\n        Ok(PathBuf::from(OsString::from_wide(unsafe {\n            path.as_wide()\n        })))\n    }\n\n    #[allow(dead_code)]\n    pub fn known_folder_item(folder_id: windows::core::GUID) -> Result<()> {\n        let item: IShellItem2 = unsafe { SHGetKnownFolderItem(&folder_id, KF_FLAG_DEFAULT, None)? };\n        let enumerator: IEnumShellItems = unsafe { item.BindToHandler(None, &BHID_EnumItems)? };\n\n        loop {\n            let mut items = [None; 1];\n            let mut fetched = 0u32;\n\n            unsafe { enumerator.Next(&mut items, Some(&mut fetched))? };\n\n            if fetched == 0 {\n                break;\n            }\n\n            if let Some(item) = &items[0] {\n                let item: IShellItem2 = item.cast()?;\n                // Obtener el nombre para mostrar\n                let display_name = unsafe {\n                    item.GetDisplayName(SIGDN_NORMALDISPLAY)?\n                        .to_hstring()\n                        .to_os_string()\n                };\n                let umid = unsafe {\n                    item.GetString(&PKEY_AppUserModel_ID)\n                        .ok()\n                        .map(|s| s.to_hstring().to_os_string())\n                };\n                println!(\"- {} // {:?}\", display_name.display(), umid);\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn execute(\n        program: String,\n        args: Option<String>,\n        working_dir: Option<PathBuf>,\n        elevated: bool,\n    ) -> Result<()> {\n        log::trace!(\"Running: {program:?} with args: {args:?} in working dir: {working_dir:?}\");\n\n        let program = WindowsString::from_str(&program);\n        let args = args.map(WindowsString::from);\n        let working_dir = working_dir.map(WindowsString::from);\n\n        let runas = WindowsString::from_str(\"runas\");\n\n        let mut info = SHELLEXECUTEINFOW {\n            cbSize: std::mem::size_of::<SHELLEXECUTEINFOW>() as _,\n            nShow: SW_SHOWNORMAL.0,\n            lpFile: program.as_pcwstr(),\n            lpParameters: match &args {\n                Some(args) => args.as_pcwstr(),\n                None => PCWSTR::null(),\n            },\n            lpDirectory: match &working_dir {\n                Some(working_dir) => working_dir.as_pcwstr(),\n                None => PCWSTR::null(),\n            },\n            lpVerb: match elevated {\n                true => runas.as_pcwstr(),\n                false => PCWSTR::null(),\n            },\n            ..Default::default()\n        };\n\n        unsafe { ShellExecuteExW(&mut info)? };\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/monitor/brightness.rs",
    "content": "use windows::Win32::{\n    Devices::Display::{\n        GetMonitorBrightness, GetMonitorCapabilities, SetMonitorBrightness, PHYSICAL_MONITOR,\n    },\n    Foundation::HANDLE,\n    Storage::FileSystem::{\n        CreateFileW, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_READ, FILE_SHARE_WRITE,\n        OPEN_EXISTING,\n    },\n};\n\nuse windows_core::{Owned, BOOL};\n\nuse crate::{\n    error::Result,\n    windows_api::{monitor::MonitorTarget, string_utils::WindowsString, WindowsApi},\n};\n\nuse super::Monitor;\n\n#[derive(Debug, Default)]\npub struct DdcciBrightnessValues {\n    pub min: u32,\n    pub current: u32,\n    pub max: u32,\n}\n\n#[allow(dead_code)]\nimpl MonitorTarget {\n    /// Opens and returns a file handle for a display device using its DOS device path.\\\n    /// These handles are only used for the `DeviceIoControl` API (for internal displays);\n    /// a handle can still be returned for external displays, but it should not be used.\n    fn get_file_handle(&self) -> Result<Owned<HANDLE>> {\n        let device_id = self.0.TryGetMonitor()?.DeviceId()?.to_os_string();\n        let device_id = WindowsString::from(device_id);\n\n        // This could fail for virtual devices e.g. Remote Desktop sessions - they are not real monitors\n        let handle = unsafe {\n            CreateFileW(\n                device_id.as_pcwstr(),\n                (FILE_GENERIC_READ | FILE_GENERIC_WRITE).0,\n                FILE_SHARE_READ | FILE_SHARE_WRITE,\n                None,\n                OPEN_EXISTING,\n                Default::default(),\n                None,\n            )?\n        };\n        Ok(unsafe { Owned::new(handle) })\n    }\n}\n\n#[allow(dead_code)]\nimpl Monitor {\n    fn main_physical(&self) -> Result<PHYSICAL_MONITOR> {\n        let physical_monitors = WindowsApi::get_physical_monitors(self.handle())?;\n        let main_physical_monitor = physical_monitors.first().ok_or(\"no physical monitor\")?;\n        Ok(*main_physical_monitor)\n    }\n\n    // Display Data Channel/Command Interface\n    pub fn supports_ddcci(&self) -> Result<bool> {\n        let physical_monitor = self.main_physical()?;\n        let ddcci_is_supported = unsafe {\n            let mut pdwmonitorcapabilities: u32 = 0;\n            let mut pdwsupportedcolortemperatures: u32 = 0;\n            // This function fails if the monitor does not support DDC/CI.\n            BOOL(GetMonitorCapabilities(\n                physical_monitor.hPhysicalMonitor,\n                &mut pdwmonitorcapabilities,\n                &mut pdwsupportedcolortemperatures,\n            ))\n            .as_bool()\n        };\n        Ok(ddcci_is_supported)\n    }\n\n    pub fn ddcci_get_monitor_brightness(&self) -> Result<DdcciBrightnessValues> {\n        let physical_monitor = self.main_physical()?;\n        let mut values = DdcciBrightnessValues::default();\n        unsafe {\n            BOOL(GetMonitorBrightness(\n                physical_monitor.hPhysicalMonitor,\n                &mut values.min,\n                &mut values.current,\n                &mut values.max,\n            ))\n            .ok()?;\n        }\n        Ok(values)\n    }\n\n    pub fn ddcci_set_monitor_brightness(&self, value: u32) -> Result<()> {\n        let physical_monitor = self.main_physical()?;\n        unsafe {\n            BOOL(SetMonitorBrightness(\n                physical_monitor.hPhysicalMonitor,\n                value,\n            ))\n            .ok()?\n        };\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/monitor/mod.rs",
    "content": "mod brightness;\n\nuse windows::{\n    Devices::Display::Core::{\n        DisplayManager, DisplayManagerOptions, DisplayTarget as WinRTDisplayTarget,\n        DisplayView as WinRTDisplayView,\n    },\n    Win32::{\n        Devices::Display::{\n            DisplayConfigGetDeviceInfo, GetDisplayConfigBufferSizes, QueryDisplayConfig,\n            DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME, DISPLAYCONFIG_DEVICE_INFO_HEADER,\n            DISPLAYCONFIG_MODE_INFO, DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, DISPLAYCONFIG_PATH_INFO,\n            DISPLAYCONFIG_TARGET_DEVICE_NAME, QDC_ONLY_ACTIVE_PATHS,\n        },\n        Graphics::Gdi::{HMONITOR, MONITORINFOEXW},\n    },\n};\n\nuse crate::{error::Result, windows_api::string_utils::WindowsString};\nuse seelen_core::{rect::Rect, system_state::MonitorId};\n\nuse super::WindowsApi;\n\n/// This struct represents a screen, a screen could be shown in multiple display devices.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct Monitor(HMONITOR);\n\nunsafe impl Send for Monitor {}\nunsafe impl Sync for Monitor {}\n\nimpl From<HMONITOR> for Monitor {\n    fn from(hmonitor: HMONITOR) -> Self {\n        Self(hmonitor)\n    }\n}\n\nimpl From<isize> for Monitor {\n    fn from(hmonitor: isize) -> Self {\n        Self(HMONITOR(hmonitor as _))\n    }\n}\n\nimpl From<&seelen_core::Point> for Monitor {\n    fn from(point: &seelen_core::Point) -> Self {\n        let hmonitor = WindowsApi::monitor_from_point(point);\n        Self(hmonitor)\n    }\n}\n\n// HMONITOR on win32 is the same concept as DisplayView in winrt\n\nimpl Monitor {\n    pub fn handle(&self) -> HMONITOR {\n        self.0\n    }\n\n    pub fn primary() -> Monitor {\n        Monitor(WindowsApi::primary_monitor())\n    }\n\n    pub fn is_primary(&self) -> bool {\n        self.0 == WindowsApi::primary_monitor()\n    }\n\n    pub fn at_point(point: &seelen_core::Point) -> Monitor {\n        Monitor(WindowsApi::monitor_from_point(point))\n    }\n\n    pub fn info(&self) -> Result<MONITORINFOEXW> {\n        WindowsApi::monitor_info(self.0)\n    }\n\n    /// Returns (MonitorId, friendly name) for this HMONITOR.\n    ///\n    /// Strategy:\n    /// 1. Obtain the monitor's virtual-desktop position from `GetMonitorInfo`.\n    /// 2. Find all `QueryDisplayConfig` paths whose source mode sits at that position.\n    /// 3. For each candidate path try `winrt_stable_id_for_target`:\n    ///    - WinRT `DisplayManager.GetCurrentTargets()` only surfaces physical display\n    ///      targets. Virtual/render-only paths (e.g. the NVIDIA dGPU Optimus path) are\n    ///      absent from that list, so they naturally produce no match and we continue\n    ///      to the next candidate.\n    ///    - The first path whose `targetInfo` matches a WinRT `DisplayTarget` gives us\n    ///      the authoritative `StableMonitorId` and the friendly name.\n    pub fn get_stable_info(&self) -> Result<(MonitorId, String)> {\n        let info = WindowsApi::monitor_info(self.0)?;\n        let rect = info.monitorInfo.rcMonitor;\n\n        let display_config = DisplayConfigAndModes::query_active()?;\n        for path in &display_config.paths {\n            unsafe {\n                // Only consider paths that have a source mode (desktop surface).\n                let mode_idx = path.sourceInfo.Anonymous.modeInfoIdx as usize;\n                if mode_idx == 0xFFFF_FFFF {\n                    continue;\n                }\n                let Some(mode) = display_config.modes.get(mode_idx) else {\n                    continue;\n                };\n                if mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE {\n                    continue;\n                }\n                // Match by top-left corner of the monitor on the virtual desktop.\n                let pos = mode.Anonymous.sourceMode.position;\n                if pos.x != rect.left || pos.y != rect.top {\n                    continue;\n                }\n\n                // Query the DisplayConfig target device name for the friendly name.\n                let mut target_name = DISPLAYCONFIG_TARGET_DEVICE_NAME {\n                    header: DISPLAYCONFIG_DEVICE_INFO_HEADER {\n                        r#type: DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,\n                        size: std::mem::size_of::<DISPLAYCONFIG_TARGET_DEVICE_NAME>() as u32,\n                        adapterId: path.targetInfo.adapterId,\n                        id: path.targetInfo.id,\n                    },\n                    ..Default::default()\n                };\n                let _ = DisplayConfigGetDeviceInfo(&mut target_name.header);\n                let friendly_name =\n                    WindowsString::from_slice(&target_name.monitorFriendlyDeviceName).to_string();\n\n                // Try WinRT lookup — virtual paths won't match and we'll skip them.\n                if let Ok(result) = winrt_stable_id_for_target(\n                    path.targetInfo.adapterId.LowPart,\n                    path.targetInfo.adapterId.HighPart,\n                    path.targetInfo.id,\n                    friendly_name,\n                ) {\n                    return Ok(result);\n                }\n            }\n        }\n        Err(\"No WinRT DisplayTarget found for HMONITOR\".into())\n    }\n\n    pub fn stable_id(&self) -> Result<MonitorId> {\n        Ok(self.get_stable_info()?.0)\n    }\n\n    pub fn friendly_name(&self) -> Result<String> {\n        Ok(self.get_stable_info()?.1)\n    }\n\n    pub fn rect(&self) -> Result<Rect> {\n        let rect = WindowsApi::monitor_info(self.0)?.monitorInfo.rcMonitor;\n        Ok(Rect {\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n        })\n    }\n\n    pub fn scale_factor(&self) -> Result<f64> {\n        let monitor_scale_factor = WindowsApi::get_monitor_scale_factor(self.0)?;\n        let text_scale_factor = WindowsApi::get_text_scale_factor()?;\n        Ok(monitor_scale_factor * text_scale_factor)\n    }\n}\n\n// =================================================================================================\n// =========================================== RT ==================================================\n// =================================================================================================\n\n/// represents a display screen view (one view can be shown in multiple displays 'mirrors')\n///\n/// # Safety\n/// this is unsafe of store for long sessions as this object could be invalid on display changes\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct DisplayView(WinRTDisplayView);\n\nimpl DisplayView {\n    pub fn as_win32_view(&self) -> Result<Monitor> {\n        let display_config = DisplayConfigAndModes::query_active()?;\n\n        for display_path in self.0.Paths()? {\n            let target = display_path.Target()?;\n            let adapter_id = target.Adapter()?.Id()?;\n            let target_id = target.AdapterRelativeId()?;\n\n            // Match directly by adapter LUID + target ID — no DeviceInterfacePath\n            // string lookup or DisplayConfigGetDeviceInfo calls needed.\n            for path in &display_config.paths {\n                unsafe {\n                    if path.targetInfo.adapterId.LowPart != adapter_id.LowPart\n                        || path.targetInfo.adapterId.HighPart != adapter_id.HighPart\n                        || path.targetInfo.id != target_id\n                    {\n                        continue;\n                    }\n                    let mode_idx = path.sourceInfo.Anonymous.modeInfoIdx as usize;\n                    if mode_idx == 0xFFFF_FFFF {\n                        continue;\n                    }\n                    let Some(mode) = display_config.modes.get(mode_idx) else {\n                        continue;\n                    };\n                    if mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE {\n                        continue;\n                    }\n\n                    let pos = mode.Anonymous.sourceMode.position;\n                    return Ok(Monitor::at_point(&seelen_core::Point {\n                        x: pos.x,\n                        y: pos.y,\n                    }));\n                }\n            }\n        }\n\n        Err(\"Win32 Monitor not found for winrt view\".into())\n    }\n\n    pub fn primary_target(&self) -> Result<MonitorTarget> {\n        Ok(self.0.Paths()?.GetAt(0)?.Target()?.into())\n    }\n}\n\nimpl From<WinRTDisplayView> for DisplayView {\n    fn from(view: WinRTDisplayView) -> Self {\n        Self(view)\n    }\n}\n\n/// represents a physical screen/monitor\npub struct MonitorTarget(WinRTDisplayTarget);\n\nimpl MonitorTarget {\n    /// Returns the WinRT StableMonitorId — the canonical, authoritative stable ID.\n    pub fn stable_id(&self) -> Result<MonitorId> {\n        Ok(self.0.StableMonitorId()?.to_string().into())\n    }\n}\n\nimpl From<WinRTDisplayTarget> for MonitorTarget {\n    fn from(target: WinRTDisplayTarget) -> Self {\n        Self(target)\n    }\n}\n\n// =================================================================================================\n// ============================= DisplayConfig helpers (Win32-only) ================================\n// =================================================================================================\n\n/// Bridge struct holding the Win32 `QueryDisplayConfig` output.\n///\n/// Used to correlate Win32 (`HMONITOR`) and WinRT (`DisplayTarget`) display objects\n/// by matching adapter LUID + target ID against paths and source mode positions.\nstruct DisplayConfigAndModes {\n    paths: Vec<DISPLAYCONFIG_PATH_INFO>,\n    modes: Vec<DISPLAYCONFIG_MODE_INFO>,\n}\n\nimpl DisplayConfigAndModes {\n    fn query_active() -> Result<Self> {\n        unsafe {\n            let mut num_paths: u32 = 0;\n            let mut num_modes: u32 = 0;\n            GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut num_paths, &mut num_modes)\n                .ok()?;\n\n            let mut paths = vec![DISPLAYCONFIG_PATH_INFO::default(); num_paths as usize];\n            let mut modes = vec![DISPLAYCONFIG_MODE_INFO::default(); num_modes as usize];\n            QueryDisplayConfig(\n                QDC_ONLY_ACTIVE_PATHS,\n                &mut num_paths,\n                paths.as_mut_ptr(),\n                &mut num_modes,\n                modes.as_mut_ptr(),\n                None,\n            )\n            .ok()?;\n            paths.truncate(num_paths as usize);\n            modes.truncate(num_modes as usize);\n            Ok(Self { paths, modes })\n        }\n    }\n}\n\n/// Looks up the WinRT `DisplayTarget` matching the given adapter LUID and target ID,\n/// and returns (StableMonitorId, friendly_name).\n///\n/// `DisplayManager.GetCurrentTargets()` only surfaces **physical** display targets.\n/// Virtual or render-only paths (e.g. the NVIDIA dGPU Optimus indirect-display path)\n/// are absent from the WinRT target list, so they naturally produce no match.\n/// This means the caller does not need extra filtering — iterating over all QDC paths\n/// at the monitor's position and calling this function for each one will succeed only\n/// on the path that corresponds to the real scan-out adapter.\nfn winrt_stable_id_for_target(\n    luid_low: u32,\n    luid_high: i32,\n    target_id: u32,\n    friendly_name: String,\n) -> Result<(MonitorId, String)> {\n    let dm = DisplayManager::Create(DisplayManagerOptions::None)?;\n    for target in dm.GetCurrentTargets()? {\n        let adapter_id = target.Adapter()?.Id()?;\n        if adapter_id.LowPart != luid_low || adapter_id.HighPart != luid_high {\n            continue;\n        }\n        if target.AdapterRelativeId()? != target_id {\n            continue;\n        }\n        let stable_id: MonitorId = target.StableMonitorId()?.to_string().into();\n        return Ok((stable_id, friendly_name));\n    }\n    Err(\"No WinRT DisplayTarget found for adapter/target\".into())\n}\n"
  },
  {
    "path": "src/background/windows_api/process.rs",
    "content": "use std::path::PathBuf;\n\nuse seelen_core::system_state::ProcessInformation;\nuse windows::{\n    ApplicationModel::AppInfo,\n    Win32::{\n        Foundation::HANDLE,\n        Storage::Packaging::Appx::GetApplicationUserModelId,\n        System::Threading::{PROCESS_QUERY_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION},\n    },\n};\nuse windows_core::Owned;\n\nuse crate::error::Result;\n\nuse super::{string_utils::WindowsString, types::AppUserModelId, window::Window, WindowsApi};\n\n// https://stackoverflow.com/questions/47300622/meaning-of-flags-in-process-extended-basic-information-struct\n#[allow(dead_code)]\npub enum ProcessInformationFlag {\n    IsProtectedProcess = 0x1,\n    IsWow64Process = 0x2,\n    IsProcessInJob = 0x4,\n    IsCrossSessionCreate = 0x8,\n    IsFrozen = 0x10,\n    IsBackground = 0x20,\n    IsStronglyNamed = 0x40,\n    IsSecureProcess = 0x80,\n    IsSubsystemProcess = 0x100,\n}\n\npub struct Process(u32);\n\nimpl Process {\n    pub fn from_id(id: u32) -> Self {\n        Self(id)\n    }\n\n    pub fn from_window(window: &Window) -> Self {\n        let (process_id, _) = WindowsApi::window_thread_process_id(window.hwnd());\n        Self(process_id)\n    }\n\n    pub fn id(&self) -> u32 {\n        self.0\n    }\n\n    pub fn open_handle(&self) -> Result<Owned<HANDLE>> {\n        WindowsApi::open_process(PROCESS_QUERY_INFORMATION, false, self.0)\n    }\n\n    /// will fail if the process is owned by another user\n    pub fn open_limited_handle(&self) -> Result<Owned<HANDLE>> {\n        WindowsApi::open_process(PROCESS_QUERY_LIMITED_INFORMATION, false, self.0)\n    }\n\n    pub fn is_frozen(&self) -> Result<bool> {\n        WindowsApi::is_process_frozen(self.0)\n    }\n\n    /// package app user model id, (appx, eg: \"Microsoft.WindowsTerminal_8wekyb3d8bbwe!TerminalApp\")\n    pub fn package_app_user_model_id(&self) -> Result<AppUserModelId> {\n        let hprocess = self.open_limited_handle()?;\n        let mut len = 1024_u32;\n        let mut id = WindowsString::new_to_fill(len as usize);\n        unsafe { GetApplicationUserModelId(*hprocess, &mut len, Some(id.as_pwstr())).ok()? };\n        Ok(AppUserModelId::Appx(id.to_string()))\n    }\n\n    #[allow(dead_code)]\n    pub fn package_app_info(&self) -> Result<AppInfo> {\n        let app_info = AppInfo::GetFromAppUserModelId(&self.package_app_user_model_id()?.into())?;\n        Ok(app_info)\n    }\n\n    pub fn program_path(&self) -> Result<PathBuf> {\n        let path_string = WindowsApi::exe_path_by_process(self.0)?;\n        if path_string.is_empty() {\n            return Err(\"exe path is empty\".into());\n        }\n        Ok(PathBuf::from(path_string))\n    }\n\n    /// program path filename\n    pub fn program_exe_name(&self) -> Result<String> {\n        Ok(self\n            .program_path()?\n            .file_name()\n            .ok_or(\"there is no file name\")?\n            .to_string_lossy()\n            .to_string())\n    }\n\n    pub fn program_display_name(&self) -> Result<String> {\n        let path = self.program_path()?;\n        match WindowsApi::get_executable_display_name(&path) {\n            Ok(name) => Ok(name.trim_end_matches(\".exe\").to_owned()),\n            Err(_) => Ok(path\n                .file_stem()\n                .ok_or(\"there is no file stem\")?\n                .to_string_lossy()\n                .to_string()),\n        }\n    }\n\n    pub fn to_serializable(&self) -> ProcessInformation {\n        ProcessInformation {\n            id: self.0,\n            path: self.program_path().ok(),\n        }\n    }\n\n    pub fn is_seelen(&self) -> bool {\n        if let Ok(exe) = self.program_path() {\n            return exe.ends_with(\"seelen-ui.exe\");\n        }\n        false\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/string_utils.rs",
    "content": "use std::{\n    ffi::{OsStr, OsString},\n    os::windows::ffi::{OsStrExt, OsStringExt},\n};\n\nuse windows::core::{HSTRING, PCWSTR, PWSTR};\n\n#[derive(Debug, Clone)]\npub struct WindowsString {\n    pub inner: Vec<u16>,\n}\n\n#[allow(dead_code)]\nimpl WindowsString {\n    pub fn new_to_fill(capacity: usize) -> Self {\n        Self {\n            inner: vec![0; capacity],\n        }\n    }\n\n    pub fn from_str(s: &str) -> Self {\n        Self {\n            inner: s.encode_utf16().chain(Some(0)).collect(),\n        }\n    }\n\n    pub fn from_slice(s: &[u16]) -> Self {\n        Self {\n            inner: s.iter().copied().chain(Some(0)).collect(),\n        }\n    }\n\n    pub fn from_os_string(s: &OsStr) -> Self {\n        Self {\n            inner: s.encode_wide().chain(Some(0)).collect(),\n        }\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner\n            .iter()\n            .position(|c| c == &0)\n            .expect(\"Invalid UTF16 Windows String\")\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    pub fn as_slice(&self) -> &[u16] {\n        &self.inner\n    }\n\n    pub fn as_mut_slice(&mut self) -> &mut [u16] {\n        &mut self.inner\n    }\n\n    pub fn as_pcwstr(&self) -> PCWSTR {\n        PCWSTR(self.inner.as_ptr())\n    }\n\n    pub fn as_pwstr(&mut self) -> PWSTR {\n        PWSTR(self.inner.as_mut_ptr())\n    }\n\n    pub fn to_hstring(&self) -> HSTRING {\n        HSTRING::from_wide(&self.inner[..self.len()])\n    }\n\n    pub fn to_os_string(&self) -> std::ffi::OsString {\n        std::ffi::OsString::from_wide(&self.inner[..self.len()])\n    }\n}\n\nimpl std::fmt::Display for WindowsString {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            String::from_utf16_lossy(&self.inner[..self.len()]).trim_end_matches(\"\\0\")\n        )\n    }\n}\n\nimpl From<&str> for WindowsString {\n    fn from(value: &str) -> Self {\n        Self::from_str(value)\n    }\n}\n\nimpl From<String> for WindowsString {\n    fn from(value: String) -> Self {\n        Self::from_str(&value)\n    }\n}\n\nimpl From<&OsStr> for WindowsString {\n    fn from(value: &OsStr) -> Self {\n        Self::from_os_string(value)\n    }\n}\n\nimpl From<OsString> for WindowsString {\n    fn from(value: OsString) -> Self {\n        Self::from_os_string(&value)\n    }\n}\n\nimpl From<&std::path::Path> for WindowsString {\n    fn from(value: &std::path::Path) -> Self {\n        Self::from_os_string(value.as_os_str())\n    }\n}\n\nimpl From<std::path::PathBuf> for WindowsString {\n    fn from(value: std::path::PathBuf) -> Self {\n        Self::from_os_string(value.as_os_str())\n    }\n}\n\nimpl From<&[u16]> for WindowsString {\n    fn from(value: &[u16]) -> Self {\n        Self::from_slice(value)\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/types.rs",
    "content": "use serde::{Deserialize, Serialize};\n\nuse super::WindowsApi;\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\n#[serde(tag = \"kind\", content = \"value\")]\npub enum AppUserModelId {\n    /// aumid added to the app start menu shortcut (eg: \"com.squirrel.Discord.Discord\")\n    PropertyStore(String),\n    /// Appx/Msix aumid (eg: \"Microsoft.WindowsTerminal_8wekyb3d8bbwe!TerminalApp\")\n    Appx(String),\n}\n\nimpl AppUserModelId {\n    pub fn is_appx(&self) -> bool {\n        matches!(self, AppUserModelId::Appx(_))\n    }\n\n    pub fn is_property_store(&self) -> bool {\n        matches!(self, AppUserModelId::PropertyStore(_))\n    }\n}\n\nimpl std::fmt::Display for AppUserModelId {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n\nimpl std::ops::Deref for AppUserModelId {\n    type Target = String;\n    fn deref(&self) -> &Self::Target {\n        match self {\n            AppUserModelId::PropertyStore(id) => id,\n            AppUserModelId::Appx(id) => id,\n        }\n    }\n}\n\nimpl From<AppUserModelId> for windows_core::HSTRING {\n    fn from(val: AppUserModelId) -> Self {\n        val.to_string().into()\n    }\n}\n\nimpl From<String> for AppUserModelId {\n    fn from(value: String) -> Self {\n        if WindowsApi::is_uwp_package_id(&value) {\n            AppUserModelId::Appx(value)\n        } else {\n            AppUserModelId::PropertyStore(value)\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/undocumented/audio_policy_config.rs",
    "content": "use std::{ffi::c_void, mem::zeroed};\n\nuse windows::{\n    core::{Interface, Param, Result, GUID, HRESULT, PCWSTR},\n    Devices::Custom::DeviceSharingMode,\n    Win32::{\n        Foundation::PROPERTYKEY,\n        Media::Audio::{ERole, WAVEFORMATEX},\n        System::Com::StructuredStorage::PROPVARIANT,\n    },\n};\nuse windows_core::BOOL;\n\n#[allow(non_upper_case_globals)]\npub const PolicyConfig: GUID = GUID::from_u128(0x870af99c_171d_4f9e_af0d_e63df40c2bc9);\n\nwindows_core::imp::define_interface!(\n    IPolicyConfig,\n    IPolicyConfig_Vtbl,\n    0xf8679f50_850a_41cf_9c72_430f290290c8\n);\n\nwindows_core::imp::interface_hierarchy!(IPolicyConfig, windows_core::IUnknown);\n#[allow(non_snake_case)]\nimpl IPolicyConfig {\n    pub unsafe fn GetMixFormat(&self, device_id: impl Param<PCWSTR>) -> Result<*mut WAVEFORMATEX> {\n        let mut result__ = zeroed::<*mut WAVEFORMATEX>();\n        (Interface::vtable(self).GetMixFormat)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            &mut result__,\n        )\n        .and_then(|| windows_core::Type::from_abi(result__))\n    }\n\n    pub unsafe fn GetDeviceFormat(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        default: impl Into<BOOL>,\n    ) -> Result<*mut WAVEFORMATEX> {\n        let mut result__ = zeroed::<*mut WAVEFORMATEX>();\n        (Interface::vtable(self).GetDeviceFormat)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            default.into().0,\n            &mut result__,\n        )\n        .and_then(|| windows_core::Type::from_abi(result__))\n    }\n\n    pub unsafe fn ResetDeviceFormat(&self, device_id: impl Param<PCWSTR>) -> Result<()> {\n        (Interface::vtable(self).ResetDeviceFormat)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n        )\n        .ok()\n    }\n\n    pub unsafe fn SetDeviceFormat(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        mut endpoint_format: WAVEFORMATEX,\n        mut mix_format: WAVEFORMATEX,\n    ) -> Result<()> {\n        (Interface::vtable(self).SetDeviceFormat)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            &mut endpoint_format,\n            &mut mix_format,\n        )\n        .ok()\n    }\n\n    pub unsafe fn GetProcessingPeriod(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        default: impl Into<BOOL>,\n        default_period: *mut i64,\n        min_period: *mut i64,\n    ) -> Result<()> {\n        (Interface::vtable(self).GetProcessingPeriod)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            default.into().0,\n            default_period,\n            min_period,\n        )\n        .ok()\n    }\n\n    pub unsafe fn SetProcessingPeriod(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        period: *mut i64,\n    ) -> Result<()> {\n        (Interface::vtable(self).SetProcessingPeriod)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            period,\n        )\n        .ok()\n    }\n\n    pub unsafe fn GetShareMode(&self, device_id: impl Param<PCWSTR>) -> Result<DeviceSharingMode> {\n        let mut result__ = zeroed::<DeviceSharingMode>();\n        (Interface::vtable(self).GetShareMode)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            &mut result__,\n        )\n        .and_then(|| windows_core::Type::from_abi(result__))\n    }\n\n    pub unsafe fn SetShareMode(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        mut mode: DeviceSharingMode,\n    ) -> Result<()> {\n        (Interface::vtable(self).SetShareMode)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            &mut mode,\n        )\n        .ok()\n    }\n\n    pub unsafe fn GetPropertyValue(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        bFxStore: impl Into<BOOL>,\n        key: *const PROPERTYKEY,\n    ) -> Result<PROPVARIANT> {\n        let mut result__ = zeroed();\n        (Interface::vtable(self).GetPropertyValue)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            bFxStore.into().0,\n            key,\n            &mut result__,\n        )\n        .map(|| result__)\n    }\n\n    pub unsafe fn SetPropertyValue(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        bFxStore: impl Into<BOOL>,\n        key: *const PROPERTYKEY,\n        propvar: *const PROPVARIANT,\n    ) -> Result<()> {\n        (Interface::vtable(self).SetPropertyValue)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            bFxStore.into().0,\n            key,\n            propvar,\n        )\n        .ok()\n    }\n\n    pub unsafe fn SetDefaultEndpoint(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        role: ERole,\n    ) -> Result<()> {\n        (Interface::vtable(self).SetDefaultEndpoint)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            role,\n        )\n        .ok()\n    }\n\n    pub unsafe fn SetEndpointVisibility(\n        &self,\n        device_id: impl Param<PCWSTR>,\n        visible: impl Into<BOOL>,\n    ) -> Result<()> {\n        (Interface::vtable(self).SetEndpointVisibility)(\n            Interface::as_raw(self),\n            device_id.param().abi(),\n            visible.into().0,\n        )\n        .ok()\n    }\n}\n\n#[repr(C)]\n#[doc(hidden)]\n#[allow(non_snake_case, non_camel_case_types)]\npub struct IPolicyConfig_Vtbl {\n    pub base__: ::windows::core::IUnknown_Vtbl,\n    pub GetMixFormat:\n        unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, *mut *mut WAVEFORMATEX) -> HRESULT,\n    pub GetDeviceFormat: unsafe extern \"system\" fn(\n        this: *mut c_void,\n        PCWSTR,\n        i32,\n        *mut *mut WAVEFORMATEX,\n    ) -> HRESULT,\n    pub ResetDeviceFormat: unsafe extern \"system\" fn(this: *mut c_void, PCWSTR) -> HRESULT,\n    pub SetDeviceFormat: unsafe extern \"system\" fn(\n        this: *mut c_void,\n        PCWSTR,\n        *mut WAVEFORMATEX,\n        *mut WAVEFORMATEX,\n    ) -> HRESULT,\n    pub GetProcessingPeriod:\n        unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, i32, *mut i64, *mut i64) -> HRESULT,\n    pub SetProcessingPeriod:\n        unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, *mut i64) -> HRESULT,\n    pub GetShareMode:\n        unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, *mut DeviceSharingMode) -> HRESULT,\n    pub SetShareMode:\n        unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, *mut DeviceSharingMode) -> HRESULT,\n    pub GetPropertyValue: unsafe extern \"system\" fn(\n        this: *mut c_void,\n        PCWSTR,\n        i32,\n        *const PROPERTYKEY,\n        *mut PROPVARIANT,\n    ) -> HRESULT,\n    pub SetPropertyValue: unsafe extern \"system\" fn(\n        this: *mut c_void,\n        PCWSTR,\n        i32,\n        *const PROPERTYKEY,\n        *const PROPVARIANT,\n    ) -> HRESULT,\n    pub SetDefaultEndpoint: unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, ERole) -> HRESULT,\n    pub SetEndpointVisibility: unsafe extern \"system\" fn(this: *mut c_void, PCWSTR, i32) -> HRESULT,\n}\n"
  },
  {
    "path": "src/background/windows_api/undocumented/mod.rs",
    "content": "mod audio_policy_config;\n\npub use audio_policy_config::*;\nuse windows::Win32::{\n    Media::Audio::{eCommunications, eConsole, eMultimedia},\n    Security::SE_DEBUG_NAME,\n};\n\nuse crate::{error::Result, windows_api::string_utils::WindowsString};\n\nuse super::{Com, WindowsApi};\n\nimpl WindowsApi {\n    /// this function will only work on win32 platform, if using UWP/MSIX\n    /// it will be blocked or not translated idk what exactly happens but\n    /// as workaround we call this function via cli to set the default device\n    pub fn set_default_audio_device(id: &str, role: &str) -> Result<()> {\n        let role = match role {\n            \"multimedia\" => eMultimedia,\n            \"communications\" => eCommunications,\n            \"console\" => eConsole,\n            _ => return Err(\"invalid role\".into()),\n        };\n\n        Com::run_with_context(|| unsafe {\n            WindowsApi::enable_privilege(SE_DEBUG_NAME)?;\n            let policy: IPolicyConfig = Com::create_instance(&PolicyConfig)?;\n            let id = WindowsString::from_str(id);\n            policy.SetDefaultEndpoint(id.as_pcwstr(), role)?;\n            Ok(())\n        })\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/window/cache.rs",
    "content": "//! Windows have a lot of api to get information about the window.\n//! Also provides events for theses to be listened, but some events like fullscreen, maximize, etc.\n//! are not standard windows events, so we handle cached windows data to check for these changes, and emit\n//! synthetic events for them.\n\nuse seelen_core::system_state::{FocusedApp, Relaunch, RelaunchArguments, UserAppWindow};\n\nuse crate::{utils::get_parts_of_inline_command, windows_api::types::AppUserModelId};\n\nuse super::Window;\n\nimpl Window {\n    pub fn to_serializable(self: &Window) -> UserAppWindow {\n        let umid = self.app_user_model_id();\n        let mut prevent_pinning = false;\n\n        let relaunch = match umid {\n            Some(AppUserModelId::PropertyStore(_)) => {\n                if let Some(cmd) = self.relaunch_command() {\n                    let (command, args) = get_parts_of_inline_command(&cmd);\n                    let args = args.map(RelaunchArguments::String);\n\n                    let icon = self.relaunch_icon();\n                    prevent_pinning = self.prevent_pinning();\n\n                    Some(Relaunch {\n                        command,\n                        args,\n                        working_dir: None,\n                        icon,\n                    })\n                } else {\n                    None\n                }\n            }\n            _ => None,\n        };\n\n        UserAppWindow {\n            hwnd: self.address(),\n            monitor: self.monitor().stable_id().unwrap_or_default(),\n            title: self.title(),\n            app_name: self.app_display_name().unwrap_or_default(),\n            is_iconic: self.is_minimized(),\n            is_zoomed: self.is_maximized(),\n            is_fullscreen: self.is_fullscreen(),\n            umid: umid.map(|umid| umid.to_string()),\n            process: self.process().to_serializable(),\n            prevent_pinning,\n            relaunch,\n        }\n    }\n\n    pub fn as_focused_app_information(&self) -> FocusedApp {\n        let process = self.process();\n\n        FocusedApp {\n            hwnd: self.address(),\n            monitor: self.monitor().stable_id().unwrap_or_default(),\n            title: self.title(),\n            class: self.class(),\n            name: self\n                .app_display_name()\n                .unwrap_or(String::from(\"Error on App Name\")),\n            exe: process.program_path().ok(),\n            umid: self.app_user_model_id().map(|umid| umid.to_string()),\n            is_maximized: self.is_maximized(),\n            is_fullscreened: self.is_fullscreen(),\n            is_seelen_overlay: self.is_seelen_overlay(),\n            rect: self.inner_rect().ok(),\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/window/event.rs",
    "content": "use std::sync::LazyLock;\n\nuse crate::hook::HookManager;\nuse crate::windows_api::window::Window;\nuse windows::Win32::UI::WindowsAndMessaging::*;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(u32)]\n#[allow(dead_code)]\npub enum WinEvent {\n    AiaEnd = EVENT_AIA_END,\n    AiaStart = EVENT_AIA_START,\n    ConsoleCaret = EVENT_CONSOLE_CARET,\n    ConsoleEnd = EVENT_CONSOLE_END,\n    ConsoleEndApplication = EVENT_CONSOLE_END_APPLICATION,\n    ConsoleLayout = EVENT_CONSOLE_LAYOUT,\n    ConsoleStartApplication = EVENT_CONSOLE_START_APPLICATION,\n    ConsoleUpdateRegion = EVENT_CONSOLE_UPDATE_REGION,\n    ConsoleUpdateScroll = EVENT_CONSOLE_UPDATE_SCROLL,\n    ConsoleUpdateSimple = EVENT_CONSOLE_UPDATE_SIMPLE,\n    ObjectAcceleratorChange = EVENT_OBJECT_ACCELERATORCHANGE,\n    ObjectCloaked = EVENT_OBJECT_CLOAKED,\n    ObjectContentScrolled = EVENT_OBJECT_CONTENTSCROLLED,\n    ObjectCreate = EVENT_OBJECT_CREATE,\n    ObjectDefActionChange = EVENT_OBJECT_DEFACTIONCHANGE,\n    ObjectDescriptionChange = EVENT_OBJECT_DESCRIPTIONCHANGE,\n    ObjectDestroy = EVENT_OBJECT_DESTROY,\n    ObjectDragCancel = EVENT_OBJECT_DRAGCANCEL,\n    ObjectDragComplete = EVENT_OBJECT_DRAGCOMPLETE,\n    ObjectDragDropped = EVENT_OBJECT_DRAGDROPPED,\n    ObjectDragEnter = EVENT_OBJECT_DRAGENTER,\n    ObjectDragLeave = EVENT_OBJECT_DRAGLEAVE,\n    ObjectDragStart = EVENT_OBJECT_DRAGSTART,\n    ObjectEnd = EVENT_OBJECT_END,\n    ObjectFocus = EVENT_OBJECT_FOCUS,\n    ObjectHelpChange = EVENT_OBJECT_HELPCHANGE,\n    ObjectHide = EVENT_OBJECT_HIDE,\n    ObjectHostedObjectsInvalidated = EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED,\n    ObjectImeChange = EVENT_OBJECT_IME_CHANGE,\n    ObjectImeHide = EVENT_OBJECT_IME_HIDE,\n    ObjectImeShow = EVENT_OBJECT_IME_SHOW,\n    ObjectInvoked = EVENT_OBJECT_INVOKED,\n    ObjectLiveRegionChanged = EVENT_OBJECT_LIVEREGIONCHANGED,\n    ObjectLocationChange = EVENT_OBJECT_LOCATIONCHANGE,\n    ObjectNameChange = EVENT_OBJECT_NAMECHANGE,\n    ObjectParentChange = EVENT_OBJECT_PARENTCHANGE,\n    ObjectReorder = EVENT_OBJECT_REORDER,\n    ObjectSelection = EVENT_OBJECT_SELECTION,\n    ObjectSelectionAdd = EVENT_OBJECT_SELECTIONADD,\n    ObjectSelectionRemove = EVENT_OBJECT_SELECTIONREMOVE,\n    ObjectSelectionWithin = EVENT_OBJECT_SELECTIONWITHIN,\n    ObjectShow = EVENT_OBJECT_SHOW,\n    ObjectStateChange = EVENT_OBJECT_STATECHANGE,\n    ObjectTextEditConversionTargetChanged = EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED,\n    ObjectTextSelectionChanged = EVENT_OBJECT_TEXTSELECTIONCHANGED,\n    ObjectUncloaked = EVENT_OBJECT_UNCLOAKED,\n    ObjectValueChange = EVENT_OBJECT_VALUECHANGE,\n    OemDefinedEnd = EVENT_OEM_DEFINED_END,\n    OemDefinedStart = EVENT_OEM_DEFINED_START,\n    SystemAlert = EVENT_SYSTEM_ALERT,\n    SystemArrangementPreview = EVENT_SYSTEM_ARRANGMENTPREVIEW,\n    SystemCaptureEnd = EVENT_SYSTEM_CAPTUREEND,\n    SystemCaptureStart = EVENT_SYSTEM_CAPTURESTART,\n    SystemContextHelpEnd = EVENT_SYSTEM_CONTEXTHELPEND,\n    SystemContextHelpStart = EVENT_SYSTEM_CONTEXTHELPSTART,\n    SystemDesktopSwitch = EVENT_SYSTEM_DESKTOPSWITCH,\n    SystemDialogEnd = EVENT_SYSTEM_DIALOGEND,\n    SystemDialogStart = EVENT_SYSTEM_DIALOGSTART,\n    SystemDragDropEnd = EVENT_SYSTEM_DRAGDROPEND,\n    SystemDragDropStart = EVENT_SYSTEM_DRAGDROPSTART,\n    SystemEnd = EVENT_SYSTEM_END,\n    SystemForeground = EVENT_SYSTEM_FOREGROUND,\n    SystemImeKeyNotification = EVENT_SYSTEM_IME_KEY_NOTIFICATION,\n    SystemMenuEnd = EVENT_SYSTEM_MENUEND,\n    SystemMenuPopupEnd = EVENT_SYSTEM_MENUPOPUPEND,\n    SystemMenuPopupStart = EVENT_SYSTEM_MENUPOPUPSTART,\n    SystemMenuStart = EVENT_SYSTEM_MENUSTART,\n    SystemMinimizeEnd = EVENT_SYSTEM_MINIMIZEEND,\n    SystemMinimizeStart = EVENT_SYSTEM_MINIMIZESTART,\n    SystemMoveSizeEnd = EVENT_SYSTEM_MOVESIZEEND,\n    SystemMoveSizeStart = EVENT_SYSTEM_MOVESIZESTART,\n    SystemScrollingEnd = EVENT_SYSTEM_SCROLLINGEND,\n    SystemScrollingStart = EVENT_SYSTEM_SCROLLINGSTART,\n    SystemSound = EVENT_SYSTEM_SOUND,\n    SystemSwitchEnd = EVENT_SYSTEM_SWITCHEND,\n    SystemSwitchStart = EVENT_SYSTEM_SWITCHSTART,\n    SystemSwitcherAppDropped = EVENT_SYSTEM_SWITCHER_APPDROPPED,\n    SystemSwitcherAppGrabbed = EVENT_SYSTEM_SWITCHER_APPGRABBED,\n    SystemSwitcherAppOverTarget = EVENT_SYSTEM_SWITCHER_APPOVERTARGET,\n    SystemSwitcherCancelled = EVENT_SYSTEM_SWITCHER_CANCELLED,\n    UiaEventIdSEnd = EVENT_UIA_EVENTID_END,\n    UiaEventIdStart = EVENT_UIA_EVENTID_START,\n    UiaPropIdSEnd = EVENT_UIA_PROPID_END,\n    UiaPropIdStart = EVENT_UIA_PROPID_START,\n    /// Fallback for unknown/missing Win32 events\n    Unknown(u32),\n    // ================== Synthetic events ==================\n    /// intended to reduce the amount of events processed by other listeners\n    SynThrottledForegroundRectChange,\n    SynDebouncedForegroundRectChange,\n    SyntheticFullscreenStart,\n    SyntheticFullscreenEnd,\n    SyntheticMonitorChanged,\n}\n\nimpl From<u32> for WinEvent {\n    fn from(value: u32) -> Self {\n        match value {\n            EVENT_AIA_END => Self::AiaEnd,\n            EVENT_AIA_START => Self::AiaStart,\n            EVENT_CONSOLE_CARET => Self::ConsoleCaret,\n            EVENT_CONSOLE_END => Self::ConsoleEnd,\n            EVENT_CONSOLE_END_APPLICATION => Self::ConsoleEndApplication,\n            EVENT_CONSOLE_LAYOUT => Self::ConsoleLayout,\n            EVENT_CONSOLE_START_APPLICATION => Self::ConsoleStartApplication,\n            EVENT_CONSOLE_UPDATE_REGION => Self::ConsoleUpdateRegion,\n            EVENT_CONSOLE_UPDATE_SCROLL => Self::ConsoleUpdateScroll,\n            EVENT_CONSOLE_UPDATE_SIMPLE => Self::ConsoleUpdateSimple,\n            EVENT_OBJECT_ACCELERATORCHANGE => Self::ObjectAcceleratorChange,\n            EVENT_OBJECT_CLOAKED => Self::ObjectCloaked,\n            EVENT_OBJECT_CONTENTSCROLLED => Self::ObjectContentScrolled,\n            EVENT_OBJECT_CREATE => Self::ObjectCreate,\n            EVENT_OBJECT_DEFACTIONCHANGE => Self::ObjectDefActionChange,\n            EVENT_OBJECT_DESCRIPTIONCHANGE => Self::ObjectDescriptionChange,\n            EVENT_OBJECT_DESTROY => Self::ObjectDestroy,\n            EVENT_OBJECT_DRAGCANCEL => Self::ObjectDragCancel,\n            EVENT_OBJECT_DRAGCOMPLETE => Self::ObjectDragComplete,\n            EVENT_OBJECT_DRAGDROPPED => Self::ObjectDragDropped,\n            EVENT_OBJECT_DRAGENTER => Self::ObjectDragEnter,\n            EVENT_OBJECT_DRAGLEAVE => Self::ObjectDragLeave,\n            EVENT_OBJECT_DRAGSTART => Self::ObjectDragStart,\n            EVENT_OBJECT_END => Self::ObjectEnd,\n            EVENT_OBJECT_FOCUS => Self::ObjectFocus,\n            EVENT_OBJECT_HELPCHANGE => Self::ObjectHelpChange,\n            EVENT_OBJECT_HIDE => Self::ObjectHide,\n            EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED => Self::ObjectHostedObjectsInvalidated,\n            EVENT_OBJECT_IME_CHANGE => Self::ObjectImeChange,\n            EVENT_OBJECT_IME_HIDE => Self::ObjectImeHide,\n            EVENT_OBJECT_IME_SHOW => Self::ObjectImeShow,\n            EVENT_OBJECT_INVOKED => Self::ObjectInvoked,\n            EVENT_OBJECT_LIVEREGIONCHANGED => Self::ObjectLiveRegionChanged,\n            EVENT_OBJECT_LOCATIONCHANGE => Self::ObjectLocationChange,\n            EVENT_OBJECT_NAMECHANGE => Self::ObjectNameChange,\n            EVENT_OBJECT_PARENTCHANGE => Self::ObjectParentChange,\n            EVENT_OBJECT_REORDER => Self::ObjectReorder,\n            EVENT_OBJECT_SELECTION => Self::ObjectSelection,\n            EVENT_OBJECT_SELECTIONADD => Self::ObjectSelectionAdd,\n            EVENT_OBJECT_SELECTIONREMOVE => Self::ObjectSelectionRemove,\n            EVENT_OBJECT_SELECTIONWITHIN => Self::ObjectSelectionWithin,\n            EVENT_OBJECT_SHOW => Self::ObjectShow,\n            EVENT_OBJECT_STATECHANGE => Self::ObjectStateChange,\n            EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED => {\n                Self::ObjectTextEditConversionTargetChanged\n            }\n            EVENT_OBJECT_TEXTSELECTIONCHANGED => Self::ObjectTextSelectionChanged,\n            EVENT_OBJECT_UNCLOAKED => Self::ObjectUncloaked,\n            EVENT_OBJECT_VALUECHANGE => Self::ObjectValueChange,\n            EVENT_OEM_DEFINED_END => Self::OemDefinedEnd,\n            EVENT_OEM_DEFINED_START => Self::OemDefinedStart,\n            EVENT_SYSTEM_ALERT => Self::SystemAlert,\n            EVENT_SYSTEM_ARRANGMENTPREVIEW => Self::SystemArrangementPreview,\n            EVENT_SYSTEM_CAPTUREEND => Self::SystemCaptureEnd,\n            EVENT_SYSTEM_CAPTURESTART => Self::SystemCaptureStart,\n            EVENT_SYSTEM_CONTEXTHELPEND => Self::SystemContextHelpEnd,\n            EVENT_SYSTEM_CONTEXTHELPSTART => Self::SystemContextHelpStart,\n            EVENT_SYSTEM_DESKTOPSWITCH => Self::SystemDesktopSwitch,\n            EVENT_SYSTEM_DIALOGEND => Self::SystemDialogEnd,\n            EVENT_SYSTEM_DIALOGSTART => Self::SystemDialogStart,\n            EVENT_SYSTEM_DRAGDROPEND => Self::SystemDragDropEnd,\n            EVENT_SYSTEM_DRAGDROPSTART => Self::SystemDragDropStart,\n            EVENT_SYSTEM_END => Self::SystemEnd,\n            EVENT_SYSTEM_FOREGROUND => Self::SystemForeground,\n            EVENT_SYSTEM_IME_KEY_NOTIFICATION => Self::SystemImeKeyNotification,\n            EVENT_SYSTEM_MENUEND => Self::SystemMenuEnd,\n            EVENT_SYSTEM_MENUPOPUPEND => Self::SystemMenuPopupEnd,\n            EVENT_SYSTEM_MENUPOPUPSTART => Self::SystemMenuPopupStart,\n            EVENT_SYSTEM_MENUSTART => Self::SystemMenuStart,\n            EVENT_SYSTEM_MINIMIZEEND => Self::SystemMinimizeEnd,\n            EVENT_SYSTEM_MINIMIZESTART => Self::SystemMinimizeStart,\n            EVENT_SYSTEM_MOVESIZEEND => Self::SystemMoveSizeEnd,\n            EVENT_SYSTEM_MOVESIZESTART => Self::SystemMoveSizeStart,\n            EVENT_SYSTEM_SCROLLINGEND => Self::SystemScrollingEnd,\n            EVENT_SYSTEM_SCROLLINGSTART => Self::SystemScrollingStart,\n            EVENT_SYSTEM_SOUND => Self::SystemSound,\n            EVENT_SYSTEM_SWITCHEND => Self::SystemSwitchEnd,\n            EVENT_SYSTEM_SWITCHER_APPDROPPED => Self::SystemSwitcherAppDropped,\n            EVENT_SYSTEM_SWITCHER_APPGRABBED => Self::SystemSwitcherAppGrabbed,\n            EVENT_SYSTEM_SWITCHER_APPOVERTARGET => Self::SystemSwitcherAppOverTarget,\n            EVENT_SYSTEM_SWITCHER_CANCELLED => Self::SystemSwitcherCancelled,\n            EVENT_SYSTEM_SWITCHSTART => Self::SystemSwitchStart,\n            EVENT_UIA_EVENTID_END => Self::UiaEventIdSEnd,\n            EVENT_UIA_EVENTID_START => Self::UiaEventIdStart,\n            EVENT_UIA_PROPID_END => Self::UiaPropIdSEnd,\n            EVENT_UIA_PROPID_START => Self::UiaPropIdStart,\n            _ => Self::Unknown(value),\n        }\n    }\n}\n\nstatic LAZY_LOCATION_CHANGE_EVENT: LazyLock<slu_utils::Throttle<isize>> = LazyLock::new(|| {\n    slu_utils::throttle(\n        |addr| {\n            let window = Window::from(addr);\n            if window.is_focused() {\n                HookManager::send((WinEvent::SynThrottledForegroundRectChange, window));\n            }\n        },\n        std::time::Duration::from_millis(100),\n    )\n});\n\nstatic LAZY_LOCATION_CHANGE_EVENT2: LazyLock<slu_utils::Debounce<isize>> = LazyLock::new(|| {\n    slu_utils::debounce(\n        |addr| {\n            let window = Window::from(addr);\n            if window.is_focused() {\n                HookManager::send((WinEvent::SynDebouncedForegroundRectChange, window));\n            }\n        },\n        std::time::Duration::from_millis(100),\n    )\n});\n\nimpl WinEvent {\n    pub fn debounce_as_needed(&self, origin: &Window) {\n        match self {\n            Self::ObjectLocationChange if origin.is_focused() => {\n                LAZY_LOCATION_CHANGE_EVENT.call(origin.address());\n                LAZY_LOCATION_CHANGE_EVENT2.call(origin.address());\n            }\n            Self::SystemMoveSizeEnd => {\n                LAZY_LOCATION_CHANGE_EVENT.terminate();\n                LAZY_LOCATION_CHANGE_EVENT2.terminate();\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "src/background/windows_api/window/mod.rs",
    "content": "pub mod cache;\npub mod event;\n\nuse seelen_core::state::WorkspaceId;\nuse seelen_core::{rect::Rect, system_state::MonitorId};\nuse slu_ipc::messages::SvcAction;\nuse std::sync::atomic::Ordering;\nuse std::sync::Arc;\nuse std::{\n    fmt::{Debug, Display},\n    path::PathBuf,\n    sync::{atomic::AtomicIsize, LazyLock},\n};\n\nuse windows::{\n    ApplicationModel::AppInfo,\n    Win32::{\n        Foundation::{HWND, RECT},\n        UI::{\n            Shell::FOLDERID_System,\n            WindowsAndMessaging::{SET_WINDOW_POS_FLAGS, SHOW_WINDOW_CMD, SW_RESTORE},\n        },\n    },\n};\n\nuse crate::virtual_desktops::SluWorkspacesManager2;\nuse crate::{\n    cli::ServicePipe,\n    error::Result,\n    modules::{apps::application::is_interactable_window, start::application::StartMenuManager},\n    utils::lock_free::TracedMutex,\n    widgets::{\n        toolbar::FancyToolbar, wallpaper_manager::SeelenWall, weg::instance::SeelenWeg,\n        window_manager::instance::WindowManagerV2,\n    },\n};\n\nuse super::{\n    monitor::Monitor, process::Process, types::AppUserModelId, WindowEnumerator, WindowsApi,\n};\n\nstatic DRAGGING_WINDOW: AtomicIsize = AtomicIsize::new(0);\nstatic LAST_DRAGGED_WINDOW: AtomicIsize = AtomicIsize::new(0);\nstatic DRAGGED_WINDOW_RECT_BEFORE_DRAG: LazyLock<Arc<TracedMutex<Option<Rect>>>> =\n    LazyLock::new(|| Arc::new(TracedMutex::new(None)));\n\n#[derive(Clone, Copy, PartialEq, Eq)]\npub struct Window(HWND);\nunsafe impl Send for Window {}\nunsafe impl Sync for Window {}\n\nimpl From<HWND> for Window {\n    fn from(hwnd: HWND) -> Self {\n        Self(hwnd)\n    }\n}\n\nimpl From<isize> for Window {\n    fn from(addr: isize) -> Self {\n        Self(HWND(addr as _))\n    }\n}\n\nimpl Debug for Window {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Window\")\n            .field(\"handle\", &self.0 .0)\n            .field(\n                \"exe\",\n                &self.process().program_exe_name().unwrap_or_default(),\n            )\n            .field(\"class\", &self.class())\n            .field(\"title\", &self.title())\n            .finish()\n    }\n}\n\nimpl Display for Window {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"Window({:?})\", self.0 .0)\n    }\n}\n\nstatic APP_FRAME_HOST_PATH: LazyLock<PathBuf> = LazyLock::new(|| {\n    WindowsApi::known_folder(FOLDERID_System)\n        .expect(\"Failed to get system folder\")\n        .join(\"ApplicationFrameHost.exe\")\n});\n\nimpl Window {\n    pub fn get_foregrounded() -> Window {\n        Window(WindowsApi::get_foreground_window())\n    }\n\n    pub fn hwnd(&self) -> HWND {\n        self.0\n    }\n\n    pub fn address(&self) -> isize {\n        self.0 .0 as isize\n    }\n\n    pub fn is_electron(&self) -> bool {\n        self.class() == \"Chrome_WidgetWin_1\"\n    }\n\n    /// Application user model id asigned to the window via property-store or inherited from the process\n    ///\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-id\n    pub fn app_user_model_id(&self) -> Option<AppUserModelId> {\n        if let Ok(umid) = WindowsApi::get_window_app_user_model_id(self.0) {\n            return match WindowsApi::is_uwp_package_id(&umid) {\n                true => Some(AppUserModelId::Appx(umid)),\n                false => Some(AppUserModelId::PropertyStore(umid)),\n            };\n        }\n\n        let process = self.process();\n        if let Ok(umid) = process.package_app_user_model_id() {\n            return Some(umid);\n        }\n\n        if self.is_electron() {\n            let path = process.program_path().ok()?;\n\n            // special manual case like there's no way to call GetCurrentProcessExplicitAppUserModelID without code injection\n            if path.file_name()?.to_string_lossy().to_lowercase() == \"discord.exe\" {\n                return Some(AppUserModelId::PropertyStore(\n                    \"com.squirrel.Discord.Discord\".to_string(),\n                ));\n            }\n\n            let guard = StartMenuManager::instance();\n            let item = guard.get_by_target(&path)?;\n            Some(AppUserModelId::PropertyStore(item.umid.clone()?))\n        } else {\n            None\n        }\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-preventpinning\n    pub fn prevent_pinning(&self) -> bool {\n        WindowsApi::get_window_prevent_pinning(self.0).unwrap_or(false)\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchcommand\n    pub fn relaunch_command(&self) -> Option<String> {\n        WindowsApi::get_window_relaunch_command(self.0).ok()\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchdisplaynameresource\n    pub fn relaunch_display_name(&self) -> Option<String> {\n        if let Ok(name) = WindowsApi::get_window_relaunch_display_name(self.0) {\n            if name.starts_with(\"@\") {\n                return WindowsApi::resolve_indirect_string(&name).ok();\n            }\n            return Some(name);\n        }\n        None\n    }\n\n    /// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchiconresource\n    pub fn relaunch_icon(&self) -> Option<String> {\n        WindowsApi::get_window_relaunch_icon_resource(self.0).ok()\n    }\n\n    pub fn title(&self) -> String {\n        WindowsApi::get_window_text(self.0)\n    }\n\n    pub fn class(&self) -> String {\n        WindowsApi::get_class(self.0).unwrap_or_default()\n    }\n\n    pub fn process(&self) -> Process {\n        Process::from_window(self)\n    }\n\n    pub fn app_display_name(&self) -> Result<String> {\n        if let Some(umid) = self.app_user_model_id() {\n            match umid {\n                AppUserModelId::Appx(umid) => {\n                    let info = AppInfo::GetFromAppUserModelId(&umid.into())?;\n                    return Ok(info.DisplayInfo()?.DisplayName()?.to_string_lossy());\n                }\n                AppUserModelId::PropertyStore(_) => {\n                    if let Some(display_name) = self.relaunch_display_name() {\n                        return Ok(display_name);\n                    }\n                }\n            }\n        }\n        self.process().program_display_name()\n    }\n\n    #[allow(dead_code)]\n    pub fn outer_rect(&self) -> Result<Rect> {\n        let rect = WindowsApi::get_outer_window_rect(self.hwnd())?;\n        Ok(Rect {\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n        })\n    }\n\n    pub fn inner_rect(&self) -> Result<Rect> {\n        let rect = WindowsApi::get_inner_window_rect(self.hwnd())?;\n        Ok(Rect {\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n        })\n    }\n\n    pub fn parent(&self) -> Option<Window> {\n        WindowsApi::get_parent(self.0).ok().map(Window::from)\n    }\n\n    pub fn owner(&self) -> Option<Window> {\n        WindowsApi::get_owner(self.0).ok().map(Window::from)\n    }\n\n    pub fn children(&self) -> Result<Vec<Window>> {\n        WindowEnumerator::new()\n            .with_parent(self.0)\n            .map(Window::from)\n    }\n\n    pub fn monitor(&self) -> Monitor {\n        Monitor::from(WindowsApi::monitor_from_window(self.0))\n    }\n\n    pub fn monitor_id(&self) -> MonitorId {\n        self.monitor()\n            .stable_id()\n            .unwrap_or_else(|_| MonitorId(\"null\".to_string()))\n    }\n\n    pub fn workspace_id(&self) -> Result<WorkspaceId> {\n        let win_id = self.address();\n        let monitor_id = self.monitor_id();\n        let workspace_id = SluWorkspacesManager2::instance()\n            .monitors\n            .get(&monitor_id, |monitor| {\n                monitor.workspaces.iter().find_map(|w| {\n                    if w.windows.contains(&win_id) {\n                        Some(w.id.clone())\n                    } else {\n                        None\n                    }\n                })\n            })\n            .ok_or(\"Monitor not found\")?\n            .ok_or(\"This window is not binded to a seelen ui workspace\")?;\n        Ok(workspace_id)\n    }\n\n    pub fn is_window(&self) -> bool {\n        WindowsApi::is_window(self.0)\n    }\n\n    pub fn is_visible(&self) -> bool {\n        WindowsApi::is_window_visible(self.0)\n    }\n\n    pub fn is_minimized(&self) -> bool {\n        WindowsApi::is_iconic(self.0)\n    }\n\n    pub fn is_maximized(&self) -> bool {\n        WindowsApi::is_zoomed(self.0)\n    }\n\n    pub fn is_cloaked(&self) -> bool {\n        WindowsApi::is_cloaked(self.0).unwrap_or(false)\n    }\n\n    pub fn is_focused(&self) -> bool {\n        WindowsApi::get_foreground_window() == self.0\n    }\n\n    pub fn is_fullscreen(&self) -> bool {\n        WindowsApi::is_fullscreen(self.0).unwrap_or(false)\n            && !self.is_desktop()\n            && !self.process().is_seelen() // we ignore seelen widgets\n    }\n\n    /// is the window an Application Frame Host\n    pub fn is_frame(&self) -> Result<bool> {\n        Ok(self\n            .process()\n            .program_path()?\n            .as_os_str()\n            .eq_ignore_ascii_case(&*APP_FRAME_HOST_PATH))\n    }\n\n    /// will fail if the window is not a frame\n    pub fn get_frame_creator(&self) -> Result<Option<Window>> {\n        if !self.is_frame()? {\n            return Err(\"Window is not a frame\".into());\n        }\n\n        let title = Some(self.title());\n        let class = Some(\"Windows.UI.Core.CoreWindow\".to_owned());\n\n        // frame creator is a child of the window while rendering\n        if let Ok(hwnd) =\n            WindowsApi::find_window(Some(self.hwnd()), None, title.clone(), class.clone())\n        {\n            return Ok(Some(Window::from(hwnd)));\n        }\n\n        // while minimized, not rendering, the creator is detached as an top level window\n        if let Ok(hwnd) = WindowsApi::find_window(None, None, title, class) {\n            return Ok(Some(Window::from(hwnd)));\n        }\n\n        Ok(None)\n    }\n\n    /// this means all windows that are part of the UI desktop not the real desktop window\n    pub fn is_desktop(&self) -> bool {\n        WindowsApi::get_desktop_window() == self.0 || {\n            let class = self.class();\n            class == \"Progman\" || {\n                class == \"WorkerW\"\n                    && self.children().is_ok_and(|children| {\n                        children\n                            .iter()\n                            .any(|child| child.class() == \"SHELLDLL_DefView\")\n                    })\n            }\n        }\n    }\n\n    pub fn is_seelen_overlay(&self) -> bool {\n        self.process().is_seelen() && {\n            [\n                FancyToolbar::TITLE,\n                WindowManagerV2::TITLE,\n                SeelenWeg::TITLE,\n                SeelenWall::TITLE,\n            ]\n            .contains(&self.title().as_str())\n        }\n    }\n\n    /// read inner called doc for more info\n    pub fn is_interactable_and_not_hidden(&self) -> bool {\n        is_interactable_window(self)\n    }\n\n    pub fn show_window(&self, command: SHOW_WINDOW_CMD) -> Result<()> {\n        if self.process().open_handle().is_ok() {\n            WindowsApi::show_window(self.hwnd(), command)\n        } else {\n            ServicePipe::request(SvcAction::ShowWindow {\n                hwnd: self.address(),\n                command: command.0,\n            })\n        }\n    }\n\n    pub fn show_window_async(&self, command: SHOW_WINDOW_CMD) -> Result<()> {\n        if self.process().open_handle().is_ok() {\n            WindowsApi::show_window_async(self.hwnd(), command)\n        } else {\n            ServicePipe::request(SvcAction::ShowWindowAsync {\n                hwnd: self.address(),\n                command: command.0,\n            })\n        }\n    }\n\n    #[allow(dead_code)]\n    pub fn set_position(&self, rect: &RECT, flags: SET_WINDOW_POS_FLAGS) -> Result<()> {\n        if self.process().open_handle().is_ok() {\n            WindowsApi::set_position(self.hwnd(), None, rect, flags)\n        } else {\n            ServicePipe::request(SvcAction::SetWindowPosition {\n                hwnd: self.address(),\n                rect: Rect {\n                    top: rect.top,\n                    left: rect.left,\n                    right: rect.right,\n                    bottom: rect.bottom,\n                },\n                flags: flags.0,\n            })\n        }\n    }\n\n    pub fn focus(&self) -> Result<()> {\n        if self.is_minimized() {\n            self.show_window(SW_RESTORE)?;\n        }\n\n        /* if self.process().open_handle().is_ok() {\n            WindowsApi::set_foreground(self.hwnd())\n        } else {\n            ServicePipe::request(SvcAction::SetForeground(self.address()))\n        } */\n        WindowsApi::set_foreground(self.hwnd())\n    }\n\n    pub fn is_dragging(&self) -> bool {\n        DRAGGING_WINDOW.load(Ordering::SeqCst) == self.address()\n    }\n\n    pub fn is_last_dragged(&self) -> bool {\n        LAST_DRAGGED_WINDOW.load(Ordering::SeqCst) == self.address()\n    }\n\n    pub fn set_dragging(&self, dragging: bool) {\n        if dragging {\n            DRAGGING_WINDOW.store(self.address(), Ordering::SeqCst);\n            LAST_DRAGGED_WINDOW.store(self.address(), Ordering::SeqCst);\n            *DRAGGED_WINDOW_RECT_BEFORE_DRAG.lock() = self.inner_rect().ok();\n        } else {\n            DRAGGING_WINDOW.store(0, Ordering::SeqCst);\n            // *DRAGGING_WINDOW_RECT_BEFORE_DRAG.lock() = None; we don't clean up to allow be used on drag end\n        }\n    }\n\n    /// if dragging returns the rect of the window before dragging\n    /// otherwise returns the current rect\n    pub fn get_rect_before_dragging(&self) -> Result<Rect> {\n        if self.is_dragging() || self.is_last_dragged() {\n            let guard = DRAGGED_WINDOW_RECT_BEFORE_DRAG.lock();\n            if let Some(rect) = guard.as_ref() {\n                Ok(rect.clone())\n            } else {\n                self.inner_rect()\n            }\n        } else {\n            self.inner_rect()\n        }\n    }\n}\n"
  },
  {
    "path": "src/build.rs",
    "content": "use std::{fs::create_dir, path::PathBuf};\n\nuse slu_utils::{checksums::CheckSums, signature::sign_minisign};\n\nfn main() {\n    let _ = create_dir(\"gen\");\n\n    let mut checksums = CheckSums::new();\n    read_folder_recursive(PathBuf::from(\"static\"), &mut |path| {\n        checksums.add(&path).unwrap();\n    });\n\n    let target_dir = target_dir();\n    let sums_path = target_dir.join(\"SHA256SUMS\");\n    checksums.write(&sums_path).unwrap();\n\n    if !cfg!(debug_assertions) {\n        sign_sha256sums(&sums_path);\n    } else {\n        std::fs::write(\n            sums_path.with_extension(\"sig\"),\n            \"NOT SIGNED NEEDED FOR DEBUG\",\n        )\n        .unwrap();\n    }\n\n    tauri_build::build();\n}\n\nfn target_dir() -> PathBuf {\n    let out_dir = PathBuf::from(std::env::var(\"OUT_DIR\").unwrap());\n    // see <https://github.com/rust-lang/cargo/issues/5457>\n    out_dir\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .to_path_buf()\n}\n\nfn read_folder_recursive<F>(path: PathBuf, cb: &mut F)\nwhere\n    F: FnMut(PathBuf),\n{\n    for entry in std::fs::read_dir(path).unwrap() {\n        let entry = entry.unwrap();\n        let path = entry.path();\n        if path.is_dir() {\n            read_folder_recursive(path, cb);\n        } else {\n            cb(path);\n        }\n    }\n}\n\nfn sign_sha256sums(path: &PathBuf) {\n    let key_base64 =\n        std::env::var(\"TAURI_SIGNING_PRIVATE_KEY\").expect(\"TAURI_SIGNING_PRIVATE_KEY missing\");\n    let password = std::env::var(\"TAURI_SIGNING_PRIVATE_KEY_PASSWORD\")\n        .expect(\"TAURI_SIGNING_PRIVATE_KEY_PASSWORD missing\");\n\n    let data = std::fs::read(path).expect(\"Failed to read SHA256SUMS file\");\n    let signature = sign_minisign(&data, &key_base64, password).expect(\"Failed to sign data\");\n\n    let sig_path = path.with_extension(\"sig\");\n    std::fs::write(&sig_path, signature).expect(\"Failed to write signature\");\n}\n"
  },
  {
    "path": "src/capabilities/general.json",
    "content": "{\n  \"$schema\": \"../gen/schemas/windows-schema.json\",\n  \"identifier\": \"general\",\n  \"description\": \"general permissions for all widgets\",\n  \"local\": true,\n  \"windows\": [\n    \"*\"\n  ],\n  \"permissions\": [\n    \"core:event:default\",\n    \"core:webview:default\",\n    \"core:webview:allow-set-webview-focus\",\n    \"core:webview:allow-set-webview-zoom\",\n    \"core:path:default\",\n    \"core:app:default\",\n    \"dialog:default\",\n    \"shell:allow-open\"\n  ]\n}\n"
  },
  {
    "path": "src/capabilities/general_window.json",
    "content": "{\n  \"$schema\": \"../gen/schemas/windows-schema.json\",\n  \"identifier\": \"general-window\",\n  \"description\": \"core:window permissions for all widgets\",\n  \"local\": true,\n  \"windows\": [\n    \"*\"\n  ],\n  \"permissions\": [\n    \"core:window:default\",\n\n    \"core:window:allow-show\",\n    \"core:window:allow-hide\",\n\n    \"core:window:deny-create\",\n    \"core:window:allow-destroy\",\n\n    \"core:window:allow-set-always-on-top\",\n    \"core:window:allow-set-always-on-bottom\",\n    \"core:window:allow-set-fullscreen\",\n\n    \"core:window:allow-center\",\n    \"core:window:allow-set-size\",\n    \"core:window:allow-set-size-constraints\",\n    \"core:window:allow-set-position\",\n    \"core:window:allow-set-min-size\",\n    \"core:window:allow-set-max-size\",\n\n    \"core:window:allow-set-resizable\",\n    \"core:window:allow-set-minimizable\",\n    \"core:window:allow-set-maximizable\",\n    \"core:window:allow-set-closable\",\n\n    \"core:window:allow-minimize\",\n    \"core:window:allow-unminimize\",\n    \"core:window:allow-maximize\",\n    \"core:window:allow-unmaximize\",\n    \"core:window:allow-toggle-maximize\",\n    \"core:window:allow-close\",\n\n    \"core:window:allow-set-title\",\n    \"core:window:allow-set-title-bar-style\",\n    \"core:window:allow-set-decorations\",\n    \"core:window:allow-set-shadow\",\n    \"core:window:allow-set-effects\",\n    \"core:window:allow-set-content-protected\",\n    \"core:window:allow-set-ignore-cursor-events\",\n\n    \"core:window:allow-set-skip-taskbar\",\n    \"core:window:allow-set-visible-on-all-workspaces\",\n\n    \"core:window:allow-set-icon\",\n    \"core:window:allow-set-progress-bar\",\n    \"core:window:allow-request-user-attention\",\n\n    \"core:window:allow-set-focus\",\n    \"core:window:allow-set-focusable\",\n    \"core:window:allow-set-cursor-grab\",\n    \"core:window:allow-set-cursor-icon\",\n    \"core:window:allow-set-cursor-position\",\n    \"core:window:allow-set-cursor-visible\",\n\n    \"core:window:allow-start-dragging\",\n    \"core:window:allow-start-resize-dragging\"\n  ]\n}\n"
  },
  {
    "path": "src/capabilities/settings_widget.json",
    "content": "{\n  \"$schema\": \"../gen/schemas/windows-schema.json\",\n  \"identifier\": \"migrated\",\n  \"description\": \"Settings Widget Capabilities\",\n  \"local\": true,\n  \"windows\": [\n    \"QHNlZWxlbi9zZXR0aW5ncw\"\n  ],\n  \"permissions\": [\n    \"fs:allow-read-text-file\",\n    \"fs:allow-write-text-file\",\n    \"fs:allow-exists\",\n    \"fs:allow-mkdir\",\n    \"fs:allow-read-dir\",\n    \"fs:allow-copy-file\",\n    \"fs:allow-remove\",\n    {\n      \"identifier\": \"fs:scope\",\n      \"allow\": [\n        {\n          \"path\": \"**\"\n        },\n        {\n          \"path\": \"**/*\"\n        },\n        {\n          \"path\": \"/**/*\"\n        }\n      ]\n    },\n\n    \"process:allow-restart\",\n    \"process:allow-exit\"\n  ]\n}\n"
  },
  {
    "path": "src/hook_dll/Cargo.toml",
    "content": "[package]\nname = \"sluhk\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nwindows = { workspace = true, features = [\n    \"Win32_Foundation\",\n    \"Win32_System_LibraryLoader\",\n    \"Win32_UI_WindowsAndMessaging\",\n    \"Win32_UI_Shell\",\n    \"Win32_System_DataExchange\",\n] }\nwindows-core = { workspace = true }\nuuid = { workspace = true, features = [\"serde\"] }\n\n# Usamos la librería IPC del proyecto\nslu-ipc = { path = \"../../libs/slu-ipc\" }\n"
  },
  {
    "path": "src/hook_dll/src/lib.rs",
    "content": "use slu_ipc::{\n    messages::{AppMessage, IconEventData, Win32TrayEvent},\n    AppIpc,\n};\nuse windows::Win32::{\n    Foundation::{HWND, LPARAM, LRESULT, WPARAM},\n    System::DataExchange::COPYDATASTRUCT,\n    UI::{\n        Shell::{\n            NIF_GUID, NIF_ICON, NIF_MESSAGE, NIF_TIP, NIM_ADD, NIM_DELETE, NIM_MODIFY,\n            NIM_SETVERSION, NIS_HIDDEN, NOTIFYICONDATAW_0, NOTIFY_ICON_DATA_FLAGS,\n            NOTIFY_ICON_INFOTIP_FLAGS, NOTIFY_ICON_MESSAGE, NOTIFY_ICON_STATE,\n        },\n        WindowsAndMessaging::{CallNextHookEx, GetClassNameW, CWPSTRUCT, WM_COPYDATA},\n    },\n};\nuse windows_core::GUID;\n\n// ============================================================================\n// Native Windows Structures\n// ============================================================================\n\n/// Tray message sent to `Shell_TrayWnd`\n#[repr(C)]\nstruct ShellTrayMessage {\n    magic_number: i32,\n    message_type: u32,\n    icon_data: NotifyIconData,\n    version: u32,\n}\n\n/// Contains the data for a system tray icon.\n#[repr(C)]\n#[derive(Clone, Copy)]\nstruct NotifyIconData {\n    callback_size: u32,\n    window_handle: u32,\n    uid: u32,\n    flags: NOTIFY_ICON_DATA_FLAGS,\n    callback_message: u32,\n    icon_handle: u32,\n    tooltip: [u16; 128],\n    state: NOTIFY_ICON_STATE,\n    state_mask: NOTIFY_ICON_STATE,\n    size_info: [u16; 256],\n    anonymous: NOTIFYICONDATAW_0,\n    info_title: [u16; 64],\n    info_flags: NOTIFY_ICON_INFOTIP_FLAGS,\n    guid_item: GUID,\n    balloon_icon_handle: u32,\n}\n\nimpl From<NotifyIconData> for IconEventData {\n    fn from(icon_data: NotifyIconData) -> Self {\n        let icon_handle = if icon_data.icon_handle != 0 && icon_data.flags.0 & NIF_ICON.0 != 0 {\n            Some(icon_data.icon_handle as isize)\n        } else {\n            None\n        };\n\n        let guid = if icon_data.guid_item != GUID::default() && icon_data.flags.0 & NIF_GUID.0 != 0\n        {\n            Some(uuid::Uuid::from_u128(icon_data.guid_item.to_u128()))\n        } else {\n            None\n        };\n\n        let tooltip = if icon_data.flags.0 & NIF_TIP.0 != 0 {\n            let tooltip_len = icon_data.tooltip.iter().position(|&c| c == 0).unwrap_or(0);\n            let tooltip_str = String::from_utf16_lossy(&icon_data.tooltip[..tooltip_len])\n                .replace('\\r', \"\")\n                .to_string();\n            (!tooltip_str.is_empty()).then_some(tooltip_str)\n        } else {\n            None\n        };\n\n        let (window_handle, uid) = if icon_data.window_handle != 0 {\n            (Some(icon_data.window_handle as isize), Some(icon_data.uid))\n        } else {\n            (None, None)\n        };\n\n        let callback_message = if icon_data.flags.contains(NIF_MESSAGE) {\n            Some(icon_data.callback_message)\n        } else {\n            None\n        };\n\n        let version = if unsafe { icon_data.anonymous.uVersion } > 0\n            && unsafe { icon_data.anonymous.uVersion } <= 4\n        {\n            Some(unsafe { icon_data.anonymous.uVersion })\n        } else {\n            None\n        };\n\n        let is_visible = icon_data.state.0 & NIS_HIDDEN.0 == 0;\n\n        IconEventData {\n            uid,\n            window_handle,\n            guid,\n            tooltip,\n            icon_handle,\n            callback_message,\n            version,\n            is_visible,\n        }\n    }\n}\n\nfn get_window_class(hwnd: HWND) -> String {\n    let mut text: [u16; 512] = [0; 512];\n    let len = unsafe { GetClassNameW(hwnd, &mut text) };\n    let length = usize::try_from(len).unwrap_or(0);\n    String::from_utf16_lossy(&text[..length])\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/// https://learn.microsoft.com/en-us/windows/win32/winmsg/about-hooks\n/// https://learn.microsoft.com/en-us/windows/win32/winmsg/callwndproc\n///\n/// # Safety\n#[no_mangle]\npub unsafe extern \"system\" fn CallWndProc(code: i32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {\n    let next = || CallNextHookEx(None, code, wparam, lparam);\n    if code < 0 {\n        return next();\n    }\n\n    let Some(msg) = (lparam.0 as *const CWPSTRUCT).as_ref() else {\n        return next();\n    };\n\n    let class = get_window_class(msg.hwnd);\n    if class != \"Shell_TrayWnd\" {\n        return next();\n    }\n\n    // Send debug message for all messages received\n    /* let debug_msg = format!(\n        \"CallWndProc - code: {}, hwnd: {:?}, message: 0x{:X}, wparam: {:?}, lparam: {:?}\",\n        code, msg.hwnd, msg.message, msg.wParam, msg.lParam\n    );\n    let _ = AppIpc::send_sync(&AppMessage::Debug(debug_msg)); */\n\n    if let Some(event) = process_tray_message(msg) {\n        send_event_via_ipc(event);\n    }\n\n    next()\n}\n\n/// Processes a tray message and returns an event if relevant\nunsafe fn process_tray_message(msg: &CWPSTRUCT) -> Option<Win32TrayEvent> {\n    if msg.message != WM_COPYDATA {\n        return None;\n    }\n\n    let copy_data = (msg.lParam.0 as *const COPYDATASTRUCT).as_ref()?;\n\n    // Type 1 is the tray icon message\n    if copy_data.dwData != 1 || copy_data.lpData.is_null() {\n        return None;\n    }\n\n    let tray_message = &*copy_data.lpData.cast::<ShellTrayMessage>();\n    let icon_data: IconEventData = tray_message.icon_data.into();\n\n    match NOTIFY_ICON_MESSAGE(tray_message.message_type) {\n        NIM_ADD => Some(Win32TrayEvent::IconAdd { data: icon_data }),\n        NIM_MODIFY | NIM_SETVERSION => Some(Win32TrayEvent::IconUpdate { data: icon_data }),\n        NIM_DELETE => Some(Win32TrayEvent::IconRemove { data: icon_data }),\n        _ => None,\n    }\n}\n\n// ============================================================================\n// IPC Implementation\n// ============================================================================\n\n/// Sends an event through the IPC channel to the main process\nfn send_event_via_ipc(event: Win32TrayEvent) {\n    // Send the event as AppMessage::TrayChanged via AppIpc\n    // If it fails, we simply ignore it (we don't want to crash the hook)\n    let _ = AppIpc::send_sync(&AppMessage::TrayChanged(event));\n}\n\n// ============================================================================\n// DLL Entry Point\n// ============================================================================\n\n/// DLL entry point\n/// This is called when the DLL is loaded/unloaded in any process\n///\n/// # Safety\n#[no_mangle]\n#[allow(non_snake_case)]\npub extern \"system\" fn DllMain(\n    _hinst_dll: windows::Win32::Foundation::HINSTANCE,\n    _fdw_reason: u32,\n    _lpv_reserved: *const std::ffi::c_void,\n) -> bool {\n    // No special initialization needed\n    // The hook will be installed by the main process\n    true\n}\n"
  },
  {
    "path": "src/service/app_management.rs",
    "content": "use slu_ipc::{AppIpc, IPC};\nuse sysinfo::ProcessesToUpdate;\n\nuse crate::{enviroment::was_installed_using_msix, error::Result, exit};\n\n/// Starts monitoring the Seelen UI app for the current session\n/// Restarts it if it crashes unexpectedly\npub fn start_app_monitoring() {\n    std::thread::spawn(move || {\n        let mut crash_counter = 0;\n        let max_tries = if cfg!(debug_assertions) { 0 } else { 5 };\n        let mut app_was_connected = false;\n\n        loop {\n            std::thread::sleep(std::time::Duration::from_secs(1));\n\n            if crate::EXITING.load(std::sync::atomic::Ordering::SeqCst) {\n                return;\n            }\n\n            if AppIpc::can_stablish_connection() {\n                // Reset counter once the app is confirmed running after a restart\n                if crash_counter > 0 && !app_was_connected {\n                    log::info!(\"Seelen UI reconnected successfully, resetting crash counter.\");\n                    crash_counter = 0;\n                }\n                app_was_connected = true;\n                continue;\n            }\n\n            // Only count as a crash if we had a confirmed connection before\n            if !app_was_connected {\n                continue;\n            }\n\n            app_was_connected = false;\n            log::info!(\"Seelen UI was closed unexpectedly.\");\n            crash_counter += 1;\n            if crash_counter > max_tries {\n                break;\n            }\n\n            crate::log_error!(launch_seelen_ui());\n            std::thread::sleep(std::time::Duration::from_secs(3));\n        }\n\n        log::error!(\"Seelen UI crashed {crash_counter} times in a row, stopping service.\");\n        exit(1);\n    });\n}\n\n/// will start the app on the interactive session\npub fn launch_seelen_ui() -> Result<()> {\n    let path = if was_installed_using_msix() {\n        \"shell:AppsFolder\\\\Seelen.SeelenUI_p6yyn03m1894e!App\".to_string()\n    } else {\n        std::env::current_exe()?\n            .with_file_name(\"seelen-ui.exe\")\n            .to_string_lossy()\n            .to_string()\n    };\n    // Use explorer.exe to spawn the app de-elevated (with the interactive user's token),\n    // since the service runs elevated (TASK_RUNLEVEL_HIGHEST). ShellExecuteExW called\n    // from an elevated process inherits the elevated token, causing an infinite restart loop.\n    std::process::Command::new(\"explorer.exe\")\n        .arg(&path)\n        .spawn()?;\n    Ok(())\n}\n\npub fn kill_all_seelen_ui_processes() -> Result<()> {\n    log::info!(\"Killing all Seelen UI processes\");\n    let mut sys = sysinfo::System::new();\n    sys.refresh_processes(ProcessesToUpdate::All, true);\n    let instances: Vec<_> = sys\n        .processes()\n        .values()\n        .filter(|p| p.exe().is_some_and(|path| path.ends_with(\"seelen-ui.exe\")))\n        .collect();\n    for instance in instances {\n        instance.kill();\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/service/cli/mod.rs",
    "content": "pub mod processing;\n\nuse slu_ipc::{messages::SvcAction, ServiceIpc};\n\nuse clap::{Arg, ArgAction, Command};\n\nuse crate::{\n    enviroment::{add_installation_dir_to_path, remove_installation_dir_from_path},\n    error::Result,\n    logger::SluServiceLogger,\n    task_scheduler::TaskSchedulerHelper,\n    SERVICE_DISPLAY_NAME,\n};\n\npub struct ServiceSubcommands;\nimpl ServiceSubcommands {\n    pub const INSTALL: &str = \"install\";\n    pub const UNINSTALL: &str = \"uninstall\";\n    pub const STOP: &str = \"stop\";\n}\n\npub fn get_cli() -> Command {\n    Command::new(SERVICE_DISPLAY_NAME.to_string())\n        .author(\"eythaann\")\n        .about(\"Seelen Command Line Interface.\")\n        .long_about(\"Seelen Command Line Interface.\")\n        .before_help(\"\")\n        .after_help(\"To read more about Seelen visit https://github.com/eythaann/seelen-ui.git\")\n        .subcommands([\n            Command::new(ServiceSubcommands::INSTALL)\n                .about(\"Installs or repairs the service (elevation required).\"),\n            Command::new(ServiceSubcommands::UNINSTALL)\n                .about(\"Uninstalls the service (elevation required).\"),\n            Command::new(ServiceSubcommands::STOP).about(\"Stops the service.\"),\n        ])\n        .args([Arg::new(\"startup\")\n            .short('S')\n            .long(\"startup\")\n            .action(ArgAction::SetTrue)\n            .help(\"Indicates that the app was invoked from the start up action.\")])\n}\n\n/// Handles the CLI and exits the process with 0 if it should\npub async fn handle_console_client() -> Result<()> {\n    let matches = get_cli().get_matches();\n    let subcommand = matches.subcommand();\n\n    match subcommand {\n        Some((ServiceSubcommands::INSTALL, _)) => {\n            add_installation_dir_to_path()?;\n            TaskSchedulerHelper::create_service_task()?;\n        }\n        Some((ServiceSubcommands::UNINSTALL, _)) => {\n            SluServiceLogger::uninstall_old_logging()?;\n            remove_installation_dir_from_path()?;\n            TaskSchedulerHelper::remove_service_task()?;\n        }\n        Some((ServiceSubcommands::STOP, _)) => {\n            ServiceIpc::send(SvcAction::Stop).await?;\n        }\n        _ => {}\n    }\n\n    if subcommand.is_some() {\n        std::process::exit(0);\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/service/cli/processing.rs",
    "content": "use std::sync::LazyLock;\n\nuse positioning::{easings::Easing, AnimationOrchestrator, PositionerBuilder};\nuse slu_ipc::messages::{IpcResponse, SvcAction};\n\nuse crate::{error::Result, task_scheduler::TaskSchedulerHelper, windows_api::WindowsApi};\n\nstatic ANIMATION_ORCHESTRATOR: LazyLock<AnimationOrchestrator> =\n    LazyLock::new(AnimationOrchestrator::new);\n\nasync fn _process_action(command: SvcAction) -> Result<()> {\n    match command {\n        SvcAction::Stop => crate::exit(0),\n        // -----------------------------------------------------------------------\n        SvcAction::SetStartup(enabled) => TaskSchedulerHelper::set_run_on_logon(enabled)?,\n        SvcAction::ShowWindow { hwnd, command } => WindowsApi::show_window(hwnd, command)?,\n        SvcAction::ShowWindowAsync { hwnd, command } => {\n            WindowsApi::show_window_async(hwnd, command)?\n        }\n        SvcAction::SetWindowPosition { hwnd, rect, flags } => WindowsApi::set_position(\n            hwnd,\n            rect.left,\n            rect.top,\n            rect.right - rect.left,\n            rect.bottom - rect.top,\n            flags,\n        )?,\n        SvcAction::DeferWindowPositions {\n            list,\n            animated,\n            animation_duration,\n            easing,\n        } => {\n            let mut builder = PositionerBuilder::new();\n            for (hwnd, rect) in list {\n                builder.add(\n                    hwnd,\n                    positioning::rect::Rect {\n                        x: rect.left,\n                        y: rect.top,\n                        width: rect.right - rect.left,\n                        height: rect.bottom - rect.top,\n                    },\n                );\n            }\n\n            if !animated {\n                builder.place()?;\n                return Ok(());\n            }\n\n            let easing = Easing::from_name(&easing).unwrap_or(Easing::Linear);\n            // The orchestrator will automatically interrupt only the windows in this batch\n            // if they're already animating, without affecting other windows\n            ANIMATION_ORCHESTRATOR.animate_batch(\n                builder.build(),\n                animation_duration,\n                easing,\n                move |result| {\n                    if let Err(err) = result {\n                        log::error!(\"Animated window placement failed: {err}\");\n                    }\n                },\n            )?;\n        }\n        SvcAction::SetForeground(hwnd) => WindowsApi::set_foreground(hwnd)?,\n        SvcAction::SetSettings(settings) => {\n            if settings.shortcuts.enabled {\n                crate::hotkeys::start_app_shortcuts(&settings)?;\n            } else {\n                crate::hotkeys::stop_app_shortcuts();\n            }\n        }\n        SvcAction::StartShortcutRegistration => {\n            crate::hotkeys::start_shortcut_registration().await?;\n        }\n        SvcAction::StopShortcutRegistration => {\n            crate::hotkeys::stop_shortcut_registration().await?;\n        }\n    }\n    Ok(())\n}\n\npub async fn process_action(command: SvcAction) -> IpcResponse {\n    log::trace!(\"Processing action: {:?}\", command);\n    match _process_action(command).await {\n        Ok(()) => IpcResponse::Success,\n        Err(err) => IpcResponse::Err(err.to_string()),\n    }\n}\n"
  },
  {
    "path": "src/service/enviroment.rs",
    "content": "use winreg::{\n    enums::{HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS},\n    RegKey,\n};\n\nuse crate::error::Result;\n\npub fn was_installed_using_msix() -> bool {\n    static CACHE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();\n    *CACHE.get_or_init(|| {\n        std::env::current_exe().is_ok_and(|p| p.with_file_name(\"AppxManifest.xml\").exists())\n    })\n}\n\npub fn open_machine_enviroment() -> Result<RegKey> {\n    let hkcr = RegKey::predef(HKEY_LOCAL_MACHINE);\n    let enviroment = hkcr.open_subkey_with_flags(\n        r\"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\",\n        KEY_ALL_ACCESS,\n    )?;\n    Ok(enviroment)\n}\n\n/// add the installation directory to the PATH environment variable\npub fn add_installation_dir_to_path() -> Result<()> {\n    if was_installed_using_msix() {\n        return Ok(());\n    }\n\n    let enviroment = open_machine_enviroment()?;\n    let paths: String = enviroment.get_value(\"Path\")?;\n    let mut paths: Vec<String> = paths.split(';').map(|s| s.to_owned()).collect();\n\n    let install_folder = std::env::current_exe()?\n        .parent()\n        .expect(\"Failed to get parent directory\")\n        .to_string_lossy()\n        .to_string();\n\n    if !paths.contains(&install_folder) {\n        log::trace!(\"Adding installation directory to PATH environment variable\");\n        paths.push(install_folder);\n        enviroment.set_value(\"Path\", &paths.join(\";\"))?;\n    }\n    Ok(())\n}\n\n/// remove the installation directory from the PATH environment variable\npub fn remove_installation_dir_from_path() -> Result<()> {\n    if was_installed_using_msix() {\n        return Ok(());\n    }\n\n    let enviroment = open_machine_enviroment()?;\n    let paths: String = enviroment.get_value(\"Path\")?;\n    let mut paths: Vec<String> = paths.split(';').map(|s| s.to_owned()).collect();\n\n    let install_folder = std::env::current_exe()?\n        .parent()\n        .expect(\"Failed to get parent directory\")\n        .to_string_lossy()\n        .to_string();\n\n    if paths.contains(&install_folder) {\n        log::trace!(\"Removing installation directory from PATH environment variable\");\n        paths.retain(|p| p != &install_folder);\n        enviroment.set_value(\"Path\", &paths.join(\";\"))?;\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/service/error.rs",
    "content": "macro_rules! define_app_errors {\n    ($(\n        $variant:ident($error_type:ty);\n    )*) => {\n        $(\n            impl From<$error_type> for ServiceError {\n                fn from(err: $error_type) -> Self {\n                    let backtrace = backtrace::Backtrace::new();\n                    ServiceError { msg: format!(\"{}({:?})\", stringify!($variant), err), backtrace }\n                }\n            }\n        )*\n    };\n}\n\n#[macro_export]\nmacro_rules! log_error {\n    ($result:expr) => {\n        if let Err(err) = $result {\n            log::error!(\"{:?}\", err);\n        }\n    };\n    ($result:expr, $context:expr) => {\n        if let Err(err) = $result {\n            log::error!(\"Context: {:?} Err: {:?}\", $context, err);\n        }\n    };\n}\n\npub struct ServiceError {\n    msg: String,\n    backtrace: backtrace::Backtrace,\n}\n\ndefine_app_errors!(\n    Custom(String);\n    Io(std::io::Error);\n    Windows(windows::core::Error);\n    SerdeJson(serde_json::Error);\n    Logger(log::SetLoggerError);\n    WideStringNull(widestring::error::MissingNulTerminator);\n    SluIpc(slu_ipc::error::Error);\n    WinHotkeys(win_hotkeys::error::WHKError);\n    TimeFormat(time::error::InvalidFormatDescription);\n    TimeOffset(time::error::IndeterminateOffset);\n    Positioning(positioning::error::Error);\n);\n\nimpl std::fmt::Debug for ServiceError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.msg)?;\n\n        let frames = self.backtrace.frames();\n        if !frames.is_empty() {\n            writeln!(f)?;\n        }\n\n        let mut index = 0;\n        for frame in frames {\n            for symbol in frame.symbols() {\n                let name = match symbol.name() {\n                    Some(name) => name.to_string(),\n                    None => continue,\n                };\n\n                // skip backtrace traces\n                if name.starts_with(\"backtrace\") {\n                    continue;\n                }\n\n                // 2) skip trace of other modules/libraries specially tracing of tao and tauri libs\n                if !name.starts_with(\"slu_service\") {\n                    index += 1;\n                    continue;\n                }\n\n                writeln!(f, \"    {index}: {name}\")?;\n                if let Some(file) = symbol.filename() {\n                    write!(f, \"        at: \\\"{}\", file.to_string_lossy())?;\n                    if let Some(line) = symbol.lineno() {\n                        write!(f, \":{line}\")?;\n                        if let Some(col) = symbol.colno() {\n                            write!(f, \":{col}\")?;\n                        }\n                    }\n                    writeln!(f, \"\\\"\")?;\n                } else {\n                    writeln!(f, \"    at: <unknown>\")?\n                }\n\n                index += 1;\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl std::fmt::Display for ServiceError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{self:?}\")\n    }\n}\n\nimpl From<&str> for ServiceError {\n    fn from(err: &str) -> Self {\n        err.to_owned().into()\n    }\n}\n\nimpl From<std::process::Output> for ServiceError {\n    fn from(output: std::process::Output) -> Self {\n        if !output.stderr.is_empty() {\n            let (cow, _used, _has_errors) = encoding_rs::GBK.decode(&output.stderr);\n            cow.to_string().into()\n        } else {\n            let (cow, _used, _has_errors) = encoding_rs::GBK.decode(&output.stdout);\n            cow.to_string().into()\n        }\n    }\n}\n\npub trait WindowsResultExt {\n    /// Call this when convertion a `BOOL` into a result using the win32 crate `BOOL::ok()`\n    ///\n    /// For some reason `BOOL` is 0 that means failure, but the error code in the `Result` is `0`\n    /// and message is `succesfully completed`\n    ///\n    /// Warn: Be careful when using this like win32 api documentation sometimes expect this type of behaviours...\n    fn filter_fake_error(self) -> core::result::Result<(), windows::core::Error>;\n}\n\nimpl WindowsResultExt for core::result::Result<(), windows::core::Error> {\n    fn filter_fake_error(self) -> core::result::Result<(), windows::core::Error> {\n        match self {\n            Ok(_) => Ok(()),\n            Err(error) => {\n                if error.code().is_ok() {\n                    // log::warn!(\"(maybe?) fake win32 error, was skipped: {:?}\", error);\n                    Ok(())\n                } else {\n                    Err(error)\n                }\n            }\n        }\n    }\n}\n\npub type Result<T = ()> = core::result::Result<T, ServiceError>;\n"
  },
  {
    "path": "src/service/hotkeys.rs",
    "content": "use seelen_core::state::{shortcuts::SluHotkeyAction, Settings};\nuse slu_ipc::{messages::AppMessage, AppIpc};\nuse win_hotkeys::{\n    error::WHKError, events::KeyboardInputEvent, Hotkey, HotkeyManager, TriggerTiming, VKey,\n};\n\nuse crate::{\n    app_management::kill_all_seelen_ui_processes, error::Result, exit, get_async_handler, log_error,\n};\n\npub fn start_app_shortcuts(settings: &Settings) -> Result<()> {\n    if let Err(err) = HotkeyManager::start_keyboard_capturing() {\n        match err {\n            WHKError::AlreadyStarted => {}\n            others => return Err(others.into()),\n        }\n    };\n\n    let mut manager = HotkeyManager::current();\n    manager.unregister_all()?; // delete previously registered shortcuts\n\n    'registration: for slu_hotkey in &settings.shortcuts.app_commands {\n        if let Some(attached) = &slu_hotkey.attached_to {\n            if !settings.is_widget_enabled(attached) {\n                continue 'registration;\n            }\n        }\n\n        if slu_hotkey.keys.is_empty() {\n            continue 'registration;\n        }\n\n        let mut vkeys = Vec::new();\n        for key in &slu_hotkey.keys {\n            let vkey = match VKey::from_keyname(key) {\n                Ok(vkey) => vkey,\n                Err(e) => {\n                    log::warn!(\"Failed to parse shortcut {:?} error: {e}\", slu_hotkey.keys);\n                    continue 'registration;\n                }\n            };\n            vkeys.push(vkey);\n        }\n\n        let action = slu_hotkey.action;\n        let mut hotkey = Hotkey::from_keys(&vkeys).action(move || {\n            log::trace!(\"Hotkey triggered: {action:?}\");\n            match action {\n                SluHotkeyAction::MiscForceRestart => {\n                    log_error!(kill_all_seelen_ui_processes());\n                }\n                SluHotkeyAction::MiscForceQuit => {\n                    crate::EXITING.store(true, std::sync::atomic::Ordering::SeqCst);\n                    log_error!(kill_all_seelen_ui_processes());\n                    exit(0);\n                }\n                _ => {}\n            }\n\n            if let Some(command) = hotkey_action_to_cli_command(action) {\n                get_async_handler().spawn(async move {\n                    log_error!(AppIpc::send(AppMessage::Cli(command)).await);\n                });\n            }\n        });\n\n        if vkeys.len() == 1 {\n            hotkey.trigger_timing = TriggerTiming::OnKeyUp;\n            hotkey.strict_sequence = true;\n        }\n\n        log_error!(manager.register_hotkey(hotkey), slu_hotkey);\n    }\n    Ok(())\n}\n\npub fn stop_app_shortcuts() {\n    HotkeyManager::stop_keyboard_capturing();\n}\n\npub async fn start_shortcut_registration() -> Result<()> {\n    let hkm = HotkeyManager::current();\n\n    let handle = tokio::runtime::Handle::current();\n    let on_free_keyboard = move || {\n        handle.spawn(async {\n            let _ = send_registering_to_app(None).await;\n        });\n        HotkeyManager::current().remove_global_keyboard_listener();\n    };\n\n    let handle = tokio::runtime::Handle::current();\n    let on_keyboard_event = move |event| {\n        handle.spawn(async {\n            match event {\n                KeyboardInputEvent::KeyDown { key, state } => {\n                    if key == VKey::Escape {\n                        return;\n                    }\n                    let keys = state.pressing.iter().map(|vkey| vkey.to_string()).collect();\n                    let _ = send_registering_to_app(Some(keys)).await;\n                }\n                KeyboardInputEvent::KeyUp { .. } => {}\n            }\n        });\n    };\n\n    send_registering_to_app(Some(vec![])).await?;\n    hkm.steal_keyboard(on_free_keyboard);\n    hkm.set_global_keyboard_listener(on_keyboard_event);\n    Ok(())\n}\n\npub async fn stop_shortcut_registration() -> Result<()> {\n    HotkeyManager::current().free_keyboard();\n    Ok(())\n}\n\nasync fn send_registering_to_app(hotkey: Option<Vec<String>>) -> Result<()> {\n    AppIpc::send(AppMessage::Cli(vec![\n        \"popup\".to_owned(),\n        \"internal-set-shortcut\".to_owned(),\n        serde_json::to_string(&hotkey)?,\n    ]))\n    .await?;\n    Ok(())\n}\n\n/// Helper macro to create a command vector with optional conditional flags\n///\n/// Usage:\n/// - Simple command: `cmd![\"wm\", \"focus\", \"up\"]`\n/// - With variables: `cmd![\"vd\", \"switch\", index]`\n/// - With conditional flags: `cmd![\"task\", \"run\"; verbose => \"--verbose\", debug => \"--debug\"]`\n/// - Mixed: `cmd![\"app\", value; flag1 => \"--flag1\", cond2 => format!(\"--opt={}\", val)]`\n///\n/// Examples:\n/// ```\n/// cmd![\"settings\"]\n/// cmd![\"vd\", \"switch-workspace\", 3]\n/// cmd![\"task-switcher\", \"select-next\"; select_on_key_up => \"--auto-confirm\"]\n/// cmd![\"wm\", \"run\"; verbose => \"--verbose\", debug => \"--debug\", force => \"--force\"]\n/// ```\nmacro_rules! cmd {\n    // Simple case: just arguments, no conditional flags\n    ($($arg:expr),+ $(,)?) => {\n        vec![$($arg.to_string()),+]\n    };\n    // With conditional flags\n    ($($base:expr),+ ; $($cond:expr => $flag:expr),+ $(,)?) => {\n        {\n            let mut v = vec![$($base.to_string()),+];\n            $(\n                if $cond {\n                    v.push($flag.to_string());\n                }\n            )+\n            v\n        }\n    };\n}\n\nfn hotkey_action_to_cli_command(action: SluHotkeyAction) -> Option<Vec<String>> {\n    use SluHotkeyAction::*;\n\n    let command = match action {\n        // task switcher\n        TaskNext { select_on_key_up } => {\n            // todo: change for \"widget\", \"trigger\",\n            cmd![\"task-switcher\", \"select-next-task\"; select_on_key_up => \"--auto-confirm\"]\n        }\n        TaskPrev { select_on_key_up } => {\n            // todo: change for \"widget\", \"trigger\",\n            cmd![\"task-switcher\", \"select-previous-task\"; select_on_key_up => \"--auto-confirm\"]\n        }\n        // Virtual Desktop\n        SwitchToNextWorkspace => cmd![\"vd\", \"switch-next\"],\n        SwitchToPreviousWorkspace => cmd![\"vd\", \"switch-prev\"],\n        SwitchWorkspace { index } => cmd![\"vd\", \"switch-workspace\", index],\n        MoveToWorkspace { index } => cmd![\"vd\", \"move-to-workspace\", index],\n        SendToWorkspace { index } => cmd![\"vd\", \"send-to-workspace\", index],\n        CreateNewWorkspace => cmd![\"vd\", \"create-new-workspace\"],\n        DestroyCurrentWorkspace => cmd![\"vd\", \"destroy-current-workspace\"],\n        ToggleWorkspacesView => cmd![\"vd\", \"toggle-workspaces-view\"],\n        // apps menu\n        ToggleAppsMenu => cmd![\"widget\", \"trigger\", \"@seelen/apps-menu\"],\n        // wallpaper manager\n        CycleWallpaperNext => cmd![\"wallpaper\", \"next\"],\n        CycleWallpaperPrev => cmd![\"wallpaper\", \"prev\"],\n        // Weg\n        StartWegApp { index } => cmd![\"weg\", \"foreground-or-run-app\", index],\n        // Window Manager\n        IncreaseWidth => cmd![\"wm\", \"width\", \"increase\"],\n        DecreaseWidth => cmd![\"wm\", \"width\", \"decrease\"],\n        IncreaseHeight => cmd![\"wm\", \"height\", \"increase\"],\n        DecreaseHeight => cmd![\"wm\", \"height\", \"decrease\"],\n        RestoreSizes => cmd![\"wm\", \"reset-workspace-size\"],\n        // Window Manger focused window sizing\n        FocusTop => cmd![\"wm\", \"focus\", \"up\"],\n        FocusBottom => cmd![\"wm\", \"focus\", \"down\"],\n        FocusLeft => cmd![\"wm\", \"focus\", \"left\"],\n        FocusRight => cmd![\"wm\", \"focus\", \"right\"],\n        // Window Manager focused window positioning\n        MoveWindowUp => cmd![\"wm\", \"move\", \"up\"],\n        MoveWindowDown => cmd![\"wm\", \"move\", \"down\"],\n        MoveWindowLeft => cmd![\"wm\", \"move\", \"left\"],\n        MoveWindowRight => cmd![\"wm\", \"move\", \"right\"],\n        // Tiling window manager reservation\n        ReserveTop => cmd![\"wm\", \"reserve\", \"top\"],\n        ReserveBottom => cmd![\"wm\", \"reserve\", \"bottom\"],\n        ReserveLeft => cmd![\"wm\", \"reserve\", \"left\"],\n        ReserveRight => cmd![\"wm\", \"reserve\", \"right\"],\n        ReserveFloat => cmd![\"wm\", \"reserve\", \"float\"],\n        ReserveStack => cmd![\"wm\", \"reserve\", \"stack\"],\n        // Tiling window manager state\n        PauseTiling => cmd![\"wm\", \"toggle\"],\n        ToggleMonocle => cmd![\"wm\", \"toggle-monocle\"],\n        ToggleFloat => cmd![\"wm\", \"toggle-float\"],\n        CycleStackNext => cmd![\"wm\", \"cycle-stack\", \"next\"],\n        CycleStackPrev => cmd![\"wm\", \"cycle-stack\", \"prev\"],\n        // others\n        MiscOpenSettings => cmd![\"settings\"],\n        MiscToggleLockTracing => cmd![\"debug\", \"toggle-trace-lock\"],\n        MiscToggleWinEventTracing => cmd![\"debug\", \"toggle-win-events\"],\n        // no command needed\n        _ => return None,\n    };\n\n    Some(command)\n}\n"
  },
  {
    "path": "src/service/logger.rs",
    "content": "use std::sync::LazyLock;\n\nuse owo_colors::OwoColorize;\nuse windows::Win32::UI::Shell::FOLDERID_LocalAppData;\n\nuse crate::{error::Result, windows_api::WindowsApi};\n\npub struct SluServiceLogger {}\n\nfn format_now() -> String {\n    static FMT: LazyLock<Vec<time::format_description::BorrowedFormatItem>> = LazyLock::new(|| {\n        time::format_description::parse(\"[[[year]-[month]-[day]][[[hour]:[minute]:[second]]\")\n            .expect(\"valid time format\")\n    });\n\n    time::OffsetDateTime::now_local()\n        .unwrap_or_else(|_| time::OffsetDateTime::now_utc())\n        .format(&FMT)\n        .unwrap_or_else(|_| \"?time?\".to_owned())\n}\n\nimpl SluServiceLogger {\n    const MAX_LOG_SIZE: u64 = 5 * 1024 * 1024; // 5MB\n\n    /// Legacy cleanup: removes old Windows Event Log registry entries if present.\n    /// No-op in the current fern-based implementation.\n    pub fn uninstall_old_logging() -> Result<()> {\n        Ok(())\n    }\n\n    pub fn register_panic_hook() {\n        let base_hook = std::panic::take_hook();\n        std::panic::set_hook(Box::new(move |info| {\n            let cause = info\n                .payload()\n                .downcast_ref::<String>()\n                .map(|s| s.to_string())\n                .unwrap_or_else(|| {\n                    info.payload()\n                        .downcast_ref::<&str>()\n                        .unwrap_or(&\"<cause unknown>\")\n                        .to_string()\n                });\n\n            let mut string_location = String::from(\"<location unknown>\");\n            if let Some(location) = info.location() {\n                string_location = format!(\n                    \"{}:{}:{}\",\n                    location.file(),\n                    location.line(),\n                    location.column()\n                );\n            }\n\n            log::error!(\"A panic occurred:\\n  Cause: {cause}\\n  Location: {string_location}\");\n            base_hook(info);\n        }));\n    }\n\n    pub fn init() -> Result<()> {\n        let logs_folder =\n            WindowsApi::known_folder(FOLDERID_LocalAppData)?.join(\"com.seelen.seelen-ui/logs\");\n        std::fs::create_dir_all(&logs_folder)?;\n\n        let log_path = logs_folder.join(\"SLU Service.log\");\n        if log_path.exists() {\n            let metadata = std::fs::metadata(&log_path)?;\n            if metadata.len() > Self::MAX_LOG_SIZE {\n                let bak_path = logs_folder.join(\"SLU Service.log.bak\");\n                std::fs::rename(&log_path, &bak_path)?;\n            }\n        }\n\n        let file_dispatch = fern::Dispatch::new()\n            .format(|out, message, record| {\n                out.finish(format_args!(\n                    \"{}[{}][{}] {}\",\n                    format_now(),\n                    record.level(),\n                    record.target(),\n                    message\n                ))\n            })\n            .chain(fern::log_file(log_path)?);\n\n        let dispatch = fern::Dispatch::new()\n            .level(log::LevelFilter::Trace)\n            .chain(file_dispatch);\n\n        #[cfg(debug_assertions)]\n        let dispatch = dispatch.chain(\n            fern::Dispatch::new()\n                .format(|out, message, record| {\n                    out.finish(format_args!(\n                        \"[{}][{}] {}\",\n                        match record.level() {\n                            log::Level::Error => \"ERROR\".to_string(),\n                            log::Level::Warn => \"WARN~\".to_string(),\n                            log::Level::Info => \"INFO~\".to_string(),\n                            log::Level::Debug => \"DEBUG\".to_string(),\n                            log::Level::Trace => \"TRACE\".to_string(),\n                        },\n                        if record.level() == log::Level::Error {\n                            record\n                                .file()\n                                .map(|f| {\n                                    format!(\n                                        \"{}:{}\",\n                                        f.replace(\"\\\\\", \"/\"),\n                                        record.line().unwrap_or_default()\n                                    )\n                                })\n                                .unwrap_or_else(|| record.target().to_owned())\n                                .bright_red()\n                                .to_string()\n                        } else {\n                            record.target().bright_black().to_string()\n                        },\n                        message\n                    ))\n                })\n                .chain(std::io::stdout()),\n        );\n\n        dispatch.apply()?;\n        Self::register_panic_hook();\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/service/main.rs",
    "content": "// Prevents additional console window on Windows in release\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n\nmod app_management;\nmod cli;\nmod enviroment;\nmod error;\nmod hotkeys;\nmod logger;\nmod shutdown;\nmod string_utils;\nmod task_scheduler;\nmod windows_api;\n\nuse cli::handle_console_client;\nuse error::Result;\nuse itertools::Itertools;\nuse logger::SluServiceLogger;\nuse shutdown::restore_native_taskbar;\nuse slu_ipc::{AppIpc, ServiceIpc, IPC};\nuse std::sync::{atomic::AtomicBool, LazyLock, OnceLock};\nuse string_utils::WindowsString;\nuse task_scheduler::TaskSchedulerHelper;\nuse tokio::sync::mpsc::Sender;\nuse windows::Win32::{\n    Foundation::{GetLastError, ERROR_ALREADY_EXISTS},\n    Security::SE_TCB_NAME,\n    System::Threading::CreateMutexW,\n    UI::WindowsAndMessaging::SW_MINIMIZE,\n};\nuse windows_api::WindowsApi;\n\nuse crate::{app_management::launch_seelen_ui, hotkeys::stop_app_shortcuts};\n\npub static SERVICE_NAME: LazyLock<WindowsString> =\n    LazyLock::new(|| WindowsString::from_str(\"slu-service\"));\npub static SERVICE_DISPLAY_NAME: LazyLock<WindowsString> =\n    LazyLock::new(|| WindowsString::from_str(\"Seelen UI Service\"));\n\nstatic ASYNC_RUNTIME_HANDLE: OnceLock<tokio::runtime::Handle> = OnceLock::new();\n\nstatic EXIT_CHANNEL: OnceLock<Sender<u32>> = OnceLock::new();\n\npub static EXITING: AtomicBool = AtomicBool::new(false);\n\npub fn get_async_handler() -> tokio::runtime::Handle {\n    ASYNC_RUNTIME_HANDLE\n        .get()\n        .expect(\"Tokio runtime was not initialized\")\n        .clone()\n}\n\npub fn is_local_dev() -> bool {\n    cfg!(dev)\n}\n\npub fn is_development() -> bool {\n    cfg!(debug_assertions)\n}\n\npub fn exit(code: u32) {\n    EXITING.store(true, std::sync::atomic::Ordering::SeqCst);\n    if let Some(tx) = EXIT_CHANNEL.get() {\n        let tx = tx.clone();\n        get_async_handler().spawn(async move {\n            if tx.send(code).await.is_err() {\n                log::warn!(\"Exit channel closed before exit signal could be sent (code={code})\");\n            }\n        });\n    } else {\n        log::error!(\"exit() called before EXIT_CHANNEL was initialized, forcing process exit\");\n        std::process::exit(code as i32);\n    }\n}\n\npub fn setup() -> Result<()> {\n    WindowsApi::set_process_dpi_aware()?;\n    WindowsApi::enable_privilege(SE_TCB_NAME)?;\n    ServiceIpc::start(crate::cli::processing::process_action)?;\n\n    if !AppIpc::can_stablish_connection() {\n        WindowsApi::wait_for_native_shell();\n        log_error!(launch_seelen_ui());\n    }\n\n    std::thread::sleep(std::time::Duration::from_secs(2));\n    crate::app_management::start_app_monitoring();\n    Ok(())\n}\n\n// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexw\nfn is_svc_already_running() -> bool {\n    unsafe {\n        let session_id = WindowsApi::current_session_id();\n        let mutex_name = format!(\"Local\\\\Seelen-UI-Service-Instance-{}\", session_id);\n        let mutex_name_wide = WindowsString::from_str(&mutex_name);\n\n        // Try to create a named mutex specific to the current session\n        let Ok(handle) = CreateMutexW(None, true, mutex_name_wide.as_pcwstr()) else {\n            // Failed to create mutex, assume not running to be safe\n            log::warn!(\"Failed to create service instance mutex, proceeding anyway\");\n            return false;\n        };\n\n        // if mutex existed before, another instance is already running for this session\n        let last_error = GetLastError();\n        if last_error == ERROR_ALREADY_EXISTS {\n            return true;\n        }\n\n        // This is the first instance for this session\n        // Keep the handle alive by leaking it (will be released when process exits)\n        Box::leak(Box::new(handle));\n        false\n    }\n}\n\n#[tokio::main]\nasync fn main() {\n    if is_local_dev() {\n        let window = WindowsApi::get_console_window();\n        let _ = WindowsApi::show_window(window.0 as _, SW_MINIMIZE.0);\n    }\n\n    if let Err(err) = SluServiceLogger::init() {\n        let fallback = std::env::temp_dir().join(\"slu-service-logger-error.log\");\n        let _ = std::fs::write(&fallback, format!(\"Failed to initialize logger: {err:?}\"));\n        std::process::exit(1);\n    }\n\n    if let Err(err) = handle_console_client().await {\n        log::error!(\"Failed to execute command: {err:?}\");\n        std::process::exit(1);\n    }\n\n    if is_svc_already_running() {\n        println!(\"Seelen UI Service is already running\");\n        return;\n    }\n\n    ASYNC_RUNTIME_HANDLE\n        .set(tokio::runtime::Handle::current())\n        .expect(\"Failed to set runtime handle\");\n\n    let (tx, mut rx) = tokio::sync::mpsc::channel(1);\n    EXIT_CHANNEL.set(tx).unwrap();\n\n    if let Err(err) = TaskSchedulerHelper::create_service_task() {\n        log::error!(\"Failed to create service task: {err:?}\");\n        std::process::exit(1);\n    }\n\n    let version = env!(\"CARGO_PKG_VERSION\");\n    let debug = if is_development() { \" (debug)\" } else { \"\" };\n    let local = if is_local_dev() { \" (local)\" } else { \"\" };\n    log::info!(\"──────────────────────────────────────────────────-\");\n    log::info!(\"Starting Seelen UI Service v{version}{local}{debug}\");\n    log::info!(\"Arguments: {:?}\", std::env::args().collect_vec());\n\n    if let Err(err) = setup() {\n        log::error!(\"Service setup failed: {:?}\", err);\n        // Run cleanup even on setup failure so the taskbar/hotkeys are restored\n        log_error!(restore_native_taskbar());\n        stop_app_shortcuts();\n        std::process::exit(1);\n    };\n\n    // ===================== wait for stop signal ====================\n    let exit_code = rx.recv().await.unwrap_or_default();\n\n    // shutdown tasks:\n    log_error!(restore_native_taskbar());\n    stop_app_shortcuts();\n    log::info!(\"Seelen UI Service exited with code {exit_code}\");\n    std::process::exit(exit_code as i32);\n}\n"
  },
  {
    "path": "src/service/shutdown.rs",
    "content": "use windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::SW_SHOWNORMAL};\n\nuse crate::{\n    error::Result,\n    windows_api::{\n        app_bar::{AppBarData, AppBarDataState},\n        iterator::WindowEnumerator,\n        WindowsApi,\n    },\n};\n\npub fn get_taskbars_handles() -> Result<Vec<HWND>> {\n    let mut founds = Vec::new();\n    WindowEnumerator::new().for_each(|hwnd| {\n        let class = WindowsApi::get_class(hwnd);\n        if (class == \"Shell_TrayWnd\" || class == \"Shell_SecondaryTrayWnd\")\n            && WindowsApi::get_title(hwnd).is_empty()\n        {\n            founds.push(hwnd);\n        }\n    })?;\n    Ok(founds)\n}\n\npub fn restore_native_taskbar() -> Result<()> {\n    for hwnd in get_taskbars_handles()? {\n        AppBarData::from_handle(hwnd).set_state(AppBarDataState::AlwaysOnTop);\n        WindowsApi::show_window_async(hwnd.0 as isize, SW_SHOWNORMAL.0)?;\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/service/string_utils.rs",
    "content": "use std::os::windows::ffi::{OsStrExt, OsStringExt};\n\nuse windows_core::{BSTR, PCWSTR, PWSTR};\n\n#[derive(Debug, Clone)]\npub struct WindowsString {\n    pub inner: Vec<u16>,\n}\n\nimpl WindowsString {\n    pub fn new_to_fill(capacity: usize) -> Self {\n        Self {\n            inner: vec![0; capacity],\n        }\n    }\n\n    pub fn from_str<S: AsRef<str>>(s: S) -> Self {\n        Self {\n            inner: s.as_ref().encode_utf16().chain(Some(0)).collect(),\n        }\n    }\n\n    pub fn from_os_string<S: AsRef<std::ffi::OsStr>>(s: S) -> Self {\n        Self {\n            inner: s.as_ref().encode_wide().chain(Some(0)).collect(),\n        }\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner\n            .iter()\n            .position(|c| *c == 0)\n            .expect(\"Invalid UTF16 Windows String\")\n    }\n\n    pub fn as_slice(&self) -> &[u16] {\n        &self.inner\n    }\n\n    pub fn as_mut_slice(&mut self) -> &mut [u16] {\n        &mut self.inner\n    }\n\n    // pcwstr: pointer constant wide string\n    pub fn as_pcwstr(&self) -> PCWSTR {\n        PCWSTR(self.inner.as_ptr())\n    }\n\n    // pwstr: pointer wide string\n    pub fn as_pwstr(&mut self) -> PWSTR {\n        PWSTR(self.inner.as_mut_ptr())\n    }\n\n    pub fn to_bstr(&self) -> BSTR {\n        BSTR::from_wide(&self.inner[..self.len()])\n    }\n\n    pub fn to_os_string(&self) -> std::ffi::OsString {\n        std::ffi::OsString::from_wide(&self.inner[..self.len()])\n    }\n}\n\nimpl std::fmt::Display for WindowsString {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            String::from_utf16_lossy(&self.inner).trim_end_matches(\"\\0\")\n        )\n    }\n}\n\nimpl From<&str> for WindowsString {\n    fn from(value: &str) -> Self {\n        Self::from_str(value)\n    }\n}\n\nimpl From<String> for WindowsString {\n    fn from(value: String) -> Self {\n        Self::from_str(value)\n    }\n}\n\nimpl From<&String> for WindowsString {\n    fn from(value: &String) -> Self {\n        Self::from_str(value)\n    }\n}\n"
  },
  {
    "path": "src/service/task_scheduler.rs",
    "content": "use windows::Win32::{\n    Foundation::{VARIANT_FALSE, VARIANT_TRUE},\n    System::TaskScheduler::{\n        IExecAction2, ITaskFolder, ITaskService, TaskScheduler, TASK_ACTION_EXEC,\n        TASK_CREATE_OR_UPDATE, TASK_LOGON_INTERACTIVE_TOKEN, TASK_RUNLEVEL_HIGHEST,\n        TASK_TRIGGER_LOGON,\n    },\n    UI::Shell::FOLDERID_LocalAppData,\n};\nuse windows_core::{Interface, BSTR};\n\nuse crate::{\n    enviroment::was_installed_using_msix,\n    error::Result,\n    windows_api::{com::Com, WindowsApi},\n};\n\npub struct TaskSchedulerHelper {}\n\nstatic GROUP_FOLDER: &str = \"\\\\Seelen\";\nstatic OLD_APP_TASK_NAME: &str = \"Seelen-UI\";\nstatic SERVICE_TASK_NAME: &str = \"Seelen UI Service\";\n\nimpl TaskSchedulerHelper {\n    unsafe fn get_task_service() -> Result<ITaskService> {\n        let task_service: ITaskService = Com::create_instance(&TaskScheduler)?;\n        task_service.Connect(\n            &Default::default(),\n            &Default::default(),\n            &Default::default(),\n            &Default::default(),\n        )?;\n        Ok(task_service)\n    }\n\n    unsafe fn register_task(folder: &ITaskFolder, task_name: &str, task_xml: &BSTR) -> Result<()> {\n        folder.RegisterTask(\n            &task_name.into(),\n            task_xml,\n            TASK_CREATE_OR_UPDATE.0,\n            &Default::default(),\n            &Default::default(),\n            TASK_LOGON_INTERACTIVE_TOKEN,\n            &Default::default(),\n        )?;\n        log::debug!(\"Registered task {}\", task_name);\n        Ok(())\n    }\n\n    /// this task handles the startup of the service and the app on login\n    pub fn create_service_task() -> Result<()> {\n        log::debug!(\"Creating service task...\");\n        let service_path = if was_installed_using_msix() {\n            WindowsApi::known_folder(FOLDERID_LocalAppData)?\n                .join(\"Microsoft\\\\WindowsApps\\\\slu-service.exe\")\n        } else {\n            std::env::current_exe()?\n        };\n        Com::run_with_context(|| unsafe {\n            let task_service = Self::get_task_service()?;\n            // remove old task as backwards compatibility\n            let mut old_task = None;\n            if let Ok(seelen_folder) = task_service.GetFolder(&GROUP_FOLDER.into()) {\n                let _ = seelen_folder.DeleteTask(&OLD_APP_TASK_NAME.into(), 0);\n                old_task = seelen_folder.GetTask(&OLD_APP_TASK_NAME.into()).ok();\n            };\n            let root_folder = task_service.GetFolder(&\"\\\\\".into())?;\n\n            let task = task_service.NewTask(0)?;\n            task.Principal()?.SetRunLevel(TASK_RUNLEVEL_HIGHEST)?;\n\n            let settings = task.Settings()?;\n            settings.SetPriority(2)?;\n            settings.SetHidden(VARIANT_TRUE)?;\n            settings.SetAllowDemandStart(VARIANT_TRUE)?;\n            settings.SetDisallowStartIfOnBatteries(VARIANT_FALSE)?;\n            settings.SetStopIfGoingOnBatteries(VARIANT_FALSE)?;\n\n            let triggers = task.Triggers()?;\n            if let Some(old) = old_task {\n                let old_triggers = old.Definition()?.Triggers()?;\n                task.SetTriggers(&old_triggers)?;\n            } else {\n                triggers.Create(TASK_TRIGGER_LOGON)?;\n            }\n\n            let actions = task.Actions()?;\n            let exec_action: IExecAction2 = actions.Create(TASK_ACTION_EXEC)?.cast()?;\n            exec_action.SetPath(&service_path.to_string_lossy().to_string().into())?;\n            exec_action.SetArguments(&\"--startup\".into())?;\n\n            let mut task_xml = BSTR::new();\n            task.XmlText(&mut task_xml)?;\n            Self::register_task(\n                &root_folder,\n                &format!(\"{GROUP_FOLDER}\\\\{SERVICE_TASK_NAME}\"),\n                &task_xml,\n            )?;\n            Ok(())\n        })\n    }\n\n    pub fn set_run_on_logon(enabled: bool) -> Result<()> {\n        Com::run_with_context(|| unsafe {\n            let task_service = Self::get_task_service()?;\n            let seelen_folder = task_service.GetFolder(&GROUP_FOLDER.into())?;\n            let task = seelen_folder.GetTask(&SERVICE_TASK_NAME.into())?;\n            let task = task.Definition()?;\n            let triggers = task.Triggers()?;\n            triggers.Clear()?;\n            if enabled {\n                triggers.Create(TASK_TRIGGER_LOGON)?;\n            }\n            let mut task_xml = BSTR::new();\n            task.XmlText(&mut task_xml)?;\n            Self::register_task(&seelen_folder, SERVICE_TASK_NAME, &task_xml)?;\n            Ok(())\n        })\n    }\n\n    pub fn remove_service_task() -> Result<()> {\n        Com::run_with_context(|| unsafe {\n            let task_service: ITaskService = Com::create_instance(&TaskScheduler)?;\n            task_service.Connect(\n                &Default::default(),\n                &Default::default(),\n                &Default::default(),\n                &Default::default(),\n            )?;\n            if let Ok(seelen_folder) = task_service.GetFolder(&GROUP_FOLDER.into()) {\n                let _ = seelen_folder.DeleteTask(&SERVICE_TASK_NAME.into(), 0);\n            }\n            Ok(())\n        })\n    }\n}\n"
  },
  {
    "path": "src/service/windows_api/app_bar.rs",
    "content": "use windows::Win32::{\n    Foundation::{HWND, LPARAM},\n    UI::Shell::{\n        SHAppBarMessage, ABE_BOTTOM, ABE_LEFT, ABE_RIGHT, ABE_TOP, ABM_SETSTATE, ABS_ALWAYSONTOP,\n        ABS_AUTOHIDE, APPBARDATA,\n    },\n};\n\n#[allow(dead_code)]\npub enum AppBarDataEdge {\n    Left = ABE_LEFT as isize,\n    Top = ABE_TOP as isize,\n    Right = ABE_RIGHT as isize,\n    Bottom = ABE_BOTTOM as isize,\n}\n\n/// https://learn.microsoft.com/en-us/windows/win32/shell/abm-setstate#parameters\n#[derive(Debug, Clone, Copy)]\npub enum AppBarDataState {\n    BothOff = 0,\n    AutoHide = ABS_AUTOHIDE as isize,\n    AlwaysOnTop = ABS_ALWAYSONTOP as isize,\n    BothOn = 3,\n}\n\nimpl From<AppBarDataState> for LPARAM {\n    fn from(val: AppBarDataState) -> Self {\n        LPARAM(val as isize)\n    }\n}\n\nimpl From<u32> for AppBarDataState {\n    fn from(state: u32) -> Self {\n        match state {\n            0 => AppBarDataState::BothOff,\n            ABS_AUTOHIDE => AppBarDataState::AutoHide,\n            ABS_ALWAYSONTOP => AppBarDataState::AlwaysOnTop,\n            3 => AppBarDataState::BothOn,\n            _ => unreachable!(),\n        }\n    }\n}\n\npub struct AppBarData(pub APPBARDATA);\nimpl AppBarData {\n    pub fn from_handle(hwnd: HWND) -> Self {\n        Self(APPBARDATA {\n            cbSize: std::mem::size_of::<APPBARDATA>() as u32,\n            hWnd: hwnd,\n            ..Default::default()\n        })\n    }\n\n    pub fn set_state(&self, state: AppBarDataState) {\n        let mut data = self.0;\n        data.lParam = state.into();\n        unsafe { SHAppBarMessage(ABM_SETSTATE, &mut data) };\n    }\n}\n"
  },
  {
    "path": "src/service/windows_api/com.rs",
    "content": "use crate::error::Result;\nuse windows::{\n    core::{Interface, GUID},\n    Win32::System::Com::{\n        CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_APARTMENTTHREADED,\n    },\n};\n\npub struct Com {}\n\n#[allow(dead_code)]\nimpl Com {\n    fn initialize() -> Result<()> {\n        let result = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) };\n        if result.is_err() {\n            return Err(\"CoInitializeEx failed\".into());\n        }\n        Ok(())\n    }\n\n    pub fn create_instance<T>(class_id: &GUID) -> Result<T>\n    where\n        T: Interface,\n    {\n        Ok(unsafe { CoCreateInstance(class_id, None, CLSCTX_ALL)? })\n    }\n\n    /// Can panic if Com instances created between init and drop are still alive (no dropped yet)\n    fn uninitialize() {\n        unsafe { CoUninitialize() };\n    }\n\n    /// Will execute init and drop in a safe way, ensuring that all instances created between init and drop are dropped\n    pub fn run_with_context<F, T>(f: F) -> Result<T>\n    where\n        F: FnOnce() -> Result<T>,\n    {\n        Self::initialize()?;\n        let result = f();\n        Self::uninitialize();\n        result\n    }\n}\n"
  },
  {
    "path": "src/service/windows_api/iterator.rs",
    "content": "use windows::Win32::{\n    Foundation::{HWND, LPARAM},\n    UI::WindowsAndMessaging::{EnumChildWindows, EnumWindows},\n};\nuse windows_core::BOOL;\n\nuse crate::error::Result;\n\n#[derive(Debug, Clone)]\npub struct WindowEnumerator {\n    parent: Option<HWND>,\n}\n\nimpl WindowEnumerator {\n    pub fn new() -> Self {\n        Self { parent: None }\n    }\n\n    #[allow(dead_code)]\n    pub fn with_parent(mut self, parent: HWND) -> Self {\n        self.parent = Some(parent);\n        self\n    }\n\n    fn enumerate(\n        &self,\n        enum_proc: unsafe extern \"system\" fn(HWND, LPARAM) -> BOOL,\n        ptr: LPARAM,\n    ) -> Result<()> {\n        if let Some(parent) = self.parent {\n            unsafe { EnumChildWindows(Some(parent), Some(enum_proc), ptr).ok()? };\n        } else {\n            unsafe { EnumWindows(Some(enum_proc), ptr)? };\n        }\n        Ok(())\n    }\n\n    /// Will call the callback for each window while enumerating.\n    /// If enumeration fails it will return error.\n    pub fn for_each<F>(&self, cb: F) -> Result<()>\n    where\n        F: FnMut(HWND),\n    {\n        type ForEachCallback<'a> = Box<dyn FnMut(HWND) + 'a>;\n        let mut callback: ForEachCallback = Box::new(cb);\n\n        unsafe extern \"system\" fn enum_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {\n            if let Some(boxed) = (lparam.0 as *mut ForEachCallback).as_mut() {\n                (*boxed)(hwnd);\n            }\n            true.into()\n        }\n\n        self.enumerate(enum_proc, LPARAM(&mut callback as *mut _ as isize))\n    }\n}\n"
  },
  {
    "path": "src/service/windows_api/mod.rs",
    "content": "pub mod app_bar;\npub mod com;\npub mod iterator;\n\nuse std::{ffi::OsString, os::windows::ffi::OsStringExt, path::PathBuf};\n\nuse windows::Win32::{\n    Foundation::{HANDLE, HWND, LUID},\n    Security::{\n        AdjustTokenPrivileges, LookupPrivilegeValueW, SE_PRIVILEGE_ENABLED,\n        TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,\n    },\n    System::{\n        Console::GetConsoleWindow,\n        RemoteDesktop::ProcessIdToSessionId,\n        Threading::{\n            AttachThreadInput, GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId,\n            OpenProcessToken,\n        },\n    },\n    UI::{\n        HiDpi::{SetProcessDpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2},\n        Shell::{SHGetKnownFolderPath, KF_FLAG_DEFAULT},\n        WindowsAndMessaging::{\n            BringWindowToTop, FindWindowW, GetClassNameW, GetForegroundWindow, GetWindowTextW,\n            GetWindowThreadProcessId, IsIconic, SetWindowPos, ShowWindow, ShowWindowAsync,\n            SET_WINDOW_POS_FLAGS, SHOW_WINDOW_CMD, SWP_NOACTIVATE, SWP_NOZORDER, SW_RESTORE,\n        },\n    },\n};\nuse windows_core::{Owned, PCWSTR};\n\nuse crate::{\n    error::{Result, WindowsResultExt},\n    string_utils::WindowsString,\n};\n\npub struct WindowsApi;\n\nimpl WindowsApi {\n    /// Behaviour is undefined if an invalid HWND is given\n    /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid\n    pub fn window_thread_process_id(hwnd: HWND) -> (u32, u32) {\n        let mut process_id: u32 = 0;\n        let thread_id = unsafe {\n            GetWindowThreadProcessId(hwnd, Option::from(std::ptr::addr_of_mut!(process_id)))\n        };\n        (process_id, thread_id)\n    }\n\n    pub fn show_window(addr: isize, command: i32) -> Result<()> {\n        // BOOL is returned but does not signify whether or not the operation was succesful\n        // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow\n        unsafe { ShowWindow(HWND(addr as _), SHOW_WINDOW_CMD(command)) }\n            .ok()\n            .filter_fake_error()?;\n        Ok(())\n    }\n\n    pub fn show_window_async(addr: isize, command: i32) -> Result<()> {\n        // BOOL is returned but does not signify whether or not the operation was succesful\n        // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindowasync\n        unsafe { ShowWindowAsync(HWND(addr as _), SHOW_WINDOW_CMD(command)) }\n            .ok()\n            .filter_fake_error()?;\n        Ok(())\n    }\n\n    pub fn bring_to_top(hwnd: HWND) -> Result<()> {\n        unsafe { BringWindowToTop(hwnd)? };\n        Ok(())\n    }\n\n    pub fn attach_thread_input(thread_id: u32, attach_to: u32, attach: bool) -> Result<()> {\n        unsafe { AttachThreadInput(thread_id, attach_to, attach).ok()? };\n        Ok(())\n    }\n\n    pub fn is_iconic(hwnd: HWND) -> bool {\n        unsafe { IsIconic(hwnd).as_bool() }\n    }\n\n    pub fn get_foreground_window() -> HWND {\n        unsafe { GetForegroundWindow() }\n    }\n\n    pub fn set_foreground(addr: isize) -> Result<()> {\n        let hwnd = HWND(addr as _);\n        if Self::is_iconic(hwnd) {\n            Self::show_window(addr, SW_RESTORE.0)?;\n        }\n\n        let (_, focused_thread) = Self::window_thread_process_id(Self::get_foreground_window());\n        let app_thread = Self::current_thread_id();\n\n        let mut attached = false;\n        if focused_thread != app_thread {\n            attached = Self::attach_thread_input(focused_thread, app_thread, true).is_ok();\n        }\n\n        Self::bring_to_top(hwnd)?;\n\n        if attached {\n            Self::attach_thread_input(focused_thread, app_thread, false)?;\n        }\n        Ok(())\n    }\n\n    pub fn set_position(\n        hwnd: isize,\n        x: i32,\n        y: i32,\n        width: i32,\n        height: i32,\n        flags: u32,\n    ) -> Result<()> {\n        unsafe {\n            SetWindowPos(\n                HWND(hwnd as _),\n                None,\n                x,\n                y,\n                width,\n                height,\n                SET_WINDOW_POS_FLAGS(flags) | SWP_NOACTIVATE | SWP_NOZORDER,\n            )\n            .filter_fake_error()?;\n        }\n        Ok(())\n    }\n\n    pub fn set_process_dpi_aware() -> Result<()> {\n        unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)? };\n        Ok(())\n    }\n\n    pub fn get_console_window() -> HWND {\n        unsafe { GetConsoleWindow() }\n    }\n\n    pub fn current_process() -> HANDLE {\n        unsafe { GetCurrentProcess() }\n    }\n\n    pub fn current_process_id() -> u32 {\n        unsafe { GetCurrentProcessId() }\n    }\n\n    pub fn current_thread_id() -> u32 {\n        unsafe { GetCurrentThreadId() }\n    }\n\n    pub fn current_session_id() -> u32 {\n        let process_id = Self::current_process_id();\n        let mut session_id = 0;\n        // this should never fail for own process\n        unsafe { ProcessIdToSessionId(process_id, &mut session_id).expect(\"Can't get session id\") };\n        session_id\n    }\n\n    pub fn open_current_process_token() -> Result<Owned<HANDLE>> {\n        let mut token_handle = HANDLE::default();\n        unsafe {\n            OpenProcessToken(\n                Self::current_process(),\n                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,\n                &mut token_handle,\n            )?;\n\n            if token_handle.is_invalid() {\n                return Err(\"OpenProcessToken failed\".into());\n            }\n            Ok(Owned::new(token_handle))\n        }\n    }\n\n    pub fn get_luid(system: PCWSTR, name: PCWSTR) -> Result<LUID> {\n        let mut luid = LUID::default();\n        unsafe { LookupPrivilegeValueW(system, name, &mut luid)? };\n        Ok(luid)\n    }\n\n    pub fn enable_privilege(name: PCWSTR) -> Result<()> {\n        let token_handle = Self::open_current_process_token()?;\n        let mut tkp = TOKEN_PRIVILEGES {\n            PrivilegeCount: 1,\n            ..Default::default()\n        };\n\n        tkp.Privileges[0].Luid = Self::get_luid(PCWSTR::null(), name)?;\n        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n        unsafe { AdjustTokenPrivileges(*token_handle, false, Some(&tkp), 0, None, None)? };\n        Ok(())\n    }\n\n    // change to some crate like dirs to allow multiple platforms\n    pub fn known_folder(folder_id: windows::core::GUID) -> Result<PathBuf> {\n        let path = unsafe { SHGetKnownFolderPath(&folder_id, KF_FLAG_DEFAULT, None)? };\n        Ok(PathBuf::from(OsString::from_wide(unsafe {\n            path.as_wide()\n        })))\n    }\n\n    pub fn get_class(hwnd: HWND) -> String {\n        let mut text: [u16; 512] = [0; 512];\n        let len = unsafe { GetClassNameW(hwnd, &mut text) };\n        let length = usize::try_from(len).unwrap_or(0);\n        String::from_utf16_lossy(&text[..length])\n    }\n\n    pub fn get_title(hwnd: HWND) -> String {\n        let mut text: [u16; 512] = [0; 512];\n        let len = unsafe { GetWindowTextW(hwnd, &mut text) };\n        let length = usize::try_from(len).unwrap_or(0);\n        String::from_utf16_lossy(&text[..length])\n    }\n\n    pub fn wait_for_native_shell() {\n        log::info!(\"Waiting for native shell...\");\n        let mut attempt = 0;\n        let class = WindowsString::from_str(\"Shell_TrayWnd\");\n        unsafe {\n            // wait for native shell until 50 attempts or 5 seconds\n            while FindWindowW(class.as_pcwstr(), None).is_err() && attempt < 50 {\n                std::thread::sleep(std::time::Duration::from_millis(100));\n                attempt += 1;\n            }\n        }\n        if attempt >= 50 {\n            log::warn!(\"Native shell not found after 5 seconds, continuing anyway...\");\n        }\n        log::info!(\"Native shell found, continueing setup...\");\n    }\n}\n"
  },
  {
    "path": "src/static/apps_templates/adobe.yml",
    "content": "- name: Photoshop Dialogs\n  identifier:\n    id: PSDialogBox\n    kind: Class\n    matchingStrategy: Equals\n  options:\n    - unmanage\n"
  },
  {
    "path": "src/static/apps_templates/browser.yml",
    "content": "- name: Picture in Picture\n  identifier:\n    id: Picture-in-Picture\n    kind: Title\n    matchingStrategy: Equals\n  options:\n    - pinned\n"
  },
  {
    "path": "src/static/apps_templates/core.yml",
    "content": "- name: Base\n  identifier:\n    id: (?i)((install)|(setup)|(complete)|(menu)|(notification))\n    kind: Title\n    matchingStrategy: Regex\n  options:\n    - unmanage\n- name: Seelen\n  identifier:\n    id: seelen-ui.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n"
  },
  {
    "path": "src/static/apps_templates/development.yml",
    "content": "- name: Pinetry\n  identifier:\n    id: pinentry.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n- name: TaskManagerWindow\n  identifier:\n    id: TaskManagerWindow\n    kind: Class\n    matchingStrategy: Equals\n  options:\n    - float\n- name: AutoHotkeyUX.exe\n  identifier:\n    id: AutoHotkeyUX.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n"
  },
  {
    "path": "src/static/apps_templates/gaming.yml",
    "content": "- name: Steam\n  identifier:\n    id: (steamwebhelper.exe)|(steam.exe)\n    kind: Exe\n    matchingStrategy: Regex\n    and:\n      - id: Steam\n        kind: Title\n        matchingStrategy: Equals\n        negation: true\n  options:\n    - unmanage\n\n- name: Steam Apps/Games\n  identifier:\n    id: steamapps\n    kind: Path\n    matchingStrategy: Contains\n  options:\n    - unmanage\n"
  },
  {
    "path": "src/static/apps_templates/password_managers.yml",
    "content": "- name: 1Password.exe\n  identifier:\n    id: 1Password.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n\n- name: KeepassXC\n  identifier:\n    id: KeePassXC.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n"
  },
  {
    "path": "src/static/apps_templates/system.yml",
    "content": "- name: Snipping Tool\n  identifier:\n    id: SnippingTool.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n\n- name: Calculator\n  identifier:\n    id: Calculator\n    kind: Title\n    matchingStrategy: Equals\n  options:\n    - unmanage\n\n- name: Dropbox\n  identifier:\n    id: Dropbox.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n\n- name: One Drive\n  identifier:\n    id: OneDrive.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n\n- name: PowerToys ColorPickerUI\n  identifier:\n    id: PowerToys.ColorPickerUI.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n- name: PowerToys CropAndLock\n  identifier:\n    id: PowerToys.CropAndLock.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n- name: PowerToys ImageResizer\n  identifier:\n    id: PowerToys.ImageResizer.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n- name: PowerToys Peek UI\n  identifier:\n    id: PowerToys.Peek.UI.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n- name: PowerToys PowerLauncher\n  identifier:\n    id: PowerToys.PowerLauncher.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n- name: PowerToys PowerAccent\n  identifier:\n    id: PowerToys.PowerAccent.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n\n- name: System Background Apps\n  identifier:\n    id: Windows\\SystemApps\n    kind: Path\n    matchingStrategy: Contains\n  options:\n    - no-interactive\n    - unmanage\n"
  },
  {
    "path": "src/static/apps_templates/video-and-streaming.yml",
    "content": "- name: Zoom.exe\n  identifier:\n    id: Zoom.exe\n    kind: Exe\n    matchingStrategy: Equals\n  options:\n    - unmanage\n"
  },
  {
    "path": "src/static/plugins/tb_cpu_usage/i18n/display_name.yml",
    "content": "yo: Sipiyu Lilo\nko: CPU 사용량\neu: CPU erabilera\nde: CPU-Auslastung\nsi: CPU භාවිතය\nsw: Matumizi ya CPU\nzu: Ukusetshenziswa kwe-CPU\ncs: Využití CPU\nfi: CPU:n käyttö\nit: Utilizzo della CPU\nsv: CPU-användning\nro: Utilizarea CPU\nuk: Використання ЦП\nsk: Využitie CPU\nen: CPU Usage\nar: استخدام وحدة المعالجة المركزية\naz: CPU İstifadəsi\nhy: CPU-ի օգտագործումը\nfr: Utilisation du processeur\naf: SVE Gebruik\nvi: Sử dụng CPU\nsr: ЦПУ Усаге\nbg: Използване на процесора\nbn: CPU ব্যবহার\nkm: ការប្រើប្រាស់ស៊ីភីយូ\npl: Użycie procesora\nhi: सीपीयू उपयोग\net: CPU kasutamine\nms: Penggunaan CPU\nel: Χρήση CPU\nso: Isticmaalka CPU\nhr: Upotreba CPU-a\nta: CPU பயன்பாடு\nur: سی پی یو کا استعمال\nfa: استفاده از CPU\ntg: Истифодаи CPU\nmk: Употреба на процесорот\nne: CPU उपयोग\npt-PT: Uso da CPU\nja: CPU使用率\npt-BR: CPU Usage\nca: Ús de la CPU\nbs: Upotreba CPU-a\ngu: CPU વપરાશ\nhe: שימוש במעבד\nlb: CPU Benotzung\nmn: CPU-ийн хэрэглээ\nte: CPU వినియోగం\ntr: CPU Kullanımı\nzh-CN: 中央处理器使用率\nka: CPU გამოყენება\nku: Bikaranîna CPU\nhu: CPU használat\npa: CPU ਵਰਤੋਂ\nno: CPU-bruk\nda: CPU-brug\nlv: CPU lietojums\nzh-TW: 中央處理器使用率\nuz: CPU foydalanish\nth: การใช้งานซีพียู\nru: Использование ЦП\nis: CPU notkun\nps: د CPU کارول\nam: የሲፒዩ አጠቃቀም\nlo: ການນຳໃຊ້ CPU\ncy: Defnydd CPU\nes: Uso de CPU\nid: Penggunaan CPU\nmt: Użu tas-CPU\nnl: CPU-gebruik\nlt: CPU naudojimas\ntl: Paggamit ng CPU\n"
  },
  {
    "path": "src/static/plugins/tb_cpu_usage/metadata.yml",
    "content": "id: \"@default/cpu-usage\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: LuCpu\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Cpu\n  template: !include plugin/template.js\n"
  },
  {
    "path": "src/static/plugins/tb_cpu_usage/plugin/template.js",
    "content": "const totalUsage = cores.reduce((total, core) => total + core.usage, 0);\nconst used = totalUsage / cores.length;\n\nreturn [icon(\"LuCpu\"), \" \", used.toFixed(0) + \"%\"];\n"
  },
  {
    "path": "src/static/plugins/tb_default_focused_app.yml",
    "content": "id: \"@default/focused-app\"\nmetadata:\n  displayName:\n    en: Focused App Display Name\n    cs: Zobrazení názvu zaměřené aplikace\n    da: Fokuseret app-skærmnavn\n    af: Gefokusde app -vertoonnaam\n    de: Fokussierte App Anzeigename\n    et: Focused App Display Name\n    es: Nombre de la aplicación\n    az: Fokuslanmış tətbiqin adı\n    bs: Fokusirano ime aplikacije\n    ca: Nom de la visualització de l'aplicació centrada\n    fr: Nom d'affichage de l'application ciblée\n    cy: Enw Arddangos Ap Ffocws\n    id: Nama Tampilan Aplikasi yang Difokuskan\n    it: Nome visualizzato dell'app focalizzata\n    eu: Fokatutako aplikazioaren bistaratzeko izena\n    tl: Nakatuon ang pangalan ng pagpapakita ng app\n    lv: Mērķtiecīgas lietojumprogrammas Rādīt nosaukumu\n    hr: Usredotočeno ime za prikaz aplikacija\n    lt: Koncentruotos programėlės rodomas pavadinimas\n    zu: IGAMA LOKUTHOLAKALA KWE-APP ESISEKELWE\n    hu: Fókuszált alkalmazás kijelzőnév\n    sw: Jina la kuonyesha la programu\n    is: Einbeitt forrit Nafn forrits\n    nl: Gerichte app Weergavenaam\n    ku: Navê Display App Focused\n    pl: Nazwa wyświetlana aplikacji Focused\n    lb: Fokusséiert App Display Numm\n    pt: Nome de exibição do aplicativo focalizado\n    ro: Nume afișare aplicație focalizată\n    ms: Nama paparan aplikasi yang difokuskan\n    sk: Zobrazenie názvu aplikácie so zameraním\n    mt: Isem tal-wiri tal-app iffokat\n    fi: Keskitetty sovellus Näyttönimi\n    \"no\": Fokusert app visningsnavn\n    sv: Fokuserad app Visningsnamn\n    uz: Fokus qilingan ilovaning displey nomi\n    tr: Odaklanmış Uygulama Görünen Adı\n    el: Όνομα εμφάνισης εστιασμένης εφαρμογής\n    bg: Фокусирано име на приложението\n    so: Magaca Muujinta Muuqaalka App\n    vi: Tên hiển thị ứng dụng tập trung\n    yo: Ti dojukọ orukọ ifihan app\n    uk: Сфокусоване відображення назви програми\n    mk: Фокусирано име на приказ на апликација\n    ru: Название активного приложения\n    ar: اسم عرض التطبيق المركّز\n    mn: Төвлөрсөн апп-ын дэлгэцийн нэр\n    sr: Фокусирано име апликације\n    tg: Номи намоишгоҳи барнома\n    hy: Կենտրոնացված հավելվածի անունը\n    ka: ფოკუსირებული აპლიკაციის ჩვენების სახელი\n    he: שם תצוגת אפליקציה ממוקדת\n    ur: مرکوز ایپ ڈسپلے کا نام\n    fa: نام نمایش برنامه متمرکز\n    bn: ফোকাসড অ্যাপ ডিসপ্লে নাম\n    hi: केंद्रित ऐप प्रदर्शन नाम\n    am: የትኩረት መተግበሪያ የማሳያ ስም\n    ps: د ایپ انکشافي نوم\n    gu: કેન્દ્રિત એપ્લિકેશન પ્રદર્શન નામ\n    ne: फोकस गरिएको अनुप्रयोग प्रदर्शन नाम\n    zh: 当前应用程序名\n    ko: 집중 앱 표시 이름\n    pa: ਫੋਕਸ ਐਪ ਡਿਸਪਲੇਅ ਦਾ ਨਾਮ\n    ja: フォーカスアプリ表示名\n    ta: கவனம் செலுத்திய பயன்பாட்டு காட்சி பெயர்\n    te: కేంద్రీకృత అనువర్తన ప్రదర్శన పేరు\n    th: ชื่อการแสดงแอพที่เน้น\n    km: ឈ្មោះបង្ហាញកម្មវិធីដែលផ្តោតអារម្មណ៍\n    si: අවධානය යොමු කළ යෙදුම් දර්ශන නාමය\n    lo: ຊື່ການສະແດງແອັບ App ທີ່ສຸມໃສ່\nicon: MdCenterFocusStrong\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - FocusedApp\n  template: 'return [AppIcon({ path: focusedApp.exe, umid: focusedApp.umid }), \"  \", focusedApp.name]'\n  style:\n    flexShrink: 0\n"
  },
  {
    "path": "src/static/plugins/tb_default_focused_app_title.yml",
    "content": "id: \"@default/focused-app-title\"\nmetadata:\n  displayName:\n    en: Focused App Title\n    cs: Název zaměřené aplikace\n    da: Fokuseret app-titel\n    de: Fokussierter App-Titel\n    et: Fookustatud rakenduse pealkiri\n    af: Gefokusde app -titel\n    es: Título de la aplicación\n    az: Fokuslanmış Tətbiq Başlığı\n    bs: Fokusirana naslov aplikacije\n    ca: Títol de l'aplicació centrat\n    fr: Titre de l'application ciblée\n    cy: Teitl ap wedi'i ffocws\n    id: Judul Aplikasi Terfokus\n    it: Titolo dell'applicazione focalizzata\n    tl: Nakatuon ang pamagat ng app\n    eu: Aplikazioaren izenburua bideratua\n    lv: Mērķtiecīga lietotnes nosaukums\n    hr: Usredotočeni naslov aplikacije\n    lt: Koncentruotos programos pavadinimas\n    hu: Fókuszált alkalmazás címe\n    zu: Isihloko sohlelo lokusebenza esigxile\n    sw: Kichwa cha programu kilicholenga\n    is: Einbeittur forrit titill\n    nl: Gerichte app-titel\n    ku: Sernavê sernavê Focused\n    pl: Skoncentrowany tytuł aplikacji\n    lb: Fokusséiert App Titel\n    pt: Título do aplicativo focalizado\n    ro: Titlul aplicației focalizate\n    sk: Názov aplikácie so zameraním\n    mt: Titolu tal-app iffokat\n    fi: Keskittynyt sovelluksen otsikko\n    ms: Tajuk aplikasi yang difokuskan\n    uz: FOCKES sarlavhasi\n    sv: Fokuserad app-titel\n    \"no\": Fokusert apptittel\n    tr: Odaklanmış Uygulama Başlığı\n    el: Εστιασμένη εφαρμογή Τίτλος\n    bg: Заглавие на фокусираното приложение\n    so: Cinwaanka app\n    vi: Tiêu đề ứng dụng tập trung\n    ru: Заголовок активного приложения\n    yo: Akọkọ ti o ni idojukọ\n    uk: Сфокусована назва програми\n    ar: عنوان التطبيق المركّز\n    mn: Төвлөрсөн апп гарчиг\n    mk: Фокусиран наслов на апликација\n    sr: Фокусирана назив апликације\n    tg: Номи барнома\n    ka: ფოკუსირებული აპლიკაციების სათაური\n    hy: Կենտրոնացված ծրագրի վերնագիրը\n    hi: केंद्रित ऐप शीर्षक\n    ps: د اپل شوي ایپ سرلیک\n    ur: توجہ مرکوز ایپ کا عنوان\n    he: כותרת אפליקציה ממוקדת\n    fa: عنوان برنامه متمرکز\n    am: የታተመ የመተግበሪያ ርዕስ\n    bn: ফোকাসড অ্যাপের শিরোনাম\n    gu: કેન્દ્રિત એપ્લિકેશન શીર્ષક\n    ne: केन्द्रित अनुप्रयोग शीर्षक\n    pa: ਫੋਕਸਡ ਐਪ ਦਾ ਸਿਰਲੇਖ\n    ta: கவனம் செலுத்திய பயன்பாட்டு தலைப்பு\n    ko: 집중된 앱 제목\n    zh: 当前应用程序标题\n    te: ఫోకస్డ్ యాప్ టైటిల్\n    si: අවධානය යොමු කළ යෙදුම් මාතෘකාව\n    ja: フォーカスアプリのタイトル\n    th: ชื่อแอพที่มุ่งเน้น\n    lo: ເອົາຫົວຂໍ້ app ທີ່ສຸມໃສ່\n    km: ចំណងជើងកម្មវិធីផ្តោតអារម្មណ៍\nicon: MdCenterFocusWeak\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - FocusedApp\n  template: return focusedApp.title\n  style:\n    flexShrink: 2\n"
  },
  {
    "path": "src/static/plugins/tb_default_power/i18n/description.yml",
    "content": "bn: একটি টুলবার আইটেম যোগ করে যা ব্যাটারির অবস্থা এবং সিস্টেম পাওয়ার অবস্থা দেখায়।\nlt: Pridedamas įrankių juostos elementas, rodantis baterijų būseną ir sistemos maitinimo būseną.\nam: የባለሙያዎችን ሁኔታ እና የስርዓት ኃይል ሁኔታን የሚያሳይ የመሣሪያ አሞሌ መሳሪያ ያክላል.\nso: Waxay ku dartaa shey qalab aalad ah oo muujinaya gobolka baytariyada iyo gobolka awoodda nidaamka.\npt-PT: Adiciona um item da barra de ferramentas que mostra o estado das baterias e o estado de energia do sistema.\nis: Bætir við atriði á tækjastiku sem sýnir stöðu rafhlöðanna og orkustöðu kerfisins.\ncy: Yn ychwanegu eitem bar offer sy'n dangos cyflwr y batris a chyflwr pŵer y system.\nsk: Pridá položku panela nástrojov, ktorá zobrazuje stav batérií a stav napájania systému.\nlv: Pievieno rīkjoslas vienumu, kas parāda akumulatoru stāvokli un sistēmas jaudas stāvokli.\nsi: බැටරිවල තත්ත්වය සහ පද්ධති බල තත්ත්වය පෙන්වන මෙවලම් තීරු අයිතමයක් එක් කරයි.\nlo: ເພີ່ມລາຍການແຖບເຄື່ອງມືທີ່ສະແດງໃຫ້ເຫັນສະພາບຂອງແບດເຕີຣີ້ແລະລັດ Power Power.\nsv: Lägger till ett verktygsfält som visar batteriernas tillstånd och systemets strömtillstånd.\nhy: \"Ավելացնում է գործիքագոտու տարր, որը ցույց է տալիս մարտկոցների վիճակը և համակարգի էներգիայի վիճակը:\"\nms: Menambah item bar alat yang menunjukkan keadaan bateri dan keadaan kuasa sistem.\nsw: Inaongeza kipengee cha zana kinachoonyesha hali ya betri na hali ya nguvu ya mfumo.\nhe: מוסיף פריט בסרגל כלים המציג את מצב הסוללות ואת מצב צריכת החשמל של המערכת.\nlb: Füügt e Toolbar Element, deen den Zoustand vun de Batterien an de Systemkraaftstaat weist.\nne: ब्याट्रीको अवस्था र प्रणालीको शक्ति अवस्था देखाउने उपकरणपट्टी वस्तु थप्छ।\nzh-TW: 添加一個工具欄項，顯示電池狀態和系統電源狀態。\naf: Voeg 'n nutsbalkitem by wat die toestand van die batterye en die stelselkragtoestand wys.\nca: Afegeix un element de la barra d'eines que mostra l'estat de les bateries i l'estat d'alimentació del sistema.\nta: பேட்டரிகளின் நிலை மற்றும் சிஸ்டம் பவர் நிலையைக் காட்டும் கருவிப்பட்டி உருப்படியைச் சேர்க்கிறது.\nmn: Батерей, системийн хүчний төлөв байдлыг харуулсан багаж самбарыг нэмж өгдөг.\ntl: Nagdaragdag ng isang item ng toolbar na nagpapakita ng estado ng mga baterya at estado ng kapangyarihan ng system.\nhu: Hozzáad egy elemet az eszköztárhoz, amely megmutatja az akkumulátorok állapotát és a rendszer tápellátási állapotát.\naz: Batareyaların vəziyyətini və sistem güc vəziyyətini göstərən bir alət çubuğu maddəsi əlavə edir.\nmk: Додава ставка од лентата со алатки што ја прикажува состојбата на батериите и состојбата на моќноста на системот.\nka: ამატებს ინსტრუმენტთა ზოლს, რომელიც აჩვენებს ბატარეების მდგომარეობას და სისტემის სიმძლავრის მდგომარეობას.\nth: เพิ่มรายการแถบเครื่องมือที่แสดงสถานะของแบตเตอรี่และสถานะพลังงานของระบบ\nbs: Dodaje stavku trake sa alatkama koja prikazuje stanje baterija i stanje napajanja sistema.\nno: Legger til et verktøylinjeelement som viser statusen til batteriene og systemets strømtilstand.\net: Lisab tööriistariba üksuse, mis näitab akude olekut ja süsteemi toiteolekut.\nyo: Ṣafikun nkan irinṣẹ irinṣẹ ti o fihan ipinle ti awọn batiri ati ipo agbara eto.\neu: Tresna-barrako elementu bat gehitzen du, baterien egoera eta sistemaren energia-egoera erakusten dituena.\nsr: Додаје ставку траке са алаткама која приказује стање батерија и стање напајања система.\nvi: Thêm mục thanh công cụ hiển thị trạng thái của pin và trạng thái nguồn của hệ thống.\nnl: Voegt een werkbalkitem toe dat de status van de batterijen en de energiestatus van het systeem toont.\npa: ਇੱਕ ਟੂਲਬਾਰ ਆਈਟਮ ਜੋੜਦਾ ਹੈ ਜੋ ਬੈਟਰੀਆਂ ਦੀ ਸਥਿਤੀ ਅਤੇ ਸਿਸਟਮ ਪਾਵਰ ਸਥਿਤੀ ਨੂੰ ਦਰਸਾਉਂਦਾ ਹੈ।\nfi: Lisää työkalupalkkiin kohteen, joka näyttää akkujen tilan ja järjestelmän virran tilan.\nmt: Iżżid oġġett tal-bar tal-għodda li juri l-istat tal-batteriji u l-istat tal-enerġija tas-sistema.\nur: ایک ٹول بار آئٹم شامل کرتا ہے جو بیٹریاں اور سسٹم پاور اسٹیٹ کی حالت کو ظاہر کرتا ہے۔\nuk: Додає елемент панелі інструментів, який показує стан батарей і стан живлення системи.\nid: Menambahkan item toolbar yang menunjukkan status baterai dan status daya sistem.\nda: Tilføjer et værktøjslinjeelement, der viser batteriernes tilstand og systemets strømtilstand.\nbg: Добавя елемент от лентата с инструменти, който показва състоянието на батериите и състоянието на захранване на системата.\nkm: បន្ថែមធាតុរបារឧបករណ៍ដែលបង្ហាញពីស្ថានភាពនៃអាគុយនិងរដ្ឋថាមពលប្រព័ន្ធ។\npl: Dodaje element paska narzędzi pokazujący stan akumulatorów i stan zasilania systemu.\ncs: Přidá položku panelu nástrojů, která zobrazuje stav baterií a stav napájení systému.\ntr: Pillerin durumunu ve sistem güç durumunu gösteren bir araç çubuğu öğesi ekler.\nes: Agrega un elemento de la barra de herramientas que muestra el estado de las baterías y el estado de energía del sistema.\nfr: Ajoute un élément de barre d'outils qui affiche l'état des batteries et l'état d'alimentation du système.\nuz: Batareyalar va tizim quvvatining holatini ko'rsatadigan asboblar panelini qo'shadi.\ntg: Ададҳои панели асбобҳоеро илова мекунад, ки вазъи батареяҳо ва давлати қудрати системаро нишон медиҳад.\nen: Adds a toolbar item that shows the state of the batteries and the system power state.\nhr: Dodaje stavku alatne trake koja prikazuje stanje baterija i stanje napajanja sustava.\nit: Aggiunge un elemento della barra degli strumenti che mostra lo stato delle batterie e lo stato di alimentazione del sistema.\nzh-CN: 添加一个工具栏项，显示电池状态和系统电源状态。\nja: バッテリーの状態とシステムの電源状態を表示するツールバー項目を追加します。\nar: إضافة عنصر شريط أدوات يوضح حالة البطاريات وحالة طاقة النظام.\nde: Fügt ein Symbolleistenelement hinzu, das den Zustand der Batterien und den Energiestatus des Systems anzeigt.\npt-BR: Adds a toolbar item that shows the state of the batteries and the system power state.\nro: Adaugă un element din bara de instrumente care arată starea bateriilor și starea de alimentare a sistemului.\nel: Προσθέτει ένα στοιχείο γραμμής εργαλείων που δείχνει την κατάσταση των μπαταριών και την κατάσταση ισχύος του συστήματος.\nru: Добавляет элемент панели инструментов, который показывает состояние батарей и состояние питания системы.\nzu: Ingeza into yamathuluzi ekhombisa isimo samabhethri kanye nesimo samandla esistimu.\nps: د وسیلې بار توکي اضافه کوي چې د بیټرۍ حالت او د سیسټم بریښنا حالت ښیې.\nte: బ్యాటరీల స్థితి మరియు సిస్టమ్ పవర్ స్థితిని చూపే టూల్‌బార్ అంశాన్ని జోడిస్తుంది.\ngu: ટૂલબાર આઇટમ ઉમેરે છે જે બેટરીની સ્થિતિ અને સિસ્ટમ પાવર સ્ટેટ દર્શાવે છે.\nko: 배터리 상태와 시스템 전원 상태를 표시하는 도구 모음 항목을 추가합니다.\nfa: یک مورد نوار ابزار را اضافه می کند که وضعیت باتری ها و وضعیت قدرت سیستم را نشان می دهد.\nhi: एक टूलबार आइटम जोड़ता है जो बैटरियों की स्थिति और सिस्टम पावर स्थिति दिखाता है।\nku: Tiştek toolbarê zêde dike ku rewşa batariyan û rewşa hêza pergalê nîşan dide.\n"
  },
  {
    "path": "src/static/plugins/tb_default_power/i18n/display_name.yml",
    "content": "es: Alimentación y batería\nde: Strom und Batterie\nth: พลังงานและแบตเตอรี่\nfi: Virta ja akku\nzh: 电源和电池\nka: ენერგია და ბატარეა\nru: Питание и аккумулятор\nja: 電源＆バッテリー\nsr: Снага и батерија\nam: ኃይል እና ባትሪ\nbn: শক্তি এবং ব্যাটারি\nit: Alimentazione e batteria\nen: Power & Battery\nlt: Maitinimas ir akumuliatorius\nms: Kuasa & bateri\nzh-CN: 电源及电池\naz: Güc və batareya\npt: Energia e bateria\nlv: Enerģijas padeve un akumulators\nuz: Quvvat va batareya\npt-PT: Energia e bateria\nlb: Kraaft & Batterie\ntl: Kapangyarihan at baterya\nhu: Táp és akkumulátor\nmt: Enerġija u batterija\naf: Krag en battery\nsw: Nguvu na betri\nps: بریښنا او بیټرۍ\nsi: බලය සහ බැටරි\nid: Daya & Baterai\nuk: Живлення та акумулятор\neu: Potentzia eta bateria\net: Toide ja aku\nhe: כוח וסוללה\nhy: Հզորություն եւ մարտկոց\nyo: Agbara & Batiri\nte: పవర్ & బ్యాటరీ\nbg: Захранване и батерия\nzh-TW: 電源及電池\nta: பவர் & பேட்டரி\nro: Putere și baterie\nda: Strøm og batteri\nmn: Цахилгаан босгон цахилгаан\nar: الطاقة والبطارية\ngu: વીજળી\nbs: Snaga i baterija\ntr: Güç ve Akü\nkm: ថាមពលនិងថ្ម\ncy: Pwer a Batri\nne: शक्ति र ब्याट्री\npt-BR: Power & Battery\nsv: Strömförsörjning och batteri\ncs: Napájení a baterie\nko: 전원 및 배터리\npa: ਪਾਵਰ ਅਤੇ ਬੈਟਰੀ\nmk: Енергија и батерија\ntg: Қудрат ва батарея\nca: Energia i bateria\nel: Ισχύς & μπαταρία\nzu: Amandla nebhethri\nfr: Alimentation et batterie\nsk: Napájanie a batérie\nno: Strøm og batteri\nvi: Năng lượng & Pin\nhr: Napajanje i baterija\nlo: ພະລັງງານແລະຫມໍ້ໄຟ\nur: پاور اور بیٹری\nku: Power & Baterî\nfa: نیرو و باتری\nnl: Voeding & batterij\nhi: बिजली और बैटरी\npl: Zasilanie i akumulator\nso: Awoodda & batteriga\nis: Afl & rafhlaða\n"
  },
  {
    "path": "src/static/plugins/tb_default_power/mod.yml",
    "content": "id: \"@default/power\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: PiBatteryMediumFill\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Power\n  tooltip: !include plugin/tooltip.js\n  template: !include plugin/template.js\n  onClickV2: open(\"ms-settings:powersleep\")\n"
  },
  {
    "path": "src/static/plugins/tb_default_power/plugin/template.js",
    "content": "if (!batteries.length) {\n  return icon(\"TbPlugConnected\");\n}\n\nconst averagePercentage = batteries.reduce((total, battery) => total + battery.percentage, 0) / batteries.length;\n\nconst isCharging = power.acLineStatus === 1 ||\n  batteries.some((battery) => {\n    return battery.state === \"charging\";\n  });\nconst isSmartChargeActive = batteries.some((battery) => battery.smartCharging);\n\nlet group = [];\n\nif (isCharging) {\n  group.push(icon(\"BsFillLightningChargeFill\", 12));\n  group.push(\" \");\n}\n\nif (powerMode === \"BetterBattery\" || powerMode === \"BatterySaver\") {\n  group.push(icon(\"FaLeaf\", 12));\n  group.push(\" \");\n} else if (powerMode === \"HighPerformance\" || powerMode === \"MaxPerformance\") {\n  group.push(icon(\"IoSpeedometer\", 12));\n  group.push(\" \");\n}\n\nif (isSmartChargeActive) {\n  group.push(icon(\"FaHeart\", 12));\n  group.push(\" \");\n}\n\ngroup.push(\n  averagePercentage > 90\n    ? icon(\"PiBatteryFullFill\")\n    : averagePercentage > 66\n    ? icon(\"PiBatteryHighFill\")\n    : averagePercentage > 33\n    ? icon(\"PiBatteryMediumFill\")\n    : averagePercentage > 5\n    ? icon(\"PiBatteryLowFill\")\n    : icon(\"PiBatteryWarning\"),\n);\n\ngroup.push(\" \");\ngroup.push(averagePercentage);\ngroup.push(\"%\");\n\nreturn group;\n"
  },
  {
    "path": "src/static/plugins/tb_default_power/plugin/tooltip.js",
    "content": "if (!batteries.length) {\n  return t(\"plugged\");\n}\n\nreturn batteries\n  .map((battery, index) => {\n    let content = \"\";\n\n    if (batteries.length > 1) {\n      content += `${index + 1}. ${battery.model}: `;\n    }\n\n    content += t(\"battery.remaining\", { 0: battery.percentage });\n    content += battery.smartCharging ? `- ${t(\"battery.smart_charge\")}` : \"\";\n\n    return content;\n  })\n  .join(\"\\n\");\n"
  },
  {
    "path": "src/static/plugins/tb_disk_usage/i18n/display_name.yml",
    "content": "tr: Disk Kullanımı\ncs: Využití disku\nhe: שימוש בדיסק\nku: Bikaranîna Dîskê\npt-BR: Disk Usage\nvi: Sử dụng đĩa\ngu: ડિસ્ક વપરાશ\nda: Diskbrug\nsi: තැටි භාවිතය\ncy: Defnydd Disg\nka: დისკის გამოყენება\npl: Użycie dysku\nzu: Ukusetshenziswa kweDiski\nis: Diskanotkun\nhu: Lemezhasználat\nhy: Սկավառակի օգտագործում\nlb: Disk Notzung\nyo: Lilo Disiki\ntg: Истифодаи диск\nlt: Disko naudojimas\nlv: Diska lietošana\npa: ਡਿਸਕ ਦੀ ਵਰਤੋਂ\nsk: Využitie disku\nuk: Використання диска\nro: Utilizarea discului\nte: డిస్క్ వినియోగం\nbg: Използване на диска\nes: Uso del disco\nfa: استفاده از دیسک\nar: استخدام القرص\nsr: Употреба диска\nuz: Diskdan foydalanish\nzh-TW: 磁盤使用情況\nja: ディスク使用量\nbn: ডিস্ক ব্যবহার\nde: Festplattennutzung\nit: Utilizzo del disco\nur: ڈسک کا استعمال\nko: 디스크 사용량\nmk: Употреба на диск\npt-PT: Uso de disco\nam: የዲስክ አጠቃቀም\nel: Χρήση δίσκου\nkm: ការប្រើប្រាស់ថាស\nsv: Diskanvändning\nhi: डिस्क उपयोग\nmn: Дискний хэрэглээ\nfr: Utilisation du disque\nmt: Użu tad-Disk\nzh-CN: 磁盘使用情况\nno: Diskbruk\nfi: Levyn käyttö\nms: Penggunaan Cakera\naf: Skyfgebruik\nru: Использование диска\nlo: ການ​ນໍາ​ໃຊ້​ແຜ່ນ​\nne: डिस्क प्रयोग\nnl: Schijfgebruik\nps: د ډیسک کارول\nhr: Upotreba diska\nbs: Upotreba diska\neu: Diskoaren erabilera\nth: การใช้งานดิสก์\nen: Disk Usage\nca: Ús del disc\naz: Disk İstifadəsi\net: Ketta kasutamine\nso: Isticmaalka Disk-ga\nid: Penggunaan Disk\nsw: Matumizi ya Diski\nta: வட்டு பயன்பாடு\ntl: Paggamit ng Disk\n"
  },
  {
    "path": "src/static/plugins/tb_disk_usage/metadata.yml",
    "content": "id: \"@default/disk-usage\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: BsDeviceHdd\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Disk\n  template: !include plugin/template.js\n  tooltip: !include plugin/tooltip.js\n"
  },
  {
    "path": "src/static/plugins/tb_disk_usage/plugin/template.js",
    "content": "const units = [\"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\nlet totalRead = disks.reduce((total, disk) => total + disk.readBytes, 0);\nlet totalWritten = disks.reduce((total, disk) => total + disk.writtenBytes, 0);\nlet unit = \"B\";\n\nunits.forEach((unitSize) => {\n  if (totalRead >= 1024 || totalWritten >= 1024) {\n    totalRead /= 1024;\n    totalWritten /= 1024;\n    unit = unitSize;\n  } else {\n    return;\n  }\n});\n\nreturn [\n  icon(\"BsDeviceHdd\"),\n  \" \",\n  totalRead.toFixed(0) + unit + \"/s\" + \" | \" + totalWritten.toFixed(0) + unit + \"/s\",\n];\n"
  },
  {
    "path": "src/static/plugins/tb_disk_usage/plugin/tooltip.js",
    "content": "const units = [\"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\nreturn disks\n  .map((disk) => {\n    let total = disk.totalSpace;\n    let used = disk.totalSpace - disk.availableSpace;\n    let unit = \"B\";\n\n    units.forEach((unitSize) => {\n      if (total >= 1024) {\n        total /= 1024;\n        used /= 1024;\n        unit = unitSize;\n      } else {\n        return;\n      }\n    });\n\n    return disk.mountPoint + \" -> \" + used.toFixed(1) + unit + \" / \" + total.toFixed(1) + unit;\n  })\n  .join(\"\\n\");\n"
  },
  {
    "path": "src/static/plugins/tb_memory_usage/i18n/display_name.yml",
    "content": "es: Uso de la memoria\net: Mälu kasutamine\nta: நினைவக பயன்பாடு\ngu: મેમરી વપરાશ\nlo: ການນຳໃຊ້ໜ່ວຍຄວາມຈຳ\nlb: Erënnerung Benotzen\ncs: Využití paměti\nka: მეხსიერების გამოყენება\nlv: Atmiņas lietojums\nro: Utilizarea memoriei\ntg: Истифодаи хотира\ntr: Bellek Kullanımı\nur: میموری کا استعمال\nps: د حافظې کارول\nen: Memory Usage\nel: Χρήση Μνήμης\nru: Использование памяти\naz: Yaddaş İstifadəsi\nbs: Upotreba memorije\nlt: Atminties naudojimas\naf: Geheuegebruik\nja: メモリ使用量\nsi: මතක භාවිතය\nid: Penggunaan Memori\nis: Minnisnotkun\nmk: Употреба на меморија\nhe: שימוש בזיכרון\npt-BR: Memory Usage\npl: Wykorzystanie pamięci\ncy: Defnydd Cof\nda: Hukommelsesbrug\nfr: Utilisation de la mémoire\nar: استخدام الذاكرة\nca: Ús de la memòria\nfi: Muistin käyttö\nku: Bikaranîna Bîrê\nte: మెమరీ వినియోగం\nsw: Matumizi ya Kumbukumbu\nhy: Հիշողության օգտագործում\nzh-CN: 内存使用情况\nbn: মেমরি ব্যবহার\nhi: स्मृति प्रयोग\nsr: Употреба меморије\nyo: Iranti Lilo\neu: Memoria Erabilera\nms: Penggunaan Memori\nfa: استفاده از حافظه\nth: การใช้หน่วยความจำ\nsv: Minnesanvändning\nne: मेमोरी प्रयोग\nkm: ការប្រើប្រាស់អង្គចងចាំ\nsk: Využitie pamäte\nuz: Xotiradan foydalanish\nde: Speichernutzung\npa: ਮੈਮੋਰੀ ਵਰਤੋਂ\nbg: Използване на паметта\nso: Isticmaalka Xusuusta\nhu: Memóriahasználat\nmn: Санах ойн хэрэглээ\nit: Utilizzo della memoria\nzh-TW: 內存使用情況\nvi: Sử dụng bộ nhớ\npt-PT: Uso de memória\nhr: Upotreba memorije\nno: Minnebruk\nzu: Ukusetshenziswa Kwenkumbulo\nmt: Użu tal-Memorja\nuk: Використання пам'яті\nam: የማህደረ ትውስታ አጠቃቀም\nnl: Geheugengebruik\nko: 메모리 사용량\ntl: Paggamit ng Memory\n"
  },
  {
    "path": "src/static/plugins/tb_memory_usage/metadata.yml",
    "content": "id: \"@default/memory-usage\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: FaMemory\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Memory\n  template: !include plugin/template.js\n"
  },
  {
    "path": "src/static/plugins/tb_memory_usage/plugin/template.js",
    "content": "const units = [\"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\nlet total = memory.total;\nlet used = memory.total - memory.free;\nlet unit = \"B\";\n\nunits.forEach((unitSize) => {\n  if (total >= 1024) {\n    total /= 1024;\n    used /= 1024;\n    unit = unitSize;\n  } else {\n    return;\n  }\n});\n\nreturn [icon(\"FaMemory\"), \" \", used.toFixed(0) + unit + \" / \" + total.toFixed(0) + unit];\n"
  },
  {
    "path": "src/static/plugins/tb_network_usage/i18n/display_name.yml",
    "content": "it: Utilizzo della rete\net: Võrgukasutus\nuz: Tarmoqdan foydalanish\nen: Network Usage\nlt: Tinklo naudojimas\nam: የአውታረ መረብ አጠቃቀም\ntl: Paggamit ng Network\nfa: استفاده از شبکه\nzh-CN: 网络使用情况\naz: Şəbəkə İstifadəsi\nbg: Използване на мрежата\nhu: Hálózathasználat\nbn: নেটওয়ার্ক ব্যবহার\nbs: Upotreba mreže\nes: Uso de la red\npl: Wykorzystanie sieci\nro: Utilizarea rețelei\nsk: Využitie siete\nsv: Nätverksanvändning\ntg: Истифодаи шабака\ntr: Ağ Kullanımı\nfi: Verkon käyttö\nmt: Użu tan-Netwerk\nzu: Ukusetshenziswa kwenethiwekhi\nyo: Nẹtiwọki Lilo\nja: ネットワークの使用状況\nhr: Upotreba mreže\nso: Isticmaalka Shabakadda\nsw: Matumizi ya Mtandao\nhy: Ցանցի օգտագործումը\nne: नेटवर्क उपयोग\nel: Χρήση Δικτύου\nmk: Употреба на мрежа\nms: Penggunaan Rangkaian\npt-BR: Network Usage\nuk: Використання мережі\nde: Netzwerknutzung\nar: استخدام الشبكة\nka: ქსელის გამოყენება\nid: Penggunaan Jaringan\nmn: Сүлжээний хэрэглээ\nps: د شبکې کارول\nhi: नेटवर्क उपयोग\nth: การใช้งานเครือข่าย\nte: నెట్‌వర్క్ వినియోగం\ncs: Využití sítě\naf: Netwerkgebruik\nhe: שימוש ברשת\nru: Использование сети\nfr: Utilisation du réseau\nur: نیٹ ورک کا استعمال\nkm: ការប្រើប្រាស់បណ្តាញ\nsr: Употреба мреже\nku: Bikaranîna Torê\npa: ਨੈੱਟਵਰਕ ਵਰਤੋਂ\nda: Netværksbrug\ngu: નેટવર્ક વપરાશ\nlv: Tīkla lietošana\nvi: Sử dụng mạng\nlo: ການນຳໃຊ້ເຄືອຂ່າຍ\nzh-TW: 網絡使用情況\nlb: Netzwierkverbrauch\nis: Netnotkun\nno: Nettverksbruk\nta: நெட்வொர்க் பயன்பாடு\nca: Ús de la xarxa\nsi: ජාල භාවිතය\ncy: Defnydd Rhwydwaith\nnl: Netwerkgebruik\neu: Sarearen erabilera\nko: 네트워크 사용량\npt-PT: Uso de rede\n"
  },
  {
    "path": "src/static/plugins/tb_network_usage/metadata.yml",
    "content": "id: \"@default/network-usage\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: PiArrowsDownUpBold\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - NetworkStatistics\n  template: !include plugin/template.js\n"
  },
  {
    "path": "src/static/plugins/tb_network_usage/plugin/template.js",
    "content": "const units = [\"Kb\", \"Mb\", \"Gb\", \"Tb\", \"Pb\", \"Eb\", \"Zb\", \"Yb\"];\n\nlet totalRecieved = networkStatistics.reduce((total, net) => total + net.received, 0) * 8;\nlet totalTransmitted = networkStatistics.reduce((total, net) => total + net.transmitted, 0) * 8;\nlet unit = \"B\";\n\nunits.forEach((unitSize) => {\n  if (totalRecieved >= 1000 || totalTransmitted >= 1000) {\n    totalRecieved /= 1000;\n    totalTransmitted /= 1000;\n    unit = unitSize;\n  } else {\n    return;\n  }\n});\n\nreturn [\n  icon(\"PiArrowsDownUpBold\"),\n  \" \",\n  totalRecieved.toFixed(0) + unit + \"ps\" + \" | \" + totalTransmitted.toFixed(0) + unit + \"ps\",\n];\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_dotted/i18n/display_name.yml",
    "content": "id: Ruang Kerja sebagai Titik\nzh-CN: 工作空间作为点\nzh: 以点为单位的工作空间\nsi: වැඩබිම තිත් ලෙස\nne: डाटको रूपमा कार्यस्पेस\nlo: ພື້ນທີ່ເຮັດວຽກເປັນຈຸດໆ\nnl: Werkplekken als stippen\nhu: Munkaterületek mint pontok\nsw: Nafasi za kazi kama dots\nyo: Awọn ibi-iṣẹ bi awọn aami\nbs: Radni prostori kao tačkice\nsr: Радне просторе као тачкице\nde: Arbeitsbereiche als Dots\nmt: Spazji tax-xogħol bħala tikek\nfi: Työtilat pisteinä\nkm: កន្លែងធ្វើការដូចចំនុច\nfa: فضای کاری به عنوان نقاط\nhi: डॉट्स के रूप में कार्यक्षेत्र\npt: Espaços de trabalho como pontos\naz: DOTS kimi iş yerləri\npt-BR: Workspaces as Dots\nso: Goobaha shaqada sida dhibcaha\ngu: બિંદુઓ તરીકે વર્કસ્પેસ\nte: వర్క్‌స్పేస్‌లు చుక్కలుగా\nka: სამუშაო ადგილები, როგორც წერტილები\ntr: Nokta Olarak Çalışma Alanları\nhy: Աշխատանքներ, որպես կետեր\nms: Ruang kerja sebagai titik\npt-PT: Espaços de trabalho como pontos\nen: Workspaces as Dots\nmk: Работните места како точки\nno: Arbeidsområder som prikker\nja: 点としてのワークスペース\ntl: Mga workspaces bilang mga tuldok\nhr: Radni prostori kao točkice\nmn: Ажлын байрыг цэгүүд болгон ашигладаг\nur: نقطوں کے طور پر ورک اسپیس\nbn: বিন্দু হিসাবে কর্মক্ষেত্র\nvi: Không gian làm việc như dấu chấm\nes: Espacios de trabajo como puntos\ntg: Корҳо ҳамчун нуқтаҳо\npa: ਬਿੰਦੀਆਂ ਦੇ ਵਰਕਸਪੇਸ\nth: พื้นที่ทำงานเป็นจุด\nuz: DOTS kabi ish joylari\nru: Рабочие места в виде точек\nbg: Работни пространства като точки\nzh-TW: 工作空間作為點\nta: பணியிடங்கள் புள்ளிகளாக\nlv: Darba vietas kā punkti\neu: Laneko guneak puntu gisa\nam: የስራ ቦታዎች እንደ ነጥቦች\net: Tööruumid kui punktid\nlb: Aarbechtsberäich wéi Punkten\nca: Espais de treball com a punts\nuk: Робочі простори як точки\nar: مساحات العمل كنقاط\nel: Χώροι εργασίας ως κουκκίδες\nku: Karên kar wekî dendikan\nit: Spazi di lavoro come punti\ncs: Pracovní prostory jako body\nda: Arbejdspladser som prikker\naf: Werkruimtes as kolletjies\nzu: Izindawo zokusebenza njengamachashazi\npl: Przestrzenie robocze jako kropki\nsk: Pracovné priestory ako body\nis: Vinnusvæði sem punktar\nro: Spațiile de lucru ca puncte\nhe: מרחבי עבודה כנקודות\nlt: Darbo vietos kaip taškai\nps: کاري ځای په توګه\nsv: Arbetsplatser som prickar\nfr: Les espaces de travail sous forme de points\ncy: Lleoedd gwaith fel dotiau\nko: 점으로 표시되는 작업 공간\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_dotted/metadata.yml",
    "content": "id: \"@default/workspaces-dotted\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: HiDotsHorizontal\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Workspaces\n  template: !include plugin/template.js\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_dotted/plugin/template.js",
    "content": "return Group({\n  content: workspaces.map((w) => {\n    const isActive = w.id === activeWorkspace;\n\n    return Button({\n      content: \"\",\n      style: {\n        padding: 0,\n        width: isActive ? \"1.5rem\" : \"0.5rem\",\n        height: \"0.5rem\",\n        borderRadius: \"0.25rem\",\n        backgroundColor: isActive ? \"var(--system-accent-color)\" : \"currentColor\",\n        transition: \"width 0.2s ease-in-out, backgroundColor 0.2s ease-in-out\",\n      },\n      onClick: `invoke(SeelenCommand.SwitchWorkspace, { workspaceId: '${w.id}' })`,\n    });\n  }),\n  style: {\n    display: \"flex\",\n    gap: \"0.75rem\",\n  },\n});\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_named/i18n/display_name.yml",
    "content": "sv: Namngivna arbetsytor\nhy: Անվանված աշխատատեղեր\nlo: ຊື່ວ່າເຮັດວຽກເຮັດງານທໍາ\nlt: Įvardytos darbo vietos\nsi: වැඩ කරන සේවා කොටස්\npt: Espaços de trabalho nomeados\nuz: Nomlangan ish joylari\nnl: Werkruimten op naam\naz: Adlı iş yerləri\nkm: កន្លែងធ្វើការដែលមានឈ្មោះ\nso: Magacooyin la yiraahdo\nlb: Mam Numm Aarbechtsberäich\naf: Werkruimtes genoem\net: Nimelised tööruumid\nbs: Imenovane radne prostore\nid: Ruang Kerja Bernama\nel: Ονομασμένοι χώροι εργασίας\nhu: Megnevezett munkaterületek\nka: დასახელებული სამუშაო ადგილები\nps: نومول کاري ځای\neu: Workspaces izendatuak\nne: नामकर्मी नाम\nko: 명명된 작업 공간\nzh: 已命名工作区\nja: 名前付きワークスペース\npt-BR: Named Workspaces\npl: Nazwane obszary robocze\nzu: Izindawo zokusebenzela\nlv: Nosauktās darbvietas\nmn: Ажлын талбарыг нэрлэсэн\nfa: فضاهای کاری نامگذاری شده\nbn: নামকরণ কর্মক্ষেত্র\nzh-TW: 命名工作空間\nhe: נקרא סביבות עבודה\nde: Benannte Arbeitsbereiche\nfr: Espaces de travail nommés\nsw: Nafasi za kazi zilizopewa jina\nvi: Có tên là không gian làm việc\nar: مساحات العمل المسماة\nmk: Именувани работни места\nhi: नामित कार्यक्षेत्र\nhr: Imenovani radni prostori\nis: Nefndur vinnusvæði\nyo: Ti a npè ni awọn iwe-iṣẹ\npa: ਨਾਮਕ ਵਰਕਸਪੇਸਾਂ\ncy: Enwir lleoedd gwaith\nam: የተሰየሙ የስራ ቦታዎች\nzh-CN: 命名工作空间\nte: పేరున్న వర్క్‌స్పేస్‌లు\ntg: Бо номҳои номбаршуда\ntr: Adlandırılmış Çalışma Alanları\ncs: Pojmenované pracovní prostory\nro: Spații de lucru numite\nur: ورک اسپیس کا نام دیا گیا\nth: Named Workspaces\nen: Named Workspaces\nit: Spazi di lavoro denominati\nno: Kalt arbeidsområder\nes: Espacios de trabajo con nombre\nuk: Іменовані робочі області\nku: Navê karkiran\nru: Именованные рабочие пространства\nbg: Именувани работни пространства\ngu: વર્કસ્પેસ નામના\nda: Navngivne arbejdsområder\nca: Espais de treball anomenats\nfi: Nimetyt työtilat\nsk: Pomenované pracovné priestory\nsr: Названи радни простори\nta: பெயரிடப்பட்ட பணியிடங்கள்\npt-PT: Espaços de trabalho nomeados\nms: Dinamakan ruang kerja\nmt: Imsemmi spazji tax-xogħol\ntl: Pinangalanang mga lugar ng trabaho\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_named/metadata.yml",
    "content": "id: \"@default/workspaces-named\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: GoNumber\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Workspaces\n  template: !include plugin/template.js\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_named/plugin/template.js",
    "content": "return Group({\n  content: workspaces.map((w, idx) => {\n    const isActive = w.id === activeWorkspace;\n\n    return Button({\n      content: w.name || `Workspace ${idx + 1}`,\n      style: {\n        fontWeight: 600,\n        color: isActive ? \"var(--system-accent-color)\" : \"currentColor\",\n        whiteSpace: \"nowrap\",\n        overflow: \"hidden\",\n        textOverflow: \"ellipsis\",\n      },\n      onClick: `invoke(SeelenCommand.SwitchWorkspace, { workspaceId: '${w.id}' })`,\n    });\n  }),\n  style: {\n    display: \"flex\",\n    gap: \"0.25rem\",\n  },\n});\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_numbered/i18n/display_name.yml",
    "content": "it: Spazi di lavoro numerati\nfr: Espaces de travail numérotés\nsk: Číslované pracovné priestory\nso: Goobaha shaqada ee lambaray\nzh-CN: 编号工作区\nro: Spații de lucru numerotate\nbn: সংখ্যাযুক্ত ওয়ার্কস্পেস\naf: Genommerde werkruimtes\nsw: Nafasi za kazi zilizohesabiwa\nam: የተቆጣባቸው የስራ ቦታዎች\nsv: Numrerade arbetsytor\nuz: Raqamlangan ish joylari\nyo: Awọn ibi-iṣẹ Nọmba\nhe: מרחבי עבודה ממוספרים\nko: 번호가 매겨진 작업 공간\nne: संख्याबद्ध कार्यस्थानहरू\ncy: Gweithleoedd wedi'u rhifo\nur: نمبر والے کام کی جگہیں\ngu: ક્રમાંકિત વર્કસ્પેસ\nzh: 编号工作区\nda: Nummererede arbejdsområder\nmt: Spazji tax-xogħol numerati\nmn: Дугаарласан ажлын талбар\nta: எண்ணுள்ள பணியிடங்கள்\npt-PT: Espaços de trabalho numerados\nel: Αριθμημένοι χώροι εργασίας\net: Nummerdatud tööruumid\nar: مساحات العمل المرقمة\nid: Ruang Kerja Bernomor\ntr: Numaralandırılmış Çalışma Alanları\npa: ਨੰਬਰ ਵਾਲੇ ਵਰਕਸਪੇਸ\nfa: فضاهای کاری شماره گذاری شده\nhr: Numerirani radni prostori\nmk: Нумерирани работни места\nms: Ruang kerja bernombor\nbg: Номерирани работни пространства\nps: شمیرې کاري ځای\nku: Hejmarên xebatê yên hejmar\nno: Nummererte arbeidsområder\nde: Nummerierte Arbeitsbereiche\ncs: Číslované pracovní prostory\nlv: Numurētas darbvietas\nlb: Nummeréiert Aarbechtsberäich\ntl: Numbered Workspaces\nca: Espais de treball numerats\nis: Númeruð vinnusvæði\nsr: Нумерисани радни простори\nuk: Пронумеровані робочі місця\naz: Nömrəli iş yerləri\nhy: Համարակալված աշխատատեղեր\nte: సంఖ్యా వర్క్‌స్పేస్‌లు\nzh-TW: 編號工作區\npt: Espaços de trabalho numerados\ntg: Корхонаҳои рақамгузорӣ\npl: Numerowane obszary robocze\nfi: Numeroidut työtilat\npt-BR: Numbered Workspaces\nka: დანომრილი სამუშაო ადგილები\neu: Zenbakitutako laneko guneak\nth: พื้นที่ทำงานที่มีหมายเลข\nzu: Izindawo zokusebenzela ezinombolo\nlt: Numeruotos darbo vietos\nsi: අංකිත වැඩබිම\nen: Numbered Workspaces\nbs: Numerirani radni prostori\nhi: गिनेदार कार्यक्षेत्र\nlo: ບ່ອນເຮັດວຽກທີ່ມີຈໍານວນ\nru: Пронумерованные рабочие места\nja: 番号付きワークスペース\nkm: កន្លែងធ្វើការដែលមានលេខរៀង\nhu: Számozott munkaterületek\nes: Espacios de trabajo numerados\nnl: Genummerde werkplekken\nvi: Không gian làm việc được đánh số\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_numbered/metadata.yml",
    "content": "id: \"@default/workspaces-numbered\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\nicon: GoNumber\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Workspaces\n  template: !include plugin/template.js\n"
  },
  {
    "path": "src/static/plugins/tb_workspaces_numbered/plugin/template.js",
    "content": "return Group({\n  content: workspaces.map((w, idx) => {\n    const isActive = w.id === activeWorkspace;\n\n    return Button({\n      content: idx + 1,\n      style: {\n        fontWeight: 600,\n        color: isActive ? \"var(--system-accent-color)\" : \"currentColor\",\n      },\n      onClick: `invoke(SeelenCommand.SwitchWorkspace, { workspaceId: '${w.id}' })`,\n    });\n  }),\n  style: {\n    display: \"flex\",\n    gap: \"0.25rem\",\n  },\n});\n"
  },
  {
    "path": "src/static/plugins/wm_bsp.yml",
    "content": "#\nid: \"@default/wm-bspwm\"\nmetadata:\n  displayName:\n    en: BSPWM Layout\n    cs: Rozložení BSPWM\n    da: BSPWM-layout\n    de: BSPWM-Layout\n    af: BSPWM -uitleg\n    et: BSPWM paigutus\n    az: Bspwm layout\n    bs: Izgled bspwm\n    es: Disposición BSPWM\n    ca: Disposició bspwm\n    cy: Cynllun BSPWM\n    fr: Disposition BSPWM\n    id: Tata Letak BSPWM\n    it: Layout BSPWM\n    eu: BSPWM diseinua\n    tl: Layout ng BSPWM\n    hr: BSPWM izgled\n    lv: BSPWM izkārtojums\n    zu: Ukuhlelwa kwe-BSPWM\n    hu: BSPWM elrendezés\n    lt: BSPWM išdėstymas\n    nl: BSPWM-indeling\n    is: BSPWM skipulag\n    ku: Bspwm Layout\n    sw: Mpangilio wa BSPWM\n    lb: Bspwm Layout\n    pl: Układ BSPWM\n    pt: Layout BSPWM\n    ro: Layout BSPWM\n    sk: Rozloženie BSPWM\n    mt: Tqassim BSPWM\n    fi: BSPWM-asettelu\n    \"no\": BSPWM -oppsett\n    ms: Susun atur BSPWM\n    sv: BSPWM Layout\n    uz: Bscwm tartibi\n    tr: BSPWM Düzeni\n    el: Διάταξη BSPWM\n    bg: BSPWM оформление\n    so: Bispwm\n    ru: Макет BSPWM\n    vi: Bố cục BSPWM\n    uk: Макет BSPWM\n    yo: Ifilelẹ BSPWM\n    mn: Bspwm байрлал\n    ar: تخطيط BSPWM\n    mk: Распоред на BSPWM\n    sr: БСПВМ изглед\n    tg: Suruty BSPWM\n    ka: BSPWM განლაგება\n    he: פריסת BSPWM\n    hy: BSPWM դասավորություն\n    ur: BSPWM لے آؤٹ\n    ps: د BSPWM ترتیب\n    fa: طرح BSPWM\n    am: BSPWM አቀማመጥ\n    ne: BSPWM लेट\n    hi: BSPWM लेआउट\n    ko: BSPWM 레이아웃\n    ta: Bspwm தளவமைப்பு\n    gu: બીએસપીડબલ્યુએમ લેઆઉટ\n    zh: BSPWM 布局\n    pa: ਬਸਵਾਸ ਲੇਆਉਟ\n    te: BSPWM లేఅవుట్\n    bn: বিএসপিডাব্লুএম লেআউট\n    ja: BSPWMレイアウト\n    si: BSPWM පිරිසැලසුම\n    lo: ຮູບແບບ BSPWM\n    th: เค้าโครง BSPWM\n    km: ប្លង់ BSPWM\n  description:\n    en: default bspwm tile layout.\n    da: standard bspwm fliselayout.\n    cs: výchozí rozložení dlaždic bspwm.\n    af: Standaard BSPWM -teëluitleg.\n    az: Defolt BSPWM kafel sxemi.\n    et: vaikimisi bspwm plaatide paigutus.\n    es: diseño de baldosas bspwm por defecto.\n    bs: Zadani raspored pločica BSPWM.\n    de: Standard-Bspwm-Kachel-Layout.\n    ca: Disposició per defecte de rajoles BSPWM.\n    cy: Cynllun teils bspwm diofyn.\n    fr: Disposition par défaut des tuiles BSPWM.\n    id: tata letak ubin bspwm default.\n    eu: BSPWM fitxa lehenetsia.\n    it: layout predefinito delle piastrelle bspwm.\n    tl: default na layout ng tile ng BSPWM.\n    hr: Zadana BSPWM izgled pločica.\n    lv: noklusējuma bspwm flīžu izkārtojums.\n    hu: alapértelmezett bspwm csempe elrendezés.\n    is: Sjálfgefið BSPWM flísarskipulag.\n    lt: numatytasis bspwm plytelių išdėstymas.\n    zu: Ukuhlelwa kwe-tile ye-BSPWM.\n    nl: standaard bspwm tile layout.\n    sw: Mpangilio wa tile wa BSPWM.\n    ku: Default BSPWM Tile Layout.\n    pl: domyślny układ kafelków bspwm.\n    pt: layout padrão de ladrilho do bspwm.\n    lb: Standard Bspwm Tile Layout.\n    ms: Susun atur jubin BSPWM lalai.\n    ro: aspectul implicit al plăcilor bspwm.\n    mt: Tqassim tal-madum BSPWM default.\n    fi: bspwm-laattojen oletusasettelu.\n    sk: predvolené rozloženie dlaždíc bspwm.\n    \"no\": Standard BSPWM Tile Layout.\n    uz: Bswwm plitka tartibi.\n    tr: varsayılan bspwm karo düzeni.\n    sv: standardlayout för bspwm-kakel.\n    el: προεπιλεγμένη διάταξη πλακιδίων bspwm.\n    bg: оформление на плочките по подразбиране на bspwm.\n    ru: Раскладка плитки bspwm по умолчанию.\n    vi: Bố cục gạch BSPWM mặc định.\n    so: Xaraashka 'BSPWM'\n    yo: aiyipada BSPWM Tilebuble.\n    uk: за замовчуванням bspwm компонування плиток.\n    mk: Стандарден распоред на плочки BSPWM.\n    mn: Үндсэн BSPWM плитлийн байрлал.\n    ar: تخطيط بلاط bspwm الافتراضي.\n    tg: Нуқтаи ноболиғи BSPWM.\n    sr: Подразумевано Изглед плочица БСПВМ.\n    ka: ნაგულისხმევი BSPWM ფილების განლაგება.\n    he: פריסת אריחי ברירת מחדל של BSPWM.\n    hy: \"Default BSPWM սալիկների դասավորությունը:\"\n    ur: پہلے سے طے شدہ BSPWM ٹائل لے آؤٹ۔\n    ps: د بیضیف ډسمه د ټیل ټایل ترکیب.\n    fa: طرح کاشی BSPWM پیش فرض.\n    ne: पूर्वनिर्धारित BSPWWM टाइल लेआउट।\n    hi: डिफ़ॉल्ट BSPWM टाइल लेआउट।\n    am: ነባሪ BPSPWMANANENETOUTOUTOUTET.\n    bn: ডিফল্ট বিএসপিডাব্লুএম টাইল লেআউট।\n    pa: ਮੂਲ ਬਸਵਾਸ ਟਾਈਲ ਲੇਆਉਟ.\n    ko: 기본 BSPWM 타일 레이아웃.\n    gu: ડિફ default લ્ટ BSPWM ટાઇલ લેઆઉટ.\n    zh: 默认 bspwm 平铺布局。\n    ta: இயல்புநிலை BSPWM ஓடு தளவமைப்பு.\n    si: පෙරනිමි BSPWM ටයිල් පිරිසැලසුම.\n    te: డిఫాల్ట్ BSPWM టైల్ లేఅవుట్.\n    lo: Default BSPWM Layout.\n    ja: デフォルトのbspwmタイルレイアウト。\n    km: ប្លង់ក្បឿង bspwm លំនាំដើម។\n    th: เค้าโครงกระเบื้อง BSPWM เริ่มต้น\ntarget: \"@seelen/window-manager\"\nplugin:\n  structure:\n    type: Horizontal\n    children:\n      - type: Leaf # 1\n      - type: Vertical\n        children:\n          - type: Leaf # 2\n          - type: Horizontal\n            children:\n              - type: Vertical\n                priority: 2\n                children:\n                  - type: Horizontal\n                    priority: 2\n                    children:\n                      - type: Leaf # 5\n                      - type: Leaf # 6\n                  - type: Leaf # 4\n                    priority: 1\n              - type: Leaf # 3\n                priority: 1\n"
  },
  {
    "path": "src/static/plugins/wm_grid.yml",
    "content": "id: \"@default/wm-grid\"\ntarget: \"@seelen/window-manager\"\nmetadata:\n  displayName:\n    en: Grid\n    cs: Síť\n    da: Gitter\n    de: Raster\n    af: Rooster\n    et: Võrk\n    es: Rejilla\n    bs: Rešetka\n    ca: Graella\n    fr: Grille\n    az: Tor\n    id: Grid\n    cy: Grid\n    it: Griglia\n    eu: Harbera\n    tl: Grid\n    lv: Tīkls\n    zu: Igridi\n    hr: Rešetka\n    hu: Rács\n    lt: Tinklelis\n    is: Rist\n    nl: Rooster\n    sw: Gridi ya taifa\n    ku: Grid\n    pl: Siatka\n    lb: Den Gëtter\n    pt: Grade\n    ro: Rețea\n    ms: Grid\n    sk: Sieť\n    mt: Grilja\n    fi: Ruutu\n    \"no\": Rutenett\n    sv: Rutnät\n    uz: Panjara\n    tr: Izgara\n    el: Πλέγμα\n    bg: Мрежа\n    so: Shiirid\n    vi: Lưới\n    ru: Сетка\n    uk: Сітка\n    yo: Akoj\n    mn: Дэвэрсэн усан\n    ar: الشبكة\n    mk: Решетка\n    he: רֶשֶׁת\n    tg: Шабака\n    sr: Мрежа\n    fa: شبکه\n    ka: ბადე\n    hy: Ցանց\n    ur: گرڈ\n    hi: ग्रिड\n    ps: گردش\n    am: ፍርግርግ\n    ne: ग्रिड\n    pa: ਗਰਿੱਡ\n    te: గ్రిడ్\n    bn: গ্রিড\n    zh: 网格\n    gu: ગ્રીક ગ્રિડ\n    ja: グリッド\n    ta: கட்டம்\n    ko: 그리드\n    si: ජාලකය\n    lo: ຕາຂ່າຍໄຟຟ້າ\n    th: กริด\n    km: បប្យេសី\n  description:\n    en: Grid Layout, useful for big monitors.\n    cs: Rozložení mřížky, užitečné pro velké monitory.\n    da: Gitterlayout, nyttigt til store skærme.\n    de: Rasterlayout, nützlich für große Monitore.\n    af: Roosteruitleg, nuttig vir groot monitors.\n    et: Ruudustiku paigutus, kasulik suurte monitoride puhul.\n    es: Diseño de cuadrícula, útil para monitores grandes.\n    az: Grid düzeni, böyük monitorlar üçün faydalıdır.\n    bs: Izgled rešetke, koristan za velike monitore.\n    fr: Disposition en grille, utile pour les grands écrans.\n    ca: Disseny de quadrícules, útil per a grans monitors.\n    cy: Cynllun grid, yn ddefnyddiol ar gyfer monitorau mawr.\n    id: Tata Letak Grid, berguna untuk monitor besar.\n    it: Layout a griglia, utile per i monitor di grandi dimensioni.\n    eu: Grid diseinua, monitore handientzako erabilgarria.\n    tl: Layout ng grid, kapaki -pakinabang para sa malaking monitor.\n    lv: Režģa izkārtojums, kas noder lieliem monitoriem.\n    hr: Izgled mreže, koristan za velike monitore.\n    lt: Tinklelio išdėstymas, naudingas dideliems monitoriams.\n    hu: Rácsos elrendezés, hasznos nagy monitorok esetén.\n    zu: Isakhiwo segridi, ewusizo kubaqaphi abakhulu.\n    is: Skipulag rist, gagnlegt fyrir stóra skjái.\n    sw: Mpangilio wa gridi ya taifa, muhimu kwa wachunguzi wakubwa.\n    nl: Rasterindeling, handig voor grote monitors.\n    ku: Rêzeya Grid, ji bo çavdêrên mezin kêrhatî ye.\n    lb: Grid Layout, nëtzlech fir grouss Méint.\n    pl: Układ siatki, przydatny w przypadku dużych monitorów.\n    pt: Layout de grade, útil para monitores grandes.\n    ro: Layout grilă, util pentru monitoare mari.\n    mt: Tqassim tal-grilja, utli għal monitors kbar.\n    sk: Rozloženie mriežky, užitočné pre veľké monitory.\n    ms: Susun atur grid, berguna untuk monitor besar.\n    fi: Ruudukkoasettelu, hyödyllinen suurille näytöille.\n    sv: Grid Layout, användbart för stora bildskärmar.\n    \"no\": Nettoppsett, nyttig for store skjermer.\n    uz: Katta monitorlar uchun foydali bo'lgan panjara.\n    tr: Izgara Düzeni, büyük monitörler için kullanışlıdır.\n    vi: Bố cục lưới, hữu ích cho màn hình lớn.\n    el: Διάταξη πλέγματος, χρήσιμη για μεγάλες οθόνες.\n    bg: Мрежово оформление, полезно за големи монитори.\n    so: Qaab-dhismeedka Grid, waxtar u leh kormeerayaasha waaweyn.\n    ru: Макет в виде сетки, полезный для больших мониторов.\n    yo: Ifilelẹ Grid, wulo fun awọn diigion nla.\n    uk: Сітчаста розкладка, корисна для великих моніторів.\n    mk: Распоред на решетки, корисно за големи монитори.\n    ar: تخطيط الشبكة، مفيد للشاشات الكبيرة.\n    mn: Big Monitors-д хэрэгтэй сүлжээ, хэрэгтэй.\n    tg: Тарҳбандии Grid, барои мониторҳои калон муфид аст.\n    sr: Грид изглед, користан за велике мониторе.\n    ka: ქსელის განლაგება, სასარგებლოა დიდი მონიტორებისთვის.\n    hy: \"Gr անցի դասավորություն, օգտակար մեծ մոնիտորների համար:\"\n    ur: گرڈ لے آؤٹ ، بڑے مانیٹر کے لئے مفید ہے۔\n    he: פריסת רשת, שימושית למסכים גדולים.\n    hi: ग्रिड लेआउट, बड़े मॉनिटर के लिए उपयोगी।\n    ps: د بریښنا ترتیب، د لوی څارونکو لپاره ګټور.\n    fa: طرح شبکه ، برای مانیتورهای بزرگ مفید است.\n    ne: ग्रिड लेआउट, ठूला मोनिटरहरूको लागि उपयोगी।\n    am: ለትላልቅ መቆጣጠሪያዎች ጠቃሚ, የፍርግርግ አቀማመጥ.\n    bn: গ্রিড লেআউট, বড় মনিটরের জন্য দরকারী।\n    ko: 그리드 레이아웃은 대형 모니터에 유용합니다.\n    pa: ਗਰਿੱਡ ਲੇਆਉਟ, ਵੱਡੇ ਮਾਨੀਟਰਾਂ ਲਈ ਲਾਭਦਾਇਕ.\n    zh: 网格布局，适用于大尺寸显示器。\n    ja: 大きなモニターに便利なグリッドレイアウト。\n    gu: ગ્રીડ લેઆઉટ, મોટા મોનિટર માટે ઉપયોગી.\n    ta: கட்டம் தளவமைப்பு, பெரிய மானிட்டர்களுக்கு பயனுள்ளதாக இருக்கும்.\n    te: గ్రిడ్ లేఅవుట్, పెద్ద మానిటర్లకు ఉపయోగపడుతుంది.\n    th: เลย์เอาต์กริดมีประโยชน์สำหรับจอภาพขนาดใหญ่\n    si: විශාල මොනිටර සඳහා ප්රයෝජනවත් ග්රිඩ් පිරිසැලසුම.\n    lo: ຮູບແບບຕາຂ່າຍໄຟຟ້າ, ມີປະໂຫຍດສໍາລັບຜູ້ຕິດຕາມຂ່າວໃຫຍ່.\n    km: ប្លង់ក្រឡាចត្រង្គមានប្រយោជន៍សម្រាប់ម៉ូនីទ័រធំ ៗ ។\nplugin:\n  structure:\n    type: Horizontal\n    children:\n      - type: Vertical\n        priority: 3\n        condition: managed >= 4\n        children:\n          - type: Leaf\n            condition: managed >= 7\n            priority: 3\n          - type: Leaf\n          - type: Leaf\n      - type: Vertical\n        priority: 1\n        children:\n          - type: Leaf\n            priority: 3\n            condition: if(is_reindexing, managed == 9, managed == 8)\n          - type: Leaf\n            priority: 1\n          - type: Leaf\n            condition: >-\n              if(is_reindexing, managed == 4 || managed >= 6, managed == 3 ||\n              managed >= 5)\n            priority: 2\n      - type: Vertical\n        priority: 2\n        children:\n          - type: Leaf\n            condition: if(is_reindexing, managed >= 7, managed >= 6)\n            priority: 3\n          - type: Leaf\n          - type: Leaf\n"
  },
  {
    "path": "src/static/plugins/wm_tall.yml",
    "content": "id: \"@default/wm-tall\"\ntarget: \"@seelen/window-manager\"\nmetadata:\n  displayName:\n    en: Tall Layout\n    cs: Vysoké rozložení\n    da: Højt layout\n    de: Großes Layout\n    af: Hoë uitleg\n    et: Kõrge paigutus\n    bs: Visok raspored\n    az: Hündür layout\n    es: Diseño alto\n    ca: Disseny alt\n    fr: Grand format\n    cy: Cynllun tal\n    id: Tata Letak Tinggi\n    tl: Matangkad na layout\n    it: Layout alto\n    eu: Diseinu altua\n    lv: Augsts izkārtojums\n    zu: Ukuhlelwa Okude\n    sw: Mpangilio mrefu\n    hr: Visok raspored\n    lt: Aukštas išdėstymas\n    hu: Magas elrendezés\n    is: Háu skipulagi\n    nl: Grote lay-out\n    ku: Şêwaza dirêj\n    pl: Wysoki układ\n    lb: Grouss Layout\n    sk: Vysoké rozloženie\n    ro: Layout înalt\n    ms: Susun atur tinggi\n    mt: Tqassim għoli\n    pt: Layout alto\n    \"no\": Høy layout\n    sv: Hög layout\n    fi: Tall Layout\n    uz: Uzun bo'yli tartib\n    tr: Uzun Düzen\n    el: Ψηλή διάταξη\n    bg: Високо оформление\n    so: Qaabeynta dhaadheer\n    vi: Bố cục cao\n    ru: Вертикальная раскладка окон\n    uk: Високе розташування\n    yo: Giga giga\n    mk: Висок распоред\n    mn: Дээд дэлх\n    ar: تخطيط طويل القامة\n    sr: Високи распоред\n    tg: Тарҳбандии баланд\n    hy: Բարձրահասակ դասավորություն\n    ka: მაღალი განლაგება\n    fa: طرح بلند\n    he: פריסה גבוהה\n    ur: لمبا ترتیب\n    ps: اوږد ترتیب\n    am: ረዣዥም አቀማመጥ\n    ne: अग्लो लेआउट\n    hi: लम्बा लेआउट\n    ta: உயரமான தளவமைப்பு\n    bn: লম্বা লেআউট\n    gu: Tallંચું લેઆઉટ\n    zh: 纵向布局\n    ja: トールレイアウト\n    pa: ਲੰਬਾ ਖਾਕਾ\n    ko: 키가 큰 레이아웃\n    te: పొడవైన లేఅవుట్\n    si: උස පිරිසැලසුම\n    th: เค้าโครงสูง\n    lo: ຮູບແບບສູງ\n    km: ប្លង់ខ្ពស់\n  description:\n    en: Tall layout for vertical monitors.\n    cs: Vysoké uspořádání pro svislé monitory.\n    da: Højt layout til lodrette skærme.\n    az: Şaquli monitorlar üçün hündür layout.\n    de: Hohes Layout für vertikale Monitore.\n    et: Kõrge paigutus vertikaalsete monitoride jaoks.\n    af: Hoë uitleg vir vertikale monitors.\n    bs: Visok raspored za vertikalne monitore.\n    ca: Disseny alt per a monitors verticals.\n    es: Disposición alta para monitores verticales.\n    fr: Disposition en hauteur pour les moniteurs verticaux.\n    cy: Cynllun tal ar gyfer monitorau fertigol.\n    id: Tata letak tinggi untuk monitor vertikal.\n    eu: Monitore bertikaletarako diseinu altua.\n    it: Layout alto per monitor verticali.\n    tl: Matangkad na layout para sa mga vertical na monitor.\n    hr: Visok izgled za vertikalne monitore.\n    lv: Augsts izkārtojums vertikāliem monitoriem.\n    lt: Aukštas vertikalių monitorių išdėstymas.\n    zu: Ukwakheka okumunwe kwabaqaphi mpo.\n    is: Hátt skipulag fyrir lóðrétta skjái.\n    hu: Magas elrendezés függőleges monitorokhoz.\n    nl: Hoge layout voor verticale monitors.\n    sw: Mpangilio mrefu kwa wachunguzi wa wima.\n    ku: Dirûvê dirêj ji bo çavdêrên vertical.\n    pl: Wysoki układ dla pionowych monitorów.\n    pt: Layout alto para monitores verticais.\n    lb: Grouss Layout fir vertikal Monitore.\n    ro: Dispunere înaltă pentru monitoare verticale.\n    sk: Vysoké rozloženie pre vertikálne monitory.\n    ms: Susun atur tinggi untuk monitor menegak.\n    fi: Pitkä asettelu pystysuorille näytöille.\n    \"no\": Høy oppsett for vertikale skjermer.\n    sv: Hög layout för vertikala bildskärmar.\n    mt: Tqassim għoli għal monitors vertikali.\n    tr: Dikey monitörler için uzun düzen.\n    uz: Vertikal monitorlar uchun baland bo'yli tartib.\n    el: Ψηλή διάταξη για κάθετες οθόνες.\n    bg: Високо оформление за вертикални монитори.\n    so: Qaab-dhismeedka dhaadheer ee kormeerayaasha toosan.\n    vi: Bố cục cao cho màn hình thẳng đứng.\n    ru: Макет раскладки окон для вертикальных мониторов.\n    yo: Giga giga fun awọn digi inaro.\n    uk: Висока розкладка для вертикальних моніторів.\n    mn: Босоо мониторын өндөр байрлал.\n    mk: Висок распоред за вертикални монитори.\n    ar: تصميم طويل للشاشات العمودية.\n    sr: Висок распоред вертикалних монитора.\n    tg: Тарҳбандии баланд барои мониторҳои амудӣ.\n    ka: მაღალი განლაგება ვერტიკალური მონიტორებისთვის.\n    ps: د عمودینو څارونکو لپاره اوږد ترتیب.\n    he: פריסה גבוהה למסכים אנכיים.\n    hy: \"Ուղղահայաց մոնիտորների բարձրահասակ դասավորություն:\"\n    ur: عمودی مانیٹر کے لئے لمبا ترتیب۔\n    am: ረዣዥም አቀማመጥ ለአቀባዊ መቆጣጠሪያዎች.\n    bn: উল্লম্ব মনিটরের জন্য লম্বা বিন্যাস।\n    fa: طرح بلند برای مانیتورهای عمودی.\n    ne: ठाडो मोनिटरहरूको लागि अग्लो सजावट।\n    hi: ऊर्ध्वाधर मॉनिटर के लिए लंबा लेआउट।\n    ko: 세로형 모니터를 위한 세로형 레이아웃.\n    pa: ਲੰਬਕਾਰੀ ਮਾਨੀਟਰਾਂ ਲਈ ਲੰਬਾ ਖਾਕਾ.\n    zh: 适用于垂直显示器的纵向布局。\n    gu: Vert ભી મોનિટર માટે tall ંચા લેઆઉટ.\n    ja: 縦長モニター用の背の高いレイアウト。\n    te: నిలువు మానిటర్ల కోసం పొడవైన లేఅవుట్.\n    ta: செங்குத்து மானிட்டர்களுக்கான உயரமான தளவமைப்பு.\n    th: เลย์เอาต์สูงสำหรับจอภาพแนวตั้ง\n    si: සිරස් මොනිටර සඳහා උස පිරිසැලසුම.\n    lo: ຮູບແບບສູງສໍາລັບຈໍຕິດຕາມຕັ້ງ.\n    km: ប្លង់ខ្ពស់សម្រាប់ម៉ូនីទ័របញ្ឈរ។\nplugin:\n  structure:\n    type: Horizontal\n    children:\n      - type: Leaf\n        growFactor: 1.5\n      - type: Vertical\n        children:\n          - type: Leaf\n          - type: Leaf\n          - type: Leaf\n          - type: Leaf\n"
  },
  {
    "path": "src/static/plugins/wm_wide.yml",
    "content": "id: \"@default/wm-wide\"\ntarget: \"@seelen/window-manager\"\nmetadata:\n  displayName:\n    en: Wide Layout\n    cs: Široké rozvržení\n    da: Bredt layout\n    de: Breites Layout\n    af: Wye uitleg\n    et: Lai paigutus\n    az: Geniş nizam\n    es: Diseño ancho\n    bs: Širok raspored\n    ca: Disseny ampli\n    fr: Disposition large\n    cy: Cynllun eang\n    id: Tata Letak Lebar\n    tl: Malawak na layout\n    eu: Diseinu zabala\n    it: Layout ampio\n    lv: Plašs izkārtojums\n    hr: Široki izgled\n    lt: Platus išdėstymas\n    hu: Széles elrendezés\n    zu: Ukwakheka okubanzi\n    is: Breitt skipulag\n    nl: Brede lay-out\n    sw: Mpangilio mpana\n    ku: Dirûvê fireh\n    pl: Szeroki układ\n    pt: Layout amplo\n    lb: Breet Layout\n    ms: Susun atur lebar\n    sk: Široké rozloženie\n    ro: Layout larg\n    mt: Tqassim wiesa '\n    fi: Laaja asettelu\n    sv: Bred layout\n    \"no\": Bred layout\n    uz: Keng tartib\n    tr: Geniş Düzen\n    el: Ευρεία διάταξη\n    bg: Широко оформление\n    so: Qaab-dhismeedka ballaaran\n    ru: Горизонтальная раскладка окон\n    mn: Дотуур хувь зүй\n    uk: Широка розкладка\n    vi: Bố cục rộng\n    yo: Aipẹ\n    mk: Широк распоред\n    ar: تخطيط عريض\n    he: פריסה רחבה\n    sr: Широк распоред\n    tg: Тарҳбандии васеъ\n    ka: ფართო განლაგება\n    hy: Լայն դասավորություն\n    ur: وسیع ترتیب\n    fa: چیدمان گسترده\n    am: ሰፊ አቀማመጥ\n    ps: پراخه ترتیب\n    ne: चौडाई लेआउट\n    bn: প্রশস্ত বিন্যাস\n    hi: चौड़ी लेआउट\n    ko: 와이드 레이아웃\n    gu: વિશાળ\n    zh: 横向布局\n    ta: பரந்த தளவமைப்பு\n    ja: ワイドレイアウト\n    pa: ਵਾਈਡ ਲੇਆਉਟ\n    te: విస్తృత లేఅవుట్\n    lo: ຮູບແບບກ້ວາງ\n    si: පුළුල් පිරිසැලසුම\n    th: เค้าโครงกว้าง\n    km: ប្លង់ធំទូលាយ\n  description:\n    en: Wide layout for horizontal monitors.\n    cs: Široké rozložení pro vodorovné monitory.\n    da: Bredt layout til vandrette skærme.\n    de: Breites Layout für horizontale Monitore.\n    et: Lai paigutus horisontaalsete monitoride jaoks.\n    es: Disposición ancha para monitores horizontales.\n    az: Üfüqi monitorlar üçün geniş plan.\n    bs: Širok raspored za horizontalne monitore.\n    af: Wye uitleg vir horisontale monitors.\n    ca: Disseny ampli per a monitors horitzontals.\n    fr: Disposition large pour les moniteurs horizontaux.\n    id: Tata letak lebar untuk monitor horizontal.\n    cy: Cynllun eang ar gyfer monitorau llorweddol.\n    tl: Malawak na layout para sa pahalang na monitor.\n    it: Layout ampio per monitor orizzontali.\n    eu: Monitore horizontaletarako diseinu zabala.\n    lv: Plašs izkārtojums horizontāliem monitoriem.\n    lt: Platus išdėstymas horizontaliems monitoriams.\n    zu: Ukwakheka okubanzi kwabaqaphi abavundlile.\n    hu: Széles elrendezés vízszintes monitorokhoz.\n    hr: Širok izgled za horizontalne monitore.\n    is: Breitt skipulag fyrir lárétta skjái.\n    sw: Mpangilio mpana kwa wachunguzi wa usawa.\n    nl: Brede lay-out voor horizontale monitoren.\n    ku: Ji bo çavdêrên horizontî şêwaza berbiçav.\n    lb: Breet Layout fir horizontale Monitore.\n    pt: Layout amplo para monitores horizontais.\n    pl: Szeroki układ dla monitorów poziomych.\n    sk: Široké rozloženie pre horizontálne monitory.\n    ms: Susun atur lebar untuk monitor mendatar.\n    mt: Tqassim wiesa 'għal monitors orizzontali.\n    ro: Dispunere largă pentru monitoare orizontale.\n    fi: Leveä asettelu vaakasuorille näytöille.\n    \"no\": Bred utforming for horisontale skjermer.\n    sv: Bred layout för horisontella bildskärmar.\n    uz: Gorizontal monitorlar uchun keng tayyorlang.\n    tr: Yatay monitörler için geniş düzen.\n    el: Ευρεία διάταξη για οριζόντιες οθόνες.\n    bg: Широко оформление за хоризонтални монитори.\n    so: Qaab-dhismeedka ballaaran ee kormeerayaasha toosan.\n    ru: Макет раскладки окон для горизонтальных мониторов.\n    vi: Bố cục rộng cho màn hình ngang.\n    yo: Jakejado akọkọ fun awọn diigi pere.\n    uk: Широка розкладка для горизонтальних моніторів.\n    mk: Широк распоред за хоризонтални монитори.\n    ar: تخطيط عريض للشاشات الأفقية.\n    sr: Широки распоред за хоризонталне мониторе.\n    mn: Хэвтээ мониторын өргөн байрлал.\n    tg: Тарҳбандии васеъ барои мониторҳои уфуқӣ.\n    ka: ფართო განლაგება ჰორიზონტალური მონიტორებისთვის.\n    he: פריסה רחבה למסכים אופקיים.\n    ur: افقی مانیٹر کے لئے وسیع ترتیب۔\n    hy: \"Հորիզոնական մոնիտորների լայն դասավորություն:\"\n    ps: پراخه ترتیب د افقی څارونکو لپاره.\n    fa: طرح گسترده برای مانیتورهای افقی.\n    hi: क्षैतिज मॉनिटर के लिए वाइड लेआउट।\n    ne: तेर्सो मोनिटरहरूको लागि फराकिलो सजावट।\n    am: ለአግድመት መቆጣጠሪያዎች ሰፊ አቀማመጥ.\n    ko: 가로 모니터를 위한 와이드 레이아웃.\n    zh: 适用于横向显示器的横向布局。\n    pa: ਖਿਤਿਜੀ ਮਾਨੀਟਰਾਂ ਲਈ ਵਾਈਡ ਲੇਆਉਟ.\n    bn: অনুভূমিক মনিটরের জন্য প্রশস্ত বিন্যাস।\n    ja: 横長モニター用のワイドレイアウト。\n    te: క్షితిజ సమాంతర మానిటర్ల కోసం విస్తృత లేఅవుట్.\n    gu: આડી મોનિટર માટે વિશાળ લેઆઉટ.\n    ta: கிடைமட்ட மானிட்டர்களுக்கான பரந்த தளவமைப்பு.\n    si: තිරස් මොනිටර සඳහා පුළුල් පිරිසැලසුම.\n    th: เค้าโครงกว้างสำหรับจอภาพแนวนอน\n    lo: ຮູບແບບກ້ວາງສໍາລັບຈໍຕິດຕາມແນວນອນ.\n    km: ប្លង់ធំទូលាយសម្រាប់ម៉ូនីទ័រផ្ដេក។\nplugin:\n  structure:\n    type: Vertical\n    children:\n      - type: Leaf\n        growFactor: 1.5\n      - type: Horizontal\n        children:\n          - type: Leaf\n          - type: Leaf\n          - type: Leaf\n          - type: Leaf\n"
  },
  {
    "path": "src/static/readme",
    "content": "All files in this folder are readonly, and should not be modified."
  },
  {
    "path": "src/static/themes/animated-start-icon/metadata.yml",
    "content": "id: \"@eythaann/animated-start-icon\"\nmetadata:\n  displayName:\n    en: Animated Start Icon\n    cs: Animovaná ikona Start\n    af: Geanimeerde begin -ikoon\n    da: Animeret start-ikon\n    et: Animeeritud Start ikoon\n    de: Animiertes Start-Symbol\n    es: Icono de inicio animado\n    az: Animated Start Icon\n    ca: Icona d'inici animat\n    bs: Ikona za početak animirane\n    fr: Icône de démarrage animée\n    cy: Eicon cychwyn animeiddiedig\n    id: Ikon Mulai Beranimasi\n    it: Icona Animata Avvio\n    eu: Animated Start ikonoa\n    tl: Animated Start icon\n    lv: Animēta sākuma ikona\n    hr: Ikona animiranog početka\n    lt: Animuota pradžios piktograma\n    hu: Animált Start ikon\n    zu: Isithonjana sokuqala\n    is: Teiknimynd upphafstákn\n    sw: Icon ya kuanza michoro\n    ku: Îkonê destpêkê ya animated\n    pl: Animowana ikona startowa\n    lb: Animéiert Start Ikon\n    pt: Ícone de início animado\n    ro: Iconiță de pornire animată\n    sk: Animovaná ikona Štart\n    mt: Ikona tal-bidu animata\n    ms: Ikon permulaan animasi\n    fi: Animoitu käynnistyskuvake\n    sv: Animerad start-ikon\n    \"no\": Animert startikon\n    uz: Animated boshlanish belgisi\n    nl: Geanimeerd startpictogram\n    el: Κινούμενο εικονίδιο έναρξης\n    bg: Анимирана начална икона\n    so: Animated bilowga sumadda\n    tr: Animasyonlu Başlangıç Simgesi\n    ru: Анимированный значок запуска\n    vi: Biểu tượng bắt đầu hoạt hình\n    yo: Aami ere idaraya bẹrẹ\n    mk: Анимирана икона за почеток\n    mn: Хөдөлгөөнт эхлүүлэх дүрс\n    ar: أيقونة البدء المتحرك\n    sr: Анимирани почетак икона\n    tg: Оғози аниматсионӣ\n    uk: Анімована піктограма запуску\n    hy: Անիմացիոն սկիզբ պատկերակ\n    ka: ანიმაციური დაწყების ხატი\n    he: סמל התחלה מונפש\n    ur: متحرک اسٹارٹ آئیکن\n    ps: متحرک د پیل کولو عکس\n    fa: نماد شروع انیمیشن\n    am: የታተሙ ጅምር አዶ\n    ne: एनिमेटेड सुरु आइकन\n    hi: एनिमेटेड स्टार्ट आइकन\n    bn: অ্যানিমেটেড স্টার্ট আইকন\n    ko: 애니메이션 시작 아이콘\n    pa: ਐਨੀਮੇਟਡ ਸਟਾਰਟ ਆਈਕਾਨ\n    th: ไอคอนเริ่มต้นเคลื่อนไหว\n    gu: એનિમેટેડ પ્રારંભ ચિહ્ન\n    ja: アニメーションスタートアイコン\n    ta: அனிமேஷன் தொடக்க ஐகான்\n    zh: 动画启动图标\n    te: యానిమేటెడ్ ప్రారంభ చిహ్నం\n    si: සජීවිකරණ ආරම්භක නිරූපකය\n    lo: ຮູບສັນຍາລັກເລີ່ມຕົ້ນເຄື່ອນໄຫວ\n    km: រូបតំណាងចាប់ផ្តើមមានចលនា\n  description:\n    en: Replace start icon by an animated version\n    cs: Nahrazení ikony spuštění animovanou verzí\n    da: Udskift start-ikonet med en animeret version\n    de: Ersetzen des Startsymbols durch eine animierte Version\n    af: Vervang Start -ikoon deur 'n geanimeerde weergawe\n    et: Asendage stardi ikoon animeeritud versiooniga\n    es: Sustituir el icono de inicio por una versión animada\n    az: Başlanğıc simgesini cizgi versiyası ilə əvəz edin\n    bs: Zamijenite ikonu Start pomoću animirane verzije\n    ca: Substituïu la icona d'inici per una versió animada\n    fr: Remplacer l'icône de démarrage par une version animée\n    cy: Amnewid eicon cychwyn gan fersiwn wedi'i animeiddio\n    id: Mengganti ikon mulai dengan versi animasi\n    it: Sostituire l'icona di avvio con una versione animata\n    eu: Ordeztu hasteko ikonoa animaziozko bertsio baten bidez\n    hr: Zamijenite ikonu Start animiranom verzijom\n    tl: Palitan ang icon ng Start sa pamamagitan ng isang animated na bersyon\n    lv: Aizstāt sākuma ikonu ar animētu versiju\n    lt: Pakeiskite pradžios piktogramą animuotu variantu\n    zu: Faka esikhundleni sesithonjana sokuqala yinguqulo enezithombe\n    hu: Start ikon cseréje animált verzióra\n    sw: Badilisha icon ya kuanza na toleo lenye michoro\n    ku: Îkonê destpêkê ji hêla guhertoyek anîmasyon ve veguherînin\n    is: Skiptu um upphafstákn með teiknimyndútgáfu\n    nl: Startpictogram vervangen door een geanimeerde versie\n    pl: Zastąp ikonę startu animowaną wersją\n    pt: Substituir o ícone de início por uma versão animada\n    ro: Înlocuiți pictograma de pornire cu o versiune animată\n    ms: Ganti ikon Mula dengan versi animasi\n    lb: Ersatz Start Ikon duerch eng animéiert Versioun\n    sk: Nahradenie ikony štartu animovanou verziou\n    uz: Boshlash belgisini jonlantirilgan versiya orqali almashtiring\n    \"no\": Erstatt Start -ikonet med en animert versjon\n    mt: Ibdel l-ikona tal-bidu b'verżjoni animata\n    sv: Ersätt startikonen med en animerad version\n    fi: Korvaa käynnistyskuvake animoidulla versiolla\n    tr: Başlat simgesini animasyonlu bir sürümle değiştirme\n    el: Αντικαταστήστε το εικονίδιο έναρξης με μια κινούμενη έκδοση\n    bg: Замяна на стартовата икона с анимирана версия\n    so: Ku beddelo bilowga icon nooc firfircoon\n    ru: Замените значок запуска на анимированный вариант\n    vi: Thay thế biểu tượng Bắt đầu bằng phiên bản hoạt hình\n    yo: Rọpo aami Ibẹrẹ nipasẹ ẹya ere idaraya\n    uk: Замінити іконку запуску на анімовану версію\n    mk: Заменете ја иконата за почеток со анимирана верзија\n    mn: Эхлэх дүрсийг хөдөлгөөнт хувилбараар солих\n    ar: استبدال أيقونة البدء بنسخة متحركة\n    sr: Замените икону Старт Анимираном верзијом\n    ur: ایک متحرک ورژن کے ذریعہ اسٹارٹ آئیکن کو تبدیل کریں\n    tg: Тасвири оғозро бо нусхаи аниматалӣ иваз кунед\n    ka: შეცვალეთ დაწყების ხატი ანიმაციური ვერსიით\n    hy: Փոխեք մեկնարկի պատկերակը անիմացիոն տարբերակի միջոցով\n    fa: نماد شروع را با یک نسخه متحرک جایگزین کنید\n    he: החלף את סמל START בגרסת אנימציה\n    ps: د متحرک نسخې لخوا د پیل کاپي بدل کړئ\n    am: በተነቃቃ ስሪት የመጀመር አዶን ይተኩ\n    hi: एक एनिमेटेड संस्करण द्वारा प्रारंभ आइकन को बदलें\n    bn: একটি অ্যানিমেটেড সংস্করণ দ্বারা শুরু আইকন প্রতিস্থাপন করুন\n    ne: एनिमेटेड संस्करण द्वारा सुरू आइकन बदल्नुहोस्\n    pa: ਸਟਾਰਟ ਆਈਕਨ ਨੂੰ ਐਨੀਮੇਟਡ ਵਰਜ਼ਨ ਦੁਆਰਾ ਬਦਲੋ\n    ta: தொடக்க ஐகானை அனிமேஷன் பதிப்பால் மாற்றவும்\n    gu: એનિમેટેડ સંસ્કરણ દ્વારા પ્રારંભ ચિહ્નને બદલો\n    zh: 将开始图标替换为动画版本\n    ko: 시작 아이콘을 애니메이션 버전으로 바꾸기\n    ja: スタートアイコンをアニメーションに置き換える\n    th: แทนที่ไอคอน Start ด้วยเวอร์ชันภาพเคลื่อนไหว\n    te: ప్రారంభ చిహ్నాన్ని యానిమేటెడ్ వెర్షన్ ద్వారా మార్చండి\n    si: සජීවිකරණ අනුවාදයකින් ආරම්භක නිරූපකය ආදේශ කරන්න\n    lo: ປ່ຽນໄອຄອນເລີ່ມຕົ້ນໂດຍສະບັບເຄື່ອນໄຫວ\n    km: ជំនួសរូបតំណាងចាប់ផ្តើមដោយកំណែដែលមានចលនា\n"
  },
  {
    "path": "src/static/themes/animated-start-icon/seelen/weg.scss",
    "content": "$wave: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 150 150'%3E%3C!-- %3Crect width='100%25' height='100%25' fill='green' /%3E --%3E%3Cpath fill='%23fff' d='M0 20 C30 20, 45 10, 75 10 S120 20, 150 20 V150 H0 Z' /%3E%3C/svg%3E\");\n\n.weg-item-start-icon {\n  display: none;\n}\n\n.weg-item:has(.weg-item-start-icon) {\n  .bg-layer-6, .bg-layer-7, .bg-layer-8, .bg-layer-9 {\n    width: 75% !important;\n    height: 75% !important;\n    left: 50%;\n    top: 50%;\n    transform: translate(-50%, -50%);\n    border-radius: 50%;\n    will-change: transform;\n  }\n\n  .bg-layer-6, .bg-layer-7, .bg-layer-8 {\n    width: 60% !important;\n    height: 60% !important;\n    mask-image: $wave;\n    mask-repeat: repeat-x;\n    mask-position: bottom;\n    mask-size: 90%;\n    animation: move-forever 5s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;\n  }\n\n  .bg-layer-6 {\n    background-color: var(--system-accent-dark-color);\n    animation-delay: -2s;\n    animation-duration: 7s;\n    animation-name: move-forever-1;\n  }\n\n  .bg-layer-7 {\n    background-color: var(--system-accent-light-color);\n    animation-delay: -3s;\n    animation-duration: 10s;\n    animation-name: move-forever-2;\n  }\n\n  .bg-layer-8 {\n    background-color: var(--system-accent-lighter-color);\n    animation-delay: -4s;\n    animation-duration: 13s;\n    animation-name: move-forever-3;\n  }\n\n  .bg-layer-9 {\n    border: 1.5px solid black;\n  }\n}\n\n@keyframes move-forever-1 {\n  0% {\n    mask-position: -900% 300%;\n  }\n  100% {\n    mask-position: 900% 300%;\n  }\n}\n\n@keyframes move-forever-2 {\n  0% {\n    mask-position: -900% 350%;\n  }\n  100% {\n    mask-position: 900% 350%;\n  }\n}\n\n@keyframes move-forever-3 {\n  0% {\n    mask-position: -900% 400%;\n  }\n  100% {\n    mask-position: 900% 400%;\n  }\n}\n"
  },
  {
    "path": "src/static/themes/bubbles/i18n/description.yml",
    "content": "ar: فقاعات تقول بوب.\nam: አረፋዎች ብቅ ይላል.\nja: 泡が弾ける。\nmt: Bubbles jgħid pop.\nlb: Bubbles seet de Pop.\nku: Bubbles dibêje pop.\nno: Bubbles sier pop.\nru: Пузырьки говорят \"хлоп\".\nis: Bubbles segir popp.\nte: బుడగలు పాప్ చెప్పారు.\npa: ਬੁਲਬਲੇ ਪੌਪ ਕਹਿੰਦਾ ਹੈ.\nth: Bubbles พูดว่าป๊อป\nro: Bubbles spune pop.\nmk: Меурчиња вели Поп.\nlv: Burbuļi saka pop.\nen: Bubbles says pop.\npt-PT: Bolhas diz pop.\naz: Bubbles deyir ki, pop.\nhi: बुलबुले कहते हैं पॉप।\nhu: Bubbles azt mondja, hogy pop.\nit: Bolle dice pop.\nyo: Awọn opo sọ pe agbejade.\nfa: حباب می گوید پاپ.\nps: بلبلونه د پاپ دي.\ncy: Mae swigod yn dweud pop.\nso: Xumbo ayaa tiri pop.\nfr: Les bulles disent \"pop\".\nnl: Bubbles zegt pop.\nbn: বুদবুদ পপ বলে।\nzh-TW: 泡泡說“流行”。\nsw: Bubbles anasema pop.\nda: Bobler siger pop.\nfi: Bubbles sanoo pop.\npl: Bąbelki mówią pop.\naf: Bubbles sê pop.\ngu: પરપોટા કહે છે પ pop પ.\npt-BR: Bubbles says pop.\nms: Bubbles mengatakan pop.\nzu: Ama-Bubble athi pop.\nde: Bubbles sagt Pop.\nuk: Бабблз каже \"поп\".\nsi: බබල් පවසන්නේ පොප් බවයි.\neu: Burbuilak Pop dio.\nzh-CN: 泡泡说“流行”。\nbs: Mjehurići kaže da pop.\nkm: ពពុះនិយាយថាប៉ុប។\nko: 버블은 팝이라고 말합니다.\nes: Bubbles dice pop.\npt: Bubbles diz pop.\ntl: Sinabi ni Bubbles Pop.\ntr: Bubbles pop diyor.\nhe: בועות אומר פופ.\nvi: Bong bóng nói pop.\nsk: Bubliny hovoria pop.\nbg: Балончетата казват pop.\nel: Οι φυσαλίδες λένε pop.\ntg: Футболҳо мегӯяд, ки поп.\nsv: Bubblorna säger \"pop\".\ncs: Bubliny říkají pop.\nlt: Burbuliukai sako pop.\nhr: Bubbles kaže pop.\nka: ბუშტები ამბობს პოპ.\net: Bubbles ütleb pop.\nuz: Pufakchalar pop.\nzh: 泡泡说 \"啪\"。\nta: குமிழ்கள் பாப் என்று கூறுகிறது.\nmn: Бөмбөлөгүүд поп хэлэв.\nur: بلبلوں کا کہنا ہے کہ پاپ۔\nid: Gelembung mengatakan pop.\nlo: ຟອງເວົ້າວ່າ POP.\nca: Bubbles diu Pop.\nhy: \"Փուչիկները ասում են փոփը:\"\nsr: Бубблес каже Поп.\nne: बुलबुले पप भन्छन्।\n"
  },
  {
    "path": "src/static/themes/bubbles/i18n/display_name.yml",
    "content": "so: Xumbo\nms: Gelembung\nde: Blasen\nuz: Pufakcha\nhy: Փուչիկներ\nth: ฟอง\nro: Bule\nuk: Бульбашки\nmk: Меурчиња\neu: Burbuilak\ntg: Футури\nlo: ຟອງ\nbn: বুদবুদ\ncs: Bubliny\nsr: Мехурићи\nmt: Bżieżaq\npt-BR: Bubbles\nsw: Bubbles\nte: బుడగలు\nhe: בועות\nne: पाता\npl: Bąbelki\nfr: Bulles\nbs: Mjehurići\nkm: ពពុះ\npt: Bolhas\nca: Bombolles\nlv: Burbuļi\nsk: Bubliny\nta: குமிழ்கள்\net: Mullid\ncy: Swigod\nlb: Blables\nid: Gelembung\nfa: حباب\npa: ਬੁਲਬਲੇ\nka: ბუშტები\nen: Bubbles\nmn: Хулсандийн цаас\nru: Пузырьки\nsi: බුබුලු\nja: 泡\naz: Baloncuklar\nlt: Burbuliukai\nzh-TW: 氣泡\nno: Bobler\nsv: Bubblor\nfi: Bubbles\ngu: પરપોટા\ntl: Mga bula\nes: Burbujas\nda: Bobler\ntr: Kabarcıklar\nnl: Bubbels\nyo: Awọn eeyan\nzh: 气泡\npt-PT: Bolhas\nar: الفقاعات\nvi: Bong bóng\nis: Bubbles\nps: بلبلونه\nhu: Buborékok\nzu: Ama-bubble\nko: 거품\nam: አረፋዎች\nhi: बबल\nhr: Mjehurići\nit: Bolle di sapone\nel: Φυσαλίδες\naf: Borrels\nbg: Балончета\nku: Bubbles\nzh-CN: 气泡\nur: بلبلوں\n"
  },
  {
    "path": "src/static/themes/bubbles/mod.yml",
    "content": "id: \"@eythaann/bubbles\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\n  tags:\n    - toolbar\nstyles:\n  \"@seelen/fancy-toolbar\": !include styles/toolbar.css\n"
  },
  {
    "path": "src/static/themes/bubbles/styles/toolbar.css",
    "content": ".ft-bar-bg-layer-1 {\n  display: none;\n}\n\n.ft-bar-bg-layer-2 {\n  display: none;\n}\n\n.ft-bar-item {\n  color: var(--color-fixed-gray-100);\n  background-color: var(--color-fixed-gray-900);\n  border-radius: 10px;\n  padding: 2px 8px;\n  height: 25px;\n  transition-property: color, background-color;\n  transition-duration: 200ms;\n  transition-timing-function: linear;\n\n  &.ft-bar-item-clickable {\n    padding: 2px 8px;\n    border-radius: 10px;\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/i18n/description.yml",
    "content": "ur: پہلے سے طے شدہ سیلن تھیم\nuk: Тема за замовчуванням\ncs: Výchozí téma Seelen\neu: Lehenetsitako Seelen Gaia\naf: Standaard Seelen -tema\nel: Προεπιλεγμένο θέμα Seelen\nro: Tema Seelen implicită\nta: இயல்புநிலை சீலன் தீம்\nes: Tema predeterminado de Seelen\nzh-CN: 默认 Seelen 主题\nka: ნაგულისხმევი Seelen თემა\nvi: Chủ đề Seelen mặc định\nsr: Подразумевано Сеелен Тхеме\nth: ธีม Seelen เริ่มต้น\nmt: Tema Seelen Default\nyo: Aiyipada Esence Verner\nfr: Thème Seelen par défaut\nmn: Үндсэн Seelen сэдэв\nms: Tema Seelen lalai\nzu: Ingqikithi ye-Seelen ezenzakalelayo\nar: سمة سيلين الافتراضية\npt: Tema padrão da Seelen\ntr: Varsayılan Seelen Teması\nis: Sjálfgefið Seelen þema\nsw: Mada ya Seelen Default\nbs: Default Seelen Theme\npt-PT: Tema Seelen padrão\nhe: נושא סילן ברירת מחדל\nbn: ডিফল্ট সেলেন থিম\nfi: Oletus Seelen-teema\nid: Tema Seelen Default\nit: Tema Seelen predefinito\nko: 기본 Seelen 테마\npt-BR: Default Selen Theme\net: Vaikimisi Seelen teema\nde: Standard-Seelen-Theme\nja: デフォルトのシーレン・テーマ\nsv: Standardtema för Seelen\nhy: Լռելյայն սելենային թեման\nmk: Стандардна тема на Селен\nbg: Тема по подразбиране на Seelen\nzh: 默认 Seelen 主题\ncy: Thema Seelen Default\nzh-TW: 默認 Seelen 主題\ntg: Мавзӯи нобаёнӣ\nlb: Standardvires Themen\ntl: Default na tema ng Seelen\nuz: Standart Sayelen mavzusida\naz: Defolt Seelen tema\nsk: Predvolená téma Seelen\ngu: ડિફોલ્ટ સીલેન થીમ\nen: Default Seelen Theme\nda: Standard Seelen-tema\npa: ਮੂਲ ਧਾਰਨ ਥੀਮ\nne: पूर्वनिर्धारित देखिने थिम\nca: Tema de Seelen per defecte\npl: Domyślny motyw Seelen\nlv: Noklusējuma Seelen tēma\nso: Mowduuca caadiga ah ee selen\nru: Тема Seelen по умолчанию\nlt: Numatytoji \"Seelen\" tema\nhr: Zadana tema Seelen\nps: د ډیفالټ لیدل شوی موضوع\nnl: Standaard Seelen Thema\nku: Mijara Default Seelen\nsi: පෙරනිමි සොලන්නා තේමාව\nhi: डिफ़ॉल्ट सेलेन थीम\nno: Standard Seelen -tema\nlo: ຫົວຂໍ້ Seelen ໃນຕອນຕົ້ນ\nfa: موضوع پیش فرض Seelen\nkm: លំនាំសេលេនលំនាំលំនាំដើម\nte: డిఫాల్ట్ సీలెన్ థీమ్\nam: ነባሪ የማያመር ጭብጥ\nhu: Alapértelmezett Seelen téma\n"
  },
  {
    "path": "src/static/themes/default/i18n/display_name.yml",
    "content": "mk: Стандардно\ntr: Varsayılan\nhe: בְּרִירַת מֶחדָל\nso: Fulin la'aan\nel: Προεπιλογή\nit: Predefinito\nzh-TW: 預設\nsv: Standard\nen: Default\nur: پہلے سے طے شدہ\nnl: Standaard\ngu: ચૂક\nda: Standard\nbs: Podrazumevano\nfr: Défaut\nte: డిఫాల్ట్\ncs: Výchozí\nhi: गलती करना\nsi: පෙරනිමි\nlt: Numatytoji\nkm: ការមិនកោរបតាមសន្យា\nro: Implicit\naf: Versuim\nuz: Bajarilish\ntl: Default\net: Vaikimisi\npa: ਮੂਲ\nlv: Noklusējuma\nzh: 默认值\nzu: Phutha\nfa: پیش فرض\nhy: Թերություն\nms: Lalai\nam: ነባሪ\ncy: Diofyn\nes: Por defecto\nhr: Zadano\ntg: Иьро накардани ӯҳдадорӣ\nis: Sjálfgefið\nuk: За замовчуванням\nsw: Chaguo -msingi\nhu: Alapértelmezett\nfi: Oletusarvo\nzh-CN: 默认\npt-PT: Predefinição\nko: 기본값\naz: Defolt\nlo: ໃນຕອນຕົ້ນ\neu: Ez ordaindu\npt-BR: Default\nsr: Подразумевано\nsk: Predvolené nastavenie\npl: Domyślne\nbg: По подразбиране\nca: No pagar\nbn: ডিফল্ট\nyo: Aiyipada\nta: இயல்புநிலை\nja: デフォルト\nka: ვალდებულების შეუსრულებლობა\nru: По умолчанию\nps: ډیفالټ\nno: Misligholde\nmt: Default\nvi: Mặc định\nlb: Asofbild\nmn: Үл хүрэлцэх\nne: पुरा नगर्नु\nde: Standard\nth: ค่าเริ่มต้น\nid: Default\nar: افتراضي\nku: Destçûnî\n"
  },
  {
    "path": "src/static/themes/default/metadata.yml",
    "content": "id: \"@default/theme\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\n  tags:\n    - weg\n    - toolbar\n    - window-manager\nstyles:\n  # For devs: key is the id of the target widget and value is the relative path to the style.\n  # For third-party widgets uses the real widget id, friendly ids are only for bundle or local themes,\n  # as friendly ids are not stable.\n  \"@seelen/fancy-toolbar\": !include styles/fancy-toolbar.scss\n  \"@seelen/power-menu\": !include styles/power-menu.scss\n  \"@seelen/task-switcher\": !include styles/task-switcher.scss\n  \"@seelen/wallpaper-manager\": !include styles/wallpaper-manager.css\n  \"@seelen/weg\": !include styles/weg.css\n  \"@seelen/window-manager\": !include styles/window-manager.css\n  \"@seelen/system-tray\": !include styles/tray-menu.scss\n  \"@seelen/quick-settings\": !include styles/quick-settings.scss\n  \"@seelen/bluetooth-popup\": !include styles/bluetooth-popup.scss\n  \"@seelen/workspaces-viewer\": !include styles/workspaces-viewer.scss\n  \"@seelen/apps-menu\": !include styles/apps-menu.scss\n  \"@seelen/keyboard-selector\": !include styles/keyboard-selector.scss\n  \"@seelen/user-menu\": !include styles/user-menu.scss\n  \"@seelen/network-popup\": !include styles/network-popup.scss\n  \"@seelen/calendar-popup\": !include styles/calendar-popup.scss\n  \"@seelen/media-popup\": !include styles/media-popup.scss\n  \"@seelen/notifications\": !include styles/notifications.scss\n  \"@seelen/context-menu\": !include styles/context-menu.scss\n  \"@seelen/flyouts\": !include styles/flyouts.css\n\n# shared styles across all widgets\nsharedStyles: !include ./shared/index.scss\n"
  },
  {
    "path": "src/static/themes/default/shared/buttons.scss",
    "content": "%common {\n  padding: 0.25em 0.5em;\n  border-radius: 6px;\n  display: flex;\n  align-items: center;\n  gap: 0.5em;\n\n  &:active {\n    transform: translateY(2px);\n  }\n}\n\ndiv[data-behavior=\"group\"] {\n  display: flex;\n\n  > button:not(:first-child) {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n  }\n\n  > button:not(:last-child) {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n}\n\nbutton[data-skin=\"transparent\"] {\n  @extend %common;\n  border: 1px solid transparent;\n\n  &:hover {\n    background-color: oklch(from var(--system-accent-color) l c h / 0.2);\n  }\n}\n\nbutton[data-skin=\"solid\"] {\n  @extend %common;\n  background-color: var(--ui-color);\n  border: 1px solid var(--ui-color);\n  color: var(--color-gray-100);\n\n  &:hover {\n    background-color: var(--ui-hover-color);\n    border: 1px solid var(--ui-hover-color);\n  }\n}\n\nbutton[data-skin=\"default\"] {\n  @extend %common;\n  background-color: var(--color-gray-50);\n  border: 1px solid var(--color-gray-300);\n\n  &:hover {\n    border: 1px solid var(--ui-hover-color);\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/shared/index.scss",
    "content": "/*\n * The idea with this feature of Shared styles over all widgets, is to provide a way to\n * reusable components and styles that can be used across all widgets.\n *\n * As example all bundled widgets related to popups uses these shared styles.\n *\n * By default we shouldn't touch global scopes like `body`, `button`, `input` etc.\n * Instead we use data attributes to target specific elements, and only if the developer\n * decides to use these shared components/styles in their widgets.\n */\n\n@import \"./inputs.scss\";\n@import \"./buttons.scss\";\n\n:root {\n  --ui-color: light-dark(var(--system-accent-dark-color), var(--system-accent-light-color));\n  --ui-hover-color: light-dark(\n    var(--system-accent-darker-color),\n    var(--system-accent-lighter-color)\n  );\n}\n\n// Used by performance mode to reduce cpu/gpu usage\nhtml[data-animations-off] *,\nhtml[data-animations-off] *::before,\nhtml[data-animations-off] *::after {\n  animation-duration: 0.001ms !important;\n  animation-iteration-count: 1 !important;\n  animation-delay: 0s !important;\n\n  transition-duration: 0.001ms !important;\n  transition-delay: 0s !important;\n\n  scroll-behavior: auto !important;\n}\n\n// Accessibility Improvement\n*:focus-visible {\n  outline-offset: 2px;\n  outline-style: solid;\n  outline-width: 2px;\n  outline-color: var(--system-accent-color);\n}\n\n// standard scroll\n* {\n  ::-webkit-scrollbar {\n    width: 8px;\n    height: 8px;\n  }\n\n  ::-webkit-scrollbar-track {\n    background-color: transparent;\n  }\n\n  ::-webkit-scrollbar-thumb {\n    background-color: var(--color-gray-400);\n    border-radius: 8px;\n\n    &:active {\n      background-color: var(--color-gray-500);\n    }\n  }\n}\n\n// Standard for popover\nbody:has(.slu-standard-popover) {\n  background-color: transparent;\n  overflow: hidden;\n\n  > #root,\n  > #app {\n    width: min-content;\n    height: min-content;\n    overflow: hidden; // disable margin collapse\n  }\n}\n\n.slu-standard-popover {\n  width: 300px;\n  max-height: 700px;\n  overflow: auto;\n\n  padding: 10px;\n  margin: 10px; // this margin will allow the box-shadow to be visible\n\n  background-color: var(--color-gray-50);\n  color: var(--color-gray-900);\n  border-radius: 10px;\n\n  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.3);\n  font-size: 0.8rem;\n}\n"
  },
  {
    "path": "src/static/themes/default/shared/inputs.scss",
    "content": "input[type=\"range\"] {\n  appearance: none;\n  width: 100%;\n  position: relative;\n  overflow: hidden;\n}\n\ninput[type=\"range\"][data-skin=\"flat\"] {\n  height: 1.5rem;\n  border-radius: 8px;\n  background: var(--color-gray-300);\n\n  &::-webkit-slider-runnable-track {\n    appearance: none;\n  }\n\n  &::-webkit-slider-thumb {\n    appearance: none;\n    width: 0;\n    height: 0;\n    box-shadow: -100vw 0 0 100vw var(--ui-color);\n  }\n\n  &[data-orientation=\"vertical\"] {\n    width: 1.5rem;\n    height: 100%;\n    writing-mode: vertical-lr;\n    direction: rtl;\n\n    &::-webkit-slider-thumb {\n      box-shadow: 0 100vh 0 100vh var(--ui-color);\n    }\n  }\n}\n\ninput[type=\"text\"][data-skin=\"default\"],\ninput[type=\"search\"][data-skin=\"default\"],\nselect[data-skin=\"default\"] {\n  width: 100%;\n  padding: 4px 8px;\n\n  background-color: var(--color-gray-50);\n  color: var(--color-gray-900);\n  border: 1px solid var(--color-gray-300);\n  border-radius: 6px;\n\n  font-size: 0.9rem;\n\n  &:hover {\n    border-color: var(--ui-hover-color);\n  }\n\n  &[data-error=\"true\"] {\n    border-color: var(--color-red-600);\n  }\n\n  &:read-only {\n    background-color: var(--color-gray-100);\n    cursor: default;\n  }\n\n  &:disabled {\n    background-color: var(--color-gray-100);\n    color: var(--color-gray-600);\n    cursor: default;\n  }\n}\n\ninput[type=\"text\"][data-skin=\"transparent\"],\ninput[type=\"search\"][data-skin=\"transparent\"] {\n  width: 100%;\n  padding: 4px 8px;\n\n  background-color: transparent;\n  color: inherit;\n  border: 1px solid transparent;\n  border-radius: 6px;\n\n  font-size: 0.9rem;\n\n  &:focus {\n    /* background-color: var(--color-gray-50);\n    border-color: var(--ui-hover-color); */\n    outline: none;\n  }\n\n  &[data-error=\"true\"] {\n    border-color: var(--color-red-600);\n  }\n\n  &:read-only {\n    background-color: transparent;\n    cursor: default;\n  }\n\n  &:disabled {\n    opacity: 0.8;\n    background-color: transparent;\n    cursor: default;\n  }\n}\n\ninput[type=\"checkbox\"][data-skin=\"switch\"] {\n  $width: 36px;\n  $height: 18px;\n\n  appearance: none;\n  position: relative;\n  width: $width;\n  height: $height;\n  background-color: var(--color-gray-400);\n  border-radius: $height / 2;\n  transition: background-color 0.3s ease;\n\n  &::before {\n    content: \"\";\n    position: absolute;\n    width: $height - 4;\n    height: $height - 4;\n    border-radius: 50%;\n    background-color: var(--color-gray-50);\n    top: 2px;\n    left: 2px;\n    transition: transform 0.3s ease;\n  }\n\n  &:checked {\n    background-color: var(--ui-color);\n\n    &::before {\n      transform: translateX($width / 2);\n    }\n  }\n\n  &:hover {\n    &:not(:checked) {\n      background-color: var(--color-gray-500);\n    }\n  }\n\n  &:disabled {\n    opacity: 0.5;\n    cursor: not-allowed;\n  }\n}\n\n::picker(select) {\n  appearance: base-select;\n  border: none;\n  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.3);\n  border-radius: 10px;\n  background-color: var(--color-gray-50);\n  padding: 0.25rem;\n}\n\nselect[data-skin=\"default\"] {\n  display: flex;\n  appearance: base-select;\n\n  &::picker-icon {\n    color: var(--color-gray-400);\n    width: min-content;\n    transition: transform 0.4s ease;\n  }\n\n  &:open::picker-icon {\n    transform: rotate(180deg);\n  }\n\n  option {\n    padding: 0.25rem 0.5rem;\n    border-radius: 8px;\n    margin-bottom: 0.5rem;\n\n    &:last-of-type {\n      margin-bottom: 0;\n    }\n\n    &:hover {\n      background-color: oklch(from var(--ui-color) l c h / 0.2);\n    }\n\n    &:checked {\n      background-color: var(--ui-hover-color);\n      color: var(--color-gray-100);\n      font-weight: 600;\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/apps-menu.scss",
    "content": ":root {\n  --columns: 7;\n  --folder-columns: 5;\n  --bg-light: RGB(from var(--system-accent-color) r g b / 0.6);\n}\n\nbody {\n  background-color: transparent;\n  overflow: hidden;\n}\n\n.apps-menu {\n  width: 100vw;\n  height: 100vh;\n  display: grid;\n  grid-template-rows: min-content 1fr min-content;\n  background-color: light-dark(#f4f4f4d0, #272727d0);\n\n  &[data-acrylic=\"true\"] {\n    background-color: light-dark(#fff8, #0008);\n  }\n\n  .apps-menu-header {\n    width: 100%;\n    padding: 10px;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 10px;\n    border-bottom: 1px solid var(--color-gray-300);\n\n    button {\n      flex-shrink: 0;\n    }\n\n    input[type=\"search\"] {\n      flex: 1;\n    }\n\n    select {\n      width: 120px;\n    }\n  }\n\n  .apps-menu-body {\n    width: 100%;\n    height: 100%;\n    overflow-y: auto;\n    overflow-x: hidden;\n  }\n\n  .apps-menu-footer {\n    width: 100%;\n    padding: 10px;\n    border-top: 1px solid var(--color-gray-300);\n\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n\n    .user-profile {\n      white-space: nowrap;\n      font-weight: 600;\n      font-size: 0.9rem;\n\n      .user-profile-picture {\n        height: 1.5rem !important;\n        width: 1.5rem !important;\n        border-radius: 50%;\n      }\n    }\n\n    .apps-menu-footer-left,\n    .apps-menu-footer-right {\n      display: flex;\n      align-items: center;\n      justify-content: flex-end;\n      gap: 0.5rem;\n    }\n  }\n}\n\n.pinned-view {\n  padding: 2rem;\n  height: 100%;\n\n  .pinned-view-list {\n    display: grid;\n    grid-template-columns: repeat(var(--columns), 1fr);\n    gap: 1rem;\n\n    // .app {}\n  }\n\n  // .pinned-view-empty {}\n}\n\n.all-apps-view {\n  padding: 2rem;\n  height: 100%;\n\n  .all-apps-view-list {\n    display: grid;\n    grid-template-columns: repeat(var(--columns), 1fr);\n    gap: 1rem;\n\n    // .app {}\n  }\n\n  // .all-apps-view-empty {}\n}\n\n.pinned-view-empty,\n.all-apps-view-empty {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  color: var(--color-gray-700);\n  font-style: italic;\n  font-size: 1rem;\n  font-weight: 600;\n  gap: 0.5rem;\n}\n\n.app {\n  display: grid;\n\n  align-items: center;\n  justify-items: center;\n  gap: 10px;\n  padding: 10px;\n  border-radius: 10px;\n\n  &:hover {\n    background-color: var(--bg-light);\n  }\n\n  &.preselected {\n    background-color: var(--bg-light);\n    outline: 2px solid var(--system-accent-color);\n    outline-offset: 2px;\n  }\n\n  &:active {\n    transform: scale(0.95);\n  }\n\n  &.is-drop-target {\n    border: 3px dashed var(--system-accent-color);\n    transform: scale(1.05);\n\n    .app-name {\n      opacity: 0;\n    }\n  }\n\n  > * {\n    pointer-events: none;\n  }\n\n  .app-icon {\n    width: 60%;\n    aspect-ratio: 1/1;\n    border-radius: 16%;\n    box-shadow: rgba(0, 0, 0, 0.1) -4px 9px 25px -6px;\n\n    img {\n      border-radius: 16%;\n    }\n\n    &:not([data-shape=\"square\"]) {\n      padding: 8%;\n      background-color: var(--color-gray-100);\n    }\n  }\n\n  .app-name {\n    width: 100%;\n    height: 2.4em;\n\n    display: -webkit-box;\n    -webkit-box-orient: vertical;\n    -webkit-line-clamp: 2;\n    line-clamp: 2;\n    overflow: hidden;\n\n    text-align: center;\n    font-size: 0.85rem;\n    line-height: 1.2em;\n    font-weight: 600;\n  }\n}\n\n.context-menu {\n  background: white;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n  min-width: 120px;\n  padding: 4px 0;\n\n  .context-menu-item {\n    width: 100%;\n    padding: 8px 16px;\n    text-align: left;\n    background: none;\n    border: none;\n    font-size: 14px;\n    color: #333;\n\n    display: flex;\n    align-items: center;\n    gap: 0.5rem;\n\n    &:hover {\n      background-color: #f0f0f0;\n    }\n  }\n}\n\n// Folder styles\n.folder {\n  display: grid;\n\n  align-items: center;\n  justify-items: center;\n  gap: 10px;\n  padding: 10px;\n  border-radius: 10px;\n\n  > * {\n    pointer-events: none;\n  }\n\n  &:hover {\n    background-color: var(--bg-light);\n  }\n\n  &.preselected {\n    background-color: var(--bg-light);\n    outline: 2px solid var(--system-accent-color);\n    outline-offset: 2px;\n  }\n\n  &:active {\n    transform: scale(0.95);\n  }\n\n  &.is-drop-target {\n    border: 3px dashed var(--system-accent-color);\n    transform: scale(1.05);\n\n    .folder-name {\n      opacity: 0;\n    }\n  }\n\n  .folder-grid {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    grid-template-rows: repeat(2, 1fr);\n\n    width: 60%;\n    aspect-ratio: 1/1;\n    gap: 4%;\n    padding: 8%;\n    box-shadow: rgba(0, 0, 0, 0.1) -4px 9px 25px -6px;\n\n    background-color: RGB(from var(--color-gray-100) r g b / 0.6);\n    border-radius: 16%;\n\n    .folder-preview {\n      width: 100%;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n\n      .folder-preview-icon {\n        width: 100%;\n        height: 100%;\n        border-radius: 16%;\n        overflow: hidden;\n\n        &:not([data-shape=\"square\"]) {\n          padding: 8%;\n          background-color: var(--color-gray-100);\n        }\n      }\n    }\n  }\n\n  .folder-name {\n    width: 100%;\n    height: 2.4em;\n\n    display: -webkit-box;\n    -webkit-box-orient: vertical;\n    -webkit-line-clamp: 2;\n    line-clamp: 2;\n    overflow: hidden;\n\n    text-align: center;\n    font-size: 0.85rem;\n    line-height: 1.2em;\n    font-weight: 600;\n  }\n}\n\n.folder-modal {\n  width: 70vw;\n  height: 70vh;\n  margin: auto;\n  z-index: 100;\n\n  background-color: RGB(from var(--color-gray-100) r g b / 0.95);\n  border-radius: 16px;\n  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n  overflow: hidden;\n\n  &::backdrop {\n    background: RGB(from var(--color-gray-100) r g b / 0.3);\n    backdrop-filter: blur(4px);\n  }\n\n  .folder-modal-content {\n    padding: 2rem;\n    height: 100%;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 1rem;\n  }\n\n  .folder-modal-name {\n    font-size: 2rem;\n    font-weight: 600;\n    text-align: center;\n  }\n\n  .folder-modal-items {\n    width: 100%;\n    display: grid;\n    grid-template-columns: repeat(var(--folder-columns), 1fr);\n    gap: 1rem;\n    overflow-y: auto;\n    align-content: start;\n\n    // .app { .. }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/bluetooth-popup.scss",
    "content": ".bluetooth-popup {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  .bluetooth-no-adapter {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 40px 20px;\n    color: var(--color-gray-500);\n    font-style: italic;\n    font-size: 0.95rem;\n    text-align: center;\n  }\n\n  .bluetooth-radio-control {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 10px 6px;\n    background-color: var(--color-gray-100);\n    border-radius: 8px;\n\n    .bluetooth-radio-label {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      font-weight: 600;\n      color: var(--color-gray-900);\n    }\n\n    .bluetooth-radio-switch {\n      cursor: pointer;\n    }\n  }\n\n  .bt-list {\n    display: flex;\n    flex-direction: column;\n\n    &:not(:last-child) {\n      padding-bottom: 10px;\n      border-bottom: 1px solid var(--color-gray-300);\n    }\n\n    .bt-list-title {\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n\n      padding-left: 8px;\n      margin-bottom: 8px;\n      font-weight: 600;\n      color: var(--color-gray-600);\n      text-transform: uppercase;\n      letter-spacing: 0.5px;\n\n      .bluetooth-scanning {\n        display: flex;\n        align-items: center;\n        color: var(--color-gray-500);\n\n        .slu-icon {\n          animation: rotateToTheLeft 1s linear infinite;\n        }\n      }\n    }\n\n    .bt-list-devices {\n      display: flex;\n      flex-direction: column;\n      gap: 8px;\n\n      max-height: 300px;\n      overflow-x: visible;\n      overflow-y: auto;\n\n      .bt-device {\n        display: flex;\n        flex-direction: column;\n        padding: 10px 8px;\n        border-radius: 8px;\n        transition: background-color 0.2s;\n\n        &:hover, &:focus {\n          outline: none;\n          background-color: var(--color-gray-200);\n        }\n\n        &.bt-device-selected {\n          background-color: #{\"rgb(from var(--system-accent-color) r g b / 0.2)\"};\n        }\n\n        .bt-device-info {\n          display: flex;\n          align-items: center;\n          gap: 8px;\n\n          .bt-device-name {\n            flex: 1;\n            font-weight: 500;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n          }\n        }\n\n        .bt-device-loading {\n          display: flex;\n          justify-content: flex-end;\n        }\n\n        .bt-device-pairing {\n          display: flex;\n          flex-direction: column;\n          gap: 8px;\n          margin-top: 12px;\n\n          .bt-device-pairing-message {\n            font-size: 0.85rem;\n            color: var(--color-gray-600);\n            font-style: italic;\n          }\n\n          .bt-device-pin-display {\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            width: 100%;\n            padding: 16px 12px;\n            background-color: #{\"rgb(from var(--system-accent-color) r g b / 0.1)\"};\n            border: 2px solid var(--system-accent-color);\n            border-radius: 8px;\n            font-size: 1.5rem;\n            font-weight: 700;\n            font-family: \"Courier New\", monospace;\n            letter-spacing: 0.3em;\n            color: var(--color-gray-900);\n            user-select: text;\n          }\n        }\n\n        .bt-device-actions {\n          display: flex;\n          justify-content: flex-end;\n          gap: 8px;\n          margin-top: 12px;\n        }\n\n        .bt-device-error {\n          margin-top: 8px;\n          padding: 8px;\n          background-color: var(--color-red-100);\n          border-radius: 6px;\n          color: var(--color-red-600);\n          font-size: 0.8rem;\n          text-align: center;\n        }\n      }\n\n      .bluetooth-empty {\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        height: 100px;\n        color: var(--color-gray-500);\n        font-style: italic;\n        font-size: 0.9rem;\n      }\n    }\n  }\n\n  .bluetooth-footer {\n    > button {\n      font-weight: 600;\n    }\n  }\n}\n\n@keyframes rotateToTheLeft {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(-360deg);\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/calendar-popup.scss",
    "content": ".calendar-popup {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  .calendar {\n    display: flex;\n    flex-direction: column;\n    user-select: none;\n  }\n\n  .calendar-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    margin-bottom: 16px;\n    padding: 0 4px;\n\n    .calendar-date {\n      font-size: 14px;\n      font-weight: 600;\n      padding: 4px 8px;\n      border-radius: 4px;\n      cursor: pointer;\n\n      &:hover {\n        background-color: var(--color-gray-200);\n      }\n    }\n\n    .calendar-actions {\n      display: flex;\n      gap: 4px;\n\n      .calendar-navigator {\n        background: transparent;\n        border: none;\n        border-radius: 4px;\n        color: inherit;\n        cursor: pointer;\n        padding: 4px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n\n        &:hover {\n          background-color: var(--color-gray-200);\n        }\n\n        &:active {\n          background-color: var(--color-gray-300);\n        }\n      }\n    }\n  }\n\n  .calendar-month-view {\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n  }\n\n  .calendar-weekdays {\n    display: grid;\n    grid-template-columns: repeat(7, 1fr);\n    gap: 4px;\n    margin-bottom: 4px;\n\n    .calendar-weekday {\n      font-size: 12px;\n      font-weight: 600;\n      padding: 8px 0;\n      opacity: 0.7;\n      text-align: center;\n    }\n  }\n\n  .calendar-days {\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n  }\n\n  .calendar-week {\n    display: grid;\n    grid-template-columns: repeat(7, 1fr);\n    gap: 4px;\n  }\n\n  .calendar-cell {\n    aspect-ratio: 1;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border-radius: 4px;\n    font-size: 13px;\n    cursor: pointer;\n\n    &:hover {\n      background-color: var(--color-gray-200);\n    }\n\n    &.calendar-cell-off-month {\n      opacity: 0.3;\n    }\n\n    &.calendar-cell-today {\n      font-weight: 700;\n      color: var(--system-accent-color);\n    }\n\n    &.calendar-cell-selected {\n      background-color: var(--system-accent-color);\n      color: white;\n      font-weight: 600;\n\n      &:hover {\n        background-color: var(--system-accent-color);\n        opacity: 0.9;\n      }\n    }\n  }\n\n  .calendar-year-view {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr);\n    gap: 8px;\n    padding: 4px;\n\n    .calendar-month-cell {\n      padding: 16px 12px;\n      border-radius: 6px;\n      font-size: 13px;\n      font-weight: 500;\n      cursor: pointer;\n      text-align: center;\n\n      &:hover {\n        background-color: var(--color-gray-200);\n      }\n\n      &.calendar-month-cell-current {\n        font-weight: 700;\n        color: var(--system-accent-color);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/context-menu.scss",
    "content": ".context-menu {\n  width: fit-content;\n  min-width: 200px;\n\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n\n  .menu-item {\n    display: flex;\n    gap: 8px;\n    padding: 8px;\n\n    .menu-item-label {\n      flex: 1;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n      text-align: left;\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/fancy-toolbar.scss",
    "content": "#root {\n  font-size: 0.8rem;\n  font-weight: 500;\n}\n\n.ft-bar {\n  color: var(--color-fixed-gray-100);\n  transition: color 0.2s linear;\n\n  > .bg-layers {\n    // we skip the first layer, to avoid break user themes, as before v2.2 the first layer was a noise layer\n    > .bg-layer-2 {\n      background-color: var(--color-fixed-gray-900);\n      transition: background-color 0.2s linear;\n      will-change: background-color;\n    }\n  }\n\n  &[data-has-margin=\"true\"] {\n    border-radius: calc(var(--config-height) / 3);\n  }\n\n  // &[data-there-is-maximized-on-background=\"true\"] {} (this attribute exist but is unused)\n  // &[data-focused-is-maximized=\"true\"] {} (this attribute exist but is unused)\n  // &[data-focused-is-overlay=\"true\"] {} (this attribute exist but is unused)\n}\n\n.ft-bar-item {\n  &.ft-bar-item-clickable {\n    border-radius: 6px;\n\n    &:hover {\n      backdrop-filter: brightness(3);\n    }\n  }\n}\n\n.ft-bar-item-content {\n  position: relative;\n}\n\n.ft-bar-item-badge {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: absolute;\n  top: 0;\n  right: 0;\n  transform: translate(25%, -25%);\n  background-color: var(--system-accent-color);\n  color: #efefef;\n  height: 12px;\n  min-width: 12px;\n  font-size: 10px;\n  border-radius: 8px;\n\n  &:empty {\n    display: none;\n  }\n}\n\n// Notifications popup styles moved to notifications-popup.scss\n\n@keyframes text-highlight {\n  0% {\n    color: var(--color-gray-600);\n  }\n  66% {\n    color: var(--system-accent-color);\n  }\n  100% {\n    color: var(--color-gray-600);\n  }\n}\n\n.ft-corner-button {\n  transition: backdrop-filter 0.2s ease;\n\n  &:hover {\n    backdrop-filter: invert(0.2);\n  }\n\n  &:active {\n    backdrop-filter: invert(0.3);\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/flyouts.css",
    "content": "body,\n#root {\n  width: min-content;\n  height: min-content;\n  overflow: hidden;\n  background: transparent;\n}\n\n.flyout {\n  border-radius: 10px;\n  padding: 8px;\n  margin: 10px;\n  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.3);\n  background: var(--color-gray-100);\n\n  .workspace,\n  .volume,\n  .brightness,\n  .player {\n    height: 1.5rem;\n    width: 16rem;\n    overflow: hidden;\n\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n  }\n\n  .workspace-name {\n    font-weight: 600;\n  }\n\n  .player {\n    .player-thumbnail-container {\n      overflow: hidden;\n      border-radius: 50%;\n      width: 1.5rem;\n      height: 1.5rem;\n    }\n\n    .player-info {\n      flex: 1;\n      overflow: hidden;\n\n      .player-title,\n      .player-author {\n        font-size: 0.8rem;\n        line-height: 1em;\n\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n      }\n\n      .player-title {\n        font-weight: 600;\n      }\n\n      .player-timeline {\n        display: none;\n      }\n    }\n\n    .player-controls {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n    }\n\n    /* other themes could display it, but by default it's unused */\n    .player-progress {\n      display: none;\n    }\n  }\n}\n\n.flyout[data-placement=\"right\"],\n.flyout[data-placement=\"left\"] {\n  .workspace,\n  .volume,\n  .brightness,\n  .player {\n    height: 16rem;\n    width: 1.5rem;\n    flex-direction: column;\n  }\n\n  .player-controls {\n    flex-direction: column;\n  }\n}\n\n.flyout[data-placement=\"right\"] {\n  .player-info,\n  .workspace-name {\n    writing-mode: sideways-rl;\n    text-orientation: sideways;\n  }\n}\n\n.flyout[data-placement=\"left\"] {\n  .player-info,\n  .workspace-name {\n    writing-mode: sideways-lr;\n    text-orientation: sideways;\n  }\n}\n\n.notification {\n  padding: 10px;\n  width: 300px;\n  font-size: 0.8rem;\n\n  .notification-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 16px;\n    padding-bottom: 6px;\n    border-bottom: solid 1px var(--color-gray-300);\n    margin-bottom: 6px;\n\n    .notification-header-info {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      color: var(--color-gray-800);\n      font-weight: 600;\n\n      overflow: hidden;\n      white-space: nowrap;\n      text-overflow: ellipsis;\n\n      .notification-icon {\n        width: 1.5em;\n      }\n    }\n  }\n\n  .notification-body {\n    display: grid;\n    grid-template-columns: auto 1fr;\n\n    .notification-body-logo-image {\n      grid-column: 1 / 2;\n      grid-row: 1 / 2;\n      width: 3rem;\n      height: 3rem;\n      margin-right: 10px;\n\n      &.notification-body-logo-image-circle {\n        border-radius: 50%;\n      }\n    }\n\n    .notification-body-content {\n      word-break: break-word;\n      overflow: hidden;\n      grid-column: 2 / 3;\n      grid-row: 1 / 2;\n\n      > p:first-of-type {\n        font-weight: 600;\n      }\n\n      .notification-group {\n        display: flex;\n        gap: 6px;\n\n        .notification-subgroup {\n          display: flex;\n          flex-direction: column;\n          gap: 10px;\n        }\n      }\n    }\n\n    .notification-body-hero-image {\n      grid-column: 1 / 3;\n      grid-row: 2 / 3;\n      width: 100%;\n      max-height: 300px;\n      object-fit: cover;\n      margin-top: 10px;\n    }\n  }\n\n  .notification-actions {\n    display: none;\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/keyboard-selector.scss",
    "content": ".keyboard-selector {\n  width: 300px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  .keyboard-selector-header {\n    font-weight: 600;\n  }\n\n  .keyboard-selector-body {\n    display: flex;\n    flex-direction: column;\n    gap: 0.5rem;\n\n    .layout {\n      display: grid;\n      grid-template-columns: 20px 1fr;\n      align-items: center;\n      gap: 10px;\n      padding: 0.5rem 0.75rem;\n      border-radius: 0.5rem;\n\n      &:hover {\n        background-color: RGB(from var(--system-accent-color) r g b / 0.2);\n      }\n\n      &.layout-active {\n        background-color: RGB(from var(--system-accent-color) r g b / 0.4);\n      }\n\n      .layout-info {\n        display: flex;\n        flex-direction: column;\n        align-items: flex-start;\n\n        .layout-lang {\n          font-weight: 600;\n        }\n\n        .layout-keyboard {\n          font-size: 0.8rem;\n          font-weight: 600;\n          color: var(--color-gray-600);\n          overflow: hidden;\n          white-space: nowrap;\n          text-overflow: ellipsis;\n        }\n      }\n    }\n  }\n\n  .keyboard-selector-footer {\n    button {\n      font-size: 0.8rem;\n      font-weight: 600;\n      color: var(--color-gray-600);\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/media-popup.scss",
    "content": ".media-popup {\n  display: flex;\n  flex-direction: column;\n\n  // Main layout views\n  .media-main-view,\n  .media-device-view {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n  }\n\n  // Common labels\n  .media-control-label {\n    font-size: 12px;\n    font-weight: 600;\n    color: var(--color-gray-600);\n    margin-top: 8px;\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n\n    &:first-of-type {\n      margin-top: 0;\n    }\n  }\n\n  // Volume control component\n  .media-control-volume {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    width: 100%;\n  }\n\n  // Device groups\n  .media-device-group {\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n  }\n\n  // Individual device item\n  .media-device {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px;\n    border-radius: 6px;\n    transition: background-color 0.2s;\n\n    &:hover {\n      background-color: var(--color-gray-100);\n    }\n\n    .media-device-name {\n      flex: 1;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n      font-size: 14px;\n    }\n  }\n\n  // Media session list\n  .media-control-session-list {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n  }\n\n  // Device detail view\n  .media-device-view {\n    .media-device-header {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      margin-bottom: 8px;\n\n      .media-device-title {\n        font-size: 14px;\n        font-weight: 600;\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n      }\n    }\n\n    .media-device-mixer {\n      display: flex;\n      flex-direction: column;\n      gap: 8px;\n\n      .media-device-mixer-entry {\n        display: flex;\n        align-items: center;\n        gap: 8px;\n\n        .media-device-mixer-entry-icon {\n          display: flex;\n          align-items: center;\n          gap: 4px;\n          height: 1rem;\n          width: 1rem;\n          margin-left: 0.25rem;\n        }\n      }\n    }\n\n    .media-device-footer {\n      margin-top: 8px;\n      padding-top: 8px;\n      border-top: 1px solid var(--color-gray-300);\n    }\n  }\n}\n\n// Individual media session\n.media-session {\n  position: relative;\n  overflow: hidden;\n  border-radius: 8px;\n  padding: 8px;\n  display: flex;\n  gap: 8px;\n\n  .media-session-blurred-thumbnail {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    filter: blur(10px);\n    opacity: 0.8;\n    z-index: 0;\n  }\n\n  .media-session-thumbnail-container {\n    position: relative;\n    z-index: 1;\n\n    .media-session-app-icon {\n      position: absolute !important;\n      right: 4px;\n      bottom: 4px;\n      width: 1rem;\n      height: 1rem;\n    }\n\n    .media-session-thumbnail {\n      width: 80px;\n      height: 80px;\n      object-fit: cover;\n      border-radius: 8px;\n    }\n  }\n\n  .media-session-info {\n    position: relative;\n    z-index: 1;\n    flex: 1;\n\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n    overflow: hidden;\n\n    .media-session-title {\n      width: 100%;\n      font-size: 14px;\n      font-weight: 600;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n    }\n\n    .media-session-author {\n      font-size: 12px;\n      opacity: 0.8;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n    }\n\n    .media-session-actions {\n      display: flex;\n      gap: 8px;\n      margin-top: 8px;\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/network-popup.scss",
    "content": ".network-popup {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  .network-no-adapter {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 40px 20px;\n    color: var(--color-gray-500);\n    font-style: italic;\n    font-size: 0.95rem;\n    text-align: center;\n  }\n\n  .network-radio-control {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 10px 6px;\n    background-color: var(--color-gray-100);\n    border-radius: 8px;\n\n    .network-radio-label {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      font-weight: 600;\n      color: var(--color-gray-900);\n    }\n\n    .network-radio-switch {\n      cursor: pointer;\n    }\n  }\n\n  .network-section {\n    display: flex;\n    flex-direction: column;\n\n    &:not(:last-child) {\n      padding-bottom: 10px;\n      border-bottom: 1px solid var(--color-gray-300);\n    }\n\n    .network-section-title {\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n\n      padding-left: 8px;\n      margin-bottom: 8px;\n      font-weight: 600;\n      color: var(--color-gray-600);\n      text-transform: uppercase;\n      letter-spacing: 0.5px;\n\n      .network-scanning {\n        display: flex;\n        align-items: center;\n        color: var(--color-gray-500);\n\n        .slu-icon {\n          animation: rotateToTheLeft 1s linear infinite;\n        }\n      }\n    }\n\n    .network-section-entries {\n      display: flex;\n      flex-direction: column;\n      gap: 8px;\n\n      max-height: 300px;\n      overflow-x: visible;\n      overflow-y: auto;\n\n      .wlan-entry {\n        display: flex;\n        flex-direction: column;\n        padding: 10px 8px;\n        border-radius: 8px;\n        transition: background-color 0.2s;\n\n        &:hover, &:focus {\n          outline: none;\n          background-color: var(--color-gray-200);\n        }\n\n        &.wlan-entry-selected {\n          background-color: #{\"rgb(from var(--system-accent-color) r g b / 0.2)\"};\n        }\n\n        .wlan-entry-info {\n          display: flex;\n          align-items: center;\n          gap: 8px;\n\n          .wlan-entry-ssid {\n            flex: 1;\n            font-weight: 500;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n          }\n\n          .wlan-entry-band {\n            font-size: 0.7rem;\n            font-weight: 600;\n            color: var(--color-gray-600);\n            border: 1px solid var(--color-gray-400);\n            border-radius: 4px;\n            padding: 2px 4px;\n          }\n        }\n\n        .wlan-entry-fields {\n          display: flex;\n          flex-direction: column;\n          gap: 8px;\n          margin-top: 12px;\n        }\n\n        .wlan-entry-actions {\n          display: flex;\n          justify-content: flex-end;\n          gap: 8px;\n          margin-top: 12px;\n        }\n      }\n\n      .network-empty {\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        height: 100px;\n        color: var(--color-gray-500);\n        font-style: italic;\n        font-size: 0.9rem;\n      }\n    }\n  }\n\n  .network-footer {\n    > button {\n      font-weight: 600;\n    }\n  }\n}\n\n@keyframes rotateToTheLeft {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(-360deg);\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/notifications.scss",
    "content": ".notifications-popup {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n  width: 350px;\n  max-height: 700px;\n  padding: 10px;\n\n  .notifications-popup-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n\n    > span {\n      font-weight: 600;\n    }\n  }\n\n  .notifications-popup-body {\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n    overflow-y: auto;\n    overflow-x: hidden;\n    padding: 5px 10px;\n    margin: 0 -10px;\n  }\n\n  .notifications-popup-footer {\n    display: flex;\n    justify-content: flex-start;\n  }\n\n  .notifications-popup-empty {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    color: var(--color-gray-500);\n    font-weight: 600;\n    font-size: 0.8rem;\n    height: 200px;\n  }\n}\n\n.notification {\n  border-radius: 12px;\n  background-color: var(--color-gray-100);\n  box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.4);\n  padding: 10px;\n  transition: scale 0.1s ease-in-out;\n\n  &:active:not(:has(button:active, input:active, select:active)) {\n    scale: 0.9;\n  }\n\n  .notification-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 16px;\n    padding-bottom: 6px;\n    border-bottom: solid 1px var(--color-gray-300);\n    margin-bottom: 6px;\n\n    .notification-header-info {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      font-size: 0.8rem;\n      color: var(--color-gray-800);\n      font-weight: 600;\n\n      .notification-icon {\n        width: 1.5em;\n      }\n    }\n  }\n\n  .notification-body {\n    display: grid;\n    grid-template-columns: auto 1fr;\n\n    .notification-body-logo-image {\n      grid-column: 1 / 2;\n      grid-row: 1 / 2;\n      width: 3rem;\n      height: 3rem;\n      margin-right: 10px;\n\n      &.notification-body-logo-image-circle {\n        border-radius: 50%;\n      }\n    }\n\n    .notification-body-content {\n      word-break: break-word;\n      overflow: hidden;\n      grid-column: 2 / 3;\n      grid-row: 1 / 2;\n\n      > p:first-of-type {\n        font-weight: 600;\n      }\n\n      .notification-group {\n        display: flex;\n        gap: 6px;\n\n        .notification-subgroup {\n          display: flex;\n          flex-direction: column;\n          gap: 10px;\n        }\n      }\n    }\n\n    .notification-body-hero-image {\n      grid-column: 1 / 3;\n      grid-row: 2 / 3;\n      width: 100%;\n      max-height: 300px;\n      object-fit: cover;\n      margin-top: 10px;\n    }\n  }\n\n  .notification-actions {\n    margin-top: 6px;\n    display: flex;\n    gap: 6px;\n    font-size: 0.8rem;\n    line-height: 0.8rem;\n\n    input,\n    select {\n      flex: 3;\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/power-menu.scss",
    "content": "// https://github.com/markdurrant/noisy-uris/tree/master\n$base-noise-010: \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAABOFBMVEWDg4NycnJnZ2ebm5tjY2OgoKCurq5lZWWoqKiKiopmZmahoaGOjo5TU1N6enp7e3uRkZGJiYmFhYWxsbFOTk6Xl5eBgYGkpKRhYWFRUVGvr69dXV2wsLBiYmKnp6dUVFR5eXmdnZ1sbGxYWFh2dnZ0dHSmpqaZmZlVVVVqamqsrKyCgoJ3d3dubm5fX19tbW2ioqKSkpJWVlaHh4epqalSUlKTk5OVlZWysrJoaGhzc3N+fn5wcHBaWlqcnJxkZGRpaWlvb2+zs7NcXFxPT09/f3+lpaWWlpaQkJCjo6OIiIitra2enp6YmJhQUFBZWVmqqqqLi4uNjY1eXl6rq6ufn599fX2AgIB8fHyEhIRxcXFra2tbW1uPj4+MjIyGhoaamppgYGB4eHhNTU1XV1d1dXW0tLSUlJSHWuNDAAAAaHRSTlMaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGsgZs4IAAAaZSURBVHhelZWFrmZVDEb3cffzq7u7u7u7u9z7/m8AhISQwMDMAzRN2/WtAhO7zOd0x0U/UNb0oWQZGLWhIHBK/lC96klgkA+3B5JoqI9ozRcn4306YeDweKG9vxo5YbGbqBkln93ZFGs3SA0RRpSO4dpdpg+VnMUv8BEqmiIcli8gJeRZc29K51qOg0OWHRGyA0ccrmbmSRj1r7x5JisCpAs+iuCd8GFc0pMGldB2BOC0VoY37qKJh5nqZNjb4XtnjRlYMQYxsN0KWTdk77hnJZB7s+MbXK3Mxawrwu8cHGNKynDQTUqhbrxmNQ+belwSPemILVuUu1p4G6xGI0yUA0lh26IduYnd2soQ0KVmwUxo7D6U0QdCJwLWDTwzFij0cE/ZvorI7kl/QuCHUy7ibZCHT9mtLaY4HJLhIHOJ+jt5DAI9MJqOs0refRcF5H7S9mb2vnsqo21xvTPVgZGrLDCTJ+kk9eQ67kPk+xP4697EDY+boY3tC4zs3yy+5XRqg58EivoohEownfBzjpeQN6v6gaY0TCzADte1m2pbFSUbpKfDqU0iq+4UPNyxFlW00Q70b9jGpIbqdoCQLZ1Lax+Bv3XUj5ZnoT1N0j3CZS95FfHDRump2ujpuLY47oI5VWjmR2PwietdJbJGZRYFFm6SWPiwmhFZqWKEwNM6Nlw7XmZuQmKu8FHq8DFcaYjAYojsS6NrLKNnMRgyu2oaXaNpyLa0Nncawan7eDOxZVSxv4GYoLCF184C0EAvuhuJNvZ1gosWDdHUfJ05uHdwhRKYb/5+4W90jQxT/pHd2hnkBgn3GFzCCzcVXPbZ3qdqLlYrDl0dUWqkXYc6LStL8QLPI3G3gVDdAa2Pr0co8wQgwRYBlTB5AEmteLPCRHMgoHi56glp5rMSrwAllRSatomKatJdy0nXEkCI2z5065bpKav5/bKgSXr+L0HgDwSsvwQaeC0SjH1cnu7WZTcxJn0kVLI/HEzNK1j8W7etR/BfXDXhak8LmTQdwMqaF/jh+k+ZVMUvWU/+OfUwz5TDJhclFAtiMYD8ss6TFNluVg6lYZaeXXv/FzqQ3yjupMEIyzlf6yt2zmyHxI43held1dMbGkLMY5Kpv4llTCazqHbKsakh+DPPZdHvqYQF1onZpg1W/H7b6DJr019WhPWucVJTcStosCf1fQ1kLWA/12vjb3PItlBUuo6FO/4kFTPGNXC4e/TRMDGwPpSG1RJwYXNH4vkHK8BSmFNrXVTwJjLAphVEKq7HS2d8pSqoZdCBAv6mdJ72revxET6giWB7PgbJph+2i011uUifL7xruTb3zv+NKvgpqRSU0yBSckeKeQzSgeZZcaQb8+JYzehtPraBkg3Jc3e8boxVXJzNW23deFoZ74Vzy6xd1+FemwZ/neOnHQh2ufopy5c/r69Cz+scIrx+uN+dzhyzEjCeNLL0hgjGUOHdvb25YDijfq/An/D+iv7BBDutUsyuvBrH2ya6j2SIkLvjxFIpk8H37wcAt9KHX9cLeNmn+8CR1xtKgrzojVXl/qikMqAsDcO1coQrEanpsrB3DlAImIwS07oN2k3C2x2jSE3jxSm908P1tUXUMD15Lpp50CHii7i2BDSdYMcfB7+X7QdqymsDWH6BJ5APN+qIRhTVc/msYf5CjOyA82VSuIEtZA3GmUuXBK2r6xJ2LXO8fCU9kmCvydDptoECLq+XXLs4w8U+DUZyir9Cw+XL3rHFGoDNI9Rw3baFy/fZwTY2Gr0WMuLaxMrWaC5rh+IeyZijp0fdaDLPg8YtugLgnwYZss1xIh1o13qB7L8pC6wEutNQVuy5aIpNkSSl2yWAiRADUVXSMqpTH8Da3gCNr8maodNIxjY7CXyvzHHfiJoto/CE9UMmX+cRqPC8RKdks7OV35txMGkdXzOkkhX9wTr+tIOGKZzjoo+qbWy3hsJJtz5D7nP+syyjxYe7eCAMIOywwFNfv/ZMNyBSxV0g7ZEJCPVE8IA5sw7jg9Kx3RXdfCQXGxpH+0kyHYpBj0H4y2VdAHRW9RyegOPPB+5NudysJji/lnxHQ9pFOMLMLeZ0O9hrnsuFsstbjczbC+14JHS+xsDf3pPgQXvUG6Q/H2fKV/B7jYX8RdOrug5BjG/1jueAPq1ElQb4AeH/sRNwnNyoFqsJwT9tWhChzL/IP/gxfleLSIgVQDdRvKBZVfu9wgKkeHEEfgIqa/F6fJ0HM8knJtkbCn4hKFvNDLWXDr8BGMywGD1Lh54AAAAASUVORK5CYII=\";\n\n.power-menu-overlay {\n  width: 100vw;\n  height: 100vh;\n  background-image: url($base-noise-010);\n  background-color: #000d;\n  color: #fcfcfc;\n\n  .power-menu {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    gap: 1rem;\n\n    .power-menu-user {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 1rem;\n\n      .power-menu-user-profile {\n        width: 100px;\n        height: 100px;\n        border-radius: 50%;\n        object-fit: cover;\n      }\n\n      .power-menu-user-email {\n        font-weight: 600;\n      }\n    }\n\n    .power-menu-bye-bye {\n      font-size: 2rem;\n      font-weight: 600;\n      text-align: center;\n      margin-bottom: 1rem;\n    }\n\n    .power-menu-list {\n      flex: 1;\n      width: 100%;\n      max-width: 1200px;\n      max-height: 500px;\n\n      display: grid;\n      grid-template-columns: repeat(3, 1fr);\n      grid-template-rows: 1fr 1fr;\n      gap: 25px;\n\n      .power-menu-item {\n        position: relative;\n\n        width: 100%;\n        height: 100%;\n        border-radius: 16px;\n        background-color: #{\"rgb(from var(--system-accent-darker-color) r g b / 0.5)\"};\n        color: #fafafa;\n\n        display: flex;\n        flex-direction: column;\n        align-items: center;\n        justify-content: center;\n        gap: 1rem;\n\n        transition: background-color 0.2s ease, transform 0.2s ease;\n\n        &:hover {\n          background-color: #{\"rgb(from var(--system-accent-dark-color) r g b / 0.5)\"};\n          transform: scale(1.05);\n        }\n\n        &:active {\n          background-color: #{\"rgb(from var(--system-accent-darker-color) r g b / 0.5)\"};\n        }\n\n        &:focus-visible {\n          outline: solid 2px #fcfcfc !important;\n        }\n\n        .slu-icon {\n          height: 40% !important;\n        }\n\n        .power-menu-item-label {\n          margin-bottom: -2.2rem; // hack to center the icon\n          font-size: 1.2rem;\n          font-weight: 600;\n          opacity: 0;\n          transition: opacity 0.2s ease;\n        }\n\n        &:hover .power-menu-item-label {\n          opacity: 1;\n        }\n      }\n    }\n\n    .power-menu-uptime {\n      font-style: italic;\n      font-size: 0.8rem;\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/quick-settings.scss",
    "content": ".quick-settings {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  .radio-buttons-container {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 8px;\n\n    .radio-button {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      gap: 6px;\n\n      background-color: var(--color-gray-100);\n      color: var(--color-gray-900);\n      box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.2);\n      padding: 12px 8px;\n      border-radius: 10px;\n\n      cursor: pointer;\n      transition: all 0.2s ease;\n      min-height: 80px;\n\n      &:hover {\n        background: var(--color-gray-200);\n      }\n\n      &.radio-button-enabled {\n        background-color: var(--system-accent-dark-color);\n        color: var(--color-gray-50);\n\n        &:hover {\n          background-color: var(--system-accent-darker-color);\n        }\n      }\n\n      .radio-button-label {\n        font-size: 12px;\n        font-weight: 500;\n        text-align: center;\n        line-height: 1.2;\n      }\n    }\n  }\n\n  .quick-settings-item {\n    display: grid;\n    grid-template-columns: 30px 1fr 30px;\n    align-items: center;\n    gap: 10px;\n  }\n\n  .quick-settings-footer {\n    display: flex;\n    flex-direction: row;\n    justify-content: flex-end;\n    gap: 6px;\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/task-switcher.scss",
    "content": ".task-switcher-overlay {\n  width: 100vw;\n  height: 100vh;\n  display: grid;\n  place-items: center;\n  padding: 5vw;\n}\n\n.task-switcher {\n  max-width: 100%;\n  max-height: 100%;\n\n  display: flex;\n  justify-content: center;\n  flex-wrap: wrap;\n\n  background-color: var(--color-gray-300);\n  box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.2);\n  border-radius: 1rem;\n  padding: 1rem;\n  gap: 1rem;\n\n  overflow-y: auto;\n  overflow-x: hidden;\n\n  .task {\n    position: relative;\n    flex-shrink: 0;\n    overflow: hidden;\n    width: fit-content;\n    border-radius: 10px;\n    background: var(--color-gray-100);\n    width: 260px;\n\n    &:focus {\n      outline: 3px solid var(--system-accent-color) !important;\n    }\n\n    &:active {\n      transform: scale(0.92);\n    }\n\n    .task-header {\n      width: 100%;\n      display: grid;\n      grid-template-columns: 20px 1fr min-content;\n      align-items: center;\n\n      gap: 0.5rem;\n      padding: 0.5rem;\n      border-bottom: 1px solid var(--color-gray-300);\n\n      .task-title {\n        font-weight: 600;\n        font-size: 0.9rem;\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n      }\n    }\n\n    .task-preview-container {\n      width: 100%;\n      height: 150px;\n      border-radius: 0 0 10px 10px;\n      overflow: hidden;\n\n      display: flex;\n      justify-content: center;\n      align-items: center;\n\n      .task-preview {\n        width: 100%;\n        height: 100%;\n        object-fit: cover;\n        object-position: top;\n      }\n\n      .task-no-preview {\n        width: 30px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/tray-menu.scss",
    "content": ".system-tray {\n  width: 300px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  .system-tray-item {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 8px 10px;\n    border-radius: 8px;\n\n    &:hover {\n      background-color: var(--system-accent-light-color);\n    }\n\n    &:active {\n      background-color: var(--system-accent-color);\n    }\n\n    .system-tray-item-icon-box {\n      .system-tray-item-icon {\n        width: 1rem;\n        height: 1rem;\n        object-fit: contain;\n      }\n    }\n\n    .system-tray-item-label {\n      flex: 1;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n\n      text-align: left;\n      font-weight: 600;\n      font-size: 0.8rem;\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/user-menu.scss",
    "content": ".user-popup {\n  min-width: 350px;\n  max-width: 400px;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n\n  .user-label {\n    padding: 0 8px;\n    font-size: 0.75rem;\n    font-weight: 600;\n    color: var(--color-gray-600);\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n  }\n\n  .user-profile-container {\n    display: flex;\n    gap: 12px;\n    padding: 4px 0;\n\n    .user-profile-picture-container {\n      position: relative;\n      width: 70px;\n      height: 70px;\n      flex-shrink: 0;\n\n      .user-profile-picture {\n        width: 100%;\n        height: 100%;\n        border-radius: 50%;\n        object-fit: cover;\n      }\n\n      .user-profile-picture-fallback {\n        width: 100%;\n        height: 100%;\n        border-radius: 50%;\n        background-color: var(--color-gray-200);\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        font-size: 2rem;\n        color: var(--color-gray-600);\n      }\n\n      .user-profile-lock-button,\n      .user-profile-settings-button {\n        position: absolute;\n        bottom: -4px;\n        width: 28px;\n        height: 28px;\n        border-radius: 50%;\n        background-color: var(--color-gray-100);\n        border: 2px solid var(--config-background-color);\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        cursor: pointer;\n        font-size: 0.9rem;\n        color: var(--color-gray-700);\n\n        &:hover {\n          background-color: var(--color-gray-200);\n        }\n      }\n\n      .user-profile-lock-button {\n        right: -4px;\n      }\n\n      .user-profile-settings-button {\n        left: -4px;\n      }\n    }\n\n    .user-profile-information {\n      flex: 1;\n      display: flex;\n      flex-direction: column;\n      gap: 6px;\n      justify-content: center;\n\n      .user-profile-name {\n        display: flex;\n        align-items: center;\n        gap: 8px;\n        font-weight: 600;\n        font-size: 1rem;\n\n        span {\n          flex: 1;\n          overflow: hidden;\n          text-overflow: ellipsis;\n          white-space: nowrap;\n        }\n      }\n\n      .user-profile-email {\n        font-size: 0.85rem;\n        color: var(--color-gray-600);\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n      }\n\n      .user-profile-action-button {\n        display: flex;\n        align-items: center;\n        gap: 6px;\n        padding: 6px 10px;\n        background-color: var(--color-gray-100);\n        border-radius: 6px;\n        cursor: pointer;\n        font-size: 0.85rem;\n        color: var(--color-gray-700);\n\n        &:hover {\n          background-color: var(--color-gray-200);\n        }\n      }\n    }\n  }\n\n  .user-seelen-options {\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n    list-style: none;\n    padding: 0;\n    margin: 0;\n\n    .user-seelen-option-item {\n      display: flex;\n      align-items: center;\n      gap: 10px;\n      padding: 10px;\n      border-radius: 6px;\n      cursor: pointer;\n\n      &:hover {\n        background-color: var(--color-gray-200);\n      }\n\n      .slu-icon {\n        font-size: 1.1rem;\n        color: var(--color-gray-600);\n      }\n\n      .user-seelen-option-item-title {\n        flex: 1;\n        font-size: 0.9rem;\n      }\n    }\n  }\n}\n\n.user-directory {\n  display: flex;\n  flex-direction: column;\n\n  .user-directory-summary {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 8px;\n    border-radius: 6px;\n    cursor: pointer;\n    font-weight: 500;\n\n    &:hover {\n      background-color: var(--color-gray-200);\n    }\n\n    .user-directory-icon {\n      font-size: 1.1rem;\n    }\n\n    .user-directory-title {\n      flex: 1;\n    }\n\n    .chevron {\n      transition: transform 0.2s;\n      font-size: 1rem;\n\n      &.chevron-active {\n        transform: rotate(180deg);\n      }\n    }\n  }\n\n  .file-list {\n    max-height: 200px;\n    overflow-y: auto;\n\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n    padding: 8px 4px 4px 4px;\n\n    .user-file {\n      display: flex;\n      align-items: center;\n      gap: 10px;\n      padding: 8px;\n      border-radius: 6px;\n      cursor: pointer;\n\n      &:hover {\n        background-color: var(--color-gray-200);\n      }\n\n      .user-file-icon {\n        width: 1.2rem;\n        height: 1.2rem;\n        font-size: 1.2rem;\n        flex-shrink: 0;\n      }\n\n      .user-file-label {\n        flex: 1;\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n        font-size: 0.9rem;\n      }\n\n      .user-file-date {\n        font-size: 0.75rem;\n        color: var(--color-gray-500);\n        flex-shrink: 0;\n      }\n    }\n\n    .user-list-extender {\n      padding: 8px;\n      text-align: center;\n      font-size: 0.85rem;\n      color: var(--system-accent-color);\n      background: none;\n      border: none;\n      border-radius: 6px;\n      cursor: pointer;\n\n      &:hover {\n        background-color: #{\"rgb(from var(--system-accent-color) r g b / 0.1)\"};\n      }\n    }\n  }\n}\n\n.user-empty-list {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 20px;\n  color: var(--color-gray-500);\n  font-style: italic;\n  font-size: 0.9rem;\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/wallpaper-manager.css",
    "content": ".wallpaper-container {\n  display: none;\n  overflow: hidden;\n  will-change: transform;\n\n  &.rendering {\n    display: block;\n    animation: ExpandOutCircle 600ms cubic-bezier(0.8, 0, 1, 1) forwards;\n  }\n\n  &.will-unrender {\n    animation: Vanish 600ms linear forwards;\n  }\n}\n\n@keyframes ExpandOutCircle {\n  0% {\n    clip-path: circle(0%);\n  }\n  100% {\n    clip-path: circle(100%);\n  }\n}\n\n@keyframes Vanish {\n  0% {\n    opacity: 1;\n  }\n  100% {\n    opacity: 0.2;\n  }\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/weg.css",
    "content": ":root {\n  --dock-item-hover-color: rgb(from var(--system-accent-light-color) r g b / 0.2);\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --dock-item-hover-color: rgb(from var(--system-accent-color) r g b / 0.2);\n  }\n}\n\n@keyframes shadow-on-horizontal-scroll {\n  from {\n    box-shadow: inset -20px 0 15px -10px rgb(0 0 0 / 0.5);\n  }\n  to {\n    box-shadow: inset 20px 0 15px -10px rgb(0 0 0 / 0.5);\n  }\n}\n\n@keyframes shadow-on-vertical-scroll {\n  from {\n    box-shadow: inset 0 -20px 15px -10px rgb(0 0 0 / 0.5);\n  }\n  to {\n    box-shadow: inset 0 20px 15px -10px rgb(0 0 0 / 0.5);\n  }\n}\n\n.taskbar {\n  > .bg-layers {\n    > .bg-layer-2 {\n      opacity: 0.8;\n      background-color: var(--color-gray-100);\n      border-radius: 15px;\n    }\n  }\n\n  > .weg-items-container {\n    border-radius: 15px;\n  }\n\n  &.horizontal {\n    > .weg-items-container {\n      animation: shadow-on-horizontal-scroll linear;\n      animation-timeline: scroll(x self);\n    }\n  }\n\n  &.vertical {\n    > .weg-items-container {\n      animation: shadow-on-vertical-scroll linear;\n      animation-timeline: scroll(y self);\n    }\n  }\n\n  &.temporal-only {\n    --empty-rule: \"delete me on use\";\n  }\n}\n\n.weg-separator {\n  .horizontal & {\n    &.weg-separator-1 {\n      border-left: 1px solid var(--color-gray-500);\n    }\n    &.weg-separator-2 {\n      border-right: 1px solid var(--color-gray-500);\n    }\n  }\n\n  .vertical & {\n    &.weg-separator-1 {\n      border-top: 1px solid var(--color-gray-500);\n    }\n    &.weg-separator-2 {\n      border-bottom: 1px solid var(--color-gray-500);\n    }\n  }\n}\n\n.weg-empty-state-label {\n  white-space: nowrap;\n  width: min-content;\n  line-height: var(--config-item-size);\n  vertical-align: middle;\n  font-style: italic;\n  color: var(--color-gray-400);\n  margin: 0 calc(var(--config-padding) * 2);\n}\n\n:root {\n  --padding-ratio: 0.15; /* this count as x2 like there is a padding on each side */\n  --inner-spacing: calc(var(--config-item-size) * var(--padding-ratio));\n  /* this represent the max item size as 3 items to follow simetry */\n  --max-item-size: calc(var(--config-item-size) * 3 + var(--config-space-between-items) * 2);\n  /* same as use 25% but this works for rectangles */\n  --border-radius: calc(var(--config-item-size) * 0.25);\n}\n\n.weg-item {\n  position: relative;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: var(--inner-spacing);\n  padding: var(--inner-spacing);\n  min-width: var(--config-item-size);\n  max-width: var(--max-item-size);\n  height: var(--config-item-size);\n  box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5);\n  border-radius: var(--border-radius);\n\n  > .bg-layers {\n    > .bg-layer-1 {\n      background-color: var(--color-gray-100);\n      border-radius: var(--border-radius);\n    }\n\n    > .bg-layer-2 {\n      border-radius: var(--border-radius);\n      transition: background-color 0.2s ease-out;\n    }\n  }\n\n  &.media-session-container {\n    padding: 0;\n\n    &.media-session-container-horizontal {\n      width: var(--max-item-size);\n    }\n\n    &.media-session-container-vertical {\n      height: var(--max-item-size);\n    }\n\n    .media-session {\n      border-radius: var(--border-radius);\n      overflow: hidden;\n    }\n  }\n\n  &:hover > .bg-layers > .bg-layer-2 {\n    background-color: var(--dock-item-hover-color);\n  }\n\n  &:active {\n    > .bg-layers > .bg-layer-1 {\n      filter: brightness(0.8);\n    }\n\n    .weg-item-icon {\n      transform: scale(0.8);\n    }\n  }\n\n  &:not(:active) {\n    > .bg-layers > .bg-layer-1 {\n      transition: filter 0.2s linear;\n    }\n\n    .weg-item-icon {\n      transition: transform 0.2s linear;\n    }\n  }\n\n  .weg-item-icon {\n    height: 100%;\n    width: unset;\n    aspect-ratio: 1/1;\n    filter: drop-shadow(0px 0px 1px #0000009a);\n    object-fit: contain;\n\n    /* &[data-shape=\"square\"] {} (this attribute exists but is not used on default theme) */\n\n    &.weg-item-start-icon,\n    &.weg-item-folder-icon {\n      filter: none;\n    }\n  }\n\n  .weg-item-title {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    flex: 1;\n    font-size: max(calc(var(--config-item-size) / 3), 12px);\n    font-weight: 600;\n    z-index: 1;\n\n    .vertical & {\n      display: none;\n    }\n  }\n\n  .weg-item-notification-badge {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    position: absolute;\n    top: 0;\n    right: 0;\n    transform: translate(25%, -25%);\n    background-color: var(--system-accent-color);\n    color: #efefef;\n    height: 16px;\n    min-width: 16px;\n    font-size: 10px;\n    font-weight: 600;\n    border-radius: 8px;\n  }\n\n  .weg-item-instance-counter-badge {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    position: absolute;\n    bottom: 0;\n    right: 0;\n    transform: translate(25%, 25%);\n    background-color: var(--color-gray-400);\n    color: var(--color-gray-100);\n    height: 16px;\n    min-width: 16px;\n    font-size: 10px;\n    font-weight: 600;\n    border-radius: 8px;\n  }\n\n  .weg-item-open-sign {\n    position: absolute;\n    width: 3px;\n    height: 3px;\n    border-radius: 6px;\n    background-color: var(--color-gray-600);\n    opacity: 0;\n    transition-property: width, height, transform, opacity, background-color, border-radius;\n    transition-duration: 0.2s;\n    transition-timing-function: linear;\n\n    .vertical & {\n      transform: translateX(-50%);\n    }\n\n    .horizontal & {\n      transform: translateY(-50%);\n    }\n\n    .top & {\n      bottom: calc(100% + var(--config-padding) / 2);\n    }\n\n    .bottom & {\n      top: calc(100% + var(--config-padding) / 2);\n    }\n\n    .left & {\n      right: calc(100% + var(--config-padding) / 2);\n    }\n\n    .right & {\n      left: calc(100% + var(--config-padding) / 2);\n    }\n\n    &.weg-item-open-sign-active {\n      opacity: 1;\n    }\n\n    &.weg-item-open-sign-focused {\n      background-color: var(--system-accent-color);\n\n      .vertical & {\n        height: 50%;\n      }\n\n      .horizontal & {\n        width: 50%;\n      }\n    }\n  }\n}\n\n.weg-context-menu-container {\n  padding: 3px;\n\n  > .bg-layers {\n    > .bg-layer-1 {\n      background-color: var(--color-gray-100);\n      border-radius: 10px;\n    }\n  }\n\n  .weg-context-menu {\n    --empty-rule: \"delete me on use\";\n  }\n\n  .weg-context-menu-item-icon {\n    width: 1em;\n  }\n}\n\n.weg-item-preview-container {\n  padding: 10px;\n  border-radius: 10px;\n\n  > .bg-layers {\n    > .bg-layer-1 {\n      background-color: var(--color-gray-100);\n      border-radius: 10px;\n    }\n  }\n\n  .weg-item-display-name {\n    white-space: nowrap;\n  }\n}\n\n.weg-item-preview {\n  padding: 6px 10px 10px 10px;\n  border-radius: 10px;\n}\n\n.weg-item-preview-topbar {\n  margin: 0 0 8px 0;\n}\n\n.weg-item-preview-title {\n  font-size: 14px;\n  font-weight: 600;\n  color: var(--color-gray-900);\n}\n\n.weg-item-preview-close {\n  --empty-rule: \"delete me on use\";\n}\n\n.weg-item-preview-image-container {\n  width: 240px;\n  height: calc(240px / 1.77);\n\n  display: flex;\n  justify-content: center;\n  align-items: center;\n\n  border-radius: 10px;\n  overflow: hidden;\n  border: 1px solid var(--color-gray-300);\n}\n\n.weg-item-preview-image {\n  max-height: 100%;\n  height: 100%;\n  object-fit: cover;\n  object-position: top;\n}\n\n.weg-item-no-preview {\n  width: 30px;\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/window-manager.css",
    "content": ".wm-leaf {\n  &.wm-leaf-with-borders {\n    border-style: solid;\n    border-width: var(--config-border-width);\n    border-color: var(--system-accent-light-color);\n\n    &.wm-leaf-focused {\n      border-color: var(--system-accent-lighter-color);\n    }\n  }\n}\n\n.wm-stack {\n  display: flex;\n  flex-direction: column;\n\n  .wm-stack-bar {\n    display: flex;\n    align-items: center;\n    height: 30px;\n    padding: 0 10px;\n    gap: var(--config-containers-gap);\n    border-radius: 10px;\n    background-color: var(--color-fixed-gray-900);\n    color: #fefefe;\n    font-size: 0.85rem;\n\n    .wm-stack-bar-item {\n      min-width: 0;\n      display: flex;\n      align-items: center;\n      gap: 5px;\n\n      .wm-stack-bar-item-icon {\n        width: 1rem;\n        height: 1rem;\n        flex-shrink: 0;\n      }\n\n      .wm-stack-bar-item-title {\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n        max-width: 100px;\n      }\n\n      &.wm-stack-bar-item-active {\n        color: var(--system-accent-lighter-color);\n      }\n    }\n  }\n}\n\n.wm-reserved {\n  background-color: var(--color-accent-lighter-color);\n}\n"
  },
  {
    "path": "src/static/themes/default/styles/workspaces-viewer.scss",
    "content": ".workspaces-viewer {\n  width: 100vw;\n  height: 100vh;\n  background-color: #000; // just in case wallpaper fails\n\n  .monitor {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 20px;\n    padding: 40px;\n\n    .active-wallpaper {\n      position: absolute;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      z-index: -1;\n      filter: blur(10px);\n    }\n\n    .windows {\n      width: 100%;\n      flex: 1;\n\n      display: flex;\n      justify-content: center;\n      align-items: flex-start;\n      flex-wrap: wrap;\n      gap: 10px;\n\n      overflow-y: auto;\n      overflow-x: hidden;\n\n      .window {\n        --header-size: 40px;\n        position: relative;\n        padding-top: var(--header-size);\n\n        display: flex;\n        flex-direction: column;\n        border-radius: 10px;\n        background: var(--color-gray-100);\n        flex-shrink: 0;\n        overflow: hidden;\n        width: fit-content;\n\n        .window-header {\n          position: absolute; // avoids taking in care the size of the title for the parent size.\n          height: var(--header-size);\n          top: 0;\n          left: 0;\n          width: 100%;\n\n          display: grid;\n          grid-template-columns: 20px 1fr min-content;\n          align-items: center;\n\n          gap: 10px;\n          padding: 10px;\n          border-bottom: 1px solid var(--color-gray-300);\n\n          .window-title {\n            font-weight: 600;\n            font-size: 0.9rem;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n          }\n        }\n\n        .window-preview-container {\n          min-width: 160px;\n          min-height: 90px;\n          width: fit-content;\n          max-width: 640px;\n          max-height: 360px;\n\n          display: flex;\n          justify-content: center;\n          align-items: center;\n\n          .window-preview {\n            width: auto;\n            height: auto;\n          }\n\n          .window-no-preview {\n            width: 30px;\n          }\n        }\n      }\n    }\n\n    .workspaces {\n      background: var(--color-gray-100);\n      border-radius: 10px;\n      padding: 20px;\n      display: flex;\n      gap: 10px;\n      flex-shrink: 0;\n\n      width: min-content;\n      max-width: 100%;\n      overflow-x: auto;\n      overflow-y: hidden;\n\n      .workspace {\n        position: relative;\n        width: 230px;\n        border-radius: 10px;\n        background: var(--color-gray-100);\n        box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.2);\n        padding: 8px;\n        flex-shrink: 0;\n\n        .workspace-header {\n          display: flex;\n          margin-bottom: 10px;\n          align-items: center;\n          gap: 10px;\n\n          .workspace-name-input {\n            flex: 1;\n            font-weight: 600;\n\n            &::placeholder {\n              color: inherit;\n            }\n          }\n        }\n\n        .workspace-preview {\n          position: relative;\n          width: 100%;\n          aspect-ratio: 16/9;\n          border-radius: 8px;\n          overflow: hidden;\n        }\n\n        &::before {\n          content: \"\";\n          position: absolute;\n          bottom: -4px;\n          left: 50%;\n          transform: translateX(-50%);\n          width: 60%;\n          border-radius: 4px;\n          height: 4px;\n          background-color: transparent;\n          transition: background-color 0.2s ease;\n        }\n\n        &.workspace-active {\n          &::before {\n            background-color: var(--system-accent-color);\n          }\n        }\n      }\n\n      .add-workspace {\n        width: 230px;\n        flex-shrink: 0;\n\n        border-radius: 8px;\n        background: var(--color-gray-100);\n        box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.2);\n\n        display: flex;\n        justify-content: center;\n        align-items: center;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/static/widgets/apps-menu/i18n/description.yml",
    "content": "hu: Menü a telepített alkalmazások böngészéséhez és elindításához\nuz: O'rnatilgan ilovalarni ko'rib chiqish va ishga tushirish uchun menyu\nmk: Мени за прелистување и стартување на инсталираните апликации\nar: قائمة لتصفح وتشغيل التطبيقات المثبتة\nhy: Տեղադրված հավելվածները զննելու և գործարկելու ընտրացանկ\nhr: Izbornik za pregledavanje i pokretanje instaliranih aplikacija\ntr: Yüklü uygulamalara göz atmak ve bunları başlatmak için bir menü\nzh-TW: 用於瀏覽和啟動已安裝應用程序的菜單\nen: A menu to browse and launch installed applications\nes: Un menú para explorar e iniciar aplicaciones instaladas.\nlt: Meniu, skirtas naršyti ir paleisti įdiegtas programas\nmt: Menu biex tfittex u tniedi applikazzjonijiet installati\nsk: Ponuka na prehľadávanie a spúšťanie nainštalovaných aplikácií\ntl: Isang menu para mag-browse at maglunsad ng mga naka-install na application\nps: د نصب شوي غوښتنلیکونو لټون او پیل کولو لپاره مینو\nbn: ইনস্টল করা অ্যাপ্লিকেশন ব্রাউজ এবং লঞ্চ করার জন্য একটি মেনু\nca: Un menú per navegar i iniciar aplicacions instal·lades\nru: Меню для просмотра и запуска установленных приложений.\nyo: Akojọ aṣayan lati lọ kiri ati ṣe ifilọlẹ awọn ohun elo ti a fi sii\nso: Menu si aad u baadho oo loo bilaabo codsiyada rakiban\nko: 설치된 애플리케이션을 탐색하고 실행하는 메뉴\nta: நிறுவப்பட்ட பயன்பாடுகளை உலாவவும் தொடங்கவும் ஒரு மெனு\nvi: Một menu để duyệt và khởi chạy các ứng dụng đã cài đặt\nte: ఇన్‌స్టాల్ చేసిన అప్లికేషన్‌లను బ్రౌజ్ చేయడానికి మరియు ప్రారంభించడానికి ఒక మెను\nid: Menu untuk menelusuri dan meluncurkan aplikasi yang diinstal\nnl: Een menu om door geïnstalleerde applicaties te bladeren en deze te starten\nfr: Un menu pour parcourir et lancer les applications installées\nne: ब्राउज गर्न र स्थापित अनुप्रयोगहरू सुरु गर्न मेनु\nms: Menu untuk menyemak imbas dan melancarkan aplikasi yang dipasang\neu: Instalatutako aplikazioak arakatzeko eta abiarazteko menua\nka: მენიუ დაინსტალირებული აპლიკაციების დასათვალიერებლად და გასაშვებად\nno: En meny for å bla gjennom og starte installerte applikasjoner\npt-PT: Um menu para navegar e iniciar aplicativos instalados\nbg: Меню за разглеждане и стартиране на инсталирани приложения\npt-BR: A menu to browse and launch installed applications\nth: เมนูสำหรับเรียกดูและเปิดแอปพลิเคชันที่ติดตั้ง\nfa: منویی برای مرور و راه اندازی برنامه های نصب شده\nde: Ein Menü zum Durchsuchen und Starten installierter Anwendungen\naz: Quraşdırılmış proqramları nəzərdən keçirmək və işə salmaq üçün menyu\nlo: ເມນູເພື່ອເອີ້ນເບິ່ງ ແລະເປີດແອັບພລິເຄຊັນທີ່ຕິດຕັ້ງ\nbs: Meni za pretraživanje i pokretanje instaliranih aplikacija\ncy: Dewislen i bori a lansio cymwysiadau sydd wedi'u gosod\nfi: Valikko asennettujen sovellusten selaamiseen ja käynnistämiseen\nsv: En meny för att bläddra och starta installerade applikationer\nuk: Меню для перегляду та запуску встановлених програм\npl: Menu umożliwiające przeglądanie i uruchamianie zainstalowanych aplikacji\nsr: Мени за претраживање и покретање инсталираних апликација\nis: Valmynd til að skoða og ræsa uppsett forrit\nmn: Суулгасан програмуудыг үзэх, эхлүүлэх цэс\ncs: Nabídka pro procházení a spouštění nainstalovaných aplikací\ntg: Меню барои дидан ва оғоз кардани барномаҳои насбшуда\nzu: Imenyu yokuphequlula futhi uqalise izinhlelo zokusebenza ezifakiwe\npa: ਸਥਾਪਿਤ ਐਪਲੀਕੇਸ਼ਨਾਂ ਨੂੰ ਬ੍ਰਾਊਜ਼ ਕਰਨ ਅਤੇ ਲਾਂਚ ਕਰਨ ਲਈ ਇੱਕ ਮੀਨੂ\nhe: תפריט לעיון והפעלת יישומים מותקנים\nkm: ម៉ឺនុយដើម្បីរុករក និងបើកដំណើរការកម្មវិធីដែលបានដំឡើង\net: Menüü installitud rakenduste sirvimiseks ja käivitamiseks\naf: '''n Kieslys om deur geïnstalleerde toepassings te blaai en te begin'\nlv: Izvēlne instalēto programmu pārlūkošanai un palaišanai\nam: የተጫኑ መተግበሪያዎችን ለማሰስ እና ለማስጀመር ምናሌ\nur: انسٹال شدہ ایپلی کیشنز کو براؤز کرنے اور لانچ کرنے کا ایک مینو\nlb: E Menü fir installéiert Uwendungen ze surfen an ze starten\nit: Un menu per sfogliare e avviare le applicazioni installate\nku: Menuyek ji bo gerîn û destpêkirina serîlêdanên sazkirî\nel: Ένα μενού για περιήγηση και εκκίνηση εγκατεστημένων εφαρμογών\ngu: ઇન્સ્ટોલ કરેલ એપ્લિકેશનોને બ્રાઉઝ કરવા અને લોંચ કરવા માટેનું મેનુ\nda: En menu til at gennemse og starte installerede applikationer\nja: インストールされているアプリケーションを参照して起動するためのメニュー\nzh-CN: 用于浏览和启动已安装应用程序的菜单\nhi: इंस्टॉल किए गए एप्लिकेशन ब्राउज़ करने और लॉन्च करने के लिए एक मेनू\nsw: Menyu ya kuvinjari na kuzindua programu zilizosakinishwa\nsi: ස්ථාපිත යෙදුම් බ්‍රවුස් කිරීමට සහ දියත් කිරීමට මෙනුවක්\nro: Un meniu pentru a răsfoi și a lansa aplicațiile instalate\n"
  },
  {
    "path": "src/static/widgets/apps-menu/i18n/display_name.yml",
    "content": "zh-TW: 應用程序菜單\naf: Toepassings-kieslys\nur: ایپس مینو\nms: Menu Apl\nlb: Apps Menu\npl: Menu aplikacji\nsw: Menyu ya Programu\nso: Menu Apps\nam: የመተግበሪያዎች ምናሌ\nhr: Izbornik aplikacija\nne: एप्स मेनु\nyo: Akojọ Apps\nth: เมนูแอพ\nnl: Apps-menu\nel: Μενού εφαρμογών\nja: アプリメニュー\nfr: Menu des applications\ntl: Menu ng Apps\nmk: Мени со апликации\nes: Menú de aplicaciones\nta: ஆப்ஸ் மெனு\nuz: Ilovalar menyusi\nbn: অ্যাপস মেনু\nka: აპების მენიუ\net: Rakenduste menüü\nis: Valmynd forrita\ntg: Менюи Барномаҳо\nzh-CN: 应用程序菜单\npt-PT: Menu de aplicativos\nfa: منوی برنامه ها\nhe: תפריט אפליקציות\nhu: Alkalmazások menü\nmn: Програмын цэс\nvi: Menu ứng dụng\nbs: Meni aplikacija\nen: Apps Menu\ngu: એપ્લિકેશન્સ મેનૂ\nlo: ເມນູແອັບ\nzu: Imenyu yezinhlelo zokusebenza\nuk: Меню програм\ncs: Nabídka aplikací\nkm: ម៉ឺនុយកម្មវិធី\neu: Aplikazioen menua\nku: Pêşeka Apps\nlv: Lietojumprogrammu izvēlne\nko: 앱 메뉴\nde: Apps-Menü\nmt: Apps Menu\nps: د ایپس مینو\nro: Meniul Aplicații\nar: قائمة التطبيقات\nsv: App-menyn\nhi: ऐप्स मेनू\npa: ਐਪਸ ਮੀਨੂ\nbg: Меню с приложения\nfi: Sovellukset-valikko\nid: Menu Aplikasi\nsr: Мени апликација\nca: Menú d'aplicacions\nit: Menù delle app\nno: Appmeny\npt-BR: Apps Menu\nhy: Հավելվածների ընտրացանկ\nsk: Ponuka aplikácií\nru: Меню приложений\nte: యాప్స్ మెనూ\ntr: Uygulamalar Menüsü\nsi: යෙදුම් මෙනුව\ncy: Dewislen Apiau\naz: Proqramlar Menyu\nlt: Programų meniu\nda: Apps menu\n"
  },
  {
    "path": "src/static/widgets/apps-menu/metadata.yml",
    "content": "id: \"@seelen/apps-menu\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\nhidden: true\n# lazy: true\nsettings:\n  - type: switch\n    key: acrylic\n    label: Acrylic Effect\n    defaultValue: false\n"
  },
  {
    "path": "src/static/widgets/bluetooth-popup/i18n/description.yml",
    "content": "he: Bluetooth device manager popup\nyo: Bluetooth device manager popup\npa: Bluetooth device manager popup\nen: Bluetooth device manager popup\net: Bluetooth device manager popup\ntg: Bluetooth device manager popup\nro: Bluetooth device manager popup\nhu: Bluetooth device manager popup\nbn: Bluetooth device manager popup\nka: Bluetooth device manager popup\nfr: Bluetooth device manager popup\nuk: Bluetooth device manager popup\nbg: Bluetooth device manager popup\nam: Bluetooth device manager popup\ncy: Bluetooth device manager popup\nms: Bluetooth device manager popup\nda: Bluetooth device manager popup\nar: Bluetooth device manager popup\nru: Bluetooth device manager popup\nsk: Bluetooth device manager popup\nzu: Bluetooth device manager popup\nfa: Bluetooth device manager popup\nhr: Bluetooth device manager popup\nps: Bluetooth device manager popup\nth: Bluetooth device manager popup\nsv: Bluetooth device manager popup\nte: Bluetooth device manager popup\nis: Bluetooth device manager popup\npt-BR: Bluetooth device manager popup\nmn: Bluetooth device manager popup\nsw: Bluetooth device manager popup\nit: Bluetooth device manager popup\nvi: Bluetooth device manager popup\naf: Bluetooth device manager popup\nca: Bluetooth device manager popup\nkm: Bluetooth device manager popup\nlb: Bluetooth device manager popup\ntl: Bluetooth device manager popup\ngu: Bluetooth device manager popup\npt-PT: Bluetooth device manager popup\nsi: Bluetooth device manager popup\nur: Bluetooth device manager popup\neu: Bluetooth device manager popup\nmk: Bluetooth device manager popup\nid: Bluetooth device manager popup\nlt: Bluetooth device manager popup\nnl: Bluetooth device manager popup\nlv: Bluetooth device manager popup\naz: Bluetooth device manager popup\nku: Bluetooth device manager popup\nes: Bluetooth device manager popup\nlo: Bluetooth device manager popup\nmt: Bluetooth device manager popup\nta: Bluetooth device manager popup\nhi: Bluetooth device manager popup\nno: Bluetooth device manager popup\nzh-TW: Bluetooth device manager popup\npl: Bluetooth device manager popup\nuz: Bluetooth device manager popup\nbs: Bluetooth device manager popup\nsr: Bluetooth device manager popup\ncs: Bluetooth device manager popup\ntr: Bluetooth device manager popup\nne: Bluetooth device manager popup\nel: Bluetooth device manager popup\nzh-CN: Bluetooth device manager popup\nja: Bluetooth device manager popup\nso: Bluetooth device manager popup\nde: Bluetooth device manager popup\nfi: Bluetooth device manager popup\nko: Bluetooth device manager popup\nhy: Bluetooth device manager popup\n"
  },
  {
    "path": "src/static/widgets/bluetooth-popup/i18n/display_name.yml",
    "content": "ms: Bluetooth Popup\nno: Bluetooth Popup\npa: Bluetooth Popup\ngu: Bluetooth Popup\nbs: Bluetooth Popup\npt-BR: Bluetooth Popup\nda: Bluetooth Popup\nel: Bluetooth Popup\nps: Bluetooth Popup\ntl: Bluetooth Popup\nkm: Bluetooth Popup\nsi: Bluetooth Popup\nka: Bluetooth Popup\nuz: Bluetooth Popup\nhu: Bluetooth Popup\naf: Bluetooth Popup\nhy: Bluetooth Popup\nja: Bluetooth Popup\nsv: Bluetooth Popup\ntr: Bluetooth Popup\nfi: Bluetooth Popup\nar: Bluetooth Popup\nth: Bluetooth Popup\nro: Bluetooth Popup\nko: Bluetooth Popup\nbg: Bluetooth Popup\nzh-TW: Bluetooth Popup\nlt: Bluetooth Popup\nlo: Bluetooth Popup\nca: Bluetooth Popup\ncs: Bluetooth Popup\nlv: Bluetooth Popup\nam: Bluetooth Popup\nte: Bluetooth Popup\nbn: Bluetooth Popup\nso: Bluetooth Popup\npt-PT: Bluetooth Popup\nyo: Bluetooth Popup\nku: Bluetooth Popup\nde: Bluetooth Popup\nnl: Bluetooth Popup\nsk: Bluetooth Popup\nuk: Bluetooth Popup\nen: Bluetooth Popup\nmk: Bluetooth Popup\nzh-CN: Bluetooth Popup\nmn: Bluetooth Popup\nhr: Bluetooth Popup\naz: Bluetooth Popup\nru: Bluetooth Popup\nmt: Bluetooth Popup\nsr: Bluetooth Popup\neu: Bluetooth Popup\nis: Bluetooth Popup\net: Bluetooth Popup\nit: Bluetooth Popup\nlb: Bluetooth Popup\nsw: Bluetooth Popup\nzu: Bluetooth Popup\nne: Bluetooth Popup\npl: Bluetooth Popup\nes: Bluetooth Popup\ntg: Bluetooth Popup\nhi: Bluetooth Popup\nfa: Bluetooth Popup\nhe: Bluetooth Popup\nvi: Bluetooth Popup\nid: Bluetooth Popup\nur: Bluetooth Popup\ncy: Bluetooth Popup\nfr: Bluetooth Popup\nta: Bluetooth Popup\n"
  },
  {
    "path": "src/static/widgets/bluetooth-popup/metadata.yml",
    "content": "id: \"@seelen/bluetooth-popup\"\nicon: TbBluetooth\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/bluetooth-popup/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-bluetooth-popup\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: TbBluetooth\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Bluetooth\n  tooltip: |-\n    return \"Bluetooth\";\n  template: |-\n    return Icon({ name: \"TbBluetooth\" });\n  onClickV2: |-\n    trigger(\"@seelen/bluetooth-popup\");\n"
  },
  {
    "path": "src/static/widgets/calendar-popup/i18n/description.yml",
    "content": "bs: Prikažite iskačući prozor kalendara kada kliknete na datum\nka: აჩვენეთ კალენდრის ამომხტარი ფანჯარა თარიღზე დაწკაპუნებისას\nku: Dema ku li ser tarîxê bikirtînin, vebijarkek salnameyê nîşan bidin\nen: Display a calendar popup when clicking the date\nde: Beim Klicken auf das Datum wird ein Kalender-Popup angezeigt\nzh-CN: 单击日期时显示日历弹出窗口\nsw: Onyesha dirisha ibukizi la kalenda unapobofya tarehe\nuk: Відображати спливаюче вікно календаря при натисканні дати\nis: Birtu sprettiglugga fyrir dagatal þegar þú smellir á dagsetninguna\nlo: ສະແດງປັອບອັບປະຕິທິນເມື່ອຄລິກວັນທີ\nar: عرض نافذة منبثقة للتقويم عند النقر فوق التاريخ\nko: 날짜 클릭 시 달력 팝업 표시\nhe: הצג חלון קופץ של לוח שנה בעת לחיצה על התאריך\naf: Vertoon 'n kalenderopspringer wanneer u op die datum klik\ncy: Arddangos ffenestr naid calendr wrth glicio ar y dyddiad\nuz: Sanani bosganingizda taqvim qalqib chiquvchi oynasini ko'rsating\ngu: તારીખ પર ક્લિક કરતી વખતે કૅલેન્ડર પૉપઅપ પ્રદર્શિત કરો\nca: Mostra una finestra emergent de calendari en fer clic a la data\npt-PT: Exibir um pop-up de calendário ao clicar na data\nkm: បង្ហាញ​ប្រតិទិន​លេចឡើង​ពេល​ចុច​កាលបរិច្ឆេទ\nsv: Visa en kalenderpopup när du klickar på datumet\nfr: Afficher une fenêtre contextuelle de calendrier lorsque vous cliquez sur la date\nro: Afișați o fereastră de tip pop-up de calendar când faceți clic pe dată\nta: தேதியைக் கிளிக் செய்யும் போது ஒரு காலண்டர் பாப்அப்பைக் காண்பிக்கவும்\nel: Εμφανίστε ένα αναδυόμενο ημερολόγιο όταν κάνετε κλικ στην ημερομηνία\nlv: Parādiet kalendāra uznirstošo logu, noklikšķinot uz datuma\nsi: දිනය ක්ලික් කරන විට දින දර්ශන උත්පතනයක් සංදර්ශන කරන්න\npt-BR: Display a calendar popup when clicking the date\nms: Paparkan pop timbul kalendar apabila mengklik tarikh\nmn: Огноог дарах үед календарийн цонх гарч ирнэ\nit: Visualizza un popup del calendario quando si fa clic sulla data\nbn: তারিখে ক্লিক করার সময় একটি ক্যালেন্ডার পপআপ প্রদর্শন করুন\npl: Wyświetl wyskakujące okienko kalendarza po kliknięciu daty\net: Kuupäeva klõpsamisel kuvatakse kalendri hüpikaken\nmk: Прикажи скокачки прозорец од календарот кога ќе се кликне на датумот\nps: کله چې په نیټه کلیک وکړئ د کیلنڈر پاپ اپ ښکاره کړئ\nda: Vis en kalender-popup, når du klikker på datoen\nso: Muuji popup kalandarka markaad gujinayso taariikhda\nhi: दिनांक पर क्लिक करते समय एक कैलेंडर पॉपअप प्रदर्शित करें\nja: 日付をクリックするとカレンダーのポップアップが表示されます\nno: Vis en kalender-popup når du klikker på datoen\naz: Tarixə kliklədikdə təqvim popupunu göstərin\nhu: A dátumra kattintva megjelenik egy felugró naptár\npa: ਮਿਤੀ 'ਤੇ ਕਲਿੱਕ ਕਰਨ ਵੇਲੇ ਇੱਕ ਕੈਲੰਡਰ ਪੌਪਅੱਪ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ\nlt: Spustelėjus datą, rodomas iššokantis kalendoriaus langas\ncs: Po kliknutí na datum zobrazit vyskakovací okno kalendáře\nvi: Hiển thị cửa sổ bật lên lịch khi nhấp vào ngày\nzu: Bonisa iphophaphu yekhalenda lapho uchofoza idethi\nbg: Показване на изскачащ прозорец на календара, когато щракнете върху датата\nes: Mostrar una ventana emergente de calendario al hacer clic en la fecha\nru: Отображать всплывающее окно календаря при нажатии на дату\nyo: Ṣe afihan igarun kalẹnda nigbati o ba tẹ ọjọ naa\nne: मिति क्लिक गर्दा क्यालेन्डर पपअप प्रदर्शन गर्नुहोस्\nam: ቀኑን ጠቅ ሲያደርጉ የቀን መቁጠሪያ ብቅ ባይ ያሳዩ\ntl: Magpakita ng popup ng kalendaryo kapag nag-click sa petsa\nid: Menampilkan popup kalender saat mengklik tanggal\nlb: Weist e Kalenner Popup wann Dir op den Datum klickt\nnl: Geef een kalenderpop-up weer wanneer u op de datum klikt\nhy: Ցուցադրել օրացույցի ելնող պատուհան՝ սեղմելով ամսաթիվը\nfi: Näytä kalenterin ponnahdusikkuna, kun napsautat päivämäärää\nhr: Prikažite skočni prozor kalendara kada kliknete na datum\nsr: Прикажите искачући прозор календара када кликнете на датум\neu: Erakutsi egutegi-leiho bat data sakatzean\nur: تاریخ پر کلک کرتے وقت کیلنڈر پاپ اپ ڈسپلے کریں\nfa: هنگام کلیک کردن روی تاریخ، یک پنجره بازشو تقویم نمایش داده شود\nmt: Uri popup tal-kalendarju meta tikklikkja d-data\ntg: Ҳангоми клик кардани сана, поп-ап тақвимро нишон диҳед\nte: తేదీని క్లిక్ చేసినప్పుడు క్యాలెండర్ పాప్‌అప్‌ని ప్రదర్శించండి\ntr: Tarihe tıklandığında bir takvim açılır penceresi görüntüle\nzh-TW: 單擊日期時顯示日曆彈出窗口\nsk: Po kliknutí na dátum sa zobrazí kontextové okno kalendára\nth: แสดงป๊อปอัปปฏิทินเมื่อคลิกวันที่\n"
  },
  {
    "path": "src/static/widgets/calendar-popup/i18n/display_name.yml",
    "content": "cs: Kalendář\nhr: Kalendar\nar: تقويم\nyo: Kalẹnda\nhi: कैलेंडर\nbs: Kalendar\nkm: ប្រតិទិន\nms: Kalendar\nhe: לוּחַ שָׁנָה\nnl: Kalender\nro: Calendaristic\ntl: Kalendaryo\nko: 달력\nis: Dagatal\nku: Salname\npt-PT: Calendário\ncy: Calendr\nta: நாட்காட்டி\nte: క్యాలెండర్\nuk: Календар\nzh-CN: 日历\nno: Kalender\nso: Kalandarka\nsw: Kalenda\nth: ปฏิทิน\nja: カレンダー\nka: კალენდარი\nit: Calendario\neu: Egutegia\nsk: Kalendár\nca: Calendari\nbn: ক্যালেন্ডার\nzh-TW: 日曆\nfi: Kalenteri\ntr: Takvim\nlv: Kalendārs\naf: Kalender\nlo: ປະຕິທິນ\npt-BR: Calendar\nes: Calendario\nam: የቀን መቁጠሪያ\nps: جنتري\ntg: Тақвим\nne: पात्रो\nel: Ημερολόγιο\npl: Kalendarz\nzu: Ikhalenda\nsr: Календар\nen: Calendar\net: Kalender\nid: Kalender\ngu: કેલેન્ડર\npa: ਕੈਲੰਡਰ\nhu: Naptár\nda: Kalender\nlt: Kalendorius\naz: Təqvim\nsi: දින දර්ශනය\nfa: تقویم\nur: کیلنڈر\nmt: Kalendarju\nmn: Хуанли\nru: Календарь\nbg: Календар\nfr: Calendrier\nsv: Kalender\nuz: Kalendar\nvi: Lịch\nmk: Календар\nhy: Օրացույց\nde: Kalender\nlb: Kalenner\n"
  },
  {
    "path": "src/static/widgets/calendar-popup/metadata.yml",
    "content": "id: \"@seelen/calendar-popup\"\nicon: FaCalendar\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/calendar-popup/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-calendar-popup\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: FaCalendar\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Date\n  template: return date\n  onClickV2: |-\n    trigger(\"@seelen/calendar-popup\");\n"
  },
  {
    "path": "src/static/widgets/context-menu/i18n/description.yml",
    "content": "ca: Menú contextual reutilitzable per widgets\nuz: Vidjetlar bo'yicha qayta foydalanish mumkin bo'lgan kontekst menyusi\nam: እንደገና ጥቅም ላይ ሊውል የሚችል የአውድ ምናሌ በመግብሮች\nlo: ເມນູບໍລິບົດໃຊ້ຄືນໄດ້ໂດຍວິດເຈັດ\nfa: منوی زمینه قابل استفاده مجدد توسط ویجت ها\nmn: Виджетээр дахин ашиглах боломжтой контекст цэс\nta: விட்ஜெட்கள் மூலம் மீண்டும் பயன்படுத்தக்கூடிய சூழல் மெனு\nzh-TW: 小部件可重複使用的上下文菜單\nes: Menú contextual reutilizable por widgets\nlv: Atkārtoti lietojama logrīku kontekstizvēlne\neu: Testuinguru-menua berrerabilgarria widgeten bidez\naf: Herbruikbare konteks kieslys deur widgets\nms: Menu konteks boleh diguna semula oleh widget\npt-BR: Reusable context menu by widgets\nru: Многоразовое контекстное меню виджетов\nno: Gjenbrukbar kontekstmeny av widgets\nko: 위젯별 재사용 가능한 컨텍스트 메뉴\npt-PT: Menu de contexto reutilizável por widgets\nis: Endurnotanleg samhengisvalmynd eftir búnaði\ntg: Менюи контекстии аз нав истифодашаванда тавассути виджетҳо\nuk: Багаторазове контекстне меню віджетами\nmk: Контексно мени за повеќекратно користење по графички контроли\nbn: উইজেট দ্বারা পুনরায় ব্যবহারযোগ্য প্রসঙ্গ মেনু\nfi: Uudelleen käytettävä kontekstivalikko widgetien avulla\nhi: \"विजेट्स द्वारा पुन: प्रयोज्य संदर्भ मेनू\"\nzh-CN: 小部件可重复使用的上下文菜单\npa: ਵਿਜੇਟਸ ਦੁਆਰਾ ਮੁੜ ਵਰਤੋਂ ਯੋਗ ਸੰਦਰਭ ਮੀਨੂ\nku: Menuya çarçovê ya ji nû ve bikar anîn ji hêla widgetan ve\nso: Liiska macnaha guud ee dib loo isticmaali karo iyada oo loo marayo widgets\nvi: Menu ngữ cảnh có thể tái sử dụng theo widget\ngu: વિજેટ્સ દ્વારા ફરીથી વાપરી શકાય તેવું સંદર્ભ મેનૂ\nsr: Контекстни мени за вишекратну употребу помоћу виџета\nbg: Контекстно меню за многократна употреба от джаджи\nde: Wiederverwendbares Kontextmenü durch Widgets\nne: \"विजेटहरू द्वारा पुन: प्रयोज्य सन्दर्भ मेनु\"\nzu: Imenyu yokuqukethwe engasetshenziswa kabusha ngamawijethi\nte: విడ్జెట్‌ల ద్వారా పునర్వినియోగ సందర్భ మెను\nit: Menu contestuale riutilizzabile tramite widget\ntl: Magagamit muli ang menu ng konteksto ng mga widget\nfr: Menu contextuel réutilisable par widgets\nsk: Opätovne použiteľné kontextové menu pomocou miniaplikácií\nps: د ویجټونو لخوا د بیا کارولو وړ شرایطو مینو\nid: Menu konteks yang dapat digunakan kembali dengan widget\nmt: Menu tal-kuntest li jista' jerġa' jintuża minn widgets\nel: Επαναχρησιμοποιήσιμο μενού περιβάλλοντος από γραφικά στοιχεία\nlt: Pakartotinai naudojamas kontekstinis meniu valdikliais\npl: Menu kontekstowe wielokrotnego użytku według widżetów\nhu: Újrafelhasználható helyi menü widgetek által\nbs: Kontekstni meni za višekratnu upotrebu po widgetima\nro: Meniu contextual reutilizabil prin widget-uri\nen: Reusable context menu by widgets\nda: Genanvendelig kontekstmenu af widgets\nhy: Վերօգտագործելի համատեքստային մենյու վիդջեթների միջոցով\nsw: Menyu ya muktadha inayoweza kutumika tena kulingana na wijeti\naz: Vidjetlər tərəfindən təkrar istifadə edilə bilən kontekst menyusu\nar: قائمة السياق القابلة لإعادة الاستخدام عن طريق الحاجيات\nhe: תפריט הקשר שניתן לשימוש חוזר לפי ווידג'טים\nnl: Herbruikbaar contextmenu door widgets\nsi: විජට් මගින් නැවත භාවිතා කළ හැකි සන්දර්භය මෙනුව\nsv: Återanvändbar snabbmeny av widgets\nth: เมนูบริบทที่ใช้ซ้ำได้ด้วยวิดเจ็ต\net: Korduvkasutatav kontekstimenüü vidinate abil\nhr: Višekratni kontekstni izbornik pomoću widgeta\nlb: Wiederverwendbare Kontextmenü vu Widgets\ntr: Widget'lara göre yeniden kullanılabilir içerik menüsü\ncs: Opakovaně použitelné kontextové menu pomocí widgetů\nur: ویجٹ کے ذریعہ دوبارہ استعمال کے قابل سیاق و سباق کا مینو\nka: ხელახლა გამოყენებადი კონტექსტური მენიუ ვიჯეტებით\ncy: Dewislen cyd-destun amldro yn ôl widgets\nyo: Akojọ aṣiwaju ọrọ atunlo nipasẹ awọn ẹrọ ailorukọ\nja: ウィジェットによる再利用可能なコンテキスト メニュー\nkm: ម៉ឺនុយបរិបទដែលអាចប្រើឡើងវិញបានដោយធាតុក្រាហ្វិក\n"
  },
  {
    "path": "src/static/widgets/context-menu/i18n/display_name.yml",
    "content": "gu: સંદર્ભ મેનૂ\nit: Menù contestuale\nsw: Menyu ya Muktadha\nuz: Kontekst menyusi\ntr: İçerik Menüsü\nms: Menu Konteks\nca: Menú contextual\npt-PT: Menu de contexto\nhu: Helyi menü\nfi: Kontekstivalikko\ntl: Menu ng Konteksto\nlt: Kontekstinis meniu\ncs: Kontextové menu\nhe: תפריט הקשר\nid: Menu Konteks\nno: Kontekstmeny\nyo: Akojọ Akojọ aṣyn\nlb: Kontextmenü\net: Kontekstimenüü\nis: Samhengisvalmynd\nes: Menú contextual\nps: د متن مینو\nde: Kontextmenü\nfr: Menu contextuel\ntg: Менюи контекстӣ\nbn: প্রসঙ্গ মেনু\nzu: Imenyu Yokuqukethwe\nen: Context Menu\nel: Μενού περιβάλλοντος\nmk: Контекстно мени\nhi: संदर्भ मेनू\naf: Kontekskieslys\npa: ਸੰਦਰਭ ਮੀਨੂ\nne: सन्दर्भ मेनु\nsv: Snabbmeny\nnl: Contextmenu\nvi: Trình đơn ngữ cảnh\nhr: Kontekstni izbornik\nur: سیاق و سباق کا مینو\ncy: Dewislen Cyd-destun\npt-BR: Context Menu\nmn: Контекст цэс\nko: 상황에 맞는 메뉴\nhy: Համատեքստային մենյու\nar: قائمة السياق\nlo: ເມນູບໍລິບົດ\nlv: Konteksta izvēlne\nta: சூழல் மெனு\nzh-CN: 上下文菜单\nzh-TW: 內容選單\nmt: Menu tal-Kuntest\nso: Menu Context\nbs: Kontekstni meni\nsr: Контекстни мени\nka: კონტექსტური მენიუ\nda: Kontekstmenu\nsi: සන්දර්භය මෙනුව\nsk: Kontextové menu\neu: Testuinguruko menua\nja: コンテキストメニュー\nuk: Контекстне меню\naz: Kontekst menyusu\npl: Menu kontekstowe\nru: Контекстное меню\nte: సందర్భ మెను\nku: Pêşeka Context\nro: Meniul contextual\nkm: ម៉ឺនុយបរិបទ\nth: เมนูบริบท\nam: የአውድ ምናሌ\nfa: منوی زمینه\nbg: Контекстно меню\n"
  },
  {
    "path": "src/static/widgets/context-menu/metadata.yml",
    "content": "id: \"@seelen/context-menu\"\nicon: HiOutlineDotsVertical\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Multiple\nloader: Internal\npreset: Popup\nlazy: true\nhidden: true\n"
  },
  {
    "path": "src/static/widgets/flyouts/i18n/description.yml",
    "content": "sv: Visar avvikelser på vissa händelser som volymändring, etc.\npt-PT: Mostra submenus sobre determinados eventos, como alteração de volume, etc.\nit: Mostra i riquadri su determinati eventi come la modifica del volume, ecc.\ntr: Ses seviyesi değişikliği vb. gibi belirli olaylara ilişkin açılır menüleri gösterir.\nde: Zeigt Flyouts zu bestimmten Ereignissen wie Lautstärkeänderungen usw. an.\nhu: Megjeleníti a repüléseket bizonyos eseményekről, például a hangerő változásáról stb.\nkm: បង្ហាញការហោះហើរនៅលើព្រឹត្តិការណ៍ជាក់លាក់ដូចជាការផ្លាស់ប្តូរកម្រិតសំឡេងជាដើម។\nlv: Rāda lidojumus par noteiktiem notikumiem, piemēram, skaļuma izmaiņām utt.\nno: Viser flyouts på visse hendelser som volumendring osv.\ntl: Nagpapakita ng mga flyout sa ilang partikular na kaganapan tulad ng pagbabago ng volume, atbp.\nzh-CN: 显示某些事件的弹出窗口，例如音量变化等。\ncs: Zobrazuje plovoucí nabídky pro určité události, jako je změna hlasitosti atd.\nzh-TW: 顯示某些事件的彈出窗口，例如音量變化等。\nuz: Ovoz balandligini o'zgartirish kabi ba'zi hodisalar bo'yicha uchishlarni ko'rsatadi.\nne: भोल्युम परिवर्तन, आदि जस्ता निश्चित घटनाहरूमा फ्लाइआउटहरू देखाउँदछ।\nda: Viser flyouts på visse begivenheder som lydstyrkeændringer osv.\naz: Həcm dəyişikliyi və s. kimi müəyyən hadisələr üzrə uçan məlumatları göstərir.\nja: 音量変更などの特定のイベントに関するポップアップを表示します。\nur: حجم میں تبدیلی ، وغیرہ جیسے کچھ واقعات پر فلائی آؤٹ دکھاتا ہے۔\naf: Wys uitblinkers op sekere gebeurtenisse soos volumeverandering, ens.\nbg: Показва падащи менюта при определени събития като промяна на звука и др.\nko: 볼륨 변경 등과 같은 특정 이벤트에 대한 플라이아웃을 표시합니다.\nru: Показывает всплывающие окна при определенных событиях, таких как изменение громкости и т. д.\nfr: Affiche des volants sur certains événements comme le changement de volume, etc.\nth: แสดงการบินในเหตุการณ์บางอย่าง เช่น การเปลี่ยนแปลงระดับเสียง ฯลฯ\nlt: Rodo tam tikrų įvykių, pvz., garsumo keitimo ir kt.\nro: Afișează avertismente pentru anumite evenimente, cum ar fi modificarea volumului etc.\nar: يعرض النشرات في أحداث معينة مثل تغيير الحجم، وما إلى ذلك.\nel: Εμφανίζει πτήσεις σε ορισμένα συμβάντα, όπως αλλαγή έντασης ήχου κ.λπ.\nhr: Prikazuje letjelice za određene događaje poput promjene glasnoće itd.\nbs: Prikazuje prelaske za određene događaje kao što je promjena jačine zvuka, itd.\nka: აჩვენებს ფრენებს გარკვეულ მოვლენებზე, როგორიცაა ხმის ცვლილება და ა.შ.\nso: Wuxuu muujiyaa duullimaadyo ku saabsan dhacdooyinka qaarkood sida isbeddelka mugga, iwm.\nte: వాల్యూమ్ మార్పు మొదలైన కొన్ని ఈవెంట్‌లపై ఫ్లైఅవుట్‌లను చూపుతుంది.\nfa: پروازها را در رویدادهای خاصی مانند تغییر صدا و غیره نشان می دهد.\nku: Li ser hin bûyeran, mîna guheztina dengan, hwd.\npt-BR: Shows flyouts on certain events like volume change, etc.\nis: Sýnir frávik á ákveðnum atburðum eins og hljóðstyrksbreytingu osfrv.\nid: Menampilkan flyout pada peristiwa tertentu seperti perubahan volume, dll.\nes: Muestra menús desplegables sobre ciertos eventos como cambios de volumen, etc.\npa: ਕੁਝ ਖਾਸ ਘਟਨਾਵਾਂ ਜਿਵੇਂ ਕਿ ਵੌਲਯੂਮ ਤਬਦੀਲੀ, ਆਦਿ 'ਤੇ ਫਲਾਈਆਉਟ ਦਿਖਾਉਂਦਾ ਹੈ।\ngu: વોલ્યુમ ચેન્જ વગેરે જેવી ચોક્કસ ઘટનાઓ પર ફ્લાયઆઉટ્સ બતાવે છે.\nhe: מראה חליפות על אירועים מסוימים כמו שינוי עוצמת הקול וכו'.\ntg: Нишондиҳандаҳоро дар рӯйдодҳои муайян, ба монанди тағирёбии ҳаҷм ва ғайра нишон медиҳад.\net: Näitab väljalende teatud sündmustel, nagu helitugevuse muutus jne.\nsi: ශබ්දය වෙනස් කිරීම වැනි ඇතැම් සිදුවීම්වල පියාසර කිරීම් පෙන්වයි.\nps: په ځینو پیښو کې پروازونه ښیې لکه د حجم بدلون، او نور.\nuk: Показує спливаючі панелі під час певних подій, наприклад зміни гучності тощо.\nen: Shows flyouts on certain events like volume change, etc.\nlb: Weist Flyouts op bestëmmten Eventer wéi Volumen änneren, etc.\nzu: Ibonisa ama-flyout emicimbini ethile efana nokushintsha kwevolumu, njll.\ncy: Yn dangos teithiau hedfan ar rai digwyddiadau fel newid cyfaint, ac ati.\nnl: Toont flyouts bij bepaalde gebeurtenissen, zoals volumewijzigingen, enz.\nmk: Прикажува прелетувања на одредени настани како што се промена на јачината на звукот итн.\nmt: Juri flyouts fuq ċerti avvenimenti bħal bidla fil-volum, eċċ.\nmn: Дууны түвшний өөрчлөлт гэх мэт тодорхой үйл явдлуудын гүйлгээг харуулна.\nta: ஒலியளவை மாற்றுதல் போன்ற சில நிகழ்வுகளின் ஃப்ளைஅவுட்களைக் காட்டுகிறது.\nhy: \"Ցույց է տալիս թռիչքները որոշակի իրադարձությունների վրա, ինչպիսիք են ձայնի ծավալի փոփոխությունը և այլն:\"\nsk: Zobrazuje rozbalenia pri určitých udalostiach, ako je zmena hlasitosti atď.\nhi: वॉल्यूम परिवर्तन आदि जैसी कुछ घटनाओं पर फ्लाईआउट दिखाता है।\npl: Pokazuje wysuwane menu dotyczące określonych zdarzeń, takich jak zmiana głośności itp.\nsr: Приказује преласке за одређене догађаје као што је промена јачине звука итд.\nca: Mostra els menús volants en determinats esdeveniments, com ara el canvi de volum, etc.\nfi: Näyttää lentoja tietyistä tapahtumista, kuten äänenvoimakkuuden muutoksista jne.\nms: Menunjukkan flyout pada acara tertentu seperti perubahan volum, dsb.\nvi: Hiển thị các tờ rơi về các sự kiện nhất định như thay đổi âm lượng, v.v.\nam: እንደ የድምጽ ለውጥ፣ ወዘተ ባሉ አንዳንድ ክስተቶች ላይ የበረራ መውጣትን ያሳያል።\nbn: ভলিউম পরিবর্তন ইত্যাদির মতো নির্দিষ্ট ইভেন্টে ফ্লাইআউট দেখায়।\nlo: ສະ​ແດງ​ໃຫ້​ເຫັນ flyouts ໃນ​ບາງ​ກິດ​ຈະ​ກໍາ​ເຊັ່ນ​ການ​ປ່ຽນ​ແປງ​ລະ​ດັບ​ສຽງ​, ແລະ​ອື່ນໆ​.\neu: Gertaera jakin batzuetako hegaldiak erakusten ditu, hala nola bolumen-aldaketa, etab.\nsw: Huonyesha miondoko ya kuruka kwenye matukio fulani kama vile mabadiliko ya sauti, n.k.\nyo: Ṣe afihan awọn ilọkuro lori awọn iṣẹlẹ kan bi iyipada iwọn didun, ati bẹbẹ lọ.\n"
  },
  {
    "path": "src/static/widgets/flyouts/i18n/display_name.yml",
    "content": "bg: Излитащи елементи\ncy: Hedfan\nuk: Випадаючі елементи\nvi: tờ rơi\nzu: Ama-Flyouts\nsk: Flyouts\nte: ఫ్లైఅవుట్‌లు\nno: Flyouts\nuz: Uchib ketishlar\nes: Menús desplegables\nja: フライアウト\nko: 플라이아웃\nmn: Нислэгүүд\nzh-CN: 弹出按钮\nmk: Прелетувања\nca: Flyouts\nsi: පියාසර කිරීම්\nro: Flyouts\nen: Flyouts\nps: پروازونه\nhy: Թռիչքներ\nkm: ការហោះហើរ\nbn: ফ্লাইআউটস\nfa: پروازها\ntg: Парвозҳо\nbs: Flyouts\naz: Uçuşlar\nar: القوائم المنبثقة\npl: Wydruki\nid: Terbang\naf: Uitvlugte\ntl: Mga flyout\nne: फ्लाईआउट्स\npa: ਫਲਾਈਆਉਟਸ\nlb: Flyouts\nis: Flyouts\nhi: फ्लाईआउट्स\nru: Всплывающие окна\nde: Flyouts\ncs: Flyouts\nsw: Flyouts\nel: Flyouts\nnl: Uitvluchten\nth: การบินออก\nyo: Flyouts\nlv: Flyouts\nit: Flyout\nda: Flyouts\nzh-TW: 彈出按鈕\neu: Flyouts\nso: Duulimaadyada\nsv: Flyouts\ntr: Açılır menüler\nta: ஃப்ளைஅவுட்கள்\nms: Flyouts\nku: Flyouts\npt-BR: Flyouts\nfr: Icônes volantes\nhu: Flyouts\nmt: Flyouts\net: Flyouts\nhe: תעופה\nsr: Флиоутс\nam: በረራዎች\nfi: Flyouts\nka: ფრენები\nlo: Flyouts\nlt: Skrydžiai\nur: فلائی آؤٹ\nhr: Flyouts\ngu: ફ્લાયઆઉટ્સ\npt-PT: Submenus\n"
  },
  {
    "path": "src/static/widgets/flyouts/metadata.yml",
    "content": "id: \"@seelen/flyouts\"\nicon: LuRectangleEllipsis\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nloader: Internal\npreset: Overlay\nsettings:\n  - type: select\n    key: placement\n    label: Placement\n    options:\n      - label: Top Left\n        value: top-left\n        icon: TbBoxAlignTopLeft\n\n      - label: Top\n        value: top\n        icon: TbBoxAlignTop\n\n      - label: Top Right\n        value: top-right\n        icon: TbBoxAlignTopRight\n\n      - label: Left\n        value: left\n        icon: TbBoxAlignLeft\n\n      - label: Right\n        value: right\n        icon: TbBoxAlignRight\n\n      - label: Bottom Left\n        value: bottom-left\n        icon: TbBoxAlignBottomLeft\n\n      - label: Bottom\n        value: bottom\n        icon: TbBoxAlignBottom\n\n      - label: Bottom Right\n        value: bottom-right\n        icon: TbBoxAlignBottomRight\n    defaultValue: top\n\n  - type: number\n    key: margin\n    label: Side Margin\n    min: 0\n    defaultValue: 30\n\n  - type: number\n    key: timeToShow\n    label: Time to show (seconds)\n    min: 1\n    max: 10\n    defaultValue: 4\n\n  - group:\n      label: \"Show on:\"\n      items:\n        - type: switch\n          key: showBrightnessChange\n          label: Brightness change\n          defaultValue: true\n\n        - type: switch\n          key: showVolumeChange\n          label: Volume change\n          defaultValue: true\n\n        - type: switch\n          key: showMediaPlayerChange\n          label: Media player change\n          defaultValue: true\n\n        - type: switch\n          key: showWorkspaceChange\n          label: Workspace change\n          defaultValue: true\n\n        - type: switch\n          key: showNotifications\n          label: Arrival notification\n          defaultValue: true\n"
  },
  {
    "path": "src/static/widgets/keyboard-selector/i18n/description.yml",
    "content": "ne: किबोर्ड लेआउट चयन गर्न मेनु देखाउँछ।\nca: Mostra un menú per seleccionar la distribució del teclat.\nfa: منویی را برای انتخاب چیدمان صفحه کلید نشان می دهد.\ncs: Zobrazí nabídku pro výběr rozložení klávesnice.\nka: აჩვენებს მენიუს კლავიატურის განლაგების ასარჩევად.\nps: د کیبورډ ترتیب غوره کولو لپاره مینو ښیې.\nmk: Прикажува мени за избор на распоред на тастатурата.\nar: يعرض قائمة لتحديد تخطيط لوحة المفاتيح.\nbs: Prikazuje meni za odabir rasporeda tastature.\nms: Menunjukkan menu untuk memilih susun atur papan kekunci.\ntg: Менюро барои интихоби тарҳи клавиатура нишон медиҳад.\ntr: Klavye düzenini seçmek için bir menü gösterir.\nkm: បង្ហាញម៉ឺនុយសម្រាប់ជ្រើសរើសប្លង់ក្តារចុច។\nde: Zeigt ein Menü zur Auswahl des Tastaturlayouts an.\npa: ਕੀਬੋਰਡ ਲੇਆਉਟ ਚੁਣਨ ਲਈ ਮੇਨੂ ਦਿਖਾਉਂਦਾ ਹੈ।\neu: Teklatu diseinua hautatzeko menu bat erakusten du.\net: Kuvab menüü klaviatuuri paigutuse valimiseks.\nmn: Гарын схемийг сонгох цэсийг харуулж байна.\nit: Mostra un menu per selezionare il layout della tastiera.\nis: Sýnir valmynd til að velja lyklaborðsuppsetningu.\nhe: מציג תפריט לבחירת פריסת מקלדת.\npl: Wyświetla menu do wyboru układu klawiatury.\nsi: යතුරු පුවරු සැකැස්ම තෝරා ගැනීමට මෙනුවක් පෙන්වයි.\nte: కీబోర్డ్ లేఅవుట్‌ను ఎంచుకోవడానికి మెనుని చూపుతుంది.\nur: کیبورڈ لے آؤٹ منتخب کرنے کے لیے مینو دکھاتا ہے۔\nel: Εμφανίζει ένα μενού για επιλογή διάταξης πληκτρολογίου.\nfi: Näyttää valikon näppäimistön asettelun valitsemiseksi.\nsk: Zobrazuje ponuku na výber rozloženia klávesnice.\nhu: Megjelenít egy menüt a billentyűzetkiosztás kiválasztásához.\nlb: Weist e Menü fir d'Tastaturlayout ze wielen.\nzh-CN: 显示用于选择键盘布局的菜单。\nam: የቁልፍ ሰሌዳ አቀማመጥ ለመምረጥ ምናሌ ያሳያል።\npt-PT: Mostra um menu para selecionar o layout do teclado.\nlt: Rodo meniu klaviatūros išdėstymo pasirinkimui.\nes: Muestra un menú para seleccionar la distribución del teclado.\nyo: Fifihan akojọ aṣayan lati yan eto kiiboodu.\nbg: Показва меню за избор на клавиатурна подредба.\nro: Afișează un meniu pentru selectarea configurației tastaturii.\ngu: કીબોર્ડ લેઆઉટ પસંદ કરવા માટે મેનુ બતાવે છે.\nja: キーボード レイアウトを選択するメニューを表示します。\ncy: Yn dangos dewislen i ddewis cynllun bysellfwrdd.\nhr: Prikazuje izbornik za odabir rasporeda tipkovnice.\nsv: Visar en meny för att välja tangentbordslayout.\nsr: Приказује мени за избор распореда тастатуре.\nda: Viser en menu til valg af tastaturlayout.\ntl: Nagpapakita ng isang menu upang pumili ng layout ng keyboard.\nta: விசைப்பலகை அமைப்பைத் தேர்ந்தெடுக்க மெனுவைக் காட்டுகிறது.\nid: Menampilkan menu untuk memilih tata letak keyboard.\nsw: Inaonyesha menyu ya kuchagua mpangilio wa kibodi.\nhi: कीबोर्ड लेआउट चुनने के लिए एक मेनू दिखाता है।\npt-BR: Shows a menu to select keyboard layout.\nvi: Hiển thị menu để chọn bố cục bàn phím.\nru: Показывает меню для выбора раскладки клавиатуры.\nmt: Juri menu biex tagħżel it-tqassim tat-tastiera.\naz: Klaviatura düzənini seçmək üçün menyu göstərir.\nso: Wuxuu muujiyaa liistada si aad u doorato qaab-dhismeedka keyboard.\nku: Pêşekek nîşan dide ji bo hilbijartina sêwirana klavyeyê.\nhy: Ցուցադրում է մենյու՝ ստեղնաշարի դասավորությունը ընտրելու համար։\nzh-TW: 顯示用於選擇鍵盤佈局的菜單。\nnl: Toont een menu om toetsenbordindeling te selecteren.\nlo: ສະແດງເມນູເພື່ອເລືອກຮູບແບບແປ້ນພິມ.\nno: Viser en meny for å velge tastaturoppsett.\nlv: Parāda izvēlni klaviatūras izkārtojuma izvēlei.\nko: 키보드 레이아웃을 선택하는 메뉴를 표시합니다.\nth: แสดงเมนูเพื่อเลือกเค้าโครงแป้นพิมพ์\naf: Wys 'n spyskaart om sleutelborduitleg te kies.\nuk: Показує меню для вибору розкладки клавіатури.\nen: Shows a menu to select keyboard layout.\nzu: Ibonisa imenyu yokukheta uhlelokwakhiwe lwekhibhodi.\nfr: Affiche un menu pour sélectionner la disposition du clavier.\nuz: Klaviatura tartibini tanlash uchun menyuni ko'rsatadi.\nbn: কীবোর্ড লেআউট নির্বাচন করার জন্য একটি মেনু দেখায়।\n"
  },
  {
    "path": "src/static/widgets/keyboard-selector/i18n/display_name.yml",
    "content": "cy: Dewisydd bysellfwrdd\nnl: Toetsenbordkiezer\nur: کیبورڈ منتخب کنندہ\nfi: Näppäimistön valitsin\nel: Επιλογέας πληκτρολογίου\nvi: Bộ chọn bàn phím\nth: ตัวเลือกคีย์บอร์ด\nlb: Tastatur Wieler\nmn: Гарын сонголт\nko: 키보드 선택기\nzh-TW: 鍵盤選擇器\nne: किबोर्ड चयनकर्ता\ngu: કીબોર્ડ પસંદગીકાર\nhy: Ստեղնաշարի ընտրիչ\naf: Sleutelbord kieser\npl: Selektor klawiatury\nca: Selector de teclat\nsr: Бирач тастатуре\nsi: යතුරු පුවරු තේරීම\nbn: কীবোর্ড নির্বাচক\npt-PT: Seletor de teclado\nam: የቁልፍ ሰሌዳ መራጭ\nta: விசைப்பலகை தேர்வி\nmk: Избирач на тастатура\ntg: Интихобкунандаи клавиатура\nde: Tastaturauswahl\npt-BR: Keyboard selector\nhi: कीबोर्ड चयनकर्ता\nfa: انتخابگر صفحه کلید\nkm: កម្មវិធីជ្រើសរើសក្តារចុច\nzh-CN: 键盘选择器\nuk: Перемикач клавіатури\npa: ਕੀਬੋਰਡ ਚੋਣਕਾਰ\nsw: Kichaguzi cha kibodi\nms: Pemilih papan kekunci\nlo: ຕົວເລືອກແປ້ນພິມ\nbg: Избор на клавиатура\nhr: Birač tipkovnice\nlt: Klaviatūros parinkiklis\nja: キーボード セレクター\nis: Lyklaborðsval\ncs: Výběr klávesnice\nda: Tastaturvælger\nro: Selector tastatură\nit: Selettore di tastiera\ntl: Keyboard selector\nen: Keyboard Selector\nku: Hilbijêrê Keyboard\nru: Переключатель раскладки\nte: కీబోర్డ్ సెలెక్టర్\nmt: Għażla tat-tastiera\neu: Teklatu hautatzailea\nbs: Birač tastature\nfr: Sélecteur de clavier\nar: محدد لوحة المفاتيح\nka: კლავიატურის ამომრჩევი\nid: Pemilih keyboard\ntr: Klavye seçici\nlv: Klaviatūras atlasītājs\nhe: בורר מקלדת\nes: Selector de teclado\nno: Tastaturvelger\nuz: Klaviatura tanlagichi\nsk: Výber klávesnice\nhu: Billentyűzet választó\nzu: Ukukhetha ikhibhodi\naz: Klaviatura seçici\nsv: Tangentbordsväljare\nps: کیبورډ انتخابونکی\nso: Xulashada Keyboard\nyo: Asayan kiiboodu\net: Klaviatuuri valija\n"
  },
  {
    "path": "src/static/widgets/keyboard-selector/metadata.yml",
    "content": "id: \"@seelen/keyboard-selector\"\nicon: FaRegKeyboard\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/keyboard-selector/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-keyboard-selector\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: FaRegKeyboard\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Keyboard\n  tooltip: return [activeLang.name, \" - \", activeKeyboard.displayName]\n  template: |-\n    const inputsLenght = activeLang.keyboardLayouts.length;\n    return [\n      activeLangPrefix,\n      inputsLenght > 1 ? \" - \" : \"\",\n      inputsLenght > 1 ? activeKeyboardPrefix : \"\"\n    ]\n  style:\n    flexShrink: 0\n    width: min-content\n    fontWeight: 600\n  onClickV2: |-\n    trigger(\"@seelen/keyboard-selector\");\n"
  },
  {
    "path": "src/static/widgets/media-popup/i18n/description.yml",
    "content": "fi: Mediaohjaimet ja äänenvoimakkuuden mikserin ponnahdusikkuna\nar: عناصر التحكم في الوسائط والنوافذ المنبثقة لخلاط الصوت\npa: ਮੀਡੀਆ ਨਿਯੰਤਰਣ ਅਤੇ ਵਾਲੀਅਮ ਮਿਕਸਰ ਪੌਪਅੱਪ\nda: Mediekontroller og volumemixer popup\naf: Mediakontroles en volumemenger-opspringer\nel: Αναδυόμενα στοιχεία ελέγχου πολυμέσων και μίκτη έντασης ήχου\nca: Controls multimèdia i menú emergent del mesclador de volum\nzh-CN: 媒体控件和音量混合器弹出窗口\naz: Media nəzarətləri və səs mikseri popup\nit: Controlli multimediali e popup del mixer del volume\nro: Comenzi media și pop-up pentru mixer de volum\nso: Xakamaynta warbaahinta iyo soo kicinta mugga\nlt: Medijos valdikliai ir garsumo maišytuvo iššokantis langas\npt-PT: Controles de mídia e pop-up do mixer de volume\nfr: Commandes multimédia et fenêtre contextuelle du mixeur de volume\nmk: Медиумски контроли и скокачки прозорец на миксер за јачина на звук\nsw: Vidhibiti vya midia na kiibukizi cha kichanganya sauti\nen: Media controls and volume mixer popup\nhr: Kontrole medija i skočni prozor miksera glasnoće\nuz: Media boshqaruvlari va ovoz balandligi mikserining qalqib chiquvchi oynasi\ngu: મીડિયા નિયંત્રણો અને વોલ્યુમ મિક્સર પોપઅપ\nhy: Մեդիա հսկիչներ և ձայնի խառնիչի ելնող պատուհան\ntr: Medya kontrolleri ve ses karıştırıcı açılır penceresi\nyo: Awọn iṣakoso media ati agbejade aladapo iwọn didun\nmt: Kontrolli tal-midja u popup tal-mixer tal-volum\nbg: Медийни контроли и изскачащ прозорец на миксера за сила на звука\nsr: Контроле медија и искачући прозор миксера за јачину звука\nzu: Izilawuli zemidiya kanye ne-popup ye-volume mixer\nis: Miðlunarstýringar og sprettigluggi fyrir hljóðstyrksblöndunartæki\nps: د میډیا کنټرول او حجم مکسر پاپ اپ\nko: 미디어 컨트롤 및 볼륨 믹서 팝업\nam: የሚዲያ መቆጣጠሪያዎች እና የድምጽ ማደባለቅ ብቅ ባይ\nur: میڈیا کنٹرولز اور حجم مکسر پاپ اپ\nid: Kontrol media dan popup pengaduk volume\nkm: ការគ្រប់គ្រងមេឌៀ និងឧបករណ៍លាយកម្រិតសំឡេងលេចឡើង\nmn: Медиа удирдлага болон дууны хэмжээг холигч цонх\nja: メディア コントロールとボリューム ミキサー ポップアップ\net: Meediumi juhtnupud ja helitugevuse mikseri hüpikaken\nne: मिडिया नियन्त्रण र भोल्युम मिक्सर पपअप\nzh-TW: 媒體控件和音量混合器彈出窗口\ntg: Назорати медиа ва поп-папкаи миксер овоз\nhe: בקרות מדיה וחלון קופץ של מערבל עוצמת הקול\nbs: Kontrole medija i iskačući prozor za mikser zvuka\nlb: Media Kontrollen a Volumenmixer Popup\nlo: ການຄວບຄຸມສື່ ແລະປັອບອັບເຄື່ອງປະສົມລະດັບສຽງ\nfa: بازشو کنترل رسانه و میکسر صدا\nms: Kawalan media dan pop timbul pengadun volum\nno: Mediekontroller og popup for volummikser\npt-BR: Media controls and volume mixer popup\nsk: Ovládacie prvky médií a kontextové okno mixéra hlasitosti\nsv: Mediakontroller och popup för volymmixer\nte: మీడియా నియంత్రణలు మరియు వాల్యూమ్ మిక్సర్ పాప్అప్\nnl: Mediabediening en volumemixer-pop-up\nbn: মিডিয়া কন্ট্রোল এবং ভলিউম মিক্সার পপআপ\nru: Элементы управления мультимедиа и всплывающее окно микшера громкости\nuk: Елементи керування мультимедіа та спливаюче вікно мікшера гучності\nes: Controles multimedia y ventana emergente del mezclador de volumen\nsi: මාධ්‍ය පාලන සහ වෙළුම් මිශ්‍ර උත්පතනය\ncy: Rheolaethau cyfryngau a naid cymysgydd cyfaint\nhu: Médiavezérlők és hangerőkeverő előugró ablak\nka: მედია კონტროლი და ხმის მიქსერის ამომხტარი ფანჯარა\nku: Kontrolên medyayê û pêlava mîkserê dengdanê\nlv: Multivides vadīklas un skaļuma miksera uznirstošais logs\nth: ส่วนควบคุมสื่อและป๊อปอัปตัวปรับแต่งระดับเสียง\ntl: Mga kontrol ng media at popup ng volume mixer\nhi: मीडिया नियंत्रण और वॉल्यूम मिक्सर पॉपअप\ncs: Ovládací prvky médií a vyskakovací okno směšovače hlasitosti\neu: Multimedia kontrolak eta bolumen-nahastagailuaren leihoa\nde: Popup-Fenster für Mediensteuerung und Lautstärkemixer\nta: மீடியா கட்டுப்பாடுகள் மற்றும் வால்யூம் மிக்சர் பாப்அப்\npl: Wyskakujące okno sterowania multimediami i miksera głośności\nvi: Điều khiển phương tiện và cửa sổ bật lên bộ trộn âm lượng\n"
  },
  {
    "path": "src/static/widgets/media-popup/i18n/display_name.yml",
    "content": "km: ប្រព័ន្ធផ្សព្វផ្សាយលេចឡើង\nso: Baahinta Warbaahinta\nuz: Media qalqib chiquvchi oyna\nar: الوسائط المنبثقة\nid: Munculan Media\nja: メディアポップアップ\nro: Popup media\naf: Media-opspringer\ntl: Media Popup\nhr: Medijski skočni prozor\nmt: Popup tal-Midja\nhi: मीडिया पॉपअप\nyo: Agbejade Media\nzh-TW: 媒體彈出窗口\nsv: Media popup\nhu: Média előugró ablak\nms: Pop Timbul Media\ncs: Vyskakovací okno médií\npt-PT: Pop-up de mídia\nsi: මාධ්‍ය උත්පතනය\nfr: Pop-up multimédia\ncy: Naid Cyfryngau\ntr: Medya Açılır Penceresi\nhe: פופאפ מדיה\nlb: Media Popup\nru: Медиа-всплывающее окно\nfa: بازشو رسانه\nen: Media Popup\npa: ਮੀਡੀਆ ਪੌਪਅੱਪ\nps: د رسنیو پاپ اپ\nfi: Median ponnahdusikkuna\nlv: Multivides uznirstošais logs\nbn: মিডিয়া পপআপ\nzu: I-Popup yemidiya\nne: मिडिया पपअप\nka: მედია ამომხტარი\nno: Media Popup\nnl: Mediapop-up\ntg: Поп-апаи медиа\net: Meedia hüpikaken\nbg: Мултимедиен изскачащ прозорец\nmk: Медиумски скокачки прозорец\nuk: Медіа спливаюче вікно\nlt: Iššokantis medijos langas\nzh-CN: 媒体弹出窗口\nda: Medie popup\naz: Media Popup\nhy: Մեդիա թռուցիկ\nku: Media Popup\nsw: Ibukizi ya Media\nur: میڈیا پاپ اپ\nvi: Cửa sổ bật lên phương tiện\nit: Popup multimediale\nte: మీడియా పాప్అప్\nbs: Media Popup\nsk: Vyskakovacie okno médií\nde: Medien-Popup\nes: Ventana emergente multimedia\neu: Media Popup\nis: Fjölmiðlasprettigluggi\nmn: Хэвлэл мэдээллийн цонх\nth: สื่อป๊อปอัป\nlo: ສື່ປັອບອັບ\nsr: Медијски искачући прозор\npl: Wyskakujące okienko multimediów\nta: மீடியா பாப்அப்\ngu: મીડિયા પોપઅપ\nca: Popup de mitjans\nel: Αναδυόμενο παράθυρο πολυμέσων\nam: የሚዲያ ብቅ-ባይ\npt-BR: Media Popup\nko: 미디어 팝업\n"
  },
  {
    "path": "src/static/widgets/media-popup/metadata.yml",
    "content": "id: \"@seelen/media-popup\"\nicon: IoVolumeHighOutline\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/media-popup/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-media-popup\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: IoVolumeHighOutline\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Media\n  template: |-\n    return isMuted\n      ? icon(\"IoVolumeMuteOutline\")\n      : volume >= 0.66\n        ? icon(\"IoVolumeHighOutline\")\n        : volume >= 0.33\n          ? icon(\"IoVolumeMediumOutline\")\n          : volume != 0\n            ? icon(\"IoVolumeLowOutline\")\n            : icon(\"IoVolumeOffOutline\")\n  tooltip: 'return [t(\"placeholder.volume\"), \": \", (volume * 100).toFixed(0), \"%\"]'\n  onClickV2: |-\n    trigger(\"@seelen/media-popup\");\n"
  },
  {
    "path": "src/static/widgets/network-popup/i18n/description.yml",
    "content": "lb: Netzwierkverbindungsmanager a Wi-Fi Wieler\nbg: Мениджър на мрежови връзки и Wi-Fi селектор\nta: பிணைய இணைப்பு மேலாளர் மற்றும் Wi-Fi தேர்வி\nvi: Trình quản lý kết nối mạng và bộ chọn Wi-Fi\nis: Nettengingarstjóri og Wi-Fi val\nkm: កម្មវិធីគ្រប់គ្រងការតភ្ជាប់បណ្តាញ និងឧបករណ៍ជ្រើសរើស Wi-Fi\npl: Menedżer połączeń sieciowych i selektor Wi-Fi\nsw: Meneja wa uunganisho wa mtandao na kichaguzi cha Wi-Fi\nde: Netzwerkverbindungsmanager und WLAN-Auswahl\nso: Maareeyaha isku xirka shabakada iyo xulashada Wi-Fi\nit: Gestione connessione di rete e selettore Wi-Fi\net: Võrguühenduse haldur ja Wi-Fi valija\nth: ตัวจัดการการเชื่อมต่อเครือข่ายและตัวเลือก Wi-Fi\nfr: Gestionnaire de connexion réseau et sélecteur Wi-Fi\nhe: מנהל חיבורי רשת ובורר Wi-Fi\ntl: Network connection manager at Wi-Fi selector\nfa: مدیر اتصال شبکه و انتخابگر Wi-Fi\ncy: Rheolwr cysylltiad rhwydwaith a dewisydd Wi-Fi\npt-BR: Network connection manager and Wi-Fi selector\nko: 네트워크 연결 관리자 및 Wi-Fi 선택기\naf: Netwerkverbindingbestuurder en Wi-Fi-kieser\nes: Administrador de conexión de red y selector de Wi-Fi\nru: Менеджер сетевых подключений и переключатель Wi-Fi\nbs: Menadžer mrežnih veza i Wi-Fi birač\nnl: Netwerkverbindingsmanager en Wi-Fi-selector\nku: Rêvebirê pêwendiya torê û hilbijêra Wi-Fi\nno: Nettverkstilkoblingsbehandling og Wi-Fi-velger\nzh-TW: 網絡連接管理器和 Wi-Fi 選擇器\ncs: Správce síťových připojení a volič Wi-Fi\nhr: Upravitelj mrežne veze i Wi-Fi birač\nca: Gestor de connexió de xarxa i selector de Wi-Fi\nsv: Nätverksanslutningshanterare och Wi-Fi-väljare\nhi: नेटवर्क कनेक्शन प्रबंधक और वाई-फाई चयनकर्ता\nlt: Tinklo ryšio tvarkyklė ir „Wi-Fi“ parinkiklis\nuk: Менеджер підключення до мережі та селектор Wi-Fi\nar: مدير اتصال الشبكة ومحدد Wi-Fi\nsi: ජාල සම්බන්ධතා කළමනාකරු සහ Wi-Fi තේරීම\nja: ネットワーク接続マネージャーと Wi-Fi セレクター\nmn: Сүлжээний холболтын менежер ба Wi-Fi сонгогч\nmt: Maniġer tal-konnessjoni tan-netwerk u selettur tal-Wi-Fi\ntr: Ağ bağlantı yöneticisi ve Wi-Fi seçici\nms: Pengurus sambungan rangkaian dan pemilih Wi-Fi\ngu: નેટવર્ક કનેક્શન મેનેજર અને Wi-Fi પસંદગીકાર\nid: Manajer koneksi jaringan dan pemilih Wi-Fi\ntg: Менеҷери пайвасти шабака ва интихоби Wi-Fi\npt-PT: Gerenciador de conexão de rede e seletor de Wi-Fi\npa: ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਮੈਨੇਜਰ ਅਤੇ Wi-Fi ਚੋਣਕਾਰ\nbn: নেটওয়ার্ক সংযোগ ব্যবস্থাপক এবং Wi-Fi নির্বাচক\nsk: Správca sieťového pripojenia a volič Wi-Fi\nsr: Менаџер мрежних веза и Ви-Фи бирач\nel: Διαχείριση σύνδεσης δικτύου και επιλογέας Wi-Fi\nfi: Verkkoyhteyden hallinta ja Wi-Fi-valitsin\neu: Sare-konexioen kudeatzailea eta Wi-Fi hautatzailea\nda: Netværksforbindelsesadministrator og Wi-Fi-vælger\nps: د شبکې اتصال مدیر او وائی فای انتخاب کونکی\nte: నెట్‌వర్క్ కనెక్షన్ మేనేజర్ మరియు Wi-Fi సెలెక్టర్\nur: نیٹ ورک کنکشن منیجر اور وائی فائی سلیکٹر\nhy: Ցանցային կապի կառավարիչ և Wi-Fi ընտրիչ\nuz: Tarmoq ulanishi menejeri va Wi-Fi selektori\nyo: Oluṣakoso asopọ nẹtiwọki ati oluyan Wi-Fi\nzh-CN: 网络连接管理器和 Wi-Fi 选择器\naz: Şəbəkə bağlantısı meneceri və Wi-Fi seçicisi\nlo: ຕົວຈັດການການເຊື່ອມຕໍ່ເຄືອຂ່າຍ ແລະຕົວເລືອກ Wi-Fi\nzu: Umphathi woxhumano lwenethiwekhi nesikhethi se-Wi-Fi\nro: Manager de conexiune la rețea și selector Wi-Fi\nhu: Hálózati kapcsolatkezelő és Wi-Fi választó\nam: የአውታረ መረብ ግንኙነት አስተዳዳሪ እና Wi-Fi መራጭ\nmk: Управувач со мрежна конекција и избирач на Wi-Fi\nen: Network connection manager and Wi-Fi selector\nka: ქსელის კავშირის მენეჯერი და Wi-Fi ამომრჩეველი\nne: नेटवर्क जडान प्रबन्धक र Wi-Fi चयनकर्ता\nlv: Tīkla savienojuma pārvaldnieks un Wi-Fi atlasītājs\n"
  },
  {
    "path": "src/static/widgets/network-popup/i18n/display_name.yml",
    "content": "bn: নেটওয়ার্ক\neu: Sarea\nzh-CN: 网络\ntg: Шабака\nuz: Tarmoq\nsk: sieť\nfi: Verkko\nlt: Tinklas\nsr: Мрежа\nte: నెట్‌వర్క్\net: Võrk\nku: Network\nsv: Nätverk\nkm: បណ្តាញ\npl: Sieć\nyo: Nẹtiwọọki\nlo: ເຄືອຂ່າຍ\nta: நெட்வொர்க்\nuk: Мережа\nel: Δίκτυο\nsi: ජාලය\nko: 회로망\ncy: Rhwydwaith\nps: شبکه\nur: نیٹ ورک\nhr: Mreža\nhy: Ցանց\nnl: Netwerk\nen: Network\npt-BR: Network\nhe: רֶשֶׁת\nru: Сеть\nzu: Inethiwekhi\naf: Netwerk\nfr: Réseau\naz: Şəbəkə\nbs: Mreža\nde: Netzwerk\nfa: شبکه\nno: Nettverk\nam: አውታረ መረብ\ntr: Ağ\nhu: Hálózat\nis: Net\ngu: નેટવર્ક\ntl: Network\nvi: Mạng\nso: Shabakadda\nar: شبكة\npt-PT: Rede\nhi: नेटवर्क\nca: Xarxa\nes: Red\nka: ქსელი\nms: Rangkaian\nja: ネットワーク\ncs: Síť\nit: Rete\nzh-TW: 網絡\nid: Jaringan\nne: नेटवर्क\nlb: Reseau\nlv: Tīkls\nmn: Сүлжээ\nro: Reţea\nth: เครือข่าย\nda: Netværk\nsw: Mtandao\nbg: мрежа\npa: ਨੈੱਟਵਰਕ\nmt: Netwerk\nmk: Мрежа\n"
  },
  {
    "path": "src/static/widgets/network-popup/metadata.yml",
    "content": "id: \"@seelen/network-popup\"\nicon: FaWifi\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/network-popup/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-network-popup\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: FaWifi\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Network\n  tooltip: >-\n    return online ? t(\"placeholder.ethernet_connected\") : t(\"placeholder.ethernet_disconnected\")\n  template: >-\n    if (!online) return icon(\"TbWorldCancel\");\n    if (usingInterface?.type === \"IEEE80211\") return icon(\"FaWifi\");\n    return icon(\"FaComputer\");\n  onClickV2: |-\n    trigger(\"@seelen/network-popup\");\n"
  },
  {
    "path": "src/static/widgets/notifications/i18n/description.yml",
    "content": "vi: Danh sách thông báo\nps: د خبرتیا لیست\nuz: Bildirishnomalar roʻyxati\nhe: רשימת הודעות\nro: Lista de notificări\nsv: Aviseringslista\nid: Daftar pemberitahuan\nsr: Листа обавештења\nbs: Lista obavještenja\npt-PT: Lista de notificações\nlt: Pranešimų sąrašas\npl: Lista powiadomień\nko: 알림 목록\ntr: Bildirim listesi\naf: Kennisgewingslys\ngu: સૂચનાઓની સૂચિ\nhu: Értesítések listája\nbg: Списък с известия\nmt: Lista ta' notifiki\nsi: දැනුම්දීම් ලැයිස්තුව\nel: Λίστα ειδοποιήσεων\nbn: বিজ্ঞপ্তি তালিকা\nam: የማሳወቂያዎች ዝርዝር\nlo: ລາຍຊື່ການແຈ້ງເຕືອນ\nms: Senarai pemberitahuan\nuk: Список сповіщень\nur: اطلاعات کی فہرست\nfr: Liste des notifications\nes: Lista de notificaciones\nit: Elenco notifiche\ntl: Listahan ng mga notification\ntg: Рӯйхати огоҳиҳо\nja: お知らせ一覧\nfi: Ilmoitusluettelo\ncs: Seznam oznámení\nhy: Ծանուցումների ցուցակ\nnl: Lijst met meldingen\nkm: បញ្ជីការជូនដំណឹង\nte: నోటిఫికేషన్‌ల జాబితా\nzu: Uhlu lwezaziso\nar: قائمة الإخطارات\nen: Notifications list\nde: Benachrichtigungsliste\nzh-TW: 通知列表\nhi: अधिसूचना सूची\npt-BR: Notifications list\nhr: Popis obavijesti\nmn: Мэдэгдлийн жагсаалт\nso: Liiska ogeysiisyada\nth: รายการแจ้งเตือน\net: Märguannete loend\neu: Jakinarazpenen zerrenda\nmk: Список со известувања\nsk: Zoznam upozornení\nta: அறிவிப்புகள் பட்டியல்\ncy: Rhestr hysbysiadau\nyo: Akojọ awọn iwifunni\nzh-CN: 通知列表\naz: Bildirişlər siyahısı\npa: ਸੂਚਨਾਵਾਂ ਦੀ ਸੂਚੀ\nru: Список уведомлений\nsw: Orodha ya arifa\nfa: لیست اعلان ها\nlv: Paziņojumu saraksts\nis: Tilkynningarlisti\nlb: Notifikatiounslëscht\nka: შეტყობინებების სია\nku: Lîsteya ragihandinê\nne: सूचनाहरूको सूची\nca: Llista de notificacions\nda: Notifikationsliste\nno: Varslingsliste\n"
  },
  {
    "path": "src/static/widgets/notifications/i18n/display_name.yml",
    "content": "ku: Notifications\naz: Bildirişlər\nhi: सूचनाएं\nhe: התראות\npa: ਸੂਚਨਾਵਾਂ\nlv: Paziņojumi\nbn: বিজ্ঞপ্তি\nca: Notificacions\nhr: Obavijesti\nuk: Сповіщення\nzu: Izaziso\ntg: Огоҳиномаҳо\nur: اطلاعات\nkm: ការជូនដំណឹង\nzh-TW: 通知\nlb: Notifikatiounen\nko: 알림\nro: Notificări\nbg: Известия\naf: Kennisgewings\nsi: දැනුම්දීම්\nar: إشعارات\nsw: Arifa\nta: அறிவிப்புகள்\nms: Pemberitahuan\ncy: Hysbysiadau\ngu: સૂચનાઓ\nmn: Мэдэгдэл\nis: Tilkynningar\nzh-CN: 通知\nsr: Обавештења\nru: Уведомления\nte: నోటిఫికేషన్‌లు\nka: შეტყობინებები\nja: 通知\nbs: Obavještenja\nhu: Értesítések\nvi: Thông báo\nuz: Bildirishnomalar\nth: การแจ้งเตือน\nam: ማሳወቂያዎች\nyo: Awọn iwifunni\nes: Notificaciones\nnl: Meldingen\npl: Powiadomienia\net: Märguanded\nid: Pemberitahuan\nsv: Aviseringar\ntl: Mga abiso\npt-PT: Notificações\ntr: Bildirimler\nfa: اطلاعیه ها\nno: Varsler\nel: Ειδοποιήσεις\nsk: Upozornenia\neu: Jakinarazpenak\npt-BR: Notifications\nhy: Ծանուցումներ\nmt: Notifiki\nit: Notifiche\ncs: Oznámení\nne: सूचनाहरू\nps: خبرتیاوې\nlo: ການແຈ້ງເຕືອນ\nso: Ogeysiisyada\nfi: Ilmoitukset\nmk: Известувања\nde: Benachrichtigungen\nda: Meddelelser\nen: Notifications\nfr: Notifications\nlt: Pranešimai\n"
  },
  {
    "path": "src/static/widgets/notifications/metadata.yml",
    "content": "id: \"@seelen/notifications\"\nicon: IoNotificationsOutline\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/notifications/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-notifications\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: IoNotificationsOutline\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Notifications\n  template: >-\n    return count > 0 ? icon(\"MdNotificationsActive\") :\n    icon(\"MdOutlineNotifications\")\n  badge: \"return count > 0 ? count : null\"\n  tooltip: 'return [t(\"placeholder.notifications\"), \": \", count]'\n  onClickV2: |-\n    trigger(\"@seelen/notifications\");\n"
  },
  {
    "path": "src/static/widgets/popup/i18n/description.yml",
    "content": "es: Este widget permite mostrar ventanas emergentes personalizadas a los usuarios.\nko: 이 위젯을 사용하면 사용자에게 맞춤형 팝업을 표시 할 수 있습니다.\nsi: මෙම විජට් ලබා ගැනීමට ඉඩ සලසයි.\nfi: Tämä widget mahdollistaa räätälöityjen ponnahdusikkunoiden käyttäjille.\nhi: यह विजेट उपयोगकर्ताओं को अनुकूलित पॉपअप दिखाने की अनुमति देता है।\nlt: Šis valdiklis leidžia vartotojams parodyti tinkintus iššokančias iššokančias iššokančias programas.\nyo: Ẹrọ ailorukọ yii ngbanilaaye awọn ikede aṣa si awọn olumulo.\nde: Dieses Widget ermöglicht den Benutzern maßgeschneiderte Popups.\nmn: Энэ виджет нь хэрэглэгчдэд тохируулсан попапыг харуулах боломжийг олгодог.\nso: Widget-kani wuxuu u oggolaanayaa in uu muujiyo dadka u habeeya ee loogu talagalay dadka isticmaala.\nam: ይህ ንዑስ ፕሮግራም የተያዙ ብቅ ያሉ ለተጠቃሚዎች ያሳያል.\nsw: Widget hii inaruhusu kuonyesha popuzi zilizobinafsishwa kwa watumiaji.\nca: Aquest widget permet mostrar emergents personalitzats als usuaris.\nuz: Ushbu vidjet foydalanuvchilarga moslashtirilgan qalqib chiquvchi popuplarni ko'rsatishga imkon beradi.\naz: Bu widget istifadəçilərə xüsusi popuplar göstərməyə imkan verir.\nhy: \"Այս վիդջեթը թույլ է տալիս ցուցադրել հարմարեցված թռուցիկներ օգտագործողներին:\"\npt-PT: Este widget permite mostrar pop-ups personalizados aos usuários.\nfr: Ce widget permet d'afficher des fenêtres personnalisées aux utilisateurs.\nro: Acest widget permite afișarea pop -up -urilor personalizate pentru utilizatori.\nku: Ev widget destûrê dide ku popupên xwerû ji bikarhêneran re nîşan bide.\nzh: 该小部件允许向用户显示自定义的弹出窗口。\nlv: Šis logrīks lietotājiem ļauj parādīt pielāgotus uznirstošos uznirstošos logus.\npa: ਇਹ ਵਿਦਜੈੱਟ ਉਪਭੋਗਤਾਵਾਂ ਨੂੰ ਅਨੁਕੂਲਿਤ ਪੌਪਅਪਸ ਦਿਖਾਉਂਦਾ ਹੈ.\neu: Widget honek erabiltzaileei pertsonalizatutako popupak erakusten ditu.\ngu: આ વિજેટ વપરાશકર્તાઓને કસ્ટમાઇઝ્ડ પ pop પઅપ્સ બતાવવાની મંજૂરી આપે છે.\npt-BR: This widget allows show customized popups to users.\nsv: Denna widget tillåter show anpassade popups till användare.\nhu: Ez a widget lehetővé teszi a felhasználók testreszabott felbukkanásainak megjelenítését.\nbs: Ovaj widget omogućava prikazivanje prilagođenih populata korisnicima.\nvi: Tiện ích này cho phép hiển thị cửa sổ bật lên tùy chỉnh cho người dùng.\nis: Þessi búnaður gerir kleift að sýna notendur sérsniðna sprettiglugga.\nnl: Met deze widget kunnen u aangepaste pop -ups tonen voor gebruikers.\nur: یہ ویجیٹ صارفین کو اپنی مرضی کے مطابق پاپ اپ دکھانے کی اجازت دیتا ہے۔\nid: Widget ini memungkinkan tampilan popup yang disesuaikan untuk pengguna.\net: See vidin võimaldab näidata kasutajatele kohandatud hüpikakente.\nth: วิดเจ็ตนี้อนุญาตให้แสดงป๊อปอัปที่กำหนดเองให้กับผู้ใช้\ntl: Pinapayagan ng widget na ito na ipakita ang mga pasadyang mga popup sa mga gumagamit.\npt: Este widget permite mostrar pop -ups personalizados para usuários.\nzh-CN: 该小部件允许向用户显示自定义弹出窗口。\nno: Denne widgeten lar vise tilpassede popups til brukere.\nja: このウィジェットにより、ユーザーにカスタマイズされたポップアップを表示できます。\nel: Αυτό το widget επιτρέπει την εμφάνιση προσαρμοσμένων αναδυόμενων παραθύρων στους χρήστες.\ncy: Mae'r teclyn hwn yn caniatáu sioeau wedi'u haddasu i ddefnyddwyr.\nms: Widget ini membolehkan menunjukkan popup tersuai kepada pengguna.\nit: Questo widget consente di mostrare popup personalizzati agli utenti.\nhr: Ovaj widget omogućuje korisnicima s prilagođenim skočnim prozorima.\nmt: Dan il-widget jippermetti li turi popups personalizzati lill-utenti.\nkm: ធាតុក្រាហ្វិកនេះអនុញ្ញាតឱ្យបង្ហាញការលេចឡើងដែលបានប្តូរតាមបំណងទៅអ្នកប្រើប្រាស់។\nzu: Lewijethi ivumela ukuboniswa kombukiso okwenziwe ngokwezifiso kubasebenzisi.\nda: Denne widget tillader at vise tilpassede popups til brugerne.\nfa: این ویجت اجازه می دهد تا پنجره های سفارشی را به کاربران نشان دهد.\nps: دا کاروونکو ته اجازه ورکوي چې کاروونکو ته دودیز پاپ اپ وښیې.\nte: ఈ విడ్జెట్ వినియోగదారులకు అనుకూలీకరించిన పాపప్‌లను చూపించడానికి అనుమతిస్తుంది.\npl: Ten widżet pozwala na dostosowane okienka do użytkowników.\nmk: Овој додаток им овозможува на корисниците да покаже кориснички скокачки прозорци.\nbn: এই উইজেটটি ব্যবহারকারীদের কাস্টমাইজড পপআপগুলি শো করতে দেয়।\nka: ეს ვიჯეტი საშუალებას აძლევს მომხმარებლებს აჩვენოს მორგებული popups.\nar: تتيح هذه القطعة عرض منبثقة مخصصة للمستخدمين.\nsk: Tento widget umožňuje používateľom zobrazovať prispôsobené kontextové okná.\nlb: Dëse Widget erlaabt Show personaliséiert Popups zu Benotzer ze weisen.\ntr: Bu widget, kullanıcılara özelleştirilmiş açılır pencereleri göstermeye izin verir.\nzh-TW: 該小部件允許向用戶顯示自定義彈出窗口。\nsr: Овај видгет омогућава приказивање прилагођених скокова корисницима.\nuk: Цей віджет дозволяє показувати індивідуальні спливаючі вікна для користувачів.\nbg: Тази джаджа позволява показване на персонализирани изскачащи прозорци на потребителите.\nru: Этот виджет позволяет показывать настраиваемые всплывающие окна для пользователей.\nen: This widget allows show customized popups to users.\nta: இந்த விட்ஜெட் பயனர்களுக்கு தனிப்பயனாக்கப்பட்ட பாப்அப்களைக் காட்ட அனுமதிக்கிறது.\ntg: Ин виҷет имкон медиҳад, ки ба корбарон поп-апро нишон диҳад.\nne: यस विजेटले प्रयोगकर्ताहरूलाई अनुकूलन पपअपहरू प्रदर्शन गर्दछ।\nlo: ບັນດາເຄື່ອງມືນີ້ຊ່ວຍໃຫ້ສະແດງການຕັ້ງລູກສອນທີ່ກໍານົດເອງໃຫ້ແກ່ຜູ້ໃຊ້.\ncs: Tento widget umožňuje uživatelům ukázat přizpůsobená vyskakovací okna.\naf: Met hierdie widget kan dit aangepaste pop -ups aan gebruikers wees.\nhe: יישומון זה מאפשר להציג פופפים מותאמים אישית למשתמשים.\n"
  },
  {
    "path": "src/static/widgets/popup/i18n/display_name.yml",
    "content": "lv: Čaumalas uznirstošie logi\nps: د شیل پاپ اپ\nda: Shell popups\nbs: Shell Popups\nbn: শেল পপআপস\nkm: ការលេចឡើងសែល\nuk: Спливаючі вікна оболонки\nzh-TW: 外殼彈出窗口\nfr: Popups système\nuz: Shell qalqib chiqilishi\nso: Shell popups\nde: Shell Popups\nbg: Изскачащи прозорци\nja: シェルポップアップ\nel: Αναδυόμενα παράθυρα\nsk: Vyskakovacie okno\nru: Всплывающие окна оболочки\ncy: Popups Shell\nlb: Shell Popups\nis: Skel sprettiglugga\nsr: Схелл Спапс\nhe: חלונות פגז\nth: ป๊อปอัพเชลล์\nne: शेल पपअपहरू\nzu: Ama-Shell popups\npt-BR: Shell Popups\nro: Popup -uri Shell\nyo: Awọn agbejade ikarahun\nku: Popupên shell\nta: ஷெல் பாப்அப்கள்\nsw: Popups za ganda\npt: Pop -ups de concha\nfa: پاپ\nsv: Skalpopup\net: Kesta hüpik\nfi: Kuori -ponnahdusikkunat\nmt: Popups tal-qoxra\nmk: Пукање на школка\npl: Wyskakujące okienka\nzh: 外壳弹出窗口\nca: Popups de closca\npa: ਸ਼ੈੱਲ ਪੌਪਅਪਸ\nhu: Kagyló felbukkanások\ngu: શેલ પ pop પઅપ્સ\nen: Shell Popups\ncs: Shell vyskakovací okna\nte: షెల్ పాపప్స్\nnl: Shell -pop -ups\neu: Shell popupak\ntr: Kabuk pop -up'ları\nvi: Bopup Shell\nka: ჭურვი popups\nid: Popup shell\nhr: Skočni prozori školjke\nlt: „Shell\" iššokantys langai\nzh-CN: 外壳弹出窗口\nmn: Бүрхүүлийн попапууд\nlo: Popup ຫອຍ\naz: Shell popupları\nms: Popup shell\nhy: Shell թռուցիկներ\nit: Popup di shell\npt-PT: Pop-ups de shell\nhi: शेल पॉपअप\nur: شیل پاپ اپ\nko: 쉘 팝업\ntg: Partups shells\nno: Shell popups\nsi: ෂෙල් උත්පතන\naf: Shell pop -ups\nes: Ventanas emergentes\ntl: Shell Popups\nar: المنبثقة شل\nam: Shell ል ብቅ ይላል\n"
  },
  {
    "path": "src/static/widgets/popup/metadata.yml",
    "content": "id: \"@seelen/popup\"\nicon: FaRegWindowRestore\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Multiple\nloader: Legacy\nhidden: true\n"
  },
  {
    "path": "src/static/widgets/power-menu/i18n/description.yml",
    "content": "lt: Rodomas perdangos meniu, skirtas paleisti iš naujo, išjungti arba atsijungti.\ntr: Yeniden başlatmak, kapatmak veya oturumu kapatmak için bir yer paylaşımı menüsü gösterir.\nhi: पुनरारंभ करने, बंद करने या लॉगआउट करने के लिए एक ओवरले मेनू दिखाता है।\nkm: បង្ហាញម៉ឺនុយត្រួតលើគ្នាដើម្បីចាប់ផ្តើមបិទឬចេញ។\nis: Sýnir yfirlagsvalmynd til að endurræsa, loka eða skrá þig út.\nzu: Ibonisa imenyu yokumbondela yokuqalisa kabusha, ukuvala noma ukuphuma.\nth: แสดงเมนูซ้อนทับเพื่อรีสตาร์ท ปิดเครื่อง หรือออกจากระบบ\nhy: \"Ցուցադրում է ծածկույթի ընտրացանկ՝ վերագործարկելու, անջատելու կամ դուրս գալու համար:\"\nde: Zeigt ein Overlay-Menü zum Neustarten, Herunterfahren oder Abmelden an.\nit: Mostra un menu in sovrapposizione per riavviare, spegnere o disconnettersi.\nar: يعرض قائمة متراكبة لإعادة التشغيل أو إيقاف التشغيل أو تسجيل الخروج.\neu: Berrabiarazteko, itzaltzeko edo saioa amaitzeko gainjarritako menu bat erakusten du.\nid: Menampilkan menu overlay untuk memulai ulang, mematikan, atau keluar.\nel: Εμφανίζει ένα μενού επικάλυψης για επανεκκίνηση, τερματισμό ή αποσύνδεση.\nzh-CN: 显示用于重新启动、关闭或注销的覆盖菜单。\nms: Menunjukkan menu overlay untuk memulakan semula, menutup atau logout.\nhu: Megjelenít egy fedvénymenüt az újraindításhoz, leállításhoz vagy kijelentkezéshez.\nen: Shows an overlay menu to restart, shutdown or logout.\nru: Показывает наложенное меню для перезагрузки, завершения работы или выхода из системы.\nmt: Juri menu overlay biex terġa’ tibda, tintefa’ jew illout.\nno: Viser en overleggsmeny for å starte på nytt, slå av eller logge ut.\naz: Yenidən başlatmaq, bağlamaq və ya çıxışı üçün bir örtük menyusunu göstərir.\npt-PT: Mostra um menu sobreposto para reiniciar, desligar ou sair.\nka: აჩვენებს გადაფარვის მენიუს გადატვირთვის, გამორთვის ან გამოსვლისთვის.\nfi: Näyttää peittovalikon uudelleenkäynnistystä, sammutusta tai uloskirjautumista varten.\nca: Mostra un menú superposat per reiniciar, apagar o tancar la sessió.\nuz: Qayta boshlash, o'chirish yoki chiqish uchun ortiqcha menyuni ko'rsatadi.\nuk: Показує накладне меню для перезавантаження, завершення роботи або виходу з системи.\nhe: מציג תפריט שכבת-על להפעלה מחדש, כיבוי או התנתקות.\nps: د بیا پیلولو، بندولو یا ننوتلو لپاره د پوښښ مینو ښیې.\nmk: Прикажува преклопено мени за рестартирање, исклучување или одјавување.\ntg: Барои бозсозӣ, хомӯш кардан ё баромадан аз менюи навбатии менюро нишон медиҳад.\ncy: Yn dangos dewislen troshaenu i ailgychwyn, cau i lawr neu allgofnodi.\nro: Afișează un meniu suprapus pentru repornire, oprire sau deconectare.\nnl: Toont een overlay-menu om opnieuw op te starten, af te sluiten of uit te loggen.\nso: Waxay muujineysaa menu dep ah oo dib loo bilaabi karo, xiritaanka ama isbadalka.\npa: ਮੁੜ ਚਾਲੂ ਕਰਨ, ਬੰਦ ਕਰਨ ਜਾਂ ਲੌਗਆਉਟ ਕਰਨ ਲਈ ਇੱਕ ਓਵਰਲੇ ਮੀਨੂ ਦਿਖਾਉਂਦਾ ਹੈ।\nhr: Prikazuje preklapajući izbornik za ponovno pokretanje, isključivanje ili odjavu.\npl: Wyświetla menu nakładki umożliwiające ponowne uruchomienie, zamknięcie lub wylogowanie.\nta: மறுதொடக்கம், பணிநிறுத்தம் அல்லது வெளியேறுவதற்கான மேலடுக்கு மெனுவைக் காட்டுகிறது.\naf: Wys 'n oorlegkieslys om te herbegin, af te sluit of uit te teken.\nfr: Affiche un menu superposé pour redémarrer, arrêter ou se déconnecter.\nlo: ສະແດງເມນູ Overlay ເພື່ອເລີ່ມຕົ້ນໃຫມ່, ປິດຫຼືອອກຈາກລະບົບ.\nne: \"पुन: सुरु, बन्द वा लगआउट गर्न ओभरले मेनु देखाउँछ।\"\nlb: Weist en Iwwerlagermenü fir nei ze starten, auszeschalten oder auszeginn.\nbs: Prikazuje meni preklapanja za ponovno pokretanje, isključivanje ili odjavu.\nam: እንደገና ለማስጀመር, ለመዝጋት ወይም ለመገጣጠም የተደራቢ ምናሌ ያሳያል.\nlv: Parāda pārklājuma izvēlni restartēšanai, izslēgšanai vai atteikšanai.\nja: 再起動、シャットダウン、またはログアウトするためのオーバーレイ メニューを表示します。\nsk: Zobrazuje prekrývajúcu ponuku na reštartovanie, vypnutie alebo odhlásenie.\nyo: Fihan akojọ aṣayan ori lati tun bẹrẹ, pipade tabi aami.\nsi: නැවත ආරම්භ කිරීමට, වසා දැමීමට හෝ පිටවීමට උඩැතිරි මෙනුවක් පෙන්වයි.\nku: Ji bo ji nû ve destpêkirin, xitimandin an jêderketin menuyek pêvekirî nîşan dide.\nur: دوبارہ اسٹارٹ ، شٹ ڈاؤن یا لاگ آؤٹ کے لئے ایک اوورلے مینو دکھاتا ہے۔\nsr: Приказује мени са преклапањем за поновно покретање, искључивање или одјаву.\nes: Muestra un menú superpuesto para reiniciar, apagar o cerrar sesión.\nte: పునఃప్రారంభించడానికి, షట్డౌన్ చేయడానికి లేదా లాగ్అవుట్ చేయడానికి అతివ్యాప్తి మెనుని చూపుతుంది.\nda: Viser en overlejringsmenu til at genstarte, lukke eller logge ud.\nbn: রিস্টার্ট, শাটডাউন বা লগআউট করার জন্য একটি ওভারলে মেনু দেখায়।\nvi: Hiển thị menu lớp phủ để khởi động lại, tắt máy hoặc đăng xuất.\nzh-TW: 顯示用於重新啟動、關閉或註銷的覆蓋菜單。\nsv: Visar en överlagringsmeny för omstart, avstängning eller utloggning.\ngu: પુનઃપ્રારંભ, શટડાઉન અથવા લોગઆઉટ કરવા માટે ઓવરલે મેનુ બતાવે છે.\nbg: Показва насложено меню за рестартиране, изключване или излизане.\ntl: Nagpapakita ng isang overlay menu upang i -restart, shutdown o logout.\nmn: Дахин эхлүүлэхийн тулд Давхардсан цэс, унтрах эсвэл унтрах эсвэл гарах.\nsw: Inaonyesha menyu ya kufunika kuanza tena, kuzima au kuingia.\nko: 다시 시작, 종료 또는 로그아웃을 위한 오버레이 메뉴를 표시합니다.\nfa: منوی همپوشانی را برای راه اندازی مجدد، خاموش کردن یا خروج از سیستم نشان می دهد.\npt-BR: Shows an overlay menu to restart, shutdown or logout.\net: Taaskäivitamiseks, sulgemiseks või väljalogimiseks kuvab ülekattemenüü.\ncs: Zobrazuje překryvnou nabídku pro restart, vypnutí nebo odhlášení.\n"
  },
  {
    "path": "src/static/widgets/power-menu/i18n/display_name.yml",
    "content": "es: Menú de energía\nar: قائمة الطاقة\nuk: Меню живлення\ntl: Power Menu\nmk: Мени за напојување\nen: Power Menu\naf: Krag spyskaart\npt-BR: Power Menu\nhr: Izbornik napajanja\nit: Menù di accensione\nsr: Повер Мену\npa: ਪਾਵਰ ਮੀਨੂ\nbg: Меню за захранване\nka: დენის მენიუ\nuz: Quvvat menyusi\nfa: منوی پاور\neu: Power Menua\nsw: Menyu ya Nguvu\nlv: Barošanas izvēlne\nnl: Vermogensmenu\nlt: Maitinimo meniu\nso: Meheradda Power\nth: เมนูพลังงาน\nsv: Strömmeny\nur: پاور مینو\nhi: पावर मेनू\nzh-CN: 电源菜单\nbs: Power Menu\nis: Power Valmynd\nte: పవర్ మెనూ\nhu: Bekapcsoló menü\nam: የኃይል ምናሌ\net: Toitemenüü\nsi: බල මෙනුව\nlb: Power Menu\nbn: পাওয়ার মেনু\nne: पावर मेनु\nno: Strømmeny\nro: Meniul Power\nms: Menu Kuasa\ngu: પાવર મેનુ\ncs: Nabídka napájení\nvi: Menu nguồn\nde: Power-Menü\nfr: Menu d'alimentation\nkm: ម៉ឺនុយថាមពល\nps: د بریښنا مینو\nmn: Цуун цэс\nmt: Power Menu\nko: 전원 메뉴\nhe: תפריט כוח\ntr: Güç Menüsü\nja: パワーメニュー\nsk: Ponuka napájania\npt-PT: Menu de energia\ntg: Менюи энергетикӣ\nlo: ເມນູພະລັງງານ\nhy: Power Menu\nda: Power Menu\nid: Menu Daya\nru: Меню питания\nyo: Akojọ aṣayan agbara\nku: Menu Power\nel: Μενού Power\nzh-TW: 電源菜單\nca: Menú d'alimentació\nta: பவர் மெனு\nzu: Imenyu yamandla\npl: Menu zasilania\nfi: Virta-valikko\naz: Güc menyusu\ncy: Dewislen Pwer\n"
  },
  {
    "path": "src/static/widgets/power-menu/metadata.yml",
    "content": "id: \"@seelen/power-menu\"\nicon: IoPower\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Overlay\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/power-menu/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-power-menu\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: IoPower\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - Power\n  tooltip: |-\n    return \"Power Menu\";\n  template: |-\n    return Icon({ name: \"IoPower\" });\n  onClickV2: |-\n    trigger(\"@seelen/power-menu\");\n"
  },
  {
    "path": "src/static/widgets/quick-settings/i18n/description.yml",
    "content": "he: גישה מהירה להגדרות מערכת כמו בהירות, עוצמת קול ואפשרויות צריכת חשמל\npa: ਚਮਕ, ਵੌਲਯੂਮ, ਅਤੇ ਪਾਵਰ ਵਿਕਲਪਾਂ ਵਰਗੀਆਂ ਸਿਸਟਮ ਸੈਟਿੰਗਾਂ ਤੱਕ ਤੁਰੰਤ ਪਹੁੰਚ\nfa: دسترسی سریع به تنظیمات سیستم مانند گزینه های روشنایی، صدا و قدرت\nsw: Ufikiaji wa haraka wa mipangilio ya mfumo kama vile mwangaza, sauti na chaguo za nishati\nes: Acceso rápido a configuraciones del sistema como brillo, volumen y opciones de energía\nka: სწრაფი წვდომა სისტემის პარამეტრებზე, როგორიცაა სიკაშკაშე, მოცულობა და კვების პარამეტრები\nku: Gihîştina bilez a mîhengên pergalê yên wekî vebijarkên ronahî, deng û hêzê\nbn: উজ্জ্বলতা, ভলিউম এবং পাওয়ার বিকল্পগুলির মতো সিস্টেম সেটিংসে দ্রুত অ্যাক্সেস\nis: Fljótur aðgangur að kerfisstillingum eins og birtustigi, hljóðstyrk og orkuvalkostum\nfi: Nopea pääsy järjestelmäasetuksiin, kuten kirkkaus, äänenvoimakkuus ja tehoasetukset\nar: الوصول السريع إلى إعدادات النظام مثل السطوع ومستوى الصوت وخيارات الطاقة\nmn: Гэрэлтүүлэг, дууны түвшин, тэжээлийн сонголт зэрэг системийн тохиргоонд хурдан нэвтрэх\nta: பிரகாசம், ஒலியளவு மற்றும் ஆற்றல் விருப்பங்கள் போன்ற கணினி அமைப்புகளுக்கான விரைவான அணுகல்\nur: نظام کی ترتیبات جیسے چمک ، حجم ، اور بجلی کے اختیارات تک فوری رسائی\ngu: તેજ, વોલ્યુમ અને પાવર વિકલ્પો જેવી સિસ્ટમ સેટિંગ્સની ઝડપી ઍક્સેસ\nhr: Brzi pristup postavkama sustava kao što su svjetlina, glasnoća i opcije napajanja\naf: Vinnige toegang tot stelselinstellings soos helderheid, volume en kragopsies\nca: Accés ràpid a la configuració del sistema, com ara la brillantor, el volum i les opcions d'alimentació\nja: 明るさ、音量、電源オプションなどのシステム設定にすばやくアクセス\nlb: Schnell Zougang zu Systemastellungen wéi Hellegkeet, Volumen a Kraaftoptiounen\nlv: Ātra piekļuve sistēmas iestatījumiem, piemēram, spilgtuma, skaļuma un jaudas opcijām\nnl: Snelle toegang tot systeeminstellingen zoals helderheid, volume en energiebeheeropties\ncs: Rychlý přístup k nastavení systému, jako je jas, hlasitost a možnosti napájení\nvi: Truy cập nhanh vào cài đặt hệ thống như tùy chọn độ sáng, âm lượng và nguồn điện\naz: Parlaqlıq, səs səviyyəsi və güc seçimləri kimi sistem parametrlərinə sürətli giriş\nuz: Yorqinlik, ovoz balandligi va quvvat parametrlari kabi tizim sozlamalariga tezkor kirish\nso: Gelitaanka degdega ah ee habaynta nidaamka sida iftiinka, mugga, iyo fursadaha awoodda\nzh-CN: 快速访问系统设置，例如亮度、音量和电源选项\nbs: Brzi pristup sistemskim postavkama kao što su svjetlina, jačina zvuka i opcije napajanja\nne: चमक, भोल्युम, र पावर विकल्पहरू जस्ता प्रणाली सेटिङहरूमा द्रुत पहुँच\nen: Quick access to system settings like brightness, volume, and power options\nms: Akses pantas kepada tetapan sistem seperti pilihan kecerahan, kelantangan dan kuasa\ntl: Mabilis na access sa mga setting ng system tulad ng brightness, volume, at power na mga opsyon\nru: Быстрый доступ к системным настройкам, таким как яркость, громкость и параметры питания.\nsk: Rýchly prístup k nastaveniam systému, ako je jas, hlasitosť a možnosti napájania\nsv: Snabb åtkomst till systeminställningar som ljusstyrka, volym och strömalternativ\neu: Sarbide azkarra sistemaren ezarpenetara, hala nola distira, bolumena eta potentzia-aukerak\ntr: Parlaklık, ses seviyesi ve güç seçenekleri gibi sistem ayarlarına hızlı erişim\nuk: Швидкий доступ до налаштувань системи, таких як параметри яскравості, гучності та живлення\net: Kiire juurdepääs süsteemiseadetele, nagu heledus, helitugevus ja toitevalikud\nyo: Wiwọle yara yara si awọn eto eto bii imọlẹ, iwọn didun, ati awọn aṣayan agbara\nko: 밝기, 볼륨, 전원 옵션과 같은 시스템 설정에 빠르게 액세스\nmt: Aċċess rapidu għall-issettjar tas-sistema bħall-luminożità, il-volum u l-għażliet tal-enerġija\nid: Akses cepat ke pengaturan sistem seperti kecerahan, volume, dan opsi daya\nlt: Greita prieiga prie sistemos nustatymų, pvz., ryškumo, garsumo ir maitinimo parinkčių\nkm: ការចូលប្រើរហ័សទៅកាន់ការកំណត់ប្រព័ន្ធដូចជាពន្លឺ កម្រិតសំឡេង និងជម្រើសថាមពល\npl: Szybki dostęp do ustawień systemowych, takich jak jasność, głośność i opcje zasilania\nbg: Бърз достъп до системни настройки като яркост, сила на звука и опции за захранване\nhi: ब्राइटनेस, वॉल्यूम और पावर विकल्प जैसी सिस्टम सेटिंग्स तक त्वरित पहुंच\nel: Γρήγορη πρόσβαση σε ρυθμίσεις συστήματος όπως φωτεινότητα, ένταση και επιλογές ενέργειας\nhu: Gyors hozzáférés a rendszerbeállításokhoz, például a fényerő-, hangerő- és energiabeállításokhoz\nfr: Accès rapide aux paramètres du système tels que les options de luminosité, de volume et d'alimentation\ntg: Дастрасии зуд ба танзимоти система ба монанди равшанӣ, ҳаҷм ва имконоти барқ\ncy: Mynediad cyflym i osodiadau system fel disgleirdeb, cyfaint, ac opsiynau pŵer\nam: እንደ ብሩህነት፣ ድምጽ እና የኃይል አማራጮች ያሉ የስርዓት ቅንብሮችን በፍጥነት መድረስ\nit: Accesso rapido alle impostazioni di sistema come luminosità, volume e opzioni di alimentazione\nhy: Արագ մուտք դեպի համակարգի կարգավորումներ, ինչպիսիք են պայծառությունը, ծավալը և էներգիայի ընտրանքները\nde: Schneller Zugriff auf Systemeinstellungen wie Helligkeit, Lautstärke und Energieoptionen\nda: Hurtig adgang til systemindstillinger som lysstyrke, lydstyrke og strømindstillinger\nno: Rask tilgang til systeminnstillinger som lysstyrke, volum og strømalternativer\nsi: දීප්තිය, පරිමාව, සහ බල විකල්ප වැනි පද්ධති සැකසීම් වෙත ඉක්මන් ප්‍රවේශය\nte: బ్రైట్‌నెస్, వాల్యూమ్ మరియు పవర్ ఆప్షన్‌ల వంటి సిస్టమ్ సెట్టింగ్‌లకు త్వరిత యాక్సెస్\nzu: Ukufinyelela okusheshayo kuzilungiselelo zesistimu njengokukhanya, ivolomu, nezinketho zamandla\nro: Acces rapid la setările sistemului, cum ar fi opțiunile de luminozitate, volum și putere\nps: د سیسټم ترتیباتو ته چټک لاسرسی لکه چمک، حجم، او د بریښنا اختیارونه\nlo: \"ເຂົ້າເຖິງການຕັ້ງຄ່າລະບົບໄດ້ໄວເຊັ່ນ: ຄວາມສະຫວ່າງ, ລະດັບສຽງ, ແລະທາງເລືອກພະລັງງານ\"\nsr: Брз приступ системским подешавањима као што су осветљеност, јачина звука и опције напајања\nth: เข้าถึงการตั้งค่าระบบอย่างรวดเร็ว เช่น ความสว่าง ระดับเสียง และตัวเลือกพลังงาน\nzh-TW: 快速訪問系統設置，例如亮度、音量和電源選項\nmk: Брз пристап до системските поставки како што се осветленоста, јачината на звукот и опциите за напојување\npt-BR: Quick access to system settings like brightness, volume, and power options\npt-PT: Acesso rápido às configurações do sistema, como brilho, volume e opções de energia\n"
  },
  {
    "path": "src/static/widgets/quick-settings/i18n/display_name.yml",
    "content": "pa: ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ\nta: விரைவு அமைப்புகள்\nes: Configuración Rápida\nhy: Արագ կարգավորումներ\nur: فوری ترتیبات\nyo: Awọn eto iyara\ntr: Hızlı Ayarlar\nlb: Quick Astellunge\nlt: Greiti nustatymai\nam: ፈጣን ቅንብሮች\nde: Schnelleinstellungen\neu: Ezarpen azkarrak\nlo: ການຕັ້ງຄ່າດ່ວນ\nlv: Ātrie iestatījumi\nmn: Шуурхай тохиргоо\npt-PT: Configurações rápidas\nhe: הגדרות מהירות\ntg: Танзимоти зуд\nnl: Snelle instellingen\nis: Flýtistillingar\ntl: Mga Mabilisang Setting\nvi: Cài đặt nhanh\nbn: দ্রুত সেটিংস\ncy: Gosodiadau Cyflym\nms: Tetapan Pantas\npl: Szybkie ustawienia\nar: الإعدادات السريعة\nel: Γρήγορες ρυθμίσεις\nru: Быстрые настройки\nro: Setări rapide\naz: Sürətli Parametrlər\nsi: ඉක්මන් සැකසුම්\nzh-TW: 快速設置\nku: Mîhengên Zû\nps: چټک ترتیبات\nso: Dejinta degdega ah\nno: Hurtiginnstillinger\nmt: Settings Quick\nit: Impostazioni rapide\nbg: Бързи настройки\nne: द्रुत सेटिङहरू\nmk: Брзи поставки\nda: Hurtige indstillinger\nhi: त्वरित सेटिंग\nja: クイック設定\net: Kiirseaded\nsw: Mipangilio ya Haraka\nfi: Pika-asetukset\nfa: تنظیمات سریع\nen: Quick Settings\nsv: Snabbinställningar\nfr: Paramètres rapides\nsr: Брза подешавања\nth: การตั้งค่าด่วน\nuz: Tezkor sozlamalar\nuk: Швидкі налаштування\nzh-CN: 快速设置\nka: სწრაფი პარამეტრები\ngu: ઝડપી સેટિંગ્સ\nhr: Brze postavke\nbs: Brza podešavanja\ncs: Rychlé nastavení\nsk: Rýchle nastavenia\nzu: Izilungiselelo Ezisheshayo\nko: 빠른 설정\nte: త్వరిత సెట్టింగ్‌లు\naf: Vinnige instellings\nhu: Gyorsbeállítások\nca: Configuració ràpida\npt-BR: Quick Settings\nkm: ការកំណត់រហ័ស\nid: Pengaturan Cepat\n"
  },
  {
    "path": "src/static/widgets/quick-settings/metadata.yml",
    "content": "id: \"@seelen/quick-settings\"\nicon: RiSettings4Fill\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/quick-settings/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-quick-settings\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: RiSettings4Fill\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  tooltip: t(\"placeholder.settings\")\n  template: |-\n    return Icon({ name: \"LuSettings2\" });\n  onClickV2: |-\n    trigger(\"@seelen/quick-settings\");\n"
  },
  {
    "path": "src/static/widgets/settings/i18n/display_name.yml",
    "content": "pt-BR: Selen UI Settings\nta: சீலன் UI அமைப்புகள்\nzh-CN: 西林用户界面设置\nar: إعدادات واجهة المستخدم Seelen\nka: Seelen UI პარამეტრები\nit: Impostazioni dell'interfaccia utente Seelen\npl: Ustawienia UI Seelen\nku: Mîhengên Seelen Ui\nes: Configuración de UI de Seelen\nbs: Seelen UI postavke\naz: SEELEN UI Parametrləri\nne: नजर बटिंग हेर्नुहोस्\nhy: Seelen UI- ի պարամետրերը\nfi: Seelen UI -asetukset\nzh: Seelen UI设置\nfa: تنظیمات Seelen UI\nur: سیلین UI کی ترتیبات\nnl: Selen UI -instellingen\net: Seeleni kasutajaliidese sätted\neu: Seelen UI ezarpenak\nkm: ការកំណត់ UI UI UI\nte: సీలెన్ UI సెట్టింగులు\nzh-TW: 西林用戶界面設置\nlb: Seelen Ui Astellunge\nmt: Seelen UI Settings\nsv: Seelen UI -inställningar\nhu: Seelen ui beállítások\nda: Seelen UI -indstillinger\nyo: Awọn eto Sisen UI\nfr: Paramètres d'interface utilisateur Seelen\nmk: Поставки за UI на Seelen\nca: Configuració de la UI de Seelen\ngu: સીલેન યુઆઈ સેટિંગ્સ\nzu: Izilungiselelo ze-Seelen UI\naf: Seelen UI -instellings\nhr: Seelen UI postavke\nuz: Seelen UI sozlamalari\nam: ዌይን ኡኒ ቅንብሮች\nru: Настройки Selen UI\nth: การตั้งค่า Seelen UI\nso: Selelen Ui Ui\nsk: Nastavenia používateľského rozhrania Seelen\nbg: Настройки на Seelen UI\nuk: Налаштування інтерфейсу SEELEN\ntr: Seelen UI Ayarları\nvi: Cài đặt UI Seelen\nhe: הגדרות ממשק המשתמש של Seelen\npa: ਅਯੂ ਆਈ ਸੈਟਿੰਗਜ਼\nis: Seelen UI stillingar\nlo: ການຕັ້ງຄ່າ SEELEN UI\nno: Seelen UI -innstillinger\nko: Seelen UI 설정\nja: Seelen UI設定\ntl: Mga setting ng Seelen UI\nbn: সিলেন ইউআই সেটিংস\nmn: Seelen ui тохиргоо\npt: SEELEN UI Configurações\nsr: Сеелен УИ подешавања\nid: Pengaturan Seelen UI\nsw: Mipangilio ya Seelen UI\npt-PT: Configurações da interface do usuário Seelen\nps: د متحده ایالاتو تنظیمات وګورئ\ntg: Танзимоти ui ui\nsi: සීලන් යූ සැකසුම්\nen: Seelen UI Settings\nro: Seelen UI setări\ncs: Nastavení uživatelského rozhraní Seelen\nms: Tetapan Seelen UI\nlv: SEELEN UI iestatījumi\nlt: SEELEN UI nustatymai\ncy: Gosodiadau Seelen UI\nde: Seelen UI -Einstellungen\nel: Ρυθμίσεις Seelen UI\nhi: सेलेन यूआई सेटिंग्स\n"
  },
  {
    "path": "src/static/widgets/settings/metadata.yml",
    "content": "id: \"@seelen/settings\"\nicon: RiSettings4Fill\nmetadata:\n  displayName: !extend i18n/display_name.yml\ninstances: Single\nloader: InternalReact\nhidden: true\nlazy: true\n"
  },
  {
    "path": "src/static/widgets/system-tray/i18n/description.yml",
    "content": "so: Wuxuu muujiyaa liistada dhammaan astaamaha saxanka nidaamka.\nsr: Приказује мени са свим иконама на системској палети.\nuk: Показує меню з усіма піктограмами системного лотка.\nno: Viser en meny med alle systemstatusfeltikonene.\nfi: Näyttää valikon, jossa on kaikki ilmaisinalueen kuvakkeet.\nzh-TW: 顯示包含所有系統托盤圖標的菜單。\nar: يعرض قائمة بجميع أيقونات علبة النظام.\nhe: מציג תפריט עם כל סמלי מגש המערכת.\nlv: Parāda izvēlni ar visām sistēmas teknes ikonām.\nps: د ټولو سیسټم ټری شبیانو سره مینو ښیې.\nzu: Ibonisa imenyu enazo zonke izithonjana ze-tray zohlelo.\nyo: Fifihan akojọ aṣayan pẹlu gbogbo awọn aami eto atẹsẹ.\nbg: Показва меню с всички икони в системната област.\nid: Menampilkan menu dengan semua ikon baki sistem.\nlo: ສະແດງເມນູທີ່ມີຮູບສັນຍາລັກຂອງຖາດລະບົບທັງຫມົດ.\nvi: Hiển thị menu có tất cả các biểu tượng trên khay hệ thống.\nja: すべてのシステム トレイ アイコンを含むメニューを表示します。\nbs: Prikazuje meni sa svim ikonama na sistemskoj paleti.\nes: Muestra un menú con todos los iconos de la bandeja del sistema.\nsv: Visar en meny med alla ikoner i systemfältet.\nde: Zeigt ein Menü mit allen Taskleistensymbolen an.\nlb: Weist e Menü mat all de Systemtablettikonen.\nnl: Toont een menu met alle systeemvakpictogrammen.\ntg: Менюро бо ҳама нишонаҳои қишри система нишон медиҳад.\nne: सबै प्रणाली ट्रे आइकनहरू भएको मेनु देखाउँछ।\nmk: Прикажува мени со сите икони во системската фиока.\nhr: Prikazuje izbornik sa svim ikonama programske trake.\nsi: සියලුම පද්ධති තැටි අයිකන සහිත මෙනුවක් පෙන්වයි.\ncs: Zobrazí nabídku se všemi ikonami na systémové liště.\nkm: បង្ហាញម៉ឺនុយដែលមានរូបតំណាងប្រព័ន្ធថាសទាំងអស់។\nhi: सभी सिस्टम ट्रे आइकन के साथ एक मेनू दिखाता है।\nko: 모든 시스템 트레이 아이콘이 포함된 메뉴를 표시합니다.\neu: Sistemaren erretiluko ikono guztiekin menu bat erakusten du.\nhu: Megjelenít egy menüt a rendszertálca összes ikonjával.\npt-PT: Mostra um menu com todos os ícones da bandeja do sistema.\ntr: Tüm sistem tepsisi simgelerini içeren bir menü gösterir.\nca: Mostra un menú amb totes les icones de la safata del sistema.\nhy: \"Ցուցադրում է մենյու բոլոր համակարգի սկուտեղի պատկերակներով:\"\nka: აჩვენებს მენიუს ყველა სისტემის უჯრის ხატებით.\npl: Wyświetla menu ze wszystkimi ikonami w zasobniku systemowym.\nms: Menunjukkan menu dengan semua ikon dulang sistem.\nen: Shows a menu with all the system tray icons.\nfr: Affiche un menu avec toutes les icônes de la barre d'état système.\ngu: બધા સિસ્ટમ ટ્રે ચિહ્નો સાથે મેનુ બતાવે છે.\nsk: Zobrazuje ponuku so všetkými ikonami na systémovej lište.\nsw: Inaonyesha menyu na icons zote za tray ya mfumo.\ntl: Nagpapakita ng isang menu kasama ang lahat ng mga icon ng tray ng system.\nzh-CN: 显示包含所有系统托盘图标的菜单。\nis: Sýnir valmynd með öllum kerfisbakkatáknum.\nth: แสดงเมนูพร้อมไอคอนถาดระบบทั้งหมด\nbn: সমস্ত সিস্টেম ট্রে আইকন সহ একটি মেনু দেখায়।\nam: ከሁሉም የስርዓት ትሪ አዶዎች ሁሉ ጋር ምናሌ ያሳያል.\ncy: Yn dangos dewislen gyda holl eiconau hambwrdd y system.\nro: Afișează un meniu cu toate pictogramele din bara de sistem.\nda: Viser en menu med alle systembakkeikoner.\npt-BR: Shows a menu with all the system tray icons.\nmt: Juri menu bl-ikoni kollha tat-trej tas-sistema.\net: Kuvab menüü kõigi süsteemse salve ikoonidega.\nlt: Rodo meniu su visomis sistemos dėklo piktogramomis.\nuz: Tizimning barcha patnisi ​​piktogrammalari bilan menyuni ko'rsatadi.\naz: Bütün sistem tepsisi nişanları olan bir menyu göstərir.\nit: Mostra un menu con tutte le icone della barra delle applicazioni.\nmn: Бүх системийн тавиурын дүрс бүхий цэсийг харуулж байна.\nta: அனைத்து சிஸ்டம் ட்ரே ஐகான்களுடன் ஒரு மெனுவைக் காட்டுகிறது.\npa: ਸਾਰੇ ਸਿਸਟਮ ਟਰੇ ਆਈਕਾਨਾਂ ਵਾਲਾ ਮੇਨੂ ਦਿਖਾਉਂਦਾ ਹੈ।\naf: Wys 'n spyskaart met al die system tray-ikone.\nku: Pêşekek bi hemî îkonên pelika pergalê nîşan dide.\nte: అన్ని సిస్టమ్ ట్రే చిహ్నాలతో కూడిన మెనుని చూపుతుంది.\nru: Показывает меню со всеми значками на панели задач.\nur: سسٹم ٹرے شبیہیں کے ساتھ ایک مینو دکھاتا ہے۔\nel: Εμφανίζει ένα μενού με όλα τα εικονίδια του δίσκου συστήματος.\nfa: منویی را با تمام نمادهای سینی سیستم نشان می دهد.\n"
  },
  {
    "path": "src/static/widgets/system-tray/i18n/display_name.yml",
    "content": "id: Baki Sistem\nsk: Systémová lišta\nlt: Sistemos dėklas\nca: Safata del sistema\ntr: Sistem Tepsisi\npa: ਸਿਸਟਮ ਟਰੇ\nku: System Tray\nps: د سیسټم ټری\nar: علبة النظام\nhr: Sustavna traka\nja: システムトレイ\naf: System Tray\nhi: सिस्टम ट्रे\nhu: Rendszertálca\nsr: Системска трака\nta: கணினி தட்டு\nur: سسٹم ٹرے\nuk: Системний трей\nko: 시스템 트레이\nlo: ຖາດລະບົບ\nlb: System Schacht\neu: Sistemaren erretilua\nbg: Системна област\npl: Taca systemowa\nes: Bandeja del sistema\nbn: সিস্টেম ট্রে\nka: სისტემის უჯრა\nsw: Tray ya mfumo\npt-BR: System Tray\nth: ถาดระบบ\nte: సిస్టమ్ ట్రే\nne: प्रणाली ट्रे\nms: Dulang sistem\nde: Systemablage\nmn: Нь хуваалтын үнэгүйз\nru: Системный трей\nvi: Khay hệ thống\npt-PT: Bandeja do sistema\nzu: Ugqoko wesistimu\nlv: Sistēmas tekne\nbs: Sistemska traka\nzh-CN: 系统托盘\nyo: Atẹ eto\nmk: Системска фиока\nno: System Tray\nsi: පද්ධති තැටිය\ncy: Hambwrdd System\nnl: Systeemvak\ngu: સિસ્ટમ ટ્રે\ntg: Санки система\nfa: سینی سیستم\nso: Nidaamka Tray\nfi: Järjestelmälokero\nro: Tava de sistem\nkm: ថាសប្រព័ន្ធ\nen: System Tray\ntl: System tray\nsv: Systemfältet\nda: Systembakke\net: Süsteemi salv\nzh-TW: 系統托盤\nfr: Barre d'état système\nel: Δίσκος συστήματος\nis: Kerfisbakki\naz: Sistem tepsisi\ncs: Systémová lišta\nam: የስርዓት ትሪ\nit: Vassoio di sistema\nhe: מגש מערכת\nuz: Tizim patnis\nmt: Trej tas-Sistema\nhy: Համակարգի սկուտեղ\n"
  },
  {
    "path": "src/static/widgets/system-tray/metadata.yml",
    "content": "id: \"@seelen/system-tray\"\nicon: IoIosArrowDropdown\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/system-tray/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-system-tray\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: IoIosArrowDropdown\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  tooltip: |-\n    return \"System Tray\";\n  template: |-\n    return Icon({ name: \"IoIosArrowDropdown\" });\n  onClickV2: |-\n    trigger(\"@seelen/system-tray\");\n"
  },
  {
    "path": "src/static/widgets/task-switcher/i18n/display_name.yml",
    "content": "sk: Prepínač úloh\ntg: Гузаргоҳи вазифа\nlv: Uzdevumu pārslēdzējs\nmn: Даалгаврын шилжүүлэгч\nuk: Перемикач завдань\nkm: អ្នកប្តូរភារកិច្ច\ntr: Görev Değiştirici\nko: 작업 전환기\naz: Tapşırıq dəyişdiricisi\nhe: מחליף משימות\nlo: ເຄື່ອງສໍາຕັ້ງ\nsv: Uppgiftsväxlare\nsi: කාර්ය මාරු කරන්නා\nsr: Таск Свитцхер\nzu: Shintshana\nzh-CN: 任务切换器\nno: Oppgavebytter\nam: የተግባር ስዊክ\nsw: Swichi ya kazi\nka: ამოცანების გადამრთველი\nhu: Feladatváltó\nmt: Task Switcher\nda: Opgaveskifter\ntl: Task switch\nbs: Task Switcher\nku: Task Switcher\nur: ٹاسک سوئچر\nid: Pengalih Tugas\nen: Task Switcher\nfi: Task Switcher\nlb: Aufgab Switcher\nca: Canviador de tasques\npt-PT: Alternador de tarefas\neu: Zereginen aldatzailea\nhr: Izmjenjivač zadataka\nfa: Task Switcher\nmk: Преминувач на задачи\nte: టాస్క్ స్విచ్చర్\nvi: Trình chuyển đổi tác vụ\nes: Cambiador de tareas\nhi: कार्य स्विचर\nth: ตัวสลับงาน\nde: Aufgabenumschalter\nbg: Превключвател на задачи\nja: タスクスイッチャー\nta: பணி மாற்றி\nuz: Vazifa almashtirish\nyo: Yipada yipada\nbn: টাস্ক সুইচার\npa: ਟਾਸਕ ਸਵਿੱਚਰ\nel: Εναλλαγή εργασιών\ncs: Přepínač úloh\npt-BR: Task Switcher\nnl: Taakwisselaar\nps: ټاسک سویچر\ncy: Newidiwr Tasg\nne: टास्क स्विचर\nso: Wareegtada Wareegtada\nis: Verkefnaskipti\nhy: Առաջադրանքների փոխարկիչ\nfr: Sélecteur de tâches\ngu: ટાસ્ક સ્વિચર\nlt: Užduočių perjungiklis\nru: Переключатель задач\nit: Commutatore di attività\naf: Taakwisselaar\nro: Comutator de sarcini\net: Ülesande vahetaja\nar: مبدل المهام\nzh-TW: 任務切換器\npl: Przełącznik zadań\nms: Penukar tugas\n"
  },
  {
    "path": "src/static/widgets/task-switcher/metadata.yml",
    "content": "id: \"@seelen/task-switcher\"\nicon: PiSelectionForegroundDuotone\nmetadata:\n  displayName: !extend i18n/display_name.yml\ninstances: Single\nloader: Internal\npreset: Overlay\nhidden: true\n"
  },
  {
    "path": "src/static/widgets/toolbar/i18n/display_name.yml",
    "content": "ro: Bara de instrumente Fancy\nnl: Fancy werkbalk\nsk: Panely nástrojov Fancy Toolbar\nde: Ausgefallene Symbolleiste\nid: Bilah Alat Mewah\nja: ファンシーツールバー\nar: شريط أدوات فاخر\ncs: Pás nástrojů Fancy Toolbar\npl: Fantazyjny pasek narzędzi\nam: Fancy የመሣሪያ አሞሌ\nte: ఫాన్సీ టూల్‌బార్\ncy: Bar offer ffansi\nhy: Fancy Toolbar\ngu: ફેન્સી ટૂલબાર\nms: Bar alat mewah\nen: Fancy Toolbar\ntl: Fancy Toolbar\nyo: Ọpa irinṣẹ Fancy\nko: 멋진 도구 모음\nzh-CN: 精美工具栏\naz: Xülya alətlər paneli\nfr: Barre d'outils sophistiquée\nbs: Fancy alatna traka\nlb: Fantastesch Toolbar\nlv: Fancy Toolbar\nth: แถบเครื่องมือแฟนซี\ntr: Süslü Araç Çubuğu\nuk: Вигадлива панель інструментів\nzu: I-Fancy Toolbar\nhu: Fancy eszköztár\neu: Fancy tresna barra\nfa: نوار ابزار فانتزی\nur: فینسی ٹول بار\nsw: Zana ya Dhana\nmn: Уран зөгнөлийн багаж самбар\nkm: របារឧបករណ៍ពុម្ពអក្សរក្បូរក្បាច់\nlo: ແຖບເຄື່ອງມື fancy\net: Fancy tööriistariba\nhr: Fantastična alatna traka\nhi: फैंसी टूलबार\nda: Fancy værktøjslinje\nes: Barra de herramientas Fancy\nno: Fancy verktøylinje\nps: د فینسي توکپټه\npt: Barra de ferramentas sofisticada\nmk: Фенси лента со алатки\ntg: Панели футбол\nbg: Fancy Toolbar\nuz: Fashion asboblar paneli\nhe: סרגל כלים מפואר\nvi: Thanh công cụ ưa thích\nsv: Fancy verktygsfält\npa: ਫੈਨਸੀ ਟੂਲਬਾਰ\nit: Barra degli strumenti di fantasia\nfi: Fancy-työkalurivi\nku: Fancy Toolbar\nbn: অভিনব সরঞ্জামদণ্ড\nsi: විසිතුරු මෙවලම් තීරුව\nca: Barra d'eines de luxe\nis: Fancy tækjastikan\nne: फेन्सी उपकरणपट्टी\npt-PT: Barra de ferramentas sofisticada\nso: Qalabka Fancy Toolbar\nzh-TW: 精美工具欄\nta: ஆடம்பரமான கருவிப்பட்டி\nel: Φανταχτερή γραμμή εργαλείων\nmt: Toolbar tal-Fancy\nzh: 花式工具栏\nlt: Išgalvota įrankių juosta\nru: Красивая панель инструментов\nka: ლამაზი პანელი\nsr: Фанци алатна трака\naf: Fancy -werkbalk\npt-BR: Fancy Toolbar\n"
  },
  {
    "path": "src/static/widgets/toolbar/metadata.yml",
    "content": "id: \"@seelen/fancy-toolbar\"\nicon: BiSolidDockTop\nmetadata:\n  displayName: !extend i18n/display_name.yml\ninstances: ReplicaByMonitor\nloader: Legacy\n"
  },
  {
    "path": "src/static/widgets/user/i18n/description.yml",
    "content": "pa: ਉਪਯੋਗਕਰਤਾ ਪ੍ਰੋਫਾਈਲ ਅਤੇ ਫੋਲਡਰਾਂ ਤੱਕ ਤੇਜ਼ ਪਹੁੰਚ\nsv: Användarprofil och snabb åtkomst till mappar\ncy: Proffil defnyddiwr a mynediad cyflym i ffolderi\npt-BR: Perfil do usuário e acesso rápido a pastas\nlv: Lietotāja profils un ātra piekļuve mapēm\ngu: વપરાશકર્તા પ્રોફાઇલ અને ફોલ્ડર્સમાં ઝડપી ઍક્સેસ\nmk: Кorisnички профил и брз пристап до папки\nso: Profile-ka isticmaalaha iyo helitaanka degdegga ah ee folderrada\nuz: Foydalanuvchi profili va papkalarga tezkor kirish\nsw: Wasifu wa mtumiaji na ufikiaji wa haraka wa folda\nka: მომხმარებლის პროფილი და საქაღალდეებზე სწრაფი წვდომა\nda: Brugerprofil og hurtig adgang til mapper\nfi: Käyttäjäprofiili ja nopea pääsy kansioihin\ncs: Uživatelský profil a rychlý přístup ke složkám\nta: பயனர் சுயவிவரம் மற்றும் கோப்புறைகளுக்கு விரைவான அணுகல்\nku: Profîla bikarhêner û gihîştina bilez a peldankan\nlb: Benotzerprofil a séier Zougang zu Dossieren\nhi: उपयोगकर्ता प्रोफ़ाइल और फ़ोल्डर तक त्वरित पहुंच\nkm: ប្រវត្តិរូបអ្នកប្រើ និងការចូលប្រើថតឯកសារយ៉ាងរហ័ស\nmn: Хэрэглэгчийн профайл болон хавтас руу хурдан хандалт\nyo: Profaili olumulo ati iraye iyara si awọn folda\nzu: Iphrofayili yomsebenzisi nokufinyelela okusheshayo kumafolda\nzh-CN: 用户配置文件和文件夹快速访问\nko: 사용자 프로필 및 폴더 빠른 액세스\nbg: Потребителски профил и бърз достъп до папки\nes: Perfil de usuario y acceso rápido a carpetas\nnl: Gebruikersprofiel en snelle toegang tot mappen\nbs: Korisnički profil i brzi pristup folderima\nbn: ব্যবহারকারী প্রোফাইল এবং ফোল্ডারগুলিতে দ্রুত অ্যাক্সেস\nfr: Profil utilisateur et accès rapide aux dossiers\nno: Brukerprofil og hurtig tilgang til mapper\nde: Benutzerprofil und Schnellzugriff auf Ordner\nzh-TW: 使用者設定檔和資料夾快速存取\npl: Profil użytkownika i szybki dostęp do folderów\nlt: Vartotojo profilis ir greita prieiga prie aplankų\nru: Профиль пользователя и быстрый доступ к папкам\nvi: Hồ sơ người dùng và truy cập nhanh vào thư mục\nro: Profil utilizator și acces rapid la dosare\nhy: Օdelays գdelays իdelays delays այdelays delays delays delays delays\npt-PT: Perfil do utilizador e acesso rápido a pastas\naz: İstifadəçi profili və qovluqlara sürətli giriş\nth: โปรไฟล์ผู้ใช้และการเข้าถึงโฟลเดอร์อย่างรวดเร็ว\nlo: ໂປຣໄຟລ໌ຜູ້ໃຊ້ ແລະການເຂົ້າເຖິງໂຟນເດີຢ່າງວ່ອງໄວ\nhe: פרופיל משתמש וגישה מהירה לתיקיות\nja: ユーザープロファイルとフォルダへのクイックアクセス\ntr: Kullanıcı profili ve klasörlere hızlı erişim\net: Kasutajaprofiil ja kiire juurdepääs kaustadele\nsr: Кориснички профил и брз приступ фасциклама\nit: Profilo utente e accesso rapido alle cartelle\nne: प्रयोगकर्ता प्रोफाइल र फोल्डरहरूमा द्रुत पहुँच\nuk: Профіль користувача та швидкий доступ до папок\nel: Προφίλ χρήστη και γρήγορη πρόσβαση σε φακέλους\nis: Notandasnið og fljótlegur aðgangur að möppum\ntg: Профили корбар ва дастрасии зуд ба ҷузвдонҳо\naf: Gebruikersprofiel en vinnige toegang tot vouers\nte: వినియోగదారు ప్రొఫైల్ మరియు ఫోల్డర్‌లకు త్వరిత ప్రాప్యత\nid: Profil pengguna dan akses cepat ke folder\nhu: Felhasználói profil és gyors hozzáférés a mappákhoz\nsk: Používateľský profil a rýchly prístup k priečinkom\nsi: පරිශීලක පැතිකඩ සහ ෆෝල්ඩර වෙත ඉක්මන් ප්‍රවේශය\ntl: Profile ng user at mabilis na access sa mga folder\nfa: نمایه کاربر و دسترسی سریع به پوشه‌ها\nps: د کارونکي پروفایل او فولډرونو ته چټک لاسرسی\nca: Perfil d'usuari i accés ràpid a carpetes\nar: ملف المستخدم والوصول السريع للمجلدات\nur: صارف پروفائل اور فولڈرز تک فوری رسائی\nam: የተጠቃሚ መገለጫ እና ወደ አቃፊዎች ፈጣን መድረስ\nhr: Korisnički profil i brzi pristup mapama\nen: User profile and quick access to folders\neu: Erabiltzaile profila eta karpetetarako sarbide azkarra\nmt: Profil tal-utent u aċċess mgħaġġel għall-folders\nms: Profil pengguna dan akses pantas ke folder\n"
  },
  {
    "path": "src/static/widgets/user/i18n/display_name.yml",
    "content": "id: Menu Pengguna\ngu: વપરાશકર્તા મેનૂ\nit: Menu Utente\npl: Menu Użytkownika\nja: ユーザーメニュー\nms: Menu Pengguna\nuk: Меню користувача\nlt: Vartotojo Meniu\nmt: Menu tal-Utent\npt-BR: Menu do Usuário\nhu: Felhasználói Menü\nne: प्रयोगकर्ता मेनु\nnl: Gebruikersmenu\ncs: Uživatelské Menu\naz: İstifadəçi Menyusu\nfa: منوی کاربر\ncy: Dewislen Defnyddiwr\ntl: Menu ng User\nsk: Používateľské Menu\nro: Meniu Utilizator\nru: Меню пользователя\net: Kasutaja Menüü\nhy: Օգտատիրdelays Մdelays\nzu: Imenyu Yomsebenzisi\nbs: Korisnički Meni\nlv: Lietotāja Izvēlne\nur: صارف مینو\ntr: Kullanıcı Menüsü\nhe: תפריט משתמש\nko: 사용자 메뉴\nbg: Потребителско Меню\nbn: ব্যবহারকারী মেনু\nte: వినియోగదారు మెనూ\nes: Menú de Usuario\nfi: Käyttäjävalikko\naf: Gebruikersmenu\npa: ਉਪਯੋਗਕਰਤਾ ਮੈਨੂ\nsi: පරිශීලක මෙනුව\nta: பயனர் மெனு\nde: Benutzermenü\nam: የተጠቃሚ ምናሌ\nzh-TW: 使用者選單\nhr: Korisnički Izbornik\nka: მომხმარებლის მენიუ\nlo: ເມນູຜູ້ໃຊ້\nps: د کارونکي مینو\nen: User Menu\nel: Μενού Χρήστη\nkm: ម៉ឺនុយអ្នកប្រើ\nmk: Мени на Корисникот\nar: قائمة المستخدم\npt-PT: Menu do Utilizador\nlb: Benotzermenü\nfr: Menu Utilisateur\nis: Notendavalmynd\nso: Liiska Isticmaalaha\nno: Brukermeny\nku: Menuya Bikarhêner\nzh-CN: 用户菜单\nsv: Användarmeny\neu: Erabiltzailearen Menua\nhi: उपयोगकर्ता मेनू\nvi: Menu Người Dùng\nth: เมนูผู้ใช้\nda: Brugermenu\nca: Menú d'Usuari\nsw: Menyu ya Mtumiaji\nmn: Хэрэглэгчийн Цэс\nsr: Кориснички Мени\ntg: Менюи корбар\nuz: Foydalanuvchi Menyusi\nyo: Akojọ Olumulo\n"
  },
  {
    "path": "src/static/widgets/user/metadata.yml",
    "content": "id: \"@seelen/user-menu\"\nicon: PiFolderUser\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\ninstances: Single\nloader: Internal\npreset: Popup\nhidden: true\nlazy: true\nsettings:\n  - type: select\n    key: displayNameSource\n    label: Display Name\n    options:\n      - label: Profile Name\n        value: profileName\n      - label: Xbox Gamertag\n        value: xboxGamertag\n    defaultValue: profileName\nplugins:\n  - !extend ./toolbar-plugin.yml\n"
  },
  {
    "path": "src/static/widgets/user/toolbar-plugin.yml",
    "content": "id: \"@seelen/tb-user-menu\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\n  description: !extend i18n/description.yml\nicon: FaRegCircleUser\ntarget: \"@seelen/fancy-toolbar\"\nplugin:\n  scopes:\n    - User\n  tooltip: |-\n    return user.displayName;\n  template: |-\n    return [Image({ path: user.profilePicturePath }), ' ', user.displayName];\n  onClickV2: |-\n    trigger(\"@seelen/user-menu\");\n"
  },
  {
    "path": "src/static/widgets/wallpaper-manager/i18n/display_name.yml",
    "content": "te: వాల్పేపర్ మేనేజర్\nsr: Менаџер позадина\ngu: વ wallpaper લપેપર મેનેજર\nid: Manajer Wallpaper\nzu: Umphathi Wallpaper\npt-PT: Gerenciador de papel de parede\nel: Διαχειριστής ταπετσαρίας\nru: Менеджер обоев\nuk: Менеджер шпалер\nzh-TW: 壁紙管理器\nbs: Bilank menadžer\nfr: Gestionnaire de fonds d'écran\nur: وال پیپر منیجر\nhe: מנהל טפטים\nsi: බිතුපත් කළමනාකරු\nfa: مدیر کاغذ دیواری\nth: ผู้จัดการวอลล์เปเปอร์\nca: Gestor de fons de pantalla\nuz: Fon rasmi menejeri\nno: Bakgrunnssjef\nhr: Upravitelj pozadine\nka: ფონი მენეჯერი\nam: የግድግዳ ወረቀት ሥራ አስኪያጅ\nmt: Maniġer tal-Wallpaper\nlb: Wallpaper Manager\neu: Wallpaper Manager\nhi: वॉलपेपर प्रबंधक\nvi: Quản lý hình nền\nlt: Tapetų tvarkyklė\npl: Menedżer tapet\nta: வால்பேப்பர் மேலாளர்\npa: ਵਾਲਪੇਪਰ ਮੈਨੇਜਰ\ntl: Tagapamahala ng Wallpaper\nmn: Хувьцааны төлөөлөгчний менежер\nde: Hintergrundbild-Manager\ntg: Обои Менеҷери\nmk: Менаџер за позадина\nnl: Behangbeheer\nen: Wallpaper Manager\nit: Gestore degli sfondi\nar: مدير الخلفيات\nzh-CN: 壁纸管理器\ncy: Rheolwr papur wal\nbn: ওয়ালপেপার ম্যানেজার\nes: Gestor de fondos de pantalla\nsw: Meneja wa Ukuta\nko: 배경화면 관리자\nbg: Мениджър на тапети\npt-BR: Wallpaper Manager\nkm: កម្មវិធីគ្រប់គ្រងផ្ទាំងរូបភាព\ncs: Správce tapet\nne: वालपेपर प्रबन्धक\nyo: Oluṣakoso ogiri\naf: Muurpapierbestuurder\nlv: Tapetes pārvaldnieks\nhy: Պաստառների կառավարիչ\naz: Divar kağızı meneceri\nzh: 壁纸管理器\nro: Manager tapet\nps: د وال پیپر سمبالګر\ntr: Duvar Kağıdı Yöneticisi\nsv: Bakgrundshanterare\nsk: Správca tapiet\nja: 壁紙マネージャー\nku: Gerînendeyê Wallpaper\nlo: ຜູ້ຈັດການ Wallpaper\nhu: Háttérkép menedzser\nms: Pengurus Wallpaper\nis: Veggfóðurstjóri\nfi: Taustakuva Manager\nso: Maamulaha Wareegtada\net: Taustapildi haldur\nda: Baggrundsmanager\npt: Gerenciador de papéis de parede\n"
  },
  {
    "path": "src/static/widgets/wallpaper-manager/metadata.yml",
    "content": "id: \"@seelen/wallpaper-manager\"\nicon: PiWallDuotone\nmetadata:\n  displayName: !extend i18n/display_name.yml\nloader: Legacy\ninstances: Single\nnoMemoryLeakWorkaround: true\n"
  },
  {
    "path": "src/static/widgets/weg/i18n/display_name.yml",
    "content": "el: Αποβάθρα/Μπάρα πιάτων\nps: ډیک / ټاسک بار\nda: Dock/Arbejdsfelt\naf: Dock/taakbalk\nro: Dock/Taskbar\nfr: Dock/Taskbar\ntr: Dock/Taskbar\nso: Dock / Tootshar\npa: ਡੌਕ / ਟਾਸਕਬਾਰ\nru: Док/Панель задач\nbs: Dock / programska traka\nfa: اسکله/نوار وظیفه\nms: Dock/Taskbar\ncs: Dok / panel s odkládací plochou\nuk: Док/Панель завдань\nde: Dock/Taskbar\nar: رصيف الميناء/شريط التسوق\nlo: ແຖບ Dock / Task\nhi: डॉक/टास्टरबार\naz: Dock / tapşırıq çubuğu\nam: Dock / Accorbar\nko: 도크/작업 표시줄\neu: Dock / ataza barra\nuz: Dok / vazifalar paneli\nka: დოკ/დავალების ზოლი\nhy: Dock / Taskbar\nen: Dock/Taskbar\nca: Moll/barra de tasques\nta: கப்பல்துறை/பணிப்பட்டி\nsi: තටාකය / කාර්ය තීරුව\npt-BR: Dock/Taskbar\nnl: Dok/taskbar\nmn: Док / Taskbar\nsk: Dok / panel s ponukou\nhr: Dock/Tagova\nhu: Dokkoló/Taskbar\nzh-TW: 停靠欄/任務欄\nno: Dock/oppgavelinje\nes: Muelle/Barra de Muelle\npt: Dock/Taskbar\nte: డాక్/టాస్క్‌బార్\nne: डक / टास्कबार\nlt: Dokas / meniu juosta\ntl: Dock/Taskbar\nmt: Dock / Taskbar\ngu: ગોદી/ટાસ્કબાર\nsr: Доцк / трака задатака\nis: Bryggju/verkefnabar\nku: Dock / Taskbar\nsv: Docka/Taskbar\nzu: Dock / taskbar\nth: แท่นวาง/บาร์\net: Dock/Taskbar\nsw: Dock/Taskbar\nkm: ចត / ភារកិច្ច\nzh: 码头/任务栏\nbn: ডক/টাস্কবার\ntg: Dock / супоришҳо\nmk: Док/лента со задачи\nyo: Dock / forkbar\nzh-CN: 停靠栏/任务栏\nit: Dock/Taskbar\nid: Dok/Bilah Tugas\nbg: Док/таскбар\npt-PT: Dock/barra de tarefas\nfi: Telakka/taskupalkki\nur: گودی/ٹاسک بار\ncy: Doc/bar tasgau\nlb: Dock / Taskbar\nlv: Doka/askarte\nvi: Dock/Thanh tác vụ\nja: ドック/タスクバー\nhe: שורת המזח/המשימות\npl: Dok/pasek zadań\n"
  },
  {
    "path": "src/static/widgets/weg/metadata.yml",
    "content": "id: \"@seelen/weg\"\nicon: BiDockBottom\nmetadata:\n  displayName: !extend i18n/display_name.yml\ninstances: ReplicaByMonitor\nloader: Legacy\n"
  },
  {
    "path": "src/static/widgets/window-manager/i18n/display_name.yml",
    "content": "sw: Meneja wa Window\nlt: Langų tvarkyklė\nta: விண்டர் மேலாளர்\nlo: ຜູ້ຈັດການ Window\nsk: Správca okien\nzu: Imenenja yewindows\nkm: កម្មវិធីគ្រប់គ្រងបង្អួច\nsi: කවුළු කළමනාකරු\nte: విండో మేనేజర్\nku: Rêvebirê pencereyê\nmn: Цонхны менежер\npl: Menedżer okien\nmt: Window Manager\nlb: Fënter Manager\nis: Gluggastjóri\nja: ウィンドウ・マネージャー\nzh-TW: 窗口管理器\nfr: Gestionnaire de fenêtres\npa: ਵਿੰਡੋ ਮੈਨੇਜਰ\nzh: 窗口管理器\nel: Διαχειριστής παραθύρων\nvi: Quản lý cửa sổ\nsr: Менаџер прозора\nam: የመስኮት አቀናባሪ\nlv: Logu pārvaldnieks\nfi: Ikkunanhallinta\ncs: Správce oken\ncy: Rheolwr Ffenestr\nru: Менеджер окон\nko: 창 관리자\nca: Gestor de finestres\nbs: Upravitelj prozora\npt: Gerenciador de janelas\ngu: બારી\naz: Pəncərə meneceri\net: Window Manager\nka: ფანჯრის მენეჯერი\npt-PT: Gerenciador de janelas\nhi: विंडो मैनेजर\npt-BR: Window Manager\nit: Gestore della finestra\nuz: Oyna menejeri\nno: Vindusleder\nid: Manajer Jendela\nhe: מנהל חלונות\nmk: Менаџер на прозорци\nhu: Ablakkezelő\naf: Vensterbestuurder\nda: Vindueshåndtering\nuk: Диспетчер вікон\nzh-CN: 窗口管理器\nso: Maamulaha daaqada\nms: Pengurus tetingkap\nsv: Fönsterhanterare\nne: वान प्रबन्धक\nro: Manager de ferestre\nth: ผู้จัดการหน้าต่าง\ntg: Менеҷери тиреза\nhr: Upravitelj prozora\ntl: Window Manager\nbg: Мениджър на прозорци\nde: Fenster-Manager\nnl: Venster Manager\nar: مدير النافذة\nyo: Oluṣakoso window\nbn: উইন্ডো ম্যানেজার\nhy: Պատուհանների կառավարիչ\nes: Gestor de ventanas\neu: Leiho kudeatzailea\nen: Window Manager\ntr: Pencere Yöneticisi\nps: د کړکۍ مدیر\nur: ونڈو مینیجر\nfa: مدیر پنجره\n"
  },
  {
    "path": "src/static/widgets/window-manager/metadata.yml",
    "content": "id: \"@seelen/window-manager\"\nicon: BsGrid1X2Fill\nmetadata:\n  displayName: !extend i18n/display_name.yml\nloader: Legacy\ninstances: ReplicaByMonitor\n"
  },
  {
    "path": "src/static/widgets/workspaces-viewer/i18n/display_name.yml",
    "content": "gu: વર્કસ્પેસ વ્યૂઅર\nde: Arbeitsbereichs-Viewer\nro: Vizualizator spații de lucru\npa: ਵਰਕਸਪੇਸ ਦਰਸ਼ਕ\nda: Workspaces Viewer\nfa: نمایشگر فضاهای کاری\nel: Προβολή χώρων εργασίας\nzh-TW: 工作區查看器\nuk: Переглядач робочих областей\nfr: Visionneuse d'espaces de travail\nbg: Преглед на работни пространства\nsw: Mtazamaji wa nafasi za kazi\nhu: Munkaterületek megjelenítője\nte: కార్యస్థలాల వీక్షకుడు\nlv: Darbvietu skatītājs\nhy: Աշխատանքային տարածքների դիտիչ\naz: İş yerləri İzləyicisi\nzh-CN: 工作区查看器\nvi: Trình xem không gian làm việc\nps: د کاري ځایونو لیدونکي\ncy: Gwyliwr Mannau Gwaith\nhr: Preglednik radnih prostora\nhi: कार्यस्थान दर्शक\nru: Просмотр рабочих пространств\nmk: Прегледувач на работни простори\nth: ผู้ดูพื้นที่ทำงาน\net: Tööruumide vaataja\ncs: Prohlížeč pracovních prostorů\nno: Workspaces Viewer\nid: Penampil Ruang Kerja\nmt: Viewer tal-Ispazji tax-Xogħol\nbn: ওয়ার্কস্পেস ভিউয়ার\nhe: מציג סביבות עבודה\nit: Visualizzatore aree di lavoro\nen: Workspaces Viewer\nis: Workspaces Viewer\npt-PT: Visualizador de espaços de trabalho\nkm: អ្នកមើលកន្លែងធ្វើការ\npl: Przeglądarka obszarów roboczych\nne: कार्यस्थान दर्शक\nfi: Työtilojen katseluohjelma\ntg: ViewPaces\naf: Werkruimtes-kyker\nbs: Workspaces Viewer\nku: Dîmenderê Cihên Xebatê\nso: Googooyaasha Muuqaalka\ntl: Viewer ng Workspaces\nsr: Воркспацес Виевер\nzu: Isibukeli Somsebenzi\nmn: Workspaces Viewer\ntr: Çalışma Alanları Görüntüleyicisi\nes: Visor de espacios de trabajo\nsk: Prehliadač pracovných priestorov\nam: የስራ ቦታዎች ተመልካቾች\nca: Visor d'espais de treball\nlb: Workspaces Viewer\nnl: Werkruimteviewer\nsi: වැඩබිම් බලන්නා\nka: სამუშაო ადგილების მაყურებელი\nar: عارض مساحات العمل\nsv: Workspaces Viewer\nms: Ruang Workspaces\nlo: ຜູ້ເບິ່ງພື້ນທີ່ເຮັດວຽກ\nja: ワークスペースビューア\nyo: Oluwo Awọn iṣẹ\npt-BR: Workspaces Viewer\nur: ورک اسپیس ناظرین\nlt: Darbo sričių peržiūros priemonė\nuz: Ishxonalar ko'ruvchisi\nko: 작업공간 뷰어\neu: Lan-eremuen ikuslea\nta: பணியிட பார்வையாளர்\n"
  },
  {
    "path": "src/static/widgets/workspaces-viewer/metadata.yml",
    "content": "id: \"@seelen/workspaces-viewer\"\nmetadata:\n  displayName: !extend i18n/display_name.yml\ninstances: Single\nloader: Internal\npreset: Overlay\nhidden: true\nlazy: true\n"
  },
  {
    "path": "src/tauri.conf.json",
    "content": "{\n  \"$schema\": \"../node_modules/@tauri-apps/cli/config.schema.json\",\n  \"productName\": \"Seelen UI\",\n  \"identifier\": \"com.seelen.seelen-ui\",\n  \"version\": \"../package.json\",\n  \"app\": {\n    \"security\": {\n      \"assetProtocol\": {\n        \"enable\": true,\n        \"scope\": [\n          \"$RESOURCE/**/*\",\n          \"$TEMP/**/*\",\n          \"$APPDATA/**/*\",\n          \"$LOCALDATA/**/*\",\n          \"**/*.jpg\",\n          \"**/*.jpeg\",\n          \"**/*.png\",\n          \"**/*.webp\",\n          \"**/*.gif\",\n          \"**/*.svg\"\n        ]\n      }\n    },\n    \"withGlobalTauri\": false\n  },\n  \"build\": {\n    \"beforeDevCommand\": \"npm run build:ui -- --serve\",\n    \"beforeBuildCommand\": \"npm run build:ui -- --production\",\n    \"devUrl\": \"http://localhost:3579\",\n    \"frontendDist\": \"../dist\",\n    \"features\": []\n  },\n  \"bundle\": {\n    \"active\": true,\n    \"createUpdaterArtifacts\": true,\n    \"publisher\": \"Seelen\",\n    \"category\": \"Utility\",\n    \"copyright\": \"Copyright © 2024\",\n    \"shortDescription\": \"Seelen UI\",\n    \"longDescription\": \"Seelen UI Fully Customizable Desktop Environment for Windows\",\n    \"licenseFile\": \"../LICENSE\",\n    \"homepage\": \"https://github.com/eythaann/Seelen-UI\",\n    \"resources\": {\n      \"static\": \"static\"\n    },\n    \"targets\": [\n      \"nsis\"\n    ],\n    \"icon\": [\n      \"static/icons/icon.ico\"\n    ],\n    \"windows\": {\n      \"certificateThumbprint\": null,\n      \"digestAlgorithm\": \"sha256\",\n      \"nsis\": {\n        \"installerIcon\": \"static/icons/icon.ico\",\n        \"sidebarImage\": \"static/icons/banner.bmp\",\n        \"template\": \"templates/installer.nsi\",\n        \"installerHooks\": \"templates/installer-hooks.nsh\",\n        \"installMode\": \"perMachine\",\n        \"startMenuFolder\": \"Seelen\",\n        \"displayLanguageSelector\": true,\n        \"languages\": [\n          \"arabic\",\n          \"bulgarian\",\n          \"dutch\",\n          \"english\",\n          \"french\",\n          \"german\",\n          \"japanese\",\n          \"korean\",\n          \"portuguesebr\",\n          \"russian\",\n          \"simpchinese\",\n          \"spanish\",\n          \"swedish\",\n          \"tradchinese\",\n          \"turkish\"\n        ]\n      }\n    },\n    \"fileAssociations\": [\n      {\n        \"ext\": [\n          \".slu\"\n        ],\n        \"name\": \"seelen-ui.file\",\n        \"description\": \"Seelen UI File\",\n        \"mimeType\": \"text/plain\",\n        \"role\": \"Viewer\"\n      }\n    ]\n  },\n  \"plugins\": {\n    \"updater\": {\n      \"pubkey\": \"\"\n    },\n    \"deep-link\": {\n      \"mobile\": [],\n      \"desktop\": {\n        \"schemes\": [\n          \"seelen-ui.uri\"\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/templates/AppxManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Package\n  xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"\n  xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"\n  xmlns:uap3=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/3\"\n  xmlns:uap5=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/5\"\n  xmlns:uap7=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/7\"\n  xmlns:uap10=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/10\"\n  xmlns:desktop6=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10/6\"\n  xmlns:desktop7=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10/7\"\n  xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"\n  xmlns:virtualization=\"http://schemas.microsoft.com/appx/manifest/virtualization/windows10\"\n  IgnorableNamespaces=\"uap uap3 uap5 uap7 uap10 desktop6 desktop7 rescap virtualization\"\n>\n  <Identity\n    Name=\"Seelen.SeelenUI\"\n    Publisher=\"CN=7E60225C-94CB-4B2E-B17F-0159A11074CB\"\n    Version=\"{{version}}\"\n    ProcessorArchitecture=\"{{architecture}}\"\n  />\n\n  <Properties>\n    <DisplayName>Seelen UI</DisplayName>\n    <PublisherDisplayName>Seelen</PublisherDisplayName>\n    <Description>Fully Customizable Desktop Enviroment for Windows.</Description>\n    <Logo>static\\icons\\256x256.png</Logo>\n\n    <uap10:PackageIntegrity>\n      <uap10:Content Enforcement=\"on\" />\n    </uap10:PackageIntegrity>\n    <!-- will be used on versions older than 21H1 -->\n    <desktop6:FileSystemWriteVirtualization>disabled</desktop6:FileSystemWriteVirtualization>\n    <!-- will be used on versions 21H1 and newer -->\n    <virtualization:FileSystemWriteVirtualization>\n      <virtualization:ExcludedDirectories>\n        <virtualization:ExcludedDirectory\n        >$(KnownFolder:LocalAppData)\\com.seelen.seelen-ui\\logs</virtualization:ExcludedDirectory>\n        <virtualization:ExcludedDirectory\n        >$(KnownFolder:RoamingAppData)\\com.seelen.seelen-ui</virtualization:ExcludedDirectory>\n      </virtualization:ExcludedDirectories>\n    </virtualization:FileSystemWriteVirtualization>\n\n    <!-- will be used on versions older than 21H1 -->\n    <desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>\n    <!-- will be used on versions 21H1 and newer -->\n    <virtualization:RegistryWriteVirtualization>\n      <virtualization:ExcludedKeys>\n        <virtualization:ExcludedKey>HKEY_CURRENT_USER\\Control Panel\\NotifyIconSettings</virtualization:ExcludedKey>\n      </virtualization:ExcludedKeys>\n    </virtualization:RegistryWriteVirtualization>\n  </Properties>\n\n  <Resources>\n    <Resource Language=\"en-us\" />\n  </Resources>\n\n  <Dependencies>\n    <TargetDeviceFamily\n      Name=\"Windows.Desktop\"\n      MinVersion=\"10.0.17763.0\"\n      MaxVersionTested=\"10.0.22000.1\"\n    />\n  </Dependencies>\n\n  <Applications>\n    <Application\n      Id=\"App\"\n      Executable=\"seelen-ui.exe\"\n      EntryPoint=\"Windows.FullTrustApplication\"\n    >\n      <uap3:VisualElements\n        DisplayName=\"Seelen UI\"\n        Description=\"Fully Customizable Desktop Enviroment for Windows.\"\n        Square150x150Logo=\"static\\icons\\128x128.png\"\n        Square44x44Logo=\"static\\icons\\48x48.png\"\n        BackgroundColor=\"transparent\"\n        AppListEntry=\"default\"\n        VisualGroup=\"Seelen\"\n      >\n      </uap3:VisualElements>\n\n      <Extensions>\n        <uap5:Extension Category=\"windows.appExecutionAlias\">\n          <uap5:AppExecutionAlias>\n            <uap5:ExecutionAlias Alias=\"seelen-ui.exe\" />\n          </uap5:AppExecutionAlias>\n        </uap5:Extension>\n\n        <uap:Extension Category=\"windows.fileTypeAssociation\">\n          <uap:FileTypeAssociation Name=\"seelen-ui.file\">\n            <uap:Logo>static\\icons\\128x128.png</uap:Logo>\n            <uap:DisplayName>Seelen UI</uap:DisplayName>\n            <uap:SupportedFileTypes>\n              <uap:FileType>.slu</uap:FileType>\n            </uap:SupportedFileTypes>\n          </uap:FileTypeAssociation>\n        </uap:Extension>\n\n        <uap10:Extension Category=\"windows.protocol\">\n          <uap10:Protocol Name=\"seelen-ui.uri\">\n            <uap10:Logo>static\\icons\\128x128.png</uap10:Logo>\n            <uap10:DisplayName>Seelen UI</uap10:DisplayName>\n          </uap10:Protocol>\n        </uap10:Extension>\n      </Extensions>\n    </Application>\n\n    <Application\n      Id=\"Service\"\n      Executable=\"slu-service.exe\"\n      EntryPoint=\"Windows.FullTrustApplication\"\n    >\n      <uap3:VisualElements\n        DisplayName=\"SLU Service\"\n        Description=\"Service Helper for Seelen UI\"\n        Square150x150Logo=\"static\\icons\\128x128.png\"\n        Square44x44Logo=\"static\\icons\\48x48.png\"\n        BackgroundColor=\"transparent\"\n        AppListEntry=\"default\"\n        VisualGroup=\"Seelen\"\n      >\n      </uap3:VisualElements>\n\n      <Extensions>\n        <uap5:Extension Category=\"windows.appExecutionAlias\">\n          <uap5:AppExecutionAlias>\n            <uap5:ExecutionAlias Alias=\"slu-service.exe\" />\n          </uap5:AppExecutionAlias>\n        </uap5:Extension>\n\n        <uap10:Extension Category=\"windows.protocol\">\n          <uap10:Protocol Name=\"slu-service.uri\">\n            <uap10:Logo>static\\icons\\128x128.png</uap10:Logo>\n            <uap10:DisplayName>Seelen UI Service</uap10:DisplayName>\n          </uap10:Protocol>\n        </uap10:Extension>\n      </Extensions>\n    </Application>\n  </Applications>\n\n  <Capabilities>\n    <uap3:Capability Name=\"userNotificationListener\" />\n    <uap7:Capability Name=\"globalMediaControl\" />\n    <rescap:Capability Name=\"runFullTrust\" />\n    <rescap:Capability Name=\"unvirtualizedResources\" />\n    <DeviceCapability Name=\"radios\" />\n    <DeviceCapability Name=\"bluetooth\" />\n    <DeviceCapability Name=\"wiFiControl\" />\n  </Capabilities>\n</Package>\n"
  },
  {
    "path": "src/templates/installer-hooks.nsh",
    "content": "!macro NSIS_HOOK_PREINSTALL\n  StrCpy $1 \"taskkill.exe /F /T /IM slu-service.exe\"\n  DetailPrint 'Exec: $1'\n  nsExec::Exec $1\n  Pop $0\n\n  StrCpy $1 \"taskkill.exe /F /T /IM seelen-ui.exe\"\n  DetailPrint 'Exec: $1'\n  nsExec::Exec $1\n  Pop $0\n\n  ; Clean static folder to remove assets from previous versions\n  DetailPrint 'Cleaning static folder from previous installation...'\n  RMDir /r \"$INSTDIR\\static\"\n\n  DetailPrint 'Cleaning webview2 runtime from previous installation...'\n  RMDir /r \"$INSTDIR\\runtime\"\n\n  File /a \"${__FILEDIR__}\\..\\..\\sluhk.dll\"\n  File /a \"${__FILEDIR__}\\..\\..\\SHA256SUMS\"\n  File /a \"${__FILEDIR__}\\..\\..\\SHA256SUMS.sig\"\n\n  ; Include PDB file only for nightly builds\n  ${StrLoc} $0 \"${VERSION}\" \"nightly\" \">\"\n  ${If} $0 != \"\"\n    File /a \"${__FILEDIR__}\\..\\..\\seelen_ui.pdb\"\n  ${EndIf}\n!macroend\n\n!macro NSIS_HOOK_POSTINSTALL\n  ; Install the service\n  DetailPrint 'Exec: slu-service.exe install'\n  nsExec::Exec '\"$INSTDIR\\slu-service.exe\" install'\n  Pop $0\n  ; Refresh file associations icons\n  !insertmacro UPDATEFILEASSOC\n!macroend\n\n!macro NSIS_HOOK_PREUNINSTALL\n  ; Gracefully stop the service\n  DetailPrint 'Exec: slu-service.exe stop'\n  nsExec::Exec '\"$INSTDIR\\slu-service.exe\" stop'\n  Pop $0\n  ; Remove the service\n  DetailPrint 'Exec: slu-service.exe uninstall'\n  nsExec::Exec '\"$INSTDIR\\slu-service.exe\" uninstall'\n  Pop $0\n!macroend\n\n!macro NSIS_HOOK_POSTUNINSTALL\n  Delete \"$INSTDIR\\sluhk.dll\"\n  Delete \"$INSTDIR\\SHA256SUMS\"\n  Delete \"$INSTDIR\\SHA256SUMS.sig\"\n  Delete \"$INSTDIR\\seelen_ui.pdb\"\n\n  ; Refresh file associations icons\n  !insertmacro UPDATEFILEASSOC\n!macroend"
  },
  {
    "path": "src/templates/installer.nsi",
    "content": "Unicode true\nManifestDPIAware true\n; Add in `dpiAwareness` `PerMonitorV2` to manifest for Windows 10 1607+ (note this should not affect lower versions since they should be able to ignore this and pick up `dpiAware` `true` set by `ManifestDPIAware true`)\n; Currently undocumented on NSIS's website but is in the Docs folder of source tree, see\n; https://github.com/kichik/nsis/blob/5fc0b87b819a9eec006df4967d08e522ddd651c9/Docs/src/attributes.but#L286-L300\n; https://github.com/tauri-apps/tauri/pull/10106\nManifestDPIAwareness PerMonitorV2\n\n!if \"{{compression}}\" == \"none\"\n  SetCompress off\n!else\n  ; Set the compression algorithm. We default to LZMA.\n  SetCompressor /SOLID \"{{compression}}\"\n!endif\n\n!include MUI2.nsh\n!include FileFunc.nsh\n!include x64.nsh\n!include WordFunc.nsh\n!include \"utils.nsh\"\n!include \"FileAssociation.nsh\"\n!include \"Win\\COM.nsh\"\n!include \"Win\\Propkey.nsh\"\n!include \"StrFunc.nsh\"\n${StrCase}\n${StrLoc}\n\n{{#if installer_hooks}}\n!include \"{{installer_hooks}}\"\n{{/if}}\n\n!define WEBVIEW2APPGUID \"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}\"\n\n!define MANUFACTURER \"{{manufacturer}}\"\n!define PRODUCTNAME \"{{product_name}}\"\n!define VERSION \"{{version}}\"\n!define VERSIONWITHBUILD \"{{version_with_build}}\"\n!define HOMEPAGE \"{{homepage}}\"\n!define INSTALLMODE \"{{install_mode}}\"\n!define LICENSE \"{{license}}\"\n!define INSTALLERICON \"{{installer_icon}}\"\n!define SIDEBARIMAGE \"{{sidebar_image}}\"\n!define HEADERIMAGE \"{{header_image}}\"\n!define MAINBINARYNAME \"{{main_binary_name}}\"\n!define MAINBINARYSRCPATH \"{{main_binary_path}}\"\n!define BUNDLEID \"{{bundle_id}}\"\n!define COPYRIGHT \"{{copyright}}\"\n!define OUTFILE \"{{out_file}}\"\n!define ARCH \"{{arch}}\"\n!define ADDITIONALPLUGINSPATH \"{{additional_plugins_path}}\"\n!define ALLOWDOWNGRADES \"{{allow_downgrades}}\"\n!define DISPLAYLANGUAGESELECTOR \"{{display_language_selector}}\"\n!define INSTALLWEBVIEW2MODE \"{{install_webview2_mode}}\"\n!define WEBVIEW2INSTALLERARGS \"{{webview2_installer_args}}\"\n!define WEBVIEW2BOOTSTRAPPERPATH \"{{webview2_bootstrapper_path}}\"\n!define WEBVIEW2INSTALLERPATH \"{{webview2_installer_path}}\"\n!define MINIMUMWEBVIEW2VERSION \"{{minimum_webview2_version}}\"\n!define UNINSTKEY \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${PRODUCTNAME}\"\n!define MANUKEY \"Software\\${MANUFACTURER}\"\n!define MANUPRODUCTKEY \"${MANUKEY}\\${PRODUCTNAME}\"\n!define UNINSTALLERSIGNCOMMAND \"{{uninstaller_sign_cmd}}\"\n!define ESTIMATEDSIZE \"{{estimated_size}}\"\n!define STARTMENUFOLDER \"{{start_menu_folder}}\"\n\nVar PassiveMode\nVar UpdateMode\nVar NoShortcutMode\nVar WixMode\nVar OldMainBinaryName\n\nName \"${PRODUCTNAME}\"\nBrandingText \"${COPYRIGHT}\"\nOutFile \"${OUTFILE}\"\n\n; We don't actually use this value as default install path,\n; it's just for nsis to append the product name folder in the directory selector\n; https://nsis.sourceforge.io/Reference/InstallDir\n!define PLACEHOLDER_INSTALL_DIR \"placeholder\\${MANUFACTURER}\\${PRODUCTNAME}\"\nInstallDir \"${PLACEHOLDER_INSTALL_DIR}\"\n\nVIProductVersion \"${VERSIONWITHBUILD}\"\nVIAddVersionKey \"ProductName\" \"${PRODUCTNAME}\"\nVIAddVersionKey \"FileDescription\" \"${PRODUCTNAME}\"\nVIAddVersionKey \"LegalCopyright\" \"${COPYRIGHT}\"\nVIAddVersionKey \"FileVersion\" \"${VERSION}\"\nVIAddVersionKey \"ProductVersion\" \"${VERSION}\"\n\n# additional plugins\n!addplugindir \"${ADDITIONALPLUGINSPATH}\"\n\n; Uninstaller signing command\n!if \"${UNINSTALLERSIGNCOMMAND}\" != \"\"\n  !uninstfinalize '${UNINSTALLERSIGNCOMMAND}'\n!endif\n\n; Handle install mode, `perUser`, `perMachine` or `both`\n!if \"${INSTALLMODE}\" == \"perMachine\"\n  RequestExecutionLevel admin\n!endif\n\n!if \"${INSTALLMODE}\" == \"currentUser\"\n  RequestExecutionLevel user\n!endif\n\n!if \"${INSTALLMODE}\" == \"both\"\n  !define MULTIUSER_MUI\n  !define MULTIUSER_INSTALLMODE_INSTDIR \"${MANUFACTURER}\\${PRODUCTNAME}\" ;slu\n  !define MULTIUSER_INSTALLMODE_COMMANDLINE\n  !if \"${ARCH}\" == \"x64\"\n    !define MULTIUSER_USE_PROGRAMFILES64\n  !else if \"${ARCH}\" == \"arm64\"\n    !define MULTIUSER_USE_PROGRAMFILES64\n  !endif\n  !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY \"${UNINSTKEY}\"\n  !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME \"CurrentUser\"\n  !define MULTIUSER_INSTALLMODEPAGE_SHOWUSERNAME\n  !define MULTIUSER_INSTALLMODE_FUNCTION RestorePreviousInstallLocation\n  !define MULTIUSER_EXECUTIONLEVEL Highest\n  !include MultiUser.nsh\n!endif\n\n; Installer & Unistaller icon\n!if \"${INSTALLERICON}\" != \"\"\n  !define MUI_ICON \"${INSTALLERICON}\"\n  !define MUI_UNICON \"${INSTALLERICON}\" ;slu\n!endif\n\n; Installer sidebar image\n!if \"${SIDEBARIMAGE}\" != \"\"\n  !define MUI_WELCOMEFINISHPAGE_BITMAP \"${SIDEBARIMAGE}\"\n!endif\n\n; Installer header image\n!if \"${HEADERIMAGE}\" != \"\"\n  !define MUI_HEADERIMAGE\n  !define MUI_HEADERIMAGE_BITMAP  \"${HEADERIMAGE}\"\n!endif\n\n; Define registry key to store installer language\n!define MUI_LANGDLL_REGISTRY_ROOT \"HKCU\"\n!define MUI_LANGDLL_REGISTRY_KEY \"${MANUPRODUCTKEY}\"\n!define MUI_LANGDLL_REGISTRY_VALUENAME \"Installer Language\"\n\n; Installer pages, must be ordered as they appear\n!define MUI_BGCOLOR 222228\n!define MUI_TEXTCOLOR fdfdfd\n!define MUI_FINISHPAGE_TEXT_COLOR fdfdfd\n\n; 1. Welcome Page\n!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive\n!insertmacro MUI_PAGE_WELCOME\n\n; 2. License Page (if defined)\n!if \"${LICENSE}\" != \"\"\n  !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive\n  !insertmacro MUI_PAGE_LICENSE \"${LICENSE}\"\n!endif\n\n; 3. Install mode (if it is set to `both`)\n!if \"${INSTALLMODE}\" == \"both\"\n  !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive\n  !insertmacro MULTIUSER_PAGE_INSTALLMODE\n!endif\n\n; 4. Custom page to ask user if he wants to reinstall/uninstall\n;    only if a previous installation was detected\nVar ReinstallPageCheck\nPage custom PageReinstall PageLeaveReinstall\nFunction PageReinstall\n  ; Uninstall previous WiX installation if exists.\n  ;\n  ; A WiX installer stores the installation info in registry\n  ; using a UUID and so we have to loop through all keys under\n  ; `HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall`\n  ; and check if `DisplayName` and `Publisher` keys match ${PRODUCTNAME} and ${MANUFACTURER}\n  ;\n  ; This has a potential issue that there maybe another installation that matches\n  ; our ${PRODUCTNAME} and ${MANUFACTURER} but wasn't installed by our WiX installer,\n  ; however, this should be fine since the user will have to confirm the uninstallation\n  ; and they can chose to abort it if doesn't make sense.\n  StrCpy $0 0\n  wix_loop:\n    EnumRegKey $1 HKLM \"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" $0\n    StrCmp $1 \"\" wix_loop_done ; Exit loop if there is no more keys to loop on\n    IntOp $0 $0 + 1\n    ReadRegStr $R0 HKLM \"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$1\" \"DisplayName\"\n    ReadRegStr $R1 HKLM \"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$1\" \"Publisher\"\n    StrCmp \"$R0$R1\" \"${PRODUCTNAME}${MANUFACTURER}\" 0 wix_loop\n    ReadRegStr $R0 HKLM \"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$1\" \"UninstallString\"\n    ${StrCase} $R1 $R0 \"L\"\n    ${StrLoc} $R0 $R1 \"msiexec\" \">\"\n    StrCmp $R0 0 0 wix_loop_done\n    StrCpy $WixMode 1\n    StrCpy $R6 \"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$1\"\n    Goto compare_version\n  wix_loop_done:\n\n  ; Check if there is an existing installation, if not, abort the reinstall page\n  ReadRegStr $R0 SHCTX \"${UNINSTKEY}\" \"\"\n  ReadRegStr $R1 SHCTX \"${UNINSTKEY}\" \"UninstallString\"\n  ${IfThen} \"$R0$R1\" == \"\" ${|} Abort ${|}\n\n  ; Compare this installar version with the existing installation\n  ; and modify the messages presented to the user accordingly\n  compare_version:\n  StrCpy $R4 \"$(older)\"\n  ${If} $WixMode = 1\n    ReadRegStr $R0 HKLM \"$R6\" \"DisplayVersion\"\n  ${Else}\n    ReadRegStr $R0 SHCTX \"${UNINSTKEY}\" \"DisplayVersion\"\n  ${EndIf}\n  ${IfThen} $R0 == \"\" ${|} StrCpy $R4 \"$(unknown)\" ${|}\n\n  nsis_tauri_utils::SemverCompare \"${VERSION}\" $R0\n  Pop $R0\n  ; Reinstalling the same version\n  ${If} $R0 = 0\n    StrCpy $R1 \"$(alreadyInstalledLong)\"\n    StrCpy $R2 \"$(addOrReinstall)\"\n    StrCpy $R3 \"$(uninstallApp)\"\n    !insertmacro MUI_HEADER_TEXT \"$(alreadyInstalled)\" \"$(chooseMaintenanceOption)\"\n  ; Upgrading\n  ${ElseIf} $R0 = 1\n    StrCpy $R1 \"$(olderOrUnknownVersionInstalled)\"\n    StrCpy $R2 \"$(uninstallBeforeInstalling)\"\n    StrCpy $R3 \"$(dontUninstall)\"\n    !insertmacro MUI_HEADER_TEXT \"$(alreadyInstalled)\" \"$(choowHowToInstall)\"\n  ; Downgrading\n  ${ElseIf} $R0 = -1\n    StrCpy $R1 \"$(newerVersionInstalled)\"\n    StrCpy $R2 \"$(uninstallBeforeInstalling)\"\n    !if \"${ALLOWDOWNGRADES}\" == \"true\"\n      StrCpy $R3 \"$(dontUninstall)\"\n    !else\n      StrCpy $R3 \"$(dontUninstallDowngrade)\"\n    !endif\n    !insertmacro MUI_HEADER_TEXT \"$(alreadyInstalled)\" \"$(choowHowToInstall)\"\n  ${Else}\n    Abort\n  ${EndIf}\n\n  ; Skip showing the page if passive\n  ;\n  ; Note that we don't call this earlier at the begining\n  ; of this function because we need to populate some variables\n  ; related to current installed version if detected and whether\n  ; we are downgrading or not.\n  ${If} $PassiveMode = 1\n    Call PageLeaveReinstall\n  ${Else}\n    nsDialogs::Create 1018\n    Pop $R4\n    ${IfThen} $(^RTL) = 1 ${|} nsDialogs::SetRTL $(^RTL) ${|}\n\n    ${NSD_CreateLabel} 0 0 100% 24u $R1\n    Pop $R1\n\n    ${NSD_CreateRadioButton} 30u 50u -30u 8u $R2\n    Pop $R2\n    ${NSD_OnClick} $R2 PageReinstallUpdateSelection\n\n    ${NSD_CreateRadioButton} 30u 70u -30u 8u $R3\n    Pop $R3\n    ; Disable this radio button if downgrading and downgrades are disabled\n    !if \"${ALLOWDOWNGRADES}\" == \"false\"\n      ${IfThen} $R0 = -1 ${|} EnableWindow $R3 0 ${|}\n    !endif\n    ${NSD_OnClick} $R3 PageReinstallUpdateSelection\n\n    ; Check the first radio button if this the first time\n    ; we enter this page or if the second button wasn't\n    ; selected the last time we were on this page\n    ${If} $ReinstallPageCheck <> 2\n      SendMessage $R2 ${BM_SETCHECK} ${BST_CHECKED} 0\n    ${Else}\n      SendMessage $R3 ${BM_SETCHECK} ${BST_CHECKED} 0\n    ${EndIf}\n\n    ${NSD_SetFocus} $R2\n    nsDialogs::Show\n  ${EndIf}\nFunctionEnd\nFunction PageReinstallUpdateSelection\n  ${NSD_GetState} $R2 $R1\n  ${If} $R1 == ${BST_CHECKED}\n    StrCpy $ReinstallPageCheck 1\n  ${Else}\n    StrCpy $ReinstallPageCheck 2\n  ${EndIf}\nFunctionEnd\nFunction PageLeaveReinstall\n  ${NSD_GetState} $R2 $R1\n\n  ; If migrating from Wix, always uninstall\n  ${If} $WixMode = 1\n    Goto reinst_uninstall\n  ${EndIf}\n\n  ; In update mode, always proceeds without uninstalling\n  ${If} $UpdateMode = 1\n    Goto reinst_done\n  ${EndIf}\n\n  ; $R0 holds whether same(0)/upgrading(1)/downgrading(-1) version\n  ; $R1 holds the radio buttons state:\n  ;   1 => first choice was selected\n  ;   0 => second choice was selected\n  ${If} $R0 = 0 ; Same version, proceed\n    ${If} $R1 = 1              ; User chose to add/reinstall\n      Goto reinst_done\n    ${Else}                    ; User chose to uninstall\n      Goto reinst_uninstall\n    ${EndIf}\n  ${ElseIf} $R0 = 1 ; Upgrading\n    ${If} $R1 = 1              ; User chose to uninstall\n      Goto reinst_uninstall\n    ${Else}\n      Goto reinst_done         ; User chose NOT to uninstall\n    ${EndIf}\n  ${ElseIf} $R0 = -1 ; Downgrading\n    ${If} $R1 = 1              ; User chose to uninstall\n      Goto reinst_uninstall\n    ${Else}\n      Goto reinst_done         ; User chose NOT to uninstall\n    ${EndIf}\n  ${EndIf}\n\n  reinst_uninstall:\n    HideWindow\n    ClearErrors\n\n    ${If} $WixMode = 1\n      ReadRegStr $R1 HKLM \"$R6\" \"UninstallString\"\n      ExecWait '$R1' $0\n    ${Else}\n      ReadRegStr $4 SHCTX \"${MANUPRODUCTKEY}\" \"\"\n      ReadRegStr $R1 SHCTX \"${UNINSTKEY}\" \"UninstallString\"\n      ${IfThen} $UpdateMode = 1 ${|} StrCpy $R1 \"$R1 /UPDATE\" ${|} ; append /UPDATE\n      ${IfThen} $PassiveMode = 1 ${|} StrCpy $R1 \"$R1 /P\" ${|} ; append /P\n      StrCpy $R1 \"$R1 _?=$4\" ; append uninstall directory\n      ExecWait '$R1' $0\n    ${EndIf}\n\n    BringToFront\n\n    ${IfThen} ${Errors} ${|} StrCpy $0 2 ${|} ; ExecWait failed, set fake exit code\n\n    ${If} $0 <> 0\n    ${OrIf} ${FileExists} \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n      ; User cancelled wix uninstaller? return to select un/reinstall page\n      ${If} $WixMode = 1\n      ${AndIf} $0 = 1602\n        Abort\n      ${EndIf}\n\n      ; User cancelled NSIS uninstaller? return to select un/reinstall page\n      ${If} $0 = 1\n        Abort\n      ${EndIf}\n\n      ; Other erros? show generic error message and return to select un/reinstall page\n      MessageBox MB_ICONEXCLAMATION \"$(unableToUninstall)\"\n      Abort\n    ${EndIf}\n  reinst_done:\nFunctionEnd\n\n; 5. Choose install directory page\n!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive\n!insertmacro MUI_PAGE_DIRECTORY\n\n; 6. Start menu shortcut page\nVar AppStartMenuFolder\n!if \"${STARTMENUFOLDER}\" != \"\"\n  !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive\n  !define MUI_STARTMENUPAGE_DEFAULTFOLDER \"${STARTMENUFOLDER}\"\n!else\n  !define MUI_PAGE_CUSTOMFUNCTION_PRE Skip\n!endif\n!insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder\n\n; 7. Installation page\n!insertmacro MUI_PAGE_INSTFILES\n\n; 8. Finish page\n;\n; Don't auto jump to finish page after installation page,\n; because the installation page has useful info that can be used debug any issues with the installer.\n!define MUI_FINISHPAGE_NOAUTOCLOSE\n; Show discord link\n!define MUI_FINISHPAGE_LINK_COLOR 59a7f6\n!define MUI_FINISHPAGE_LINK \"Join us on Discord!\"\n!define MUI_FINISHPAGE_LINK_LOCATION \"https://discord.gg/ABfASx5ZAJ\"\n!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive\n!define MUI_PAGE_CUSTOMFUNCTION_LEAVE RunMainBinary\n!insertmacro MUI_PAGE_FINISH\n\nFunction RunMainBinary\n  ; Start the service\n  DetailPrint 'Exec: slu-service.exe'\n  Exec '\"$INSTDIR\\slu-service.exe\"'\n  ; nsis_tauri_utils::RunAsUser \"$INSTDIR\\${MAINBINARYNAME}.exe\" \"\"\nFunctionEnd\n\n; Uninstaller Pages\n; 1. Confirm uninstall page\nVar DeleteAppDataCheckbox\nVar DeleteAppDataCheckboxState\n!define /ifndef WS_EX_LAYOUTRTL         0x00400000\n!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.ConfirmShow\nFunction un.ConfirmShow ; Add add a `Delete app data` check box\n  ; $1 inner dialog HWND\n  ; $2 window DPI\n  ; $3 style\n  ; $4 x\n  ; $5 y\n  ; $6 width\n  ; $7 height\n  FindWindow $1 \"#32770\" \"\" $HWNDPARENT ; Find inner dialog\n  System::Call \"user32::GetDpiForWindow(p r1) i .r2\"\n  ${If} $(^RTL) = 1\n    StrCpy $3 \"${__NSD_CheckBox_EXSTYLE} | ${WS_EX_LAYOUTRTL}\"\n    IntOp $4 50 * $2\n  ${Else}\n    StrCpy $3 \"${__NSD_CheckBox_EXSTYLE}\"\n    IntOp $4 0 * $2\n  ${EndIf}\n  IntOp $5 100 * $2\n  IntOp $6 400 * $2\n  IntOp $7 25 * $2\n  IntOp $4 $4 / 96\n  IntOp $5 $5 / 96\n  IntOp $6 $6 / 96\n  IntOp $7 $7 / 96\n  System::Call 'user32::CreateWindowEx(i r3, w \"${__NSD_CheckBox_CLASS}\", w \"$(deleteAppData)\", i ${__NSD_CheckBox_STYLE}, i r4, i r5, i r6, i r7, p r1, i0, i0, i0) i .s'\n  Pop $DeleteAppDataCheckbox\n  SendMessage $HWNDPARENT ${WM_GETFONT} 0 0 $1\n  SendMessage $DeleteAppDataCheckbox ${WM_SETFONT} $1 1\nFunctionEnd\n!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.ConfirmLeave\nFunction un.ConfirmLeave\n  SendMessage $DeleteAppDataCheckbox ${BM_GETCHECK} 0 0 $DeleteAppDataCheckboxState\nFunctionEnd\n!define MUI_PAGE_CUSTOMFUNCTION_PRE un.SkipIfPassive\n!insertmacro MUI_UNPAGE_CONFIRM\n\n; 2. Uninstalling Page\n!insertmacro MUI_UNPAGE_INSTFILES\n\n;Languages\n{{#each languages}}\n!insertmacro MUI_LANGUAGE \"{{this}}\"\n{{/each}}\n!insertmacro MUI_RESERVEFILE_LANGDLL\n{{#each language_files}}\n  !include \"{{this}}\"\n{{/each}}\n\nFunction .onInit\n  ${GetOptions} $CMDLINE \"/P\" $PassiveMode\n  ${IfNot} ${Errors}\n    StrCpy $PassiveMode 1\n  ${EndIf}\n\n  ${GetOptions} $CMDLINE \"/NS\" $NoShortcutMode\n  ${IfNot} ${Errors}\n    StrCpy $NoShortcutMode 1\n  ${EndIf}\n\n  ${GetOptions} $CMDLINE \"/UPDATE\" $UpdateMode\n  ${IfNot} ${Errors}\n    StrCpy $UpdateMode 1\n  ${EndIf}\n\n  !if \"${DISPLAYLANGUAGESELECTOR}\" == \"true\"\n    !insertmacro MUI_LANGDLL_DISPLAY\n  !endif\n\n  !insertmacro SetContext\n\n  ${If} $INSTDIR == \"${PLACEHOLDER_INSTALL_DIR}\"\n    ; Set default install location\n    !if \"${INSTALLMODE}\" == \"perMachine\"\n      ${If} ${RunningX64}\n        !if \"${ARCH}\" == \"x64\"\n          StrCpy $INSTDIR \"$PROGRAMFILES64\\${MANUFACTURER}\\${PRODUCTNAME}\"\n        !else if \"${ARCH}\" == \"arm64\"\n          StrCpy $INSTDIR \"$PROGRAMFILES64\\${MANUFACTURER}\\${PRODUCTNAME}\"\n        !else\n          StrCpy $INSTDIR \"$PROGRAMFILES\\${MANUFACTURER}\\${PRODUCTNAME}\"\n        !endif\n      ${Else}\n        StrCpy $INSTDIR \"$PROGRAMFILES\\${MANUFACTURER}\\${PRODUCTNAME}\"\n      ${EndIf}\n    !else if \"${INSTALLMODE}\" == \"currentUser\"\n      StrCpy $INSTDIR \"$LOCALAPPDATA\\${MANUFACTURER}\\${PRODUCTNAME}\"\n    !endif\n\n    Call RestorePreviousInstallLocation\n  ${EndIf}\n\n\n  !if \"${INSTALLMODE}\" == \"both\"\n    !insertmacro MULTIUSER_INIT\n  !endif\nFunctionEnd\n\n\nSection EarlyChecks\n  ; Abort silent installer if downgrades is disabled\n  !if \"${ALLOWDOWNGRADES}\" == \"false\"\n  ${If} ${Silent}\n    ; If downgrading\n    ${If} $R0 = -1\n      System::Call 'kernel32::AttachConsole(i -1)i.r0'\n      ${If} $0 <> 0\n        System::Call 'kernel32::GetStdHandle(i -11)i.r0'\n        System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color\n        FileWrite $0 \"$(silentDowngrades)\"\n      ${EndIf}\n      Abort\n    ${EndIf}\n  ${EndIf}\n  !endif\n\nSectionEnd\n\nSection WebView2\n  ; Check if Webview2 is already installed and skip this section\n  ${If} ${RunningX64}\n    ReadRegStr $4 HKLM \"SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\${WEBVIEW2APPGUID}\" \"pv\"\n  ${Else}\n    ReadRegStr $4 HKLM \"SOFTWARE\\Microsoft\\EdgeUpdate\\Clients\\${WEBVIEW2APPGUID}\" \"pv\"\n  ${EndIf}\n  ${If} $4 == \"\"\n    ReadRegStr $4 HKCU \"SOFTWARE\\Microsoft\\EdgeUpdate\\Clients\\${WEBVIEW2APPGUID}\" \"pv\"\n  ${EndIf}\n\n  ${If} $4 == \"\"\n    ; Webview2 installation\n    ;\n    ; Skip if updating\n    ${If} $UpdateMode <> 1\n      !if \"${INSTALLWEBVIEW2MODE}\" == \"downloadBootstrapper\"\n        Delete \"$TEMP\\MicrosoftEdgeWebview2Setup.exe\"\n        DetailPrint \"$(webview2Downloading)\"\n        NSISdl::download \"https://go.microsoft.com/fwlink/p/?LinkId=2124703\" \"$TEMP\\MicrosoftEdgeWebview2Setup.exe\"\n        Pop $0\n        ${If} $0 == \"success\"\n          DetailPrint \"$(webview2DownloadSuccess)\"\n        ${Else}\n          DetailPrint \"$(webview2DownloadError)\"\n          Abort \"$(webview2AbortError)\"\n        ${EndIf}\n        StrCpy $6 \"$TEMP\\MicrosoftEdgeWebview2Setup.exe\"\n        Goto install_webview2\n      !endif\n\n      !if \"${INSTALLWEBVIEW2MODE}\" == \"embedBootstrapper\"\n        Delete \"$TEMP\\MicrosoftEdgeWebview2Setup.exe\"\n        File \"/oname=$TEMP\\MicrosoftEdgeWebview2Setup.exe\" \"${WEBVIEW2BOOTSTRAPPERPATH}\"\n        DetailPrint \"$(installingWebview2)\"\n        StrCpy $6 \"$TEMP\\MicrosoftEdgeWebview2Setup.exe\"\n        Goto install_webview2\n      !endif\n\n      !if \"${INSTALLWEBVIEW2MODE}\" == \"offlineInstaller\"\n        Delete \"$TEMP\\MicrosoftEdgeWebView2RuntimeInstaller.exe\"\n        File \"/oname=$TEMP\\MicrosoftEdgeWebView2RuntimeInstaller.exe\" \"${WEBVIEW2INSTALLERPATH}\"\n        DetailPrint \"$(installingWebview2)\"\n        StrCpy $6 \"$TEMP\\MicrosoftEdgeWebView2RuntimeInstaller.exe\"\n        Goto install_webview2\n      !endif\n\n      Goto webview2_done\n\n      install_webview2:\n        DetailPrint \"$(installingWebview2)\"\n        ; $6 holds the path to the webview2 installer\n        ExecWait \"$6 ${WEBVIEW2INSTALLERARGS} /install\" $1\n        ${If} $1 = 0\n          DetailPrint \"$(webview2InstallSuccess)\"\n        ${Else}\n          DetailPrint \"$(webview2InstallError)\"\n          Abort \"$(webview2AbortError)\"\n        ${EndIf}\n      webview2_done:\n    ${EndIf}\n  ${Else}\n    !if \"${MINIMUMWEBVIEW2VERSION}\" != \"\"\n      ${VersionCompare} \"${MINIMUMWEBVIEW2VERSION}\" \"$4\" $R0\n      ${If} $R0 = 1\n        update_webview:\n          DetailPrint \"$(installingWebview2)\"\n          ${If} ${RunningX64}\n            ReadRegStr $R1 HKLM \"SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\" \"path\"\n          ${Else}\n            ReadRegStr $R1 HKLM \"SOFTWARE\\Microsoft\\EdgeUpdate\" \"path\"\n          ${EndIf}\n          ${If} $R1 == \"\"\n            ReadRegStr $R1 HKCU \"SOFTWARE\\Microsoft\\EdgeUpdate\" \"path\"\n          ${EndIf}\n          ${If} $R1 != \"\"\n            ; Chromium updater docs: https://source.chromium.org/chromium/chromium/src/+/main:docs/updater/user_manual.md\n            ; Modified from \"HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft EdgeWebView\\ModifyPath\"\n            ExecWait `\"$R1\" /install appguid=${WEBVIEW2APPGUID}&needsadmin=true` $1\n            ${If} $1 = 0\n              DetailPrint \"$(webview2InstallSuccess)\"\n            ${Else}\n              MessageBox MB_ICONEXCLAMATION|MB_ABORTRETRYIGNORE \"$(webview2InstallError)\" IDIGNORE ignore IDRETRY update_webview\n              Quit\n              ignore:\n            ${EndIf}\n          ${EndIf}\n      ${EndIf}\n    !endif\n  ${EndIf}\nSectionEnd\n\nSection Install\n  SetOutPath $INSTDIR\n\n  !ifmacrodef NSIS_HOOK_PREINSTALL\n    !insertmacro NSIS_HOOK_PREINSTALL\n  !endif\n\n  !insertmacro CheckIfAppIsRunning \"${MAINBINARYNAME}.exe\" \"${PRODUCTNAME}\"\n\n  ; Copy main executable\n  File \"${MAINBINARYSRCPATH}\"\n\n  ; Copy resources\n  {{#each resources_dirs}}\n    CreateDirectory \"$INSTDIR\\\\{{this}}\"\n  {{/each}}\n  {{#each resources}}\n    File /a \"/oname={{this.[1]}}\" \"{{no-escape @key}}\"\n  {{/each}}\n\n  ; Copy external binaries\n  {{#each binaries}}\n    File /a \"/oname={{this}}\" \"{{no-escape @key}}\"\n  {{/each}}\n\n  ; Create file associations\n  {{#each file_associations as |association| ~}}\n    {{#each association.ext as |ext| ~}}\n       !insertmacro APP_ASSOCIATE \"{{ext}}\" \"{{or association.name ext}}\" \"{{association-description association.description ext}}\" \"$INSTDIR\\${MAINBINARYNAME}.exe,0\" \"Open with ${PRODUCTNAME}\" \"$INSTDIR\\${MAINBINARYNAME}.exe $\\\"%1$\\\"\"\n    {{/each}}\n  {{/each}}\n\n  ; Register deep links\n  {{#each deep_link_protocols as |protocol| ~}}\n    WriteRegStr SHCTX \"Software\\Classes\\\\{{protocol}}\" \"URL Protocol\" \"\"\n    WriteRegStr SHCTX \"Software\\Classes\\\\{{protocol}}\" \"\" \"URL:${BUNDLEID} protocol\"\n    WriteRegStr SHCTX \"Software\\Classes\\\\{{protocol}}\\DefaultIcon\" \"\" \"$\\\"$INSTDIR\\${MAINBINARYNAME}.exe$\\\",0\"\n    WriteRegStr SHCTX \"Software\\Classes\\\\{{protocol}}\\shell\\open\\command\" \"\" \"$\\\"$INSTDIR\\${MAINBINARYNAME}.exe$\\\" $\\\"%1$\\\"\"\n  {{/each}}\n\n  ; Create uninstaller\n  WriteUninstaller \"$INSTDIR\\uninstall.exe\"\n\n  ; Save $INSTDIR in registry for future installations\n  WriteRegStr SHCTX \"${MANUPRODUCTKEY}\" \"\" $INSTDIR\n\n  !if \"${INSTALLMODE}\" == \"both\"\n    ; Save install mode to be selected by default for the next installation such as updating\n    ; or when uninstalling\n    WriteRegStr SHCTX \"${UNINSTKEY}\" $MultiUser.InstallMode 1\n  !endif\n\n  ; Remove old main binary if it doesn't match new main binary name\n  ReadRegStr $OldMainBinaryName SHCTX \"${UNINSTKEY}\" \"MainBinaryName\"\n  ${If} $OldMainBinaryName != \"\"\n  ${AndIf} $OldMainBinaryName != \"${MAINBINARYNAME}.exe\"\n    Delete \"$INSTDIR\\$OldMainBinaryName\"\n  ${EndIf}\n\n  ; Save current MAINBINARYNAME for future updates\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"MainBinaryName\" \"${MAINBINARYNAME}.exe\"\n\n  ; Registry information for add/remove programs\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"DisplayName\" \"${PRODUCTNAME}\"\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"DisplayIcon\" \"$\\\"$INSTDIR\\${MAINBINARYNAME}.exe$\\\"\"\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"DisplayVersion\" \"${VERSION}\"\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"Publisher\" \"${MANUFACTURER}\"\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"InstallLocation\" \"$\\\"$INSTDIR$\\\"\"\n  WriteRegStr SHCTX \"${UNINSTKEY}\" \"UninstallString\" \"$\\\"$INSTDIR\\uninstall.exe$\\\"\"\n  WriteRegDWORD SHCTX \"${UNINSTKEY}\" \"NoModify\" \"1\"\n  WriteRegDWORD SHCTX \"${UNINSTKEY}\" \"NoRepair\" \"1\"\n\n  ${GetSize} \"$INSTDIR\" \"/M=uninstall.exe /S=0K /G=0\" $0 $1 $2\n  IntOp $0 $0 + ${ESTIMATEDSIZE}\n  IntFmt $0 \"0x%08X\" $0\n  WriteRegDWORD SHCTX \"${UNINSTKEY}\" \"EstimatedSize\" \"$0\"\n\n  !if \"${HOMEPAGE}\" != \"\"\n    WriteRegStr SHCTX \"${UNINSTKEY}\" \"URLInfoAbout\" \"${HOMEPAGE}\"\n    WriteRegStr SHCTX \"${UNINSTKEY}\" \"URLUpdateInfo\" \"${HOMEPAGE}\"\n    WriteRegStr SHCTX \"${UNINSTKEY}\" \"HelpLink\" \"${HOMEPAGE}\"\n  !endif\n\n  ; Create start menu shortcut\n  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n    Call CreateOrUpdateStartMenuShortcut\n  !insertmacro MUI_STARTMENU_WRITE_END\n\n  ; Create desktop shortcut for silent and passive installers\n  ; because finish page will be skipped\n  ${If} $PassiveMode = 1\n  ${OrIf} ${Silent}\n    Call CreateOrUpdateDesktopShortcut\n  ${EndIf}\n\n  !ifmacrodef NSIS_HOOK_POSTINSTALL\n    !insertmacro NSIS_HOOK_POSTINSTALL\n  !endif\n\n  ; Auto close this page for passive mode\n  ${If} $PassiveMode = 1\n    SetAutoClose true\n  ${EndIf}\nSectionEnd\n\nFunction .onInstSuccess\n  ; Check for `/R` flag only in silent and passive installers because\n  ; GUI installer has a toggle for the user to (re)start the app\n  ${If} $PassiveMode = 1\n  ${OrIf} ${Silent}\n    ${GetOptions} $CMDLINE \"/R\" $R0\n    ${IfNot} ${Errors}\n      ${GetOptions} $CMDLINE \"/ARGS\" $R0\n      nsis_tauri_utils::RunAsUser \"$INSTDIR\\${MAINBINARYNAME}.exe\" \"$R0\"\n    ${EndIf}\n  ${EndIf}\nFunctionEnd\n\nFunction un.onInit\n  !insertmacro SetContext\n\n  !if \"${INSTALLMODE}\" == \"both\"\n    !insertmacro MULTIUSER_UNINIT\n  !endif\n\n  !insertmacro MUI_UNGETLANGUAGE\n\n  ${GetOptions} $CMDLINE \"/P\" $PassiveMode\n  ${IfNot} ${Errors}\n    StrCpy $PassiveMode 1\n  ${EndIf}\n\n  ${GetOptions} $CMDLINE \"/UPDATE\" $UpdateMode\n  ${IfNot} ${Errors}\n    StrCpy $UpdateMode 1\n  ${EndIf}\nFunctionEnd\n\nSection Uninstall\n\n  !ifmacrodef NSIS_HOOK_PREUNINSTALL\n    !insertmacro NSIS_HOOK_PREUNINSTALL\n  !endif\n\n  !insertmacro CheckIfAppIsRunning \"${MAINBINARYNAME}.exe\" \"${PRODUCTNAME}\"\n\n  ; Delete the app directory and its content from disk\n  ; Copy main executable\n  Delete \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n\n  ; Delete resources\n  {{#each resources}}\n    Delete \"$INSTDIR\\\\{{this.[1]}}\"\n  {{/each}}\n\n  ; Delete external binaries\n  {{#each binaries}}\n    Delete \"$INSTDIR\\\\{{this}}\"\n  {{/each}}\n\n  ; Delete app associations\n  {{#each file_associations as |association| ~}}\n    {{#each association.ext as |ext| ~}}\n      !insertmacro APP_UNASSOCIATE \"{{ext}}\" \"{{or association.name ext}}\"\n    {{/each}}\n  {{/each}}\n\n  ; Delete deep links\n  {{#each deep_link_protocols as |protocol| ~}}\n    ReadRegStr $R7 SHCTX \"Software\\Classes\\\\{{protocol}}\\shell\\open\\command\" \"\"\n    ${If} $R7 == \"$\\\"$INSTDIR\\${MAINBINARYNAME}.exe$\\\" $\\\"%1$\\\"\"\n      DeleteRegKey SHCTX \"Software\\Classes\\\\{{protocol}}\"\n    ${EndIf}\n  {{/each}}\n\n\n  ; Delete uninstaller\n  Delete \"$INSTDIR\\uninstall.exe\"\n\n  {{#each resources_ancestors}}\n  RMDir /REBOOTOK \"$INSTDIR\\\\{{this}}\"\n  {{/each}}\n  RMDir \"$INSTDIR\"\n\n  ; Remove shortcuts if not updating\n  ${If} $UpdateMode <> 1\n    !insertmacro DeleteAppUserModelId\n\n    ; Remove start menu shortcut\n    !insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder\n    !insertmacro IsShortcutTarget \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    Pop $0\n    ${If} $0 = 1\n      !insertmacro UnpinShortcut \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\"\n      Delete \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\"\n      RMDir \"$SMPROGRAMS\\$AppStartMenuFolder\"\n    ${EndIf}\n    !insertmacro IsShortcutTarget \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    Pop $0\n    ${If} $0 = 1\n      !insertmacro UnpinShortcut \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\"\n      Delete \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\"\n    ${EndIf}\n\n    ; Remove desktop shortcuts\n    !insertmacro IsShortcutTarget \"$DESKTOP\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    Pop $0\n    ${If} $0 = 1\n      !insertmacro UnpinShortcut \"$DESKTOP\\${PRODUCTNAME}.lnk\"\n      Delete \"$DESKTOP\\${PRODUCTNAME}.lnk\"\n    ${EndIf}\n  ${EndIf}\n\n  ; Remove registry information for add/remove programs\n  !if \"${INSTALLMODE}\" == \"both\"\n    DeleteRegKey SHCTX \"${UNINSTKEY}\"\n  !else if \"${INSTALLMODE}\" == \"perMachine\"\n    DeleteRegKey HKLM \"${UNINSTKEY}\"\n  !else\n    DeleteRegKey HKCU \"${UNINSTKEY}\"\n  !endif\n\n  ; Removes the Autostart entry for ${PRODUCTNAME} from the HKCU Run key if it exists.\n  ; This ensures the program does not launch automatically after uninstallation if it exists.\n  ; If it doesn't exist, it does nothing.\n  ; We do this when not updating (to preserve the registry value on updates)\n  ${If} $UpdateMode <> 1\n    DeleteRegValue HKCU \"Software\\Microsoft\\Windows\\CurrentVersion\\Run\" \"${PRODUCTNAME}\"\n  ${EndIf}\n\n  ; Delete app data if the checkbox is selected\n  ; and if not updating\n  ${If} $DeleteAppDataCheckboxState = 1\n  ${AndIf} $UpdateMode <> 1\n    ; Clear the install location $INSTDIR from registry\n    DeleteRegKey SHCTX \"${MANUPRODUCTKEY}\"\n    DeleteRegKey /ifempty SHCTX \"${MANUKEY}\"\n\n    ; Clear the install language from registry\n    DeleteRegValue HKCU \"${MANUPRODUCTKEY}\" \"Installer Language\"\n    DeleteRegKey /ifempty HKCU \"${MANUPRODUCTKEY}\"\n    DeleteRegKey /ifempty HKCU \"${MANUKEY}\"\n\n    SetShellVarContext current\n    RmDir /r \"$APPDATA\\${BUNDLEID}\"\n    RmDir /r \"$LOCALAPPDATA\\${BUNDLEID}\"\n  ${EndIf}\n\n  !ifmacrodef NSIS_HOOK_POSTUNINSTALL\n    !insertmacro NSIS_HOOK_POSTUNINSTALL\n  !endif\n\n  ; Auto close if passive mode or updating\n  ${If} $PassiveMode = 1\n  ${OrIf} $UpdateMode = 1\n    SetAutoClose true\n  ${EndIf}\nSectionEnd\n\nFunction RestorePreviousInstallLocation\n  ReadRegStr $4 SHCTX \"${MANUPRODUCTKEY}\" \"\"\n  StrCmp $4 \"\" +2 0\n    StrCpy $INSTDIR $4\nFunctionEnd\n\nFunction Skip\n  Abort\nFunctionEnd\n\nFunction SkipIfPassive\n  ${IfThen} $PassiveMode = 1  ${|} Abort ${|}\nFunctionEnd\nFunction un.SkipIfPassive\n  ${IfThen} $PassiveMode = 1  ${|} Abort ${|}\nFunctionEnd\n\nFunction CreateOrUpdateStartMenuShortcut\n  ; We used to use product name as MAINBINARYNAME\n  ; migrate old shortcuts to target the new MAINBINARYNAME\n  StrCpy $R0 0\n\n  !insertmacro IsShortcutTarget \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\$OldMainBinaryName\"\n  Pop $0\n  ${If} $0 = 1\n    !insertmacro SetShortcutTarget \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    StrCpy $R0 1\n  ${EndIf}\n\n  !insertmacro IsShortcutTarget \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\$OldMainBinaryName\"\n  Pop $0\n  ${If} $0 = 1\n    !insertmacro SetShortcutTarget \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    StrCpy $R0 1\n  ${EndIf}\n\n  ${If} $R0 = 1\n    Return\n  ${EndIf}\n\n  ; Skip creating shortcut if in update mode or no shortcut mode\n  ; but always create if migrating from wix\n  ${If} $WixMode = 0\n    ${If} $UpdateMode = 1\n    ${OrIf} $NoShortcutMode = 1\n      Return\n    ${EndIf}\n  ${EndIf}\n\n  !if \"${STARTMENUFOLDER}\" != \"\"\n    CreateDirectory \"$SMPROGRAMS\\$AppStartMenuFolder\"\n    CreateShortcut \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    !insertmacro SetLnkAppUserModelId \"$SMPROGRAMS\\$AppStartMenuFolder\\${PRODUCTNAME}.lnk\"\n  !else\n    CreateShortcut \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    !insertmacro SetLnkAppUserModelId \"$SMPROGRAMS\\${PRODUCTNAME}.lnk\"\n  !endif\nFunctionEnd\n\nFunction CreateOrUpdateDesktopShortcut\n  ; We used to use product name as MAINBINARYNAME\n  ; migrate old shortcuts to target the new MAINBINARYNAME\n  !insertmacro IsShortcutTarget \"$DESKTOP\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\$OldMainBinaryName\"\n  Pop $0\n  ${If} $0 = 1\n    !insertmacro SetShortcutTarget \"$DESKTOP\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n    Return\n  ${EndIf}\n\n  ; Skip creating shortcut if in update mode or no shortcut mode\n  ; but always create if migrating from wix\n  ${If} $WixMode = 0\n    ${If} $UpdateMode = 1\n    ${OrIf} $NoShortcutMode = 1\n      Return\n    ${EndIf}\n  ${EndIf}\n\n  CreateShortcut \"$DESKTOP\\${PRODUCTNAME}.lnk\" \"$INSTDIR\\${MAINBINARYNAME}.exe\"\n  !insertmacro SetLnkAppUserModelId \"$DESKTOP\\${PRODUCTNAME}.lnk\"\nFunctionEnd"
  },
  {
    "path": "src/ui/globals.d.ts",
    "content": "declare module \"real-react-dom/server\" {\n  export function renderToStaticMarkup(element: any): string;\n}\n\ndeclare module \"*.module.css\" {\n  const classnames: Record<string, string>;\n  export default classnames;\n}\n\ndeclare module \"*.module.scss\" {\n  const classnames: Record<string, string>;\n  export default classnames;\n}\n\ndeclare module \"*.yml\" {\n  export default string;\n}\n\ninterface ObjectConstructor {\n  keys<T>(o: T): (T extends any ? keyof T : PropertyKey)[];\n}\n\ninterface Window {\n  __TAURI_INTERNALS__: {\n    metadata?: {\n      currentWebview?: {\n        label?: string;\n      };\n    };\n    invoke: any;\n  };\n  __SLU_WIDGET: import(\"@seelen-ui/lib/types\").Widget;\n}\n\ntype anyObject = Record<PropertyKey, any>;\n"
  },
  {
    "path": "src/ui/react/popup/app.tsx",
    "content": "import { signal } from \"@preact/signals\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport type { SluPopupConfig, SluPopupContent as ISluPopupContent } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon\";\n\nconst currentWidget = Widget.getCurrent();\nconst state = signal<SluPopupConfig>({\n  width: 0,\n  height: 0,\n  title: [],\n  content: [],\n  footer: [],\n});\n\ninvoke(SeelenCommand.GetPopupConfig, {\n  instanceId: currentWidget.decoded.instanceId!,\n})\n  .then(async (data) => {\n    state.value = data;\n    currentWidget.window.setTitle(getOnlyText(data.title));\n    await currentWidget.show();\n    await currentWidget.focus();\n  })\n  .catch((err) => {\n    console.error(err);\n    closePopup();\n  });\n\nsubscribe(SeelenEvent.PopupContentChanged, (e) => {\n  state.value = e.payload;\n  currentWidget.window.setTitle(getOnlyText(e.payload.title));\n});\n\nfunction closePopup() {\n  currentWidget.webview.close();\n}\n\nexport function App() {\n  return (\n    <div className=\"popup\">\n      <header data-tauri-drag-region className=\"header\">\n        <div className=\"header-content\">\n          {state.value.title.map((subEntry, idx) => <SluPopupContent key={idx} entry={subEntry} />)}\n        </div>\n        <button className=\"header-close\" onClick={closePopup}>\n          <Icon iconName=\"CgClose\" />\n        </button>\n      </header>\n\n      <main className=\"content\">\n        {state.value.content.map((subEntry, idx) => <SluPopupContent key={idx} entry={subEntry} />)}\n      </main>\n\n      <footer className=\"footer\">\n        {state.value.footer.map((subEntry, idx) => <SluPopupContent key={idx} entry={subEntry} />)}\n      </footer>\n    </div>\n  );\n}\n\nfunction SluPopupContent({ entry }: { entry: ISluPopupContent }) {\n  switch (entry.type) {\n    case \"text\":\n      return (\n        <p className=\"text\" style={entry.styles || {}}>\n          {entry.value}\n        </p>\n      );\n    case \"icon\":\n      return (\n        <Icon\n          className=\"icon\"\n          iconName={entry.name as any}\n          style={entry.styles || {}}\n        />\n      );\n    case \"image\":\n      return (\n        <img\n          className=\"image\"\n          src={entry.href}\n          style={entry.styles || {}}\n          alt={entry.href}\n        />\n      );\n    case \"button\":\n      return (\n        <button\n          className=\"button\"\n          onClick={() => {\n            if (entry.onClick === \"exit\") {\n              closePopup();\n              return;\n            }\n            currentWidget.webview.emitTo(\n              currentWidget.webview.label,\n              `${entry.onClick}`,\n            );\n          }}\n          style={entry.styles || {}}\n        >\n          {entry.inner.map((subEntry, idx) => <SluPopupContent key={idx} entry={subEntry} />)}\n        </button>\n      );\n    case \"group\":\n      return (\n        <div className=\"group\" style={entry.styles || {}}>\n          {entry.items.map((subEntry, idx) => <SluPopupContent key={idx} entry={subEntry} />)}\n        </div>\n      );\n    default:\n      return null;\n  }\n}\n\nfunction getOnlyText(content: ISluPopupContent[]) {\n  let text = \"\";\n  for (const entry of content) {\n    if (entry.type === \"text\") {\n      text += `${entry.value} `;\n    }\n\n    if (entry.type === \"group\") {\n      text += getOnlyText(entry.items);\n      text += \" \";\n    }\n  }\n  return text;\n}\n"
  },
  {
    "path": "src/ui/react/popup/global.css",
    "content": "body {\n  background-color: transparent !important;\n  overflow: hidden;\n}\n\n#root {\n  width: 100vw;\n  height: 100vh;\n}\n\n.popup {\n  width: 100%;\n  height: 100%;\n  display: grid;\n  grid-template-rows: min-content 1fr min-content;\n  overflow-wrap: anywhere;\n\n  .header {\n    width: 100%;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    background-color: #fff9;\n    padding: 6px 6px 6px 10px;\n    font-weight: 600;\n    app-region: drag;\n\n    button {\n      app-region: no-drag;\n    }\n\n    .header-content {\n      display: flex;\n      flex-direction: column;\n      gap: 4px;\n    }\n\n    .header-close {\n      padding: 4px;\n      border-radius: 4px;\n      transition-property: background-color, color;\n      transition-duration: 0.2s;\n      transition-timing-function: ease-in-out;\n\n      &:hover {\n        background-color: var(--color-red-900);\n        color: var(--color-gray-200);\n      }\n    }\n  }\n\n  .content {\n    width: 100%;\n    padding: 12px 16px;\n    overflow: auto;\n  }\n\n  .footer {\n    width: 100%;\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    padding: 10px;\n    gap: 8px;\n  }\n\n  .group {\n    display: flex;\n    gap: 10px;\n  }\n\n  .button {\n    height: 24px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 4px;\n    padding: 0 8px;\n    background-color: var(--system-accent-color);\n    color: var(--color-fixed-white);\n    border-radius: 6px;\n    transition: background-color 0.2s ease-in-out;\n    font-size: 0.9rem;\n    line-height: 1.2em;\n\n    &:hover {\n      filter: brightness(1.1);\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/popup/index.tsx",
    "content": "import { Widget } from \"@seelen-ui/lib\";\nimport { getRootContainer } from \"libs/ui/react/utils/index\";\nimport { createRoot } from \"react-dom/client\";\n\nimport { App } from \"./app.tsx\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\nimport \"./global.css\";\n\nawait Widget.getCurrent().init();\n\nconst container = getRootContainer();\ncreateRoot(container).render(<App />);\n"
  },
  {
    "path": "src/ui/react/popup/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/react/settings/app.tsx",
    "content": "import { useDarkMode } from \"libs/ui/react/utils/styling.ts\";\nimport { ConfigProvider, theme } from \"antd\";\nimport { useEffect } from \"react\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport { Routing } from \"./router.tsx\";\nimport { ThumbnailGeneratorModal } from \"./components/ThumbnailGeneratorModal/index.tsx\";\nimport { WelcomeModal } from \"./components/WelcomeModal/infra.tsx\";\nimport { uiColors } from \"./state/system.ts\";\n\nexport function App() {\n  const isDarkMode = useDarkMode();\n\n  useEffect(() => {\n    setTimeout(() => {\n      let splashscreen = document.getElementById(\"splashscreen\");\n      splashscreen?.classList.add(\"vanish\");\n      setTimeout(() => splashscreen?.classList.add(\"hidden\"), 300);\n    }, 300);\n\n    Widget.self.ready();\n  }, []);\n\n  return (\n    <ConfigProvider\n      componentSize=\"small\"\n      theme={{\n        token: {\n          colorPrimary: isDarkMode ? uiColors.value.accent_light : uiColors.value.accent_dark,\n        },\n        algorithm: isDarkMode ? theme.darkAlgorithm : theme.defaultAlgorithm,\n      }}\n    >\n      <Routing />\n      <ThumbnailGeneratorModal />\n      <WelcomeModal />\n    </ConfigProvider>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/SettingsBox/index.module.css",
    "content": ".group {\n  flex: 1;\n  background-color: light-dark(rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.1));\n  border-radius: var(--config-border-radius);\n  padding: var(--spacing-s);\n  overflow: hidden;\n\n  &:not(:last-child) {\n    margin-bottom: var(--spacing-s);\n  }\n\n  .content {\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-s);\n  }\n\n  .setting {\n    display: grid;\n    grid-template-columns: 1fr auto;\n    gap: var(--spacing-s);\n    align-items: center;\n\n    &.disabled {\n      opacity: 0.6;\n    }\n\n    :global(.ant-input) {\n      max-width: 150px;\n    }\n\n    :global(.ant-slider) {\n      margin: 0;\n    }\n\n    :global(.ant-slider-horizontal) {\n      width: 100%;\n    }\n\n    .info {\n      height: 100%;\n      grid-column: 1 / 2;\n      display: flex;\n      flex-direction: column;\n\n      .label {\n        gap: var(--spacing-2xs);\n        display: flex;\n        align-items: center;\n        font-weight: 600;\n      }\n\n      .description {\n        color: var(--color-gray-600);\n        font-size: 0.8rem;\n        line-height: 1.4em;\n\n        a {\n          color: var(--color-blue-500);\n        }\n      }\n    }\n\n    .action {\n      grid-column: 2 / 3;\n      width: 100%;\n      min-width: 150px;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: flex-end;\n      gap: var(--spacing-xs);\n    }\n  }\n\n  .subgroup {\n    .subtitle {\n      margin-bottom: var(--spacing-xs);\n      font-size: 1rem;\n      line-height: 1.4em;\n      font-weight: 600;\n    }\n\n    .content {\n      padding-left: var(--spacing-s);\n      border-left: 1.5px solid light-dark(#0002, #fff2);\n\n      .label {\n        font-weight: normal;\n      }\n    }\n  }\n\n  :global(.ant-select) {\n    min-width: 150px;\n  }\n\n  :global(.ant-input),\n  :global(.ant-input-number) {\n    min-width: 90px;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/SettingsBox/index.tsx",
    "content": "import { Icon } from \"libs/ui/react/components/Icon\";\nimport { ConfigProvider, Tooltip } from \"antd\";\nimport type { ComponentChildren } from \"preact\";\n\nimport cs from \"./index.module.css\";\nimport { cx } from \"libs/ui/react/utils/styling\";\n\ninterface Props {\n  children: React.ReactNode;\n}\n\nexport const SettingsGroup = ({ children }: Props) => {\n  return (\n    <div className={cs.group}>\n      <div className={cs.content}>{children}</div>\n    </div>\n  );\n};\n\ninterface SubGroupProps {\n  children: React.ReactNode;\n  label: React.ReactNode;\n  disabled?: boolean;\n}\n\nexport const SettingsSubGroup = ({ children, label, disabled }: SubGroupProps) => {\n  return (\n    <div className={cs.subgroup}>\n      <div className={cs.subtitle}>{label}</div>\n      <ConfigProvider componentDisabled={disabled}>\n        <div className={cs.content}>{children}</div>\n      </ConfigProvider>\n    </div>\n  );\n};\n\ntype OptionProps = {\n  label?: ComponentChildren;\n  tip?: ComponentChildren;\n  description?: ComponentChildren;\n  action?: ComponentChildren;\n  children?: ComponentChildren;\n  disabled?: boolean;\n};\n\nexport const SettingsOption = (props: OptionProps) => {\n  return (\n    <div className={cx(cs.setting, { [cs.disabled!]: props.disabled })}>\n      {props.children\n        ? (\n          props.children\n        )\n        : (\n          <>\n            <div className={cs.info}>\n              <div className={cs.label}>\n                {props.label}\n                {props.tip && (\n                  <Tooltip title={props.tip}>\n                    <Icon iconName=\"HiOutlineInformationCircle\" />\n                  </Tooltip>\n                )}\n              </div>\n              {props.description && <div className={cs.description}>{props.description}</div>}\n            </div>\n            <div className={cs.action}>{props.action}</div>\n          </>\n        )}\n    </div>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/components/SortableSelector/index.module.css",
    "content": ".container {\n  height: 100%;\n  max-height: 400px;\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  grid-template-rows: 1fr;\n  gap: var(--spacing-s);\n}\n\n.box {\n  width: 100%;\n  display: grid;\n  grid-template-rows: 40px 1fr;\n  grid-template-columns: 1fr;\n  background-color: #0001;\n  border-radius: 8px;\n  overflow: hidden;\n\n  .header {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border-bottom: 1px solid #fff5;\n    font-weight: 600;\n  }\n\n  .list {\n    padding: var(--spacing-s);\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-s);\n    overflow-y: auto;\n  }\n}\n\n.item {\n  padding: var(--spacing-xs);\n  border-radius: var(--config-border-radius);\n  background-color: var(--color-white);\n  cursor: grab;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/SortableSelector/index.tsx",
    "content": "import { DragDropProvider, DragOverlay, useDroppable } from \"@dnd-kit/react\";\nimport { useSortable } from \"@dnd-kit/react/sortable\";\nimport { arrayMove } from \"@dnd-kit/helpers\";\nimport { throttle } from \"lodash\";\nimport type { ComponentChildren } from \"preact\";\nimport { useMemo } from \"preact/hooks\";\nimport { genericHandleDragOver } from \"../../../../../../libs/ui/react/utils/DndKit/utils.ts\";\n\nimport cs from \"./index.module.css\";\n\ninterface Props<T> {\n  disabled?: boolean;\n  options: { label: ComponentChildren; value: T }[];\n  enabled: T[];\n  onChange: (enabled: T[]) => void;\n}\n\nexport function VerticalSortableSelect<T extends string>({\n  options,\n  enabled,\n  onChange,\n  disabled = false,\n}: Props<T>) {\n  const enabledOpts = options\n    .filter(({ value }) => enabled.includes(value))\n    .toSorted((a, b) => enabled.indexOf(a.value) - enabled.indexOf(b.value));\n  const disabledOpts = options.filter(({ value }) => !enabled.includes(value));\n\n  const containers = [\n    {\n      id: \"enabled\" as T,\n      items: enabledOpts.map(({ value }) => value),\n    },\n    {\n      id: \"disabled\" as T,\n      items: disabledOpts.map(({ value }) => value),\n    },\n  ];\n\n  const _handleDragOver = useMemo(() => throttle(genericHandleDragOver<T>, 100), []);\n  function handleDragOver(event: any) {\n    _handleDragOver(event, containers, (newContainers) => {\n      const enabledIds = newContainers.find((c) => c.id === \"enabled\")?.items ?? [];\n      onChange(enabledIds);\n    });\n  }\n\n  function handleDragEnd(event: any) {\n    const { source, target } = event.operation;\n    if (!target || source.id === target.id || event.canceled) return;\n\n    const oldPos = enabled.indexOf(source.id as T);\n    const newPos = enabled.indexOf(target.id as T);\n    const newEnabled = arrayMove(enabled, oldPos, newPos).filter(Boolean);\n    onChange(newEnabled);\n  }\n\n  return (\n    <DragDropProvider\n      onDragOver={handleDragOver}\n      onDragEnd={handleDragEnd}\n    >\n      <div className={cs.container}>\n        {containers.map(({ id, items }) => (\n          <div className={cs.box}>\n            <div className={cs.header}>{id === \"enabled\" ? \"Enabled\" : \"Disabled\"}</div>\n            <DndDropableAndSortableContainer key={id} id={id} items={items} className={cs.list}>\n              {items.map((id, index) => (\n                <Entry key={id} value={id} disabled={disabled} index={index}>\n                  <div className={cs.item}>{options.find(({ value }) => value === id)?.label}</div>\n                </Entry>\n              ))}\n            </DndDropableAndSortableContainer>\n          </div>\n        ))}\n        <DragOverlay>\n          {(source) => {\n            const opt = options.find(({ value }) => value === source.id);\n            return opt ? <div className={cs.item}>{opt.label}</div> : null;\n          }}\n        </DragOverlay>\n      </div>\n    </DragDropProvider>\n  );\n}\n\nfunction DndDropableAndSortableContainer({\n  id,\n  className,\n  children,\n}: {\n  id: string;\n  items: string[];\n  className?: string;\n  children: ComponentChildren;\n}) {\n  const droppable = useDroppable({ id });\n\n  return (\n    <div ref={droppable.ref} className={className}>\n      {children}\n    </div>\n  );\n}\n\nfunction Entry({\n  value,\n  children,\n  disabled,\n  index,\n}: {\n  value: string;\n  children: ComponentChildren;\n  disabled: boolean;\n  index: number;\n}) {\n  const sortable = useSortable({\n    id: value,\n    index,\n    disabled,\n  });\n\n  let opacity = 1;\n\n  if (sortable.isDragging) {\n    opacity = 0.1;\n  }\n\n  if (disabled) {\n    opacity = 0.6;\n  }\n\n  return (\n    <div ref={sortable.ref} style={{ opacity }}>\n      {children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/ThumbnailGeneratorModal/index.tsx",
    "content": "import { Button, Flex, List, Modal, Progress } from \"antd\";\nimport { useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { $corruptedWallpapers } from \"../../modules/shared/signals.ts\";\nimport { generateThumbnails, getVideosWithoutThumbnail, type ThumbGenerationProgress } from \"./thumbnailGenerator.ts\";\nimport { useSignal } from \"@preact/signals\";\nimport { wallpapers } from \"../../state/resources.ts\";\n\nexport function ThumbnailGeneratorModal() {\n  const { t } = useTranslation();\n\n  const open = useSignal(false);\n  const progress = useSignal<ThumbGenerationProgress | null>(null);\n\n  useEffect(() => {\n    // Do nothing if already processing\n    if (open.value) {\n      return;\n    }\n\n    const videosWithoutThumbnail = getVideosWithoutThumbnail(wallpapers.value);\n\n    if (videosWithoutThumbnail.length > 0) {\n      open.value = true;\n      progress.value = null;\n\n      generateThumbnails(videosWithoutThumbnail, (newProgress) => {\n        progress.value = newProgress;\n      });\n    }\n  }, [wallpapers, progress.value, open.value]);\n\n  const percent = progress.value && progress.value.total > 0\n    ? Math.round((progress.value.current / progress.value.total) * 100)\n    : 0;\n\n  // Get corrupted wallpaper names\n  const corruptedWallpapers = wallpapers.value.filter((w) => $corruptedWallpapers.value.has(w.id));\n  const isFinished = percent === 100;\n\n  return (\n    <Modal\n      open={open.value}\n      title={isFinished ? t(\"wall.thumbnail_generation_complete\") : t(\"wall.generating_thumbnails\")}\n      footer={null}\n      closable={isFinished}\n      onCancel={() => {\n        open.value = false;\n      }}\n      centered\n    >\n      {!isFinished\n        ? (\n          <Flex vertical align=\"center\" justify=\"center\" gap=\"16px\">\n            <p>\n              {t(\"wall.processing_video\")} {`${(progress.value?.current || 0) + 1} / ${progress.value?.total || 0}`}\n            </p>\n            <p>{progress.value?.currentVideoName}</p>\n            <Progress percent={percent} status=\"active\" />\n          </Flex>\n        )\n        : (\n          <Flex vertical gap=\"16px\">\n            <p>{t(\"wall.thumbnail_generation_finished\")}</p>\n\n            {corruptedWallpapers.length > 0 && (\n              <Flex vertical gap=\"8px\">\n                <p style={{ color: \"var(--color-red-900)\", fontWeight: \"bold\" }}>\n                  {t(\"wall.corrupted_wallpapers_message\")}\n                </p>\n                <List\n                  size=\"small\"\n                  bordered\n                  dataSource={corruptedWallpapers}\n                  renderItem={(wallpaper) => {\n                    const displayName = typeof wallpaper.metadata.displayName === \"string\"\n                      ? wallpaper.metadata.displayName\n                      : wallpaper.metadata.displayName.en ||\n                        Object.values(wallpaper.metadata.displayName)[0] ||\n                        wallpaper.filename ||\n                        \"Unknown\";\n                    return <List.Item style={{ color: \"var(--color-red-900)\" }}>{displayName}</List.Item>;\n                  }}\n                />\n              </Flex>\n            )}\n\n            <Flex justify=\"flex-end\">\n              <Button\n                type=\"primary\"\n                onClick={() => {\n                  open.value = false;\n                }}\n              >\n                {t(\"close\")}\n              </Button>\n            </Flex>\n          </Flex>\n        )}\n    </Modal>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/ThumbnailGeneratorModal/thumbnailGenerator.ts",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport type { Wallpaper } from \"@seelen-ui/lib/types\";\nimport { WallpaperKind } from \"@seelen-ui/lib/types\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\n\nimport { extractThumbnailFromSource } from \"./videoThumbnail.ts\";\nimport { $corruptedWallpapers } from \"../../modules/shared/signals.ts\";\n\nexport interface ThumbGenerationProgress {\n  current: number;\n  total: number;\n  currentVideoName: string | null;\n}\n\nexport type ProgressCallback = (progress: ThumbGenerationProgress) => void;\n\n/**\n * Gets a display name from ResourceText\n */\nfunction getDisplayName(wallpaper: Wallpaper): string {\n  const displayName = wallpaper.metadata.displayName;\n  if (typeof displayName === \"string\") {\n    return displayName;\n  }\n  // If it's an object, try to get the English translation or first available\n  return displayName.en || Object.values(displayName)[0] || wallpaper.filename || \"Unknown\";\n}\n\n/**\n * Filters video wallpapers that don't have a thumbnail\n * Excludes corrupted wallpapers that failed extraction during this session\n */\nexport function getVideosWithoutThumbnail(wallpapers: Wallpaper[]): Wallpaper[] {\n  const corruptedIds = $corruptedWallpapers.value;\n  return wallpapers.filter(\n    (wallpaper) =>\n      wallpaper.type === WallpaperKind.Video &&\n      !wallpaper.thumbnailFilename &&\n      !wallpaper.thumbnailUrl &&\n      wallpaper.filename && // Only process if it has a local file\n      !corruptedIds.has(wallpaper.id), // Skip corrupted wallpapers\n  );\n}\n\n/**\n * Generates thumbnails for video wallpapers one by one\n * @param wallpapers - List of wallpapers to process (should be video wallpapers without thumbnails)\n * @param onProgress - Callback to report progress\n * @returns Promise that resolves when all thumbnails are generated\n */\nexport async function generateThumbnails(\n  wallpapers: Wallpaper[],\n  onProgress: ProgressCallback,\n): Promise<void> {\n  const total = wallpapers.length;\n\n  for (let i = 0; i < wallpapers.length; i++) {\n    const wallpaper = wallpapers[i];\n    if (!wallpaper) {\n      continue;\n    }\n\n    onProgress({\n      current: i,\n      total,\n      currentVideoName: getDisplayName(wallpaper),\n    });\n\n    try {\n      if (!wallpaper.filename) {\n        console.warn(`Wallpaper ${wallpaper.id} has no filename, skipping`);\n        continue;\n      }\n\n      const videoSrc = convertFileSrc(wallpaper.metadata.path + \"\\\\\" + wallpaper.filename);\n      const thumbnailBytes = await extractThumbnailFromSource(videoSrc, 0.9);\n      if (!thumbnailBytes) {\n        console.error(`Failed to extract thumbnail for ${wallpaper.id}`);\n        // Mark as corrupted - won't retry during this session\n        $corruptedWallpapers.value = new Set($corruptedWallpapers.value).add(wallpaper.id);\n        continue;\n      }\n\n      await invoke(SeelenCommand.WallpaperSaveThumbnail, {\n        wallpaperId: wallpaper.id,\n        thumbnailBytes: Array.from(thumbnailBytes),\n      });\n    } catch (error) {\n      console.error(`Error generating thumbnail for ${wallpaper.id}:`, error);\n      // Mark as corrupted - won't retry during this session\n      $corruptedWallpapers.value = new Set($corruptedWallpapers.value).add(wallpaper.id);\n    }\n  }\n\n  onProgress({\n    current: total,\n    total,\n    currentVideoName: null,\n  });\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/ThumbnailGeneratorModal/videoThumbnail.ts",
    "content": "/**\n * Extracts the first frame of a video as a thumbnail image\n * @param videoElement - The video element to extract the frame from\n * @param quality - JPEG quality (0-1), default 0.9\n * @returns Promise with the thumbnail as Uint8Array bytes\n */\nexport async function extractVideoThumbnail(\n  videoElement: HTMLVideoElement,\n  quality: number = 0.9,\n): Promise<Uint8Array | null> {\n  try {\n    // Wait for video to load metadata if not already loaded\n    if (videoElement.readyState < HTMLMediaElement.HAVE_METADATA) {\n      await new Promise<void>((resolve, reject) => {\n        const handleLoaded = () => {\n          videoElement.removeEventListener(\"loadedmetadata\", handleLoaded);\n          videoElement.removeEventListener(\"error\", handleError);\n          resolve();\n        };\n        const handleError = () => {\n          videoElement.removeEventListener(\"loadedmetadata\", handleLoaded);\n          videoElement.removeEventListener(\"error\", handleError);\n          reject(new Error(\"Failed to load video metadata\"));\n        };\n        videoElement.addEventListener(\"loadedmetadata\", handleLoaded);\n        videoElement.addEventListener(\"error\", handleError);\n      });\n    }\n\n    // Seek to first frame (1 second to ensure we get a good frame)\n    videoElement.currentTime = 1;\n\n    // Wait for seek to complete\n    await new Promise<void>((resolve, reject) => {\n      const handleSeeked = () => {\n        videoElement.removeEventListener(\"seeked\", handleSeeked);\n        videoElement.removeEventListener(\"error\", handleError);\n        resolve();\n      };\n      const handleError = () => {\n        videoElement.removeEventListener(\"seeked\", handleSeeked);\n        videoElement.removeEventListener(\"error\", handleError);\n        reject(new Error(\"Failed to seek video\"));\n      };\n      videoElement.addEventListener(\"seeked\", handleSeeked);\n      videoElement.addEventListener(\"error\", handleError);\n    });\n\n    // Create canvas with video dimensions\n    const canvas = document.createElement(\"canvas\");\n    canvas.width = videoElement.videoWidth;\n    canvas.height = videoElement.videoHeight;\n\n    // Draw video frame to canvas\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) {\n      throw new Error(\"Failed to get canvas 2D context\");\n    }\n\n    ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);\n\n    // Convert canvas to blob\n    const blob = await new Promise<Blob | null>((resolve) => {\n      canvas.toBlob((blob) => resolve(blob), \"image/jpeg\", quality);\n    });\n\n    if (!blob) {\n      throw new Error(\"Failed to create thumbnail blob\");\n    }\n\n    // Convert blob to Uint8Array for efficient transfer to Rust\n    const arrayBuffer = await blob.arrayBuffer();\n    return new Uint8Array(arrayBuffer);\n  } catch (error) {\n    console.error(\"Failed to extract video thumbnail:\", error);\n    return null;\n  }\n}\n\n/**\n * Creates a hidden video element to extract thumbnail from a video source\n * @param videoSrc - The video source URL\n * @param quality - JPEG quality (0-1), default 0.9\n * @returns Promise with the thumbnail as Uint8Array bytes\n */\nexport async function extractThumbnailFromSource(\n  videoSrc: string,\n  quality: number = 0.9,\n): Promise<Uint8Array | null> {\n  const video = document.createElement(\"video\");\n  video.style.display = \"none\";\n  video.crossOrigin = \"anonymous\";\n  video.preload = \"metadata\";\n  video.src = videoSrc;\n\n  document.body.appendChild(video);\n\n  try {\n    const thumbnail = await extractVideoThumbnail(video, quality);\n    return thumbnail;\n  } finally {\n    // Cleanup\n    video.pause();\n    video.removeAttribute(\"src\");\n    video.load();\n    document.body.removeChild(video);\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/WelcomeModal/index.module.css",
    "content": ".welcome {\n  :global(.ant-modal-confirm-content) {\n    text-align: justify;\n    b {\n      font-weight: 600;\n    }\n  }\n\n  :global(.ant-modal-title) {\n    font-size: 1.4rem;\n  }\n}\n\n.starRating {\n  display: flex;\n  justify-content: center;\n  gap: var(--spacing-xs);\n  padding: var(--spacing-m);\n\n  .star {\n    cursor: pointer;\n    color: var(--color-gray-400);\n    transition: color 100ms ease-in-out;\n\n    &.filled {\n      color: #ffc107;\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/WelcomeModal/infra.tsx",
    "content": "import { Button, Flex, Modal } from \"antd\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n\nimport cs from \"./index.module.css\";\nimport { useState } from \"preact/hooks\";\nimport { useTranslation } from \"react-i18next\";\nimport { Icon } from \"libs/ui/react/components/Icon\";\nimport { cx } from \"libs/ui/react/utils/styling\";\n\nconst REVIEW_URL = \"ms-windows-store://review/?ProductId=9P67C2D4T9FB&mode=mini\";\nconst ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;\n\nfunction shouldShowReviewModal(): boolean {\n  if (localStorage.getItem(\"alreadyReviewed\")) {\n    return false;\n  }\n\n  const firstSeenStr = localStorage.getItem(\"lastReviewPrompt\");\n  if (!firstSeenStr) {\n    localStorage.setItem(\"lastReviewPrompt\", Date.now().toString());\n    return false;\n  }\n\n  const firstSeen = parseInt(firstSeenStr, 10);\n  return Date.now() - firstSeen >= ONE_WEEK_MS;\n}\n\nfunction StarRating({ onRate }: { onRate: () => void }) {\n  const [hovered, setHovered] = useState(-1);\n\n  return (\n    <div className={cs.starRating}>\n      {[0, 1, 2, 3, 4].map((index) => (\n        <Icon\n          key={index}\n          className={cx(cs.star, { [cs.filled!]: index <= hovered })}\n          iconName=\"FaStar\"\n          onMouseEnter={() => setHovered(index)}\n          onMouseLeave={() => setHovered(-1)}\n          onClick={onRate}\n          size={32}\n        />\n      ))}\n    </div>\n  );\n}\n\nexport const WelcomeModal = () => {\n  const [isNewUser, setIsNewUser] = useState(!localStorage.getItem(\"welcomeShown\"));\n  const [showReviewModal, setShowReviewModal] = useState(\n    () => !isNewUser && shouldShowReviewModal(),\n  );\n\n  const { t } = useTranslation();\n\n  const openReviewAndMark = () => {\n    invoke(SeelenCommand.OpenFile, { path: REVIEW_URL });\n    localStorage.setItem(\"alreadyReviewed\", \"yes\");\n    localStorage.setItem(\"welcomeShown\", \"yes\");\n    setIsNewUser(false);\n    setShowReviewModal(false);\n  };\n\n  return (\n    <>\n      <Modal\n        className={cs.welcome}\n        open={isNewUser}\n        centered\n        closable={false}\n        title={`🎉 ${t(\"welcome.title\")} 🥳`}\n        footer={\n          <Flex justify=\"flex-end\" gap=\"1rem\">\n            <Button type=\"link\" onClick={openReviewAndMark}>\n              {t(\"welcome.give_a_review\")} 😉\n            </Button>\n            <Button\n              type=\"primary\"\n              onClick={() => {\n                setIsNewUser(false);\n                localStorage.setItem(\"welcomeShown\", \"yes\");\n              }}\n            >\n              {t(\"welcome.ok\")}\n            </Button>\n          </Flex>\n        }\n      >\n        <p>{t(\"welcome.message\")}</p>\n        <br />\n        <p>{t(\"welcome.review\")}</p>\n        <br />\n      </Modal>\n\n      <Modal\n        className={cs.welcome}\n        open={showReviewModal}\n        centered\n        closable={false}\n        title={`😄 ${t(\"review_request.title\")}`}\n        footer={\n          <Flex justify=\"flex-end\" gap=\"1rem\">\n            <Button\n              onClick={() => {\n                localStorage.setItem(\"lastReviewPrompt\", Date.now().toString());\n                setShowReviewModal(false);\n              }}\n            >\n              {t(\"review_request.not_now\")}\n            </Button>\n            <Button type=\"primary\" onClick={openReviewAndMark} style={{ minWidth: \"70px\" }}>\n              {t(\"review_request.sure\")}\n            </Button>\n          </Flex>\n        }\n      >\n        <p>{t(\"welcome.review\")}</p>\n        <StarRating onRate={openReviewAndMark} />\n      </Modal>\n    </>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/components/header/ExtraInfo.tsx",
    "content": "import { Tooltip } from \"antd\";\nimport type { AnyComponent } from \"preact\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { RoutePath } from \"../navigation/routes.tsx\";\n\nexport const RouteExtraInfo: { [key: string]: AnyComponent } = {\n  [RoutePath.SettingsByApplication]: () => {\n    const { t } = useTranslation();\n    return (\n      <Tooltip title={t(\"apps_configurations.extra_info\")}>\n        <span>🛈</span>\n      </Tooltip>\n    );\n  },\n};\n"
  },
  {
    "path": "src/ui/react/settings/components/header/UpdateButton.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { Icon } from \"libs/ui/react/components/Icon\";\nimport { Badge, Button, Tooltip } from \"antd\";\nimport { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nexport function UpdateButton() {\n  const [downloading, setDownloading] = useState<boolean>(false);\n  const [update, setUpdate] = useState<boolean>(false);\n\n  const { t } = useTranslation();\n\n  useEffect(() => {\n    invoke(SeelenCommand.CheckForUpdates)\n      .then(setUpdate)\n      .catch(() => setUpdate(false));\n  }, []);\n\n  if (!update) {\n    return null;\n  }\n\n  return (\n    <Tooltip\n      title={downloading ? t(\"update.downloading\") : t(\"update.available\")}\n    >\n      <Button\n        type=\"text\"\n        loading={downloading}\n        onClick={() => {\n          if (!downloading) {\n            setDownloading(true);\n            invoke(SeelenCommand.InstallLastAvailableUpdate).finally(() => setDownloading(false));\n          }\n        }}\n      >\n        <Badge dot>\n          <Icon iconName=\"TbDownload\" />\n        </Badge>\n      </Button>\n    </Tooltip>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/header/index.module.css",
    "content": ".header {\n  height: 100%;\n\n  background-color: light-dark(var(--color-fixed-gray-50), var(--color-fixed-gray-900));\n  border-bottom: 1px solid var(--color-gray-200);\n\n  font-weight: 600;\n  padding: 0 var(--spacing-s);\n  display: flex;\n  display: grid;\n  grid-template-columns: 1fr min-content;\n  align-items: center;\n  gap: var(--spacing-s);\n  app-region: drag;\n\n  button {\n    app-region: no-drag;\n  }\n\n  .title {\n    font-size: 1.2rem;\n    line-height: 1.8rem;\n    font-weight: 600;\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-s);\n    overflow: hidden;\n\n    a:hover {\n      color: var(--color-blue-500);\n      cursor: pointer;\n    }\n\n    .part {\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n    }\n  }\n\n  .actions {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-2xs);\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/header/index.tsx",
    "content": "import { process } from \"@seelen-ui/lib/tauri\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { getCurrentWebviewWindow } from \"@tauri-apps/api/webviewWindow\";\nimport { Button } from \"antd\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { NavLink, useLocation, useSearchParams } from \"react-router\";\n\nimport { shortcutsError } from \"../../modules/shortcuts/application.ts\";\n\nimport { RouteExtraInfo } from \"./ExtraInfo.tsx\";\nimport { UpdateButton } from \"./UpdateButton.tsx\";\nimport cs from \"./index.module.css\";\nimport { themes as themeList, widgets as widgetList } from \"../../state/resources.ts\";\nimport { hasChanges, needRestart, restoreToLastSaved, saveSettings } from \"../../state/mod.ts\";\n\nexport const Header = () => {\n  const location = useLocation();\n  const [searchParams] = useSearchParams();\n  const { t } = useTranslation();\n\n  const SaveOrQuit = async () => {\n    if (hasChanges.value) {\n      await saveSettings();\n      if (needRestart.value) {\n        await process.relaunch();\n      }\n    } else {\n      await getCurrentWebviewWindow().close();\n    }\n  };\n\n  const saveBtnLabel = needRestart.value ? t(\"save_and_restart\") : t(\"save\");\n\n  let label: React.ReactNode = <span>null!?</span>;\n  let parts = location.pathname === \"/\" ? [\"home\"] : location.pathname.split(\"/\").filter(Boolean);\n\n  if (parts[0] === \"widget\") {\n    const widgetId = searchParams.get(\"id\");\n    const widget = widgetList.value.find((w) => w.id === widgetId);\n    label = widget ? <ResourceText text={widget.metadata.displayName} /> : <span>{widgetId}</span>;\n  } else if (parts[0] === \"theme\") {\n    const themeId = searchParams.get(\"id\");\n    const theme = themeList.value.find((t) => t.id === themeId);\n    label = theme ? <ResourceText text={theme.metadata.displayName} /> : <span>{themeId}</span>;\n  } else {\n    if (parts[0] === \"wallpaper\") {\n      parts = [\"resources\", \"wallpaper\", \"config\"];\n    }\n\n    label = parts.map((part, idx) => (\n      <React.Fragment key={part}>\n        {idx !== parts.length - 1\n          ? (\n            <NavLink to={`/${parts.slice(0, idx + 1).join(\"/\")}`} className={cs.part}>\n              {t(`header.labels.${part}`)}\n            </NavLink>\n          )\n          : <span className={cs.part}>{t(`header.labels.${part}`)}</span>}\n        {++idx < parts.length ? \">\" : \"\"}\n      </React.Fragment>\n    ));\n  }\n\n  const ExtraInfo = RouteExtraInfo[location.pathname];\n\n  return (\n    <div className={cs.header} data-tauri-drag-region>\n      <div className={cs.title}>\n        {label}\n        {ExtraInfo && <ExtraInfo />}\n      </div>\n      <div className={cs.actions}>\n        <UpdateButton />\n        <Button\n          style={{ minWidth: 60 }}\n          type=\"default\"\n          danger\n          disabled={!hasChanges.value}\n          onClick={restoreToLastSaved}\n        >\n          {t(\"cancel\")}\n        </Button>\n        <Button\n          style={{ minWidth: 60 }}\n          type=\"primary\"\n          danger={!hasChanges.value}\n          disabled={hasChanges.value && shortcutsError.value.size > 0}\n          onClick={SaveOrQuit}\n        >\n          {hasChanges.value ? saveBtnLabel : t(\"close\")}\n        </Button>\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/components/layout/index.module.css",
    "content": ".layout {\n  width: 100%;\n  height: 100%;\n  display: grid;\n  grid-template-columns: min-content 1fr;\n  grid-template-rows: 50px 1fr;\n\n  .content {\n    height: 100%;\n    background:\n      linear-gradient(217deg, var(--color-red-100), rgba(255, 0, 0, 0) 70%),\n      linear-gradient(127deg, var(--color-blue-100), rgba(0, 0, 255, 0) 70%),\n      linear-gradient(336deg, var(--color-cyan-100), rgba(0, 255, 0, 0) 70%);\n    padding: var(--spacing-s);\n    overflow: auto;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/layout/index.tsx",
    "content": "import { useLayoutEffect, useRef } from \"preact/hooks\";\nimport { Outlet, useLocation } from \"react-router\";\n\nimport { Header } from \"../header/index.tsx\";\nimport { Navigation } from \"../navigation/index.tsx\";\nimport cs from \"./index.module.css\";\n\nexport function Layout() {\n  const location = useLocation();\n  const contentRef = useRef<HTMLDivElement>(null);\n\n  useLayoutEffect(() => {\n    // Scroll to the top of the page when the route changes\n    contentRef.current?.scrollTo({ top: 0, left: 0, behavior: \"instant\" });\n  }, [location.pathname]);\n\n  return (\n    <div className={cs.layout}>\n      <Navigation />\n      <Header />\n      <div ref={contentRef} className={cs.content}>\n        <Outlet />\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/monitor/index.module.css",
    "content": ".monitorContainer {\n  height: 100%;\n  width: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.monitor {\n  border-radius: 12px;\n  background-color: var(--color-fixed-gray-800);\n  padding: var(--spacing-2xs);\n\n  .screen {\n    position: relative;\n    border-radius: 8px;\n    background: linear-gradient(\n      40deg,\n      var(--color-blue-500) 2%,\n      var(--color-blue-300) 60%,\n      var(--color-blue-100) 100%\n    );\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n\n    .wallpaper {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      position: absolute;\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/monitor/index.tsx",
    "content": "import { Wallpaper } from \"libs/ui/react/components/Wallpaper/index.tsx\";\n\nimport cs from \"./index.module.css\";\nimport { $virtual_desktops } from \"../../modules/shared/signals.ts\";\nimport { wallpapers } from \"../../state/resources.ts\";\nimport { settings } from \"../../state/mod.ts\";\n\ninterface Props {\n  monitorId: string;\n  width?: number;\n  height?: number;\n}\n\nexport function Monitor({ monitorId, width = 1920, height = 1080 }: Props) {\n  const wallpaperSettings = settings.value.byWallpaper;\n\n  const monitor = $virtual_desktops.value.monitors[monitorId];\n  const workspace = monitor?.workspaces.find((w) => w.id === monitor.active_workspace);\n\n  const wallpaperId = workspace?.wallpaper;\n  const wallpaper = wallpapers.value.find((w) => w.id === wallpaperId);\n\n  const style: React.CSSProperties = {\n    aspectRatio: `${width} / ${height}`,\n  };\n  if (width > height) {\n    style.width = \"100%\";\n  } else {\n    style.height = \"100%\";\n  }\n\n  return (\n    <div className={cs.monitorContainer}>\n      <div className={cs.monitor} style={style}>\n        <div className={cs.screen}>\n          <Wallpaper\n            definition={wallpaper}\n            config={wallpaperId ? wallpaperSettings[wallpaperId] : undefined}\n            muted\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/navigation/index.module.css",
    "content": ".navigation {\n  grid-row: 1/3;\n  background-color: var(--color-fixed-gray-900);\n  color: var(--color-fixed-gray-50);\n  width: 180px;\n  transition: width 300ms ease, padding 300ms ease;\n\n  display: grid;\n  grid-template-rows: 50px 1fr min-content;\n  grid-template-columns: 1fr;\n\n  &.collapsed {\n    width: 50px;\n  }\n\n  .header {\n    margin: 0 var(--spacing-s);\n    height: 100%;\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-s);\n    border-bottom: 1px solid var(--color-fixed-gray-700);\n\n    > img {\n      width: 24px;\n      transition: width 100ms linear;\n    }\n\n    > h1 {\n      font-size: 1.2rem;\n      font-weight: 600;\n      flex: 1;\n      text-wrap: nowrap;\n    }\n\n    .chevron {\n      height: 20px;\n      width: 20px;\n      padding: var(--spacing-2xs);\n      transition: transform 300ms linear;\n    }\n\n    .collapsed & {\n      gap: 0;\n      justify-content: space-between;\n\n      > h1 {\n        display: none;\n      }\n\n      > img {\n        width: 20px;\n      }\n\n      .chevron {\n        width: 10px;\n        padding: var(--spacing-2xs) 0;\n        transform: rotate(180deg);\n      }\n    }\n  }\n\n  .body {\n    width: 100%;\n    overflow: hidden;\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-s);\n    overflow-x: hidden;\n    overflow-y: scroll;\n    direction: rtl;\n    padding: var(--spacing-xs);\n    margin-left: var(--spacing-2xs);\n\n    &::-webkit-scrollbar {\n      width: 2px;\n    }\n  }\n\n  .footer {\n    margin: 0 var(--spacing-s);\n    padding: var(--spacing-s) 0;\n    border-top: 1px solid var(--color-fixed-gray-700);\n  }\n\n  .group {\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-xs);\n    direction: ltr;\n  }\n\n  .separator {\n    width: 100%;\n    height: 1px;\n    min-height: 1px;\n    background-color: var(--color-fixed-gray-700);\n  }\n\n  .item {\n    height: 30px;\n    width: 100%;\n    display: flex;\n    align-items: center;\n    justify-content: flex-start;\n    overflow: hidden;\n    padding: 0 var(--spacing-2xs);\n    border-radius: var(--config-border-radius);\n    transition: background-color 200ms ease-out;\n\n    &:hover {\n      background-color: var(--system-accent-light-color);\n    }\n\n    &.active {\n      background-color: var(--system-accent-color);\n    }\n\n    :global(.slu-icon) {\n      width: 20px;\n      min-width: 20px;\n    }\n\n    .label {\n      margin-left: var(--spacing-2xs);\n      text-wrap: nowrap;\n      font-size: 0.8rem;\n      font-weight: 600;\n      overflow: hidden;\n      text-overflow: ellipsis;\n\n      .collapsed & {\n        display: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/components/navigation/index.tsx",
    "content": "import { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { getResourceText } from \"libs/ui/react/utils/index.ts\";\nimport { Tooltip } from \"antd\";\nimport { memo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { NavLink, useLocation } from \"react-router\";\n\nimport { cx } from \"../../modules/shared/utils/app.ts\";\n\nimport { RouteIcons, RoutePath } from \"./routes.tsx\";\nimport cs from \"./index.module.css\";\nimport { settings, themes, widgets } from \"../../state/mod.ts\";\n\nexport const Navigation = memo(() => {\n  const [collapsed, setCollapsed] = useState(false);\n\n  const activeThemes = settings.value.activeThemes;\n  const devTools = settings.value.devTools;\n\n  const { t, i18n } = useTranslation();\n  const location = useLocation();\n\n  const Mapper = (route: RoutePath | null) => {\n    if (!route) return null;\n    return (\n      <Item\n        key={route}\n        route={route}\n        isActive={location.pathname.startsWith(route)}\n        collapsed={collapsed}\n        label={t(`header.labels.${route.replace(\"/\", \"\")}`)}\n        icon={RouteIcons[route]}\n      />\n    );\n  };\n\n  const themesDirectAccess = themes.value.filter(\n    (theme) => theme.settings.length && activeThemes.includes(theme.id),\n  );\n\n  const advanceGroup = [\n    RoutePath.SettingsByMonitor,\n    RoutePath.SettingsByApplication,\n    RoutePath.Shortcuts,\n  ];\n  const devGroup = [RoutePath.DevTools];\n\n  if (devTools) {\n    devGroup.push(RoutePath.IconPackEditor);\n  }\n\n  return (\n    <div\n      className={cx(cs.navigation, {\n        [cs.collapsed!]: collapsed,\n      })}\n    >\n      <div className={cs.header}>\n        <img src=\"./logo.svg\" onClick={() => setCollapsed(!collapsed)} loading=\"lazy\" />\n        <h1>Seelen UI</h1>\n        <Icon\n          className={cs.chevron}\n          iconName=\"FaChevronLeft\"\n          onClick={() => setCollapsed(!collapsed)}\n        />\n      </div>\n\n      <div className={cs.body}>\n        <div className={cs.group}>\n          <Item\n            route={RoutePath.Home}\n            isActive={location.pathname === \"/\"}\n            label={t(\"header.labels.home\")}\n            icon={<Icon iconName=\"TbHome\" />}\n            collapsed={collapsed}\n          />\n          {[RoutePath.General, RoutePath.Resource].map(Mapper)}\n        </div>\n\n        <div className={cs.separator} />\n        <div className={cs.group}>\n          {widgets.value\n            .filter((widget) => !widget.hidden)\n            .toSorted((a, b) => {\n              const aName = getResourceText(a.metadata.displayName, i18n.language);\n              const bName = getResourceText(b.metadata.displayName, i18n.language);\n              return aName.localeCompare(bName, i18n.language);\n            })\n            .map((widget) => (\n              <Item\n                key={widget.id}\n                route={`/widget?${new URLSearchParams({ id: widget.id })}`}\n                isActive={location.pathname === `/widget?${new URLSearchParams({ id: widget.id })}`}\n                collapsed={collapsed}\n                label={<ResourceText text={widget.metadata.displayName} />}\n                icon={<Icon iconName={(widget.icon as any) || \"BiSolidWidget\"} />}\n              />\n            ))}\n        </div>\n\n        {!!themesDirectAccess.length && (\n          <>\n            <div className={cs.separator} />\n            <div className={cs.group}>\n              {themesDirectAccess\n                .toSorted((a, b) => {\n                  const aName = getResourceText(a.metadata.displayName, i18n.language);\n                  const bName = getResourceText(b.metadata.displayName, i18n.language);\n                  return aName.localeCompare(bName, i18n.language);\n                })\n                .map((theme) => (\n                  <Item\n                    key={theme.id}\n                    route={`/theme?${new URLSearchParams({ id: theme.id })}`}\n                    isActive={location.pathname === `/theme?${new URLSearchParams({ id: theme.id })}`}\n                    collapsed={collapsed}\n                    label={<ResourceText text={theme.metadata.displayName} />}\n                    icon={<Icon iconName=\"BiSolidPalette\" />}\n                  />\n                ))}\n            </div>\n          </>\n        )}\n\n        <div className={cs.separator} />\n        <div className={cs.group}>{advanceGroup.map(Mapper)}</div>\n\n        <div className={cs.separator} />\n        <div className={cs.group}>{devGroup.map(Mapper)}</div>\n      </div>\n\n      <div className={cs.footer}>{[RoutePath.Extras].map(Mapper)}</div>\n    </div>\n  );\n});\n\ninterface ItemProps {\n  route: string;\n  isActive: boolean;\n  collapsed: boolean;\n  icon?: React.ReactNode;\n  label: React.ReactNode;\n}\n\nconst Item = ({ route, icon, label, isActive, collapsed }: ItemProps) => {\n  return (\n    <Tooltip placement=\"right\" title={collapsed ? label : null}>\n      <NavLink\n        to={route}\n        className={cx(cs.item, {\n          [cs.active!]: isActive,\n        })}\n      >\n        {icon}\n        <span className={cs.label}>{label}</span>\n      </NavLink>\n    </Tooltip>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/components/navigation/routes.tsx",
    "content": "import { Icon } from \"libs/ui/react/components/Icon\";\nimport type React from \"react\";\n\nexport enum RoutePath {\n  Home = \"/\",\n  General = \"/general\",\n  Resource = \"/resources\",\n  Shortcuts = \"/shortcuts\",\n  SettingsByMonitor = \"/monitors\",\n  SettingsByApplication = \"/specific_apps\",\n  DevTools = \"/developer\",\n  IconPackEditor = \"/icon_pack_editor\",\n  Extras = \"/extras\",\n}\n\nexport const RouteIcons: { [key in RoutePath]?: React.ReactNode } = {\n  [RoutePath.Home]: <Icon iconName=\"TbHome\" />,\n  [RoutePath.General]: <Icon iconName=\"RiSettings3Fill\" />,\n  [RoutePath.Resource]: <Icon iconName=\"IoColorPalette\" />,\n  [RoutePath.SettingsByMonitor]: <Icon iconName=\"PiMonitorBold\" />,\n  [RoutePath.SettingsByApplication]: <Icon iconName=\"IoIosApps\" />,\n  [RoutePath.Shortcuts]: <Icon iconName=\"MdLaunch\" />,\n  [RoutePath.Extras]: <Icon iconName=\"PiInfoFill\" />,\n  [RoutePath.DevTools]: <Icon iconName=\"PiCodeBold\" />,\n  [RoutePath.IconPackEditor]: <Icon iconName=\"PiCodeBold\" />,\n};\n"
  },
  {
    "path": "src/ui/react/settings/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport i18n from \"i18next\";\nimport yaml from \"js-yaml\";\nimport { initReactI18next } from \"react-i18next\";\n\ni18n.use(initReactI18next).init(\n  {\n    lng: \"en\",\n    fallbackLng: \"en\",\n    interpolation: {\n      escapeValue: false,\n    },\n    resources: {},\n  },\n  undefined,\n);\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  for (const [key, value] of Object.entries(translations)) {\n    i18n.addResourceBundle(key, \"translation\", yaml.load(value.default));\n  }\n}\n\nexport default i18n;\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/af.yml",
    "content": "action:\n  confirm: Is jy seker?\n  confirm_body: Hierdie aksie kan nie ongedaan gemaak word nie.\napps_configurations:\n  app:\n    bindings: Binding (let op albei opsies is nodig)\n    category: Kategorie\n    category_placeholder: Geen\n    monitor: Monitor\n    monitor_placeholder: Geen\n    name: Naam\n    ok_create: Skep\n    ok_edit: Opdateer\n    ok_readonly: Wysig as nuut\n    options:\n      NoInteractive: Geen interaktief nie\n      VdPinned: Wys in alle werkruimtes\n      WmFloat: Twm - Begin dryf\n      WmForce: Twm - Force Bestuur\n      WmUnmanage: Twm - Onbestuur\n    options_label: Ekstra opsies\n    title_create: Skep {{name}}\n    title_edit: Redigering {{name}}\n    title_readonly: Besigtig {{name}}\n    weg_options_label: Dock/taakbalkopsies\n    wm_options_label: Vensterbestuurderopsies\n    workspace: Werkruimte\n    workspace_placeholder: Geen\n  bundled_msg: >-\n    Hierdie saamgevoegde konfigurasies is nie bewerkbaar nie en is ontwerp om u\n    die beste ervaring sonder aanpassing te bied. Hulle stel outomaties die\n    algemeenste toepassings vir u op.\n  bundled_title: App -konfigurasie saam met Seelen\n  confirm_delete: Is u seker dat u hierdie konfigurasie/s wil uitvee?\n  confirm_delete_title: Bevestig delete\n  delete: Verwyder\n  export: Uitvoer\n  export_full: Uitvoerinstellings volgens aansoek\n  extra_info: >-\n    Seelen UI gebruik slegs een identifiseerder per app (eerste wedstryd\n    gevind), dus die volgorde in hoe dit gespesifiseer is, is belangrik, die\n    nuutste bygevoeg sal geprioritiseer word, aangesien die tabel standaard van\n    die nuutste na oud gesorteer is.\n  identifier:\n    add_block: Voeg blok by\n    and: En\n    id: Identifiseerder\n    kind: Identifiseer deur\n    matching_strategy: Bypassende strategie\n    matching_strategy_option:\n      contains: Bevat\n      ends_with: Eindig met\n      equals: Gelyk\n      regex: Gereelde uitdrukking\n      starts_with: Begin met\n    negation: Ontken ooreenstemming\n    or: Of\n    remove: Vee blok uit\n    type:\n      class: Klas\n      exe: Exe\n      path: Pad\n      title: Titel\n  import: Invoerproduk\n  import_full: Voer instellings volgens aansoek in\n  new: Nuut\n  search: Soek\n  swap: Ruil\ncancel: Kanselleer\nclose: Dig\ndelete: Verwyder\ndevtools:\n  app_folders: APP -vouers\n  custom_config_file: Laai Custom Config -lêer\n  data_folder: Datapap\n  enable: Aktiveer ontwikkelaarsgereedskap\n  install_folder: Installasie -lêergids\n  load: Vrag\n  settings_file: Instellingslêer\n  simulate_perm:\n    label: Simuleer Widget Toestemming Versoek\n    result_allowed: ✓ Toestemming verleen\n    result_denied: ✗ Toestemming geweier\n    trigger: Simuleer\n    widget_id_placeholder: '@outeur/legstuknaam'\nextras:\n  clear_icons: Maak stelselikone kas uit\n  clear_icons_tooltip: '''N Herbegin kan nodig wees om alle widgets ten volle in werking te stel'\n  exit: Hou op/uitgang\n  links: Amptelike skakels\n  relaunch: Herlaai\n  version: Weergawe\n  version_fixed: >-\n    Die toepassing en WebView2 Runtime-weergawes is reggestel. Dit beteken dat\n    die toepassing nie opdaterings sal ontvang nie en die WebView2 Runtime sal\n    nie outomaties opgedateer word met Windows-opdaterings nie.\ngeneral:\n  accent_color: Aksentkleur\n  date_format: Datumformaat\n  date_format_how_to: Hoe om 'n datumformaat te skryf?\n  hardware_acceleration: Hardeware versnelling\n  hardware_acceleration_description: >-\n    Deaktiveer hardewareversnelling sal geheuegebruik verminder, maar kan\n    prestasieprobleme veroorsaak. Is veilig om te deaktiveer as jy nie lewendige\n    agtergronde sal gebruik nie.\n  icon_pack:\n    available: Beskikbare ikoonpakkette\n    selected: Aktiewe ikoonpakkette\n  language: Taal\n  monday: Maandag\n  performance_mode:\n    on_battery: Op battery\n    on_energy_saver: Op energiebesparing\n    options:\n      disabled: Gebreklik\n      extreme: Uiterste\n      minimal: Minimaal\n    plugged: Ingeprop of laai\n  polling_interval: Stelselpeilingsinterval\n  polling_interval_description: >-\n    Hoe gereeld (in sekondes) Seelen UI stelselhulpbronne soos SVE, RAM, netwerk\n    en skyfaktiwiteit nagaan. 'n Kleiner getal beteken meer gereelde\n    opdaterings, maar effens hoër hulpbrongebruik.\n  saturday: Saterdag\n  start_of_week: Begin van week\n  startup: Hardloop met opstart?\n  sunday: Sondag\n  theme:\n    available: Beskikbare temas\n    selected: Aktiewe temas\nheader:\n  labels:\n    config: Konfigurasies\n    developer: Vir ontwikkelaars\n    extras: Ekstra's\n    general: Generaal\n    home: Tuiste\n    icon_pack_editor: Cache -ikone\n    iconpack: Ikoonpakkies\n    monitors: Monitors\n    plugin: Plugins\n    resources: Hulpbronne\n    shortcuts: Kortpaaie\n    soundpack: Klankpakkies\n    specific_apps: Instellings volgens aansoek\n    theme: Temas\n    virtual_desk: Virtuele tafelrekenaars\n    wallpaper: Agtergronde\n    widget: Widgets\nhome:\n  new_resources: Nuwe hulpbronne\ninherit: Erf\ninProgress: Aan die gang ...\ninsert: Invoeg\nloading: Laai ...\nmiscellaneous: Verskillend\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Meer\n'no': Nee\nopen: Oopmaak\nquit: Laat vaar\nremove: Verwyder\nreset_all_to_default: Stel alles terug na standaardwaardes\nreset_to_default: Stel weer in standaardwaarde\nresources:\n  app_outdated: >-\n    Hierdie hulpbron vereis dat 'n nuwer weergawe van Seelen UI behoorlik moet\n    werk.\n  corrupted_wallpaper: Kon nie kleinkiekie onttrek nie - korrupte of ongesteunde videoformaat\n  delete: Vee hulpbron uit\n  discover: Ontdek meer bronne\n  has_update: '''N Opdatering is beskikbaar'\n  high_impact: Hoë impak op prestasie\n  import_wallpapers: Voer plaaslike agtergronde in\n  open_folder: Open Resource Folder\n  outdated: >-\n    Hierdie hulpbron is ontwerp vir 'n ouer weergawe van Seelen UI en werk\n    moontlik nie behoorlik nie.\n  see_on_website: Sien op webwerf\nreview_request:\n  not_now: Nie nou nie\n  sure: Sekerlik!\n  title: Geniet jy van Seelen UI?\nsave: Red\nsave_and_restart: Stoor en herbegin\nsearch: Soek\nsee_more: Sien meer\nshortcuts:\n  duplicate_error: Hierdie kortpad is gedupliseer\n  enable: Aktiveer ingeboude kortpadstelsel\n  enable_tooltip: >-\n    Deaktiveer as u u eie kortpadstelsel met behulp van die Seelen UI -kliënt\n    implementeer\n  labels:\n    create_new_workspace: Skep nuwe werkruimte\n    cycle_stack_next: Fietsstapel volgende\n    cycle_stack_prev: Siklusstapel vorige\n    cycle_wallpaper_next: Verander na die volgende muurpapier\n    cycle_wallpaper_prev: Verander na vorige muurpapier\n    decrease_height: Verminder hoogte\n    decrease_width: Verminder breedte\n    destroy_current_workspace: Vernietig die huidige werkruimte\n    focus_bottom: Fokus onder\n    focus_latest: Fokus nuut\n    focus_left: Fokus links\n    focus_right: Fokus reg\n    focus_top: Fokus top\n    increase_height: Verhoog die hoogte\n    increase_width: Verhoog die breedte\n    misc_force_quit: Force Quit\n    misc_force_restart: Force herbegin\n    misc_open_settings: Open instellings\n    misc_toggle_lock_tracing: Wisselslotopsporing (logs)\n    misc_toggle_win_event_tracing: Skakel wen -byeenkomsopsporing (logs)\n    move_to_workspace: Beweeg na WorkSpace {{0}}\n    move_window_down: Skuif venster na onder\n    move_window_left: Skuif venster na links\n    move_window_right: Skuif venster na regs\n    move_window_up: Skuif venster na bo\n    pause_tiling: Pouse Tiling Window Manager\n    reserve_bottom: Reservaat onder\n    reserve_float: Reservaat vlot\n    reserve_left: Reservaat links\n    reserve_right: Reserveer reg\n    reserve_stack: Reservaatstapel\n    reserve_top: Reserve Top\n    restore_sizes: Herstel groottes\n    send_to_workspace: Stuur na WorkSpace {{0}}\n    start_weg_app: Fokus of begin toepassing {{0}}\n    switch_to_next_workspace: Skakel oor na die volgende werkruimte\n    switch_to_previous_workspace: Skakel oor na vorige werkruimte\n    switch_workspace: Skakel oor na WorkSpace {{0}}\n    toggle_float: Wisselvenstervlotmodus\n    toggle_monocle: Toggle Workspace Monocle -modus\n  readonly_tooltip: Dit is 'n leesalleen kortpad\n  reset: Stel weer in standaard\nsides:\n  bottom: Bodem\n  left: Links\n  right: Regs\n  top: Top\ntoolbar:\n  auto_hide: Outomatiese vel\n  delay_to_hide: Vertraag om weg te steek\n  delay_to_show: Vertraag om te wys\n  dock_side: Posisie\n  enable: Aktiveer Fancy Toolbar\n  hide_mode:\n    always: Altyd\n    never: Nooit nie\n    on_overlap: Op oorvleueling\n  item_size: Item Grootte\n  label: Werkbalk\n  margin: Marge Grootte\n  padding: Opvulling Grootte\n  placeholder: {}\nupdate:\n  available: Opdatering beskikbaar!\n  channel: Update Channel\n  downloading: Aflaai ...\nwall:\n  backgrounds: Agtergronde\n  blur: Vervaag\n  cancel: Kanselleer\n  close: Maak toe\n  collection_name: Versameling Naam\n  collections: Versamelings\n  contrast: Kontras\n  corrupted_wallpapers_message: 'Korrupte agtergronde, oorweeg dit om hierdie uit te vee:'\n  create: Skep\n  create_collection: Skep versameling\n  default_collection: Verstekversameling\n  delete_collection: Vee versameling uit\n  edit_collection: Wysig versameling\n  enable: Aktiveer agtergrondbestuurder\n  extend: Verleng primêre\n  fit:\n    contain: Bedwing\n    cover: Bedekking\n    fill: Vul\n  flipHorizontal: Flip horisontaal\n  flipVertical: Flip vertikaal\n  generating_thumbnails: Genereer plakpapier-kleinkiekies\n  hours: ure\n  interval: Verander die muurpapier elke\n  minutes: minute\n  monitor_collection: Monitor versameling\n  multimonitor_behaviour: Multimonitor Gedrag\n  muted: Demp video-oudio\n  no_background: Leë skyfievertoning en gebruik die agtergrond van die tema eerder.\n  no_collections: Geen versamelings is nog geskep nie. Klik op die +-knoppie om een ​​te skep.\n  objectFit: Agtergrond pas\n  objectPosition: Agtergrondposisie\n  overlayColor: Oorlegkleur\n  overlayMixBlendMode: Overlay Mix mengmodus\n  per_monitor: Per monitor\n  playback: Playback Speed\n  position:\n    bottom: Bodem\n    center: Middel\n    left: Links\n    right: Regs\n    top: Kruin\n  processing_video: Verwerk video\n  random: Randomize skyfievertoning\n  saturation: Versadiging\n  seconds: sekondes\n  select_collection: Kies Versameling\n  thumbnail_generation_complete: Kleinkiekie-generasie voltooi\n  thumbnail_generation_finished: Kleinkiekiegenerering is suksesvol voltooi\n  wallpaper_collection: Muurpapierversameling\n  wallpaper_settings: Muurpapier instellings\n  withOverlay: Met oorleg\n  workspace_collections: Werkspasie Versamelings\nweg:\n  auto_hide: Outo -vel\n  delay_to_hide: Vertraag om weg te steek\n  delay_to_show: Vertraag om te wys\n  dock_side: Posisie\n  enable: Aktiveer dok/taakbalk\n  filtering: Itemfiltering\n  gap: Gat\n  hide_mode:\n    always: Altyd\n    never: Nooit nie\n    on_overlap: Op oorvleueling\n  items:\n    gap: Ruimte tussen items\n    label: Voorwerpe\n    pinned_visibility:\n      always: Altyd\n      label: Vasgespelde items sigbaarheid\n      when_primary: Wanneer die monitor primêr is\n    show_instance_counter: Wys oop Windows -toonbank\n    show_window_title: Toon oop venstertitel (slegs horisontaal)\n    size: Itemgrootte\n    split_windows: Verdeelde vensters (een item per venster)\n    temporal_visibility:\n      all: Almal\n      label: Sigbaarheid van ontspelde items\n      on_monitor: Op Monitor\n    visible_separators: Sigbare skeiers\n  label: Dock/Taakbalk\n  margin: Marge\n  mode:\n    full_width: Volle skerm breedte\n    min_content: Klein soos kan wees\n  padding: Opvulling\n  show_end_task: Toon eindtaak in die taakbalk\n  width: Wydte\nwelcome:\n  give_a_review: Gee 'n resensie\n  message: >-\n    Seelen UI is 'n gratis en oopbron lessenaaromgewing vir Windows. Hier het jy\n    volle beheer oor hoe jou lessenaar lyk en optree, sodat jy elke detail kan\n    aanpas om by jou werkvloei en styl te pas.\n  ok: Kom ons begin!\n  review: >-\n    As jy daarvan hou om Seelen UI te gebruik, oorweeg dit om 'n resensie in die\n    winkel te gee - jou terugvoer help die projek om te groei en te verbeter.\n  title: Welkom by Seelen UI!\nwidget:\n  enable: Aktiveer hierdie widget\n  enable_for_monitor: Aktiveer op hierdie monitor\n  instances: Instansies\nwm:\n  animations:\n    duration: Animasie -duur (MS)\n    ease_function: Animasie -verligingsfunksie\n    enable: Aktiveer venster se animasies\n  author: Outeur\n  border:\n    enable: Aktiveer venster se grens\n    offset: Grens offset\n    width: Grenswydte\n  description: Beskrywing\n  drag_behavior: Sleepgedrag\n  drag_behavior_options:\n    sort: Sorteer (herrangskik vensters terwyl jy sleep)\n    swap: Ruil (ruil vensters op sleepkant)\n  enable: Aktiveer teëlvensterbestuurder\n  layout: Uitleg\n  resize_delta: Verander die grootte delta (%)\n  space_between_containers: Ruimte tussen houers\n  workspace_offset: Werkruimtes offset (marges)\n  workspace_padding: Werkruimtes opvulling\n'yes': Ja\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/am.yml",
    "content": "action:\n  confirm: ኧረ\n  confirm_body: ይህ እርምጃ ሊቀለበስ አይችልም.\napps_configurations:\n  app:\n    bindings: ማሰሪያ (ሁለቱንም አማራጮች ልብ ይበሉ)\n    category: ምድብ\n    category_placeholder: የለም\n    monitor: ተቆጣጠር\n    monitor_placeholder: የለም\n    name: ስም\n    ok_create: ፍጠር\n    ok_edit: ዝመና\n    ok_readonly: እንደ አዲስ ያርትዑ\n    options:\n      NoInteractive: በይነተገናኝ የለም\n      VdPinned: በሁሉም የስራ ቦታዎች ውስጥ አሳይ\n      WmFloat: Twm - ተንሳፋፊ ጅምር\n      WmForce: Twm - ኃይል ያስተዳድሩ\n      WmUnmanage: Twm - አለመግባባት\n    options_label: ተጨማሪ አማራጮች\n    title_create: '{{{ስም} መፍጠር}}'\n    title_edit: አርት editing {{ስም}}}\n    title_readonly: '{{{}}}}'\n    weg_options_label: የመርከሪያ / የተግባር አሞሌ አማራጮች\n    wm_options_label: የመስኮት አቀናባሪ አማራጮች\n    workspace: የስራ ቦታ\n    workspace_placeholder: የለም\n  bundled_msg: >-\n    እነዚህ የታሸጉ ውቅሮች አርት edity ት አይደሉም እናም ያለ ማበጀት ምርጥ ተሞክሮዎን ለማቅረብ የተቀየሱ ናቸው. በጣም\n    የተለመዱ ትግበራዎችን በራስ-ሰር ያዋቅሩታል.\n  bundled_title: የመተግበሪያ ውቅረት በጥይት የተደመሰሰ\n  confirm_delete: እርግጠኛ ነዎት ይህንን ውቅር / ቶች መሰረዝ ይፈልጋሉ?\n  confirm_delete_title: መሰረዝ ያረጋግጡ\n  delete: ሰርዝ\n  export: ወደ ውጭ ይላኩ\n  export_full: ቅንብሮችን በመላክ በትግበራ\n  extra_info: >-\n    Seelen UI በአንድ መተግበሪያ አንድ መለያ ብቻ ነው የሚጠቀመው (የመጀመሪያው ግጥሚያ ተገኝቷል) ስለዚህ እንዴት\n    እንደሚገለጹ ቅደም ተከተላቸው አስፈላጊ ነው፣ የቅርብ ጊዜዎቹ የተጨመሩት ቅድሚያ ይሰጣቸዋል፣ ምክንያቱም ሰንጠረዡ ከቅርቡ\n    ወደ አሮጌው በነባሪ የተደረደረ ነው።\n  identifier:\n    add_block: ብሎክ ያክሉ\n    and: እና\n    id: መለያ\n    kind: መለየት በ\n    matching_strategy: ተዛማጅ ስትራቴጂ\n    matching_strategy_option:\n      contains: ይይዛል\n      ends_with: በዚህ ያበቃል\n      equals: እኩል ነው።\n      regex: መደበኛ አገላለጽ\n      starts_with: ይጀምራል\n    negation: ማዛመድን\n    or: ወይም\n    remove: ብሎክ ሰርዝ\n    type:\n      class: ክፍል\n      exe: Ex\n      path: መንገድ\n      title: ርዕስ\n  import: ማስመጣት\n  import_full: በትግበራ ​​ቅንብሮች ያስመጡ\n  new: አዲስ\n  search: ፍለጋ\n  swap: መቀያየር\ncancel: ይቅር\nclose: ገጠመ\ndelete: ሰርዝ\ndevtools:\n  app_folders: የመተግበሪያ አቃፊዎች\n  custom_config_file: ብጁ ውቅያ ፋይልን ይጫኑ\n  data_folder: የውሂብ አቃፊ\n  enable: የገንቢ መሳሪያዎችን ያንቁ\n  install_folder: የመጫን አቃፊ\n  load: ጭነት\n  settings_file: የቅንብሮች ፋይል\n  simulate_perm:\n    label: የመግብር ፍቃድ ጥያቄን አስመስለው\n    result_allowed: ✓ ፍቃድ ተሰጥቷል።\n    result_denied: ✗ ፍቃድ ተከልክሏል።\n    trigger: አስመስለው\n    widget_id_placeholder: '@ደራሲ/መግብር-ስም'\nextras:\n  clear_icons: የስርዓት አዶዎች መሸጎጫ\n  clear_icons_tooltip: ዳግም ማስጀመር በሁሉም ፍርግሞች ላይ ሙሉ በሙሉ እንዲተገበር ሊፈልግ ይችላል\n  exit: ማቆም / መውጣት\n  links: ኦፊሴላዊ አገናኞች\n  relaunch: እንደገና መነገር\n  version: ስሪት\n  version_fixed: >-\n    አፕሊኬሽኑ እና WebView2 Runtime ስሪቶች ተስተካክለዋል። ይህ ማለት አፕሊኬሽኑ ዝመናዎችን አይቀበልም እና የዌብ\n    ቪው2 Runtime በዊንዶውስ ዝመናዎች በራስ-ሰር አይዘመንም ማለት ነው።\ngeneral:\n  accent_color: የዝግጅት ቀለም\n  date_format: የቀን ቅርጸት\n  date_format_how_to: የቀን ቅርጸት እንዴት እንደሚፃፍ?\n  hardware_acceleration: የሃርድዌር ማጣደፍ\n  hardware_acceleration_description: >-\n    የሃርድዌር ማጣደፍን ማሰናከል የማህደረ ትውስታ አጠቃቀምን ይቀንሳል፣ ነገር ግን የአፈጻጸም ችግሮችን ሊያስከትል ይችላል።\n    የቀጥታ የግድግዳ ወረቀቶችን የማይጠቀሙ ከሆነ ለማሰናከል ደህንነቱ የተጠበቀ ነው።\n  icon_pack:\n    available: የሚገኙ የአዶ ጥቅሎች\n    selected: ንቁ አዶ ጥቅሎች\n  language: ቋንቋ\n  monday: ሰኞ\n  performance_mode:\n    on_battery: ባትሪ ላይ\n    on_energy_saver: በሃይል ቆጣቢ ላይ\n    options:\n      disabled: ተሰናክሏል\n      extreme: በጣም\n      minimal: አነስተኛ\n    plugged: ተሰኪ ወይም መሙላት\n  polling_interval: የስርዓት ምርጫ ክፍተት\n  polling_interval_description: >-\n    Seelen UI እንደ ሲፒዩ፣ RAM፣ አውታረ መረብ እና የዲስክ እንቅስቃሴ ያሉ የስርዓት ሀብቶችን ምን ያህል ጊዜ\n    (በሴኮንዶች ውስጥ) ይፈትሻል። አነስ ያለ ቁጥር ብዙ ተደጋጋሚ ዝመናዎች ማለት ነው፣ ነገር ግን ትንሽ ከፍ ያለ የሃብት\n    አጠቃቀም ማለት ነው።\n  saturday: ቅዳሜ\n  start_of_week: የሳምንቱ መጀመሪያ\n  startup: ጅምር ላይ ይሮጡ?\n  sunday: እሁድ\n  theme:\n    available: የሚገኙ ገጽታዎች\n    selected: ንቁ ገጽታዎች\nheader:\n  labels:\n    config: ውቅሮች\n    developer: ለገንቢዎች\n    extras: ተጨማሪዎች\n    general: አጠቃላይ\n    home: ቤት\n    icon_pack_editor: የተሸጡ አዶዎች\n    iconpack: አዶዎች ፓኬጆች\n    monitors: ተቆጣጣሪዎች\n    plugin: ተሰኪዎች\n    resources: ሀብቶች\n    shortcuts: አቋራጮች\n    soundpack: የድምፅ ማካካሻዎች\n    specific_apps: በቅንብሮች በትግበራ\n    theme: ገጽታዎች\n    virtual_desk: ምናባዊ ዴስክቶፕስ\n    wallpaper: የግድግዳ ወረቀቶች\n    widget: ፍርግሞች\nhome:\n  new_resources: አዲስ ሀብቶች\ninherit: ውርስ\ninProgress: በሂደት ላይ...\ninsert: ያስገቡ\nloading: በመጫን ላይ ...\nmiscellaneous: ልዩነቶች\nmonitors_configurations:\n  label: ይቆጣጠሩ {{አው.ፊ.}}}}\nmore: ተጨማሪ\n'no': አይ\nopen: ክፈት\nquit: አቁም\nremove: ያስወግዱ\nreset_all_to_default: ሁሉንም ወደ ነባሪ እሴቶች ዳግም ያስጀምሩ\nreset_to_default: ወደ ነባሪ እሴት ዳግም ያስጀምሩ\nresources:\n  app_outdated: ይህ ሀብት በአግባቡ እንዲሠራ አዲስ የአዲስ ስሪት ይፈልጋል.\n  corrupted_wallpaper: ድንክዬ ማውጣት አልተሳካም - የተበላሸ ወይም የማይደገፍ የቪዲዮ ቅርጸት\n  delete: ምንጭን ሰርዝ\n  discover: ተጨማሪ ሀብቶችን ያግኙ\n  has_update: ዝመና ይገኛል\n  high_impact: በአፈፃፀም ላይ ከፍተኛ ተጽዕኖ\n  import_wallpapers: የአካባቢያዊ የግድግዳ ወረቀቶች ያስመጡ\n  open_folder: ክፍት የመረጃ ምንጭ አቃፊ\n  outdated: ይህ የመረጃ ሀብት ለተረጋጋሪ ዌይ ስሪት ለተጨማሪ ስሪት የተሰራ ሲሆን በትክክል ላይሰራ ይችላል.\n  see_on_website: በድር ጣቢያው ላይ ይመልከቱ\nreview_request:\n  not_now: አሁን አይደለም\n  sure: በእርግጠኝነት!\n  title: በ Seelen UI እየተዝናኑ ነው?\nsave: አስቀምጥ\nsave_and_restart: አስቀምጥ & እንደገና ያስጀምሩ\nsearch: ፈልግ\nsee_more: ተጨማሪ ይመልከቱ\nshortcuts:\n  duplicate_error: ይህ አቋራጭ የተባዛ ነው።\n  enable: አብሮገነብ አቋራጭ አቋራጭ ስርዓት\n  enable_tooltip: የእራስዎን አቋራጮችን ስርዓት የሚተገበሩበትን የእራስዎን አቋራጮዎች ስርዓት የሚተገበሩ ከሆነ ያሰናክሉ\n  labels:\n    create_new_workspace: አዲስ የስራ ቦታ ይፍጠሩ\n    cycle_stack_next: ቀጣይ የዑደት ቁልል\n    cycle_stack_prev: ዑደት ቀድሟል\n    cycle_wallpaper_next: ወደ ቀጣዩ የግድግዳ ወረቀት ይለውጡ\n    cycle_wallpaper_prev: ወደ ቀዳሚው የግድግዳ ወረቀት ይቀይሩ\n    decrease_height: ቁመት ቀንሷል\n    decrease_width: ስፋት መቀነስ\n    destroy_current_workspace: የአሁኑን የስራ ቦታ አጥፋ\n    focus_bottom: ወደ ታች ትኩረት\n    focus_latest: የቅርብ ጊዜዎችን ያተኩሩ\n    focus_left: ትኩረት ቀርቷል\n    focus_right: በትክክል ትኩረት ያድርጉ\n    focus_top: ትኩረት\n    increase_height: ቁመት ይጨምሩ\n    increase_width: ስፋት መጨመር\n    misc_force_quit: ኃይል አቁሟል\n    misc_force_restart: አስገባ\n    misc_open_settings: ክፍት ቅንብሮች\n    misc_toggle_lock_tracing: መቆለፊያ መቆለፊያ (ምዝግብ ማስታወሻዎች)\n    misc_toggle_win_event_tracing: የ UNSCUCT PORTERSERSESTERE (ምዝግብ ማስታወሻዎች)\n    move_to_workspace: ወደ የሥራ ቦታ ይሂዱ {{0}}}\n    move_window_down: መስኮት ወደ ታች ያንቀሳቅሱ\n    move_window_left: መስኮት ወደ ግራ ይሂዱ\n    move_window_right: መስኮት ወደ ቀኝ ያንቀሳቅሱ\n    move_window_up: መስኮት ወደ ላይ ያንቀሳቅሱ\n    pause_tiling: የቆዳ መስኮት አቀናባሪ ለአፍታ አቁም\n    reserve_bottom: የተጠባበቅ ክፍል\n    reserve_float: የተጠባባቂ ተንሳፋፊ\n    reserve_left: የተቆራረጠ\n    reserve_right: መብት\n    reserve_stack: የተጠባባቂ ቁልል\n    reserve_top: የላይኛው ክፍል\n    restore_sizes: መጠኖች ወደነበሩበት ይመልሱ\n    send_to_workspace: ወደ የሥራ ቦታ {{0}}}}\n    start_weg_app: የትኩረት ወይም የትግበራ ትግበራ {{0}}}\n    switch_to_next_workspace: ወደ ቀጣዩ የስራ ቦታ ይቀይሩ\n    switch_to_previous_workspace: ወደ ቀዳሚው የስራ ቦታ ይቀይሩ\n    switch_workspace: ወደ የሥራ ቦታ ቀይር {{0}}}\n    toggle_float: የመስኮት ተንሳፋፊ ሁነታን ይቀያይሩ\n    toggle_monocle: የስራ ቦታ ቦታን ይቀያይሩ\n  readonly_tooltip: ይህ የተነበበ ብቻ አቋራጭ ነው\n  reset: ወደ ነባሪዎች ዳግም ያስጀምሩ\nsides:\n  bottom: ታች\n  left: ግራ\n  right: ቀኝ\n  top: ከላይ\ntoolbar:\n  auto_hide: ራስ-ሰር መደበቅ\n  delay_to_hide: ለመደበቅ መዘግየት\n  delay_to_show: ለማሳየት መዘግየት\n  dock_side: አቀማመጥ\n  enable: Fangance የመሳሪያ አሞሌን ያንቁ\n  hide_mode:\n    always: ሁሌም\n    never: በጭራሽ\n    on_overlap: መደራረብ ላይ\n  item_size: የንጥል መጠን\n  label: የመሣሪያ አሞሌ\n  margin: የኅዳግ መጠን\n  padding: የፓዲንግ መጠን\n  placeholder: {}\nupdate:\n  available: ዝመና ይገኛል!\n  channel: ጣቢያውን አዘምን\n  downloading: ማውረድ ...\nwall:\n  backgrounds: የግድግዳ ወረቀቶች\n  blur: ብዥታ\n  cancel: ሰርዝ\n  close: ገጠመ\n  collection_name: የስብስብ ስም\n  collections: ስብስቦች\n  contrast: ንፅፅር\n  corrupted_wallpapers_message: የተበላሹ የግድግዳ ወረቀቶች፣ እነዚህን መሰረዝ ያስቡበት፡-\n  create: ፍጠር\n  create_collection: ስብስብ ይፍጠሩ\n  default_collection: ነባሪ ስብስብ\n  delete_collection: ስብስብን ሰርዝ\n  edit_collection: ስብስብ አርትዕ\n  enable: የግድግዳ ወረቀት ሥራ አስኪያጅን ያንቁ\n  extend: የመጀመሪያ ደረጃን ያራዝሙ\n  fit:\n    contain: መያዝ\n    cover: ሽፋን\n    fill: ሙላ\n  flipHorizontal: አግድም\n  flipVertical: ቀጥ ያለ አቀባዊ\n  generating_thumbnails: የግድግዳ ወረቀት ድንክዬዎችን በማመንጨት ላይ\n  hours: ሰዓታት\n  interval: የግድግዳ ወረቀት እያንዳንዱን ይለውጡ\n  minutes: ደቂቃዎች\n  monitor_collection: ስብስብን ተቆጣጠር\n  multimonitor_behaviour: ባለብዙ መቆጣጠሪያ ባህሪ\n  muted: ድምጸ-ከል ቪዲዮ ኦዲዮ\n  no_background: በመከራየት የፊት ገጽታውን ዳራ በመጠቀም ባዶ ተንሸራታች ትዕይንት.\n  no_collections: እስካሁን ምንም ስብስቦች አልተፈጠሩም። ለመፍጠር የ+ ቁልፍን ጠቅ ያድርጉ።\n  objectFit: ዳራ ተስማሚ\n  objectPosition: የጀርባ አቀማመጥ\n  overlayColor: የተደራቢ ቀለም\n  overlayMixBlendMode: የተደራቢ ድብልቅ ድብልቅ ድብልቅ ሁኔታ\n  per_monitor: በእያንዳንዱ ማሳያ\n  playback: መልሶ ማጫወት ፍጥነት\n  position:\n    bottom: ታች\n    center: ማዕከል\n    left: ግራ\n    right: ቀኝ\n    top: ከላይ\n  processing_video: ቪዲዮን በመስራት ላይ\n  random: የዘፈቀደ ተንሸራታች ትዕይንት\n  saturation: ማሞቂያ\n  seconds: ሰከንዶች\n  select_collection: ስብስብ ይምረጡ\n  thumbnail_generation_complete: ድንክዬ ማመንጨት ተጠናቋል\n  thumbnail_generation_finished: ድንክዬ ማመንጨት በተሳካ ሁኔታ ተጠናቅቋል\n  wallpaper_collection: የግድግዳ ወረቀት ስብስብ\n  wallpaper_settings: የግድግዳ ወረቀት ቅንጅቶች\n  withOverlay: በተደራቢ\n  workspace_collections: የስራ ቦታ ስብስቦች\nweg:\n  auto_hide: ራስ-ሰር መደበቅ\n  delay_to_hide: ለመደበቅ መዘግየት\n  delay_to_show: ለማሳየት መዘግየት\n  dock_side: አቀማመጥ\n  enable: መትከያ / ተግባር አሞሌን ያንቁ\n  filtering: ዕቃ ማጣሪያ\n  gap: ክፍተት\n  hide_mode:\n    always: ሁሌም\n    never: በጭራሽ\n    on_overlap: መደራረብ ላይ\n  items:\n    gap: በእቃዎች መካከል ቦታ\n    label: ዕቃዎች\n    pinned_visibility:\n      always: ሁሌም\n      label: የተሰኩ ዕቃዎች ታይነት\n      when_primary: መቆጣጠሪያው ቀዳሚ ሲሆን\n    show_instance_counter: ክፍት የመስኮቶች ቆጣሪ አሳይ\n    show_window_title: ክፍት የመስኮት ርዕስ አሳይ (አግድም ብቻ)\n    size: የንጥል መጠን\n    split_windows: ዊንዶውስ ክፋይ (በአንድ መስኮት አንድ ንጥል)\n    temporal_visibility:\n      all: ሁሉም\n      label: ያልተሰካ እቃዎች ታይነት\n      on_monitor: በሞኒተር ላይ\n    visible_separators: የሚታዩ መለያዎች\n  label: Dock / Accorbar\n  margin: ህዳግ\n  mode:\n    full_width: የሙሉ ማያ ገጽ ስፋት\n    min_content: በተቻለ መጠን ትንሽ\n  padding: ፓድ\n  show_end_task: በተግባር አሞሌ ውስጥ የመጨረሻውን ተግባር ያሳዩ\n  width: ስፋት\nwelcome:\n  give_a_review: ግምገማ ስጥ\n  message: >-\n    Seelen UI ለዊንዶውስ ነፃ እና ክፍት ምንጭ ዴስክቶፕ አካባቢ ነው። እዚህ ዴስክቶፕዎ እንዴት እንደሚመስል እና\n    ባህሪው ላይ ሙሉ ቁጥጥር አለዎት፣ ይህም እያንዳንዱን ዝርዝር ከስራ ሂደትዎ እና ዘይቤዎ ጋር ለማዛመድ እንዲያበጁ\n    ያስችልዎታል።\n  ok: እንጀምር!\n  review: >-\n    Seelen UIን መጠቀም ከወደዱ፣ በመደብሩ ላይ ግምገማ ለመተው ያስቡበት — የእርስዎ አስተያየት ፕሮጀክቱ እንዲያድግ\n    እና እንዲሻሻል ይረዳል።\n  title: ወደ Seelen UI እንኳን በደህና መጡ!\nwidget:\n  enable: ይህን ንዑስ ፕሮግራም ያንቁ\n  enable_for_monitor: በዚህ መቆጣጠሪያ ላይ ያንቁ\n  instances: ሁኔታዎች\nwm:\n  animations:\n    duration: እነማ ቆይታ (MS)\n    ease_function: አኒሜሽን ኢኮኖሚ ተግባር\n    enable: የመስኮት እነማዎችን አንቃ\n  author: ደራሲ ደራሲ\n  border:\n    enable: የመስኮት ድንበር አንቃ\n    offset: ድንበር ማካካሻ\n    width: ድንበር ስፋት\n  description: መግለጫ\n  drag_behavior: ጎትት ባህሪ\n  drag_behavior_options:\n    sort: ደርድር (በመጎተት ላይ መስኮቶችን እንደገና ይዘዙ)\n    swap: ስዋፕ (በመጎተት መጨረሻ ላይ መስኮቶችን ይቀያይሩ)\n  enable: የታሸገ መስኮት አቀናባሪን ያንቁ\n  layout: አቀማመጥ\n  resize_delta: ዴልታ (%)\n  space_between_containers: በእቃ መጫዎቻዎች መካከል ያለው ቦታ\n  workspace_offset: የስራ ቦታዎች ማካካሻ (ማርጂኖች)\n  workspace_padding: የስራ ቦታዎች ፓድ\n'yes': አዎ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ar.yml",
    "content": "action:\n  confirm: هل أنت متأكد؟\n  confirm_body: لا يمكن التراجع عن هذا الإجراء.\napps_configurations:\n  app:\n    bindings: الزامي (لاحظ أن كلا الخيارين مطلوبه)\n    category: الفئة\n    category_placeholder: لا يوجد\n    monitor: الشاشة\n    monitor_placeholder: لا يوجد\n    name: الاسم\n    ok_create: إنشاء\n    ok_edit: تعديل\n    ok_readonly: عرض قرائه فقط\n    options:\n      NoInteractive: لا التفاعلية\n      VdPinned: إظهار في كافة مساحات العمل\n      WmFloat: Twm - ابدأ بالطفو\n      WmForce: Twm - إدارة القوة\n      WmUnmanage: Twm - عدم الإدارة\n    options_label: خيارات إضافية\n    title_create: إنشاء {{الأسم}}\n    title_edit: تعديل {{العنوان}}\n    title_readonly: عرض {{الأسم}}\n    weg_options_label: خيارات دائرة المهام/شريط المهام\n    wm_options_label: خيارات مدير النوافذ\n    workspace: مساحة العمل\n    workspace_placeholder: لا يوجد\n  bundled_msg: >-\n    هذه الأضافات المجمعة غير قابلة للتحرير وهي مصممة لتوفر لك أفضل تجربة من دون\n    تخصيص. يقومون تلقائيًا بإضافة التطبيقات الأكثر شهره لك.\n  bundled_title: أضافه التطبيقات المجمعه مع Seelen\n  confirm_delete: هل أنت متأكد أنك تريد حذف هذه الأضافات/التجميعات؟\n  confirm_delete_title: تأكيد الحذف\n  delete: حذف\n  export: صدر\n  export_full: إعدادات التصدير حسب التطبيق\n  extra_info: >-\n    تستخدم واجهة Seelen معرف واحد  فقط لكل تطبيق (تم العثور على أول تطابق) فإن\n    الترتيب في كيفية التحديد مهم، وسيتم إعطاء الأولوية لآخر إضافة، كما تلاحظ أنه\n    يتم فرز الجدول افتراضيًا من الأحدث إلى الأقدم.\n  identifier:\n    add_block: أضف بلوك\n    and: و\n    id: المعرف\n    kind: تحديد بواسطة\n    matching_strategy: استراتيجية المطابقة\n    matching_strategy_option:\n      contains: يتضمن\n      ends_with: ينتهي ب\n      equals: يساوي\n      regex: التعبير العادي\n      starts_with: يبدأ ب\n    negation: نفي المطابقة\n    or: أو\n    remove: حذف الحظر\n    type:\n      class: فصل\n      exe: إملف تنفيذى\n      path: طريق\n      title: عنوان\n  import: استيراد\n  import_full: استيراد الإعدادات حسب التطبيق\n  new: جديد\n  search: البحث\n  swap: تبديل\ncancel: إلغاء\nclose: يغلق\ndelete: حذف\ndevtools:\n  app_folders: مجلدات التطبيق\n  custom_config_file: تحميل ملف الأعدادات المخصص\n  data_folder: مجلد البيانات\n  enable: تفعيل أدوات المطور\n  install_folder: مجلد التثبيت\n  load: تحميل\n  settings_file: ملف الإعدادات\n  simulate_perm:\n    label: محاكاة طلب إذن القطعة\n    result_allowed: ✓ تم منح الإذن\n    result_denied: ✗ تم رفض الإذن\n    trigger: محاكاة\n    widget_id_placeholder: '@author/اسم القطعة'\nextras:\n  clear_icons: مسح ذاكرة التخزين المؤقت لأيقونات النظام\n  clear_icons_tooltip: قد تكون إعادة التشغيل مطلوبة لتدخل جميع الأدوات حيز التنفيذ بالكامل\n  exit: إنهاء/خروج\n  links: الروابط الرسمية\n  relaunch: إعادة التشغيل\n  version: الإصدار\n  version_fixed: >-\n    تم إصلاح إصدارات التطبيق وWebView2 Runtime. وهذا يعني أن التطبيق لن يتلقى\n    التحديثات ولن يتم تحديث WebView2 Runtime تلقائيًا بتحديثات Windows.\ngeneral:\n  accent_color: اللون الثانوي\n  date_format: تنسيق التاريخ\n  date_format_how_to: كيفية كتابة تنسيق التاريخ؟\n  hardware_acceleration: تسريع الأجهزة\n  hardware_acceleration_description: >-\n    سيؤدي تعطيل تسريع الأجهزة إلى تقليل استخدام الذاكرة، ولكنه قد يسبب مشكلات في\n    الأداء. يعد تعطيله آمنًا إذا كنت لن تستخدم خلفيات حية.\n  icon_pack:\n    available: حزم الأيقونات المتوفرة\n    selected: حزم الأيقونات النشطة\n  language: اللغات\n  monday: الاثنين\n  performance_mode:\n    on_battery: على البطارية\n    on_energy_saver: على توفير الطاقة\n    options:\n      disabled: عاجز\n      extreme: أقصى\n      minimal: الحد الأدنى\n    plugged: توصيل أو شحن\n  polling_interval: الفاصل الزمني لاستقصاء النظام\n  polling_interval_description: >-\n    كم مرة (بالثواني) تقوم Seelen UI بفحص موارد النظام مثل وحدة المعالجة\n    المركزية وذاكرة الوصول العشوائي والشبكة ونشاط القرص. الرقم الأصغر يعني\n    تحديثات أكثر تكرارًا، ولكن استخدامًا أعلى قليلاً للموارد.\n  saturday: السبت\n  start_of_week: بداية الأسبوع\n  startup: تفعيل عند تشغيل الكمبيوتر؟\n  sunday: الأحد\n  theme:\n    available: السمات المتاحة\n    selected: المواضيع النشطة\nheader:\n  labels:\n    config: التكوينات\n    developer: للمطورين\n    extras: الإضافات\n    general: عام\n    home: المنزل\n    icon_pack_editor: الأيقونات المخزنة مؤقتاً\n    iconpack: حزم الأيقونات\n    monitors: الشاشات\n    plugin: المكونات الإضافية\n    resources: الموارد\n    shortcuts: الاختصارات\n    soundpack: حزم الصوت\n    specific_apps: الإعدادات حسب التطبيق\n    theme: الموضوعات\n    virtual_desk: أجهزة الكمبيوتر المكتبية الافتراضية\n    wallpaper: خلفيات\n    widget: الأدوات\nhome:\n  new_resources: الموارد الجديدة\ninherit: ورث\ninProgress: قيد التنفيذ...\ninsert: أدخل\nloading: جاري التحميل...\nmiscellaneous: متنوع\nmonitors_configurations:\n  label: شاشة {{index}}\nmore: أكثر\n'no': لا\nopen: فتح\nquit: الخروج\nremove: الحذف\nreset_all_to_default: إعادة تعيين الكل إلى القيم الافتراضية\nreset_to_default: إعادة التعيين إلى القيمة الافتراضية\nresources:\n  app_outdated: يتطلب هذا المورد إصدارًا أحدث من Seelen UI ليعمل بشكل صحيح.\n  corrupted_wallpaper: فشل استخراج الصورة المصغرة - تنسيق الفيديو تالف أو غير مدعوم\n  delete: حذف المورد\n  discover: اكتشف المزيد من الموارد\n  has_update: تحديث متاح\n  high_impact: تأثير كبير على الأداء\n  import_wallpapers: استيراد الخلفيات المحلية\n  open_folder: فتح مجلد الموارد\n  outdated: تم تصميم هذا المورد لإصدار أقدم من Seelen UI وقد لا يعمل بشكل صحيح.\n  see_on_website: انظر على الموقع\nreview_request:\n  not_now: ليس الآن\n  sure: بالتأكيد!\n  title: هل تستمتع بواجهة Seelen؟\nsave: حفظ\nsave_and_restart: حفظ وإعد التشغيل\nsearch: يبحث\nsee_more: شاهد المزيد\nshortcuts:\n  duplicate_error: هذا الاختصار مكرر\n  enable: تمكين نظام الاختصارات المدمجة\n  enable_tooltip: تعطيل إذا كنت ستنفذ نظام الاختصارات الخاص بك باستخدام عميل Seelen UI\n  labels:\n    create_new_workspace: إنشاء مساحة عمل جديدة\n    cycle_stack_next: كومة الدراجة التالية\n    cycle_stack_prev: كومة الدراجة السابقة\n    cycle_wallpaper_next: التغيير إلى خلفية القادمة\n    cycle_wallpaper_prev: تغيير إلى خلفية سابقة\n    decrease_height: انخفاض الارتفاع\n    decrease_width: انخفاض العرض\n    destroy_current_workspace: تدمير مساحة العمل الحالية\n    focus_bottom: التركيز القاع\n    focus_latest: التركيز الأخير\n    focus_left: التركيز اليسار\n    focus_right: التركيز بشكل صحيح\n    focus_top: التركيز أعلى\n    increase_height: زيادة الارتفاع\n    increase_width: زيادة العرض\n    misc_force_quit: استقال\n    misc_force_restart: فرض إعادة التشغيل\n    misc_open_settings: فتح الإعدادات\n    misc_toggle_lock_tracing: تبديل قفل تتبع (سجلات)\n    misc_toggle_win_event_tracing: تبديل الفوز تتبع حدث (سجلات)\n    move_to_workspace: انتقل إلى مساحة العمل {{0}}\n    move_window_down: نقل النافذة إلى أسفل\n    move_window_left: تحرك النافذة إلى اليسار\n    move_window_right: نقل النافذة إلى اليمين\n    move_window_up: نقل النافذة إلى الأعلى\n    pause_tiling: وقفة مدير نافذة الإبلاغ\n    reserve_bottom: قاع الاحتياطي\n    reserve_float: تعويم الاحتياطي\n    reserve_left: الاحتياطي اليسار\n    reserve_right: حجز الحق\n    reserve_stack: كومة الاحتياطي\n    reserve_top: قمة الاحتياط\n    restore_sizes: استعادة الأحجام\n    send_to_workspace: أرسل إلى مساحة العمل {{0}}\n    start_weg_app: التركيز أو بدء التطبيق {{0}}\n    switch_to_next_workspace: التبديل إلى مساحة العمل التالية\n    switch_to_previous_workspace: التبديل إلى مساحة العمل السابقة\n    switch_workspace: التبديل إلى مساحة العمل {{0}}\n    toggle_float: تبديل النافذة وضع تعويم\n    toggle_monocle: تبديل وضع مساحة العمل monocle\n  readonly_tooltip: هذا اختصار للقراءة فقط\n  reset: إعادة ضبط على الافتراضات\nsides:\n  bottom: تحت\n  left: يسار\n  right: يمين\n  top: فوق\ntoolbar:\n  auto_hide: الأخفاء التلقائي\n  delay_to_hide: تأخير الأخفاء\n  delay_to_show: تأخير الظهور\n  dock_side: المنصب\n  enable: فعل شريط الأدوات الفاخر\n  hide_mode:\n    always: دائماً\n    never: أبداً\n    on_overlap: على التداخل\n  item_size: حجم السلعة\n  label: شريط الأدوات\n  margin: حجم الهامش\n  padding: حجم الحشو\n  placeholder: {}\nupdate:\n  available: تحديث متاح!\n  channel: تحديث قناة\n  downloading: تنزيل ...\nwall:\n  backgrounds: خلفيات\n  blur: الضبابية\n  cancel: يلغي\n  close: يغلق\n  collection_name: اسم المجموعة\n  collections: المجموعات\n  contrast: التباين\n  corrupted_wallpapers_message: 'الخلفيات التالفة، فكر في حذفها:'\n  create: يخلق\n  create_collection: إنشاء مجموعة\n  default_collection: المجموعة الافتراضية\n  delete_collection: حذف المجموعة\n  edit_collection: تحرير المجموعة\n  enable: تمكين مدير الخلفيات\n  extend: تمديد الابتدائية\n  fit:\n    contain: احتواء\n    cover: الغلاف\n    fill: تعبئة\n  flipHorizontal: قلب أفقي\n  flipVertical: قلب عمودي\n  generating_thumbnails: توليد الصور المصغرة للجدران\n  hours: ساعات\n  interval: تغيير الخلفية كل\n  minutes: دقائق\n  monitor_collection: مجموعة المراقبة\n  multimonitor_behaviour: سلوك الشاشات المتعددة\n  muted: كتم صوت الفيديو\n  no_background: بدون خلفية\n  no_collections: لم يتم إنشاء أي مجموعات حتى الآن. انقر فوق الزر + لإنشاء واحدة.\n  objectFit: الخلفية الملائمة\n  objectPosition: موقف الخلفية\n  overlayColor: لون التراكب\n  overlayMixBlendMode: وضع مزج مزيج التراكب\n  per_monitor: لكل شاشة\n  playback: سرعة التشغيل\n  position:\n    bottom: القاع\n    center: المركز\n    left: يسار\n    right: صحيح\n    top: أعلى\n  processing_video: معالجة الفيديو\n  random: عرض عشوائي للشرائح\n  saturation: التشبع\n  seconds: ثوان\n  select_collection: حدد المجموعة\n  thumbnail_generation_complete: اكتمل إنشاء الصورة المصغرة\n  thumbnail_generation_finished: تم الانتهاء من إنشاء الصور المصغرة بنجاح\n  wallpaper_collection: مجموعة خلفيات\n  wallpaper_settings: إعدادات ورق الحائط\n  withOverlay: مع تراكب\n  workspace_collections: مجموعات مساحة العمل\nweg:\n  auto_hide: الأخفاء التلقائي\n  delay_to_hide: تأخير الأخفاء\n  delay_to_show: تأخير الظهور\n  dock_side: المكان\n  enable: تفعيل قفص الاتهام/شريط المهام\n  filtering: تصفية العناصر\n  gap: الفراغ\n  hide_mode:\n    always: دائماً\n    never: أبداً\n    on_overlap: على التداخل\n  items:\n    gap: الفراغ بين العناصر\n    label: العناصر\n    pinned_visibility:\n      always: دائماً\n      label: رؤية العناصر المثبتة\n      when_primary: عندما تكون الشاشة أساسية\n    show_instance_counter: إظهار عداد النوافذ المفتوحة\n    show_window_title: إظهار عنوان النافذة المفتوحة (أفقيًا فقط)\n    size: حجم العنصر\n    split_windows: تقسيم النوافذ (عنصر واحد لكل نافذة)\n    temporal_visibility:\n      all: الجميع\n      label: رؤية العناصر غير المثبتة\n      on_monitor: على الشاشة\n    visible_separators: الفواصل المرئية\n  label: قفص الاتهام / شريط المهام\n  margin: الهامش\n  mode:\n    full_width: عرض كامل الشاشة\n    min_content: صغيرة كما يمكن أن تكون\n  padding: المساحه\n  show_end_task: إظهار مهمة النهاية في شريط المهام\n  width: العرض\nwelcome:\n  give_a_review: إعطاء مراجعة\n  message: >-\n    Seelen UI هي بيئة سطح مكتب مجانية ومفتوحة المصدر لنظام التشغيل Windows. هنا\n    يمكنك التحكم بشكل كامل في شكل سطح المكتب الخاص بك وسلوكه، مما يسمح لك بتخصيص\n    كل التفاصيل لتتناسب مع سير عملك وأسلوبك.\n  ok: لنبدأ!\n  review: >-\n    إذا كنت تستمتع باستخدام Seelen UI، ففكر في ترك تعليق على المتجر - فتعليقاتك\n    تساعد المشروع على النمو والتحسن.\n  title: مرحبًا بك في واجهة مستخدم Seelen!\nwidget:\n  enable: تمكين هذه الأداة\n  enable_for_monitor: التمكين على هذه الشاشة\n  instances: المثيلات\nwm:\n  animations:\n    duration: مدة الرسوم المتحركة (MS)\n    ease_function: وظيفة تسهيل الرسوم المتحركة\n    enable: تمكين الرسوم المتحركة للنافذة\n  author: المؤلف\n  border:\n    enable: تفعيل حدود النافذة\n    offset: إزاحة الحدود\n    width: عرض الحدود\n  description: الوصف\n  drag_behavior: سلوك السحب\n  drag_behavior_options:\n    sort: فرز (إعادة ترتيب النوافذ أثناء السحب)\n    swap: مبادلة (تبديل النوافذ عند نهاية السحب)\n  enable: تفعيل مدير نافذة البلاط\n  layout: التخطيط\n  resize_delta: غير حجم دلتا (%)\n  space_between_containers: المساحه بين الحاويات\n  workspace_offset: المساحه حول مساحات العمل\n  workspace_padding: المساحه بين مساحات العمل\n'yes': نعم\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/az.yml",
    "content": "action:\n  confirm: Əminsən?\n  confirm_body: Bu hərəkət geri qaytarıla bilməz.\napps_configurations:\n  app:\n    bindings: Bağlama (hər iki variant tələb olunur)\n    category: Kateqoriya\n    category_placeholder: Heç kim\n    monitor: Ekran\n    monitor_placeholder: Heç kim\n    name: Ad\n    ok_create: Yaratmaq\n    ok_edit: Aktuallaşdırmaq\n    ok_readonly: Yeni kimi redaktə edin\n    options:\n      NoInteractive: İnteraktiv yoxdur\n      VdPinned: Bütün iş yerlərində göstərin\n      WmFloat: TWM - üzənlərə başlayın\n      WmForce: TWM - Güc idarə edin\n      WmUnmanage: TWM - Unmanage\n    options_label: Əlavə seçimlər\n    title_create: Yaratmaq {{ad}}\n    title_edit: Redaktə {{ad}}\n    title_readonly: Bax {{ad}}\n    weg_options_label: Dock / tapşırıq çubuğu seçimləri\n    wm_options_label: Pəncərə meneceri variantları\n    workspace: İş sahəsi\n    workspace_placeholder: Heç kim\n  bundled_msg: >-\n    Bu paketlənmiş konfiqurasiya düzəliş deyil və özelleştirme olmadan ən yaxşı\n    təcrübəni təmin etmək üçün hazırlanmışdır. Avtomatik olaraq sizin üçün ən\n    çox yayılmış tətbiqləri konfiqurasiya edirlər.\n  bundled_title: App Config Seelen ilə birləşdirilmişdir\n  confirm_delete: Bu konfiqurasiya / s silmək istədiyinizə əminsiniz?\n  confirm_delete_title: Silmək təsdiqləyin\n  delete: Silmək\n  export: İxrac etmək\n  export_full: Tətbiq tərəfindən parametrləri ixrac edin\n  extra_info: >-\n    Seelen UI, bir tətbiq başına yalnız bir identifikatordan istifadə edir (ilk\n    matç tapıldı), buna görə əmiklər necə vacibdir, ən son əlavə olaraq,\n    cədvəlin ən sondan köhnəyə görə sıralandığı üçün prioritetləşdiriləcəkdir.\n  identifier:\n    add_block: Blok əlavə edin\n    and: Və\n    id: Eyniləşdirici\n    kind: Tərəfindən müəyyənləşdirmək\n    matching_strategy: Uyğun strategiya\n    matching_strategy_option:\n      contains: ehtiva edir\n      ends_with: ilə bitir\n      equals: Bərabərdir\n      regex: Daimi ifadə\n      starts_with: ilə başlayır\n    negation: Uyğunlaşmanı rədd edin\n    or: Və ya\n    remove: Bloku silmək\n    type:\n      class: Sinif\n      exe: Exe\n      path: Yol\n      title: Başlıq\n  import: İdxal etmək\n  import_full: Tətbiqlə Parametrləri İdxal edin\n  new: Yeni\n  search: Axtarış\n  swap: Dəyişdirmək\ncancel: Ləğv etmək\nclose: Yaxın\ndelete: Silmək\ndevtools:\n  app_folders: Tətbiq qovluqları\n  custom_config_file: Xüsusi konfiqurasiya faylını yükləyin\n  data_folder: Məlumat qovluğu\n  enable: Developer alətlərini aktivləşdirin\n  install_folder: Quraşdırma qovluğu\n  load: Yükləmək\n  settings_file: Parametrlər faylı\n  simulate_perm:\n    label: Widget İcazə Sorğunu simulyasiya edin\n    result_allowed: ✓ İcazə verildi\n    result_denied: ✗ İcazə rədd edildi\n    trigger: Simulyasiya edin\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Sistem nişanlar önbelleği təmizləyin\n  clear_icons_tooltip: Bütün vidjetlərə tam təsir göstərmək üçün yenidən başlama tələb oluna bilər\n  exit: Çıxmaq / çıxmaq\n  links: Rəsmi bağlantılar\n  relaunch: Başsız\n  version: Versiya\n  version_fixed: >-\n    Proqram və WebView2 Runtime versiyaları sabitdir. Bu o deməkdir ki, proqram\n    yeniləmələri qəbul etməyəcək və WebView2 Runtime avtomatik olaraq Windows\n    yeniləmələri ilə yenilənməyəcək.\ngeneral:\n  accent_color: Vurğu\n  date_format: Tarix formatı\n  date_format_how_to: Tarix formatını necə yazmaq olar?\n  hardware_acceleration: Avadanlıq sürətləndirilməsi\n  hardware_acceleration_description: >-\n    Aparat sürətləndirilməsinin söndürülməsi yaddaş istifadəsini azaldacaq,\n    lakin performans problemlərinə səbəb ola bilər. Canlı divar kağızlarından\n    istifadə etməyəcəksinizsə, onu söndürmək təhlükəsizdir.\n  icon_pack:\n    available: Mövcud İkon Paketləri\n    selected: Aktiv İkon Paketləri\n  language: Dil\n  monday: bazar ertəsi\n  performance_mode:\n    on_battery: Batareyada\n    on_energy_saver: Enerji qənaətçisi haqqında\n    options:\n      disabled: Əlil\n      extreme: Ekstremal\n      minimal: Minimal\n    plugged: Qoşuldu və ya şarj\n  polling_interval: Sistem sorğu intervalı\n  polling_interval_description: >-\n    Seelen UI CPU, RAM, şəbəkə və disk fəaliyyəti kimi sistem resurslarını nə\n    qədər tez-tez (saniyələrlə) yoxlayır. Daha kiçik rəqəm daha tez-tez\n    yeniləmələr, lakin bir qədər yüksək resurs istifadəsi deməkdir.\n  saturday: şənbə\n  start_of_week: Həftənin başlanğıcı\n  startup: Başlanğıcda qaçırsınız?\n  sunday: bazar günü\n  theme:\n    available: Mövcud Mövzular\n    selected: Aktiv Mövzular\nheader:\n  labels:\n    config: Konfiqurasiya\n    developer: Yaradıcılar üçün\n    extras: Əlavələr\n    general: General\n    home: Evdə\n    icon_pack_editor: Keşişli nişanlar\n    iconpack: Icon paketləri\n    monitors: Monitorlar\n    plugin: Plaginlər\n    resources: Resurslar\n    shortcuts: Qısaylıq\n    soundpack: Səs paketləri\n    specific_apps: Tətbiq tərəfindən parametrlər\n    theme: Mövzular\n    virtual_desk: Virtual masaüstü\n    wallpaper: Divar kağızları\n    widget: Vidjetlər\nhome:\n  new_resources: Yeni mənbələr\ninherit: Varis olmaq\ninProgress: Davam edir ...\ninsert: Daxil etmək\nloading: Yükləmə ...\nmiscellaneous: Müxtəlif\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Daha çox\n'no': Yox\nopen: Açıq-saçıq\nquit: Çıxmaq\nremove: Çıxarmaq\nreset_all_to_default: Hamısını standart dəyərlərə yenidən qurun\nreset_to_default: Defolt dəyərinə yenidən qurun\nresources:\n  app_outdated: >-\n    Bu qaynaq, Seelen UI-nin düzgün işləməsi üçün daha yeni bir versiyasını\n    tələb edir.\n  corrupted_wallpaper: Miniatürü çıxarmaq alınmadı - zədələnmiş və ya dəstəklənməyən video formatı\n  delete: Resurs silmək\n  discover: Daha çox mənbəyi kəşf edin\n  has_update: Bir yeniləmə mövcuddur\n  high_impact: Performansa yüksək təsir\n  import_wallpapers: Yerli divar kağızları idxal edin\n  open_folder: Açıq Resurs qovluğu\n  outdated: >-\n    Bu qaynaq Seelen UI-nin köhnə bir versiyası üçün hazırlanmış və düzgün\n    işləməyə bilər.\n  see_on_website: Veb saytında baxın\nreview_request:\n  not_now: İndi yox\n  sure: Əlbəttə!\n  title: Seelen UI-dən zövq alırsınız?\nsave: Yadda saxla\nsave_and_restart: Saxla və yenidən başladın\nsearch: Axtar\nsee_more: Daha çoxuna baxın\nshortcuts:\n  duplicate_error: Bu qısayol dublikatdır\n  enable: Quraşdırılmış qısa yol sistemini aktivləşdirin\n  enable_tooltip: >-\n    Seelen UI müştərisindən istifadə edərək öz qısa yol sisteminizi həyata\n    keçirəcəksənsə deaktiv edin\n  labels:\n    create_new_workspace: Yeni iş sahəsi yaradın\n    cycle_stack_next: Növbəti dövr yığını\n    cycle_stack_prev: Əvvəlki dövr yığını\n    cycle_wallpaper_next: Növbəti divar kağızı dəyişdirin\n    cycle_wallpaper_prev: Əvvəlki divar kağızı dəyişdirin\n    decrease_height: Hündürlük azalmaq\n    decrease_width: Enini azaltmaq\n    destroy_current_workspace: Cari iş sahəsini məhv edin\n    focus_bottom: Fokuslanmaq\n    focus_latest: Ən son fokus\n    focus_left: Fokus sol\n    focus_right: Fokuslanmaq\n    focus_top: Fokuslanmaq\n    increase_height: Boylanmaq\n    increase_width: Enini artırmaq\n    misc_force_quit: Çıxmaq\n    misc_force_restart: Güc yenidən başladın\n    misc_open_settings: Parametrləri açın\n    misc_toggle_lock_tracing: Kilid izləmə (qeydlər)\n    misc_toggle_win_event_tracing: TOXGE WIN TƏDBİRLƏRİ İSTƏYİR (LOGS)\n    move_to_workspace: İş sahəsinə keçin {{0}}\n    move_window_down: Pəncərəni altına köçürün\n    move_window_left: Pəncərəni sola köçürün\n    move_window_right: Pəncərəni sağa köçürün\n    move_window_up: Pəncərəni yuxarıya köçürün\n    pause_tiling: Pəncərə meneceri\n    reserve_bottom: Ehtiyat\n    reserve_float: Ehtiyat float\n    reserve_left: Qoruğu\n    reserve_right: Ehtiyat ehtiyat\n    reserve_stack: Ehtiyat yığını\n    reserve_top: Bron etmək\n    restore_sizes: Ölçüləri bərpa etmək\n    send_to_workspace: İş sahəsinə göndərin {{0}}\n    start_weg_app: Fokus və ya başlama tətbiqinə {{0}}\n    switch_to_next_workspace: Növbəti iş sahəsinə keçin\n    switch_to_previous_workspace: Əvvəlki iş sahəsinə keçin\n    switch_workspace: İş sahəsinə keçin {{0}}\n    toggle_float: Pəncərənin üzmə rejimini dəyişdirin\n    toggle_monocle: İş sahəsi Monocle rejimi keçid\n  readonly_tooltip: Bu yalnız bir oxu qısa yoldur\n  reset: Defoltlara yenidən qurun\nsides:\n  bottom: Dibli\n  left: Sol\n  right: Haqlı\n  top: Üst\ntoolbar:\n  auto_hide: Avtomatik gizlətmək\n  delay_to_hide: Gizlətmək üçün gecikmə\n  delay_to_show: Göstərmək üçün gecikmə\n  dock_side: Mövqe\n  enable: Zərif alət çubuğunu aktivləşdirin\n  hide_mode:\n    always: Həmişə\n    never: Heç vaxt\n    on_overlap: Üst-üstə düşür\n  item_size: Element Ölçüsü\n  label: Alət çubuğu\n  margin: Marja Ölçüsü\n  padding: Doldurma Ölçüsü\n  placeholder: {}\nupdate:\n  available: YENİLƏNİB!\n  channel: Kanal yeniləmə\n  downloading: Yükləmə ...\nwall:\n  backgrounds: Divar kağızları\n  blur: Sönük\n  cancel: Ləğv et\n  close: Bağlayın\n  collection_name: Kolleksiya Adı\n  collections: Kolleksiyalar\n  contrast: Əksinə\n  corrupted_wallpapers_message: 'Zədələnmiş divar kağızları, bunları silməyi düşünün:'\n  create: Yaradın\n  create_collection: Kolleksiya yaradın\n  default_collection: Defolt Kolleksiya\n  delete_collection: Kolleksiyanı silin\n  edit_collection: Kolleksiyaya düzəliş edin\n  enable: Divar kağızı menecerini aktivləşdirin\n  extend: Əsası genişləndirin\n  fit:\n    contain: Ehtiva etmək\n    cover: Örtük\n    fill: Doldurmaq\n  flipHorizontal: Üfüqi flip\n  flipVertical: Şaquli sürüşmək\n  generating_thumbnails: Divar kağızı miniatürlərinin yaradılması\n  hours: sair\n  interval: Divar kağızı hər birini dəyişdirin\n  minutes: dəqiqəlik\n  monitor_collection: Monitor kolleksiyası\n  multimonitor_behaviour: Multimonitor Davranışı\n  muted: Səssiz video audio\n  no_background: Bunun əvəzinə mövzunun fonundan istifadə edərək boş slayd şousu.\n  no_collections: Hələ kolleksiya yaradılmayıb. Yaratmaq üçün + düyməsini basın.\n  objectFit: Arxa plan\n  objectPosition: Fon mövqeyi\n  overlayColor: Örtük rəngi\n  overlayMixBlendMode: Qarışıq qarışığı rejimi\n  per_monitor: Monitor başına\n  playback: Sürətini çalın\n  position:\n    bottom: Dibli\n    center: Mərkəzi\n    left: Sol\n    right: Haqlı\n    top: Üst\n  processing_video: Video işlənir\n  random: Slayd şousu\n  saturation: Doydurma\n  seconds: saniyə\n  select_collection: Kolleksiya seçin\n  thumbnail_generation_complete: Miniatürlərin yaradılması tamamlandı\n  thumbnail_generation_finished: Miniatür yaradılması uğurla tamamlandı\n  wallpaper_collection: Divar kağızı Kolleksiyası\n  wallpaper_settings: Divar kağızı Parametrləri\n  withOverlay: Örtülü ilə\n  workspace_collections: İş sahəsi kolleksiyaları\nweg:\n  auto_hide: Avtomatik gizlətmək\n  delay_to_hide: Gizlətmək üçün gecikmə\n  delay_to_show: Göstərmək üçün gecikmə\n  dock_side: Mövqe\n  enable: Dock / tapşırıq çubuğunu aktivləşdirin\n  filtering: Element süzgəci\n  gap: Gap\n  hide_mode:\n    always: Həmişə\n    never: Heç vaxt\n    on_overlap: Üst-üstə düşür\n  items:\n    gap: Əşyalar arasındakı boşluq\n    label: Maddələr\n    pinned_visibility:\n      always: Həmişə\n      label: Saxlanmış Elementlərin Görünüşü\n      when_primary: Monitor əsas olduqda\n    show_instance_counter: Açıq pəncərələrin sayğacını göstərin\n    show_window_title: Açıq pəncərə başlığını göstər (yalnız üfüqi)\n    size: Maddə ölçüsü\n    split_windows: Pəncərələri bölün (hər pəncərədə bir element)\n    temporal_visibility:\n      all: Hamısı\n      label: Saxlanmamış Elementlərin Görünüşü\n      on_monitor: Monitorda\n    visible_separators: Görünən ayırıcılar\n  label: Dock / tapşırıq çubuğu\n  margin: Kənarə\n  mode:\n    full_width: Tam ekran eni\n    min_content: Ola bildiyi qədər kiçik\n  padding: Paddend\n  show_end_task: Tapşırıq çubuğunda son tapşırığı göstərin\n  width: Geniştəhər\nwelcome:\n  give_a_review: Rəy verin\n  message: >-\n    Seelen UI Windows üçün pulsuz və açıq mənbəli iş masası mühitidir. Burada iş\n    masanızın necə göründüyünə və davranışına tam nəzarət edirsiniz, bu da sizə\n    hər bir detalı iş axınınıza və üslubunuza uyğunlaşdırmağa imkan verir.\n  ok: Başlayaq!\n  review: >-\n    Seelen UI-dən istifadə etməkdən zövq alırsınızsa, mağazada rəy yazmağı\n    düşünün – rəyiniz layihənin böyüməsinə və təkmilləşdirilməsinə kömək edir.\n  title: Seelen UI-ə xoş gəlmisiniz!\nwidget:\n  enable: Bu widgetı aktivləşdirin\n  enable_for_monitor: Bu monitorda aktivləşdirin\n  instances: Hal\nwm:\n  animations:\n    duration: Animasiya müddəti (MS)\n    ease_function: Animasiya asanlaşdırma funksiyası\n    enable: Pəncərənin animasiyalarını aktivləşdirin\n  author: Müəllif\n  border:\n    enable: Pəncərənin sərhədini aktivləşdirin\n    offset: Sərhəd əvəzinə\n    width: Sərhəd eni\n  description: Təsvir\n  drag_behavior: Sürükləmə Davranışı\n  drag_behavior_options:\n    sort: Sırala (sürükləyərkən pəncərələri yenidən sırala)\n    swap: Swap (sürük sonunda pəncərələri dəyişdirin)\n  enable: Tiling pəncərə menecerini aktivləşdirin\n  layout: Sxem\n  resize_delta: Delta'nın ölçüsünü (%)\n  space_between_containers: Konteynerlər arasındakı boşluq\n  workspace_offset: İş yerləri ofset (kənarları)\n  workspace_padding: İş sahələri padding\n'yes': Bəli\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/bg.yml",
    "content": "action:\n  confirm: Сигурен ли си?\n  confirm_body: Това действие не може да бъде отменено.\napps_configurations:\n  app:\n    bindings: Обвързване (Забележете, че и двете опции са необходими)\n    category: Категория\n    category_placeholder: Нито един\n    monitor: Монитор\n    monitor_placeholder: Нито един\n    name: Име\n    ok_create: Създаване\n    ok_edit: Актуализация\n    ok_readonly: Редактиране като ново\n    options:\n      NoInteractive: Без интерактивност\n      VdPinned: Показване във всички работни пространства\n      WmFloat: Twm - Започнете да плавате\n      WmForce: Twm - Принудително управление\n      WmUnmanage: Twm - Премахване на управление\n    options_label: Допълнителни опции\n    title_create: Създаване {{name}}\n    title_edit: Редактиране {{name}}\n    title_readonly: Преглед {{name}}\n    weg_options_label: Опции за док/лента на задачите\n    wm_options_label: Опции за мениджър на прозорци\n    workspace: Работно пространство\n    workspace_placeholder: Нито един\n  bundled_msg: >-\n    Тези пакетни конфигурации не са редактируеми и са предназначени да ви\n    осигурят най -доброто изживяване без персонализиране. Те автоматично\n    конфигурират най -често срещаните приложения за вас.\n  bundled_title: App Config, вградено със Seelen\n  confirm_delete: Сигурни ли сте, че искате да изтриете тази конфигурация/и?\n  confirm_delete_title: Потвърдете изтриването\n  delete: Изтрий\n  export: Експорт\n  export_full: Експортиране на настройки по приложения\n  extra_info: >-\n    Потребителският интерфейс на Seelen използва само един идентификатор за\n    приложение (първото намерено съвпадение), така че редът на посочване е\n    важен, последно добавеното ще бъде с приоритет, като имайте предвид, че\n    таблицата е сортирана по подразбиране от най-новото към старото.\n  identifier:\n    add_block: Добавете блок\n    and: И\n    id: Идентификатор\n    kind: Определете от\n    matching_strategy: Съответстваща стратегия\n    matching_strategy_option:\n      contains: Съдържа\n      ends_with: Завършва с\n      equals: Равно\n      regex: Редовен израз\n      starts_with: Започва с\n    negation: Отрицание за съвпадение\n    or: ИЛИ\n    remove: Изтриване на блок\n    type:\n      class: Клас\n      exe: Exe\n      path: Пътека\n      title: Заглавие\n  import: Импортиране\n  import_full: Импортиране на настройки по приложения\n  new: Ново\n  search: Търсене\n  swap: Размяна\ncancel: Отказ\nclose: Затворете\ndelete: Изтрий\ndevtools:\n  app_folders: Папки на приложения\n  custom_config_file: Заредете персонализиран конфигурационен файл\n  data_folder: Папка с данни\n  enable: Активирайте инструментите за разработчици\n  install_folder: Инсталационна папка\n  load: Зареждане\n  settings_file: Файл за настройки\n  simulate_perm:\n    label: Искане за разрешение за симулиране на Widget\n    result_allowed: ✓ Предоставено разрешение\n    result_denied: ✗ Разрешението е отказано\n    trigger: Симулирайте\n    widget_id_placeholder: '@автор/име-на-притурка'\nextras:\n  clear_icons: Изчистване на кеша на системните икони\n  clear_icons_tooltip: >-\n    Може да се наложи рестартиране, за да се прояви напълно ефектът върху всички\n    джаджи.\n  exit: QUIT/EXIT\n  links: Официални връзки\n  relaunch: Рестартиране\n  version: Версия\n  version_fixed: >-\n    Версиите на приложението и WebView2 Runtime са коригирани. Това означава, че\n    приложението няма да получава актуализации и WebView2 Runtime няма да се\n    актуализира автоматично с актуализации на Windows.\ngeneral:\n  accent_color: Цвят на акцент\n  date_format: Формат на дата\n  date_format_how_to: Как да напиша формат на дата?\n  hardware_acceleration: Хардуерно ускорение\n  hardware_acceleration_description: >-\n    Деактивирането на хардуерното ускорение ще намали използването на паметта,\n    но може да причини проблеми с производителността. Безопасно е да се\n    деактивира, ако няма да използвате живи тапети.\n  icon_pack:\n    available: Налични пакети с икони\n    selected: Активни пакети с икони\n  language: Език\n  monday: понеделник\n  performance_mode:\n    on_battery: На батерия\n    on_energy_saver: На Saver Energy\n    options:\n      disabled: Деактивиран\n      extreme: Крайно\n      minimal: Минимален\n    plugged: Включени или зареждане\n  polling_interval: Интервал на запитване на системата\n  polling_interval_description: >-\n    Колко често (в секунди) Seelen UI проверява системни ресурси като CPU, RAM,\n    мрежа и дискова активност. По-малък брой означава по-чести актуализации, но\n    малко по-високо използване на ресурси.\n  saturday: Събота\n  start_of_week: Начало на седмицата\n  startup: Да се ​​стартирате при стартиране?\n  sunday: неделя\n  theme:\n    available: Налични теми\n    selected: Активни теми\nheader:\n  labels:\n    config: Конфигурации\n    developer: За разработчици\n    extras: Екстри\n    general: Общ\n    home: Начало\n    icon_pack_editor: Икони в кеша\n    iconpack: Пакети с икони\n    monitors: Монитори\n    plugin: Плъгини\n    resources: Ресурси\n    shortcuts: Преки пътища\n    soundpack: Звукови пакети\n    specific_apps: Настройки по приложение\n    theme: Теми\n    virtual_desk: Виртуални настолни компютри\n    wallpaper: Тапети\n    widget: Уиджети\nhome:\n  new_resources: Нови ресурси\ninherit: Наследяване\ninProgress: В ход ...\ninsert: Вмъкнете\nloading: Зареждане...\nmiscellaneous: Разни\nmonitors_configurations:\n  label: Монитор {{index}}\nmore: Още\n'no': Не\nopen: Отворен\nquit: Напусна\nremove: Извадете\nreset_all_to_default: Възстановяване на всички стойности по подразбиране\nreset_to_default: Възстановяване на стойността по подразбиране\nresources:\n  app_outdated: Този ресурс изисква по-нова версия на Seelen UI, за да работи правилно.\n  corrupted_wallpaper: >-\n    Неуспешно извличане на миниизображение - повреден или неподдържан видео\n    формат\n  delete: Изтриване на ресурс\n  discover: Открийте още ресурси\n  has_update: Налична е актуализация\n  high_impact: Силно въздействие върху производителността\n  import_wallpapers: Импортиране на местни тапети\n  open_folder: Отваряне на папката с ресурси\n  outdated: >-\n    Този ресурс е разработен за по-стара версия на Seelen UI и може да не работи\n    правилно.\n  see_on_website: Вижте на сайта\nreview_request:\n  not_now: Не сега\n  sure: Разбира се!\n  title: Харесвате ли Seelen UI?\nsave: Запазете\nsave_and_restart: Запазване и рестартиране\nsearch: Търсене\nsee_more: Вижте повече\nshortcuts:\n  duplicate_error: Този пряк път е дублиран\n  enable: Активирайте вградената система за преки пътища\n  enable_tooltip: >-\n    Деактивирайте, ако ще внедрите собствена система за преки пътища с помощта\n    на клиента на Seelen UI\n  labels:\n    create_new_workspace: Създайте ново работно пространство\n    cycle_stack_next: Стека на цикъл Напред\n    cycle_stack_prev: Цикъл стек предишен\n    cycle_wallpaper_next: Променете в следващия тапет\n    cycle_wallpaper_prev: Промяна в предишния тапет\n    decrease_height: Намаляване на височината\n    decrease_width: Намалете ширината\n    destroy_current_workspace: Унищожете текущото работно пространство\n    focus_bottom: Фокусирайте дъното\n    focus_latest: Фокусирайте последното\n    focus_left: Фокусирайте вляво\n    focus_right: Фокусирайте се вдясно\n    focus_top: Фокус отгоре\n    increase_height: Увеличете височината\n    increase_width: Увеличете ширината\n    misc_force_quit: Силата напусна\n    misc_force_restart: Принудително рестартиране\n    misc_open_settings: Отворени настройки\n    misc_toggle_lock_tracing: Toggle Lock Tracing (Logs)\n    misc_toggle_win_event_tracing: Превключване на победа проследяване на събитията (logs)\n    move_to_workspace: Преместете се в работно пространство {{0}}\n    move_window_down: Преместете прозореца до дъното\n    move_window_left: Преместете прозореца вляво\n    move_window_right: Преместете прозореца надясно\n    move_window_up: Преместете прозореца отгоре\n    pause_tiling: Направете пауза за облицовка на прозорци\n    reserve_bottom: Резервно дъно\n    reserve_float: Резервен поплавък\n    reserve_left: Резервирайте вляво\n    reserve_right: Резервирайте правилно\n    reserve_stack: Резервен стек\n    reserve_top: Резервен връх\n    restore_sizes: Възстановяване на размерите\n    send_to_workspace: Изпратете в Workspace {{0}}\n    start_weg_app: Фокусирайте или стартирайте приложение {{0}}\n    switch_to_next_workspace: Превключете към следващото работно пространство\n    switch_to_previous_workspace: Превключете към предишното работно пространство\n    switch_workspace: Превключете в работно пространство {{0}}\n    toggle_float: Toggle Window Float Mode\n    toggle_monocle: Toggle Workspace Monocle Mode\n  readonly_tooltip: Това е пряк път само за четене\n  reset: Нулирайте по подразбиране\nsides:\n  bottom: Отдолу\n  left: Наляво\n  right: Точно\n  top: Връх\ntoolbar:\n  auto_hide: Автоматично скриване\n  delay_to_hide: Забавяне, за да се скрие\n  delay_to_show: Забавяне за показване\n  dock_side: Позиция\n  enable: Активирайте фантастичната лента с инструменти\n  hide_mode:\n    always: Винаги\n    never: Никога\n    on_overlap: При припокриване\n  item_size: Размер на артикула\n  label: Лента с инструменти\n  margin: Размер на маржа\n  padding: Размер на подложката\n  placeholder: {}\nupdate:\n  available: Налична актуализация!\n  channel: Канал за актуализиране\n  downloading: Изтегляне ...\nwall:\n  backgrounds: Тапети\n  blur: Размазване\n  cancel: Отказ\n  close: затвори\n  collection_name: Име на колекцията\n  collections: Колекции\n  contrast: Контраст\n  corrupted_wallpapers_message: 'Повредени тапети, помислете за изтриване на тези:'\n  create: Създавайте\n  create_collection: Създаване на колекция\n  default_collection: Колекция по подразбиране\n  delete_collection: Изтриване на колекция\n  edit_collection: Редактиране на колекция\n  enable: Активирайте мениджъра на тапети\n  extend: Удължете основното\n  fit:\n    contain: Съдържат\n    cover: Корица\n    fill: Напълнете\n  flipHorizontal: Хоризонтално обръщане\n  flipVertical: Вертикално обръщане\n  generating_thumbnails: Генериране на тапети с миниатюри\n  hours: часове\n  interval: Променете тапетите всеки\n  minutes: минути\n  monitor_collection: Колекция монитори\n  multimonitor_behaviour: Мултимониторно поведение\n  muted: Заглушаване на видео аудио\n  no_background: Празно слайдшоу, вместо това използвайки фона на темата.\n  no_collections: >-\n    Все още няма създадени колекции. Щракнете върху бутона +, за да създадете\n    такъв.\n  objectFit: Подходящ фон\n  objectPosition: Фонова позиция\n  overlayColor: Цвят на наслагване\n  overlayMixBlendMode: Режим на смесване на наслагване\n  per_monitor: На монитор\n  playback: Скорост на възпроизвеждане\n  position:\n    bottom: Дъно\n    center: Център\n    left: Вляво\n    right: Вдясно\n    top: Топ\n  processing_video: Обработка на видео\n  random: Рандомизиране слайдшоу\n  saturation: Насищане\n  seconds: секунди\n  select_collection: Изберете Колекция\n  thumbnail_generation_complete: Генерирането на миниатюри е завършено\n  thumbnail_generation_finished: Генерирането на миниатюри завърши успешно\n  wallpaper_collection: Колекция тапети\n  wallpaper_settings: Настройки на тапета\n  withOverlay: С наслагване\n  workspace_collections: Колекции на работното пространство\nweg:\n  auto_hide: Автоматично скриване\n  delay_to_hide: Забавяне, за да се скрие\n  delay_to_show: Забавяне за показване\n  dock_side: Позиция\n  enable: Активирайте док/лента на задачите\n  filtering: Филтриране на елементи\n  gap: Пропаст\n  hide_mode:\n    always: Винаги\n    never: Никога\n    on_overlap: При припокриване\n  items:\n    gap: Пространство между артикулите\n    label: Елементи\n    pinned_visibility:\n      always: Винаги\n      label: Видимост на фиксираните елементи\n      when_primary: Когато мониторът е основен\n    show_instance_counter: Показване на брояча на отворените прозорци\n    show_window_title: Показване на заглавието на отворения прозорец (само хоризонтално)\n    size: Размер на артикула\n    split_windows: Разделени прозорци (един елемент на прозорец)\n    temporal_visibility:\n      all: Всички\n      label: Видимост на незакрепените елементи\n      on_monitor: На монитора\n    visible_separators: Видими разделители\n  label: Док/лента на задачите\n  margin: Марж\n  mode:\n    full_width: Пълна ширина на екрана\n    min_content: Малък, колкото може да бъде\n  padding: Подплънки\n  show_end_task: Показване на крайната задача в лентата на задачите\n  width: Ширина\nwelcome:\n  give_a_review: Дайте преглед\n  message: >-\n    Seelen UI е безплатна десктоп среда с отворен код за Windows. Тук имате\n    пълен контрол върху това как вашият работен плот изглежда и се държи, което\n    ви позволява да персонализирате всеки детайл, за да съответства на вашия\n    работен процес и стил.\n  ok: Да започваме!\n  review: >-\n    Ако обичате да използвате Seelen UI, помислете дали да не оставите отзив в\n    магазина - вашата обратна връзка помага на проекта да расте и да се\n    подобрява.\n  title: Добре дошли в Seelen UI!\nwidget:\n  enable: Активиране на тази джаджа\n  enable_for_monitor: Разрешаване на този монитор\n  instances: Инстанции\nwm:\n  animations:\n    duration: Продължителност на анимацията (MS)\n    ease_function: Функция за облекчаване на анимацията\n    enable: Активирайте анимациите на Window\n  author: Автор\n  border:\n    enable: Активирайте границата на Windows\n    offset: Гранично изместване\n    width: Гранична ширина\n  description: Описание\n  drag_behavior: Плъзнете поведение\n  drag_behavior_options:\n    sort: Сортиране (пренареждане на прозорците при плъзгане)\n    swap: Размяна (размяна на прозорци при плъзгане)\n  enable: Активирайте мениджъра на прозорци за облицовка\n  layout: Оформление\n  resize_delta: Оразмерете делтата (%)\n  space_between_containers: Пространство между контейнерите\n  workspace_offset: Изместване на работните пространства (маржове)\n  workspace_padding: Работни пространства подплънки\n'yes': Да\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/bn.yml",
    "content": "action:\n  confirm: তুমি কি নিশ্চিত?\n  confirm_body: এই ক্রিয়াটি পূর্বাবস্থায় ফিরে যেতে পারে না।\napps_configurations:\n  app:\n    bindings: বাইন্ডিং (নোট উভয় বিকল্পের প্রয়োজন)\n    category: বিভাগ\n    category_placeholder: কিছুই না\n    monitor: মনিটর\n    monitor_placeholder: কিছুই না\n    name: নাম\n    ok_create: সৃষ্টি\n    ok_edit: হালনাগাদ\n    ok_readonly: নতুন হিসাবে সম্পাদনা\n    options:\n      NoInteractive: কোন ইন্টারেক্টিভ\n      VdPinned: সমস্ত কর্মক্ষেত্রে দেখান\n      WmFloat: Twm - ভাসতে শুরু করুন\n      WmForce: Twm - ফোর্স ম্যানেজ\n      WmUnmanage: Twm - Unmanage\n    options_label: অতিরিক্ত বিকল্প\n    title_create: '{{নাম}} তৈরি করা'\n    title_edit: সম্পাদনা {{নাম}}\n    title_readonly: '{{নাম}} দেখুন'\n    weg_options_label: ডক/টাস্কবার বিকল্প\n    wm_options_label: উইন্ডো ম্যানেজার বিকল্পগুলি\n    workspace: কর্মক্ষেত্র\n    workspace_placeholder: কিছুই না\n  bundled_msg: >-\n    এই বান্ডিলযুক্ত কনফিগারেশনগুলি সম্পাদনাযোগ্য নয় এবং আপনাকে কাস্টমাইজেশন\n    ছাড়াই সেরা অভিজ্ঞতা সরবরাহ করার জন্য ডিজাইন করা হয়েছে। তারা\n    স্বয়ংক্রিয়ভাবে আপনার জন্য সর্বাধিক সাধারণ অ্যাপ্লিকেশনগুলি কনফিগার করে।\n  bundled_title: অ্যাপ কনফিগারেশন সিলেনের সাথে বান্ডিল\n  confirm_delete: আপনি কি নিশ্চিত যে আপনি এই কনফিগারেশন/গুলি মুছতে চান?\n  confirm_delete_title: নিশ্চিত বাতিল\n  delete: মুছে ফেলা\n  export: রফতানি\n  export_full: অ্যাপ্লিকেশন দ্বারা রফতানি সেটিংস\n  extra_info: >-\n    সিলেন ইউআই প্রতি অ্যাপ্লিকেশনটিতে কেবলমাত্র একটি শনাক্তকারী ব্যবহার করুন\n    (প্রথম ম্যাচটি পাওয়া গেছে) সুতরাং কীভাবে নির্দিষ্ট করা হয়েছে তার ক্রমটি\n    গুরুত্বপূর্ণ, সর্বশেষ যোগ করা অগ্রাধিকার দেওয়া হবে, কারণ দ্রষ্টব্য টেবিলটি\n    সর্বশেষ থেকে পুরানো পর্যন্ত ডিফল্টরূপে বাছাই করা হয়েছে।\n  identifier:\n    add_block: ব্লক যুক্ত করুন\n    and: এবং\n    id: সনাক্তকারী\n    kind: দ্বারা চিহ্নিত করুন\n    matching_strategy: ম্যাচিং কৌশল\n    matching_strategy_option:\n      contains: ধারণ করে\n      ends_with: দিয়ে শেষ হয়\n      equals: সমান\n      regex: নিয়মিত অভিব্যক্তি\n      starts_with: দিয়ে শুরু হয়\n    negation: ম্যাচিং অবহেলা\n    or: বা\n    remove: ব্লক মুছুন\n    type:\n      class: ক্লাস\n      exe: Exe\n      path: পথ\n      title: শিরোনাম\n  import: আমদানি\n  import_full: আবেদন দ্বারা সেটিংস আমদানি করুন\n  new: নতুন\n  search: অনুসন্ধান\n  swap: অদলবদল\ncancel: বাতিল\nclose: বন্ধ\ndelete: মুছে ফেলা\ndevtools:\n  app_folders: অ্যাপ্লিকেশন ফোল্ডার\n  custom_config_file: কাস্টম কনফিগার ফাইল লোড করুন\n  data_folder: ডেটা ফোল্ডার\n  enable: বিকাশকারী সরঞ্জাম সক্ষম করুন\n  install_folder: ইনস্টলেশন ফোল্ডার\n  load: বোঝা\n  settings_file: সেটিংস ফাইল\n  simulate_perm:\n    label: উইজেট অনুমতি অনুরোধ অনুকরণ\n    result_allowed: ✓ অনুমতি দেওয়া হয়েছে\n    result_denied: ✗ অনুমতি অস্বীকৃত\n    trigger: অনুকরণ\n    widget_id_placeholder: '@লেখক/উইজেট-নাম'\nextras:\n  clear_icons: ক্লিয়ার সিস্টেম আইকন ক্যাশে\n  clear_icons_tooltip: >-\n    সমস্ত উইজেটগুলিতে পুরোপুরি কার্যকর করার জন্য একটি পুনঃসূচনা প্রয়োজন হতে\n    পারে\n  exit: প্রস্থান/প্রস্থান\n  links: অফিসিয়াল লিঙ্ক\n  relaunch: পুনরায় চালু\n  version: সংস্করণ\n  version_fixed: >-\n    অ্যাপ্লিকেশন এবং WebView2 রানটাইম সংস্করণ স্থির করা হয়েছে। এর মানে হল যে\n    অ্যাপ্লিকেশনটি আপডেট পাবে না এবং WebView2 রানটাইম স্বয়ংক্রিয়ভাবে উইন্ডোজ\n    আপডেটের সাথে আপডেট হবে না।\ngeneral:\n  accent_color: সুরের ধাপের রঙ\n  date_format: তারিখ ফর্ম্যাট\n  date_format_how_to: কিভাবে একটি তারিখ বিন্যাস লিখতে?\n  hardware_acceleration: হার্ডওয়্যার ত্বরণ\n  hardware_acceleration_description: >-\n    হার্ডওয়্যার ত্বরণ নিষ্ক্রিয় করা মেমরি ব্যবহার হ্রাস করবে, কিন্তু\n    কর্মক্ষমতা সমস্যা সৃষ্টি করতে পারে। আপনি লাইভ ওয়ালপেপার ব্যবহার না করলে\n    অক্ষম করা নিরাপদ।\n  icon_pack:\n    available: উপলব্ধ আইকন প্যাক\n    selected: সক্রিয় আইকন প্যাক\n  language: ভাষা\n  monday: সোমবার\n  performance_mode:\n    on_battery: ব্যাটারিতে\n    on_energy_saver: শক্তি সঞ্চয় উপর\n    options:\n      disabled: অক্ষম\n      extreme: চরম\n      minimal: ন্যূনতম\n    plugged: প্লাগ বা চার্জিং\n  polling_interval: সিস্টেম পোলিং ব্যবধান\n  polling_interval_description: >-\n    কত ঘন ঘন (সেকেন্ডে) Seelen UI CPU, RAM, নেটওয়ার্ক এবং ডিস্ক কার্যকলাপের মতো\n    সিস্টেম সংস্থানগুলি পরীক্ষা করে। একটি ছোট সংখ্যা মানে আরো ঘন ঘন আপডেট,\n    কিন্তু সামান্য বেশি সম্পদ ব্যবহার।\n  saturday: শনিবার\n  start_of_week: সপ্তাহের শুরু\n  startup: প্রারম্ভকালে চালানো?\n  sunday: রবিবার\n  theme:\n    available: উপলব্ধ থিম\n    selected: সক্রিয় থিম\nheader:\n  labels:\n    config: কনফিগারেশন\n    developer: বিকাশকারীদের জন্য\n    extras: অতিরিক্ত\n    general: সাধারণ\n    home: বাড়ি\n    icon_pack_editor: ক্যাশেড আইকন\n    iconpack: আইকন প্যাকস\n    monitors: মনিটর\n    plugin: প্লাগইন\n    resources: সংস্থান\n    shortcuts: শর্টকাটস\n    soundpack: সাউন্ড প্যাকস\n    specific_apps: আবেদন দ্বারা সেটিংস\n    theme: থিম\n    virtual_desk: ভার্চুয়াল ডেস্কটপস\n    wallpaper: ওয়ালপেপার\n    widget: উইজেটস\nhome:\n  new_resources: নতুন সংস্থান\ninherit: উত্তরাধিকারী\ninProgress: চলমান...\ninsert: .োকান\nloading: লোড হচ্ছে ...\nmiscellaneous: বিবিধ\nmonitors_configurations:\n  label: '{{সূচক}} মনিটর'\nmore: আরও\n'no': না\nopen: খোলা\nquit: প্রস্থান\nremove: সরান\nreset_all_to_default: সমস্ত ডিফল্ট মানগুলিতে পুনরায় সেট করুন\nreset_to_default: ডিফল্ট মান পুনরায় সেট করুন\nresources:\n  app_outdated: >-\n    এই সংস্থানটির জন্য সঠিকভাবে কাজ করার জন্য সিলেন ইউআইয়ের একটি নতুন সংস্করণ\n    প্রয়োজন।\n  corrupted_wallpaper: থাম্বনেল বের করতে ব্যর্থ হয়েছে - দূষিত বা অসমর্থিত ভিডিও বিন্যাস\n  delete: সংস্থান মুছুন\n  discover: আরও সংস্থান আবিষ্কার করুন\n  has_update: একটি আপডেট উপলব্ধ\n  high_impact: কর্মক্ষমতা উপর উচ্চ প্রভাব\n  import_wallpapers: স্থানীয় ওয়ালপেপার আমদানি করুন\n  open_folder: ওপেন রিসোর্স ফোল্ডার\n  outdated: >-\n    এই সংস্থানটি সিলেন ইউআইয়ের একটি পুরানো সংস্করণের জন্য ডিজাইন করা হয়েছিল\n    এবং এটি সঠিকভাবে কাজ নাও করতে পারে।\n  see_on_website: ওয়েবসাইটে দেখুন\nreview_request:\n  not_now: এখন না\n  sure: নিশ্চিত!\n  title: Seelen UI উপভোগ করছেন?\nsave: সংরক্ষণ\nsave_and_restart: সংরক্ষণ করুন এবং পুনরায় চালু করুন\nsearch: অনুসন্ধান করুন\nsee_more: আরও দেখুন\nshortcuts:\n  duplicate_error: এই শর্টকাট ডুপ্লিকেট করা হয়\n  enable: অন্তর্নির্মিত শর্টকাট সিস্টেম সক্ষম করুন\n  enable_tooltip: >-\n    আপনি যদি সিলেন ইউআই ক্লায়েন্ট ব্যবহার করে নিজের শর্টকাট সিস্টেমটি প্রয়োগ\n    করেন তবে অক্ষম করুন\n  labels:\n    create_new_workspace: নতুন কর্মক্ষেত্র তৈরি করুন\n    cycle_stack_next: সাইকেল স্ট্যাক পরবর্তী\n    cycle_stack_prev: চক্র স্ট্যাক পূর্ববর্তী\n    cycle_wallpaper_next: পরবর্তী ওয়ালপেপারে পরিবর্তন করুন\n    cycle_wallpaper_prev: পূর্ববর্তী ওয়ালপেপারে পরিবর্তন করুন\n    decrease_height: উচ্চতা হ্রাস\n    decrease_width: প্রস্থ হ্রাস\n    destroy_current_workspace: বর্তমান কর্মক্ষেত্র ধ্বংস করুন\n    focus_bottom: ফোকাস নীচে\n    focus_latest: সর্বশেষ ফোকাস\n    focus_left: ফোকাস বাম\n    focus_right: ডান ফোকাস\n    focus_top: ফোকাস শীর্ষ\n    increase_height: উচ্চতা বৃদ্ধি\n    increase_width: প্রস্থ বৃদ্ধি\n    misc_force_quit: ফোর্স প্রস্থান\n    misc_force_restart: জোর পুনরায় চালু করুন\n    misc_open_settings: ওপেন সেটিংস\n    misc_toggle_lock_tracing: টগল লক ট্রেসিং (লগস)\n    misc_toggle_win_event_tracing: টগল উইন ইভেন্ট ট্রেসিং (লগস)\n    move_to_workspace: ওয়ার্কস্পেসে যান {{0}}\n    move_window_down: নীচে উইন্ডো সরান\n    move_window_left: বাম দিকে উইন্ডো সরান\n    move_window_right: উইন্ডোটি ডানদিকে সরান\n    move_window_up: শীর্ষে উইন্ডো সরান\n    pause_tiling: টাইলিং উইন্ডো ম্যানেজার বিরতি দিন\n    reserve_bottom: রিজার্ভ নীচে\n    reserve_float: রিজার্ভ ফ্লোট\n    reserve_left: রিজার্ভ বাম\n    reserve_right: রিজার্ভ রিজার\n    reserve_stack: রিজার্ভ স্ট্যাক\n    reserve_top: রিজার্ভ শীর্ষ\n    restore_sizes: আকার পুনরুদ্ধার\n    send_to_workspace: ওয়ার্কস্পেসে প্রেরণ করুন {{0}}\n    start_weg_app: ফোকাস বা শুরু করুন অ্যাপ্লিকেশন {{0}}\n    switch_to_next_workspace: পরবর্তী কর্মক্ষেত্রে স্যুইচ করুন\n    switch_to_previous_workspace: পূর্ববর্তী কর্মক্ষেত্রে স্যুইচ করুন\n    switch_workspace: ওয়ার্কস্পেসে স্যুইচ করুন {{0}}\n    toggle_float: টগল উইন্ডো ফ্লোট মোড\n    toggle_monocle: টগল ওয়ার্কস্পেস মনোকল মোড\n  readonly_tooltip: এটি কেবল একটি পঠিত শর্টকাট\n  reset: ডিফল্ট পুনরায় সেট করুন\nsides:\n  bottom: নীচে\n  left: বাম\n  right: ঠিক আছে\n  top: শীর্ষ\ntoolbar:\n  auto_hide: অটো আড়াল\n  delay_to_hide: আড়াল করতে বিলম্ব\n  delay_to_show: দেখাতে বিলম্ব\n  dock_side: অবস্থান\n  enable: অভিনব সরঞ্জামদণ্ড সক্ষম করুন\n  hide_mode:\n    always: সর্বদা\n    never: কখনই না\n    on_overlap: ওভারল্যাপে\n  item_size: আইটেম আকার\n  label: সরঞ্জামদণ্ড\n  margin: মার্জিন সাইজ\n  padding: প্যাডিং আকার\n  placeholder: {}\nupdate:\n  available: আপডেট উপলব্ধ!\n  channel: আপডেট চ্যানেল\n  downloading: ডাউনলোড ...\nwall:\n  backgrounds: ওয়ালপেপার\n  blur: অস্পষ্টতা\n  cancel: বাতিল করুন\n  close: বন্ধ\n  collection_name: সংগ্রহের নাম\n  collections: সংগ্রহ\n  contrast: বিপরীতে\n  corrupted_wallpapers_message: 'দূষিত ওয়ালপেপার, এগুলি মুছে ফেলার কথা বিবেচনা করুন:'\n  create: তৈরি করুন\n  create_collection: সংগ্রহ তৈরি করুন\n  default_collection: ডিফল্ট সংগ্রহ\n  delete_collection: সংগ্রহ মুছুন\n  edit_collection: সংগ্রহ সম্পাদনা করুন\n  enable: ওয়ালপেপার ম্যানেজার সক্ষম করুন\n  extend: প্রাথমিক প্রসারিত\n  fit:\n    contain: ধারণ করে\n    cover: কভার\n    fill: পূরণ করুন\n  flipHorizontal: অনুভূমিক ফ্লিপ\n  flipVertical: উল্লম্ব ফ্লিপ\n  generating_thumbnails: ওয়ালপেপার থাম্বনেল তৈরি করা হচ্ছে\n  hours: ঘন্টা\n  interval: প্রতিটি ওয়ালপেপার পরিবর্তন করুন\n  minutes: মিনিট\n  monitor_collection: সংগ্রহ মনিটর\n  multimonitor_behaviour: মাল্টিমনিটর আচরণ\n  muted: ভিডিও অডিও নিঃশব্দ করুন\n  no_background: পরিবর্তে থিমের পটভূমি ব্যবহার করে খালি স্লাইডশো।\n  no_collections: এখনো কোনো সংগ্রহ তৈরি করা হয়নি। একটি তৈরি করতে + বোতামে ক্লিক করুন।\n  objectFit: পটভূমি ফিট\n  objectPosition: পটভূমি অবস্থান\n  overlayColor: ওভারলে রঙ\n  overlayMixBlendMode: ওভারলে মিক্স মিশ্রণ মোড\n  per_monitor: মনিটর প্রতি\n  playback: প্লেব্যাক গতি\n  position:\n    bottom: নীচে\n    center: কেন্দ্র\n    left: বাম\n    right: ঠিক আছে\n    top: শীর্ষ\n  processing_video: ভিডিও প্রসেস করা হচ্ছে\n  random: এলোমেলোভাবে স্লাইডশো\n  saturation: স্যাচুরেশন\n  seconds: সেকেন্ড\n  select_collection: সংগ্রহ নির্বাচন করুন\n  thumbnail_generation_complete: থাম্বনেইল জেনারেশন সম্পূর্ণ\n  thumbnail_generation_finished: থাম্বনেইল জেনারেশন সফলভাবে শেষ হয়েছে\n  wallpaper_collection: ওয়ালপেপার সংগ্রহ\n  wallpaper_settings: ওয়ালপেপার সেটিংস\n  withOverlay: ওভারলে সহ\n  workspace_collections: ওয়ার্কস্পেস সংগ্রহ\nweg:\n  auto_hide: স্বয়ং চামড়া\n  delay_to_hide: আড়াল করতে বিলম্ব\n  delay_to_show: দেখাতে বিলম্ব\n  dock_side: অবস্থান\n  enable: ডক/টাস্কবার সক্ষম করুন\n  filtering: আইটেম ফিল্টারিং\n  gap: ফাঁক\n  hide_mode:\n    always: সর্বদা\n    never: কখনই না\n    on_overlap: ওভারল্যাপে\n  items:\n    gap: আইটেম মধ্যে স্থান\n    label: আইটেম\n    pinned_visibility:\n      always: সর্বদা\n      label: পিন করা আইটেম দৃশ্যমানতা\n      when_primary: যখন মনিটর প্রাথমিক হয়\n    show_instance_counter: ওপেন উইন্ডোজ কাউন্টার দেখান\n    show_window_title: ওপেন উইন্ডো শিরোনাম দেখান (কেবল অনুভূমিক)\n    size: আইটেমের আকার\n    split_windows: স্প্লিট উইন্ডোজ (প্রতি উইন্ডোতে একটি আইটেম)\n    temporal_visibility:\n      all: সব\n      label: আনপিন করা আইটেম দৃশ্যমানতা\n      on_monitor: মনিটরে\n    visible_separators: দৃশ্যমান বিভাজক\n  label: ডক/টাস্কবার\n  margin: মার্জিন\n  mode:\n    full_width: পূর্ণ পর্দা প্রস্থ\n    min_content: যতটা সম্ভব ছোট\n  padding: প্যাডিং\n  show_end_task: টাস্কবারে শেষ টাস্ক দেখান\n  width: প্রস্থ\nwelcome:\n  give_a_review: একটি পর্যালোচনা দিন\n  message: >-\n    Seelen UI হল উইন্ডোজের জন্য একটি বিনামূল্যের এবং ওপেন সোর্স ডেস্কটপ পরিবেশ।\n    এখানে আপনার ডেস্কটপ কেমন দেখায় এবং আচরণ করে তার উপর আপনার সম্পূর্ণ\n    নিয়ন্ত্রণ রয়েছে, আপনাকে আপনার কর্মপ্রবাহ এবং শৈলীর সাথে মেলে প্রতিটি বিবরণ\n    কাস্টমাইজ করার অনুমতি দেয়।\n  ok: শুরু করা যাক!\n  review: >-\n    আপনি যদি Seelen UI ব্যবহার করে উপভোগ করেন, তাহলে দোকানে একটি পর্যালোচনা করার\n    কথা বিবেচনা করুন — আপনার প্রতিক্রিয়া প্রকল্পের বৃদ্ধি এবং উন্নতিতে সাহায্য\n    করে।\n  title: Seelen UI তে স্বাগতম!\nwidget:\n  enable: এই উইজেট সক্ষম করুন\n  enable_for_monitor: এই মনিটরে সক্ষম করুন\n  instances: উদাহরণ\nwm:\n  animations:\n    duration: অ্যানিমেশন সময়কাল (এমএস)\n    ease_function: অ্যানিমেশন ইজিং ফাংশন\n    enable: উইন্ডোর অ্যানিমেশনগুলি সক্ষম করুন\n  author: লেখক\n  border:\n    enable: উইন্ডোর সীমানা সক্ষম করুন\n    offset: সীমান্ত অফসেট\n    width: সীমানার প্রশস্থতা\n  description: বর্ণনা\n  drag_behavior: টেনে আনার আচরণ\n  drag_behavior_options:\n    sort: সাজান (ড্র্যাগ করার সময় উইন্ডোজ পুনরায় সাজান)\n    swap: অদলবদল (ড্র্যাগ এন্ডে সোয়াপ উইন্ডো)\n  enable: টাইলিং উইন্ডো ম্যানেজার সক্ষম করুন\n  layout: বিন্যাস\n  resize_delta: পুনরায় আকার ডেল্টা (%)\n  space_between_containers: পাত্রে স্থান\n  workspace_offset: ওয়ার্কস্পেস অফসেট (মার্জিন)\n  workspace_padding: ওয়ার্কস্পেস প্যাডিং\n'yes': হ্যাঁ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/bs.yml",
    "content": "action:\n  confirm: Jeste li sigurni?\n  confirm_body: Ova akcija se ne može poništiti.\napps_configurations:\n  app:\n    bindings: Vezanje (napominje su obavezne opcije)\n    category: Kategorija\n    category_placeholder: Nijedan\n    monitor: Monitor\n    monitor_placeholder: Nijedan\n    name: Ime\n    ok_create: Stvoriti\n    ok_edit: Ažurirati\n    ok_readonly: Uredi kao novo\n    options:\n      NoInteractive: No Interactive\n      VdPinned: Prikaži u svim radnim prostorima\n      WmFloat: Twm - Počnite plutati\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Dodatne opcije\n    title_create: Stvarajući {{ime}}\n    title_edit: Uređivanje {{ime}}\n    title_readonly: Gledanje {{ime}}\n    weg_options_label: Opcije pristaništa / trake zadataka\n    wm_options_label: Opcije prozora upravitelja\n    workspace: Radni prostor\n    workspace_placeholder: Nijedan\n  bundled_msg: >-\n    Ove uleteljene konfiguracije nisu uređivanje i dizajnirane su da vam pruže\n    najbolje iskustvo bez prilagodbe. Oni automatski konfiguriraju najčešće\n    aplikacije za vas.\n  bundled_title: Konfiguracija aplikacija paketa sa seelen\n  confirm_delete: Jeste li sigurni da želite izbrisati ovu konfiguraciju / s?\n  confirm_delete_title: Potvrdite Izbriši\n  delete: Izbrisati\n  export: Izvoz\n  export_full: Izvoz postavki po nanosu\n  extra_info: >-\n    Seelen UI Koristite samo jedan identifikator po aplikaciji (prvo pronađeno),\n    tako da je narudžba u tome kako je specifično, najnoviji dodani bit će\n    prioritet, jer je u priopćenju tablica po zadanim postavkama sortirana iz\n    posljednjeg.\n  identifier:\n    add_block: Dodajte blok\n    and: I\n    id: Identifikator\n    kind: Identificirati\n    matching_strategy: Strategija podudaranja\n    matching_strategy_option:\n      contains: Sadrži\n      ends_with: Završava sa\n      equals: Jednako\n      regex: Regularni izraz\n      starts_with: Počinje sa\n    negation: Negirati podudaranje\n    or: Ili\n    remove: Obriši blok\n    type:\n      class: Klasa\n      exe: Exe\n      path: Put\n      title: Naslov\n  import: Uvoz\n  import_full: Uvoz postavki po nanosu\n  new: Novo\n  search: Pretražiti\n  swap: Swap\ncancel: Otkazati\nclose: Zatvoriti\ndelete: Izbrisati\ndevtools:\n  app_folders: App Mape\n  custom_config_file: Učitajte prilagođenu datoteku config config\n  data_folder: Mapa podataka\n  enable: Omogućite alate za programere\n  install_folder: Instalacijska mapa\n  load: Opteretiti\n  settings_file: Datoteka postavki\n  simulate_perm:\n    label: Simulirajte zahtjev za dozvolu za widget\n    result_allowed: ✓ Dozvola data\n    result_denied: ✗ Dozvola odbijena\n    trigger: Simuliraj\n    widget_id_placeholder: '@autor/ime-widgeta'\nextras:\n  clear_icons: Clear System Icons predmemorija\n  clear_icons_tooltip: Može se ponovo pokrenuti za u potpunosti stupiti na snagu na svim widgetima\n  exit: Prestanite / izlaz\n  links: Službene veze\n  relaunch: Ponovno pokretanje\n  version: Verzija\n  version_fixed: >-\n    Popravljene su verzije aplikacije i WebView2 Runtime. To znači da aplikacija\n    neće primati ažuriranja i da WebView2 Runtime neće biti automatski ažuriran\n    Windows ažuriranjima.\ngeneral:\n  accent_color: Akcentna boja\n  date_format: Format datuma\n  date_format_how_to: Kako napisati format datuma?\n  hardware_acceleration: Hardversko ubrzanje\n  hardware_acceleration_description: >-\n    Onemogućavanje hardverskog ubrzanja će smanjiti upotrebu memorije, ali može\n    uzrokovati probleme s performansama. Sigurno je onemogućiti ako nećete\n    koristiti žive pozadine.\n  icon_pack:\n    available: Dostupni paketi ikona\n    selected: Aktivni paketi ikona\n  language: Jezik\n  monday: ponedjeljak\n  performance_mode:\n    on_battery: Na bateriji\n    on_energy_saver: Na uštedu energije\n    options:\n      disabled: Invalid\n      extreme: Ekstremno\n      minimal: Minimalan\n    plugged: Uključen ili punjenje\n  polling_interval: Sistemski interval anketiranja\n  polling_interval_description: >-\n    Koliko često (u sekundama) Seelen UI provjerava sistemske resurse kao što su\n    CPU, RAM, mreža i aktivnost diska. Manji broj znači češće ažuriranje, ali\n    nešto veću upotrebu resursa.\n  saturday: Subota\n  start_of_week: Početak sedmice\n  startup: Trčite na pokretanju?\n  sunday: Nedjelja\n  theme:\n    available: Dostupne teme\n    selected: Aktivne teme\nheader:\n  labels:\n    config: Konfiguracija\n    developer: Za programere\n    extras: Dodaci\n    general: Opći\n    home: Dom\n    icon_pack_editor: Keširane ikone\n    iconpack: Paketa ikona\n    monitors: Monitori\n    plugin: Dodaci\n    resources: Resursi\n    shortcuts: Prečice\n    soundpack: Zvučni paketi\n    specific_apps: Podešavanja po nanosu\n    theme: Tema\n    virtual_desk: Virtualne radne površine\n    wallpaper: Pozadine\n    widget: Widgeti\nhome:\n  new_resources: Novi resursi\ninherit: Nasljediti\ninProgress: U toku...\ninsert: Umetnuti\nloading: Učitavanje...\nmiscellaneous: Ostalo\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Više\n'no': Ne\nopen: Otvoren\nquit: Prestati\nremove: Ukloniti\nreset_all_to_default: Poništite sve na zadane vrijednosti\nreset_to_default: Resetiranje na zadanu vrijednost\nresources:\n  app_outdated: Ovaj resurs zahtijeva noviju verziju Seelen Ui da radi pravilno.\n  corrupted_wallpaper: Ekstrahiranje sličice nije uspjelo - oštećen ili nepodržan video format\n  delete: Izbriši resurs\n  discover: Otkrijte više resursa\n  has_update: Dostupno je ažuriranje\n  high_impact: Veliki uticaj na performanse\n  import_wallpapers: Uvoz lokalnih pozadina\n  open_folder: Otvorite mapu resursa\n  outdated: >-\n    Ovaj je resurs dizajniran za stariju verziju Seelena UI-a i možda neće\n    raditi ispravno.\n  see_on_website: Vidi na web stranici\nreview_request:\n  not_now: Ne sada\n  sure: Naravno!\n  title: Uživate u korisničkom sučelju Seelen?\nsave: Sačuvati\nsave_and_restart: Spremi i ponovno pokretanje\nsearch: Traži\nsee_more: Pogledajte više\nshortcuts:\n  duplicate_error: Ova prečica je duplirana\n  enable: Omogući ugrađene sistem prečaca\n  enable_tooltip: >-\n    Onemogućite ako ćete implementirati vlastite sustav prečaca pomoću klijenta\n    SEELEN UI\n  labels:\n    create_new_workspace: Kreirajte novi radni prostor\n    cycle_stack_next: Sljedeći ciklus stack\n    cycle_stack_prev: Ciklus Stack Prethodni\n    cycle_wallpaper_next: Promjena u sljedeću pozadinu\n    cycle_wallpaper_prev: Promjena u prethodnu pozadinu\n    decrease_height: Smanjenje visine\n    decrease_width: Smanjenje širine\n    destroy_current_workspace: Uništite trenutni radni prostor\n    focus_bottom: Fokus dno\n    focus_latest: Fokus najnovije\n    focus_left: Fokus ostavljen\n    focus_right: Fokusirajte se\n    focus_top: Focus Top\n    increase_height: Povećati visinu\n    increase_width: Povećati širinu\n    misc_force_quit: Prisiliti\n    misc_force_restart: Restart sile\n    misc_open_settings: Otvorite postavke\n    misc_toggle_lock_tracing: Prebacivanje zaključavanja (trupci)\n    misc_toggle_win_event_tracing: Toggle win za praćenje događaja (trupci)\n    move_to_workspace: Pređite na radni prostor {{0}}\n    move_window_down: Pomičite prozor na dno\n    move_window_left: Pomičite prozor ulijevo\n    move_window_right: Pomičite prozor udesno\n    move_window_up: Pomičite prozor na vrh\n    pause_tiling: Pauzirajte menadžer za polaganje prozora\n    reserve_bottom: Rezervno dno\n    reserve_float: Rezervni plutajući\n    reserve_left: Rezerva\n    reserve_right: Rezervirajte pravo\n    reserve_stack: Rezervat\n    reserve_top: Rezervat\n    restore_sizes: Vrati veličine\n    send_to_workspace: Pošaljite u radni prostor {{0}}\n    start_weg_app: Fokus ili pokretanje aplikacije {{0}}\n    switch_to_next_workspace: Prebacite se na sljedeći radni prostor\n    switch_to_previous_workspace: Prebacite se na prethodni radni prostor\n    switch_workspace: Prebacite se na radni prostor {{0}}\n    toggle_float: Preklopni režim plovka prozora\n    toggle_monocle: Toggle Workspace Monocle mod\n  readonly_tooltip: Ovo je prečica za čitanje\n  reset: Resetiranje na zadane vrijednosti\nsides:\n  bottom: Dno\n  left: Lijevo\n  right: Pravo\n  top: Vrh\ntoolbar:\n  auto_hide: Auto se sakrio\n  delay_to_hide: Odložite da se sakrijete\n  delay_to_show: Kašnjenje za prikaz\n  dock_side: Položaj\n  enable: Omogući fantastičnu alatnu traku\n  hide_mode:\n    always: Uvijek\n    never: Nikad\n    on_overlap: Na preklapanje\n  item_size: Veličina artikla\n  label: Alatna traka\n  margin: Veličina margine\n  padding: Padding Size\n  placeholder: {}\nupdate:\n  available: Ažuriraj dostupno!\n  channel: Ažuriraj kanal\n  downloading: Preuzimanje ...\nwall:\n  backgrounds: Pozadine\n  blur: Zamutiti\n  cancel: Otkaži\n  close: Zatvori\n  collection_name: Naziv kolekcije\n  collections: Zbirke\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Oštećene pozadine, razmislite o brisanju ovih:'\n  create: Kreiraj\n  create_collection: Kreiraj kolekciju\n  default_collection: Default Collection\n  delete_collection: Izbriši kolekciju\n  edit_collection: Uredi kolekciju\n  enable: Omogućite menadžer pozadina\n  extend: Produžite primarni\n  fit:\n    contain: Sadržiti\n    cover: Pokriti\n    fill: Ispuniti\n  flipHorizontal: Flip horizontalni\n  flipVertical: Okrenuti vertikalu\n  generating_thumbnails: Generiranje sličica pozadine\n  hours: sati\n  interval: Promijenite pozadinu svake\n  minutes: minuta\n  monitor_collection: Monitor Collection\n  multimonitor_behaviour: Multimonitor Behavior\n  muted: Isključite zvuk video zapisa\n  no_background: Prazno prezentacija, koristeći temu, umjesto toga.\n  no_collections: Još nema kreiranih kolekcija. Kliknite na dugme + da ga kreirate.\n  objectFit: Pozadina fit\n  objectPosition: Pozadinsko pozicija\n  overlayColor: Boja prekrivanja\n  overlayMixBlendMode: Prekrivač miks mod mješavine\n  per_monitor: Po monitoru\n  playback: Brzina reprodukcije\n  position:\n    bottom: Dno\n    center: Centar\n    left: Lijevo\n    right: Pravo\n    top: Vrh\n  processing_video: Obrada videa\n  random: Randomizirajte prezentaciju\n  saturation: Zasićenje\n  seconds: sekundi\n  select_collection: Odaberite Kolekcija\n  thumbnail_generation_complete: Generacija sličica je završena\n  thumbnail_generation_finished: Generiranje sličica je uspješno završeno\n  wallpaper_collection: Kolekcija tapeta\n  wallpaper_settings: Postavke pozadine\n  withOverlay: Sa prekrivanjem\n  workspace_collections: Zbirke radnog prostora\nweg:\n  auto_hide: Auto se sakrio\n  delay_to_hide: Odložite da se sakrijete\n  delay_to_show: Kašnjenje za prikaz\n  dock_side: Položaj\n  enable: Omogući pristanište / traku zadataka\n  filtering: Filtriranje predmeta\n  gap: Jaz\n  hide_mode:\n    always: Uvijek\n    never: Nikad\n    on_overlap: Na preklapanje\n  items:\n    gap: Prostor između predmeta\n    label: Predmeti\n    pinned_visibility:\n      always: Uvijek\n      label: Vidljivost zakačenih stavki\n      when_primary: Kada je monitor primarni\n    show_instance_counter: Pokaži otvoriti brojač Windows\n    show_window_title: Prikaži naziv otvorenog prozora (samo horizontalno)\n    size: Veličina predmeta\n    split_windows: Podijeljeni prozori (jedna stavka po prozoru)\n    temporal_visibility:\n      all: Sve\n      label: Vidljivost nezakačenih stavki\n      on_monitor: Na monitoru\n    visible_separators: Vidljivi separatori\n  label: Dock / programska traka\n  margin: Marža\n  mode:\n    full_width: Cijela širina ekrana\n    min_content: Manji koliko može\n  padding: Obloga\n  show_end_task: Prikaži krajnji zadatak u traci zadataka\n  width: Širina\nwelcome:\n  give_a_review: Dajte recenziju\n  message: >-\n    Seelen UI je besplatno desktop okruženje otvorenog koda za Windows. Ovdje\n    imate potpunu kontrolu nad načinom na koji vaš desktop izgleda i kako se\n    ponaša, što vam omogućava da prilagodite svaki detalj kako bi odgovarao\n    vašem toku rada i stilu.\n  ok: Počnimo!\n  review: >-\n    Ako uživate u korišćenju Seelen korisničkog sučelja, razmislite o tome da\n    ostavite recenziju o trgovini - vaše povratne informacije pomažu da se\n    projekt razvija i poboljšava.\n  title: Dobrodošli u Seelen UI!\nwidget:\n  enable: Omogući ovaj widget\n  enable_for_monitor: Omogući na ovom monitoru\n  instances: Slučajevi\nwm:\n  animations:\n    duration: Trajanje animacije (MS)\n    ease_function: Funkcija olakšavanja animacije\n    enable: Omogući animacije prozora\n  author: Autor\n  border:\n    enable: Omogući granicu prozora\n    offset: Offset granice\n    width: Širina granice\n  description: Opis\n  drag_behavior: Drag Behavior\n  drag_behavior_options:\n    sort: Sortiraj (promijeni redoslijed prozora tokom prevlačenja)\n    swap: Zamijeni (zamijeni prozore na kraju povlačenja)\n  enable: Omogući oblaganje menadžera prozora\n  layout: Izgled\n  resize_delta: Promijenite veličinu Delta (%)\n  space_between_containers: Prostor između kontejnera\n  workspace_offset: Offset radne prostore (margine)\n  workspace_padding: Djelatna površina obloga\n'yes': Da\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ca.yml",
    "content": "action:\n  confirm: Esteu segur?\n  confirm_body: Aquesta acció no es pot desfer.\napps_configurations:\n  app:\n    bindings: Enquadernació (nota les dues opcions són necessàries)\n    category: Categoria\n    category_placeholder: Res\n    monitor: Observar\n    monitor_placeholder: Res\n    name: Nom\n    ok_create: Crear\n    ok_edit: Actualitzar\n    ok_readonly: Edita com a nou\n    options:\n      NoInteractive: No interactiu\n      VdPinned: Mostra a tots els espais de treball\n      WmFloat: Twm - Comença a flotar\n      WmForce: Twm - Força de gestió\n      WmUnmanage: Twm - Desadministrar\n    options_label: Opcions addicionals\n    title_create: Creació {{nom}}\n    title_edit: Edició {{nom}}\n    title_readonly: Visualització {{nom}}\n    weg_options_label: Opcions de moll/barra de tasques\n    wm_options_label: Opcions del gestor de finestres\n    workspace: Espai de treball\n    workspace_placeholder: Res\n  bundled_msg: >-\n    Aquestes configuracions agrupades no són editables i estan dissenyades per\n    proporcionar -vos la millor experiència sense personalització. Configuen\n    automàticament les aplicacions més habituals per a vosaltres.\n  bundled_title: Configuració de l'aplicació agrupada amb Seelen\n  confirm_delete: Esteu segur que voleu suprimir aquesta configuració/s?\n  confirm_delete_title: Confirmeu Suprimeix\n  delete: Esborrar\n  export: Exportar\n  export_full: Exporta la configuració per sol·licitud\n  extra_info: >-\n    La interfície d'usuari de Seelen només utilitza un identificador per\n    aplicació (primer partit que es troba), de manera que l'ordre de com\n    s'especifica és important, es prioritzarà l'últim afegit, ja que nota que la\n    taula s'ordena de manera predeterminada de l'antiga a la vella.\n  identifier:\n    add_block: Afegiu bloc\n    and: I\n    id: Identificador\n    kind: Identificar -se per\n    matching_strategy: Estratègia de coincidència\n    matching_strategy_option:\n      contains: Conté\n      ends_with: Acaba amb\n      equals: Iguals\n      regex: Expressió regular\n      starts_with: Comença amb\n    negation: Negar la concordança\n    or: O\n    remove: Esborra el bloc\n    type:\n      class: Classe\n      exe: Exe\n      path: Camí\n      title: Títol\n  import: Importar\n  import_full: Importa la configuració per sol·licitud\n  new: Nou\n  search: Cerca\n  swap: Canviar\ncancel: Cancel · lar\nclose: Tancar -se\ndelete: Esborrar\ndevtools:\n  app_folders: Carpetes d'aplicacions\n  custom_config_file: Carregueu el fitxer de configuració personalitzada\n  data_folder: Carpeta de dades\n  enable: Activa les eines de desenvolupadors\n  install_folder: Carpeta d'instal·lació\n  load: Carregar\n  settings_file: Arxiu de configuració\n  simulate_perm:\n    label: Simula la sol·licitud de permís de widget\n    result_allowed: ✓ Autorització concedida\n    result_denied: ✗ Permís denegat\n    trigger: Simular\n    widget_id_placeholder: '@autor/nom-widget'\nextras:\n  clear_icons: Esborrar la memòria cau de les icones del sistema\n  clear_icons_tooltip: Es pot requerir un reinici per tenir efecte sobre tots els ginys\n  exit: Deixar/sortir\n  links: Enllaços oficials\n  relaunch: Rellançar\n  version: Versió\n  version_fixed: >-\n    Les versions de l'aplicació i WebView2 Runtime estan arreglades. Això vol\n    dir que l'aplicació no rebrà actualitzacions i que el temps d'execució de\n    WebView2 no s'actualitzarà automàticament amb les actualitzacions de\n    Windows.\ngeneral:\n  accent_color: Color d’accent\n  date_format: Format de la data\n  date_format_how_to: Com escriure un format de data?\n  hardware_acceleration: Acceleració de maquinari\n  hardware_acceleration_description: >-\n    Desactivar l'acceleració de maquinari reduirà l'ús de memòria, però pot\n    provocar problemes de rendiment. És segur desactivar-lo si no feu servir\n    fons de pantalla en viu.\n  icon_pack:\n    available: Paquets d'icones disponibles\n    selected: Paquets d'icones actius\n  language: Llenguatge\n  monday: dilluns\n  performance_mode:\n    on_battery: A la bateria\n    on_energy_saver: A l'estalvi d'energia\n    options:\n      disabled: Inepte\n      extreme: Extrem\n      minimal: Mínim\n    plugged: Tassa o càrrega\n  polling_interval: Interval de sondeig del sistema\n  polling_interval_description: >-\n    Amb quina freqüència (en segons) la interfície d'usuari de Seelen comprova\n    els recursos del sistema com la CPU, la memòria RAM, la xarxa i l'activitat\n    del disc. Un nombre més petit significa actualitzacions més freqüents, però\n    un ús de recursos lleugerament superior.\n  saturday: dissabte\n  start_of_week: Inici de setmana\n  startup: Executeu la posada en marxa?\n  sunday: diumenge\n  theme:\n    available: Temes disponibles\n    selected: Temes actius\nheader:\n  labels:\n    config: Configuracions\n    developer: Per als desenvolupadors\n    extras: Extres\n    general: General\n    home: Casa\n    icon_pack_editor: Icones en memòria cau\n    iconpack: Paquets d’icones\n    monitors: Monitors\n    plugin: Tapins\n    resources: Recursos\n    shortcuts: Dreceres\n    soundpack: Paquets de son\n    specific_apps: Configuració per aplicació\n    theme: Temes\n    virtual_desk: Desktops virtuals\n    wallpaper: Fons de pantalla\n    widget: Widgets\nhome:\n  new_resources: Recursos nous\ninherit: Heretar\ninProgress: En progrés...\ninsert: Inserir\nloading: Carregant ...\nmiscellaneous: Divers\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Més\n'no': 'No'\nopen: Obrir\nquit: Abandonar\nremove: Treure\nreset_all_to_default: Restableix tot als valors predeterminats\nreset_to_default: Restableix al valor predeterminat\nresources:\n  app_outdated: >-\n    Aquest recurs requereix una versió més recent de la interfície d'usuari de\n    Seelen per funcionar correctament.\n  corrupted_wallpaper: 'No s''ha pogut extreure la miniatura: format de vídeo danyat o no compatible'\n  delete: Esborra el recurs\n  discover: Descobriu més recursos\n  has_update: Hi ha disponible una actualització\n  high_impact: Alt impacte en el rendiment\n  import_wallpapers: Importa fons de pantalla locals\n  open_folder: Obrir la carpeta de recursos\n  outdated: >-\n    Aquest recurs va ser dissenyat per a una versió anterior de la interfície\n    d'usuari de Seelen i pot no funcionar correctament.\n  see_on_website: Veure al lloc web\nreview_request:\n  not_now: Ara no\n  sure: Segur!\n  title: Us agrada la interfície d'usuari de Seelen?\nsave: Estalviar\nsave_and_restart: Desar i reiniciar\nsearch: Cerca\nsee_more: Veure més\nshortcuts:\n  duplicate_error: Aquesta drecera està duplicada\n  enable: Activa el sistema integrat de dreceres\n  enable_tooltip: >-\n    Desactiveu si implementareu el vostre propi sistema de dreceres mitjançant\n    el client de la interfície d'usuari de Seelen\n  labels:\n    create_new_workspace: Creeu un espai de treball nou\n    cycle_stack_next: Stack Cycle Següent\n    cycle_stack_prev: Pila de cicles anterior\n    cycle_wallpaper_next: Canvieu al fons de pantalla següent\n    cycle_wallpaper_prev: Canvieu a fons de pantalla anterior\n    decrease_height: Disminuir l'alçada\n    decrease_width: Disminuir l'amplada\n    destroy_current_workspace: Destrueix l'espai de treball actual\n    focus_bottom: Focus Bottom\n    focus_latest: Focus més recent\n    focus_left: Focus deixat\n    focus_right: Centra't a la dreta\n    focus_top: Focus Top\n    increase_height: Augmentar l'alçada\n    increase_width: Augment d'amplada\n    misc_force_quit: Força deixar de fumar\n    misc_force_restart: Reinici de la força\n    misc_open_settings: Configuració oberta\n    misc_toggle_lock_tracing: Tornada de tancament de bloqueig (registres)\n    misc_toggle_win_event_tracing: Toggle Win Traça (registres)\n    move_to_workspace: Desplaceu -vos a l'espai de treball {{0}}\n    move_window_down: Moveu la finestra a baix\n    move_window_left: Moveu la finestra a l'esquerra\n    move_window_right: Moveu la finestra a la dreta\n    move_window_up: Moveu la finestra a la part superior\n    pause_tiling: Pausa Gestor de finestres de rajoles\n    reserve_bottom: Reserva de fons\n    reserve_float: Reserva flotant\n    reserve_left: Reserva a l'esquerra\n    reserve_right: Reserva a la dreta\n    reserve_stack: Pila de reserva\n    reserve_top: Reserva superior\n    restore_sizes: Restaurar les mides\n    send_to_workspace: Enviar a l'espai de treball {{0}}\n    start_weg_app: Aplicació Focus o Inici {{0}}\n    switch_to_next_workspace: Canvieu al següent espai de treball\n    switch_to_previous_workspace: Canvieu a l'espai de treball anterior\n    switch_workspace: Switch a Workspace {{0}}\n    toggle_float: Mode de flotació de la finestra de commutació\n    toggle_monocle: Mode Monocle de l'espai de treball\n  readonly_tooltip: Aquesta és una drecera de només lectura\n  reset: Restableix als valors predeterminats\nsides:\n  bottom: Fons\n  left: Esquerre\n  right: Dret\n  top: Superior\ntoolbar:\n  auto_hide: Auto amaga\n  delay_to_hide: Tarda en amagar-se\n  delay_to_show: Retard per mostrar\n  dock_side: Posició\n  enable: Activa la barra d’eines de luxe\n  hide_mode:\n    always: Sempre\n    never: Mai\n    on_overlap: Sobre la superposició\n  item_size: Mida de l'article\n  label: Barra d’eines\n  margin: Mida del marge\n  padding: Mida del farciment\n  placeholder: {}\nupdate:\n  available: Actualitzeu disponible.\n  channel: Canal d'actualització\n  downloading: Descarregant ...\nwall:\n  backgrounds: Fons de pantalla\n  blur: Borrosa\n  cancel: Cancel·la\n  close: Tancar\n  collection_name: Nom de la col·lecció\n  collections: Col·leccions\n  contrast: Contrast\n  corrupted_wallpapers_message: 'Fons de pantalla danyats, penseu a suprimir-los:'\n  create: Crear\n  create_collection: Crea col·lecció\n  default_collection: Col·lecció per defecte\n  delete_collection: Suprimeix la col·lecció\n  edit_collection: Edita la col·lecció\n  enable: Activa el gestor de fons de pantalla\n  extend: Ampliar primària\n  fit:\n    contain: Contenir\n    cover: Tapa\n    fill: Replenar\n  flipHorizontal: Flipeu horitzontal\n  flipVertical: Flip vertical\n  generating_thumbnails: Generació de miniatures de fons de pantalla\n  hours: hores\n  interval: Canvieu el fons de pantalla cada\n  minutes: minuts\n  monitor_collection: Col·lecció de monitors\n  multimonitor_behaviour: Comportament multimonitor\n  muted: Silencia l'àudio del vídeo\n  no_background: A la presentació de diapositives buides, utilitzant el fons del tema.\n  no_collections: Encara no s'ha creat cap col·lecció. Feu clic al botó + per crear-ne un.\n  objectFit: Fixació de fons\n  objectPosition: Posició de fons\n  overlayColor: Color de superposició\n  overlayMixBlendMode: Mode de barreja de barreja de superposició\n  per_monitor: Per monitor\n  playback: Velocitat de reproducció\n  position:\n    bottom: Fons\n    center: Centre\n    left: Esquerre\n    right: Dret\n    top: Cim\n  processing_video: Processament de vídeo\n  random: Randomize Show de diapositives\n  saturation: Saturació\n  seconds: segons\n  select_collection: Seleccioneu Col·lecció\n  thumbnail_generation_complete: S'ha completat la generació de miniatures\n  thumbnail_generation_finished: La generació de miniatures ha finalitzat correctament\n  wallpaper_collection: Col·lecció de fons de pantalla\n  wallpaper_settings: Configuració del fons de pantalla\n  withOverlay: Amb superposició\n  workspace_collections: Col·leccions d'espais de treball\nweg:\n  auto_hide: Amaga automàtic\n  delay_to_hide: Tarda en amagar-se\n  delay_to_show: Retard per mostrar\n  dock_side: Posició\n  enable: Activa el moll/la barra de tasques\n  filtering: Filtratge d'elements\n  gap: Escletxa\n  hide_mode:\n    always: Sempre\n    never: Mai\n    on_overlap: Sobre la superposició\n  items:\n    gap: Espai entre articles\n    label: Plantes\n    pinned_visibility:\n      always: Sempre\n      label: Visibilitat dels elements fixats\n      when_primary: Quan el monitor és principal\n    show_instance_counter: Mostra el comptador de Windows obert\n    show_window_title: Mostra el títol de la finestra oberta (només horitzontal)\n    size: Mida de l'article\n    split_windows: Dividir finestres (un element per finestra)\n    temporal_visibility:\n      all: Tots\n      label: Visibilitat dels elements sense fixar\n      on_monitor: Al monitor\n    visible_separators: Separadors visibles\n  label: Moll/barra de tasques\n  margin: Marge\n  mode:\n    full_width: Amplada de pantalla completa\n    min_content: Petit com pot ser\n  padding: Encoir\n  show_end_task: Mostra la tasca final a la barra de tasques\n  width: Amplada\nwelcome:\n  give_a_review: Fes una ressenya\n  message: >-\n    Seelen UI és un entorn d'escriptori gratuït i de codi obert per a Windows.\n    Aquí teniu un control total sobre com es veu i es comporta el vostre\n    escriptori, cosa que us permet personalitzar tots els detalls perquè\n    coincideixin amb el vostre flux de treball i estil.\n  ok: Comencem!\n  review: >-\n    Si us agrada utilitzar Seelen UI, penseu a deixar una ressenya a la botiga:\n    els vostres comentaris ajuden el projecte a créixer i millorar.\n  title: Benvingut a Seelen UI!\nwidget:\n  enable: Activa aquest widget\n  enable_for_monitor: Activa en aquest monitor\n  instances: Instàncies\nwm:\n  animations:\n    duration: Durada de l'animació (MS)\n    ease_function: Funció d'alleujament d'animació\n    enable: Activa les animacions de la finestra\n  author: Autora\n  border:\n    enable: Activa la vora de la finestra\n    offset: Compensació fronterera\n    width: Amplada de la frontera\n  description: Descripció\n  drag_behavior: Comportament de l'arrossegament\n  drag_behavior_options:\n    sort: Ordena (reordena les finestres mentre arrossegues)\n    swap: Canvi (canvi de finestres a l'extrem d'arrossegament)\n  enable: Activa el gestor de finestres de rajoles\n  layout: Pla\n  resize_delta: Redimensiona el delta (%)\n  space_between_containers: Espai entre contenidors\n  workspace_offset: Offset d'espais de treball (marges)\n  workspace_padding: Rado d’espais de treball\n'yes': Sí\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/cs.yml",
    "content": "action:\n  confirm: Jsi si jistý?\n  confirm_body: Tuto akci nelze vrátit zpět.\napps_configurations:\n  app:\n    bindings: Vazba (jsou vyžadovány obě možnosti)\n    category: Kategorie\n    category_placeholder: Žádný\n    monitor: Monitor\n    monitor_placeholder: Žádný\n    name: Název\n    ok_create: Vytvořit\n    ok_edit: Aktualizovat\n    ok_readonly: Upravit jako nové\n    options:\n      NoInteractive: Žádná interaktivní\n      VdPinned: Zobrazit ve všech pracovních prostorech\n      WmFloat: Twm - Spustit plovoucí\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm – Unmanage\n    options_label: Další možnosti\n    title_create: Vytváření {{name}}\n    title_edit: Úpravy {{name}}\n    title_readonly: Prohlížení {{name}}\n    weg_options_label: Možnosti lišty/hlavního panelu\n    wm_options_label: Možnosti správce oken\n    workspace: Pracovní prostor\n    workspace_placeholder: Žádný\n  bundled_msg: >-\n    Tyto sdružené konfigurace nejsou upravitelné a jsou navrženy tak, aby vám\n    poskytly nejlepší zkušenost bez přizpůsobení. Automaticky pro  Vás\n    nakonfigurují nejběžnější aplikace.\n  bundled_title: Konfigurace aplikace spojená s Seelen\n  confirm_delete: Určitě chcete tuto konfiguraci smazat?\n  confirm_delete_title: Potvrďte smazání\n  delete: Smazat\n  export: Exportovat\n  export_full: Export nastavení podle aplikace\n  extra_info: >-\n    SEELEN UI používá pouze jeden identifikátor na aplikaci (první nalezený),\n    takže pořadí v tom, jak jsou specifikovány, je důležité, nejnovější přidaná\n    bude upřednostňována, protože tabulka je ve výchozím nastavení tříděna od\n    nejnovější po nejstarší.\n  identifier:\n    add_block: Přidat blok\n    and: A\n    id: Identifikátor\n    kind: Identifikovat podle\n    matching_strategy: Srovnávací strategie\n    matching_strategy_option:\n      contains: Obsahuje\n      ends_with: Končí s\n      equals: Rovná se\n      regex: Regulární výraz\n      starts_with: Začíná s\n    negation: Negovat odpovídající\n    or: NEBO\n    remove: Odstranit blok\n    type:\n      class: Třída\n      exe: Exe\n      path: Cesta\n      title: Titulek\n  import: Importovat\n  import_full: Import nastavení podle aplikace\n  new: Nový\n  search: Vyhledat\n  swap: Prohodit\ncancel: Zrušit\nclose: Zavřít\ndelete: Vymazat\ndevtools:\n  app_folders: Složky aplikací\n  custom_config_file: Načíst vlastní konfigurační soubor\n  data_folder: Datová složka\n  enable: Povolit vývojové nástroje\n  install_folder: Instalační složka\n  load: Načíst\n  settings_file: Nastavení souboru\n  simulate_perm:\n    label: Simulovat žádost o povolení widgetu\n    result_allowed: ✓ Povolení uděleno\n    result_denied: ✗ Povolení odepřeno\n    trigger: Simulovat\n    widget_id_placeholder: '@autor/název-widgetu'\nextras:\n  clear_icons: Vymazání mezipaměti systémových ikon\n  clear_icons_tooltip: Aby se plně projevily všechny widgety, mohlo by být nutné restartování.\n  exit: Ukončit\n  links: Oficiální odkazy\n  relaunch: Znovu spustit\n  version: Verze\n  version_fixed: >-\n    Verze aplikace a WebView2 Runtime jsou opraveny. To znamená, že aplikace\n    nebude přijímat aktualizace a WebView2 Runtime nebude automaticky\n    aktualizován aktualizacemi Windows.\ngeneral:\n  accent_color: Barvný nádech\n  date_format: Formát datumu\n  date_format_how_to: Jak napsat formát data?\n  hardware_acceleration: Hardwarová akcelerace\n  hardware_acceleration_description: >-\n    Zakázání hardwarové akcelerace sníží využití paměti, ale může způsobit\n    problémy s výkonem. Je bezpečné zakázat, pokud nebudete používat živé\n    tapety.\n  icon_pack:\n    available: Dostupné balíčky ikon\n    selected: Aktivní balíčky ikon\n  language: Jazyk\n  monday: pondělí\n  performance_mode:\n    on_battery: Na baterii\n    on_energy_saver: Na spořič energie\n    options:\n      disabled: Deaktivované\n      extreme: Extrémní\n      minimal: Minimální\n    plugged: Připojené nebo nabíjení\n  polling_interval: Interval dotazování systému\n  polling_interval_description: >-\n    Jak často (v sekundách) uživatelské rozhraní Seelen kontroluje systémové\n    prostředky, jako je CPU, RAM, síť a aktivita disku. Menší číslo znamená\n    častější aktualizace, ale o něco vyšší využití zdrojů.\n  saturday: sobota\n  start_of_week: Začátek týdne\n  startup: Spoustit při každém startu?\n  sunday: neděle\n  theme:\n    available: Dostupné motivy\n    selected: Aktivní motivy\nheader:\n  labels:\n    config: Konfigurace\n    developer: Pro vývojáře\n    extras: Doplňky\n    general: Všeobecné\n    home: Domov\n    icon_pack_editor: Ikony v mezipaměti\n    iconpack: Balíčky ikon\n    monitors: Monitory\n    plugin: Zásuvné moduly\n    resources: Zdroje\n    shortcuts: Zkratky\n    soundpack: Zvukové balíčky\n    specific_apps: Nastavení podle aplikace\n    theme: Témata\n    virtual_desk: Virtuální plochy\n    wallpaper: Tapety na plochu\n    widget: Widgety\nhome:\n  new_resources: Nové zdroje\ninherit: Zdědit\ninProgress: Probíhá ...\ninsert: Vložit\nloading: Načítání...\nmiscellaneous: Smíšený\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Více\n'no': Žádný\nopen: Otevřeno\nquit: Přestat\nremove: Odstranit\nreset_all_to_default: Obnovení všech výchozích hodnot\nreset_to_default: Obnovení výchozí hodnoty\nresources:\n  app_outdated: >-\n    Tento zdroj vyžaduje pro správnou funkci novější verzi uživatelského\n    rozhraní Seelen.\n  corrupted_wallpaper: >-\n    Nepodařilo se extrahovat miniaturu – poškozený nebo nepodporovaný formát\n    videa\n  delete: Odstranit zdroj\n  discover: Objevte další zdroje\n  has_update: K dispozici je aktualizace\n  high_impact: Vysoký dopad na výkon\n  import_wallpapers: Import místních tapet\n  open_folder: Otevření složky se zdroji\n  outdated: >-\n    Tento zdroj byl navržen pro starší verzi uživatelského rozhraní Seelen a\n    nemusí fungovat správně.\n  see_on_website: Viz na webu\nreview_request:\n  not_now: Teď ne\n  sure: Jasně!\n  title: Líbí se vám uživatelské rozhraní Seelen?\nsave: Uložit\nsave_and_restart: Uložit a restartovat\nsearch: Vyhledávání\nsee_more: Viz více\nshortcuts:\n  duplicate_error: Tato zkratka je duplicitní\n  enable: Povolení vestavěného systému zkratek\n  enable_tooltip: >-\n    Zakázat, pokud budete implementovat vlastní systém zkratek pomocí klienta\n    Seelen UI.\n  labels:\n    create_new_workspace: Vytvoření nového pracovního prostoru\n    cycle_stack_next: Cyklus Stack Další\n    cycle_stack_prev: Cyklus Stack Předchozí\n    cycle_wallpaper_next: Změna na další tapetu\n    cycle_wallpaper_prev: Změna na předchozí tapetu\n    decrease_height: Snížení výšky\n    decrease_width: Snížení šířky\n    destroy_current_workspace: Zničit aktuální pracovní prostor\n    focus_bottom: Zaměření Dno\n    focus_latest: Zaměřte se na nejnovější\n    focus_left: Zaostřeno vlevo\n    focus_right: Zaměřit se na pravou stranu\n    focus_top: Zaměření Top\n    increase_height: Zvýšení výšky\n    increase_width: Zvětšení šířky\n    misc_force_quit: Síla přestat\n    misc_force_restart: Vynutit restart\n    misc_open_settings: Otevřít nastavení\n    misc_toggle_lock_tracing: Přepínání sledování zámků (protokoly)\n    misc_toggle_win_event_tracing: Přepnutí sledování událostí Win (protokoly)\n    move_to_workspace: Přesun do pracovního prostoru {{0}}\n    move_window_down: Přesunutí okna do spodní části\n    move_window_left: Přesunutí okna doleva\n    move_window_right: Přesunutí okna doprava\n    move_window_up: Přesunutí okna na začátek\n    pause_tiling: Pozastavení Správce dlaždicového okna\n    reserve_bottom: Rezerva Dno\n    reserve_float: Rezervní plovák\n    reserve_left: Rezerva vlevo\n    reserve_right: Právo na rezervaci\n    reserve_stack: Zásobník rezerv\n    reserve_top: Reserve Top\n    restore_sizes: Obnovení velikostí\n    send_to_workspace: Odeslat do pracovního prostoru {{0}}\n    start_weg_app: Zaměření nebo spuštění aplikace {{0}}\n    switch_to_next_workspace: Přepnutí na další pracovní plochu\n    switch_to_previous_workspace: Přepnutí na předchozí pracovní plochu\n    switch_workspace: Přepnutí na pracovní plochu {{0}}\n    toggle_float: Přepínání režimu plovoucího okna\n    toggle_monocle: Přepínání režimu Monokl na pracovní ploše\n  readonly_tooltip: Toto je zkratka pouze pro čtení\n  reset: Obnovení výchozího nastavení\nsides:\n  bottom: Spodek\n  left: Vlevo\n  right: Vpravo\n  top: Svršek\ntoolbar:\n  auto_hide: Automaticky skrýt\n  delay_to_hide: Zpoždění schování\n  delay_to_show: Zpoždění zobrazení\n  dock_side: Pozice\n  enable: Povolte ozdobný panel nástrojů\n  hide_mode:\n    always: Vždy\n    never: Nikdy\n    on_overlap: Na překrytí\n  item_size: Velikost položky\n  label: Panel nástrojů\n  margin: Velikost okraje\n  padding: Velikost vycpávky\n  placeholder: {}\nupdate:\n  available: Aktualizace k dispozici!\n  channel: Aktualizovat kanál\n  downloading: Stahování ...\nwall:\n  backgrounds: Tapety\n  blur: Rozmazání\n  cancel: Zrušit\n  close: Blízko\n  collection_name: Název sbírky\n  collections: Sbírky\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Poškozené tapety, zvažte smazání těchto:'\n  create: Vytvořit\n  create_collection: Vytvořit sbírku\n  default_collection: Výchozí kolekce\n  delete_collection: Smazat sbírku\n  edit_collection: Upravit sbírku\n  enable: Povolit správce tapet\n  extend: Prodloužit primární\n  fit:\n    contain: Obsahuje\n    cover: Obálka\n    fill: Naplňte\n  flipHorizontal: Flip Horizontální\n  flipVertical: Flip Vertical\n  generating_thumbnails: Generování miniatur tapet\n  hours: hodiny\n  interval: Změňte tapetu každých\n  minutes: minuty\n  monitor_collection: Sbírka monitorů\n  multimonitor_behaviour: Multimonitorové chování\n  muted: Ztlumit zvuk videa\n  no_background: Prázdná prezentace, použití pozadí motivu.\n  no_collections: Dosud nebyly vytvořeny žádné sbírky. Kliknutím na tlačítko + jej vytvoříte.\n  objectFit: Pozadí Fit\n  objectPosition: Pozice v pozadí\n  overlayColor: Barva překrytí\n  overlayMixBlendMode: Režim prolnutí překryvné směsi\n  per_monitor: Na monitor\n  playback: Rychlost přehrávání\n  position:\n    bottom: Spodní část\n    center: Středisko\n    left: Vlevo\n    right: Vpravo\n    top: Nahoru\n  processing_video: Zpracování videa\n  random: Randomize prezentace\n  saturation: Nasycení\n  seconds: sekundy\n  select_collection: Vyberte Sbírka\n  thumbnail_generation_complete: Generování náhledů dokončeno\n  thumbnail_generation_finished: Generování náhledů bylo úspěšně dokončeno\n  wallpaper_collection: Kolekce tapet\n  wallpaper_settings: Nastavení tapety\n  withOverlay: S překrytím\n  workspace_collections: Kolekce pracovního prostoru\nweg:\n  auto_hide: Automatické skrytí\n  delay_to_hide: Zpoždění schování\n  delay_to_show: Zpoždění zobrazení\n  dock_side: Pozice\n  enable: Povolte lištu/hlavní panel\n  filtering: Filtrování položek\n  gap: Mezera\n  hide_mode:\n    always: Vždy\n    never: Nikdy\n    on_overlap: Při překrytí\n  items:\n    gap: Prostor mezi položkami\n    label: Položky\n    pinned_visibility:\n      always: Vždy\n      label: Viditelnost připnutých položek\n      when_primary: Když je monitor primární\n    show_instance_counter: Zobrazit počítadlo otevřených oken\n    show_window_title: Zobrazit název otevřeného okna (pouze horizontálně)\n    size: Velikost položky\n    split_windows: Rozdělit okna (jedna položka na okno)\n    temporal_visibility:\n      all: Vše\n      label: Viditelnost nepřipnutých položek\n      on_monitor: Na monitoru\n    visible_separators: Viditelné oddělovače\n  label: Lišta/Hlavní panel\n  margin: Okraj\n  mode:\n    full_width: Celá šířka obrazovky\n    min_content: Malý jak jen může být\n  padding: Padding\n  show_end_task: Zobrazení koncové úlohy na hlavním panelu\n  width: Šířka\nwelcome:\n  give_a_review: Dejte recenzi\n  message: >-\n    Seelen UI je bezplatné a open source desktopové prostředí pro Windows. Zde\n    máte plnou kontrolu nad tím, jak váš desktop vypadá a chová se, což vám\n    umožňuje přizpůsobit každý detail tak, aby odpovídal vašemu pracovnímu\n    postupu a stylu.\n  ok: Začněme!\n  review: >-\n    Pokud rádi používáte Seelen UI, zvažte zanechání recenze v obchodě – vaše\n    zpětná vazba pomáhá projektu růst a zlepšovat se.\n  title: Vítejte v uživatelském rozhraní Seelen!\nwidget:\n  enable: Povolení tohoto widgetu\n  enable_for_monitor: Povolit na tomto monitoru\n  instances: Instance\nwm:\n  animations:\n    duration: Animace doba trvání (MS)\n    ease_function: Funkce uvolňování animace\n    enable: Povolit animace okna\n  author: Autor\n  border:\n    enable: Povolte hranici okna\n    offset: Posun hranice\n    width: Šířka hranice\n  description: Popis\n  drag_behavior: Chování přetažení\n  drag_behavior_options:\n    sort: Řadit (změnit pořadí oken při přetahování)\n    swap: Zaměnit (vyměnit okna na konci přetažení)\n  enable: Povolit správce oken\n  layout: Rozložení\n  resize_delta: Změna velikosti (%)\n  space_between_containers: Prostor mezi kontejnery\n  workspace_offset: Posun pracovních prostor (marže)\n  workspace_padding: Padding pracovních prostorů\n'yes': Ano\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/cy.yml",
    "content": "action:\n  confirm: Ydych chi'n sicr?\n  confirm_body: Ni ellir dadwneud y weithred hon.\napps_configurations:\n  app:\n    bindings: Rhwymo (nodwch fod angen y ddau opsiwn)\n    category: Categori\n    category_placeholder: Neb\n    monitor: Monitrest\n    monitor_placeholder: Neb\n    name: Alwai\n    ok_create: Chrëid\n    ok_edit: Diweddara ’\n    ok_readonly: Golygu fel NEWYDD\n    options:\n      NoInteractive: Dim Rhyngweithiol\n      VdPinned: Dangoswch ym mhob man gwaith\n      WmFloat: Twm - Dechrau arnofio\n      WmForce: Twm - Rheolwr yr Heddlu\n      WmUnmanage: Twm - Unmanage\n    options_label: Opsiynau ychwanegol\n    title_create: Creu {{enw}}\n    title_edit: Golygu {{enw}}\n    title_readonly: Gweld {{enw}}\n    weg_options_label: Opsiynau doc/bar tasgau\n    wm_options_label: Opsiynau Rheolwr Ffenestr\n    workspace: Gweithle\n    workspace_placeholder: Neb\n  bundled_msg: >-\n    Nid oes modd golygu'r cyfluniadau bwndelu hyn ac fe'u cynlluniwyd i roi'r\n    profiad gorau i chi heb addasu. Maent yn ffurfweddu'r cymwysiadau mwyaf\n    cyffredin ar eich cyfer yn awtomatig.\n  bundled_title: App Config wedi'i bwndelu â Seelen\n  confirm_delete: Ydych chi'n siŵr eich bod chi am ddileu'r cyfluniad/au hwn?\n  confirm_delete_title: Cadarnhau Dileu\n  delete: Croeswn\n  export: Allforiff\n  export_full: Gosodiadau Allforio yn ôl Cais\n  extra_info: >-\n    Mae Seelen UI yn defnyddio dim ond un dynodwr i bob app (y gêm gyntaf a\n    ddarganfuwyd) felly mae'r gorchymyn yn sut yn cael ei nodi yn bwysig, bydd\n    yr ychwanegiad diweddaraf yn cael ei flaenoriaethu, fel nodyn mae'r tabl yn\n    cael ei ddidoli yn ddiofyn o'r diweddaraf i'r hen.\n  identifier:\n    add_block: Ychwanegu bloc\n    and: A\n    id: Dynodwr\n    kind: Adnabod gan\n    matching_strategy: Strategaeth gyfatebol\n    matching_strategy_option:\n      contains: Yn cynnwys\n      ends_with: Yn gorffen gyda\n      equals: Yn gyfartal\n      regex: Mynegiant rheolaidd\n      starts_with: Yn dechrau gyda\n    negation: Negate paru\n    or: Neu\n    remove: Dileu bloc\n    type:\n      class: Dosbarth\n      exe: Exe\n      path: Llwybr\n      title: Teitl\n  import: Mewnforied\n  import_full: Gosodiadau mewnforio yn ôl cais\n  new: Newydd\n  search: Chwiloon\n  swap: Trwciff\ncancel: Chansliff\nclose: Chaewch\ndelete: Croeswn\ndevtools:\n  app_folders: Ffolderau app\n  custom_config_file: Llwythwch ffeil ffurfweddu arfer\n  data_folder: Ffolder data\n  enable: Galluogi Offer Datblygwr\n  install_folder: Ffolder gosod\n  load: Lwythet\n  settings_file: Ffeil Gosodiadau\n  simulate_perm:\n    label: Efelychu Cais Caniatâd Widget\n    result_allowed: ✓ Rhoddwyd caniatâd\n    result_denied: ✗ Gwrthodwyd caniatâd\n    trigger: Efelychu\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Storfa eiconau system glir\n  clear_icons_tooltip: Gellid bod yn ofynnol i ailgychwyn ddod i rym yn llawn ar bob teclyn\n  exit: Rhoi'r gorau iddi/allanfa\n  links: Dolenni Swyddogol\n  relaunch: Hail -deirion\n  version: Fersiwn\n  version_fixed: >-\n    Mae'r cymhwysiad a fersiynau WebView2 Runtime yn sefydlog. Mae hyn yn golygu\n    na fydd y rhaglen yn derbyn diweddariadau ac ni fydd WebView2 Runtime yn\n    cael ei ddiweddaru'n awtomatig gyda diweddariadau Windows.\ngeneral:\n  accent_color: Lliw acen\n  date_format: Fformat dyddiad\n  date_format_how_to: Sut i ysgrifennu fformat dyddiad?\n  hardware_acceleration: Cyflymiad Caledwedd\n  hardware_acceleration_description: >-\n    Bydd analluogi cyflymiad caledwedd yn lleihau'r defnydd o gof, ond gall\n    achosi problemau perfformiad. Yn ddiogel i'w analluogi os na fyddwch yn\n    defnyddio papurau wal byw.\n  icon_pack:\n    available: Pecynnau Eicon Ar Gael\n    selected: Pecynnau Eicon Actif\n  language: Iaith\n  monday: Dydd Llun\n  performance_mode:\n    on_battery: Ar fatri\n    on_energy_saver: Ar arbed ynni\n    options:\n      disabled: Anabl\n      extreme: Eithafol\n      minimal: Lleiaf posibl\n    plugged: Plygio neu wefru\n  polling_interval: Cyfwng pleidleisio system\n  polling_interval_description: >-\n    Pa mor aml (mewn eiliadau) mae Seelen UI yn gwirio adnoddau system fel CPU,\n    RAM, rhwydwaith, a gweithgaredd disg. Mae nifer llai yn golygu diweddariadau\n    amlach, ond defnydd adnoddau ychydig yn uwch.\n  saturday: dydd Sadwrn\n  start_of_week: Dechrau'r Wythnos\n  startup: Rhedeg ar gychwyn?\n  sunday: Sul\n  theme:\n    available: Themâu sydd ar Gael\n    selected: Themâu Gweithredol\nheader:\n  labels:\n    config: Cyfluniadau\n    developer: Ar gyfer datblygwyr\n    extras: Hetiau\n    general: Gyffredinol\n    home: Nghartrefi\n    icon_pack_editor: Eiconau wedi'u storio\n    iconpack: Pecynnau eicon\n    monitors: Monitorau\n    plugin: Ategion\n    resources: Adnoddau\n    shortcuts: Llwybrau byr\n    soundpack: Pecynnau sain\n    specific_apps: Gosodiadau yn ôl cais\n    theme: Themâu\n    virtual_desk: Penbyrddau Rhithwir\n    wallpaper: Papurau Wal\n    widget: Widgets\nhome:\n  new_resources: Adnoddau Newydd\ninherit: Hetifedd\ninProgress: Ar y gweill...\ninsert: Mewnosodem\nloading: Llwytho ...\nmiscellaneous: Hamddenol\nmonitors_configurations:\n  label: Monitro {{mynegai}}\nmore: Mwy\n'no': Na\nopen: Ymagorant\nquit: Gadawen\nremove: Godon\nreset_all_to_default: Ailosod popeth i werthoedd diofyn\nreset_to_default: Ailosod i werth diofyn\nresources:\n  app_outdated: >-\n    Mae'r adnodd hwn yn gofyn am fersiwn mwy newydd o Seelen UI i weithio'n\n    iawn.\n  corrupted_wallpaper: Wedi methu echdynnu mân-lun - fformat fideo llygredig neu heb ei gefnogi\n  delete: Dileu Adnodd\n  discover: Darganfyddwch fwy o adnoddau\n  has_update: Mae diweddariad ar gael\n  high_impact: Effaith uchel ar berfformiad\n  import_wallpapers: Mewnforio papurau wal lleol\n  open_folder: Ffolder Adnoddau Agored\n  outdated: >-\n    Dyluniwyd yr adnodd hwn ar gyfer fersiwn hŷn o Seelen UI ac efallai na\n    fyddai'n gweithio'n iawn.\n  see_on_website: Gweler ar y wefan\nreview_request:\n  not_now: Ddim nawr\n  sure: Cadarn!\n  title: Yn mwynhau Seelen UI?\nsave: Hiachasit\nsave_and_restart: Cadw ac Ailgychwyn\nsearch: Chwiliwch\nsee_more: Gweld mwy\nshortcuts:\n  duplicate_error: Mae'r llwybr byr hwn yn cael ei ddyblygu\n  enable: Galluogi system llwybrau byr adeiledig\n  enable_tooltip: >-\n    Analluoga os byddwch chi'n gweithredu'ch system llwybrau byr eich hun gan\n    ddefnyddio cleient Seelen UI\n  labels:\n    create_new_workspace: Creu man gwaith newydd\n    cycle_stack_next: Pentwr beicio nesaf\n    cycle_stack_prev: Pentwr beicio blaenorol\n    cycle_wallpaper_next: Newid i'r papur wal nesaf\n    cycle_wallpaper_prev: Newid i bapur wal blaenorol\n    decrease_height: Lleihau uchder\n    decrease_width: Lleihau lled\n    destroy_current_workspace: Dinistrio man gwaith cyfredol\n    focus_bottom: Ffocws Gwaelod\n    focus_latest: Canolbwyntiwch y Diweddaraf\n    focus_left: Ffocws ar ôl\n    focus_right: Canolbwyntiwch yn iawn\n    focus_top: Top Ffocws\n    increase_height: Cynyddu uchder\n    increase_width: Cynyddu lled\n    misc_force_quit: Rhoi'r gorau iddi\n    misc_force_restart: Ailgychwyn grym\n    misc_open_settings: Gosodiadau Agored\n    misc_toggle_lock_tracing: Olrhain clo togl (logiau)\n    misc_toggle_win_event_tracing: Olrhain Digwyddiad Win Toggle (Logiau)\n    move_to_workspace: Symud i Workpace {{0}}\n    move_window_down: Symud ffenestr i'r gwaelod\n    move_window_left: Symud ffenestr i'r chwith\n    move_window_right: Symud ffenestr i'r dde\n    move_window_up: Symud ffenestr i'r brig\n    pause_tiling: Oedi rheolwr ffenestri teils\n    reserve_bottom: Cadwch y gwaelod\n    reserve_float: Arnofio wrth gefn\n    reserve_left: Gwarchodfa Chwith\n    reserve_right: Cadw Iawn\n    reserve_stack: Pentwr wrth gefn\n    reserve_top: Top wrth gefn\n    restore_sizes: Adfer Meintiau\n    send_to_workspace: Anfonwch i Workpace {{0}}\n    start_weg_app: CYFLWYNO NEU SEART CAIS {{0}}\n    switch_to_next_workspace: Newid i ofod gwaith nesaf\n    switch_to_previous_workspace: Newid i Workpace Blaenorol\n    switch_workspace: Newid i Workpace {{0}}\n    toggle_float: Modd arnofio ffenestr togl\n    toggle_monocle: Toggle Workpace Monocle Modd\n  readonly_tooltip: Llwybr byr darllen yn unig yw hwn\n  reset: Ailosod i ddiffygion\nsides:\n  bottom: Waelod\n  left: Chwith\n  right: Dde\n  top: Brigant\ntoolbar:\n  auto_hide: Cuddio awto\n  delay_to_hide: Oedi i guddio\n  delay_to_show: Oedi i ddangos\n  dock_side: Safle\n  enable: Galluogi bar offer ffansi\n  hide_mode:\n    always: Bob amser\n    never: Byth\n    on_overlap: Ar gorgyffwrdd\n  item_size: Maint yr Eitem\n  label: Bariau\n  margin: Maint Ymyl\n  padding: Maint Padin\n  placeholder: {}\nupdate:\n  available: Diweddariad ar gael!\n  channel: Diweddaru Sianel\n  downloading: Lawrlwytho ...\nwall:\n  backgrounds: Papurau Wal\n  blur: Aneglur\n  cancel: Canslo\n  close: Cau\n  collection_name: Enw Casgliad\n  collections: Casgliadau\n  contrast: Gyferbynnwch\n  corrupted_wallpapers_message: 'Papurau wal llygredig, ystyriwch ddileu''r rhain:'\n  create: Creu\n  create_collection: Creu Casgliad\n  default_collection: Casgliad Diofyn\n  delete_collection: Dileu Casgliad\n  edit_collection: Casgliad Golygu\n  enable: Galluogi Rheolwr Papur Wal\n  extend: Ymestyn cynradd\n  fit:\n    contain: Gynhwysaf\n    cover: Orchuddia ’\n    fill: Lanwem\n  flipHorizontal: Fflip llorweddol\n  flipVertical: Fflip fertigol\n  generating_thumbnails: Cynhyrchu Mân-luniau Papur Wal\n  hours: oriau\n  interval: Newid papur wal bob\n  minutes: munudau\n  monitor_collection: Casgliad Monitro\n  multimonitor_behaviour: Ymddygiad Amlfonitro\n  muted: Tewi sain fideo\n  no_background: Sioe sleidiau gwag, gan ddefnyddio cefndir thema yn lle.\n  no_collections: Dim casgliadau wedi eu creu eto. Cliciwch ar y botwm + i greu un.\n  objectFit: Ffit Cefndir\n  objectPosition: Safle cefndir\n  overlayColor: Lliw Troshaen\n  overlayMixBlendMode: Modd cyfuniad cymysgedd troshaen\n  per_monitor: Fesul monitor\n  playback: Cyflymder chwarae\n  position:\n    bottom: Waelod\n    center: Nghanolfan\n    left: Gadawaf\n    right: Dde\n    top: Brigant\n  processing_video: Prosesu fideo\n  random: Sioe sleidiau ar hap\n  saturation: Dirlawnder\n  seconds: eiliadau\n  select_collection: Dewiswch Casgliad\n  thumbnail_generation_complete: Cynhyrchu Mân-luniau Wedi'i Gyflawni\n  thumbnail_generation_finished: Mae cynhyrchu mân-luniau wedi gorffen yn llwyddiannus\n  wallpaper_collection: Casgliad Papur Wal\n  wallpaper_settings: Gosodiadau Papur Wal\n  withOverlay: Gyda throshaen\n  workspace_collections: Casgliadau Gweithle\nweg:\n  auto_hide: Cuddio awto\n  delay_to_hide: Oedi i guddio\n  delay_to_show: Oedi i ddangos\n  dock_side: Safle\n  enable: Galluogi doc/bar tasg\n  filtering: Hidlo Eitem\n  gap: Bwlch\n  hide_mode:\n    always: Bob amser\n    never: Byth\n    on_overlap: Ar gorgyffwrdd\n  items:\n    gap: Lle rhwng eitemau\n    label: Eitemau\n    pinned_visibility:\n      always: Bob amser\n      label: Eitemau wedi'u Pinio Gwelededd\n      when_primary: Pan fydd y monitor yn gynradd\n    show_instance_counter: Dangos cownter ffenestri agored\n    show_window_title: Dangos teitl ffenestr agored (dim ond llorweddol)\n    size: Maint eitem\n    split_windows: Hollti Windows (un eitem fesul ffenestr)\n    temporal_visibility:\n      all: Pawb\n      label: Eitemau heb eu Pinio Gwelededd\n      on_monitor: Ar Fonitor\n    visible_separators: Gwahanyddion gweladwy\n  label: Doc/bar tasgau\n  margin: Ngherlong\n  mode:\n    full_width: Lled sgrin lawn\n    min_content: Bach ag y gall fod\n  padding: Padin\n  show_end_task: Dangos tasg diwedd yn y bar tasgau\n  width: Lled\nwelcome:\n  give_a_review: Rhowch Adolygiad\n  message: >-\n    Mae Seelen UI yn amgylchedd bwrdd gwaith ffynhonnell agored am ddim ar gyfer\n    Windows. Yma mae gennych reolaeth lawn dros sut mae'ch bwrdd gwaith yn\n    edrych ac yn ymddwyn, sy'n eich galluogi i addasu pob manylyn i gyd-fynd\n    â'ch llif gwaith a'ch steil.\n  ok: Gadewch i ni ddechrau!\n  review: >-\n    Os ydych chi'n mwynhau defnyddio Seelen UI, ystyriwch adael adolygiad ar y\n    siop - mae eich adborth yn helpu'r prosiect i dyfu a gwella.\n  title: Croeso i UI Seelen!\nwidget:\n  enable: Galluogi'r teclyn hwn\n  enable_for_monitor: Galluogi ar y monitor hwn\n  instances: Achosion\nwm:\n  animations:\n    duration: Hyd animeiddio (MS)\n    ease_function: Swyddogaeth lleddfu animeiddiad\n    enable: Galluogi animeiddiadau ffenestr\n  author: Awduron\n  border:\n    enable: Galluogi ffin ffenestr\n    offset: Gwrthbwyso ar y ffin\n    width: Lled\n  description: Disgrifiadau\n  drag_behavior: Ymddygiad Llusgo\n  drag_behavior_options:\n    sort: Trefnu (ail-archebu ffenestri wrth lusgo)\n    swap: Cyfnewid (cyfnewid ffenestri ar y pen llusgo)\n  enable: Galluogi Rheolwr Ffenestr Teils\n  layout: Gynllun\n  resize_delta: Newid maint Delta (%)\n  space_between_containers: Lle rhwng cynwysyddion\n  workspace_offset: Gwrthbwyso lleoedd gwaith (ymylon)\n  workspace_padding: Padin lleoedd gwaith\n'yes': Ie\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/da.yml",
    "content": "action:\n  confirm: Er du sikker?\n  confirm_body: Denne handling kan ikke fortrydes.\napps_configurations:\n  app:\n    bindings: Binding (note begge muligheder er påkrævet)\n    category: Kategori\n    category_placeholder: Ingen\n    monitor: Overvåge\n    monitor_placeholder: Ingen\n    name: Navn\n    ok_create: skab\n    ok_edit: Opdatering\n    ok_readonly: Rediger som ny\n    options:\n      NoInteractive: Ingen interaktiv\n      VdPinned: Vis i alle arbejdsområder\n      WmFloat: Twm - Start flydende\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Ekstra muligheder\n    title_create: Oprettelse af {{navn}}\n    title_edit: Redigering {{navn}}\n    title_readonly: Visning {{navn}}\n    weg_options_label: Dock/proceslinjemuligheder\n    wm_options_label: Vindueschefindstillinger\n    workspace: Arbejdsområde\n    workspace_placeholder: Ingen\n  bundled_msg: >-\n    Disse samlede konfigurationer kan ikke redigeres og er designet til at give\n    dig den bedste oplevelse uden tilpasning. De konfigurerer automatisk de mest\n    almindelige applikationer til dig.\n  bundled_title: App Config bundtet med Seelen\n  confirm_delete: Er du sikker på, at du vil slette denne konfiguration/er?\n  confirm_delete_title: Bekræft slet\n  delete: Slet\n  export: Eksport\n  export_full: Eksporter indstillinger efter program\n  extra_info: >-\n    Seelen UI bruger kun én identifikator pr. app (første match fundet), så\n    rækkefølgen i, hvordan de specificeres, er vigtig, det senest tilføjede vil\n    blive prioriteret, da bemærk, at tabellen er sorteret som standard fra\n    seneste til gammel.\n  identifier:\n    add_block: Tilføj blok\n    and: OG\n    id: Identifikator\n    kind: Identificer ved\n    matching_strategy: Matchende strategi\n    matching_strategy_option:\n      contains: Indeholder\n      ends_with: Slutter med\n      equals: Lige til\n      regex: Regelmæssigt udtryk\n      starts_with: Starter med\n    negation: Negat matchning\n    or: ELLER\n    remove: Slet blok\n    type:\n      class: klasse\n      exe: Exe\n      path: Sti\n      title: Titel\n  import: Importere\n  import_full: Importer indstillinger efter program\n  new: Ny\n  search: Søg\n  swap: Bytte rundt\ncancel: Afbestille\nclose: Tæt\ndelete: Slet\ndevtools:\n  app_folders: Appmapper\n  custom_config_file: Indlæs brugerdefineret konfigurationsfil\n  data_folder: Datamappe\n  enable: Aktivér udviklerværktøjer\n  install_folder: Installationsmappe\n  load: belastning\n  settings_file: Indstillinger fil\n  simulate_perm:\n    label: Simuler anmodning om widgettilladelse\n    result_allowed: ✓ Tilladelse givet\n    result_denied: ✗ Tilladelse nægtet\n    trigger: Simuler\n    widget_id_placeholder: '@forfatter/widget-navn'\nextras:\n  clear_icons: Ryd cache for systemikoner\n  clear_icons_tooltip: En genstart kan være nødvendig for at få fuld effekt på alle widgets\n  exit: Afslut/exit\n  links: Officielle links\n  relaunch: Relancering\n  version: Version\n  version_fixed: >-\n    Applikationen og WebView2 Runtime-versionerne er faste. Det betyder, at\n    applikationen ikke vil modtage opdateringer, og WebView2 Runtime vil ikke\n    automatisk blive opdateret med Windows-opdateringer.\ngeneral:\n  accent_color: Accentfarve\n  date_format: Datoformat\n  date_format_how_to: Hvordan skriver man et datoformat?\n  hardware_acceleration: Hardwareacceleration\n  hardware_acceleration_description: >-\n    Deaktivering af hardwareacceleration vil reducere hukommelsesforbruget, men\n    kan forårsage ydeevneproblemer. Er sikkert at deaktivere, hvis du ikke vil\n    bruge levende tapeter.\n  icon_pack:\n    available: Tilgængelige ikonpakker\n    selected: Aktive ikonpakker\n  language: Sprog\n  monday: mandag\n  performance_mode:\n    on_battery: På batteri\n    on_energy_saver: På energibesparende\n    options:\n      disabled: Handicappet\n      extreme: Ekstrem\n      minimal: Minimal\n    plugged: Tilsluttet eller opladning\n  polling_interval: System polling interval\n  polling_interval_description: >-\n    Hvor ofte (i sekunder) Seelen UI kontrollerer systemressourcer som CPU, RAM,\n    netværk og diskaktivitet. Et mindre antal betyder hyppigere opdateringer,\n    men lidt højere ressourceforbrug.\n  saturday: lørdag\n  start_of_week: Start på ugen\n  startup: Kør ved opstart?\n  sunday: søndag\n  theme:\n    available: Tilgængelige temaer\n    selected: Aktive temaer\nheader:\n  labels:\n    config: Konfigurationer\n    developer: For udviklere\n    extras: Ekstramateriale\n    general: Generel\n    home: Hjem\n    icon_pack_editor: Cachelagrede ikoner\n    iconpack: Ikon-pakker\n    monitors: Monitors\n    plugin: Plugins\n    resources: Ressourcer\n    shortcuts: Genveje\n    soundpack: Lydpakker\n    specific_apps: Indstillinger efter applikation\n    theme: Temaer\n    virtual_desk: Virtuelle desktops\n    wallpaper: Baggrunde\n    widget: Widgets\nhome:\n  new_resources: Nye ressourcer\ninherit: Arve\ninProgress: I gang...\ninsert: Indsæt\nloading: Indlæser...\nmiscellaneous: Diverse\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Mere\n'no': Ingen\nopen: Åben\nquit: Afslut\nremove: Fjerne\nreset_all_to_default: Nulstil alt til standardværdier\nreset_to_default: Nulstil til standardværdi\nresources:\n  app_outdated: Denne ressource kræver en nyere version af Seelen UI for at fungere korrekt.\n  corrupted_wallpaper: >-\n    Kunne ikke udtrække miniaturebilledet - beskadiget eller ikke-understøttet\n    videoformat\n  delete: Slet ressource\n  discover: Opdag flere ressourcer\n  has_update: En opdatering er tilgængelig\n  high_impact: Stor indflydelse på ydeevnen\n  import_wallpapers: Importer lokale baggrunde\n  open_folder: Åbn ressourcemappen\n  outdated: >-\n    Denne ressource er designet til en ældre version af Seelen UI og fungerer\n    muligvis ikke korrekt.\n  see_on_website: Se på hjemmesiden\nreview_request:\n  not_now: Ikke nu\n  sure: Sikker!\n  title: Nyder du Seelen UI?\nsave: Gemme\nsave_and_restart: Gem & genstart\nsearch: Søge\nsee_more: Se mere\nshortcuts:\n  duplicate_error: Denne genvej er duplikeret\n  enable: Aktivér det indbyggede genvejssystem\n  enable_tooltip: >-\n    Deaktiver, hvis du vil implementere dit eget genvejssystem ved hjælp af\n    Seelen UI Client\n  labels:\n    create_new_workspace: Opret et nyt arbejdsområde\n    cycle_stack_next: Cyklus-stak Næste\n    cycle_stack_prev: Cyklus-stak Tidligere\n    cycle_wallpaper_next: Skift til næste baggrund\n    cycle_wallpaper_prev: Skift til forrige baggrund\n    decrease_height: Reducer højden\n    decrease_width: Reducer bredden\n    destroy_current_workspace: Ødelæg det nuværende arbejdsområde\n    focus_bottom: Fokus på bunden\n    focus_latest: Fokus på det seneste\n    focus_left: Fokus til venstre\n    focus_right: Fokuser rigtigt\n    focus_top: Fokus på toppen\n    increase_height: Forøg højden\n    increase_width: Øg bredden\n    misc_force_quit: Force stopper\n    misc_force_restart: Fremtving genstart\n    misc_open_settings: Åbn indstillinger\n    misc_toggle_lock_tracing: Skift låsesporing (logfiler)\n    misc_toggle_win_event_tracing: Slå Win Event Tracing til (logfiler)\n    move_to_workspace: Flyt til arbejdsområde {{0}}\n    move_window_down: Flyt vinduet til bunden\n    move_window_left: Flyt vinduet til venstre\n    move_window_right: Flyt vinduet til højre\n    move_window_up: Flyt vinduet til toppen\n    pause_tiling: Sæt Tiling Window Manager på pause\n    reserve_bottom: Reserve bund\n    reserve_float: Reserve Float\n    reserve_left: Reserve til venstre\n    reserve_right: Reserver ret\n    reserve_stack: Reserve-stak\n    reserve_top: Reserver top\n    restore_sizes: Gendan størrelser\n    send_to_workspace: Send til arbejdsområde {{0}}\n    start_weg_app: Fokuser eller start programmet {{0}}\n    switch_to_next_workspace: Skift til næste arbejdsområde\n    switch_to_previous_workspace: Skift til forrige arbejdsområde\n    switch_workspace: Skift til arbejdsområde {{0}}\n    toggle_float: Skift mellem vinduets flydende tilstand\n    toggle_monocle: Skift arbejdsområdets Monocle-tilstand\n  readonly_tooltip: Dette er en skrivebeskyttet genvej\n  reset: Nulstil til standardindstillinger\nsides:\n  bottom: Bund\n  left: Venstre\n  right: Højre\n  top: Top\ntoolbar:\n  auto_hide: Auto skjul\n  delay_to_hide: Forsink med at skjule\n  delay_to_show: Forsinket fremvisning\n  dock_side: Position\n  enable: Aktivér fancy værktøjslinje\n  hide_mode:\n    always: Altid\n    never: Aldrig\n    on_overlap: På overlapning\n  item_size: Varestørrelse\n  label: Værktøjslinje\n  margin: Marginstørrelse\n  padding: Polstring størrelse\n  placeholder: {}\nupdate:\n  available: Opdatering tilgængelig!\n  channel: Opdateringskanal\n  downloading: Download ...\nwall:\n  backgrounds: Baggrunde\n  blur: Sløring\n  cancel: Ophæve\n  close: Tæt\n  collection_name: Samlingens navn\n  collections: Samlinger\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Ødelagte tapeter, overvej at slette disse:'\n  create: Skabe\n  create_collection: Opret samling\n  default_collection: Standard samling\n  delete_collection: Slet samling\n  edit_collection: Rediger samling\n  enable: Aktivér tapet manager\n  extend: Forlæng primær\n  fit:\n    contain: Indeholder\n    cover: Omslag\n    fill: Fyld\n  flipHorizontal: Vend vandret\n  flipVertical: Vend lodret\n  generating_thumbnails: Generering af tapetminiaturer\n  hours: timer\n  interval: Skift tapet hver\n  minutes: minutter\n  monitor_collection: Monitor Indsamling\n  multimonitor_behaviour: Multimonitor-adfærd\n  muted: Slå videolyd fra\n  no_background: Tom lysbilledshow ved hjælp af temas baggrund i stedet.\n  no_collections: Ingen samlinger oprettet endnu. Klik på knappen + for at oprette en.\n  objectFit: Baggrundstilpasning\n  objectPosition: Baggrundsposition\n  overlayColor: Overlay-farve\n  overlayMixBlendMode: Overlay Mix Blend Mode\n  per_monitor: Per skærm\n  playback: Afspilningshastighed\n  position:\n    bottom: I bunden\n    center: Center\n    left: Til venstre\n    right: Til højre\n    top: Til toppen\n  processing_video: Behandler video\n  random: Randomiser slideshow\n  saturation: Mætning\n  seconds: sekunder\n  select_collection: Vælg Samling\n  thumbnail_generation_complete: Generering af miniaturebilleder er fuldført\n  thumbnail_generation_finished: Generering af miniaturebilleder er afsluttet\n  wallpaper_collection: Tapet samling\n  wallpaper_settings: Baggrundsindstillinger\n  withOverlay: Med overlay\n  workspace_collections: Arbejdsrumssamlinger\nweg:\n  auto_hide: Auto skjul\n  delay_to_hide: Forsink med at skjule\n  delay_to_show: Forsinket fremvisning\n  dock_side: Position\n  enable: Aktivér dock/proceslinje\n  filtering: Filtrering af varer\n  gap: Gap\n  hide_mode:\n    always: Altid\n    never: Aldrig\n    on_overlap: På overlapning\n  items:\n    gap: Rum mellem genstande\n    label: Genstande\n    pinned_visibility:\n      always: Altid\n      label: Fastgjorte elementers synlighed\n      when_primary: Når monitoren er primær\n    show_instance_counter: Vis tæller for åbne vinduer\n    show_window_title: Vis titel på åbent vindue (kun vandret)\n    size: Varestørrelse\n    split_windows: Opdelte vinduer (et element pr. vindue)\n    temporal_visibility:\n      all: Alle\n      label: Synlighed af frigjorte elementer\n      on_monitor: På monitor\n    visible_separators: Synlige separatorer\n  label: Dock/proceslinje\n  margin: Margin\n  mode:\n    full_width: Fuld skærmbredde\n    min_content: Så lille som det kan være\n  padding: Polstring\n  show_end_task: Vis afslutning af opgave på proceslinjen\n  width: Bredde\nwelcome:\n  give_a_review: Giv en anmeldelse\n  message: >-\n    Seelen UI er et gratis og open source-skrivebordsmiljø til Windows. Her har\n    du fuld kontrol over, hvordan dit skrivebord ser ud og opfører sig, så du\n    kan tilpasse hver eneste detalje, så den matcher din arbejdsgang og stil.\n  ok: Lad os starte!\n  review: >-\n    Hvis du kan lide at bruge Seelen UI, kan du overveje at skrive en anmeldelse\n    i butikken – din feedback hjælper projektet med at vokse og forbedre.\n  title: Velkommen til Seelen UI!\nwidget:\n  enable: Aktivér denne widget\n  enable_for_monitor: Aktiver på denne skærm\n  instances: Forekomster\nwm:\n  animations:\n    duration: Animationsvarighed (MS)\n    ease_function: Animationsaflastningsfunktion\n    enable: Aktivér vindues animationer\n  author: Forfatter\n  border:\n    enable: Aktivér vindues grænse\n    offset: Border Offset\n    width: Grænsebredde\n  description: Beskrivelse\n  drag_behavior: Træk adfærd\n  drag_behavior_options:\n    sort: Sorter (omarranger vinduer, mens du trækker)\n    swap: Swap (byt vinduer ved træk-enden)\n  enable: Aktivér flisevindue manager\n  layout: Layout\n  resize_delta: Ændre størrelse på Delta (%)\n  space_between_containers: Rum mellem containere\n  workspace_offset: Arbejdsområder offset (marginer)\n  workspace_padding: Arbejdsområder polstring\n'yes': Ja\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/de.yml",
    "content": "action:\n  confirm: Bist du sicher?\n  confirm_body: Diese Aktion kann nicht rückgängig gemacht werden.\napps_configurations:\n  app:\n    bindings: Bindung (beide Optionen sind erforderlich)\n    category: Kategorie\n    category_placeholder: Keine\n    monitor: Monitor\n    monitor_placeholder: Keine\n    name: Name\n    ok_create: Erstellen\n    ok_edit: Ändern\n    ok_readonly: Als neu bearbeiten\n    options:\n      NoInteractive: Keine Interaktivität\n      VdPinned: In allen Arbeitsbereichen anzeigen\n      WmFloat: Twm – Beginnen Sie mit dem Schweben\n      WmForce: Twm – Force Manage\n      WmUnmanage: Twm – Nicht verwalten\n    options_label: Zusätzliche Optionen\n    title_create: '{{name}} erstellen'\n    title_edit: Bearbeitung {{Name}}\n    title_readonly: '{{name}} ansehen'\n    weg_options_label: Dock/Taskleistenoptionen\n    wm_options_label: Fenstermanageroptionen\n    workspace: Arbeitsbereich\n    workspace_placeholder: Keine\n  bundled_msg: >-\n    Diese gebündelten Konfigurationen sind nicht bearbeitbar und wurden\n    entwickelt, um dir das beste Erlebnis ohne Anpassung zu bieten. Sie\n    konfigurieren die gebräuchlichsten Anwendungen automatisch für dich.\n  bundled_title: Mit Seelen gebündelte App-Konfiguration\n  confirm_delete: Möchtest du diese Konfiguration wirklich löschen?\n  confirm_delete_title: Löschen bestätigen\n  delete: Löschen\n  export: Exportieren\n  export_full: Einstellungen nach Anwendung exportieren\n  extra_info: >-\n    Seelen UI Verwenden Sie nur einen Kenner pro App (zuerst übereinstimmend\n    gefunden). Die Reihenfolge, in der spezifisch ist, ist wichtig. Die neueste\n    hinzugefügte zusätzliche wird priorisiert, da die Tabelle standardmäßig von\n    der neuesten nach alt sortiert wird.\n  identifier:\n    add_block: Block hinzufügen\n    and: UND\n    id: Identifikator\n    kind: Identifizieren nach\n    matching_strategy: Abgleichstrategie\n    matching_strategy_option:\n      contains: Enthält\n      ends_with: Endet mit\n      equals: Gleich\n      regex: Regulärer Ausdruck\n      starts_with: Beginnt mit\n    negation: Abgleich negieren\n    or: ODER\n    remove: Block löschen\n    type:\n      class: Klasse\n      exe: Exe\n      path: Weg\n      title: Titel\n  import: Importieren\n  import_full: Einstellungen nach Anwendung importieren\n  new: Neu\n  search: Suchen\n  swap: Austauschen\ncancel: Abbrechen\nclose: Schließen\ndelete: Löschen\ndevtools:\n  app_folders: Anwendungsordner\n  custom_config_file: Benutzerdefinierte Konfigurationsdatei laden\n  data_folder: Datenordner\n  enable: Entwicklerwerkzeuge aktivieren\n  install_folder: Installationsordner\n  load: Laden\n  settings_file: Einstellungsdatei\n  simulate_perm:\n    label: Widget-Berechtigungsanfrage simulieren\n    result_allowed: ✓ Erlaubnis erteilt\n    result_denied: ✗ Erlaubnis verweigert\n    trigger: Simulieren\n    widget_id_placeholder: '@Autor/Widget-Name'\nextras:\n  clear_icons: System-Symbol-Cache löschen\n  clear_icons_tooltip: >-\n    Ein Neustart könnte erforderlich sein, um alle Widgets vollständig zu\n    aktivieren.\n  exit: Beenden\n  links: Offizielle Links\n  relaunch: Neu starten\n  version: Version\n  version_fixed: >-\n    Die Anwendungs- und WebView2-Runtime-Versionen sind behoben. Dies bedeutet,\n    dass die Anwendung keine Updates erhält und die WebView2 Runtime nicht\n    automatisch mit Windows-Updates aktualisiert wird.\ngeneral:\n  accent_color: Akzentfarbe\n  date_format: Datumsformat\n  date_format_how_to: Wie schreibe ich ein Datumsformat?\n  hardware_acceleration: Hardwarebeschleunigung\n  hardware_acceleration_description: >-\n    Das Deaktivieren der Hardwarebeschleunigung verringert die Speichernutzung,\n    kann jedoch zu Leistungsproblemen führen. Kann sicher deaktiviert werden,\n    wenn Sie keine Live-Hintergründe verwenden.\n  icon_pack:\n    available: Verfügbare Icon-Pakete\n    selected: Aktive Icon-Pakete\n  language: Sprache\n  monday: Montag\n  performance_mode:\n    on_battery: Batterie\n    on_energy_saver: Auf Energiesparer\n    options:\n      disabled: Deaktiviert\n      extreme: Extrem\n      minimal: Minimal\n    plugged: Verstopft oder aufgeladen\n  polling_interval: Systemabfrageintervall\n  polling_interval_description: >-\n    Wie oft (in Sekunden) prüft Seelen UI Systemressourcen wie CPU, RAM,\n    Netzwerk und Festplattenaktivität? Eine kleinere Zahl bedeutet häufigere\n    Updates, aber eine etwas höhere Ressourcennutzung.\n  saturday: Samstag\n  start_of_week: Wochenbeginn\n  startup: Beim Hochfahren ausführen?\n  sunday: Sonntag\n  theme:\n    available: Verfügbare Themen\n    selected: Aktive Themen\nheader:\n  labels:\n    config: Konfigurationen\n    developer: Für Entwickler\n    extras: Extras\n    general: Allgemein\n    home: Home\n    icon_pack_editor: Zwischengespeicherte Icons\n    iconpack: Icon Packs\n    monitors: Monitore\n    plugin: Plugins\n    resources: Ressourcen\n    shortcuts: Kurzbefehle\n    soundpack: Sound Packs\n    specific_apps: Einstellungen nach Anwendung\n    theme: Themen\n    virtual_desk: Virtuelle Desktops\n    wallpaper: Bildschirmhintergründe\n    widget: Widgets\nhome:\n  new_resources: Neue Ressourcen\ninherit: Erben\ninProgress: In Bearbeitung...\ninsert: Einfügen\nloading: Am Laden...\nmiscellaneous: Verschiedenes\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Mehr\n'no': NEIN\nopen: Öffnen\nquit: Beenden\nremove: Entfernen\nreset_all_to_default: Alles auf Standardwerte zurücksetzen\nreset_to_default: Zurücksetzen auf den Standardwert\nresources:\n  app_outdated: >-\n    Diese Ressource erfordert eine neuere Version von Seelen UI, um richtig zu\n    funktionieren.\n  corrupted_wallpaper: >-\n    Miniaturansicht konnte nicht extrahiert werden – beschädigtes oder nicht\n    unterstütztes Videoformat\n  delete: Ressource löschen\n  discover: Weitere Ressourcen entdecken\n  has_update: Ein Update ist verfügbar\n  high_impact: Hoher Einfluss auf die Leistung\n  import_wallpapers: Lokale Hintergrundbilder importieren\n  open_folder: Ressourcenordner öffnen\n  outdated: >-\n    Diese Ressource wurde für eine ältere Version von Seelen UI entwickelt und\n    funktioniert möglicherweise nicht richtig.\n  see_on_website: Siehe auf der Website\nreview_request:\n  not_now: Nicht jetzt\n  sure: Sicher!\n  title: Gefällt dir die Benutzeroberfläche von Seelen?\nsave: Speichern\nsave_and_restart: Speichern & neu starten\nsearch: Suchen\nsee_more: Mehr sehen\nshortcuts:\n  duplicate_error: Diese Verknüpfung ist dupliziert\n  enable: Aktivieren Sie ein integriertes Verknüpfungssystem\n  enable_tooltip: >-\n    Deaktivieren Sie, wenn Sie Ihr eigenes Verknüpfungssystem mit dem Seelen UI\n    -Client implementieren\n  labels:\n    create_new_workspace: Erstellen Sie einen neuen Arbeitsbereich\n    cycle_stack_next: Fahrradstapel als nächstes\n    cycle_stack_prev: Zyklusstapel vorher\n    cycle_wallpaper_next: Wechseln Sie zum nächsten Tapeten\n    cycle_wallpaper_prev: Wechseln Sie in die vorherige Tapete\n    decrease_height: Abnahme der Höhe\n    decrease_width: Verringerung der Breite\n    destroy_current_workspace: Zerstören Sie den aktuellen Arbeitsbereich\n    focus_bottom: Fokus auf den Boden\n    focus_latest: Fokus neuest\n    focus_left: Fokus links\n    focus_right: Fokus rechts\n    focus_top: Fokus auf die Spitze\n    increase_height: Erhöhen Sie die Größe\n    increase_width: Breite erhöhen\n    misc_force_quit: Kraft verlassen\n    misc_force_restart: Neustart erzwingen\n    misc_open_settings: Einstellungen geöffnet\n    misc_toggle_lock_tracing: Schaltersperrverfolgung (Protokolle)\n    misc_toggle_win_event_tracing: Verfolgung des Sieg -Ereignisses (Protokolle) umschalten\n    move_to_workspace: Wechseln Sie zum Arbeitsbereich {{0}}\n    move_window_down: Fenster nach unten bewegen\n    move_window_left: Fenster nach links bewegen\n    move_window_right: Fenster nach rechts bewegen\n    move_window_up: Fenster nach oben bewegen\n    pause_tiling: Pause von Fliesenfenstermanager innehalten\n    reserve_bottom: Reservenboden\n    reserve_float: Reserve Float\n    reserve_left: Reserve links\n    reserve_right: Rechts reservieren\n    reserve_stack: Reservestapel\n    reserve_top: Reserve Top\n    restore_sizes: Größen wiederherstellen\n    send_to_workspace: An den Arbeitsbereich senden {{0}}\n    start_weg_app: Fokus oder Start Application {{0}}\n    switch_to_next_workspace: Wechseln Sie zum nächsten Arbeitsbereich\n    switch_to_previous_workspace: Wechseln Sie zum vorherigen Arbeitsbereich\n    switch_workspace: Wechseln Sie zum Arbeitsbereich {{0}}\n    toggle_float: Wechselfensterfloatmodus umschalten\n    toggle_monocle: Umschalten des Arbeitsbereichs Monocle -Modus\n  readonly_tooltip: Dies ist eine schreibgeschützte Verknüpfung\n  reset: Auf Standards zurücksetzen\nsides:\n  bottom: Unten\n  left: Links\n  right: Rechts\n  top: Oben\ntoolbar:\n  auto_hide: Automatisch ausblenden\n  delay_to_hide: Verzögerung zum Verstecken\n  delay_to_show: Verzögerung beim Anzeigen\n  dock_side: Position\n  enable: Elegante Werkzeugleiste aktivieren\n  hide_mode:\n    always: Stets\n    never: Niemals\n    on_overlap: Auf Überlappung\n  item_size: Artikelgröße\n  label: Symbolleiste\n  margin: Randgröße\n  padding: Polstergröße\n  placeholder: {}\nupdate:\n  available: Update verfügbar!\n  channel: Update-Kanal\n  downloading: Am Herunterladen ...\nwall:\n  backgrounds: Hintergrundbilder\n  blur: Unschärfe\n  cancel: Stornieren\n  close: Schließen\n  collection_name: Sammlungsname\n  collections: Sammlungen\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Beschädigte Hintergrundbilder. Löschen Sie diese:'\n  create: Erstellen\n  create_collection: Sammlung erstellen\n  default_collection: Standardsammlung\n  delete_collection: Sammlung löschen\n  edit_collection: Sammlung bearbeiten\n  enable: Tapetenmanager aktivieren\n  extend: Grundschule verlängern\n  fit:\n    contain: Enthält\n    cover: Abdeckung\n    fill: Füllen Sie\n  flipHorizontal: Horizontal spiegeln\n  flipVertical: Vertikal drehen\n  generating_thumbnails: Generieren von Hintergrundbild-Miniaturansichten\n  hours: Std.\n  interval: Wechseln Sie die Hintergrundbild\n  minutes: Minuten\n  monitor_collection: Sammlung überwachen\n  multimonitor_behaviour: Multimonitor-Verhalten\n  muted: Video-Audio stumm schalten\n  no_background: Leere Diashow, stattdessen wird der Hintergrund des Themas verwendet.\n  no_collections: >-\n    Es wurden noch keine Sammlungen erstellt. Klicken Sie auf die Schaltfläche\n    „+“, um eins zu erstellen.\n  objectFit: Hintergrund Passform\n  objectPosition: Hintergrund Position\n  overlayColor: Überlagerungsfarbe\n  overlayMixBlendMode: Mischmodus Überlagerung\n  per_monitor: Pro Monitor\n  playback: Wiedergabegeschwindigkeit\n  position:\n    bottom: Unten\n    center: Zentrum\n    left: Links\n    right: Rechts\n    top: Top\n  processing_video: Video verarbeiten\n  random: Die Diashow randomisieren\n  saturation: Sättigung\n  seconds: Sekunden\n  select_collection: Wählen Sie Sammlung\n  thumbnail_generation_complete: Miniaturbild-Erstellung abgeschlossen\n  thumbnail_generation_finished: Die Miniaturbildgenerierung wurde erfolgreich abgeschlossen\n  wallpaper_collection: Tapetensammlung\n  wallpaper_settings: Hintergrundeinstellungen\n  withOverlay: Mit Overlay\n  workspace_collections: Arbeitsbereichssammlungen\nweg:\n  auto_hide: Automatisch ausblenden\n  delay_to_hide: Verzögerung zum Verstecken\n  delay_to_show: Verzögerung beim Anzeigen\n  dock_side: Position\n  enable: Dock/Taskleiste aktivieren\n  filtering: Element-Filterung\n  gap: Abstand\n  hide_mode:\n    always: Stets\n    never: Niemals\n    on_overlap: Auf Überlappung\n  items:\n    gap: Abstand zwischen Elementen\n    label: Elemente\n    pinned_visibility:\n      always: Stets\n      label: Sichtbarkeit angehefteter Elemente\n      when_primary: Wenn der Monitor primär ist\n    show_instance_counter: Zähler für offene Fenster anzeigen\n    show_window_title: Titel des geöffneten Fensters anzeigen (nur horizontal)\n    size: Elementgröße\n    split_windows: Geteilte Fenster (ein Element pro Fenster)\n    temporal_visibility:\n      all: Alle\n      label: Sichtbarkeit nicht angehefteter Elemente\n      on_monitor: Auf dem Monitor\n    visible_separators: Sichtbare Trennzeichen\n  label: Dock/Taskleiste\n  margin: Rand\n  mode:\n    full_width: Volle Bildschirmbreite\n    min_content: So klein wie möglich\n  padding: Polsterung\n  show_end_task: Beendete Aufgabe in der Taskleiste anzeigen\n  width: Breite\nwelcome:\n  give_a_review: Geben Sie eine Bewertung ab\n  message: >-\n    Seelen UI ist eine kostenlose Open-Source-Desktopumgebung für Windows. Hier\n    haben Sie die volle Kontrolle über das Aussehen und Verhalten Ihres Desktops\n    und können jedes Detail an Ihren Arbeitsablauf und Stil anpassen.\n  ok: Fangen wir an!\n  review: >-\n    Wenn Ihnen die Nutzung der Seelen-Benutzeroberfläche Spaß macht, denken Sie\n    darüber nach, eine Bewertung im Store abzugeben – Ihr Feedback trägt dazu\n    bei, dass das Projekt wächst und sich verbessert.\n  title: Willkommen bei Seelen UI!\nwidget:\n  enable: Aktivieren Sie dieses Widget\n  enable_for_monitor: Auf diesem Monitor aktivieren\n  instances: Instanzen\nwm:\n  animations:\n    duration: Animationsdauer (MS)\n    ease_function: Animation Lockering -Funktion\n    enable: Aktivieren Sie die Animationen von Windows\n  author: Autor\n  border:\n    enable: Fensterrand aktivieren\n    offset: Randversatz\n    width: Randbreite\n  description: Beschreibung\n  drag_behavior: Drag-Verhalten\n  drag_behavior_options:\n    sort: Sortieren (Fenster beim Ziehen neu anordnen)\n    swap: Tauschen (Fenster am Ende des Ziehens tauschen)\n  enable: Aktivieren Sie den Tiling Window Manager\n  layout: Layout\n  resize_delta: Relative Größenänderung (%)\n  space_between_containers: Abstand zwischen Containern\n  workspace_offset: Arbeitsbereichs-Versatz (Ränder)\n  workspace_padding: Arbeitsbereichs-Padding\n'yes': Ja\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/el.yml",
    "content": "action:\n  confirm: Είστε σίγουροι;\n  confirm_body: Αυτή η ενέργεια δεν μπορεί να ανατραπεί.\napps_configurations:\n  app:\n    bindings: Σύνδεση (σημειώστε ότι και οι δύο επιλογές απαιτούνται)\n    category: Κατηγορία\n    category_placeholder: Κανένας\n    monitor: Οθόνη\n    monitor_placeholder: Κανένας\n    name: Ονομα\n    ok_create: Δημιουργώ\n    ok_edit: Εκσυγχρονίζω\n    ok_readonly: Επεξεργαστείτε ως νέο\n    options:\n      NoInteractive: Χωρίς διαδραστικό\n      VdPinned: Εμφάνιση σε όλους τους χώρους εργασίας\n      WmFloat: Twm - Ξεκινήστε να επιπλέετε\n      WmForce: Twm - Διαχείριση δύναμης\n      WmUnmanage: Twm - Κατάργηση διαχείρισης\n    options_label: Επιπλέον επιλογές\n    title_create: Δημιουργία {{name}}\n    title_edit: Επεξεργασία {{όνομα}}\n    title_readonly: Προβολή {{όνομα}}\n    weg_options_label: Επιλογές της γραμμής αποβάθρας/εργασιών\n    wm_options_label: Επιλογές διαχείρισης παραθύρων\n    workspace: Χώρος εργασίας\n    workspace_placeholder: Κανένας\n  bundled_msg: >-\n    Αυτές οι συνδεδεμένες διαμορφώσεις δεν είναι επεξεργάσιμες και έχουν\n    σχεδιαστεί για να σας παρέχουν την καλύτερη εμπειρία χωρίς προσαρμογή.\n    Διαμορφώνουν αυτόματα τις πιο συνηθισμένες εφαρμογές για εσάς.\n  bundled_title: App config που συνδέεται με Seelen\n  confirm_delete: Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν τη διαμόρφωση/s;\n  confirm_delete_title: Επιβεβαιώστε τη διαγραφή\n  delete: Διαγράφω\n  export: Εξαγωγή\n  export_full: Εξαγωγή ρυθμίσεων ανά εφαρμογή\n  extra_info: >-\n    Η διεπαφή χρήστη Seelen χρησιμοποιεί μόνο ένα αναγνωριστικό ανά εφαρμογή\n    (βρέθηκε η πρώτη αντιστοίχιση), επομένως η σειρά στον τρόπο καθορισμού είναι\n    σημαντική, τα πιο πρόσφατα που προστέθηκαν θα τεθούν σε προτεραιότητα, καθώς\n    σημειώστε ότι ο πίνακας ταξινομείται από προεπιλογή από το πιο πρόσφατο στο\n    παλαιό.\n  identifier:\n    add_block: Προσθέστε μπλοκ\n    and: ΚΑΙ\n    id: Αναγνωριστικό\n    kind: Προσδιορίζω\n    matching_strategy: Στρατηγική αντιστοίχισης\n    matching_strategy_option:\n      contains: Περιέχει\n      ends_with: Τελειώνει με\n      equals: Ίσο\n      regex: Τακτική έκφραση\n      starts_with: Ξεκινά με\n    negation: Αρνείστε την αντιστοίχιση\n    or: Ή\n    remove: Διαγραφή μπλοκ\n    type:\n      class: Τάξη\n      exe: Exe\n      path: Μονοπάτι\n      title: Τίτλος\n  import: Εισαγωγή\n  import_full: Εισαγωγή ρυθμίσεων ανά εφαρμογή\n  new: Νέος\n  search: Αναζήτηση\n  swap: Ανταλαγή\ncancel: Ματαίωση\nclose: Κοντά\ndelete: Διαγράφω\ndevtools:\n  app_folders: Φακέλους εφαρμογών\n  custom_config_file: Φόρτωση προσαρμοσμένου αρχείου ρυθμίσεων\n  data_folder: Φάκελος δεδομένων\n  enable: Ενεργοποίηση εργαλείων προγραμματιστή\n  install_folder: Φάκελος εγκατάστασης\n  load: Φορτώνω\n  settings_file: Αρχείο ρυθμίσεων\n  simulate_perm:\n    label: Προσομοίωση αιτήματος άδειας γραφικού στοιχείου\n    result_allowed: ✓ Χορηγήθηκε άδεια\n    result_denied: ✗ Δεν επιτρέπεται η άδεια\n    trigger: Προσποιούμαι\n    widget_id_placeholder: '@συγγραφέας/όνομα γραφικού στοιχείου'\nextras:\n  clear_icons: Εκκαθάριση προσωρινής μνήμης εικονιδίων συστήματος\n  clear_icons_tooltip: Μπορεί να απαιτηθεί επανεκκίνηση για να ισχύσει πλήρως σε όλα τα widgets\n  exit: Εγκατάλειψη/έξοδο\n  links: Επίσημοι σύνδεσμοι\n  relaunch: Ξεκίνησα ξανά\n  version: Εκδοχή\n  version_fixed: >-\n    Η εφαρμογή και η έκδοση WebView2 Runtime έχουν επιδιορθωθεί. Αυτό σημαίνει\n    ότι η εφαρμογή δεν θα λαμβάνει ενημερώσεις και ο χρόνος εκτέλεσης WebView2\n    δεν θα ενημερώνεται αυτόματα με ενημερώσεις των Windows.\ngeneral:\n  accent_color: Έμφαση στο χρώμα\n  date_format: Μορφή ημερομηνίας\n  date_format_how_to: Πώς να γράψετε μια μορφή ημερομηνίας;\n  hardware_acceleration: Επιτάχυνση υλικού\n  hardware_acceleration_description: >-\n    Η απενεργοποίηση της επιτάχυνσης υλικού θα μειώσει τη χρήση της μνήμης, αλλά\n    μπορεί να προκαλέσει προβλήματα απόδοσης. Είναι ασφαλές να απενεργοποιήσετε\n    εάν δεν χρησιμοποιείτε ζωντανές ταπετσαρίες.\n  icon_pack:\n    available: Διαθέσιμα πακέτα εικονιδίων\n    selected: Ενεργά πακέτα εικονιδίων\n  language: Γλώσσα\n  monday: Δευτέρα\n  performance_mode:\n    on_battery: Σε μπαταρία\n    on_energy_saver: Σχετικά με την εξοικονόμηση ενέργειας\n    options:\n      disabled: Ανάπηρος\n      extreme: Ακρο\n      minimal: Ελάχιστος\n    plugged: Συνδεδεμένος ή φόρτιση\n  polling_interval: Διάστημα ψηφοφορίας συστήματος\n  polling_interval_description: >-\n    Πόσο συχνά (σε δευτερόλεπτα) το Seelen UI ελέγχει τους πόρους του συστήματος\n    όπως η CPU, η RAM, το δίκτυο και η δραστηριότητα του δίσκου. Ένας μικρότερος\n    αριθμός σημαίνει πιο συχνές ενημερώσεις, αλλά ελαφρώς υψηλότερη χρήση πόρων.\n  saturday: Σάββατο\n  start_of_week: Έναρξη Εβδομάδας\n  startup: Εκτέλεση κατά την εκκίνηση;\n  sunday: Κυριακή\n  theme:\n    available: Διαθέσιμα θέματα\n    selected: Ενεργά θέματα\nheader:\n  labels:\n    config: Διαμορφώσεις\n    developer: Για προγραμματιστές\n    extras: Πρόσθετα\n    general: Γενικός\n    home: Σπίτι\n    icon_pack_editor: Αποθηκευμένα εικονίδια\n    iconpack: Πακέτα εικονιδίων\n    monitors: Παρακολουθεί\n    plugin: Plugins\n    resources: Πόροι\n    shortcuts: Συντομεύσεις\n    soundpack: Πακέτα ήχου\n    specific_apps: Ρυθμίσεις ανά εφαρμογή\n    theme: Θέματα\n    virtual_desk: Εικονικοί υπολογιστές γραφείου\n    wallpaper: Ταπετσαρίες\n    widget: Widgets\nhome:\n  new_resources: Νέοι πόροι\ninherit: Κληρονομώ\ninProgress: Σε εξέλιξη...\ninsert: Εισάγω\nloading: Φόρτωση...\nmiscellaneous: Ανάμικτος\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Περισσότερο\n'no': Οχι\nopen: Ανοιξε\nquit: Εγκαταλείπω\nremove: Αφαιρώ\nreset_all_to_default: Επαναφορά όλων στις προεπιλεγμένες τιμές\nreset_to_default: Επαναφορά στην προεπιλεγμένη τιμή\nresources:\n  app_outdated: >-\n    Αυτός ο πόρος απαιτεί μια νεότερη έκδοση του Seelen UI για να λειτουργήσει\n    σωστά.\n  corrupted_wallpaper: >-\n    Αποτυχία εξαγωγής μικρογραφίας - κατεστραμμένη ή μη υποστηριζόμενη μορφή\n    βίντεο\n  delete: Διαγραφή πόρου\n  discover: Ανακαλύψτε περισσότερους πόρους\n  has_update: Μια ενημέρωση είναι διαθέσιμη\n  high_impact: Υψηλό αντίκτυπο στην απόδοση\n  import_wallpapers: Εισαγωγή τοπικών ταπετσαριών\n  open_folder: Άνοιγμα φακέλου πόρων\n  outdated: >-\n    Αυτός ο πόρος σχεδιάστηκε για μια παλαιότερη έκδοση του Seelen UI και\n    ενδέχεται να μην λειτουργεί σωστά.\n  see_on_website: Δείτε στην ιστοσελίδα\nreview_request:\n  not_now: Όχι τώρα\n  sure: Σίγουρος!\n  title: Σας αρέσει το Seelen UI;\nsave: Αποθηκεύσετε\nsave_and_restart: Αποθήκευση & επανεκκίνηση\nsearch: Ερευνα\nsee_more: Δείτε περισσότερα\nshortcuts:\n  duplicate_error: Αυτή η συντόμευση είναι διπλότυπη\n  enable: Ενεργοποίηση ενσωματωμένου συστήματος συντομεύσεων\n  enable_tooltip: >-\n    Απενεργοποιήστε εάν θα εφαρμόσετε το δικό σας σύστημα συντομεύσεων\n    χρησιμοποιώντας τον πελάτη SEELEN UI\n  labels:\n    create_new_workspace: Δημιουργήστε νέο χώρο εργασίας\n    cycle_stack_next: Στοίβα Cycle Next\n    cycle_stack_prev: Στοίβα πριν\n    cycle_wallpaper_next: Αλλαγή στην επόμενη ταπετσαρία\n    cycle_wallpaper_prev: Αλλαγή σε προηγούμενη ταπετσαρία\n    decrease_height: Μείωση του ύψους\n    decrease_width: Μείωση του πλάτους\n    destroy_current_workspace: Καταστρέψτε τον τρέχοντα χώρο εργασίας\n    focus_bottom: Επικεντρωμένος κάτω\n    focus_latest: Επικεντρώνεται το τελευταίο\n    focus_left: Αριστερά\n    focus_right: Εστίαση σωστά\n    focus_top: Επικεντρώνεται στην κορυφή\n    increase_height: Ύψος αύξησης\n    increase_width: Αύξηση πλάτους\n    misc_force_quit: Φρικιασμένη δύναμη\n    misc_force_restart: Αναγκαστική επανεκκίνηση\n    misc_open_settings: Ανοίξτε τις ρυθμίσεις\n    misc_toggle_lock_tracing: Εναλλαγή Lock Tracing (αρχεία καταγραφής)\n    misc_toggle_win_event_tracing: Εναλλαγή εντοπισμού συμβάντων Win (Logs)\n    move_to_workspace: Μετακίνηση στο χώρο εργασίας {{0}}\n    move_window_down: Μετακίνηση του παραθύρου στο κάτω μέρος\n    move_window_left: Μετακίνηση παραθύρου στα αριστερά\n    move_window_right: Μετακίνηση παραθύρου προς τα δεξιά\n    move_window_up: Μετακίνηση του παραθύρου στην κορυφή\n    pause_tiling: Παύση διαχείρισης παραθύρων πλακάκια\n    reserve_bottom: Αποθεματικός\n    reserve_float: Επιφυλάκισμα\n    reserve_left: Αποθεματικό αριστερά\n    reserve_right: Διατηρήστε το δικαίωμα\n    reserve_stack: Επιφυλάκιση\n    reserve_top: Επιφυλάξτε την κορυφή\n    restore_sizes: Επαναφορά μεγέθους\n    send_to_workspace: Αποστολή στο χώρο εργασίας {{0}}\n    start_weg_app: Εστίαση ή εκκίνηση της εφαρμογής {{0}}\n    switch_to_next_workspace: Μεταβείτε στον επόμενο χώρο εργασίας\n    switch_to_previous_workspace: Μεταβείτε σε προηγούμενο χώρο εργασίας\n    switch_workspace: Μεταβείτε στο χώρο εργασίας {{0}}\n    toggle_float: Εναλλαγή παραθύρου Float Mode\n    toggle_monocle: Εναλλαγή λειτουργίας Monocle Workspace\n  readonly_tooltip: Αυτή είναι μια συντόμευση μόνο για ανάγνωση\n  reset: Επαναφορά σε προεπιλογές\nsides:\n  bottom: Κάτω μέρος\n  left: Αριστερά\n  right: σωστά\n  top: Μπλουζα\ntoolbar:\n  auto_hide: Αυτόματη απόκρυψη\n  delay_to_hide: Καθυστέρηση για απόκρυψη\n  delay_to_show: Καθυστέρηση εμφάνισης\n  dock_side: Θέση\n  enable: Ενεργοποίηση φανταχτερά εργαλεία εργαλείων\n  hide_mode:\n    always: Πάντοτε\n    never: Ποτέ\n    on_overlap: Σε επικάλυψη\n  item_size: Μέγεθος αντικειμένου\n  label: Γραμμή εργαλείων\n  margin: Μέγεθος περιθωρίου\n  padding: Μέγεθος padding\n  placeholder: {}\nupdate:\n  available: Διαθέσιμη ενημέρωση!\n  channel: Κανάλι ενημέρωσης\n  downloading: Λήψη ...\nwall:\n  backgrounds: Ταπετσαρίες\n  blur: Θολούρα\n  cancel: Ματαίωση\n  close: Κοντά\n  collection_name: Όνομα συλλογής\n  collections: Συλλογές\n  contrast: Αντίθεση\n  corrupted_wallpapers_message: 'Κατεστραμμένες ταπετσαρίες, σκεφτείτε να τις διαγράψετε:'\n  create: Δημιουργώ\n  create_collection: Δημιουργία συλλογής\n  default_collection: Προεπιλεγμένη συλλογή\n  delete_collection: Διαγραφή συλλογής\n  edit_collection: Επεξεργασία συλλογής\n  enable: Ενεργοποίηση διαχειριστή ταπετσαρίας\n  extend: Επέκταση πρωτοβάθμιας\n  fit:\n    contain: Περιέχει\n    cover: Εξώφυλλο\n    fill: Συμπλήρωση\n  flipHorizontal: Αναδίπλωση οριζόντια\n  flipVertical: Flip κάθετα\n  generating_thumbnails: Δημιουργία μικρογραφιών ταπετσαρίας\n  hours: ώρες\n  interval: Αλλάξτε ταπετσαρία κάθε\n  minutes: πρακτικά\n  monitor_collection: Συλλογή οθονών\n  multimonitor_behaviour: Συμπεριφορά πολλαπλών οθονών\n  muted: Σίγαση ήχου βίντεο\n  no_background: Άδειο slideshow, χρησιμοποιώντας το φόντο του θέματος αντ 'αυτού.\n  no_collections: >-\n    Δεν έχουν δημιουργηθεί ακόμη συλλογές. Κάντε κλικ στο κουμπί + για να\n    δημιουργήσετε ένα.\n  objectFit: Φόντο Fit\n  objectPosition: Ιστορικό Θέση\n  overlayColor: Χρώμα επικάλυψης\n  overlayMixBlendMode: Λειτουργία ανάμειξης μίξης επικάλυψης\n  per_monitor: Ανά οθόνη\n  playback: Ταχύτητα αναπαραγωγής\n  position:\n    bottom: Κάτω μέρος\n    center: Κέντρο\n    left: Αριστερά\n    right: Δεξιά\n    top: Κορυφή\n  processing_video: Επεξεργασία βίντεο\n  random: Τυχαία παρουσίαση\n  saturation: Κορεσμός\n  seconds: δευτερόλεπτα\n  select_collection: Επιλέξτε Συλλογή\n  thumbnail_generation_complete: Ολοκληρώθηκε η δημιουργία μικρογραφιών\n  thumbnail_generation_finished: Η δημιουργία μικρογραφιών ολοκληρώθηκε με επιτυχία\n  wallpaper_collection: Συλλογή ταπετσαριών\n  wallpaper_settings: Ρυθμίσεις ταπετσαρίας\n  withOverlay: Με επικάλυψη\n  workspace_collections: Συλλογές χώρου εργασίας\nweg:\n  auto_hide: Αυτόματη απόκρυψη\n  delay_to_hide: Καθυστέρηση για απόκρυψη\n  delay_to_show: Καθυστέρηση εμφάνισης\n  dock_side: Θέση\n  enable: Ενεργοποίηση λίστα/γραμμή εργασιών\n  filtering: Φιλτράρισμα στοιχείων\n  gap: Χάσμα\n  hide_mode:\n    always: Πάντοτε\n    never: Ποτέ\n    on_overlap: Σε επικάλυψη\n  items:\n    gap: Χώρος μεταξύ αντικειμένων\n    label: Αντικείμενα\n    pinned_visibility:\n      always: Πάντοτε\n      label: Ορατότητα καρφιτσωμένων αντικειμένων\n      when_primary: Όταν η οθόνη είναι κύρια\n    show_instance_counter: Εμφάνιση μετρητή ανοιχτών παραθύρων\n    show_window_title: Εμφάνιση τίτλου ανοιχτού παραθύρου (μόνο οριζόντια)\n    size: Μέγεθος αντικειμένου\n    split_windows: Διαχωρισμός Windows (ένα στοιχείο ανά παράθυρο)\n    temporal_visibility:\n      all: Ολοι\n      label: Ορατότητα ξεκαρφιτσωμένων αντικειμένων\n      on_monitor: Στην οθόνη\n    visible_separators: Ορατοί διαχωριστές\n  label: Λέσχη εργασιών\n  margin: Περιθώριο\n  mode:\n    full_width: Πλάτος πλήρους οθόνης\n    min_content: Όσο μικρό μπορεί να είναι\n  padding: Υλικό παραγεμίσματος\n  show_end_task: Εμφάνιση της εργασίας τέλους στη γραμμή εργασιών\n  width: Πλάτος\nwelcome:\n  give_a_review: Δώστε μια κριτική\n  message: >-\n    Το Seelen UI είναι ένα δωρεάν και ανοιχτού κώδικα περιβάλλον επιφάνειας\n    εργασίας για Windows. Εδώ έχετε τον πλήρη έλεγχο της εμφάνισης και της\n    συμπεριφοράς της επιφάνειας εργασίας σας, επιτρέποντάς σας να προσαρμόσετε\n    κάθε λεπτομέρεια ώστε να ταιριάζει με τη ροή εργασίας και το στυλ σας.\n  ok: Ας ξεκινήσουμε!\n  review: >-\n    Εάν σας αρέσει να χρησιμοποιείτε το Seelen UI, σκεφτείτε να αφήσετε μια\n    κριτική στο κατάστημα — τα σχόλιά σας βοηθούν το έργο να αναπτυχθεί και να\n    βελτιωθεί.\n  title: Καλώς ήρθατε στο Seelen UI!\nwidget:\n  enable: Ενεργοποιήστε αυτό το widget\n  enable_for_monitor: Ενεργοποίηση σε αυτή την οθόνη\n  instances: Περιπτώσεις\nwm:\n  animations:\n    duration: Διάρκεια κινούμενων σχεδίων (MS)\n    ease_function: Λειτουργία χαλάρωσης κινούμενων σχεδίων\n    enable: Ενεργοποιήστε τα κινούμενα σχέδια του παραθύρου\n  author: Συγγραφέας\n  border:\n    enable: Ενεργοποιήστε τα σύνορα του παραθύρου\n    offset: Μετατόπιση των συνόρων\n    width: Πλάτος συνόρων\n  description: Περιγραφή\n  drag_behavior: Σύρετε τη συμπεριφορά\n  drag_behavior_options:\n    sort: Ταξινόμηση (αναδιάταξη των παραθύρων κατά τη μεταφορά)\n    swap: Εναλλαγή (εναλλαγή παραθύρων στο τέλος μεταφοράς)\n  enable: Ενεργοποίηση διαχειριστή παραθύρων πλακιδίων\n  layout: Διάταξη\n  resize_delta: Αλλαγή μεγέθους Delta (%)\n  space_between_containers: Χώρος μεταξύ των εμπορευματοκιβωτίων\n  workspace_offset: Μετατόπιση χώρων εργασίας (περιθώρια)\n  workspace_padding: Πλατύφυλλα\n'yes': Ναί\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/en.yml",
    "content": "action:\n  confirm: Are you sure?\n  confirm_body: This action cannot be undone.\napps_configurations:\n  app:\n    bindings: Binding (note both options are required)\n    category: Category\n    category_placeholder: None\n    monitor: Monitor\n    monitor_placeholder: None\n    name: Name\n    ok_create: Create\n    ok_edit: Update\n    ok_readonly: Edit as New\n    options:\n      NoInteractive: No Interactive\n      VdPinned: Show in all workspaces\n      WmFloat: Twm - Start Floating\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Extra Options\n    title_create: Creating {{name}}\n    title_edit: Editing {{name}}\n    title_readonly: Viewing {{name}}\n    weg_options_label: Dock/Taskbar Options\n    wm_options_label: Window Manager Options\n    workspace: Workspace\n    workspace_placeholder: None\n  bundled_msg: >-\n    These bundled configurations are not editable and are designed to provide\n    you with the best experience without customization. They automatically\n    configure the most common applications for you.\n  bundled_title: App Config Bundled with Seelen\n  confirm_delete: Are you sure you want to delete this configuration/s?\n  confirm_delete_title: Confirm Delete\n  delete: Delete\n  export: Export\n  export_full: Export settings by application\n  extra_info: >-\n    Seelen UI use only one identifier per app (first match found) so the order\n    in how are specificated is important, the latest added will be prioritized,\n    as note the table is sorted by default from latest to old.\n  identifier:\n    add_block: Add Block\n    and: AND\n    id: Identifier\n    kind: Identify By\n    matching_strategy: Matching Strategy\n    matching_strategy_option:\n      contains: Contains\n      ends_with: Ends with\n      equals: Equals\n      regex: Regular expression\n      starts_with: Starts with\n    negation: Negate Matching\n    or: OR\n    remove: Delete Block\n    type:\n      class: Class\n      exe: Exe\n      path: Path\n      title: Title\n  import: Import\n  import_full: Import settings by application\n  new: New\n  search: Search\n  swap: Swap\ncancel: Cancel\nclose: Close\ndelete: Delete\ndevtools:\n  app_folders: App Folders\n  custom_config_file: Load Custom Config File\n  data_folder: Data Folder\n  enable: Enable Developer Tools\n  install_folder: Installation Folder\n  load: Load\n  settings_file: Settings File\n  simulate_perm:\n    label: Simulate Widget Permission Request\n    result_allowed: ✓ Permission granted\n    result_denied: ✗ Permission denied\n    trigger: Simulate\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Clear System Icons Cache\n  clear_icons_tooltip: A restart could be required to fully take effect on all widgets\n  exit: Quit/Exit\n  links: Official Links\n  relaunch: Relaunch\n  version: Version\n  version_fixed: >-\n    The application and WebView2 Runtime versions are fixed. This means that the\n    application will not receive updates and the WebView2 Runtime will not be\n    automatically updated with Windows updates.\ngeneral:\n  accent_color: Accent color\n  date_format: Date format\n  date_format_how_to: How to write a date format?\n  hardware_acceleration: Hardware acceleration\n  hardware_acceleration_description: >-\n    Disabling hardware acceleration will reduce memory usage, but can cause\n    performance issues. Is safe to disable if you won't use live wallpapers.\n  icon_pack:\n    available: Available Icon Packs\n    selected: Active Icon Packs\n  language: Language\n  monday: Monday\n  performance_mode:\n    on_battery: On battery\n    on_energy_saver: On energy saver\n    options:\n      disabled: Disabled\n      extreme: Extreme\n      minimal: Minimal\n    plugged: Plugged or charging\n  polling_interval: System polling interval\n  polling_interval_description: >-\n    How often (in seconds) Seelen UI checks system resources like CPU, RAM,\n    network, and disk activity. A smaller number means more frequent updates,\n    but slightly higher resource usage.\n  saturday: Saturday\n  start_of_week: Start of week\n  startup: Run on startup?\n  sunday: Sunday\n  theme:\n    available: Available Themes\n    selected: Active Themes\nheader:\n  labels:\n    config: Configurations\n    developer: For developers\n    extras: Extras\n    general: General\n    home: Home\n    icon_pack_editor: Cached Icons\n    iconpack: Icon Packs\n    monitors: Monitors\n    plugin: Plugins\n    resources: Resources\n    shortcuts: Shortcuts\n    soundpack: Sound Packs\n    specific_apps: Settings by Application\n    theme: Themes\n    virtual_desk: Virtual Desktops\n    wallpaper: Wallpapers\n    widget: Widgets\nhome:\n  new_resources: New Resources\ninherit: Inherit\ninProgress: In Progress...\ninsert: Insert\nloading: Loading...\nmiscellaneous: Miscellaneous\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: More\n'no': 'No'\nopen: Open\nquit: Quit\nremove: Remove\nreset_all_to_default: Reset all to default values\nreset_to_default: Reset to default value\nresources:\n  app_outdated: This resource requires a newer version of Seelen UI to work properly.\n  corrupted_wallpaper: Failed to extract thumbnail - corrupted or unsupported video format\n  delete: Delete Resource\n  discover: Discover more resources\n  has_update: An update is available\n  high_impact: High impact on performance\n  import_wallpapers: Import local wallpapers\n  open_folder: Open resource folder\n  outdated: >-\n    This resource was designed for an older version of Seelen UI and might not\n    work properly.\n  see_on_website: See on website\nreview_request:\n  not_now: Not now\n  sure: Sure!\n  title: Enjoying Seelen UI?\nsave: Save\nsave_and_restart: Save & Restart\nsearch: Search\nsee_more: See More\nshortcuts:\n  duplicate_error: This shortcut is duplicated\n  enable: Enable built-in shortcuts system\n  enable_tooltip: >-\n    Disable if you will implement your own shortcuts system using the Seelen UI\n    Client\n  labels:\n    create_new_workspace: Create new Workspace\n    cycle_stack_next: Cycle Stack Next\n    cycle_stack_prev: Cycle Stack Previous\n    cycle_wallpaper_next: Change to Next Wallpaper\n    cycle_wallpaper_prev: Change to Previous Wallpaper\n    decrease_height: Decrease Height\n    decrease_width: Decrease Width\n    destroy_current_workspace: Destroy Current Workspace\n    focus_bottom: Focus Bottom\n    focus_latest: Focus Latest\n    focus_left: Focus Left\n    focus_right: Focus Right\n    focus_top: Focus Top\n    increase_height: Increase Height\n    increase_width: Increase Width\n    misc_force_quit: Force Quit\n    misc_force_restart: Force Restart\n    misc_open_settings: Open Settings\n    misc_toggle_lock_tracing: Toggle Lock Tracing (logs)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (logs)\n    move_to_workspace: Move to Workspace {{0}}\n    move_window_down: Move Window to Bottom\n    move_window_left: Move Window to Left\n    move_window_right: Move Window to Right\n    move_window_up: Move Window to Top\n    pause_tiling: Pause Tiling Window Manager\n    reserve_bottom: Reserve Bottom\n    reserve_float: Reserve Float\n    reserve_left: Reserve Left\n    reserve_right: Reserve Right\n    reserve_stack: Reserve Stack\n    reserve_top: Reserve Top\n    restore_sizes: Restore Sizes\n    send_to_workspace: Send to Workspace {{0}}\n    start_weg_app: Focus or Start Application {{0}}\n    switch_to_next_workspace: Switch to Next Workspace\n    switch_to_previous_workspace: Switch to Previous Workspace\n    switch_workspace: Switch to Workspace {{0}}\n    toggle_float: Toggle Window Float Mode\n    toggle_monocle: Toggle Workspace Monocle Mode\n  readonly_tooltip: This is a read-only shortcut\n  reset: Reset to Defaults\nsides:\n  bottom: Bottom\n  left: Left\n  right: Right\n  top: Top\ntoolbar:\n  auto_hide: Auto Hide\n  delay_to_hide: Delay to hide\n  delay_to_show: Delay to show\n  dock_side: Position\n  enable: Enable Fancy Toolbar\n  hide_mode:\n    always: Always\n    never: Never\n    on_overlap: On overlap\n  item_size: Item Size\n  label: Toolbar\n  margin: Margin Size\n  padding: Padding Size\n  placeholder: {}\nupdate:\n  available: Update Available!\n  channel: Update Channel\n  downloading: Downloading...\nwall:\n  backgrounds: Wallpapers\n  blur: Blur\n  cancel: Cancel\n  close: Close\n  collection_name: Collection Name\n  collections: Collections\n  contrast: Contrast\n  corrupted_wallpapers_message: 'Corrupted wallpapers, consider deleting these:'\n  create: Create\n  create_collection: Create Collection\n  default_collection: Default Collection\n  delete_collection: Delete Collection\n  edit_collection: Edit Collection\n  enable: Enable Wallpaper Manager\n  extend: Extend primary\n  fit:\n    contain: Contain\n    cover: Cover\n    fill: Fill\n  flipHorizontal: Flip Horizontal\n  flipVertical: Flip Vertical\n  generating_thumbnails: Generating Wallpaper Thumbnails\n  hours: hours\n  interval: Change wallpaper every\n  minutes: minutes\n  monitor_collection: Monitor Collection\n  multimonitor_behaviour: Multimonitor Behaviour\n  muted: Mute video audio\n  no_background: Empty slideshow, using theme's background instead.\n  no_collections: No collections created yet. Click the + button to create one.\n  objectFit: Background Fit\n  objectPosition: Background Position\n  overlayColor: Overlay Color\n  overlayMixBlendMode: Overlay Mix Blend Mode\n  per_monitor: Per monitor\n  playback: Playback speed\n  position:\n    bottom: Bottom\n    center: Center\n    left: Left\n    right: Right\n    top: Top\n  processing_video: Processing video\n  random: Randomize slideshow\n  saturation: Saturation\n  seconds: seconds\n  select_collection: Select Collection\n  thumbnail_generation_complete: Thumbnail Generation Complete\n  thumbnail_generation_finished: Thumbnail generation has finished successfully\n  wallpaper_collection: Wallpaper Collection\n  wallpaper_settings: Wallpaper Settings\n  withOverlay: With Overlay\n  workspace_collections: Workspace Collections\nweg:\n  auto_hide: Auto Hide\n  delay_to_hide: Delay to hide\n  delay_to_show: Delay to show\n  dock_side: Position\n  enable: Enable Dock/Taskbar\n  filtering: Item Filtering\n  gap: Gap\n  hide_mode:\n    always: Always\n    never: Never\n    on_overlap: On overlap\n  items:\n    gap: Space Between Items\n    label: Items\n    pinned_visibility:\n      always: Always\n      label: Pinned Items Visibility\n      when_primary: When the monitor is primary\n    show_instance_counter: Show open windows counter\n    show_window_title: Show open window title (only horizontal)\n    size: Item Size\n    split_windows: Split Windows (one item per window)\n    temporal_visibility:\n      all: All\n      label: Unpinned Items Visibility\n      on_monitor: On Monitor\n    visible_separators: Visible Separators\n  label: Dock/Taskbar\n  margin: Margin\n  mode:\n    full_width: Full screen width\n    min_content: Small as can be\n  padding: Padding\n  show_end_task: Show end task in taskbar\n  width: Width\nwelcome:\n  give_a_review: Give a Review\n  message: >-\n    Seelen UI is a free and open source desktop environment for Windows. Here\n    you have full control over how your desktop looks and behaves, allowing you\n    to customize every detail to match your workflow and style.\n  ok: Let's start!\n  review: >-\n    If you enjoy using Seelen UI, consider leaving a review on the store — your\n    feedback helps the project grow and improve.\n  title: Welcome to Seelen UI!\nwidget:\n  enable: Enable this widget\n  enable_for_monitor: Enable on this monitor\n  instances: Instances\nwm:\n  animations:\n    duration: Animation Duration (ms)\n    ease_function: Animation Easing Function\n    enable: Enable Window's Animations\n  author: Author\n  border:\n    enable: Enable Window's Border\n    offset: Border Offset\n    width: Border Width\n  description: Description\n  drag_behavior: Drag Behavior\n  drag_behavior_options:\n    sort: Sort (reorder windows while dragging)\n    swap: Swap (swap windows on drag end)\n  enable: Enable Tiling Window Manager\n  layout: Layout\n  resize_delta: Resize Delta (%)\n  space_between_containers: Space Between Containers\n  workspace_offset: Workspaces Offset (Margins)\n  workspace_padding: Workspaces Padding\n'yes': 'Yes'\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/es.yml",
    "content": "action:\n  confirm: ¿Está seguro?\n  confirm_body: Esta acción no se puede deshacer.\napps_configurations:\n  app:\n    bindings: Encuadernación (ambas opciones son obligatorias)\n    category: Categoría\n    category_placeholder: Ninguno\n    monitor: Monitor\n    monitor_placeholder: Ninguno\n    name: Nombre\n    ok_create: Cree\n    ok_edit: Actualización\n    ok_readonly: Editar como nuevo\n    options:\n      NoInteractive: Sin interactivo\n      VdPinned: Mostrar en todos los espacios de trabajo\n      WmFloat: Twm - Empezar a flotar\n      WmForce: Twm - Gestión de fuerza\n      WmUnmanage: Twm - No administrar\n    options_label: Opciones adicionales\n    title_create: Crear {{nombre}}\n    title_edit: Editar {{nombre}}\n    title_readonly: Ver {{nombre}}\n    weg_options_label: Opciones del muelle/barra de tareas\n    wm_options_label: Opciones del gestor de ventanas\n    workspace: Espacio de trabajo\n    workspace_placeholder: Ninguno\n  bundled_msg: >-\n    Estas configuraciones no son editables y están diseñadas para ofrecerle la\n    mejor experiencia sin necesidad de personalización. Configuran\n    automáticamente las aplicaciones más comunes.\n  bundled_title: App Config incluido con Seelen\n  confirm_delete: ¿Está seguro de que desea eliminar esta configuración?\n  confirm_delete_title: Confirmar Borrar\n  delete: Borrar\n  export: Exportar\n  export_full: Exportar ajustes por aplicación\n  extra_info: >-\n    Seelen UI utiliza sólo un identificador por aplicación (primera coincidencia\n    encontrada) por lo que el orden en que se especifican es importante, el\n    último añadido tendrá prioridad, como nota la tabla se ordena por defecto de\n    más reciente a más antiguo.\n  identifier:\n    add_block: Añadir bloque\n    and: 'Y'\n    id: Identificador\n    kind: Identificar por\n    matching_strategy: Estrategia de emparejamiento\n    matching_strategy_option:\n      contains: Contiene\n      ends_with: Termina con\n      equals: Es igual a\n      regex: Expresión regular\n      starts_with: Empieza por\n    negation: Negar coincidencia\n    or: O\n    remove: Borrar bloque\n    type:\n      class: Clase\n      exe: Exe\n      path: Ruta\n      title: Título\n  import: Importar\n  import_full: Importar ajustes por aplicación\n  new: Nuevo\n  search: Buscar en\n  swap: Intercambiar\ncancel: Cancelar\nclose: Cerrar\ndelete: Borrar\ndevtools:\n  app_folders: Carpetas de aplicaciones\n  custom_config_file: Cargar archivo de configuración personalizado\n  data_folder: Carpeta de datos\n  enable: Activar las herramientas para desarrolladores\n  install_folder: Carpeta de instalación\n  load: Carga\n  settings_file: Archivo de configuración\n  simulate_perm:\n    label: Simular solicitud de permiso de widget\n    result_allowed: ✓ Permiso concedido\n    result_denied: ✗ Permiso denegado\n    trigger: Simular\n    widget_id_placeholder: '@autor/nombre-widget'\nextras:\n  clear_icons: Borrar caché de iconos del sistema\n  clear_icons_tooltip: >-\n    Podría ser necesario reiniciar el sistema para que surta efecto en todos los\n    widgets.\n  exit: Salir\n  links: Enlaces oficiales\n  relaunch: Relanzamiento\n  version: Versión\n  version_fixed: >-\n    Las versiones de la aplicación y WebView2 Runtime son fijas. Esto significa\n    que la aplicación no recibirá actualizaciones y WebView2 Runtime no se\n    actualizará automáticamente con las actualizaciones de Windows.\ngeneral:\n  accent_color: Color de acento\n  date_format: Formato de fecha\n  date_format_how_to: ¿Cómo escribir un formato de fecha?\n  hardware_acceleration: Aceleración de hardware\n  hardware_acceleration_description: >-\n    Deshabilitar la aceleración de hardware reducirá el uso de memoria, pero\n    puede causar problemas de rendimiento. Es seguro desactivarlo si no vas a\n    utilizar fondos de pantalla animados.\n  icon_pack:\n    available: Paquetes de iconos disponibles\n    selected: Packs de iconos activos\n  language: Idioma\n  monday: Lunes\n  performance_mode:\n    on_battery: Con batería\n    on_energy_saver: Save de energía\n    options:\n      disabled: Desactivado\n      extreme: Extremo\n      minimal: Mínimo\n    plugged: Enchufado o carga\n  polling_interval: Intervalo de sondeo del sistema\n  polling_interval_description: >-\n    Con qué frecuencia (en segundos) Seelen UI verifica los recursos del sistema\n    como la CPU, la RAM, la red y la actividad del disco. Un número menor\n    significa actualizaciones más frecuentes, pero un uso de recursos\n    ligeramente mayor.\n  saturday: Sábado\n  start_of_week: Inicio de semana\n  startup: ¿Se ejecuta al iniciar con Windows?\n  sunday: Domingo\n  theme:\n    available: Temas disponibles\n    selected: Temas activos\nheader:\n  labels:\n    config: Configuraciones\n    developer: Para desarrolladores\n    extras: Extras\n    general: General\n    home: Inicio\n    icon_pack_editor: Iconos en caché\n    iconpack: Paquetes de iconos\n    monitors: Monitores\n    plugin: Plugins\n    resources: Recursos\n    shortcuts: Atajos\n    soundpack: Paquetes de sonido\n    specific_apps: Ajustes por aplicación\n    theme: Temas\n    virtual_desk: Escritorios virtuales\n    wallpaper: Fondos de pantalla\n    widget: Widgets\nhome:\n  new_resources: Nuevos recursos\ninherit: Heredar\ninProgress: En curso...\ninsert: Inserte\nloading: Cargando...\nmiscellaneous: Varios\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Más\n'no': 'No'\nopen: Abrir\nquit: Deje de\nremove: Eliminar\nreset_all_to_default: Restablecer todos los valores por defecto\nreset_to_default: Restablecer valor por defecto\nresources:\n  app_outdated: >-\n    Este recurso requiere una versión más reciente de Seelen UI para funcionar\n    correctamente.\n  corrupted_wallpaper: 'No se pudo extraer la miniatura: formato de video dañado o no compatible'\n  delete: Eliminar recursos\n  discover: Descubra más recursos\n  has_update: Una actualización está disponible\n  high_impact: Alto impacto en el rendimiento\n  import_wallpapers: Importar fondos de pantalla locales\n  open_folder: Abrir carpeta de recursos\n  outdated: >-\n    Este recurso fue diseñado para una versión anterior de Seelen UI y podría no\n    funcionar correctamente.\n  see_on_website: Ver en el sitio web\nreview_request:\n  not_now: Ahora no\n  sure: ¡Seguro!\n  title: ¿Disfrutas de la interfaz de usuario de Seelen?\nsave: Guardar\nsave_and_restart: Guardar y reiniciar\nsearch: Buscar\nsee_more: Ver más\nshortcuts:\n  duplicate_error: Este atajo está duplicado.\n  enable: Habilitar el sistema de accesos directos integrados\n  enable_tooltip: >-\n    Deshabilite si implementa su propio sistema de atajos utilizando el cliente\n    Seelen UI\n  labels:\n    create_new_workspace: Crea un nuevo espacio de trabajo\n    cycle_stack_next: Pila de ciclo a continuación\n    cycle_stack_prev: Pila de ciclo anterior\n    cycle_wallpaper_next: Cambiar al siguiente fondo de pantalla\n    cycle_wallpaper_prev: Cambio a papel tapiz anterior\n    decrease_height: Disminuir la altura\n    decrease_width: Disminuir el ancho\n    destroy_current_workspace: Destruir el espacio de trabajo actual\n    focus_bottom: Fondo de enfoque\n    focus_latest: Focus Último\n    focus_left: Se enfoca a la izquierda\n    focus_right: Concentrarse correctamente\n    focus_top: Top de enfoque\n    increase_height: Aumentar la altura\n    increase_width: Aumento de ancho\n    misc_force_quit: Fuerza deja de salir\n    misc_force_restart: Forzar reinicio\n    misc_open_settings: Abrir configuración\n    misc_toggle_lock_tracing: Rastreo de bloqueo de al revés (registros)\n    misc_toggle_win_event_tracing: Rastreo de eventos para alternar (registros)\n    move_to_workspace: Mover al espacio de trabajo {{0}}\n    move_window_down: Mueva la ventana a la parte inferior\n    move_window_left: Mover la ventana a la izquierda\n    move_window_right: Mover la ventana a la derecha\n    move_window_up: Mover la ventana hacia arriba\n    pause_tiling: PAUSE Manager de ventana de mosaico\n    reserve_bottom: Parte inferior de reserva\n    reserve_float: Reservar flotar\n    reserve_left: Reserva a la izquierda\n    reserve_right: Reservar a la derecha\n    reserve_stack: Reserva\n    reserve_top: Top de reserva\n    restore_sizes: Tamaños de restauración\n    send_to_workspace: Enviar al espacio de trabajo {{0}}\n    start_weg_app: Aplicación de enfoque o inicio {{0}}\n    switch_to_next_workspace: Cambiar al siguiente espacio de trabajo\n    switch_to_previous_workspace: Cambiar al espacio de trabajo anterior\n    switch_workspace: Cambiar al espacio de trabajo {{0}}\n    toggle_float: Modo de flotación de la ventana de alternativa\n    toggle_monocle: Modo de monocle del espacio de trabajo de al revés\n  readonly_tooltip: Este es un atajo de solo lectura\n  reset: Reiniciar a los valores predeterminados\nsides:\n  bottom: Fondo\n  left: Izquierda\n  right: Derecha\n  top: Top\ntoolbar:\n  auto_hide: Auto Ocultar\n  delay_to_hide: Retrasar para ocultar\n  delay_to_show: Retraso para mostrar\n  dock_side: Posición\n  enable: Activar la barra de herramientas Fancy\n  hide_mode:\n    always: Siempre\n    never: Nunca\n    on_overlap: Sobre el solapamiento\n  item_size: Tamaño del artículo\n  label: Barra de herramientas\n  margin: Tamaño del margen\n  padding: Tamaño del relleno\n  placeholder: {}\nupdate:\n  available: Actualización disponible\n  channel: Canal de actualización\n  downloading: Descargando...\nwall:\n  backgrounds: Fondos de pantalla\n  blur: Desenfoque\n  cancel: Cancelar\n  close: Cerca\n  collection_name: Nombre de la colección\n  collections: Colecciones\n  contrast: Contraste\n  corrupted_wallpapers_message: 'Fondos de pantalla corruptos, considere eliminar estos:'\n  create: Crear\n  create_collection: Crear colección\n  default_collection: Colección predeterminada\n  delete_collection: Eliminar colección\n  edit_collection: Editar colección\n  enable: Activar el gestor de fondos de pantalla\n  extend: Ampliar primaria\n  fit:\n    contain: Contiene\n    cover: Portada\n    fill: Rellene\n  flipHorizontal: Voltear horizontal\n  flipVertical: Voltear vertical\n  generating_thumbnails: Generando miniaturas de fondos de pantalla\n  hours: horas\n  interval: Cambia el fondo de pantalla cada\n  minutes: minutos\n  monitor_collection: Colección de monitores\n  multimonitor_behaviour: Comportamiento multimonitor\n  muted: Silenciar audio de vídeo\n  no_background: Presentación de fondos vacía, utilizando el fondo del tema en su lugar.\n  no_collections: Aún no se han creado colecciones. Haga clic en el botón + para crear uno.\n  objectFit: Ajuste de fondo\n  objectPosition: Antecedentes\n  overlayColor: Color de superposición\n  overlayMixBlendMode: Superposición Mezcla Modo de fusión\n  per_monitor: Por monitor\n  playback: Velocidad de reproducción\n  position:\n    bottom: Fondo\n    center: Centro\n    left: Izquierda\n    right: Derecha\n    top: Top\n  processing_video: Procesando vídeo\n  random: Cambio de fondo aleatorio\n  saturation: Saturación\n  seconds: segundos\n  select_collection: Seleccionar colección\n  thumbnail_generation_complete: Generación de miniaturas completa\n  thumbnail_generation_finished: La generación de miniaturas ha finalizado correctamente.\n  wallpaper_collection: Colección de papel tapiz\n  wallpaper_settings: Configuración del fondo de pantalla\n  withOverlay: Con superposición\n  workspace_collections: Colecciones de espacios de trabajo\nweg:\n  auto_hide: Auto Ocultar\n  delay_to_hide: Retrasar para ocultar\n  delay_to_show: Retraso para mostrar\n  dock_side: Posición\n  enable: Activar Dock/Barra de tareas\n  filtering: Filtrado de artículos\n  gap: Brecha\n  hide_mode:\n    always: Siempre\n    never: Nunca\n    on_overlap: Sobre el solapamiento\n  items:\n    gap: Espacio entre elementos\n    label: Elementos\n    pinned_visibility:\n      always: Siempre\n      label: Visibilidad de los elementos anclados\n      when_primary: Cuando el monitor es primario\n    show_instance_counter: Mostrar contador de ventanas abiertas\n    show_window_title: Mostrar el título de la ventana abierta (sólo horizontal)\n    size: Tamaño del elemento\n    split_windows: Ventanas divididas (un elemento por ventana)\n    temporal_visibility:\n      all: Todos\n      label: Visibilidad de los elementos no fijados\n      on_monitor: En monitor\n    visible_separators: Separadores visibles\n  label: Muelle/Barra de Muelle\n  margin: Margen\n  mode:\n    full_width: Ancho de pantalla completo\n    min_content: Más pequeño imposible\n  padding: Acolchado\n  show_end_task: Mostrar la tarea final en la barra de tareas\n  width: Anchura\nwelcome:\n  give_a_review: Dar una reseña\n  message: >-\n    Seelen UI es un entorno de escritorio gratuito y de código abierto para\n    Windows. Aquí tienes control total sobre el aspecto y el comportamiento de\n    tu escritorio, lo que te permite personalizar cada detalle para que coincida\n    con tu flujo de trabajo y estilo.\n  ok: ¡Empecemos!\n  review: >-\n    Si le gusta usar Seelen UI, considere dejar una reseña en la tienda; sus\n    comentarios ayudan a que el proyecto crezca y mejore.\n  title: ¡Bienvenido a la interfaz de usuario de Seelen!\nwidget:\n  enable: Activar este widget\n  enable_for_monitor: Activar en este monitor\n  instances: Instancias\nwm:\n  animations:\n    duration: Duración de animación (MS)\n    ease_function: Función de flexión de animación\n    enable: Habilitar animaciones de Window\n  author: Autor\n  border:\n    enable: Activar el borde de la ventana\n    offset: Desplazamiento del borde\n    width: Anchura del borde\n  description: Descripción\n  drag_behavior: Comportamiento de arrastre\n  drag_behavior_options:\n    sort: Ordenar (reordenar ventanas mientras arrastra)\n    swap: Intercambiar (intercambiar ventanas al final del arrastre)\n  enable: Activar el gestor de ventanas en mosaico\n  layout: Diseño\n  resize_delta: Redimensionar Delta (%)\n  space_between_containers: Espacio entre contenedores\n  workspace_offset: Desplazamiento de los espacios de trabajo (márgenes)\n  workspace_padding: Espacios de trabajo Acolchado\n'yes': Sí\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/et.yml",
    "content": "action:\n  confirm: Kas olete kindel?\n  confirm_body: Seda toimingut ei saa tagasi võtta.\napps_configurations:\n  app:\n    bindings: Sidumine (pange tähele mõlemat suvandit)\n    category: Kategooria\n    category_placeholder: Mitte ükski\n    monitor: Ekraan\n    monitor_placeholder: Mitte ükski\n    name: Nimetus\n    ok_create: Looma\n    ok_edit: Värskendama\n    ok_readonly: Redigeerimine uuena\n    options:\n      NoInteractive: Pole interaktiivset\n      VdPinned: Kuva kõikides tööruumides\n      WmFloat: Twm – alusta ujumist\n      WmForce: Twm – jõu haldamine\n      WmUnmanage: Twm – haldamata\n    options_label: Lisavalikud\n    title_create: '{{Name}} loomine'\n    title_edit: Redigeerimine {{nimi}}\n    title_readonly: Vaatamine {{nimi}}\n    weg_options_label: Dokk/ülesanderiba valikud\n    wm_options_label: Aknahalduri valikud\n    workspace: Tööruum\n    workspace_placeholder: Mitte ükski\n  bundled_msg: >-\n    Need komplekteeritud konfiguratsioonid ei ole redigeeritavad ja on loodud\n    selleks, et pakkuda teile parimat kogemust ilma kohandamiseta. Nad\n    konfigureerivad teie jaoks automaatselt kõige levinumaid rakendusi.\n  bundled_title: Rakenduse konfiguratsioon, mis on komplekteeritud Seeleniga\n  confirm_delete: Kas olete kindel, et soovite selle konfiguratsiooni/S kustutada?\n  confirm_delete_title: Kinnitage kustutamine\n  delete: Kustutama\n  export: Ekspordi-\n  export_full: Ekspordi seaded rakenduste kaupa\n  extra_info: >-\n    Seeleni kasutajaliides kasutab rakenduse kohta ainult ühte identifikaatorit\n    (leiti esimene vaste), seega on määramise järjekord oluline, viimati lisatud\n    on prioriteediks, kuna tabel sorteeritakse vaikimisi uusimast vanani.\n  identifier:\n    add_block: Lisa plokk\n    and: Ja\n    id: Identifikaator\n    kind: Tuvastama\n    matching_strategy: Sobiv strateegia\n    matching_strategy_option:\n      contains: Sisaldab\n      ends_with: Lõpeb koos\n      equals: Võrdub\n      regex: Regulaaravaldis\n      starts_with: Alustab sellest\n    negation: Eitav sobitamine\n    or: Või\n    remove: Kustutamisplokk\n    type:\n      class: Klass\n      exe: Exe\n      path: Tee\n      title: Pealkiri\n  import: Import\n  import_full: Impordi seaded rakenduste kaupa\n  new: Uus\n  search: Otsing\n  swap: Vahetama\ncancel: Tühistama\nclose: Sulgur\ndelete: Kustutama\ndevtools:\n  app_folders: Rakenduse kaustad\n  custom_config_file: Laadige kohandatud konfiguratsioonifail\n  data_folder: Andmekaust\n  enable: Luba arendaja tööriistad\n  install_folder: Installi kaust\n  load: Laadima\n  settings_file: Seadete fail\n  simulate_perm:\n    label: Simuleeri vidina loa taotlust\n    result_allowed: ✓ Luba antud\n    result_denied: ✗ Luba ei antud\n    trigger: Simuleerida\n    widget_id_placeholder: '@autor/vidina nimi'\nextras:\n  clear_icons: Tühjenda süsteemi ikoonide vahemälu\n  clear_icons_tooltip: Kõigi vidinate täielikuks jõustumiseks võib olla vajalik taaskäivitamine.\n  exit: Lõpeta/väljumine\n  links: Ametlikud lingid\n  relaunch: Taaskäivitama\n  version: Versioon\n  version_fixed: >-\n    Rakenduse ja WebView2 Runtime versioonid on fikseeritud. See tähendab, et\n    rakendus ei saa värskendusi ja WebView2 Runtime ei värskendata automaatselt\n    Windowsi värskendustega.\ngeneral:\n  accent_color: Aktsentvärv\n  date_format: Kuupäeva vorming\n  date_format_how_to: Kuidas kirjutada kuupäevavormingut?\n  hardware_acceleration: Riistvaraline kiirendus\n  hardware_acceleration_description: >-\n    Riistvaralise kiirenduse keelamine vähendab mälukasutust, kuid võib\n    põhjustada jõudlusprobleeme. Seda on ohutu keelata, kui te ei kasuta\n    reaalajas taustapilte.\n  icon_pack:\n    available: Saadaval ikoonipaketid\n    selected: Aktiivsed ikoonipaketid\n  language: Keel\n  monday: esmaspäev\n  performance_mode:\n    on_battery: Akul\n    on_energy_saver: Energiasäästjal\n    options:\n      disabled: Puudega\n      extreme: Äärmuslik\n      minimal: Minimaalne\n    plugged: Ühendatud või laadimine\n  polling_interval: Süsteemi küsitlusintervall\n  polling_interval_description: >-\n    Kui sageli (sekundites) Seelen UI kontrollib süsteemiressursse, nagu\n    protsessor, RAM, võrk ja ketta aktiivsus. Väiksem arv tähendab sagedasemaid\n    uuendusi, kuid veidi suuremat ressursikasutust.\n  saturday: laupäeval\n  start_of_week: Nädala algus\n  startup: Kas joosta startup?\n  sunday: pühapäev\n  theme:\n    available: Saadaolevad teemad\n    selected: Aktiivsed teemad\nheader:\n  labels:\n    config: Konfiguratsioonid\n    developer: Arendajatele\n    extras: Lisad\n    general: Üld-\n    home: Kodu\n    icon_pack_editor: Vaikimisi salvestatud ikoonid\n    iconpack: Ikoonipaketid\n    monitors: Monitorid\n    plugin: Plugins\n    resources: Ressursid\n    shortcuts: Otseteed\n    soundpack: Helipaketid\n    specific_apps: Seadistused rakenduse järgi\n    theme: Teemad\n    virtual_desk: Virtuaalsed töölauad\n    wallpaper: Taustapildid\n    widget: Vidinad\nhome:\n  new_resources: Uued ressursid\ninherit: Pärima\ninProgress: Pooleli ...\ninsert: Sisestama\nloading: Laadimine ...\nmiscellaneous: Mitmesuguseid\nmonitors_configurations:\n  label: Monitor {{indeks}}\nmore: Rohkem\n'no': Mitte\nopen: Avatud\nquit: Loobuma\nremove: Eemaldama\nreset_all_to_default: Nullida kõik vaikeväärtused\nreset_to_default: Nullida vaikimisi väärtus\nresources:\n  app_outdated: Selle ressursi korralikuks kasutamiseks on vaja Seelen UI uuemat versiooni.\n  corrupted_wallpaper: >-\n    Pisipildi ekstraktimine ebaõnnestus – videovorming on rikutud või seda ei\n    toetata\n  delete: Ressurss kustutama\n  discover: Avasta rohkem ressursse\n  has_update: Värskendus on saadaval\n  high_impact: Suur mõju jõudlusele\n  import_wallpapers: Kohalike taustapiltide importimine\n  open_folder: Avage ressursside kaust\n  outdated: >-\n    See ressurss on loodud Seelen UI vanema versiooni jaoks ja see ei pruugi\n    korralikult töötada.\n  see_on_website: Vaata kodulehelt\nreview_request:\n  not_now: Mitte praegu\n  sure: Muidugi!\n  title: Kas teile meeldib Seeleni kasutajaliides?\nsave: Kokkuhoid\nsave_and_restart: Salvesta ja taaskäivitage\nsearch: Otsi\nsee_more: Vaata lähemalt\nshortcuts:\n  duplicate_error: See otsetee on dubleeritud\n  enable: Luba sisseehitatud otseteede süsteem\n  enable_tooltip: >-\n    Keelake, kui rakendate oma otseteede süsteemi, kasutades seeneni\n    kasutajaliidese klienti\n  labels:\n    create_new_workspace: Looge uus tööruum\n    cycle_stack_next: Tsükli virn järgmisena\n    cycle_stack_prev: Tsükli virna eelmine\n    cycle_wallpaper_next: Muutke järgmisele tapeedile\n    cycle_wallpaper_prev: Muutke eelmisele tapeedile\n    decrease_height: Vähendamine kõrgus\n    decrease_width: Languslaius\n    destroy_current_workspace: Hävitada praegune tööruum\n    focus_bottom: Fookuse põhi\n    focus_latest: Keskenduda uusim\n    focus_left: Fookus vasakule\n    focus_right: Fookuse õigus\n    focus_top: Fookus ülaosa\n    increase_height: Suurendage kõrgust\n    increase_width: Suurendama laiust\n    misc_force_quit: Sund lõpetama\n    misc_force_restart: Taaskäivituse sundimine\n    misc_open_settings: Avatud sätted\n    misc_toggle_lock_tracing: Lülitage lukustusjälg (logid)\n    misc_toggle_win_event_tracing: Lülitage võidu sündmuste jälgimine (logid)\n    move_to_workspace: Liikuge tööruumi {{0}} saidile\n    move_window_down: Liigutage aken põhja\n    move_window_left: Liigutage aken vasakule\n    move_window_right: Liigutage aken paremale\n    move_window_up: Liigutage aken tippu\n    pause_tiling: Pausi plaatide aknahaldur\n    reserve_bottom: Reservpõhja\n    reserve_float: Reservvajutus\n    reserve_left: Reserv vasak\n    reserve_right: Reservõigus\n    reserve_stack: Reservvirn\n    reserve_top: Reservi ülaosa\n    restore_sizes: Taastada suurused\n    send_to_workspace: Saada tööruumi {{0}}\n    start_weg_app: Keskenduge või käivitage rakendus {{0}}\n    switch_to_next_workspace: Lülitage järgmisele tööalale\n    switch_to_previous_workspace: Lülitage eelmisele tööalale\n    switch_workspace: Lülitage tööruumi {{0}}\n    toggle_float: Lülitage akna ujukrežiim\n    toggle_monocle: Tööruumi monokli režiimi lülitamine\n  readonly_tooltip: See on kirjutuskaitstud otsetee\n  reset: Lähtestage vaikeseadetele\nsides:\n  bottom: Alumine\n  left: Vasakul\n  right: Paremale\n  top: Tipus\ntoolbar:\n  auto_hide: Automaatne peida\n  delay_to_hide: Viivitage peitmine\n  delay_to_show: Näitamisega viivitus\n  dock_side: Positsioon\n  enable: Luba väljamõeldud tööriistariba\n  hide_mode:\n    always: Alati\n    never: Mitte kunagi\n    on_overlap: Kattumises\n  item_size: Kauba suurus\n  label: Tööriistariba\n  margin: Veerise suurus\n  padding: Polsterduse suurus\n  placeholder: {}\nupdate:\n  available: Värskendage saadaval!\n  channel: Värskenduskanal\n  downloading: Allalaadimine ...\nwall:\n  backgrounds: Taustapildid\n  blur: Blur\n  cancel: Tühista\n  close: Sule\n  collection_name: Kollektsiooni nimi\n  collections: Kollektsioonid\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Rikutud taustapildid, kaaluge nende kustutamist:'\n  create: Loo\n  create_collection: Loo kollektsioon\n  default_collection: Vaikekogu\n  delete_collection: Kustuta kogu\n  edit_collection: Redigeeri kogu\n  enable: Luba tapeedihaldur\n  extend: Laiendage esmast\n  fit:\n    contain: Sisaldab\n    cover: Kaane\n    fill: Täitke\n  flipHorizontal: Flip Horisontaalne\n  flipVertical: Flip vertikaalne\n  generating_thumbnails: Taustapildi pisipiltide loomine\n  hours: tööaeg\n  interval: Vahetage tapeeti iga\n  minutes: protokoll\n  monitor_collection: Monitori kollektsioon\n  multimonitor_behaviour: Mitme monitori käitumine\n  muted: Video heli vaigistamine\n  no_background: Tühi slaidiseanss, kasutades selle asemel teema tausta.\n  no_collections: Ühtegi kogu pole veel loodud. Selle loomiseks klõpsake nuppu +.\n  objectFit: Taust sobivus\n  objectPosition: Taustapositsioon\n  overlayColor: Kattekihi värv\n  overlayMixBlendMode: Overlay Mix segamisrežiim\n  per_monitor: Monitori kohta\n  playback: Taasesituse kiirus\n  position:\n    bottom: Alumine\n    center: Keskus\n    left: Vasakpoolne\n    right: Õigus\n    top: Top\n  processing_video: Video töötlemine\n  random: Randoriseeri slaidiseanss\n  saturation: Küllastumine\n  seconds: sekundid\n  select_collection: Valige Kogu\n  thumbnail_generation_complete: Pisipiltide genereerimine on lõpetatud\n  thumbnail_generation_finished: Pisipiltide genereerimine on edukalt lõppenud\n  wallpaper_collection: Tapeedikogu\n  wallpaper_settings: Taustapildi seaded\n  withOverlay: Koos kattekihiga\n  workspace_collections: Tööruumi kogud\nweg:\n  auto_hide: Automaatne peida\n  delay_to_hide: Viivitage peitmine\n  delay_to_show: Näitamisega viivitus\n  dock_side: Positsioon\n  enable: Luba dokk/ülesanderiba\n  filtering: Objektide filtreerimine\n  gap: Lüngad\n  hide_mode:\n    always: Alati\n    never: Mitte kunagi\n    on_overlap: Kattumises\n  items:\n    gap: Esemete vaheline ruum\n    label: Esemed\n    pinned_visibility:\n      always: Alati\n      label: Kinnitatud üksuste nähtavus\n      when_primary: Kui monitor on esmane\n    show_instance_counter: Näita avatud akende loendurit\n    show_window_title: Näita avatud akna pealkirja (ainult horisontaalne)\n    size: Üksuse suurus\n    split_windows: Poolitatud aknad (üks üksus akna kohta)\n    temporal_visibility:\n      all: Kõik\n      label: Kinnitamata üksuste nähtavus\n      on_monitor: Monitoril\n    visible_separators: Nähtavad eraldajad\n  label: Dokk/ülesanderiba\n  margin: Marginaal\n  mode:\n    full_width: Täisekraani laius\n    min_content: Nii väike kui olla saab\n  padding: Polsterdamine\n  show_end_task: Ülesande lõpuülesande kuvamine tegumiribal\n  width: Laius\nwelcome:\n  give_a_review: Andke ülevaade\n  message: >-\n    Seelen UI on tasuta ja avatud lähtekoodiga töölauakeskkond Windowsi jaoks.\n    Siin on teil täielik kontroll selle üle, kuidas teie töölaud välja näeb ja\n    käitub, võimaldades teil kohandada iga detaili oma töövoo ja stiiliga\n    sobivaks.\n  ok: Alustame!\n  review: >-\n    Kui teile meeldib Seeleni kasutajaliidese kasutamine, kaaluge poes arvustuse\n    jätmist – teie tagasiside aitab projektil kasvada ja täiustada.\n  title: Tere tulemast Seeleni kasutajaliidese!\nwidget:\n  enable: Võta see vidin kasutusele\n  enable_for_monitor: Lubatud sellel monitoril\n  instances: Instants\nwm:\n  animations:\n    duration: Animatsiooni kestus (MS)\n    ease_function: Animatsiooni leevendusfunktsioon\n    enable: Luba akna animatsioonid\n  author: Autor\n  border:\n    enable: Luba akna piir\n    offset: Piiride nihke\n    width: Piirilaius\n  description: Kirjeldus\n  drag_behavior: Lohistuskäitumine\n  drag_behavior_options:\n    sort: Sorteeri (akende järjestamine lohistamise ajal ümber)\n    swap: Vaheta (vahetage aknad lohistamise lõpus)\n  enable: Luba plaatide aknahaldur\n  layout: Paigutus\n  resize_delta: Suuruse muutmine delta (%)\n  space_between_containers: Konteinerite vaheline ruum\n  workspace_offset: Tööruumide nihkumine (veerised)\n  workspace_padding: Tööruumide polsterdus\n'yes': Jah\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/eu.yml",
    "content": "action:\n  confirm: Ziur al zaude?\n  confirm_body: Ekintza hau ezin da desegin.\napps_configurations:\n  app:\n    bindings: Lotura (kontuan bi aukerak behar dira)\n    category: Kategoria\n    category_placeholder: Ezein ez\n    monitor: Behatu\n    monitor_placeholder: Ezein ez\n    name: Izen\n    ok_create: Sortu\n    ok_edit: Eguneratu\n    ok_readonly: Editatu berria\n    options:\n      NoInteractive: Ez interaktiborik\n      VdPinned: Erakutsi laneko gune guztietan\n      WmFloat: Twm - Hasi flotatzen\n      WmForce: Twm - Indarra kudeatu\n      WmUnmanage: Twm - Deskudeatu\n    options_label: Aukera gehigarriak\n    title_create: '{{Name}} sortzea'\n    title_edit: '{{Name}} edizioa'\n    title_readonly: '{{Name}} ikustea'\n    weg_options_label: Dock / ataza barrako aukerak\n    wm_options_label: Leiho kudeatzailearen aukerak\n    workspace: Laneko espazioa\n    workspace_placeholder: Ezein ez\n  bundled_msg: >-\n    Konfigurazio multzo hauek ez dira editagarriak eta pertsonalizaziorik gabeko\n    esperientzia onena emateko diseinatuta daude. Zuretzako aplikazio ohikoenak\n    automatikoki konfiguratzen dituzte.\n  bundled_title: App konfigurazioa Seelen-ekin lotuta\n  confirm_delete: Ziur konfigurazio hau ezabatu nahi duzula?\n  confirm_delete_title: Berretsi Ezabatu\n  delete: Ezabatu\n  export: Esportagai\n  export_full: Esportatu ezarpenak aplikazioaren arabera\n  extra_info: >-\n    Seelen UIk aplikazio bakoitzeko identifikatzaile bakarra erabili (lehen\n    partidua aurkitu da), beraz, zehatz-mehatz azaltzen da, azken erantsia\n    lehentasuna izango da, ohar gisa taula lehenetsi da lehenetsitakoa\n    zaharretik azkenetik ordenatuta.\n  identifier:\n    add_block: Gehitu blokea\n    and: Eta\n    id: Identifikatzaile\n    kind: Identifikatu\n    matching_strategy: Bat datorren estrategia\n    matching_strategy_option:\n      contains: Dauka\n      ends_with: Honekin amaitzen da\n      equals: Berdin\n      regex: Adierazpen erregularra\n      starts_with: '-rekin hasten da'\n    negation: Ezeztatu bat datozenak\n    or: Ala\n    remove: Ezabatu blokea\n    type:\n      class: Klasea\n      exe: Exe\n      path: Bidea\n      title: Izenburua\n  import: Inportatu\n  import_full: Inportatu ezarpenak aplikazioaren arabera\n  new: Berri\n  search: Araketa\n  swap: Tratu\ncancel: Indargabetu\nclose: Hurbileko\ndelete: Ezabatu\ndevtools:\n  app_folders: Aplikazio karpetak\n  custom_config_file: Kargatu Config fitxategi pertsonalizatua\n  data_folder: Datu karpeta\n  enable: Gaitu garatzaileen tresnak\n  install_folder: Instalazio karpeta\n  load: Zamatu\n  settings_file: Ezarpenak fitxategia\n  simulate_perm:\n    label: Simulatu widget-aren baimen-eskaera\n    result_allowed: ✓ Emandako baimena\n    result_denied: ✗ Baimena ukatuta\n    trigger: Simulatu\n    widget_id_placeholder: '@egilea/widget-izena'\nextras:\n  clear_icons: Garbitu sistemaren ikonoak cachea\n  clear_icons_tooltip: Berrabiarazi behar da widget guztietan erabat indarrean egon dadin\n  exit: Irten / irten\n  links: Lotura Ofizialak\n  relaunch: Errorefrika\n  version: Bertsio\n  version_fixed: >-\n    Aplikazioa eta WebView2 Runtime bertsioak konpondu dira. Horrek esan nahi du\n    aplikazioak ez dituela eguneraketak jasoko eta WebView2 Runtime ez dela\n    automatikoki eguneratuko Windows eguneratzeekin.\ngeneral:\n  accent_color: Azentu kolorea\n  date_format: Data formatua\n  date_format_how_to: Nola idatzi data-formatua?\n  hardware_acceleration: Hardwarearen azelerazioa\n  hardware_acceleration_description: >-\n    Hardwarearen azelerazioa desgaitzeak memoriaren erabilera murriztuko du,\n    baina errendimendu arazoak sor ditzake. Zuzeneko horma-irudiak erabiltzen ez\n    badituzu desgaitzea segurua da.\n  icon_pack:\n    available: Eskuragarri dauden ikono paketeak\n    selected: Ikono pakete aktiboak\n  language: Mintzaira\n  monday: astelehena\n  performance_mode:\n    on_battery: Bateria gainean\n    on_energy_saver: Energia aurrezteari buruz\n    options:\n      disabled: Minusbaliatu\n      extreme: Muturreko\n      minimal: Gutxien\n    plugged: Entxufatua edo kargatzea\n  polling_interval: Sistemaren galdeketa tartea\n  polling_interval_description: >-\n    Zenbateko maiztasuna (segundotan) Seelen UI-k sistemaren baliabideak\n    egiaztatzen ditu, hala nola CPU, RAM, sarea eta diskoaren jarduera. Kopuru\n    txikiagoak eguneratzeak maizago esan nahi du, baina baliabideen erabilera\n    apur bat handiagoa da.\n  saturday: Larunbata\n  start_of_week: Astearen hasiera\n  startup: Korrika abiaraztean?\n  sunday: Igandea\n  theme:\n    available: Eskuragarri dauden gaiak\n    selected: Gai aktiboak\nheader:\n  labels:\n    config: Konfigurazio\n    developer: Garatzaileentzako\n    extras: Aput\n    general: Jeneral\n    home: Etxe\n    icon_pack_editor: Ikono katxeak\n    iconpack: Ikono paketeak\n    monitors: Monitore\n    plugin: Plakins\n    resources: Baliabide\n    shortcuts: Lasterbideak\n    soundpack: Soinu paketeak\n    specific_apps: Ezarpenak aplikazioaren arabera\n    theme: Haibideak\n    virtual_desk: Mahaigain birtualak\n    wallpaper: Horma-irudiak\n    widget: Widgets\nhome:\n  new_resources: Baliabide berriak\ninherit: Jarauntsi\ninProgress: Abian ...\ninsert: Txertatu\nloading: Kargatzen ...\nmiscellaneous: Heterogeneo\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Gehiago\n'no': Ez-\nopen: Ireki\nquit: Laga\nremove: Atera\nreset_all_to_default: Berrezarri guztiak balio lehenetsiak\nreset_to_default: Berrezarri lehenetsitako balioa\nresources:\n  app_outdated: >-\n    Baliabide honek Seelen UI bertsio berriagoa behar du behar bezala funtziona\n    dezan.\n  corrupted_wallpaper: Ezin izan da atera miniatura - hondatuta edo onartzen ez den bideo formatua\n  delete: Baliabidea ezabatu\n  discover: Ezagutu baliabide gehiago\n  has_update: Eguneratze bat eskuragarri dago\n  high_impact: Errendimenduan eragin handia\n  import_wallpapers: Inportatu tokiko horma-irudiak\n  open_folder: Baliabide irekiko karpeta\n  outdated: >-\n    Baliabide hau Seelen UI bertsio zaharragoa da eta agian ez du behar bezala\n    funtzionatuko.\n  see_on_website: Ikus webgunean\nreview_request:\n  not_now: Orain ez\n  sure: Noski!\n  title: Seelen UI gozatzen?\nsave: Aurreztu\nsave_and_restart: Gorde eta berrabiarazi\nsearch: Bilatu\nsee_more: Ikusi gehiago\nshortcuts:\n  duplicate_error: Lasterbide hau bikoiztuta dago\n  enable: Gaitu zuzeneko lasterbideen sistema\n  enable_tooltip: >-\n    Desgaitu Seelen UI bezeroa erabiliz zure lasterbideen sistema ezartzen\n    baduzu\n  labels:\n    create_new_workspace: Sortu laneko espazioa berria\n    cycle_stack_next: Ziklo pila hurrengoa\n    cycle_stack_prev: Ziklo pila Aurrekoa\n    cycle_wallpaper_next: Aldatu hurrengo horma-irudira\n    cycle_wallpaper_prev: Aldatu aurreko horma-irudira\n    decrease_height: Altuera gutxitu\n    decrease_width: Murriztu zabalera\n    destroy_current_workspace: Destroy uneko lan-eremua\n    focus_bottom: Fokua Behean\n    focus_latest: Fokua azkena\n    focus_left: Focus ezkerrera\n    focus_right: Argindu eskubidea\n    focus_top: Focus Top\n    increase_height: Altuera handitzea\n    increase_width: Zabalera handitzea\n    misc_force_quit: Behartu irten\n    misc_force_restart: Indarra berrabiarazi\n    misc_open_settings: Ezarpenak irekitzea\n    misc_toggle_lock_tracing: Txandakatu blokeoaren trazadura (erregistroak)\n    misc_toggle_win_event_tracing: Txandakatu Gertaeren trazatzea (erregistroak)\n    move_to_workspace: Mugitu lan-eremura {{0}}\n    move_window_down: Mugitu leihoa beherantz\n    move_window_left: Mugitu leihoa ezkerrera\n    move_window_right: Mugitu leihoa eskuinera\n    move_window_up: Mugitu leihoa goitik\n    pause_tiling: Pausatu fitxa leiho kudeatzailea\n    reserve_bottom: Erreserbatu behealdea\n    reserve_float: Erreserba karroza\n    reserve_left: Erreserba ezkerrera\n    reserve_right: Erreserbatu eskubidea\n    reserve_stack: Erreserba pila\n    reserve_top: Erreserba Top\n    restore_sizes: Neurriak leheneratu\n    send_to_workspace: Bidali lantokiari {{0}}\n    start_weg_app: Fokatu edo abiarazi aplikazioa {{0}}\n    switch_to_next_workspace: Aldatu hurrengo lantokira\n    switch_to_previous_workspace: Aldatu aurreko lan-eremura\n    switch_workspace: Aldatu lan-eremura {{0}}\n    toggle_float: Txandakatu leiho karroza modua\n    toggle_monocle: Txandakatu Workspace Monocle modua\n  readonly_tooltip: Hau irakurtzeko lasterbidea da\n  reset: Berrezarri lehenetsitakoak\nsides:\n  bottom: Azpi\n  left: Ezker\n  right: Zuzen\n  top: Tontor\ntoolbar:\n  auto_hide: Ezkutatu automatikoa\n  delay_to_hide: Ezkutatzeko atzeratu\n  delay_to_show: Erakusteko atzerapena\n  dock_side: Kokaleku\n  enable: Gaitu tresna-barra\n  hide_mode:\n    always: Beti\n    never: Inoiz ez\n    on_overlap: Gainjartzean\n  item_size: Elementuaren tamaina\n  label: Tresna barra\n  margin: Marjinaren Tamaina\n  padding: Betegarriaren Tamaina\n  placeholder: {}\nupdate:\n  available: Eguneratu eskuragarri!\n  channel: Eguneratu kanala\n  downloading: Deskargatzen ...\nwall:\n  backgrounds: Horma-irudiak\n  blur: Lau lauak\n  cancel: Utzi\n  close: Itxi\n  collection_name: Bildumaren izena\n  collections: Bildumak\n  contrast: Kontraste\n  corrupted_wallpapers_message: 'Hondatutako horma-paperak, kontuan hartu hauek ezabatzea:'\n  create: Sortu\n  create_collection: Sortu bilduma\n  default_collection: Bilduma lehenetsia\n  delete_collection: Ezabatu bilduma\n  edit_collection: Editatu bilduma\n  enable: Gaitu horma-kudeatzailea\n  extend: Lehen mailakoa luzatzea\n  fit:\n    contain: Eduki\n    cover: Estalki\n    fill: Bete\n  flipHorizontal: Flip horizontala\n  flipVertical: Irauli bertikala\n  generating_thumbnails: Horma-irudi txikiak sortzea\n  hours: ordire ordu\n  interval: Aldatu horma-irudia guztietan\n  minutes: minutuak\n  monitor_collection: Monitore bilduma\n  multimonitor_behaviour: Monitore anitzeko portaera\n  muted: Ezkutatu bideoaren audioa\n  no_background: Aurkezpen hutsak, gaiaren atzeko planoa erabiliz.\n  no_collections: Oraindik ez da bildumarik sortu. Egin klik + botoian bat sortzeko.\n  objectFit: Atzeko planoaren moldaketa\n  objectPosition: Atzeko planoaren posizioa\n  overlayColor: Overlay kolorea\n  overlayMixBlendMode: Overlay nahastu nahasketa modua\n  per_monitor: Monitore bakoitzeko\n  playback: Erreproduzitzeko abiadura\n  position:\n    bottom: Azpi\n    center: Gune\n    left: Ezker\n    right: Zuzen\n    top: Tontor\n  processing_video: Bideoa prozesatzen\n  random: Aurkezpen diaporama\n  saturation: Saturazio\n  seconds: bizkarbete\n  select_collection: Hautatu Bilduma\n  thumbnail_generation_complete: Miniaturak sortzea amaituta\n  thumbnail_generation_finished: Miniaturak sortzea ondo amaitu da\n  wallpaper_collection: Horma-irudi bilduma\n  wallpaper_settings: Horma-paperaren ezarpenak\n  withOverlay: Gainjartzearekin\n  workspace_collections: Lan-esparruko bildumak\nweg:\n  auto_hide: Ezkutatu automatikoa\n  delay_to_hide: Ezkutatzeko atzeratu\n  delay_to_show: Erakusteko atzerapena\n  dock_side: Kokaleku\n  enable: Gaitu kaia / ataza barra\n  filtering: Elementua iragaztea\n  gap: Tarte\n  hide_mode:\n    always: Beti\n    never: Inoiz ez\n    on_overlap: Gainjartzean\n  items:\n    gap: Elementuen arteko espazioa\n    label: Manatu\n    pinned_visibility:\n      always: Beti\n      label: Ainguratutako elementuen ikusgaitasuna\n      when_primary: Monitorea nagusia denean\n    show_instance_counter: Erakutsi irekitako leihoen kontagailua\n    show_window_title: Erakutsi leiho irekiko titulua (horizontala bakarrik)\n    size: Elementuen tamaina\n    split_windows: Zatitu leihoak (elementu bat leiho bakoitzeko)\n    temporal_visibility:\n      all: Denak\n      label: Ainguratu gabeko elementuen ikusgaitasuna\n      on_monitor: Monitorean\n    visible_separators: Ikusgai bereizgailuak\n  label: Dock / ataza barra\n  margin: Emandar\n  mode:\n    full_width: Pantaila osoko zabalera\n    min_content: Txikia izan daitekeen bezala\n  padding: Betegarri\n  show_end_task: Erakutsi zeregin-barran\n  width: Zabalera\nwelcome:\n  give_a_review: Iritzia eman\n  message: >-\n    Seelen UI kode irekiko mahaigaineko ingurunea da Windows-erako. Hemen zure\n    mahaigainaren itxura eta portaeraren gaineko kontrol osoa daukazu, eta\n    xehetasun guztiak pertsonaliza ditzakezu zure lan-fluxuarekin eta\n    estiloarekin bat etortzeko.\n  ok: Has gaitezen!\n  review: >-\n    Seelen UI erabiltzea gustatzen bazaizu, kontuan hartu berrikuspena dendan\n    uztea; zure iritziak proiektua hazten eta hobetzen laguntzen du.\n  title: Ongi etorri Seelen UI-ra!\nwidget:\n  enable: Gaitu widget hau\n  enable_for_monitor: Gaitu monitore honetan\n  instances: Makineria\nwm:\n  animations:\n    duration: Animazioaren iraupena (MS)\n    ease_function: Animazioaren arakuntzaren funtzioa\n    enable: Gaitu leihoen animazioak\n  author: Idazle\n  border:\n    enable: Gaitu leihoen ertza\n    offset: Ertzaren desplazamendua\n    width: Ertzaren zabalera\n  description: Deskribapen\n  drag_behavior: Arrastatu portaera\n  drag_behavior_options:\n    sort: Ordenatu (berrantolatu leihoak arrastatu bitartean)\n    swap: Trukatu (trukatu leihoak arrastatu amaieran)\n  enable: Gaitu fitxa leiho kudeatzailea\n  layout: Diseinu\n  resize_delta: RETIZE DELTA (%)\n  space_between_containers: Edukiontzien arteko espazioa\n  workspace_offset: Laneko espazioak desplazamendua (marjinak)\n  workspace_padding: Laneko guneak betegarria\n'yes': Bai\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/fa.yml",
    "content": "action:\n  confirm: مطمئن هستید؟\n  confirm_body: این عمل نمی تواند خنثی شود.\napps_configurations:\n  app:\n    bindings: اتصال (توجه داشته باشید هر دو گزینه مورد نیاز است)\n    category: دسته بندی\n    category_placeholder: هیچ یک\n    monitor: نظارت\n    monitor_placeholder: هیچ یک\n    name: نام\n    ok_create: ايجاد كردن\n    ok_edit: به روز رسانی\n    ok_readonly: ویرایش به عنوان جدید\n    options:\n      NoInteractive: بدون تعامل\n      VdPinned: نمایش در همه فضاهای کاری\n      WmFloat: Twm - شناور را شروع کنید\n      WmForce: Twm - مدیریت نیرو\n      WmUnmanage: Twm - مدیریت کنید\n    options_label: گزینه های اضافی\n    title_create: ایجاد {{name}}\n    title_edit: ویرایش {{نام}}\n    title_readonly: مشاهده {{نام}}\n    weg_options_label: گزینه های Dock/TaskBar\n    wm_options_label: گزینه های مدیر پنجره\n    workspace: فضای کاری\n    workspace_placeholder: هیچ یک\n  bundled_msg: >-\n    این تنظیمات بسته بندی شده قابل ویرایش نیستند و به گونه ای طراحی شده اند که\n    بدون شخصی سازی بهترین تجربه را برای شما فراهم می کنند. آنها به طور خودکار\n    متداول ترین برنامه ها را برای شما پیکربندی می کنند.\n  bundled_title: پیکربندی برنامه همراه با Seelen\n  confirm_delete: آیا مطمئن هستید که می خواهید این پیکربندی/s را حذف کنید؟\n  confirm_delete_title: تایید حذف\n  delete: حذف\n  export: صادر کردن\n  export_full: تنظیمات صادرات توسط برنامه\n  extra_info: >-\n    رابط کاربری Seelen تنها از یک شناسه برای هر برنامه استفاده می کند (اولین\n    تطابق پیدا شد) بنابراین ترتیب نحوه مشخص شدن مهم است، آخرین موارد اضافه شده\n    در اولویت قرار می گیرند، زیرا توجه داشته باشید جدول به طور پیش فرض از\n    جدیدترین به قدیمی مرتب شده است.\n  identifier:\n    add_block: اضافه کردن\n    and: وت\n    id: مشخص کننده\n    kind: شناسایی توسط\n    matching_strategy: استراتژی تطبیق\n    matching_strategy_option:\n      contains: حاوی\n      ends_with: به پایان می رسد با\n      equals: برابر است\n      regex: بیان منظم\n      starts_with: شروع می شود\n    negation: نفی تطبیق\n    or: یا\n    remove: حذف بلوک\n    type:\n      class: کلاس\n      exe: Exe\n      path: مسیر\n      title: عنوان\n  import: وارد كردن\n  import_full: وارد کردن تنظیمات توسط برنامه\n  new: جدید\n  search: جستجو کردن\n  swap: مبادله کردن\ncancel: لغو کردن\nclose: نزدیک\ndelete: حذف\ndevtools:\n  app_folders: پوشه های برنامه\n  custom_config_file: بارگیری پرونده پیکربندی سفارشی\n  data_folder: پوشه داده ها\n  enable: ابزارهای توسعه دهنده را فعال کنید\n  install_folder: پوشه نصب\n  load: بار\n  settings_file: پرونده تنظیمات\n  simulate_perm:\n    label: درخواست مجوز ویجت را شبیه سازی کنید\n    result_allowed: ✓ مجوز داده شده است\n    result_denied: ✗ اجازه داده نشد\n    trigger: شبیه سازی کنید\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: حافظه نهان سیستم را پاک کنید\n  clear_icons_tooltip: >-\n    یک راه اندازی مجدد می تواند مورد نیاز باشد تا به طور کامل در همه ویجت ها\n    تأثیر بگذارد\n  exit: ترک/خروج\n  links: پیوندهای رسمی\n  relaunch: مجدداً\n  version: نسخه\n  version_fixed: >-\n    نسخه برنامه و WebView2 Runtime رفع شده است. این بدان معنی است که برنامه به\n    روز رسانی دریافت نمی کند و WebView2 Runtime به طور خودکار با به روز رسانی\n    ویندوز به روز نمی شود.\ngeneral:\n  accent_color: رنگ لهجه\n  date_format: قالب تاریخ\n  date_format_how_to: چگونه فرمت تاریخ بنویسیم؟\n  hardware_acceleration: شتاب سخت افزاری\n  hardware_acceleration_description: >-\n    غیرفعال کردن شتاب سخت افزاری مصرف حافظه را کاهش می دهد، اما می تواند باعث\n    مشکلات عملکرد شود. اگر از تصاویر پس زمینه زنده استفاده نکنید، غیرفعال کردن\n    آن بی خطر است.\n  icon_pack:\n    available: بسته‌های آیکون موجود\n    selected: بسته های آیکون فعال\n  language: زبان\n  monday: دوشنبه\n  performance_mode:\n    on_battery: در باتری\n    on_energy_saver: در مورد صرفه جویی در انرژی\n    options:\n      disabled: معلول\n      extreme: شدید\n      minimal: حداقل\n    plugged: وصل شده یا شارژ\n  polling_interval: فاصله نظرسنجی سیستم\n  polling_interval_description: >-\n    هر چند وقت یک‌بار (در چند ثانیه) UI Seelen منابع سیستم مانند CPU، RAM، شبکه\n    و فعالیت دیسک را بررسی می‌کند. تعداد کمتر به معنای به‌روزرسانی‌های مکرر است،\n    اما استفاده از منابع کمی بیشتر است.\n  saturday: شنبه\n  start_of_week: شروع هفته\n  startup: در راه اندازی اجرا می شود؟\n  sunday: یکشنبه\n  theme:\n    available: تم های موجود\n    selected: تم های فعال\nheader:\n  labels:\n    config: تنظیمات\n    developer: برای توسعه دهندگان\n    extras: اضافی\n    general: عمومی\n    home: خانه\n    icon_pack_editor: نمادهای ذخیره شده\n    iconpack: بسته های نماد\n    monitors: مانیتور\n    plugin: پلاگین\n    resources: منابع\n    shortcuts: میانبرها\n    soundpack: بسته های صدا\n    specific_apps: تنظیمات توسط برنامه\n    theme: مضامین\n    virtual_desk: دسک تاپ مجازی\n    wallpaper: تصاویر پس زمینه\n    widget: ویراچه\nhome:\n  new_resources: منابع جدید\ninherit: ارث\ninProgress: در حال پیش رفت...\ninsert: درج کردن\nloading: بارگذاری...\nmiscellaneous: متفاوتی\nmonitors_configurations:\n  label: مانیتور {{فهرست}}\nmore: بیشتر\n'no': هیچ\nopen: باز کن\nquit: ترک\nremove: برداشتن\nreset_all_to_default: تنظیم مجدد همه به مقادیر پیش فرض\nreset_to_default: تنظیم مجدد به مقدار پیش فرض\nresources:\n  app_outdated: این منبع به نسخه جدیدتر Seelen UI نیاز دارد تا به درستی کار کند.\n  corrupted_wallpaper: استخراج تصویر کوچک انجام نشد - فرمت ویدیو خراب یا پشتیبانی نشده است\n  delete: منبع را حذف کنید\n  discover: منابع بیشتری را کشف کنید\n  has_update: به روزرسانی در دسترس است\n  high_impact: تاثیر زیاد بر عملکرد\n  import_wallpapers: تصاویر پس زمینه محلی را وارد کنید\n  open_folder: پوشه منبع باز\n  outdated: >-\n    این منبع برای نسخه قدیمی تر Seelen UI طراحی شده است و ممکن است به درستی کار\n    نکند.\n  see_on_website: در وب سایت ببینید\nreview_request:\n  not_now: الان نه\n  sure: مطمئنا!\n  title: از رابط کاربری Seelen لذت می برید؟\nsave: صرفه جویی\nsave_and_restart: ذخیره و راه اندازی مجدد\nsearch: جستجو کنید\nsee_more: بیشتر ببینید\nshortcuts:\n  duplicate_error: این میانبر تکراری است\n  enable: سیستم میانبر داخلی را فعال کنید\n  enable_tooltip: >-\n    اگر سیستم میانبر خود را با استفاده از مشتری Seelen UI پیاده سازی خواهید کرد\n    ، غیرفعال کنید\n  labels:\n    create_new_workspace: فضای کاری جدید ایجاد کنید\n    cycle_stack_next: پشته چرخه بعدی\n    cycle_stack_prev: چرخه قبلی\n    cycle_wallpaper_next: تغییر به تصویر زمینه بعدی\n    cycle_wallpaper_prev: تغییر به تصویر زمینه قبلی\n    decrease_height: کاهش قد\n    decrease_width: کاهش عرض\n    destroy_current_workspace: فضای کاری فعلی را نابود کنید\n    focus_bottom: پایین تمرکز\n    focus_latest: آخرین تمرکز\n    focus_left: تمرکز سمت چپ\n    focus_right: راست تمرکز کنید\n    focus_top: تمرکز بالا\n    increase_height: افزایش ارتفاع\n    increase_width: عرض را افزایش دهید\n    misc_force_quit: نیرو ترک\n    misc_force_restart: مجدداً نیرو\n    misc_open_settings: تنظیمات باز\n    misc_toggle_lock_tracing: Toggle Lock Tracing (سیاهههای مربوط)\n    misc_toggle_win_event_tracing: Toggle Win Tracing (سیاهههای مربوط)\n    move_to_workspace: حرکت به فضای کاری {{0}}\n    move_window_down: پنجره را به پایین منتقل کنید\n    move_window_left: پنجره را به سمت چپ حرکت دهید\n    move_window_right: پنجره را به سمت راست حرکت دهید\n    move_window_up: پنجره را به بالا منتقل کنید\n    pause_tiling: مکث مدیر پنجره کاشی کاری\n    reserve_bottom: قسمت پایین ذخیره\n    reserve_float: شناور رزرو\n    reserve_left: رزرو سمت چپ\n    reserve_right: حق رزرو\n    reserve_stack: پشته ذخیره\n    reserve_top: بالای ذخیره\n    restore_sizes: اندازه ها را بازیابی کنید\n    send_to_workspace: ارسال به فضای کاری {{0}}\n    start_weg_app: تمرکز یا شروع برنامه {{0}}\n    switch_to_next_workspace: به فضای کاری بعدی بروید\n    switch_to_previous_workspace: به فضای کاری قبلی بروید\n    switch_workspace: تغییر به فضای کاری {{0}}\n    toggle_float: حالت شناور پنجره\n    toggle_monocle: حالت مونوکلر فضای کاری را تغییر دهید\n  readonly_tooltip: این یک میانبر فقط خواندنی است\n  reset: تنظیم مجدد به پیش فرض\nsides:\n  bottom: پایین\n  left: ترک کرد\n  right: درست\n  top: بالا\ntoolbar:\n  auto_hide: پنهان خودکار\n  delay_to_hide: تاخیر برای پنهان شدن\n  delay_to_show: تاخیر برای نمایش\n  dock_side: موقعیت\n  enable: نوار ابزار فانتزی را فعال کنید\n  hide_mode:\n    always: همیشه\n    never: هرگز\n    on_overlap: روی همپوشانی\n  item_size: اندازه مورد\n  label: نوار ابزار\n  margin: اندازه حاشیه\n  padding: اندازه بالشتک\n  placeholder: {}\nupdate:\n  available: بروزرسانی موجود!\n  channel: کانال\n  downloading: بارگیری ...\nwall:\n  backgrounds: تصاویر پس زمینه\n  blur: مبهم\n  cancel: لغو کنید\n  close: بستن\n  collection_name: نام مجموعه\n  collections: مجموعه ها\n  contrast: کنتراست\n  corrupted_wallpapers_message: 'والپیپرهای خراب، این موارد را حذف کنید:'\n  create: ایجاد کنید\n  create_collection: ایجاد مجموعه\n  default_collection: مجموعه پیش‌فرض\n  delete_collection: حذف مجموعه\n  edit_collection: ویرایش مجموعه\n  enable: مدیر تصویر زمینه را فعال کنید\n  extend: اولیه را گسترش دهید\n  fit:\n    contain: حاوی بودن\n    cover: پوشش\n    fill: پر کردن\n  flipHorizontal: تلنگر افقی\n  flipVertical: به صورت عمودی\n  generating_thumbnails: ایجاد تصاویر ریز کاغذ دیواری\n  hours: ساعت\n  interval: کاغذ دیواری را تغییر دهید\n  minutes: دقایقی\n  monitor_collection: مجموعه مانیتور\n  multimonitor_behaviour: رفتار چند مانیتور\n  muted: صدای ویدیو را بی صدا کنید\n  no_background: نمایش اسلاید خالی ، به جای آن با استفاده از پس زمینه تم.\n  no_collections: هنوز هیچ مجموعه ای ایجاد نشده است. برای ایجاد یک دکمه + را کلیک کنید.\n  objectFit: متناسب با پس زمینه\n  objectPosition: موقعیت پس زمینه\n  overlayColor: رنگ روکش\n  overlayMixBlendMode: حالت مخلوط مخلوط روکش\n  per_monitor: در هر مانیتور\n  playback: سرعت پخش\n  position:\n    bottom: پایین\n    center: مرکز\n    left: چپ\n    right: حق\n    top: بالا\n  processing_video: در حال پردازش ویدیو\n  random: نمایش پرده ای تصادفی\n  saturation: اشباع\n  seconds: ثانیه\n  select_collection: مجموعه را انتخاب کنید\n  thumbnail_generation_complete: تولید ریز عکسها کامل شد\n  thumbnail_generation_finished: تولید تصویر بند انگشتی با موفقیت به پایان رسید\n  wallpaper_collection: مجموعه کاغذ دیواری\n  wallpaper_settings: تنظیمات کاغذ دیواری\n  withOverlay: با روکش\n  workspace_collections: مجموعه های فضای کاری\nweg:\n  auto_hide: پنهان خودکار\n  delay_to_hide: تاخیر برای پنهان شدن\n  delay_to_show: تاخیر برای نمایش\n  dock_side: موقعیت\n  enable: Dock/TaskBar را فعال کنید\n  filtering: فیلتر کردن مورد\n  gap: شکاف\n  hide_mode:\n    always: همیشه\n    never: هرگز\n    on_overlap: روی همپوشانی\n  items:\n    gap: فضای بین موارد\n    label: موارد\n    pinned_visibility:\n      always: همیشه\n      label: قابلیت مشاهده موارد پین شده\n      when_primary: وقتی مانیتور اولیه است\n    show_instance_counter: پیشخوان ویندوز باز را نشان دهید\n    show_window_title: عنوان پنجره باز (فقط افقی)\n    size: اندازه مورد\n    split_windows: تقسیم ویندوز (یک مورد در هر پنجره)\n    temporal_visibility:\n      all: همه\n      label: قابلیت مشاهده موارد پین نشده\n      on_monitor: روی مانیتور\n    visible_separators: جداکننده های قابل مشاهده\n  label: اسکله/نوار وظیفه\n  margin: لبه\n  mode:\n    full_width: عرض تمام صفحه\n    min_content: کوچک که می تواند باشد\n  padding: لایه گذاری\n  show_end_task: نمایش پایان کار در نوار وظیفه\n  width: عرض\nwelcome:\n  give_a_review: نظر بدهید\n  message: >-\n    Seelen UI یک محیط دسکتاپ رایگان و متن باز برای ویندوز است. در اینجا شما\n    کنترل کاملی بر نحوه ظاهر و رفتار دسکتاپ خود دارید و به شما این امکان را می\n    دهد که هر جزئیات را مطابق با جریان کار و سبک خود سفارشی کنید.\n  ok: بیایید شروع کنیم!\n  review: >-\n    اگر از استفاده از رابط کاربری Seelen لذت می برید، نظر خود را در فروشگاه\n    بگذارید - بازخورد شما به رشد و بهبود پروژه کمک می کند.\n  title: به UI Seelen خوش آمدید!\nwidget:\n  enable: این ویجت را فعال کنید\n  enable_for_monitor: این مانیتور را فعال کنید\n  instances: نمونه\nwm:\n  animations:\n    duration: مدت زمان انیمیشن (MS)\n    ease_function: عملکرد تسکین انیمیشن\n    enable: انیمیشن های پنجره را فعال کنید\n  author: نویسنده\n  border:\n    enable: مرز پنجره را فعال کنید\n    offset: جبران مرز\n    width: عرض مرز\n  description: شرح\n  drag_behavior: Drag Behavior\n  drag_behavior_options:\n    sort: مرتب سازی (ترتیب مجدد پنجره ها در حین کشیدن)\n    swap: تعویض (تعویض پنجره ها در انتهای کشیدن)\n  enable: مدیر پنجره کاشی کاری را فعال کنید\n  layout: چیدمان\n  resize_delta: تغییر اندازه دلتا (٪)\n  space_between_containers: فضای بین ظروف\n  workspace_offset: مکانهای کار افست (حاشیه)\n  workspace_padding: قسمتهای کاری PADING\n'yes': بله\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/fi.yml",
    "content": "action:\n  confirm: Oletko varma?\n  confirm_body: Tätä toimintaa ei voida kumota.\napps_configurations:\n  app:\n    bindings: Sidonta (huomautus tarvitaan molemmat vaihtoehdot)\n    category: Kategoria\n    category_placeholder: Ei mitään\n    monitor: Monitori\n    monitor_placeholder: Ei mitään\n    name: Nimi\n    ok_create: Luoda\n    ok_edit: Päivittää\n    ok_readonly: Muokata UUSIA\n    options:\n      NoInteractive: Ei interaktiivista\n      VdPinned: Näytä kaikissa työtiloissa\n      WmFloat: Twm - Aloita kelluminen\n      WmForce: Twm - Pakota hallinta\n      WmUnmanage: Twm - Ei hallintaa\n    options_label: Ylimääräiset vaihtoehdot\n    title_create: '{{Nimi}} luominen'\n    title_edit: Muokkaaminen {{nimi}}\n    title_readonly: Katselu {{nimi}}\n    weg_options_label: Dock/tehtäväpalkin vaihtoehdot\n    wm_options_label: Window Manager -vaihtoehdot\n    workspace: Työtila\n    workspace_placeholder: Ei mitään\n  bundled_msg: >-\n    Näitä pauhutettuja kokoonpanoja ei voida muokata, ja ne on suunniteltu\n    tarjoamaan sinulle paras kokemus ilman räätälöintiä. Ne määrittelevät\n    sinulle yleisimmät sovellukset automaattisesti.\n  bundled_title: APP -konfigurointi, joka on mukana Seelenin kanssa\n  confirm_delete: Haluatko varmasti poistaa tämän kokoonpanon/S?\n  confirm_delete_title: Vahvista poistaminen\n  delete: Poistaa\n  export: Viedä\n  export_full: Vie asetukset sovelluksittain\n  extra_info: >-\n    Seelen -käyttöliittymä käyttää vain yhtä tunnistetta sovellusta kohden\n    (ensimmäinen ottelu löydetty), joten tilaus kuinka spesifikaatio on tärkeä,\n    viimeisin lisätty lisätään etusijalle, kuten huomaa, että taulukko\n    lajitellaan oletuksena viimeisimmästä vanhaan.\n  identifier:\n    add_block: Lisätä\n    and: JA\n    id: Tunniste\n    kind: Tunnistaa jtk\n    matching_strategy: Sovitusstrategia\n    matching_strategy_option:\n      contains: Sisältää\n      ends_with: päättyy\n      equals: Yhtä\n      regex: Säännöllinen lauseke\n      starts_with: Alkaa kanssa\n    negation: Negatiivista vastaavuus\n    or: TAI\n    remove: Poistaa lohko\n    type:\n      class: Luokka\n      exe: Exe\n      path: Polku\n      title: Otsikko\n  import: Tuonti\n  import_full: Tuo asetukset sovelluksittain\n  new: Uusi\n  search: Hae\n  swap: Vaihtaa\ncancel: Peruuttaa\nclose: Lähellä\ndelete: Poistaa\ndevtools:\n  app_folders: Sovelluskansiot\n  custom_config_file: Lataa mukautettu konfigurointitiedosto\n  data_folder: Tietokansio\n  enable: Ota kehittäjätyökalut käyttöön\n  install_folder: Asennuskansio\n  load: Ladata\n  settings_file: Asetustiedosto\n  simulate_perm:\n    label: Simuloi widgetin lupapyyntö\n    result_allowed: ✓ Lupa myönnetty\n    result_denied: ✗ Lupa evätty\n    trigger: Simuloida\n    widget_id_placeholder: '@author/widgetin nimi'\nextras:\n  clear_icons: Tyhjennä järjestelmän kuvakkeiden välimuisti\n  clear_icons_tooltip: >-\n    Uudelleenkäynnistystä saatetaan tarvita, jotta kaikki widgetit tulisivat\n    täysin voimaan.\n  exit: Lopettaa/poistua\n  links: Viralliset linkit\n  relaunch: Käynnistää uudelleen\n  version: Versio\n  version_fixed: >-\n    Sovellus ja WebView2 Runtime -versiot ovat kiinteät. Tämä tarkoittaa, että\n    sovellus ei saa päivityksiä ja WebView2 Runtime ei päivity automaattisesti\n    Windows-päivityksillä.\ngeneral:\n  accent_color: Korostusväri\n  date_format: Päiväysmuoto\n  date_format_how_to: Kuinka kirjoittaa päivämäärämuoto?\n  hardware_acceleration: Laitteistokiihdytys\n  hardware_acceleration_description: >-\n    Laitteistokiihdytyksen poistaminen käytöstä vähentää muistin käyttöä, mutta\n    voi aiheuttaa suorituskykyongelmia. On turvallista poistaa käytöstä, jos et\n    käytä live-taustakuvia.\n  icon_pack:\n    available: Saatavilla kuvakepaketit\n    selected: Aktiiviset kuvakepaketit\n  language: Kieli\n  monday: maanantai\n  performance_mode:\n    on_battery: Akun päällä\n    on_energy_saver: Energiansäästäjällä\n    options:\n      disabled: Vammainen\n      extreme: Äärimmäinen\n      minimal: Minimaalinen\n    plugged: Kytketty tai lataus\n  polling_interval: Järjestelmän äänestysväli\n  polling_interval_description: >-\n    Kuinka usein (sekunneissa) Seelen UI tarkistaa järjestelmäresurssit, kuten\n    suorittimen, RAM-muistin, verkon ja levytoiminnan. Pienempi luku tarkoittaa\n    useammin päivityksiä, mutta hieman suurempaa resurssien käyttöä.\n  saturday: lauantai\n  start_of_week: Viikon alku\n  startup: Suorittaa käynnistyksen yhteydessä?\n  sunday: sunnuntai\n  theme:\n    available: Käytettävissä olevat teemat\n    selected: Aktiiviset teemat\nheader:\n  labels:\n    config: Kokoonpanot\n    developer: Kehittäjille\n    extras: Lisävarusteet\n    general: Yleinen\n    home: Kotiin\n    icon_pack_editor: Välimuistissa olevat kuvakkeet\n    iconpack: Kuvakepaketit\n    monitors: Näytöt\n    plugin: Liitännäiset\n    resources: Resurssit\n    shortcuts: Pikakuvakkeet\n    soundpack: Sound Packit\n    specific_apps: Asetukset sovelluksen mukaan\n    theme: Teemat\n    virtual_desk: Virtuaaliset työpöydät\n    wallpaper: Taustakuvat\n    widget: Widgetit\nhome:\n  new_resources: Uudet resurssit\ninherit: Periä\ninProgress: Käynnissä...\ninsert: Lisätä\nloading: Ladataan...\nmiscellaneous: Sekalainen\nmonitors_configurations:\n  label: Monitor {{hakemisto}}\nmore: Lisää\n'no': Ei\nopen: Avata\nquit: Lopettaa\nremove: Poistaa\nreset_all_to_default: Nollaa kaikki oletusarvot\nreset_to_default: Nollaa oletusarvoon\nresources:\n  app_outdated: Tämä resurssi vaatii uudemman version Seelen UI:sta toimiakseen oikein.\n  corrupted_wallpaper: >-\n    Pikkukuvan purkaminen epäonnistui – videomuoto on vioittunut tai sitä ei\n    tueta\n  delete: Poistaa resurssi\n  discover: Löydä lisää resursseja\n  has_update: Päivitys on käytettävissä\n  high_impact: Suuri vaikutus suorituskykyyn\n  import_wallpapers: Tuo paikallisia taustakuvia\n  open_folder: Avaa resurssikansio\n  outdated: >-\n    Tämä resurssi on suunniteltu Seelen UI:n vanhemmalle versiolle, eikä se\n    välttämättä toimi oikein.\n  see_on_website: Katso nettisivuilta\nreview_request:\n  not_now: Ei nyt\n  sure: Varma!\n  title: Pidätkö Seelen-käyttöliittymästä?\nsave: Tallentaa\nsave_and_restart: Tallenna ja käynnistä uudelleen\nsearch: Haku\nsee_more: Katso lisää\nshortcuts:\n  duplicate_error: Tämä pikanäppäin on kopioitu\n  enable: Ota sisäänrakennettu pikakuvausjärjestelmä\n  enable_tooltip: >-\n    Poista käytöstä, jos otat käyttöön omat pikakuvausjärjestelmäsi Seelen UI\n    -asiakkaan avulla\n  labels:\n    create_new_workspace: Luo uusi työtila\n    cycle_stack_next: Sykli pino seuraavaksi\n    cycle_stack_prev: Pyöräpino edellinen\n    cycle_wallpaper_next: Vaihda seuraavaan taustakuvaksi\n    cycle_wallpaper_prev: Vaihda edelliseen taustakuvaan\n    decrease_height: Pienenee\n    decrease_width: Vähentynyt leveys\n    destroy_current_workspace: Tuhoa nykyinen työtila\n    focus_bottom: Tarkennuspohja\n    focus_latest: Focus -viimeisin\n    focus_left: Focus jäljellä\n    focus_right: Keskittyä oikein\n    focus_top: Keskittyä\n    increase_height: Lisätä korkeutta\n    increase_width: Lisätä leveyttä\n    misc_force_quit: Pakottaa\n    misc_force_restart: Pakota uudelleenkäynnistys\n    misc_open_settings: Avaa asetukset\n    misc_toggle_lock_tracing: Vaihda lukkojen jäljitys (lokit)\n    misc_toggle_win_event_tracing: Vaihda Win Win Event Tracing (lokit)\n    move_to_workspace: Siirry työtilaan {{0}}\n    move_window_down: Siirrä ikkuna pohjaan\n    move_window_left: Siirrä ikkuna vasemmalle\n    move_window_right: Siirrä ikkuna oikealle\n    move_window_up: Siirrä ikkuna yläosaan\n    pause_tiling: Tauko laatoitus ikkunanhallinta\n    reserve_bottom: Varata pohja\n    reserve_float: Varaa kelluva\n    reserve_left: Varaa jäljellä\n    reserve_right: Varaa oikea\n    reserve_stack: Varantopino\n    reserve_top: Varaa\n    restore_sizes: Palauttaa koot\n    send_to_workspace: Lähetä työtilaan {{0}}\n    start_weg_app: Keskittyä tai käynnistä sovellus {{0}}\n    switch_to_next_workspace: Vaihda seuraavaan työtilaan\n    switch_to_previous_workspace: Vaihda edelliseen työtilaan\n    switch_workspace: Vaihda työtilaan {{0}}\n    toggle_float: Vaihda ikkunan kelluva tila\n    toggle_monocle: Vaihda työtilan monokle -tila\n  readonly_tooltip: Tämä on vain luku-pikakuvake\n  reset: Palauta laiminlyöntiin\nsides:\n  bottom: Pohja\n  left: Vasen\n  right: Oikea\n  top: Ylhäältä\ntoolbar:\n  auto_hide: Automaattinen piilo\n  delay_to_hide: Viive piilottaa\n  delay_to_show: Viive näyttöön\n  dock_side: Asema\n  enable: Ota hieno työkalurivi käyttöön\n  hide_mode:\n    always: Aina\n    never: Ei koskaan\n    on_overlap: Päällekkäin\n  item_size: Tuotteen koko\n  label: Työkalurivi\n  margin: Marginaalin koko\n  padding: Pehmusteen koko\n  placeholder: {}\nupdate:\n  available: Päivitä saatavilla!\n  channel: Päivityskanava\n  downloading: Lataaminen ...\nwall:\n  backgrounds: Taustakuvat\n  blur: Sumeus\n  cancel: Peruuttaa\n  close: Lähellä\n  collection_name: Kokoelman nimi\n  collections: Kokoelmat\n  contrast: Kontrasti\n  corrupted_wallpapers_message: 'Vioittuneet taustakuvat, harkitse näiden poistamista:'\n  create: Luoda\n  create_collection: Luo kokoelma\n  default_collection: Oletuskokoelma\n  delete_collection: Poista kokoelma\n  edit_collection: Muokkaa kokoelmaa\n  enable: Ota taustakuvan johtaja käyttöön\n  extend: Laajenna ensisijaista\n  fit:\n    contain: Sisältää\n    cover: Kansi\n    fill: Täytä\n  flipHorizontal: Käännä vaakasuora\n  flipVertical: Flip pystysuora\n  generating_thumbnails: Taustakuvien pikkukuvien luominen\n  hours: tuntia\n  interval: Vaihda taustakuva jokainen\n  minutes: minuutti\n  monitor_collection: Näytön kokoelma\n  multimonitor_behaviour: Multimonitorin käyttäytyminen\n  muted: Mykistä videon ääni\n  no_background: Tyhjä diaesitys käyttämällä sen sijaan teeman taustalla.\n  no_collections: Kokoelmia ei ole vielä luotu. Luo sellainen napsauttamalla +-painiketta.\n  objectFit: Tausta Fit\n  objectPosition: Tausta Asema\n  overlayColor: Päällekkäisväri\n  overlayMixBlendMode: Päällekkäinen sekoitus sekoitustila\n  per_monitor: per monitori\n  playback: Toistonopeus\n  position:\n    bottom: Pohja\n    center: Keskusta\n    left: Vasen\n    right: Oikea\n    top: Top\n  processing_video: Videota käsitellään\n  random: Satunnaista diaesitys\n  saturation: Saturaatio\n  seconds: sekunti\n  select_collection: Valitse Kokoelma\n  thumbnail_generation_complete: Pikkukuvien luominen valmis\n  thumbnail_generation_finished: Pikkukuvien luominen on päättynyt onnistuneesti\n  wallpaper_collection: Taustakuva kokoelma\n  wallpaper_settings: Taustakuvan asetukset\n  withOverlay: Päällysteen kanssa\n  workspace_collections: Työtilakokoelmat\nweg:\n  auto_hide: Automaattinen piilo\n  delay_to_hide: Viive piilottaa\n  delay_to_show: Viive näyttöön\n  dock_side: Sijainti\n  enable: Ota Dock/tehtäväpalkki käyttöön\n  filtering: Kohteen suodatus\n  gap: Kuilu\n  hide_mode:\n    always: Aina\n    never: Ei koskaan\n    on_overlap: Päällekkäin\n  items:\n    gap: Tilaa esineiden välillä\n    label: Kohteet\n    pinned_visibility:\n      always: Aina\n      label: Kiinnitettyjen kohteiden näkyvyys\n      when_primary: Kun näyttö on ensisijainen\n    show_instance_counter: Näytä avoimien ikkunoiden laskuri\n    show_window_title: Näytä avoimen ikkunan otsikko (vain vaakasuora)\n    size: Esineen koko\n    split_windows: Jaetut ikkunat (yksi kohde per ikkuna)\n    temporal_visibility:\n      all: Kaikki\n      label: Irrotettujen kohteiden näkyvyys\n      on_monitor: Monitorissa\n    visible_separators: Näkyvät erottimet\n  label: Telakka/tehtäväpalkki\n  margin: Marginaali\n  mode:\n    full_width: Koko näytön leveys\n    min_content: Niin pieni kuin voi olla\n  padding: Pehmuste\n  show_end_task: Näyttää lopetetun tehtävän tehtäväpalkissa\n  width: Leveys\nwelcome:\n  give_a_review: Anna arvostelu\n  message: >-\n    Seelen UI on ilmainen ja avoimen lähdekoodin työpöytäympäristö Windowsille.\n    Täällä voit hallita täysin sitä, miten työpöytäsi näyttää ja käyttäytyy,\n    joten voit mukauttaa jokaisen yksityiskohdan työnkulkusi ja tyylisi\n    mukaiseksi.\n  ok: Aloitetaan!\n  review: >-\n    Jos pidät Seelen-käyttöliittymän käytöstä, harkitse arvostelun jättämistä\n    kauppaan – palautteesi auttaa projektia kasvamaan ja kehittymään.\n  title: Tervetuloa Seelen-käyttöliittymään!\nwidget:\n  enable: Ota tämä widget käyttöön\n  enable_for_monitor: Ota käyttöön tällä näytöllä\n  instances: Instanssit\nwm:\n  animations:\n    duration: Animaation kesto (MS)\n    ease_function: Animaation helpotustoiminto\n    enable: Ota ikkunan animaatiot käyttöön\n  author: Kirjoittaja\n  border:\n    enable: Ota ikkunan reuna käyttöön\n    offset: Rajan siirtyminen\n    width: Rajan leveys\n  description: Kuvaus\n  drag_behavior: Vetokäyttäytyminen\n  drag_behavior_options:\n    sort: Lajittele (järjestä ikkunat uudelleen vetäessäsi)\n    swap: Vaihda (vaihda ikkunoita vetämällä)\n  enable: Ota laatta -ikkunanhallinta käyttöön\n  layout: Layout\n  resize_delta: Muuttaa Delta (%)\n  space_between_containers: Konttien välinen tila\n  workspace_offset: Työtilojen siirtymä (marginaalit)\n  workspace_padding: Työtilat\n'yes': Kyllä\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/fr.yml",
    "content": "action:\n  confirm: Es-tu sûr?\n  confirm_body: Cette action ne peut pas être annulée.\napps_configurations:\n  app:\n    bindings: Raccourcis (notez que les deux options sont nécessaires)\n    category: Catégorie\n    category_placeholder: Aucun\n    monitor: Moniteur\n    monitor_placeholder: Aucun\n    name: Nom\n    ok_create: Créer\n    ok_edit: Mettre à jour\n    ok_readonly: Modifier en tant que nouveau\n    options:\n      NoInteractive: Pas d'interactivité\n      VdPinned: Afficher dans tous les espaces de travail\n      WmFloat: Twm - Commencer à flotter\n      WmForce: Twm - Forcer la gestion\n      WmUnmanage: Twm - Annuler la gestion\n    options_label: Options supplémentaires\n    title_create: Création de {{name}}\n    title_edit: Modification de {{nom}}\n    title_readonly: Visualisation de {{name}}\n    weg_options_label: Options Dock/Barre des tâches\n    wm_options_label: Options gestionnaire de fenêtres\n    workspace: Espace de travail\n    workspace_placeholder: Aucun\n  bundled_msg: >-\n    Ces configurations groupées ne sont pas modifiables et sont conçues pour\n    vous offrir la meilleure expérience sans personnalisation. Ils configurent\n    automatiquement pour vous les applications les plus courantes.\n  bundled_title: Configuration des applications groupées avec Seelen\n  confirm_delete: Êtes-vous sûr de vouloir supprimer cette/ces configuration(s) ?\n  confirm_delete_title: Confirmer la suppression\n  delete: Supprimer\n  export: Exporter\n  export_full: Exporter les paramètres par application\n  extra_info: >-\n    Seelen UI utilise un seul identifiant par application (premier correspondant\n    trouvé), donc l'ordre dans lequel ils sont spécifiés est important, le\n    dernier ajouté sera priorisée, car le tableau est trié par défaut du plus\n    récent au plus ancien.\n  identifier:\n    add_block: Ajouter un bloc\n    and: ET\n    id: Identifiant\n    kind: Identifier par\n    matching_strategy: Stratégie de correspondance\n    matching_strategy_option:\n      contains: Contient\n      ends_with: Se termine par\n      equals: Égal\n      regex: Expression régulière\n      starts_with: Commence par\n    negation: Annuler la correspondance\n    or: OU\n    remove: Supprimer le bloc\n    type:\n      class: Classe\n      exe: Exe\n      path: Chemin\n      title: Titre\n  import: Importer\n  import_full: Importer des paramètres par application\n  new: Nouveau\n  search: Rechercher\n  swap: Échanger\ncancel: Annuler\nclose: Fermer\ndelete: Supprimer\ndevtools:\n  app_folders: Dossiers de Seelen UI\n  custom_config_file: Charger un fichier de configuration personnalisé\n  data_folder: Dossier des données de l'application\n  enable: Activer les outils de développement\n  install_folder: Dossier d'installation\n  load: Charger\n  settings_file: Fichier de paramètres\n  simulate_perm:\n    label: Simuler une demande d'autorisation de widget\n    result_allowed: ✓ Autorisation accordée\n    result_denied: ✗ Autorisation refusée\n    trigger: Simuler\n    widget_id_placeholder: '@auteur/nom-widget'\nextras:\n  clear_icons: Effacer le cache des icônes du système\n  clear_icons_tooltip: >-\n    Un redémarrage peut être nécessaire pour que cela prenne effet sur tous les\n    widgets\n  exit: Quitter\n  links: Liens officiels\n  relaunch: Redémarrer Seelen UI\n  version: Version\n  version_fixed: >-\n    Les versions de l'application et de WebView2 Runtime sont corrigées. Cela\n    signifie que l'application ne recevra pas de mises à jour et que WebView2\n    Runtime ne sera pas automatiquement mis à jour avec les mises à jour\n    Windows.\ngeneral:\n  accent_color: Couleur d'accentuation\n  date_format: Format de la date\n  date_format_how_to: Comment écrire un format de date ?\n  hardware_acceleration: Accélération matérielle\n  hardware_acceleration_description: >-\n    La désactivation de l'accélération matérielle réduira l'utilisation de la\n    mémoire, mais peut entraîner des problèmes de performances. Peut être\n    désactivé en toute sécurité si vous n'utilisez pas de fonds d'écran animés.\n  icon_pack:\n    available: Packs d'icônes disponibles\n    selected: Packs d'icônes actifs\n  language: Langue\n  monday: Lundi\n  performance_mode:\n    on_battery: Sur la batterie\n    on_energy_saver: Sur l'énergie\n    options:\n      disabled: Désactivé\n      extreme: Extrême\n      minimal: Minimal\n    plugged: Bouchée ou charge\n  polling_interval: Intervalle d'interrogation du système\n  polling_interval_description: >-\n    À quelle fréquence (en secondes) Seelen UI vérifie les ressources système\n    telles que l'activité du processeur, de la RAM, du réseau et du disque. Un\n    nombre plus petit signifie des mises à jour plus fréquentes, mais une\n    utilisation des ressources légèrement plus élevée.\n  saturday: Samedi\n  start_of_week: Début de semaine\n  startup: Lancer au démarrage ?\n  sunday: Dimanche\n  theme:\n    available: Thèmes disponibles\n    selected: Thèmes actifs\nheader:\n  labels:\n    config: Configurations\n    developer: Pour les développeurs\n    extras: Extras\n    general: Général\n    home: Accueil\n    icon_pack_editor: Icônes en cache\n    iconpack: Packs d'icônes\n    monitors: Moniteurs\n    plugin: Plugins\n    resources: Ressources\n    shortcuts: Raccourcis\n    soundpack: Paquets de sons\n    specific_apps: Paramètres par application\n    theme: Thèmes\n    virtual_desk: Bureaux virtuels\n    wallpaper: Fonds d'écran\n    widget: Widgets\nhome:\n  new_resources: Nouvelles ressources\ninherit: Hériter\ninProgress: En cours...\ninsert: Insérer\nloading: Chargement...\nmiscellaneous: Divers\nmonitors_configurations:\n  label: Moniteur {{index}}\nmore: Plus\n'no': Non\nopen: Ouvrir\nquit: Quitter\nremove: Retirer\nreset_all_to_default: Rétablir les valeurs par défaut\nreset_to_default: Remise à la valeur par défaut\nresources:\n  app_outdated: >-\n    Cette ressource nécessite une version plus récente de Seelen UI pour\n    fonctionner correctement.\n  corrupted_wallpaper: \"Échec de l'extraction de la vignette\\_: format vidéo corrompu ou non pris en charge\"\n  delete: Supprimer les ressources\n  discover: Découvrez d'autres ressources\n  has_update: Une mise à jour est disponible\n  high_impact: Impact élevé sur les performances\n  import_wallpapers: Importer des fonds d'écran locaux\n  open_folder: Ouvrir le dossier des ressources\n  outdated: >-\n    Cette ressource a été conçue pour une ancienne version de Seelen UI et peut\n    ne pas fonctionner correctement.\n  see_on_website: Voir sur le site\nreview_request:\n  not_now: Pas maintenant\n  sure: Bien sûr!\n  title: \"Vous appréciez l'interface utilisateur de Seelen\\_?\"\nsave: Enregistrer\nsave_and_restart: Enregistrer et redémarrer\nsearch: Recherche\nsee_more: Voir plus\nshortcuts:\n  duplicate_error: Ce raccourci est dupliqué\n  enable: Activer le système de raccourcis intégrés\n  enable_tooltip: >-\n    Désactiver si vous implémentez votre propre système de raccourcis à l'aide\n    du client de l'interface utilisateur Seelen\n  labels:\n    create_new_workspace: Créer un nouvel espace de travail\n    cycle_stack_next: Pile de cycle Suivant\n    cycle_stack_prev: Pile de cycle Précédent\n    cycle_wallpaper_next: Passer au fond d'écran suivant\n    cycle_wallpaper_prev: Passer au fond d'écran précédent\n    decrease_height: Diminuer la hauteur\n    decrease_width: Diminuer la largeur\n    destroy_current_workspace: Détruire l'espace de travail actuel\n    focus_bottom: Focus Bottom\n    focus_latest: Focus plus tard\n    focus_left: Focus à gauche\n    focus_right: Focus à droite\n    focus_top: Focus top\n    increase_height: Augmenter la hauteur\n    increase_width: Augmenter la largeur\n    misc_force_quit: Force quitter\n    misc_force_restart: Forcer le redémarrage\n    misc_open_settings: Paramètres ouvrir\n    misc_toggle_lock_tracing: Togue de verrouillage (journaux)\n    misc_toggle_win_event_tracing: Togle Win Event Traçage (journaux)\n    move_to_workspace: Déplacer dans l'espace de travail {{0}}\n    move_window_down: Déplacer la fenêtre en bas\n    move_window_left: Déplacer la fenêtre à gauche\n    move_window_right: Déplacer la fenêtre à droite\n    move_window_up: Déplacer la fenêtre en haut\n    pause_tiling: Gestionnaire de fenêtres en carrelage en pause\n    reserve_bottom: Bottom\n    reserve_float: Réserver flotter\n    reserve_left: Réserve à gauche\n    reserve_right: Réserver à droite\n    reserve_stack: Pile de réserve\n    reserve_top: Réserve de réserve\n    restore_sizes: Restaurer les tailles\n    send_to_workspace: Envoyer à Workspace {{0}}\n    start_weg_app: Focus ou démarrer l'application {{0}}\n    switch_to_next_workspace: Passez à l'espace de travail suivant\n    switch_to_previous_workspace: Passez à l'espace de travail précédent\n    switch_workspace: Passez à Workspace {{0}}\n    toggle_float: Mode de flotteur de fenêtre à bascule\n    toggle_monocle: Mode monocle d'espace de travail à bascule\n  readonly_tooltip: Ceci est un raccourci en lecture seule\n  reset: Réinitialiser les valeurs par défaut\nsides:\n  bottom: Bas\n  left: Gauche\n  right: Droite\n  top: Haut\ntoolbar:\n  auto_hide: Masquer automatiquement\n  delay_to_hide: Délai avant de masquer\n  delay_to_show: Délai avant d'afficher\n  dock_side: Position\n  enable: Activer la barre d'outils sophistiquée\n  hide_mode:\n    always: Toujours\n    never: Jamais\n    on_overlap: Sur chevauchement\n  item_size: Taille de l'article\n  label: Barre d'outils\n  margin: Taille de la marge\n  padding: Taille du rembourrage\n  placeholder: {}\nupdate:\n  available: Mise à jour disponible !\n  channel: Canal de mise à jour\n  downloading: Téléchargement...\nwall:\n  backgrounds: Fonds d'écran\n  blur: Flou\n  cancel: Annuler\n  close: Fermer\n  collection_name: Nom de la collection\n  collections: Collections\n  contrast: Contraste\n  corrupted_wallpapers_message: \"Fonds d'écran corrompus, pensez à les supprimer\\_:\"\n  create: Créer\n  create_collection: Créer une collection\n  default_collection: Collection par défaut\n  delete_collection: Supprimer la collection\n  edit_collection: Modifier la collection\n  enable: Activer le gestionnaire de fonds d'écran\n  extend: Étendre le primaire\n  fit:\n    contain: Contenir\n    cover: Couverture\n    fill: Remplir\n  flipHorizontal: Retournement horizontal\n  flipVertical: Flip Vertical\n  generating_thumbnails: Générer des vignettes de fond d'écran\n  hours: heures\n  interval: Changer de fond d'écran toutes les\n  minutes: minutes\n  monitor_collection: Collection de moniteurs\n  multimonitor_behaviour: Comportement multi-écrans\n  muted: Couper le son de la vidéo\n  no_background: Liste vide, le fond d'écran du thème sera utilisé.\n  no_collections: >-\n    Aucune collection créée pour l'instant. Cliquez sur le bouton + pour en\n    créer un.\n  objectFit: Conforme à l'arrière-plan\n  objectPosition: Position d'arrière-plan\n  overlayColor: Couleur de la superposition\n  overlayMixBlendMode: Mode de mélange de la superposition\n  per_monitor: Par moniteur\n  playback: Vitesse de lecture\n  position:\n    bottom: Le fond\n    center: Centre\n    left: Gauche\n    right: Droit\n    top: Haut de page\n  processing_video: Traitement vidéo\n  random: Diaporama aléatoire\n  saturation: Saturation\n  seconds: secondes\n  select_collection: Sélectionnez une collection\n  thumbnail_generation_complete: Génération de vignettes terminée\n  thumbnail_generation_finished: La génération des vignettes s'est terminée avec succès\n  wallpaper_collection: Collection de papiers peints\n  wallpaper_settings: Paramètres du papier peint\n  withOverlay: Avec recouvrement\n  workspace_collections: Collections d'espaces de travail\nweg:\n  auto_hide: Masquage automatique\n  delay_to_hide: Délai avant masquage\n  delay_to_show: Délai avant affichage\n  dock_side: Position\n  enable: Activer le Dock/Barre des tâches\n  filtering: Filtrage des applications\n  gap: Écart\n  hide_mode:\n    always: Toujours\n    never: Jamais\n    on_overlap: Si chevauchement\n  items:\n    gap: Espace entre les éléments\n    label: Applications\n    pinned_visibility:\n      always: Toujours\n      label: Visibilité des éléments épinglés\n      when_primary: Lorsque le moniteur est principal\n    show_instance_counter: Compteur de fenêtres ouvertes\n    show_window_title: Afficher le titre de la fenêtre ouverte (uniquement à l'horizontale)\n    size: Taille des éléments\n    split_windows: Fenêtres divisées (un élément par fenêtre)\n    temporal_visibility:\n      all: Tous\n      label: Visibilité des éléments non épinglés\n      on_monitor: Sur le moniteur\n    visible_separators: Séparateurs visibles\n  label: Dock/Barre des tâches\n  margin: Marge\n  mode:\n    full_width: Pleine largeur\n    min_content: Aussi petit que possible\n  padding: Hauteur\n  show_end_task: Afficher la tâche finale dans la barre des tâches\n  width: Largeur\nwelcome:\n  give_a_review: Donner un avis\n  message: >-\n    Seelen UI est un environnement de bureau gratuit et open source pour\n    Windows. Ici, vous avez un contrôle total sur l'apparence et le comportement\n    de votre bureau, vous permettant de personnaliser chaque détail en fonction\n    de votre flux de travail et de votre style.\n  ok: \"Commençons\\_!\"\n  review: >-\n    Si vous aimez utiliser Seelen UI, pensez à laisser un avis sur la boutique :\n    vos commentaires aident le projet à se développer et à s'améliorer.\n  title: \"Bienvenue dans l'interface utilisateur de Seelen\\_!\"\nwidget:\n  enable: Activer ce widget\n  enable_for_monitor: Activer sur ce moniteur\n  instances: Instances\nwm:\n  animations:\n    duration: Durée d'animation (MS)\n    ease_function: Fonction d'assouplissement de l'animation\n    enable: Activer les animations de Window\n  author: Auteur\n  border:\n    enable: Activer la bordure de fenêtre\n    offset: Décalage de bordure\n    width: Largeur de la bordure\n  description: Description\n  drag_behavior: Comportement de traînée\n  drag_behavior_options:\n    sort: Trier (réorganiser les fenêtres en les faisant glisser)\n    swap: Swap (échanger les fenêtres à la fin du glissement)\n  enable: Activer le gestionnaire de fenêtres en mosaïque\n  layout: Disposition\n  resize_delta: Delta de redimensionnement (%)\n  space_between_containers: Espacement entre les conteneurs\n  workspace_offset: Décalage des bureaux virtuels (Marges)\n  workspace_padding: Rembourrage(Padding) des bureaux virtuels\n'yes': Oui\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/gu.yml",
    "content": "action:\n  confirm: તમને ખાતરી છે?\n  confirm_body: આ ક્રિયા પૂર્વવત્ કરી શકાતી નથી.\napps_configurations:\n  app:\n    bindings: બંધનકર્તા (નોંધો કે બંને વિકલ્પો જરૂરી છે)\n    category: શ્રેણી\n    category_placeholder: કોઈ નહિ\n    monitor: મોનીટર\n    monitor_placeholder: કોઈ નહિ\n    name: નામ\n    ok_create: બનાવો\n    ok_edit: અપડેટ કરો\n    ok_readonly: નવા તરીકે સંપાદિત કરો\n    options:\n      NoInteractive: કોઈ ઇન્ટરેક્ટિવ નથી\n      VdPinned: તમામ કાર્યસ્થળોમાં બતાવો\n      WmFloat: Twm - ફ્લોટિંગ શરૂ કરો\n      WmForce: Twm - ફોર્સ મેનેજ કરો\n      WmUnmanage: Twm - unmanage\n    options_label: વધારાના વિકલ્પો\n    title_create: '{{name}} બનાવી રહ્યાં છીએ'\n    title_edit: સંપાદન {{નામ}}\n    title_readonly: '{{name}} જોઈ રહ્યાં છીએ'\n    weg_options_label: ડોક/ટાસ્કબાર વિકલ્પો\n    wm_options_label: વિંડો મેનેજર વિકલ્પો\n    workspace: વર્કસ્પેસ\n    workspace_placeholder: કોઈ નહિ\n  bundled_msg: >-\n    આ બંડલ કરેલ રૂપરેખાંકનો સંપાદનયોગ્ય નથી અને કસ્ટમાઇઝેશન વિના તમને શ્રેષ્ઠ\n    અનુભવ પ્રદાન કરવા માટે રચાયેલ છે. તેઓ આપમેળે તમારા માટે સૌથી સામાન્ય\n    એપ્લિકેશનોને ગોઠવે છે.\n  bundled_title: સીલેન સાથે બંડલ કરેલ એપ્લિકેશન રૂપરેખા\n  confirm_delete: શું તમે ખરેખર આ ગોઠવણી/ઓ કાઢી નાખવા માંગો છો?\n  confirm_delete_title: કાઢી નાખવાની પુષ્ટિ કરો\n  delete: કાઢી નાખો\n  export: નિકાસ કરો\n  export_full: અરજી દ્વારા નિકાસ સેટિંગ્સ\n  extra_info: >-\n    સીલેન યુઆઈ એપ્લિકેશન દીઠ માત્ર એક ઓળખકર્તાનો ઉપયોગ કરે છે (પ્રથમ મેચ મળી)\n    તેથી કેવી રીતે વિશિષ્ટ છે તે ક્રમમાં મહત્વપૂર્ણ છે, નવીનતમ ઉમેરવામાં\n    પ્રાધાન્ય આપવામાં આવશે, કારણ કે નોંધ કારણ કે ટેબલ ડિફ default લ્ટથી નવીનતમથી\n    જૂની સુધી સ orted ર્ટ કરવામાં આવે છે.\n  identifier:\n    add_block: બ્લોક ઉમેરો\n    and: અને\n    id: ઓળખકર્તા\n    kind: દ્વારા ઓળખો\n    matching_strategy: મેચિંગ સ્ટ્રેટેજી\n    matching_strategy_option:\n      contains: સમાવે છે\n      ends_with: સાથે સમાપ્ત થાય છે\n      equals: સમકક્ષ\n      regex: નિયમિત અભિવ્યક્તિ\n      starts_with: થી શરૂ થાય છે\n    negation: નેગેટ મેચિંગ\n    or: અથવા\n    remove: બ્લોક કાઢી નાખો\n    type:\n      class: વર્ગ\n      exe: Exe\n      path: પાથ\n      title: શીર્ષક\n  import: આયાત કરો\n  import_full: અરજી દ્વારા આયાત સેટિંગ્સ\n  new: નવી\n  search: શોધો\n  swap: સ્વેપ\ncancel: રદ કરો\nclose: બંધ\ndelete: કાઢી નાખો\ndevtools:\n  app_folders: એપ્લિકેશન ફોલ્ડર્સ\n  custom_config_file: કસ્ટમ રૂપરેખા ફાઇલ લોડ કરો\n  data_folder: ડેટા ફોલ્ડર\n  enable: વિકાસકર્તા સાધનો સક્ષમ કરો\n  install_folder: ઇન્સ્ટોલેશન ફોલ્ડર\n  load: લોડ\n  settings_file: સેટિંગ્સ ફાઇલ\n  simulate_perm:\n    label: વિજેટ પરવાનગી વિનંતીનું અનુકરણ કરો\n    result_allowed: ✓ પરવાનગી આપવામાં આવી\n    result_denied: ✗ પરવાનગી નકારી\n    trigger: અનુકરણ કરો\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: સ્પષ્ટ સિસ્ટમ ચિહ્નો કેશ\n  clear_icons_tooltip: બધા વિજેટો પર સંપૂર્ણ અસર કરવા માટે ફરીથી પ્રારંભ કરવાની જરૂર પડી શકે છે\n  exit: છોડો/બહાર નીકળો\n  links: સત્તાવાર લિંક્સ\n  relaunch: ફરીથી લોંચ કરો\n  version: સંસ્કરણ\n  version_fixed: >-\n    એપ્લિકેશન અને WebView2 રનટાઇમ સંસ્કરણો નિશ્ચિત છે. આનો અર્થ એ છે કે\n    એપ્લિકેશન અપડેટ્સ પ્રાપ્ત કરશે નહીં અને WebView2 રનટાઇમ Windows અપડેટ્સ સાથે\n    આપમેળે અપડેટ થશે નહીં.\ngeneral:\n  accent_color: ઉચ્ચારણ રંગ\n  date_format: તારીખ ફોર્મેટ\n  date_format_how_to: તારીખ ફોર્મેટ કેવી રીતે લખવું?\n  hardware_acceleration: હાર્ડવેર પ્રવેગક\n  hardware_acceleration_description: >-\n    હાર્ડવેર પ્રવેગકને અક્ષમ કરવાથી મેમરીનો વપરાશ ઘટશે, પરંતુ પ્રદર્શન\n    સમસ્યાઓનું કારણ બની શકે છે. જો તમે લાઇવ વૉલપેપરનો ઉપયોગ ન કરો તો તેને અક્ષમ\n    કરવું સલામત છે.\n  icon_pack:\n    available: ઉપલબ્ધ આઇકન પેક\n    selected: સક્રિય આઇકન પેક્સ\n  language: ભાષા\n  monday: સોમવાર\n  performance_mode:\n    on_battery: બેટરી પર\n    on_energy_saver: Energy ર્જા બચતકાર પર\n    options:\n      disabled: અયોગ્ય\n      extreme: આત્યંતિક\n      minimal: પ્રમાણસર\n    plugged: પ્લગ અથવા ચાર્જિંગ\n  polling_interval: સિસ્ટમ મતદાન અંતરાલ\n  polling_interval_description: >-\n    સીલેન UI કેટલી વાર (સેકન્ડોમાં) સીપીયુ, રેમ, નેટવર્ક અને ડિસ્ક પ્રવૃત્તિ\n    જેવા સિસ્ટમ સંસાધનોની તપાસ કરે છે. નાની સંખ્યાનો અર્થ વધુ વારંવાર અપડેટ્સ,\n    પરંતુ સંસાધનનો થોડો વધારે ઉપયોગ.\n  saturday: શનિવાર\n  start_of_week: સપ્તાહની શરૂઆત\n  startup: સ્ટાર્ટઅપ પર ચલાવો?\n  sunday: રવિવાર\n  theme:\n    available: ઉપલબ્ધ થીમ્સ\n    selected: સક્રિય થીમ્સ\nheader:\n  labels:\n    config: ગોઠવણી\n    developer: વિકાસકર્તાઓ માટે\n    extras: વધારાની રકમ\n    general: જનરલ\n    home: ઘર\n    icon_pack_editor: કેશ ચિહ્નો\n    iconpack: ચિહ્ન\n    monitors: મોનિટર\n    plugin: તૃણ\n    resources: સાધનો\n    shortcuts: શૉર્ટકટ્સ\n    soundpack: સાચા પેક\n    specific_apps: અરજી દ્વારા સેટિંગ્સ\n    theme: થીમ\n    virtual_desk: વર્ચ્યુઅલ ડેસ્કટ .પ\n    wallpaper: વ wallpapers લપેપર્સ\n    widget: વિજેટો\nhome:\n  new_resources: નવી સાધનો\ninherit: વારસો મેળવવો\ninProgress: ચાલુ છે...\ninsert: દાખલ કરવું\nloading: લોડ કરી રહ્યું છે...\nmiscellaneous: પરચુરણ\nmonitors_configurations:\n  label: મોનિટર કરો {{અનુક્રમણિકા}}\nmore: વધારે\n'no': કોઈ\nopen: ખુલ્લા\nquit: છોડો\nremove: દૂર કરવું\nreset_all_to_default: બધાને ડિફ default લ્ટ મૂલ્યો પર ફરીથી સેટ કરો\nreset_to_default: ડિફ default લ્ટ મૂલ્ય પર ફરીથી સેટ કરો\nresources:\n  app_outdated: આ સંસાધનને યોગ્ય રીતે કાર્ય કરવા માટે સીલેન યુઆઈનું નવું સંસ્કરણ જરૂરી છે.\n  corrupted_wallpaper: થંબનેલ કાઢવામાં નિષ્ફળ - દૂષિત અથવા અસમર્થિત વિડિઓ ફોર્મેટ\n  delete: સાધન કા delો\n  discover: વધુ સંસાધનો શોધો\n  has_update: એક અપડેટ ઉપલબ્ધ છે\n  high_impact: કામગીરી પર ઉચ્ચ અસર\n  import_wallpapers: સ્થાનિક વ wallp લપેપર્સ આયાત કરો\n  open_folder: ખુલ્લા સંસાધન ફોલ્ડર\n  outdated: >-\n    આ સંસાધન સીલેન UI ના જૂના સંસ્કરણ માટે બનાવવામાં આવ્યું હતું અને કદાચ યોગ્ય\n    રીતે કાર્ય કરશે નહીં.\n  see_on_website: વેબસાઇટ પર જુઓ\nreview_request:\n  not_now: અત્યારે નહિ\n  sure: ચોક્કસ!\n  title: Seelen UI માણી રહ્યાં છો?\nsave: સાચવો\nsave_and_restart: સાચવો અને ફરીથી પ્રારંભ કરો\nsearch: શોધો\nsee_more: વધુ જુઓ\nshortcuts:\n  duplicate_error: આ શોર્ટકટ ડુપ્લિકેટ છે\n  enable: બિલ્ટ-ઇન શ shortc ર્ટકટ્સ સિસ્ટમને સક્ષમ કરો\n  enable_tooltip: >-\n    જો તમે સીલેન યુઆઈ ક્લાયંટનો ઉપયોગ કરીને તમારી પોતાની શ shortc ર્ટકટ્સ સિસ્ટમ\n    લાગુ કરશો તો અક્ષમ કરો\n  labels:\n    create_new_workspace: નવી વર્કસ્પેસ બનાવો\n    cycle_stack_next: ચક્ર સ્ટેક આગળ\n    cycle_stack_prev: સાયકલ સ્ટેક અગાઉ\n    cycle_wallpaper_next: આગલા વ wallp લપેપરમાં બદલો\n    cycle_wallpaper_prev: પાછલા વ wallp લપેપરમાં બદલો\n    decrease_height: ઘટાડો\n    decrease_width: પહોળાઈ ઘટાડવી\n    destroy_current_workspace: વર્તમાન વર્કસ્પેસનો નાશ કરો\n    focus_bottom: ફોકસ\n    focus_latest: ફોકસ ફોકસ\n    focus_left: ફોકસ\n    focus_right: ધ્યાન કેન્દ્રિત કરવું\n    focus_top: ફોકસ\n    increase_height: .ંચાઇમાં વધારો\n    increase_width: પહોળાઈમાં વધારો\n    misc_force_quit: છોડી દેવું\n    misc_force_restart: બળનો ફરીથી પ્રારંભ કરવો\n    misc_open_settings: ખુલ્લી સેટિંગ્સ\n    misc_toggle_lock_tracing: ટ g ગલ લ lock ક ટ્રેસિંગ (લોગ)\n    misc_toggle_win_event_tracing: ટ g ગલ વિન ઇવેન્ટ ટ્રેસિંગ (લોગ)\n    move_to_workspace: વર્કસ્પેસ {{0}} પર ખસેડો\n    move_window_down: વિંડોને નીચે ખસેડો\n    move_window_left: ડાબી બાજુ વિંડો ખસેડો\n    move_window_right: વિંડોને જમણે ખસેડો\n    move_window_up: વિંડોને ટોચ પર ખસેડો\n    pause_tiling: ટાઈલિંગ વિંડો મેનેજર થોભો\n    reserve_bottom: અનામત તળિયા\n    reserve_float: અનામત ફ્લોટ\n    reserve_left: અનામત બાકી\n    reserve_right: અનામત હક\n    reserve_stack: અનામત સ્ટેક\n    reserve_top: અનામત અનામત\n    restore_sizes: કદ પુન restore સ્થાપિત કરવું\n    send_to_workspace: વર્કસ્પેસ {{0}} પર મોકલો\n    start_weg_app: ધ્યાન કેન્દ્રિત કરો અથવા એપ્લિકેશન પ્રારંભ કરો {{0}}\n    switch_to_next_workspace: આગળના કાર્યસ્થળ પર સ્વિચ કરો\n    switch_to_previous_workspace: પાછલા વર્કસ્પેસ પર સ્વિચ કરો\n    switch_workspace: વર્કસ્પેસ પર સ્વિચ કરો {{0}}\n    toggle_float: ટ g ગલ વિંડો ફ્લોટ મોડ\n    toggle_monocle: ટ g ગલ વર્કસ્પેસ મોનોક્લ મોડ\n  readonly_tooltip: આ ફક્ત એક વાંચવા માટે શોર્ટકટ છે\n  reset: ડિફોલ્ટ પર ફરીથી સેટ કરો\nsides:\n  bottom: તળિયે\n  left: ડાબી\n  right: અધિકાર\n  top: ટોચ\ntoolbar:\n  auto_hide: સ્વત hideપ\n  delay_to_hide: છુપાવવામાં વિલંબ\n  delay_to_show: બતાવવામાં વિલંબ\n  dock_side: પદ\n  enable: ફેન્સી ટૂલબાર સક્ષમ કરો\n  hide_mode:\n    always: હંમેશા\n    never: ક્યારેય નહીં\n    on_overlap: ઓવરલેપ પર\n  item_size: આઇટમનું કદ\n  label: વમળ\n  margin: માર્જિન માપ\n  padding: ગાદી કદ\n  placeholder: {}\nupdate:\n  available: અપડેટ ઉપલબ્ધ!\n  channel: અપડેટ\n  downloading: ડાઉનલોડ ...\nwall:\n  backgrounds: વ wallpapers લપેપર્સ\n  blur: અસ્પષ્ટતા\n  cancel: રદ કરો\n  close: બંધ કરો\n  collection_name: સંગ્રહનું નામ\n  collections: સંગ્રહો\n  contrast: વિપરીત\n  corrupted_wallpapers_message: 'દૂષિત વૉલપેપર્સ, આને કાઢી નાખવાનું વિચારો:'\n  create: બનાવો\n  create_collection: સંગ્રહ બનાવો\n  default_collection: ડિફૉલ્ટ કલેક્શન\n  delete_collection: સંગ્રહ કાઢી નાખો\n  edit_collection: સંગ્રહ સંપાદિત કરો\n  enable: વ wallp લપેપર મેનેજરને સક્ષમ કરો\n  extend: પ્રાથમિક વિસ્તારો\n  fit:\n    contain: સમાવવું\n    cover: આવરણ\n    fill: ભરવું\n  flipHorizontal: આડું\n  flipVertical: Verીટી પચે\n  generating_thumbnails: વૉલપેપર થંબનેલ્સ જનરેટ કરી રહ્યાં છીએ\n  hours: સમય\n  interval: દરેક વ wallp લપેપર બદલો\n  minutes: પ્રકાર\n  monitor_collection: મોનિટર કલેક્શન\n  multimonitor_behaviour: મલ્ટિમોનિટર બિહેવિયર\n  muted: વિડિઓ ઑડિઓ મ્યૂટ કરો\n  no_background: તેના બદલે થીમની પૃષ્ઠભૂમિનો ઉપયોગ કરીને ખાલી સ્લાઇડશો.\n  no_collections: હજી સુધી કોઈ સંગ્રહ બનાવ્યો નથી. એક બનાવવા માટે + બટન પર ક્લિક કરો.\n  objectFit: પૃષ્ઠભૂમિ\n  objectPosition: પૃષ્ઠભૂમિ\n  overlayColor: Overંચો રંગ\n  overlayMixBlendMode: ઓવરલે મિક્સ બ્લેન્ડ મોડ\n  per_monitor: મોનિટર દીઠ\n  playback: પ્લેબેક ગતિ\n  position:\n    bottom: તળિયે\n    center: કેન્દ્ર\n    left: ડાબી બાજુ\n    right: અધિકાર\n    top: ટોચ\n  processing_video: વિડિઓ પર પ્રક્રિયા કરી રહ્યું છે\n  random: સ્લાઇડશોને રેન્ડમાઇઝ કરો\n  saturation: સચોટતા\n  seconds: સેકન્ડ\n  select_collection: સંગ્રહ પસંદ કરો\n  thumbnail_generation_complete: થંબનેલ જનરેશન પૂર્ણ\n  thumbnail_generation_finished: થંબનેલ જનરેશન સફળતાપૂર્વક સમાપ્ત થયું\n  wallpaper_collection: વૉલપેપર કલેક્શન\n  wallpaper_settings: વૉલપેપર સેટિંગ્સ\n  withOverlay: ઓવરલે સાથે\n  workspace_collections: વર્કસ્પેસ સંગ્રહો\nweg:\n  auto_hide: સ્વતઃ છુપાવો\n  delay_to_hide: છુપાવવામાં વિલંબ\n  delay_to_show: બતાવવામાં વિલંબ\n  dock_side: પદ\n  enable: ડોક/ટાસ્કબારને સક્ષમ કરો\n  filtering: બાબત\n  gap: ગેપ\n  hide_mode:\n    always: હંમેશા\n    never: ક્યારેય નહીં\n    on_overlap: ઓવરલેપ પર\n  items:\n    gap: વસ્તુઓ વચ્ચે જગ્યા\n    label: વસ્તુઓ\n    pinned_visibility:\n      always: હંમેશા\n      label: પિન કરેલી વસ્તુઓની દૃશ્યતા\n      when_primary: જ્યારે મોનિટર પ્રાથમિક છે\n    show_instance_counter: ખુલ્લા વિંડોઝ કાઉન્ટર બતાવો\n    show_window_title: ખુલ્લી વિંડો શીર્ષક બતાવો (ફક્ત આડી)\n    size: વસ્તુનું કદ\n    split_windows: વિન્ડોઝ સ્પ્લિટ કરો (વિન્ડો દીઠ એક આઇટમ)\n    temporal_visibility:\n      all: બધા\n      label: અનપિન કરેલ આઇટમ્સની દૃશ્યતા\n      on_monitor: મોનિટર પર\n    visible_separators: દૃશ્યમાન વિભાજક\n  label: ડોક/ટાસ્કબાર\n  margin: માર્જિન\n  mode:\n    full_width: પૂર્ણ સ્ક્રીનની પહોળાઈ\n    min_content: બની શકે તેટલું નાનું\n  padding: ગાદી\n  show_end_task: ટાસ્કબારમાં અંતિમ કાર્ય બતાવો\n  width: પહોળાઈ\nwelcome:\n  give_a_review: એક સમીક્ષા આપો\n  message: >-\n    સીલેન UI એ Windows માટે ફ્રી અને ઓપન સોર્સ ડેસ્કટોપ એન્વાર્યમેન્ટ છે. અહીં\n    તમારું ડેસ્કટૉપ કેવું દેખાય છે અને કેવી રીતે વર્તે છે તેના પર તમારું સંપૂર્ણ\n    નિયંત્રણ છે, જે તમને તમારા વર્કફ્લો અને શૈલી સાથે મેળ ખાતી દરેક વિગતને\n    કસ્ટમાઇઝ કરવાની મંજૂરી આપે છે.\n  ok: ચાલો શરૂ કરીએ!\n  review: >-\n    જો તમને Seelen UI નો ઉપયોગ કરવાનો આનંદ આવે, તો સ્ટોર પર સમીક્ષા કરવાનું\n    વિચારો — તમારો પ્રતિસાદ પ્રોજેક્ટને વધવા અને સુધારવામાં મદદ કરે છે.\n  title: Seelen UI માં આપનું સ્વાગત છે!\nwidget:\n  enable: આ વિજેટને સક્ષમ કરો\n  enable_for_monitor: આ મોનિટર પર સક્ષમ કરો\n  instances: દાખલા\nwm:\n  animations:\n    duration: એનિમેશન અવધિ (એમએસ)\n    ease_function: એનિમેશન સરળ કાર્ય\n    enable: વિંડોના એનિમેશનને સક્ષમ કરો\n  author: લેખક\n  border:\n    enable: વિન્ડોની બોર્ડર સક્ષમ કરો\n    offset: બોર્ડર ઓફસેટ\n    width: સરહદની પહોળાઈ\n  description: વર્ણન\n  drag_behavior: ખેંચો વર્તન\n  drag_behavior_options:\n    sort: સૉર્ટ કરો (ડ્રેગ કરતી વખતે વિંડોઝને ફરીથી ગોઠવો)\n    swap: સ્વેપ (ડ્રેગ એન્ડ પર વિન્ડોઝ સ્વેપ)\n  enable: ટાઇલિંગ વિંડો મેનેજરને સક્ષમ કરો\n  layout: લેઆઉટ\n  resize_delta: ડેલ્ટાનું કદ બદલો (%)\n  space_between_containers: કન્ટેનર વચ્ચે જગ્યા\n  workspace_offset: વર્કસ્પેસ ઓફસેટ (માર્જિન)\n  workspace_padding: વર્કસ્પેસ પેડિંગ\n'yes': હા\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/he.yml",
    "content": "action:\n  confirm: אתה בטוח?\n  confirm_body: לא ניתן לבטל פעולה זו.\napps_configurations:\n  app:\n    bindings: כריכה (שימו לב לשתי האפשרויות נדרשות)\n    category: קטגוריה\n    category_placeholder: אף אחד\n    monitor: צג\n    monitor_placeholder: אף אחד\n    name: שם\n    ok_create: לִיצוֹר\n    ok_edit: עדכון\n    ok_readonly: ערוך כחדש\n    options:\n      NoInteractive: אין אינטראקטיבי\n      VdPinned: הצג בכל סביבות העבודה\n      WmFloat: Twm - התחל לצוף\n      WmForce: Twm - ניהול כוח\n      WmUnmanage: Twm - בטל ניהול\n    options_label: אפשרויות נוספות\n    title_create: יצירת {{name}}\n    title_edit: עריכה {{name}}\n    title_readonly: צפייה {{name}}\n    weg_options_label: אפשרויות עגינה/שורת משימות\n    wm_options_label: אפשרויות מנהל חלונות\n    workspace: סביבת עבודה\n    workspace_placeholder: אף אחד\n  bundled_msg: >-\n    תצורות מצורפות אלה אינן ניתנות לעריכה ונועדו לספק לך את החוויה הטובה ביותר\n    ללא התאמה אישית. הם מגדירים באופן אוטומטי את היישומים הנפוצים ביותר עבורך.\n  bundled_title: תצורת אפליקציה מצורפת עם Seelen\n  confirm_delete: האם אתה בטוח שברצונך למחוק תצורה/ים אלה?\n  confirm_delete_title: אשר מחק\n  delete: לִמְחוֹק\n  export: יְצוּא\n  export_full: הגדרות ייצוא לפי יישום\n  extra_info: >-\n    ממשק המשתמש של Seelen משתמש רק במזהה אחד לכל אפליקציה (נמצאה התאמה ראשונה),\n    כך שסדר האופן שבו הם מוגדרים הוא חשוב, העדיפות האחרונה שנוספה תהיה בסדר\n    עדיפויות, שים לב שהטבלה ממוינת כברירת מחדל מהאחרון לישנה.\n  identifier:\n    add_block: הוסף בלוק\n    and: וכן\n    id: מזהה\n    kind: לזהות על ידי\n    matching_strategy: אסטרטגיה תואמת\n    matching_strategy_option:\n      contains: מכיל\n      ends_with: מסתיים ב\n      equals: שווה\n      regex: ביטוי רגולרי\n      starts_with: מתחיל עם\n    negation: שולל התאמה\n    or: או\n    remove: מחק בלוק\n    type:\n      class: מחלקה\n      exe: Exe\n      path: נָתִיב\n      title: כּוֹתֶרֶת\n  import: יְבוּא\n  import_full: ייבא הגדרות לפי יישום\n  new: חָדָשׁ\n  search: לחפש\n  swap: לְהַחלִיף\ncancel: לְבַטֵל\nclose: לִסְגוֹר\ndelete: לִמְחוֹק\ndevtools:\n  app_folders: תיקיות אפליקציות\n  custom_config_file: טען קובץ תצורה מותאם אישית\n  data_folder: תיקיית נתונים\n  enable: אפשר כלי מפתחים\n  install_folder: תיקיית התקנה\n  load: טען\n  settings_file: קובץ הגדרות\n  simulate_perm:\n    label: הדמיית בקשת הרשאת יישומון\n    result_allowed: ✓ הרשאה ניתנה\n    result_denied: ✗ הרשאה נדחתה\n    trigger: לְחַקוֹת\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: מטמון סמלי מערכת נקה\n  clear_icons_tooltip: ניתן להידרש להפעלה מחדש כדי להיכנס לתוקף באופן מלא על כל הווידג'טים\n  exit: צא\n  links: קישורים רשמיים\n  relaunch: השקה מחדש\n  version: גרסה\n  version_fixed: >-\n    גרסאות היישום ו-WebView2 Runtime קבועות. המשמעות היא שהאפליקציה לא תקבל\n    עדכונים ו- WebView2 Runtime לא יעודכן אוטומטית עם עדכוני Windows.\ngeneral:\n  accent_color: צבע הדגשה\n  date_format: פורמט תאריך\n  date_format_how_to: איך כותבים פורמט תאריך?\n  hardware_acceleration: האצת חומרה\n  hardware_acceleration_description: >-\n    השבתת האצת החומרה תפחית את השימוש בזיכרון, אך עלולה לגרום לבעיות ביצועים.\n    בטוח להשבית אם לא תשתמש בטפטים חיים.\n  icon_pack:\n    available: חבילות אייקונים זמינות\n    selected: ערכות אייקונים פעילות\n  language: שפה\n  monday: יוֹם שֵׁנִי\n  performance_mode:\n    on_battery: על סוללה\n    on_energy_saver: על חוסך אנרגיה\n    options:\n      disabled: נָכֶה\n      extreme: קיצוני\n      minimal: מִינִימָלִי\n    plugged: מחובר או טעינה\n  polling_interval: מרווח סקרים של המערכת\n  polling_interval_description: >-\n    באיזו תדירות (בשניות) ממשק המשתמש של Seelen בודק משאבי מערכת כמו מעבד,\n    זיכרון RAM, רשת ודיסק פעילות. מספר קטן יותר פירושו עדכונים תכופים יותר, אך\n    שימוש מעט יותר במשאבים.\n  saturday: שַׁבָּת\n  start_of_week: תחילת השבוע\n  startup: לרוץ בהפעלה?\n  sunday: יוֹם רִאשׁוֹן\n  theme:\n    available: ערכות נושא זמינות\n    selected: ערכות נושא פעילות\nheader:\n  labels:\n    config: תצורות\n    developer: למפתחים\n    extras: תוספות\n    general: כללי\n    home: בַּיִת\n    icon_pack_editor: אייקונים במטמון\n    iconpack: חבילות אייקון\n    monitors: צגים\n    plugin: תוספים\n    resources: אֶמְצָעִי\n    shortcuts: קיצורי דרך\n    soundpack: חבילות סאונד\n    specific_apps: הגדרות לפי יישום\n    theme: נושאים\n    virtual_desk: שולחנות עבודה וירטואליים\n    wallpaper: טפטים\n    widget: ווידג'טים\nhome:\n  new_resources: משאבים חדשים\ninherit: ירושה\ninProgress: בתהליך...\ninsert: הכנס\nloading: טוען...\nmiscellaneous: שונות\nmonitors_configurations:\n  label: צג {{index}}\nmore: יוֹתֵר\n'no': לֹא\nopen: לִפְתוֹחַ\nquit: לְהַפְסִיק\nremove: לְהַסִיר\nreset_all_to_default: אפס את כל ערכי ברירת המחדל\nreset_to_default: אפס לערך ברירת המחדל\nresources:\n  app_outdated: משאב זה דורש גרסה חדשה יותר של ממשק המשתמש של Seelen כדי לעבוד כראוי.\n  corrupted_wallpaper: חילוץ התמונה הממוזערת נכשל - פורמט וידאו פגום או לא נתמך\n  delete: מחק משאב\n  discover: לגלות משאבים נוספים\n  has_update: ניתן לקבל עדכון\n  high_impact: השפעה רבה על הביצועים\n  import_wallpapers: ייבא טפטים מקומיים\n  open_folder: פתיחת תיקיית משאבים\n  outdated: משאב זה תוכנן לגרסה ישנה יותר של ממשק המשתמש של Seelen ואולי לא יעבוד כראוי.\n  see_on_website: ראה באתר\nreview_request:\n  not_now: לא עכשיו\n  sure: בַּטוּחַ!\n  title: נהנה ממשק המשתמש של Seelen?\nsave: להציל\nsave_and_restart: שמור והפעלה מחדש\nsearch: לְחַפֵּשׂ\nsee_more: ראה עוד\nshortcuts:\n  duplicate_error: קיצור זה משוכפל\n  enable: אפשר מערכת קיצורי דרך מובנים\n  enable_tooltip: השבת אם תיישם מערכת קיצורי דרך משלך באמצעות לקוח ממשק המשתמש של Seelen\n  labels:\n    create_new_workspace: צור סביבת עבודה חדשה\n    cycle_stack_next: מחזור מחזור הבא\n    cycle_stack_prev: מחזור מחזור קודם\n    cycle_wallpaper_next: שנה לטפט הבא\n    cycle_wallpaper_prev: שנה לטפט הקודם\n    decrease_height: ירידה בגובה\n    decrease_width: ירידה ברוחב\n    destroy_current_workspace: להרוס את סביבת העבודה הנוכחית\n    focus_bottom: מיקוד תחתון\n    focus_latest: התמקד האחרון\n    focus_left: פוקוס שמאלה\n    focus_right: להתמקד נכון\n    focus_top: מיקוד למעלה\n    increase_height: הגדל את הגובה\n    increase_width: הגדל את הרוחב\n    misc_force_quit: כוח התפטר\n    misc_force_restart: הכוח להפעיל מחדש\n    misc_open_settings: פתח הגדרות\n    misc_toggle_lock_tracing: מעקב אחר נעילת נעילה (יומנים)\n    misc_toggle_win_event_tracing: מעקב אחר WIN WIN (יומנים)\n    move_to_workspace: עברו לסביבת עבודה {{0}}\n    move_window_down: העבירו את החלון לתחתית\n    move_window_left: העבר חלון שמאלה\n    move_window_right: העבירו את החלון לימין\n    move_window_up: העבר חלון למעלה\n    pause_tiling: השהה את מנהל החלונות\n    reserve_bottom: שמורה בתחתית\n    reserve_float: שמורה לצוף\n    reserve_left: רזרבה שמאלה\n    reserve_right: שמור ימינה\n    reserve_stack: ערימת מילואים\n    reserve_top: עליון מילואים\n    restore_sizes: שחזור גדלים\n    send_to_workspace: שלח לסביבת עבודה {{0}}\n    start_weg_app: מיקוד או התחל יישום {{0}}\n    switch_to_next_workspace: עבור לסביבת העבודה הבאה\n    switch_to_previous_workspace: עבור לסביבת העבודה הקודמת\n    switch_workspace: עברו לסביבת עבודה {{0}}\n    toggle_float: החלף מצב צף חלון\n    toggle_monocle: החלף את מצב המונוקל של סביבת העבודה\n  readonly_tooltip: זהו קיצור דרך לקריאה בלבד\n  reset: אפס לברירות מחדל\nsides:\n  bottom: למטה\n  left: שמאל\n  right: ימין\n  top: למעלה\ntoolbar:\n  auto_hide: הסתר אוטומטי\n  delay_to_hide: השהה הסתרה\n  delay_to_show: השהה הצגה\n  dock_side: מצב\n  enable: אפשר\n  hide_mode:\n    always: תמיד\n    never: לעולם לא\n    on_overlap: על חפיפה\n  item_size: גודל פריט\n  label: סרגל כלים\n  margin: גודל השוליים\n  padding: גודל ריפוד\n  placeholder: {}\nupdate:\n  available: עדכון זמין!\n  channel: ערוץ עדכונים\n  downloading: מוריד ...\nwall:\n  backgrounds: טפטים\n  blur: לְטַשׁטֵשׁ\n  cancel: לְבַטֵל\n  close: לִסְגוֹר\n  collection_name: שם האוסף\n  collections: אוספים\n  contrast: לְהַשְׁווֹת\n  corrupted_wallpapers_message: 'רקעים פגומים, שקול למחוק את אלה:'\n  create: לִיצוֹר\n  create_collection: צור אוסף\n  default_collection: אוסף ברירת מחדל\n  delete_collection: מחק אוסף\n  edit_collection: ערוך אוסף\n  enable: אפשר מנהל טפטים\n  extend: הארך את היסוד\n  fit:\n    contain: לְהַכִיל\n    cover: לְכַסוֹת\n    fill: לְמַלֵא\n  flipHorizontal: הפוך אופקי\n  flipVertical: הפוך אנכי\n  generating_thumbnails: יצירת תמונות ממוזערות של טפטים\n  hours: שעות\n  interval: החלף טפטים כל\n  minutes: דקות\n  monitor_collection: אוסף מוניטור\n  multimonitor_behaviour: התנהגות מולטימוניטור\n  muted: השתקת אודיו של וידאו\n  no_background: מצגת שקופיות ריקה, באמצעות הרקע של הנושא במקום זאת.\n  no_collections: עדיין לא נוצרו אוספים. לחץ על הלחצן + כדי ליצור אחד.\n  objectFit: כושר רקע\n  objectPosition: מיקום רקע\n  overlayColor: צבע שכבה\n  overlayMixBlendMode: מצב תערובת תערובת שכבה\n  per_monitor: לכל מוניטור\n  playback: מהירות ההפעלה\n  position:\n    bottom: תַחתִית\n    center: מֶרְכָּז\n    left: שְׁמֹאל\n    right: יָמִינָה\n    top: רֹאשׁ\n  processing_video: מעבד וידאו\n  random: מצגת שקופיות אקראית\n  saturation: רִוּוּי\n  seconds: שניות\n  select_collection: בחר אוסף\n  thumbnail_generation_complete: יצירת תמונות ממוזערות הושלמה\n  thumbnail_generation_finished: יצירת התמונות הממוזערות הסתיים בהצלחה\n  wallpaper_collection: אוסף טפטים\n  wallpaper_settings: הגדרות טפט\n  withOverlay: עם שכבת -על\n  workspace_collections: אוספי סביבת עבודה\nweg:\n  auto_hide: הסתר אוטומטי\n  delay_to_hide: עיכוב להסתיר\n  delay_to_show: עיכוב להצגה\n  dock_side: מַצָב\n  enable: הפעל את המזח/שורת המשימות\n  filtering: סינון פריטים\n  gap: פער\n  hide_mode:\n    always: תָמִיד\n    never: לעולם לא\n    on_overlap: על חפיפה\n  items:\n    gap: רווח בין פריטים\n    label: פריטים\n    pinned_visibility:\n      always: תמיד\n      label: נראות פריטים מוצמדים\n      when_primary: כאשר המוניטור הוא ראשי\n    show_instance_counter: הצג דלפק Windows פתוח\n    show_window_title: הצג כותרת חלון פתוחה (אופקית בלבד)\n    size: גודל פריט\n    split_windows: חלונות מפוצלים (פריט אחד לכל חלון)\n    temporal_visibility:\n      all: כל\n      label: נראות פריטים לא מוצמדים\n      on_monitor: על מוניטור\n    visible_separators: מפרידים גלויים\n  label: שורת המזח/המשימות\n  margin: שולים\n  mode:\n    full_width: רוחב מסך מלא\n    min_content: קטן ככל שניתן\n  padding: ריפוד\n  show_end_task: הצג את משימת הסיום בשורת המשימות\n  width: רוחב\nwelcome:\n  give_a_review: תן סקירה\n  message: >-\n    ממשק המשתמש של Seelen הוא סביבת שולחן עבודה חינמית וקוד פתוח עבור Windows.\n    כאן יש לך שליטה מלאה על איך שולחן העבודה שלך נראה ומתנהג, מה שמאפשר לך\n    להתאים אישית כל פרט כך שיתאים לזרימת העבודה ולסגנון שלך.\n  ok: בואו נתחיל!\n  review: >-\n    אם אתה נהנה להשתמש בממשק המשתמש של Seelen, שקול להשאיר ביקורת בחנות - המשוב\n    שלך עוזר לפרויקט לצמוח ולהשתפר.\n  title: ברוכים הבאים לממשק המשתמש של Seelen!\nwidget:\n  enable: אפשר יישומון זה\n  enable_for_monitor: אפשר בצג זה\n  instances: מקרים\nwm:\n  animations:\n    duration: משך אנימציה (MS)\n    ease_function: הפונקציה הקלת האנימציה\n    enable: אפשר אנימציות של חלון\n  author: מחבר\n  border:\n    enable: אפשר את גבול החלון\n    offset: קיזוז גבול\n    width: רוחב גבול\n  description: תיאור\n  drag_behavior: התנהגות גרירה\n  drag_behavior_options:\n    sort: מיון (סדר מחדש את החלונות תוך כדי גרירה)\n    swap: החלפה (החלפה של חלונות בקצה הגרירה)\n  enable: אפשר מנהל חלונות אריחים\n  layout: מערך\n  resize_delta: שינוי גודל דלתא (%)\n  space_between_containers: רווח בין מיכלים\n  workspace_offset: הסטת שולי סביבת עבודה\n  workspace_padding: ריפוד סביבת עבודה\n'yes': כֵּן\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/hi.yml",
    "content": "action:\n  confirm: क्या आपको यकीन है?\n  confirm_body: इस एक्शन को वापस नहीं किया जा सकता।\napps_configurations:\n  app:\n    bindings: बाइंडिंग (ध्यान दें कि दोनों विकल्पों की आवश्यकता है)\n    category: वर्ग\n    category_placeholder: कोई नहीं\n    monitor: निगरानी करना\n    monitor_placeholder: कोई नहीं\n    name: नाम\n    ok_create: बनाएं\n    ok_edit: अद्यतन\n    ok_readonly: नए के रूप में संपादित करें\n    options:\n      NoInteractive: कोई इंटरैक्टिव नहीं\n      VdPinned: सभी कार्यस्थानों में दिखाएं\n      WmFloat: Twm - तैरना प्रारंभ करें\n      WmForce: Twm - फोर्स मैनेज\n      WmUnmanage: Twm - अनमैनेज\n    options_label: अतिरिक्त विकल्प\n    title_create: '{{नाम}} बनाना'\n    title_edit: संपादन {{नाम}}\n    title_readonly: '{{नाम}} देखना'\n    weg_options_label: डॉक/टास्कबार विकल्प\n    wm_options_label: विंडो मैनेजर विकल्प\n    workspace: कार्यस्थान\n    workspace_placeholder: कोई नहीं\n  bundled_msg: >-\n    ये बंडल किए गए कॉन्फ़िगरेशन संपादन योग्य नहीं हैं और आपको अनुकूलन के बिना\n    सबसे अच्छा अनुभव प्रदान करने के लिए डिज़ाइन किए गए हैं। वे स्वचालित रूप से\n    आपके लिए सबसे आम अनुप्रयोगों को कॉन्फ़िगर करते हैं।\n  bundled_title: ऐप कॉन्फ़िगरेशन सेलेन के साथ बंडल किया गया\n  confirm_delete: क्या आप सुनिश्चित हैं कि आप इस कॉन्फ़िगरेशन/एस को हटाना चाहते हैं?\n  confirm_delete_title: हटाने की पुष्टि करें\n  delete: मिटाना\n  export: निर्यात\n  export_full: आवेदन द्वारा निर्यात सेटिंग्स\n  extra_info: >-\n    Seelen UI प्रति ऐप केवल एक पहचानकर्ता का उपयोग करता है (पहला मैच मिला) इसलिए\n    कि कैसे विशिष्ट है, इसका क्रम महत्वपूर्ण है, नवीनतम जोड़ा को प्राथमिकता दी\n    जाएगी, क्योंकि ध्यान दें कि तालिका को डिफ़ॉल्ट रूप से नवीनतम से पुराने तक\n    सॉर्ट किया गया है।\n  identifier:\n    add_block: ब्लॉक जोड़ें\n    and: और\n    id: पहचानकर्ता\n    kind: से पहचानना\n    matching_strategy: मिलान रणनीति\n    matching_strategy_option:\n      contains: रोकना\n      ends_with: इसी के साथ समाप्त होता है\n      equals: बराबर\n      regex: नियमित अभिव्यक्ति\n      starts_with: इसके साथ आरंभ होता है\n    negation: नकारात्मक मिलान\n    or: या\n    remove: ब्लॉक हटाएं\n    type:\n      class: कक्षा\n      exe: प्रोग्राम फ़ाइल\n      path: पथ\n      title: शीर्षक\n  import: आयात\n  import_full: आवेदन द्वारा आयात सेटिंग्स\n  new: नया\n  search: खोज\n  swap: बदलना\ncancel: रद्द करना\nclose: बंद करना\ndelete: मिटाना\ndevtools:\n  app_folders: ऐप फ़ोल्डर्स\n  custom_config_file: कस्टम कॉन्फ़िगर फ़ाइल लोड करें\n  data_folder: डेटा फ़ोल्डर\n  enable: डेवलपर टूल सक्षम करें\n  install_folder: स्थापना फ़ोल्डर\n  load: भार\n  settings_file: सेटिंग संचिका\n  simulate_perm:\n    label: विजेट अनुमति अनुरोध का अनुकरण करें\n    result_allowed: ✓ अनुमति दी गई\n    result_denied: ✗ अनुमति अस्वीकृत\n    trigger: अनुकरण\n    widget_id_placeholder: '@लेखक/विजेट-नाम'\nextras:\n  clear_icons: स्पष्ट प्रणाली आइकन कैश\n  clear_icons_tooltip: >-\n    सभी विजेट पर पूरी तरह से प्रभावी होने के लिए एक पुनरारंभ की आवश्यकता हो सकती\n    है\n  exit: छोड़ देना/बाहर निकलना\n  links: आधिकारिक संबंध\n  relaunch: 'पुन: लॉन्च'\n  version: संस्करण\n  version_fixed: >-\n    एप्लिकेशन और WebView2 रनटाइम संस्करण ठीक हो गए हैं। इसका मतलब यह है कि\n    एप्लिकेशन को अपडेट प्राप्त नहीं होंगे और WebView2 रनटाइम स्वचालित रूप से\n    विंडोज अपडेट के साथ अपडेट नहीं होगा।\ngeneral:\n  accent_color: स्वरोंका रंग\n  date_format: तारिख का प्रारूप\n  date_format_how_to: दिनांक प्रारूप कैसे लिखें?\n  hardware_acceleration: हार्डवेयर एक्सिलरेशन\n  hardware_acceleration_description: >-\n    हार्डवेयर एक्सेलेरेशन को अक्षम करने से मेमोरी का उपयोग कम हो जाएगा, लेकिन\n    प्रदर्शन संबंधी समस्याएं पैदा हो सकती हैं। यदि आप लाइव वॉलपेपर का उपयोग नहीं\n    करेंगे तो इसे अक्षम करना सुरक्षित है।\n  icon_pack:\n    available: उपलब्ध चिह्न पैक\n    selected: सक्रिय चिह्न पैक\n  language: भाषा\n  monday: सोमवार\n  performance_mode:\n    on_battery: बैटरी पर\n    on_energy_saver: ऊर्जा सेवर पर\n    options:\n      disabled: अक्षम\n      extreme: चरम\n      minimal: न्यूनतम\n    plugged: प्लग या चार्जिंग\n  polling_interval: सिस्टम मतदान अंतराल\n  polling_interval_description: >-\n    सीलेन यूआई कितनी बार (सेकंड में) सीपीयू, रैम, नेटवर्क और डिस्क गतिविधि जैसे\n    सिस्टम संसाधनों की जांच करता है। छोटी संख्या का अर्थ है अधिक लगातार अपडेट,\n    लेकिन थोड़ा अधिक संसाधन उपयोग।\n  saturday: शनिवार\n  start_of_week: सप्ताह की शुरुआत\n  startup: स्टार्टअप पर चलाएं?\n  sunday: रविवार\n  theme:\n    available: उपलब्ध थीम्स\n    selected: सक्रिय विषय-वस्तु\nheader:\n  labels:\n    config: विन्यास\n    developer: डेवलपर्स के लिए\n    extras: अतिरिक्त\n    general: सामान्य\n    home: घर\n    icon_pack_editor: कैश्ड आइकन\n    iconpack: आइकन पैक\n    monitors: पर नज़र रखता है\n    plugin: प्लग-इन\n    resources: संसाधन\n    shortcuts: शॉर्टकट\n    soundpack: साउंड पैक\n    specific_apps: आवेदन द्वारा सेटिंग्स\n    theme: विषय-वस्तु\n    virtual_desk: आभासी डेस्कटॉप्स\n    wallpaper: वॉलपेपर\n    widget: विजेट\nhome:\n  new_resources: नए संसाधन\ninherit: इनहेरिट\ninProgress: प्रगति पर है...\ninsert: डालना\nloading: लोड हो रहा है...\nmiscellaneous: मिश्रित\nmonitors_configurations:\n  label: मॉनिटर {{इंडेक्स}}\nmore: अधिक\n'no': नहीं\nopen: खुला\nquit: छोड़ना\nremove: निकालना\nreset_all_to_default: सभी को डिफ़ॉल्ट मानों पर रीसेट करें\nreset_to_default: डिफ़ॉल्ट मान पर रीसेट करें\nresources:\n  app_outdated: >-\n    इस संसाधन को ठीक से काम करने के लिए सेलेन यूआई के एक नए संस्करण की आवश्यकता\n    होती है।\n  corrupted_wallpaper: थंबनेल निकालने में विफल - दूषित या असमर्थित वीडियो प्रारूप\n  delete: संसाधन हटाएं\n  discover: अधिक संसाधनों की खोज करें\n  has_update: एक अपडेट उपलब्ध है\n  high_impact: प्रदर्शन पर उच्च प्रभाव\n  import_wallpapers: स्थानीय वॉलपेपर आयात करें\n  open_folder: संसाधन फ़ोल्डर खोलें\n  outdated: >-\n    यह संसाधन सीलेन यूआई के पुराने संस्करण के लिए डिज़ाइन किया गया था और ठीक से\n    काम नहीं कर सकता है।\n  see_on_website: वेबसाइट पर देखें\nreview_request:\n  not_now: अभी नहीं\n  sure: ज़रूर!\n  title: सेलेन यूआई का आनंद ले रहे हैं?\nsave: बचाना\nsave_and_restart: सहेजें और पुनरारंभ करें\nsearch: खोज\nsee_more: और देखें\nshortcuts:\n  duplicate_error: यह शॉर्टकट डुप्लिकेट है\n  enable: अंतर्निहित शॉर्टकट सिस्टम सक्षम करें\n  enable_tooltip: >-\n    अक्षम करें यदि आप सेलेन यूआई क्लाइंट का उपयोग करके अपने स्वयं के शॉर्टकट\n    सिस्टम को लागू करेंगे\n  labels:\n    create_new_workspace: नया कार्यक्षेत्र बनाएं\n    cycle_stack_next: साइकिल ढेर अगला\n    cycle_stack_prev: चक्र ढेर पिछला\n    cycle_wallpaper_next: अगले वॉलपेपर में बदलें\n    cycle_wallpaper_prev: पिछले वॉलपेपर में बदलें\n    decrease_height: ऊंचाई में कमी\n    decrease_width: चौड़ाई में कमी\n    destroy_current_workspace: वर्तमान कार्यक्षेत्र को नष्ट करें\n    focus_bottom: फोकस बॉटम\n    focus_latest: नवीनतम ध्यान केंद्रित करें\n    focus_left: फोकस लेफ्ट\n    focus_right: ध्यान केंद्रित करना\n    focus_top: फोकस टॉप\n    increase_height: ऊंचाई बढ़ाना\n    increase_width: चौड़ाई में वृद्धि\n    misc_force_quit: जबरन छोड़ना\n    misc_force_restart: बल फिर से शुरू करना\n    misc_open_settings: खुली सेटिंग\n    misc_toggle_lock_tracing: टॉगल लॉक ट्रेसिंग (लॉग)\n    misc_toggle_win_event_tracing: टॉगल जीत इवेंट ट्रेसिंग (लॉग)\n    move_to_workspace: कार्यक्षेत्र {{0}} पर जाएं\n    move_window_down: खिड़की को नीचे तक ले जाएं\n    move_window_left: खिड़की को बाईं ओर ले जाएं\n    move_window_right: खिड़की को दाईं ओर ले जाएं\n    move_window_up: खिड़की को शीर्ष पर ले जाएं\n    pause_tiling: टाइलिंग विंडो मैनेजर को रोकें\n    reserve_bottom: रिजर्व\n    reserve_float: रिजर्व फ्लोट\n    reserve_left: आरक्षित आरक्षित\n    reserve_right: आरक्षित अधिकार\n    reserve_stack: आरक्षित स्टैक\n    reserve_top: आरक्षित शीर्ष\n    restore_sizes: आकारों को बहाल करें\n    send_to_workspace: कार्यक्षेत्र {{0}} को भेजें\n    start_weg_app: फोकस करें या आवेदन शुरू करें {{0}}\n    switch_to_next_workspace: अगले कार्यक्षेत्र पर स्विच करें\n    switch_to_previous_workspace: पिछले कार्यक्षेत्र पर स्विच करें\n    switch_workspace: कार्यक्षेत्र {{0}} पर स्विच करें\n    toggle_float: टॉगल विंडो फ्लोट मोड\n    toggle_monocle: टॉगल वर्कस्पेस मोनोकल मोड\n  readonly_tooltip: यह केवल एक रीड-शॉर्टकट है\n  reset: डिफ़ॉल्ट पर पुनः सेट करें\nsides:\n  bottom: तल\n  left: बाएं\n  right: सही\n  top: शीर्ष\ntoolbar:\n  auto_hide: खुद से छिपना\n  delay_to_hide: छिपाने में देरी\n  delay_to_show: दिखाने में देरी\n  dock_side: पद\n  enable: फैंसी टूलबार सक्षम करें\n  hide_mode:\n    always: हमेशा\n    never: कभी नहीं\n    on_overlap: ओवरलैप पर\n  item_size: आइटम का आकार\n  label: उपकरण पट्टी\n  margin: मार्जिन आकार\n  padding: गद्दी का आकार\n  placeholder: {}\nupdate:\n  available: उपलब्ध अद्यतन!\n  channel: अद्यतन चैनल\n  downloading: डाउनलोड करना ...\nwall:\n  backgrounds: वॉलपेपर\n  blur: कलंक\n  cancel: रद्द करना\n  close: बंद करना\n  collection_name: संग्रह का नाम\n  collections: संग्रह\n  contrast: अंतर\n  corrupted_wallpapers_message: 'दूषित वॉलपेपर, इन्हें हटाने पर विचार करें:'\n  create: बनाएं\n  create_collection: संग्रह बनाएँ\n  default_collection: डिफ़ॉल्ट संग्रह\n  delete_collection: संग्रह हटाएँ\n  edit_collection: संग्रह संपादित करें\n  enable: वॉलपेपर प्रबंधक सक्षम करें\n  extend: प्राथमिक बढ़ाएँ\n  fit:\n    contain: रोकना\n    cover: ढकना\n    fill: भरना\n  flipHorizontal: क्षैतिज\n  flipVertical: तलवाड़ा\n  generating_thumbnails: वॉलपेपर थंबनेल उत्पन्न करना\n  hours: घंटे\n  interval: वॉलपेपर हर बदलें\n  minutes: मिनट\n  monitor_collection: मॉनिटर संग्रह\n  multimonitor_behaviour: मल्टीमॉनिटर व्यवहार\n  muted: वीडियो ऑडियो म्यूट करें\n  no_background: खाली स्लाइड शो, इसके बजाय थीम की पृष्ठभूमि का उपयोग करके।\n  no_collections: अभी तक कोई संग्रह नहीं बनाया गया. एक बनाने के लिए + बटन पर क्लिक करें।\n  objectFit: पृष्ठभूमि फिट\n  objectPosition: पृष्ठभूमि स्थिति\n  overlayColor: ओवरले रंग\n  overlayMixBlendMode: ओवरले मिश्रण मिश्रण मोड\n  per_monitor: प्रति मॉनिटर\n  playback: प्लेबैक गति\n  position:\n    bottom: तल\n    center: केंद्र\n    left: बाएं\n    right: सही\n    top: शीर्ष\n  processing_video: वीडियो संसाधित किया जा रहा है\n  random: यादृच्छिक स्लाइड शो\n  saturation: परिपूर्णता\n  seconds: सेकंड\n  select_collection: संग्रह का चयन करें\n  thumbnail_generation_complete: थंबनेल जनरेशन पूर्ण\n  thumbnail_generation_finished: थंबनेल पीढ़ी सफलतापूर्वक समाप्त हो गई है\n  wallpaper_collection: वॉलपेपर संग्रह\n  wallpaper_settings: वॉलपेपर सेटिंग्स\n  withOverlay: ओवरले के साथ\n  workspace_collections: कार्यक्षेत्र संग्रह\nweg:\n  auto_hide: खुद से छिपना\n  delay_to_hide: छिपाने में देरी\n  delay_to_show: दिखाने में देरी\n  dock_side: पद\n  enable: डॉक/टास्कबार सक्षम करें\n  filtering: आइटम फ़िल्टरिंग\n  gap: अंतर\n  hide_mode:\n    always: हमेशा\n    never: कभी नहीं\n    on_overlap: ओवरलैप पर\n  items:\n    gap: वस्तुओं के बीच का स्थान\n    label: सामान\n    pinned_visibility:\n      always: हमेशा\n      label: पिन किए गए आइटम दृश्यता\n      when_primary: जब मॉनिटर प्राइमरी हो\n    show_instance_counter: ओपन विंडोज काउंटर दिखाएं\n    show_window_title: ओपन विंडो शीर्षक दिखाएं (केवल क्षैतिज)\n    size: आइटम आकार\n    split_windows: विभाजित विंडोज़ (प्रति विंडो एक आइटम)\n    temporal_visibility:\n      all: सभी\n      label: अनपिन किए गए आइटम दृश्यता\n      on_monitor: मॉनिटर पर\n    visible_separators: दृश्य विभाजक\n  label: डॉक/टास्टरबार\n  margin: अंतर\n  mode:\n    full_width: पूर्ण स्क्रीन चौड़ाई\n    min_content: जितना छोटा हो सकता है\n  padding: गद्दी\n  show_end_task: टास्कबार में अंतिम कार्य दिखाएं\n  width: चौड़ाई\nwelcome:\n  give_a_review: एक समीक्षा दें\n  message: >-\n    सीलेन यूआई विंडोज़ के लिए एक मुफ़्त और खुला स्रोत डेस्कटॉप वातावरण है। यहां\n    आपका डेस्कटॉप कैसा दिखता है और कैसे व्यवहार करता है, इस पर आपका पूरा\n    नियंत्रण होता है, जिससे आप अपने वर्कफ़्लो और शैली से मेल खाने के लिए हर\n    विवरण को अनुकूलित कर सकते हैं।\n  ok: चलो शुरू करो!\n  review: >-\n    यदि आप सेलेन यूआई का उपयोग करना पसंद करते हैं, तो स्टोर पर एक समीक्षा छोड़ने\n    पर विचार करें - आपकी प्रतिक्रिया परियोजना को बढ़ने और बेहतर बनाने में मदद\n    करती है।\n  title: सीलेन यूआई में आपका स्वागत है!\nwidget:\n  enable: इस विजेट को सक्षम करें\n  enable_for_monitor: इस मॉनिटर पर सक्षम करें\n  instances: उदाहरण\nwm:\n  animations:\n    duration: एनीमेशन अवधि (एमएस)\n    ease_function: एनीमेशन सहजता समारोह\n    enable: विंडो के एनिमेशन सक्षम करें\n  author: लेखक\n  border:\n    enable: विंडो की सीमा सक्षम करें\n    offset: सीमावर्ती ऑफसेट\n    width: सीमा चौड़ाई\n  description: विवरण\n  drag_behavior: खींचें व्यवहार\n  drag_behavior_options:\n    sort: क्रमबद्ध करें (खींचते समय विंडोज़ को पुनः व्यवस्थित करें)\n    swap: स्वैप (ड्रैग एंड पर विंडोज़ स्वैप करें)\n  enable: टाइलिंग विंडो मैनेजर सक्षम करें\n  layout: लेआउट\n  resize_delta: डेल्टा का आकार बदलें (%)\n  space_between_containers: कंटेनरों के बीच की जगह\n  workspace_offset: कार्यक्षेत्र ऑफसेट (मार्जिन)\n  workspace_padding: कार्यक्षेत्र पैडिंग\n'yes': हाँ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/hr.yml",
    "content": "action:\n  confirm: Jeste li sigurni?\n  confirm_body: Ova se radnja ne može poništiti.\napps_configurations:\n  app:\n    bindings: Vezivanje (napomena potrebne su obje opcije)\n    category: Kategorija\n    category_placeholder: Nijedan\n    monitor: Monitor\n    monitor_placeholder: Nijedan\n    name: Ime\n    ok_create: Stvoriti\n    ok_edit: Ažuriraj\n    ok_readonly: Uredi kao novo\n    options:\n      NoInteractive: Nema interaktivnosti\n      VdPinned: Prikaži u svim radnim prostorima\n      WmFloat: Twm - Počnite plutati\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Poništi upravljanje\n    options_label: Dodatne opcije\n    title_create: Stvaranje {{name}}\n    title_edit: Uređivanje {{name}}\n    title_readonly: Pregled {{name}}\n    weg_options_label: Opcije pristaništa/trake\n    wm_options_label: Opcije upravitelja prozora\n    workspace: Radni prostor\n    workspace_placeholder: Nijedan\n  bundled_msg: >-\n    Ove paketne konfiguracije nisu moguće uređivati ​​i dizajnirane su tako da\n    vam pruže najbolje iskustvo bez prilagodbe. Oni automatski konfiguriraju\n    najčešće aplikacije za vas.\n  bundled_title: Aplikacija config sa Seelen\n  confirm_delete: Jeste li sigurni da želite izbrisati ovu konfiguraciju?\n  confirm_delete_title: Potvrdi Delete\n  delete: Izbrisati\n  export: Izvoz\n  export_full: Postavke izvoza putem aplikacije\n  extra_info: >-\n    Seelen UI koristi samo jedan identifikator po aplikaciji (prvo pronađeno\n    podudaranje) tako da je redoslijed u načinu na koji su specificirani važan,\n    najnovije dodano će imati prioritet, imajte na umu da je tablica sortirana\n    prema zadanim postavkama od najnovijeg prema starom.\n  identifier:\n    add_block: Dodajte blok\n    and: I\n    id: Identifikator\n    kind: Identificirati\n    matching_strategy: Podudarna strategija\n    matching_strategy_option:\n      contains: Sadrži\n      ends_with: Završava s\n      equals: Jednako\n      regex: Uobičajeni izraz\n      starts_with: Počinje s\n    negation: Negiranje podudaranja\n    or: ILI\n    remove: Izbriši blok\n    type:\n      class: Klasa\n      exe: Exe\n      path: Put\n      title: Titula\n  import: Uvoz\n  import_full: Postavke uvoza putem aplikacije\n  new: Novi\n  search: traži\n  swap: Mijenjati\ncancel: Otkazati\nclose: Zatvoriti\ndelete: Izbrisati\ndevtools:\n  app_folders: Mape aplikacija\n  custom_config_file: Učitajte prilagođenu konfiguracijsku datoteku\n  data_folder: Mapa podataka\n  enable: Omogućite alate za razvojne programere\n  install_folder: Mapa za instalaciju\n  load: Opterećenje\n  settings_file: Datoteka postavki\n  simulate_perm:\n    label: Zahtjev za dopuštenje za simulaciju widgeta\n    result_allowed: ✓ Dopuštenje dano\n    result_denied: ✗ Dopuštenje odbijeno\n    trigger: Simulirati\n    widget_id_placeholder: '@autor/ime-widgeta'\nextras:\n  clear_icons: Očistite predmemoriju ikona sustava\n  clear_icons_tooltip: Ponovno pokretanje moglo bi se u potpunosti stupiti na sve widgete\n  exit: Prestati/izaći\n  links: Službene veze\n  relaunch: Ponovno pokretanje\n  version: Verzija\n  version_fixed: >-\n    Verzije aplikacije i WebView2 Runtime su popravljene. To znači da aplikacija\n    neće primati ažuriranja i WebView2 Runtime neće biti automatski ažuriran\n    ažuriranjima za Windows.\ngeneral:\n  accent_color: Akcentna boja\n  date_format: Format datuma\n  date_format_how_to: Kako napisati format datuma?\n  hardware_acceleration: Hardversko ubrzanje\n  hardware_acceleration_description: >-\n    Onemogućavanje hardverskog ubrzanja smanjit će upotrebu memorije, ali može\n    uzrokovati probleme s performansama. Sigurno je onemogućiti ako ne želite\n    koristiti žive pozadine.\n  icon_pack:\n    available: Dostupni paketi ikona\n    selected: Aktivni paketi ikona\n  language: Jezik\n  monday: ponedjeljak\n  performance_mode:\n    on_battery: Na bateriji\n    on_energy_saver: Na uštedu energije\n    options:\n      disabled: Onemogućen\n      extreme: Ekstrem\n      minimal: Minimalan\n    plugged: Priključeno ili punjenje\n  polling_interval: Interval prozivanja sustava\n  polling_interval_description: >-\n    Koliko često (u sekundama) Seelen UI provjerava sistemske resurse kao što su\n    CPU, RAM, mreža i aktivnost diska. Manji broj znači češća ažuriranja, ali\n    malo veću upotrebu resursa.\n  saturday: subota\n  start_of_week: Početak tjedna\n  startup: Trčati na startup?\n  sunday: nedjelja\n  theme:\n    available: Dostupne teme\n    selected: Aktivne teme\nheader:\n  labels:\n    config: Konfiguracije\n    developer: Za programere\n    extras: Dodaci\n    general: Općenito\n    home: Dom\n    icon_pack_editor: Ikone predmemorirane\n    iconpack: Ikona pakiranja\n    monitors: Monitori\n    plugin: Dodaci\n    resources: Resursi\n    shortcuts: Prečac\n    soundpack: Zvučni paketi\n    specific_apps: Postavke prema aplikaciji\n    theme: Teme\n    virtual_desk: Virtualna radna računala\n    wallpaper: Pozadine\n    widget: Widgeti\nhome:\n  new_resources: Novi resursi\ninherit: Naslijediti\ninProgress: U nastajanju...\ninsert: Umetnuti\nloading: Učitavam...\nmiscellaneous: Razni\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Više\n'no': Ne\nopen: Otvoren\nquit: Prestati\nremove: Ukloniti\nreset_all_to_default: Resetirajte sve na zadane vrijednosti\nreset_to_default: Poništite na zadanu vrijednost\nresources:\n  app_outdated: >-\n    Ovaj resurs zahtijeva da novija verzija Seelen korisničkog sučelja radi\n    ispravno.\n  corrupted_wallpaper: >-\n    Izdvajanje minijature nije uspjelo - format videozapisa je oštećen ili nije\n    podržan\n  delete: Izbriši resurs\n  discover: Otkrijte više resursa\n  has_update: Dostupno je ažuriranje\n  high_impact: Veliki utjecaj na performanse\n  import_wallpapers: Uvoz lokalnih pozadina\n  open_folder: Mapa otvorenih resursa\n  outdated: >-\n    Ovaj je resurs dizajniran za stariju verziju seelen korisničkog sučelja i\n    možda neće raditi ispravno.\n  see_on_website: Vidi na web stranici\nreview_request:\n  not_now: Ne sad\n  sure: Naravno!\n  title: Uživate li u Seelen korisničkom sučelju?\nsave: Uštedjeti\nsave_and_restart: Spremi i ponovno pokrenite\nsearch: Pretraživanje\nsee_more: Pogledajte više\nshortcuts:\n  duplicate_error: Ovaj prečac je dupliciran\n  enable: Omogući ugrađeni sustav prečaca\n  enable_tooltip: >-\n    Onemogući ako ćete implementirati vlastiti sustav prečaca pomoću klijenta\n    Seelen UI\n  labels:\n    create_new_workspace: Stvorite novi radni prostor\n    cycle_stack_next: Sljedeći snop ciklusa\n    cycle_stack_prev: STAM STAKU PRETHONI\n    cycle_wallpaper_next: Promijenite na sljedeću pozadinu\n    cycle_wallpaper_prev: Promijenite na prethodnu pozadinu\n    decrease_height: Visina smanjenja\n    decrease_width: Širina smanjenja\n    destroy_current_workspace: Uništiti trenutni radni prostor\n    focus_bottom: Fokus dna\n    focus_latest: Fokus najnovije\n    focus_left: Usredotočite se lijevo\n    focus_right: Usredotočite se u pravu\n    focus_top: Fokus na vrhu\n    increase_height: Povećati visinu\n    increase_width: Povećati širinu\n    misc_force_quit: Sila je odustala\n    misc_force_restart: Ponovno pokrenite\n    misc_open_settings: Otvorene postavke\n    misc_toggle_lock_tracing: Preklopljenje zaključavanja (zapisnici)\n    misc_toggle_win_event_tracing: Pregazivanje traganja događaja (zapisnici)\n    move_to_workspace: Pomaknite se na radni prostor {{0}}\n    move_window_down: Pomaknite prozor na dno\n    move_window_left: Pomaknite prozor u lijevo\n    move_window_right: Pomaknite prozor udesno\n    move_window_up: Pomaknite prozor na vrh\n    pause_tiling: Upravitelj prozora za pauzu\n    reserve_bottom: Rezervirati\n    reserve_float: Rezervirati plutaj\n    reserve_left: Rezerva\n    reserve_right: Rezervirati\n    reserve_stack: Rezerva\n    reserve_top: Rezervirajte vrh\n    restore_sizes: Vrati veličine\n    send_to_workspace: Pošaljite u radni prostor {{0}}\n    start_weg_app: Fokus ili pokretanje aplikacije {{0}}\n    switch_to_next_workspace: Prebacite se na sljedeći radni prostor\n    switch_to_previous_workspace: Prebacite se na prethodni radni prostor\n    switch_workspace: Prebacite se na radni prostor {{0}}\n    toggle_float: Prebacivanje prozora plutajući način\n    toggle_monocle: Prebacite Monocle način rada radnog prostora\n  readonly_tooltip: Ovo je prečac samo za čitanje\n  reset: Ponovno postavite na zadane vrijednosti\nsides:\n  bottom: Dno\n  left: Lijevo\n  right: Pravo\n  top: Vrh\ntoolbar:\n  auto_hide: Auto sakrij\n  delay_to_hide: Odgoda za skrivanje\n  delay_to_show: Odgoditi prikaz\n  dock_side: Položaj\n  enable: Omogućite maštovitu alatnu traku\n  hide_mode:\n    always: Uvijek\n    never: Nikada\n    on_overlap: Na preklapanju\n  item_size: Veličina artikla\n  label: Alatna traka\n  margin: Veličina margine\n  padding: Veličina podloge\n  placeholder: {}\nupdate:\n  available: Ažuriranje dostupno!\n  channel: Kanal za ažuriranje\n  downloading: Preuzimanje ...\nwall:\n  backgrounds: Pozadine\n  blur: Zamutiti\n  cancel: Otkazati\n  close: Zatvoriti\n  collection_name: Naziv zbirke\n  collections: Zbirke\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Oštećene pozadine, razmislite o brisanju ovih:'\n  create: Stvoriti\n  create_collection: Stvori zbirku\n  default_collection: Zadana zbirka\n  delete_collection: Izbriši zbirku\n  edit_collection: Uredi zbirku\n  enable: Omogući upravitelja pozadina\n  extend: Proširi primarni\n  fit:\n    contain: Obuzdati\n    cover: Poklopac\n    fill: Ispuniti\n  flipHorizontal: Okrenuti vodoravni\n  flipVertical: Okrenuti okomit\n  generating_thumbnails: Generiranje sličica pozadine\n  hours: sate\n  interval: Promijenite pozadinu svaku\n  minutes: minute\n  monitor_collection: Zbirka monitora\n  multimonitor_behaviour: Ponašanje više monitora\n  muted: Isključi video zvuk\n  no_background: Prazni prezentacija, umjesto toga, koristeći pozadinu teme.\n  no_collections: Još nije stvorena nijedna zbirka. Kliknite gumb + da biste ga izradili.\n  objectFit: Fit\n  objectPosition: Pozadinski položaj\n  overlayColor: Boja prekrivanja\n  overlayMixBlendMode: Način miješanja preklapanja mješavine\n  per_monitor: Po monitoru\n  playback: Brzina reprodukcije\n  position:\n    bottom: Dno\n    center: Centar\n    left: Lijevo\n    right: Pravo\n    top: Vrh\n  processing_video: Obrada videa\n  random: Randomizirana prezentacija\n  saturation: Zasićenost\n  seconds: sekundi\n  select_collection: Odaberite Zbirka\n  thumbnail_generation_complete: Generiranje sličica dovršeno\n  thumbnail_generation_finished: Generiranje sličica je uspješno završeno\n  wallpaper_collection: Zbirka tapeta\n  wallpaper_settings: Postavke pozadine\n  withOverlay: S prekrivanjem\n  workspace_collections: Zbirke radnog prostora\nweg:\n  auto_hide: Auto sakrij\n  delay_to_hide: Odgoda za skrivanje\n  delay_to_show: Odgoditi prikaz\n  dock_side: Položaj\n  enable: Omogućite pristanište/zadaću\n  filtering: Filtriranje predmeta\n  gap: Jaz\n  hide_mode:\n    always: Uvijek\n    never: Nikada\n    on_overlap: Na preklapanju\n  items:\n    gap: Prostor između predmeta\n    label: Predmeti\n    pinned_visibility:\n      always: Uvijek\n      label: Vidljivost prikvačenih stavki\n      when_primary: Kada je monitor primarni\n    show_instance_counter: Prikaži otvoreni prozorni brojač\n    show_window_title: Prikaži naslov otvorenog prozora (samo vodoravni)\n    size: Veličina predmeta\n    split_windows: Podijeljeni prozori (jedna stavka po prozoru)\n    temporal_visibility:\n      all: Sve\n      label: Vidljivost otkvačenih stavki\n      on_monitor: Na monitoru\n    visible_separators: Vidljivi separatori\n  label: Pristanište/trag\n  margin: Margina\n  mode:\n    full_width: Puna širina zaslona\n    min_content: Mali koliko može biti\n  padding: Jadnja\n  show_end_task: Pokažite krajnji zadatak na zadaćama\n  width: Širina\nwelcome:\n  give_a_review: Dajte recenziju\n  message: >-\n    Seelen UI je besplatno desktop okruženje otvorenog koda za Windows. Ovdje\n    imate potpunu kontrolu nad izgledom i ponašanjem vaše radne površine, što\n    vam omogućuje da prilagodite svaki detalj kako bi odgovarao vašem tijeku\n    rada i stilu.\n  ok: Počnimo!\n  review: >-\n    Ako volite koristiti Seelen UI, razmislite o tome da ostavite recenziju u\n    trgovini - vaše povratne informacije pomažu razvoju i poboljšanju projekta.\n  title: Dobro došli u Seelen UI!\nwidget:\n  enable: Omogući ovaj widget\n  enable_for_monitor: Omogućite ovaj monitor\n  instances: Slučajevi\nwm:\n  animations:\n    duration: Trajanje animacije (MS)\n    ease_function: Funkcija ublažavanja animacije\n    enable: Omogući animacije prozora\n  author: Autor\n  border:\n    enable: Omogućite obrub prozora\n    offset: Offset\n    width: Širina granice\n  description: Opis\n  drag_behavior: Ponašanje povlačenja\n  drag_behavior_options:\n    sort: Poredaj (promjena redoslijeda prozora tijekom povlačenja)\n    swap: Zamjena (zamjena prozora na kraju povlačenja)\n  enable: Omogući upravitelj prozora za plod\n  layout: Raspored\n  resize_delta: Promijenite veličinu delta (%)\n  space_between_containers: Prostor između spremnika\n  workspace_offset: Pomak radnog prostora (margine)\n  workspace_padding: Radni prostori jastučići\n'yes': Da\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/hu.yml",
    "content": "action:\n  confirm: Biztos vagy benne?\n  confirm_body: Ezt a műveletet nem lehet visszavonni.\napps_configurations:\n  app:\n    bindings: Kötés (vegye figyelembe, hogy mindkét lehetőség szükséges)\n    category: Kategória\n    category_placeholder: Egyik sem\n    monitor: Monitor\n    monitor_placeholder: Egyik sem\n    name: Név\n    ok_create: Teremt\n    ok_edit: Frissít\n    ok_readonly: Szerkesztés újként\n    options:\n      NoInteractive: Nincs interaktív\n      VdPinned: Megjelenítés minden munkaterületen\n      WmFloat: Twm – Indítsa el a lebegést\n      WmForce: Twm – Kényszerkezelés\n      WmUnmanage: Twm – Kezelés megszüntetése\n    options_label: Extra lehetőségek\n    title_create: '{{Név}} létrehozása'\n    title_edit: Szerkesztés {{név}}\n    title_readonly: '{{Név}} megtekintés'\n    weg_options_label: Dokk/tálcás lehetőségek\n    wm_options_label: Ablakkezelő opciók\n    workspace: Munkaterület\n    workspace_placeholder: Egyik sem\n  bundled_msg: >-\n    Ezek a csomagolt konfigurációk nem szerkeszthetők, és úgy tervezték, hogy\n    testreszabás nélkül biztosítsa a legjobb élményt. Automatikusan\n    konfigurálják a leggyakoribb alkalmazásokat az Ön számára.\n  bundled_title: A Seelen -vel csomagolt alkalmazás konfigurációja\n  confirm_delete: Biztos benne, hogy törölni akarja ezt a konfigurációt/S -t?\n  confirm_delete_title: Törlés jóváhagyása\n  delete: Töröl\n  export: Export\n  export_full: Beállítások exportálása alkalmazásonként\n  extra_info: >-\n    A Seelen UI alkalmazásonként csak egy azonosítót használ (az első egyezés\n    található), ezért fontos a sorrend, a legutolsó hozzáadott lesz prioritás,\n    mivel a táblázat alapértelmezés szerint a legújabbtól a régiig van rendezve.\n  identifier:\n    add_block: Blokk hozzáadása\n    and: ÉS\n    id: Azonosító\n    kind: Azonosít\n    matching_strategy: Megfelelő stratégia\n    matching_strategy_option:\n      contains: Tartalmaz\n      ends_with: Ezzel végződik\n      equals: Egyenlő\n      regex: Reguláris kifejezés\n      starts_with: Ezzel kezdődik\n    negation: Tagadás egyeztetés\n    or: VAGY\n    remove: Blokk törlése\n    type:\n      class: Osztály\n      exe: Exe\n      path: Útvonal\n      title: Cím\n  import: Behozatal\n  import_full: Beállítások importálása alkalmazásonként\n  new: Új\n  search: Keresés\n  swap: Csere\ncancel: Megszünteti\nclose: Közeli\ndelete: Töröl\ndevtools:\n  app_folders: App mappák\n  custom_config_file: Töltse be az egyéni konfigurációs fájlt\n  data_folder: Adatmappa\n  enable: Engedélyezze a fejlesztői eszközöket\n  install_folder: Telepítési mappa\n  load: Betöltés\n  settings_file: Beállítási fájl\n  simulate_perm:\n    label: Szimulálja a Widget engedélykérését\n    result_allowed: ✓ Engedély megadva\n    result_denied: ✗ Engedély megtagadva\n    trigger: Szimulál\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Rendszerikonok gyorsítótárának törlése\n  clear_icons_tooltip: >-\n    Újraindítás szükséges lehet ahhoz, hogy az összes widget teljes mértékben\n    érvénybe lépjen.\n  exit: Kilépés/kilépés\n  links: Hivatalos linkek\n  relaunch: Újraindít\n  version: Változat\n  version_fixed: >-\n    Az alkalmazás és a WebView2 Runtime verziója rögzített. Ez azt jelenti, hogy\n    az alkalmazás nem kap frissítéseket, és a WebView2 Runtime nem frissül\n    automatikusan Windows-frissítésekkel.\ngeneral:\n  accent_color: Akcentus szín\n  date_format: Dátum formátum\n  date_format_how_to: Hogyan írjunk dátumformátumot?\n  hardware_acceleration: Hardveres gyorsítás\n  hardware_acceleration_description: >-\n    A hardveres gyorsítás letiltása csökkenti a memóriahasználatot, de\n    teljesítménybeli problémákat okozhat. Biztonságosan letiltható, ha nem\n    használ élő háttérképeket.\n  icon_pack:\n    available: Elérhető ikoncsomagok\n    selected: Aktív ikoncsomagok\n  language: Nyelv\n  monday: hétfő\n  performance_mode:\n    on_battery: Akkumulátorra\n    on_energy_saver: Az energiatakarékosról\n    options:\n      disabled: Fogyatékkal élők\n      extreme: Szélső\n      minimal: Minimális\n    plugged: Bedugva vagy töltés\n  polling_interval: Rendszerlekérdezési intervallum\n  polling_interval_description: >-\n    Milyen gyakran (másodpercben) ellenőrzi a Seelen UI a rendszererőforrásokat,\n    például a CPU-t, a RAM-ot, a hálózatot és a lemeztevékenységet. A kisebb\n    szám gyakoribb frissítéseket jelent, de valamivel nagyobb\n    erőforrás-felhasználást.\n  saturday: szombat\n  start_of_week: Hét kezdete\n  startup: Futtatni indításkor?\n  sunday: vasárnap\n  theme:\n    available: Elérhető témák\n    selected: Aktív témák\nheader:\n  labels:\n    config: Konfigurációk\n    developer: Fejlesztők számára\n    extras: Extrák\n    general: Tábornok\n    home: Otthon\n    icon_pack_editor: Cache-olt ikonok\n    iconpack: Ikon csomagok\n    monitors: Monitorok\n    plugin: Plugins\n    resources: Források\n    shortcuts: Parancsikonok\n    soundpack: Hang csomagok\n    specific_apps: Alkalmazásonkénti beállítások\n    theme: Témák\n    virtual_desk: Virtuális asztalok\n    wallpaper: Háttérképek\n    widget: Widgetek\nhome:\n  new_resources: Új források\ninherit: Örököl\ninProgress: Folyamatban...\ninsert: Beilleszt\nloading: Betöltés...\nmiscellaneous: Különféle\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Több\n'no': Nem\nopen: Nyisd ki\nquit: Kilépés\nremove: Eltávolítás\nreset_all_to_default: Mindent visszaállít az alapértelmezett értékekre\nreset_to_default: Alapértelmezett értékre visszaállítása\nresources:\n  app_outdated: Ez az erőforrás a Seelen UI újabb verzióját igényli a megfelelő működéshez.\n  corrupted_wallpaper: Nem sikerült a miniatűr kibontása – sérült vagy nem támogatott videóformátum\n  delete: Az erőforrás törlése\n  discover: További források felfedezése\n  has_update: Frissítés érhető el\n  high_impact: Nagy hatás a teljesítményre\n  import_wallpapers: Helyi háttérképek importálása\n  open_folder: Nyissa meg az erőforrás mappát\n  outdated: >-\n    Ez az erőforrás a Seelen UI régebbi verziójához készült, és előfordulhat,\n    hogy nem működik megfelelően.\n  see_on_website: Lásd a weboldalon\nreview_request:\n  not_now: Most nem\n  sure: Persze!\n  title: Élvezed a Seelen felhasználói felületet?\nsave: Megment\nsave_and_restart: Mentés és újraindítás\nsearch: Keresés\nsee_more: Lásd még\nshortcuts:\n  duplicate_error: Ez a parancsikon megkettőződik\n  enable: Engedélyezze a beépített parancsikonok rendszerét\n  enable_tooltip: >-\n    Tiltsa le, ha a Seelen UI kliens segítségével megvalósítja saját\n    parancsikonjait\n  labels:\n    create_new_workspace: Hozzon létre új munkaterületet\n    cycle_stack_next: Cycle Stack Next\n    cycle_stack_prev: Cycle Stack előző\n    cycle_wallpaper_next: Váltson a következő háttérképre\n    cycle_wallpaper_prev: Váltás az előző háttérképre\n    decrease_height: Csökkenési magasság\n    decrease_width: Csökken a szélesség\n    destroy_current_workspace: Pusztítsd el az aktuális munkaterületet\n    focus_bottom: Fókusz alsó\n    focus_latest: Fókuszban a legújabb\n    focus_left: Fókusz balra\n    focus_right: Fókuszál\n    focus_top: Fókusz teteje\n    increase_height: Növelje a magasságot\n    increase_width: Növelje a szélességet\n    misc_force_quit: Erőt hagy\n    misc_force_restart: Újraindítás kikényszerítése\n    misc_open_settings: Nyissa meg a beállításokat\n    misc_toggle_lock_tracing: Váltás zárkövetés (naplók)\n    misc_toggle_win_event_tracing: Váltás a győzelem események nyomon követése (naplók)\n    move_to_workspace: Költözni a munkaterületre {{0}}\n    move_window_down: Mozgassa az ablakot az aljára\n    move_window_left: Mozgassa az ablakot balra\n    move_window_right: Mozgassa az ablakot jobbra\n    move_window_up: Mozgassa az ablakot a tetejére\n    pause_tiling: Szünet a burkolólap ablakkezelőnek\n    reserve_bottom: Tartalék alsó rész\n    reserve_float: Tartalék úszó\n    reserve_left: Tartalék balra\n    reserve_right: Tartalékolási jog\n    reserve_stack: Tartalékköteg\n    reserve_top: Tartalék\n    restore_sizes: Visszaállítani a méreteket\n    send_to_workspace: Küldje el a munkaterületre {{0}}\n    start_weg_app: Fókusz vagy indítási alkalmazás {{0}}\n    switch_to_next_workspace: Váltás a következő munkaterületre\n    switch_to_previous_workspace: Váltás az előző munkaterületre\n    switch_workspace: Váltás a munkaterületre {{0}}\n    toggle_float: Windows úszó mód váltása\n    toggle_monocle: Váltás a munkaterület monokle módja\n  readonly_tooltip: Ez egy csak olvasható parancsikon\n  reset: Alapértelmezés visszaállítása az alapértelmezésekre\nsides:\n  bottom: Alsó\n  left: Bal\n  right: Jobb\n  top: Felső\ntoolbar:\n  auto_hide: Autóbizalom\n  delay_to_hide: Késleltesse az elrejtést\n  delay_to_show: Megjelenítés késleltetése\n  dock_side: Pozíció\n  enable: Engedélyezze a divatos eszköztárat\n  hide_mode:\n    always: Mindig\n    never: Soha\n    on_overlap: Az átfedésben\n  item_size: Elem mérete\n  label: Eszköztár\n  margin: Margó mérete\n  padding: Padding Méret\n  placeholder: {}\nupdate:\n  available: A frissítés elérhető!\n  channel: Frissítési csatorna\n  downloading: Letöltés ...\nwall:\n  backgrounds: Háttérképek\n  blur: Elmosódás\n  cancel: Mégsem\n  close: Közeli\n  collection_name: Gyűjtemény neve\n  collections: Gyűjtemények\n  contrast: Kontraszt\n  corrupted_wallpapers_message: 'Sérült háttérképek, fontolja meg ezek törlését:'\n  create: Teremt\n  create_collection: Gyűjtemény létrehozása\n  default_collection: Alapértelmezett gyűjtemény\n  delete_collection: Gyűjtemény törlése\n  edit_collection: Gyűjtemény szerkesztése\n  enable: Engedélyezze a háttérképkezelőt\n  extend: Hosszabbítsa meg az elsődleges\n  fit:\n    contain: Tartalmazza a  címet.\n    cover: Borító\n    fill: Töltse ki a  címet.\n  flipHorizontal: Flip vízszintes\n  flipVertical: Flip függőleges\n  generating_thumbnails: Háttérkép miniatűrök generálása\n  hours: órák\n  interval: Változtasd meg minden háttérképet\n  minutes: jegyzőkönyv\n  monitor_collection: Monitor gyűjtemény\n  multimonitor_behaviour: Többmonitoros viselkedés\n  muted: Videó hang némítása\n  no_background: Üres diavetítés, inkább a téma hátterének felhasználásával.\n  no_collections: >-\n    Még nem jött létre gyűjtemény. Kattintson a + gombra, hogy létrehozzon\n    egyet.\n  objectFit: Háttér illeszkedés\n  objectPosition: Háttér Pozíció\n  overlayColor: Overlay szín\n  overlayMixBlendMode: Overlay Mix keverési mód\n  per_monitor: Monitoronként\n  playback: Lejátszási sebesség\n  position:\n    bottom: Alul\n    center: Központ\n    left: Balra\n    right: Jobbra\n    top: Top\n  processing_video: Videó feldolgozása\n  random: Véletlenszerűsítse a diavetítést\n  saturation: Telítettség\n  seconds: másodpercek\n  select_collection: Válassza a Gyűjtemény lehetőséget\n  thumbnail_generation_complete: A miniatűrök generálása kész\n  thumbnail_generation_finished: A miniatűrök generálása sikeresen befejeződött\n  wallpaper_collection: Háttérkép gyűjtemény\n  wallpaper_settings: Háttérkép beállításai\n  withOverlay: Overlay-vel\n  workspace_collections: Munkaterület-gyűjtemények\nweg:\n  auto_hide: Automatikus elrejtés\n  delay_to_hide: Késleltesse az elrejtést\n  delay_to_show: Megjelenítés késleltetése\n  dock_side: Pozíció\n  enable: Engedélyezze a dokkolót/tálcát\n  filtering: Elemszűrés\n  gap: Rés\n  hide_mode:\n    always: Mindig\n    never: Soha\n    on_overlap: Az átfedésben\n  items:\n    gap: Hely az elemek között\n    label: Tárgyak\n    pinned_visibility:\n      always: Mindig\n      label: Rögzített elemek láthatósága\n      when_primary: Amikor a monitor az elsődleges\n    show_instance_counter: Megnyílt ablakok számlálójának megjelenítése\n    show_window_title: Megnyílt ablak címének megjelenítése (csak vízszintes)\n    size: Elemméret\n    split_windows: Osztott ablakok (ablakonként egy elem)\n    temporal_visibility:\n      all: Minden\n      label: Rögzített elemek láthatósága\n      on_monitor: Monitoron\n    visible_separators: Látható elválasztók\n  label: Dokk/tálca\n  margin: Árrés\n  mode:\n    full_width: Teljes képernyő szélesség\n    min_content: Amennyire csak lehet\n  padding: Párnázás\n  show_end_task: A feladat befejezésének megjelenítése a feladatsorban\n  width: Szélesség\nwelcome:\n  give_a_review: Adj véleményt\n  message: >-\n    A Seelen UI egy ingyenes és nyílt forráskódú asztali környezet Windowshoz.\n    Itt teljes mértékben irányíthatja az asztali számítógép megjelenését és\n    viselkedését, így minden részletet személyre szabhat a munkafolyamatának és\n    stílusának megfelelően.\n  ok: Kezdjük!\n  review: >-\n    Ha szívesen használja a Seelen UI-t, fontolja meg, hogy véleményt írjon az\n    áruházban – visszajelzése segíti a projekt növekedését és fejlődését.\n  title: Üdvözöljük a Seelen UI-n!\nwidget:\n  enable: Engedélyezze ezt a widgetet\n  enable_for_monitor: Engedélyezés ezen a monitoron\n  instances: Instances\nwm:\n  animations:\n    duration: Animációs időtartam (MS)\n    ease_function: Animáció enyhítő funkciója\n    enable: Engedélyezze az ablak animációit\n  author: Szerző\n  border:\n    enable: Engedélyezze az ablak szegélyét\n    offset: Határ eltolás\n    width: Határszélesség\n  description: Leírás\n  drag_behavior: Húzási viselkedés\n  drag_behavior_options:\n    sort: Rendezés (ablakok átrendezése húzás közben)\n    swap: Csere (ablak cseréje a húzás végén)\n  enable: Engedélyezze a csempe ablakkezelőt\n  layout: Elrendezés\n  resize_delta: Átméretezze a DELTA -t (%)\n  space_between_containers: Hely a konténerek között\n  workspace_offset: A munkaterületek eltolása (margók)\n  workspace_padding: Munkaterületek párnázása\n'yes': Igen\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/hy.yml",
    "content": "action:\n  confirm: Վստահ եք\n  confirm_body: 'Այս գործողությունը չի կարող վերացնել:'\napps_configurations:\n  app:\n    bindings: Պարտադիր (նշում է, որ երկու տարբերակները պարտադիր են)\n    category: Կատեգորիա\n    category_placeholder: Ոչ ոք\n    monitor: Մոնիտոր\n    monitor_placeholder: Ոչ ոք\n    name: Անուն\n    ok_create: Ստեղծել\n    ok_edit: Թարմացում\n    ok_readonly: Խմբագրել որպես նոր\n    options:\n      NoInteractive: Ինտերակտիվ չկա\n      VdPinned: Ցուցադրել բոլոր աշխատանքային տարածքներում\n      WmFloat: Twm - Սկսեք լողալ\n      WmForce: Twm - ուժի կառավարում\n      WmUnmanage: Twm - Չկառավարել\n    options_label: Լրացուցիչ ընտրանքներ\n    title_create: Ստեղծելով {{name}\n    title_edit: Խմբագրում {{name}\n    title_readonly: Դիտում է {{Անունը}\n    weg_options_label: Dock / Taskbar Ընտրանքներ\n    wm_options_label: Պատուհանների մենեջեր Ընտրանքներ\n    workspace: Աշխատատեղ\n    workspace_placeholder: Ոչ ոք\n  bundled_msg: >-\n    Այս փաթեթավորված կազմաձեւերը խմբագրելի չեն եւ նախագծված են `առանց\n    հարմարեցման լավագույն փորձը տրամադրելու: Նրանք ինքնաբերաբար կարգավորում են\n    ձեզ համար ամենատարածված դիմումները:\n  bundled_title: Ծրագրի կազմաձեւումը փաթեթավորված է Seelen- ի հետ\n  confirm_delete: 'Համոզված եք, որ ցանկանում եք ջնջել այս կազմաձեւը:'\n  confirm_delete_title: Հաստատեք ջնջել\n  delete: Ջնջել\n  export: Արտահանում\n  export_full: Արտահանման պարամետրերը ըստ դիմումի\n  extra_info: >-\n    Seelen UI-ն օգտագործում է միայն մեկ նույնացուցիչ յուրաքանչյուր հավելվածի\n    համար (գտնվել է առաջին համընկնումը), այնպես որ կարևոր է ճշգրտման կարգը,\n    առաջնահերթությունը կտրվի վերջին ավելացվածներին, քանի որ աղյուսակը ըստ\n    կանխադրման դասավորված է վերջինից հին:\n  identifier:\n    add_block: Ավելացնել բլոկ\n    and: Մի քանազոր\n    id: Նույնացնող\n    kind: Նույնացնել ըստ\n    matching_strategy: Համապատասխան ռազմավարություն\n    matching_strategy_option:\n      contains: Պարունակում է\n      ends_with: Ավարտվում է\n      equals: Հավասար է\n      regex: Կանոնավոր արտահայտություն\n      starts_with: Սկսվում է\n    negation: Ժխտել համապատասխանությունը\n    or: ԿԱՄ\n    remove: Delete նջել բլոկը\n    type:\n      class: Դասարան\n      exe: Exe\n      path: Ճանապարհ\n      title: Վերնագիր\n  import: Ներմուծում\n  import_full: Ներմուծման պարամետրերը ըստ դիմումի\n  new: Նոր\n  search: Որոնել\n  swap: Փոխանակել\ncancel: Չեղարկել\nclose: Սերտ\ndelete: Ջնջել\ndevtools:\n  app_folders: Ծրագրի թղթապանակներ\n  custom_config_file: Բեռնեք պատվերով կազմաձեւման ֆայլը\n  data_folder: Տվյալների պանակ\n  enable: Միացնել մշակողի գործիքները\n  install_folder: Տեղադրման պանակ\n  load: Բեռ\n  settings_file: Կարգավորումներ ֆայլ\n  simulate_perm:\n    label: Վիդջեթի թույլտվության հայտի նմանակում\n    result_allowed: ✓ Տրված է թույլտվություն\n    result_denied: ✗ Թույլտվությունը մերժված է\n    trigger: Մոդելավորել\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Մաքրել համակարգի սրբապատկերների քեշը\n  clear_icons_tooltip: >-\n    Վերագործարկումը կարող է պահանջվել բոլոր վիդջեթների վրա լիարժեք ուժի մեջ\n    մտնելու համար\n  exit: Հանգստացեք / Ելք\n  links: Պաշտոնական հղումներ\n  relaunch: Վերակառուցվել\n  version: Տարբերակ\n  version_fixed: >-\n    Հավելվածը և WebView2 Runtime տարբերակները ամրագրված են: Սա նշանակում է, որ\n    հավելվածը թարմացումներ չի ստանա, և WebView2 Runtime-ն ավտոմատ կերպով չի\n    թարմացվի Windows-ի թարմացումներով:\ngeneral:\n  accent_color: Շեշտադրիչ գույն\n  date_format: Ամսաթիվ ձեւաչափ\n  date_format_how_to: 'Ինչպե՞ս գրել ամսաթվի ձևաչափ:'\n  hardware_acceleration: Սարքավորումների արագացում\n  hardware_acceleration_description: >-\n    Սարքավորման արագացումն անջատելը կնվազեցնի հիշողության օգտագործումը, բայց\n    կարող է առաջացնել աշխատանքի հետ կապված խնդիրներ: Ապահով է անջատել, եթե\n    չօգտագործեք կենդանի պաստառներ:\n  icon_pack:\n    available: Հասանելի պատկերակների փաթեթներ\n    selected: Ակտիվ պատկերակների փաթեթներ\n  language: Լեզու\n  monday: Երկուշաբթի\n  performance_mode:\n    on_battery: Մարտկոցի վրա\n    on_energy_saver: Էներգախնայողաբար\n    options:\n      disabled: Հաշմանդամացած\n      extreme: Ծայրահեղություն\n      minimal: Նվազագույն\n    plugged: Միացված կամ լիցքավորող\n  polling_interval: Համակարգի հարցումների ընդմիջում\n  polling_interval_description: >-\n    Որքա՞ն հաճախ (վայրկյանների ընթացքում) Seelen UI-ն ստուգում է համակարգի\n    ռեսուրսները, ինչպիսիք են CPU-ն, RAM-ը, ցանցը և սկավառակի ակտիվությունը:\n    Ավելի փոքր թիվը նշանակում է ավելի հաճախակի թարմացումներ, բայց մի փոքր ավելի\n    մեծ ռեսուրսների օգտագործում:\n  saturday: շաբաթ օրը\n  start_of_week: Շաբաթվա սկիզբ\n  startup: 'Գործարկման վրա գործարկեք:'\n  sunday: Կիրակի\n  theme:\n    available: Հասանելի թեմաներ\n    selected: Ակտիվ թեմաներ\nheader:\n  labels:\n    config: Կազմաձեւեր\n    developer: Մշակողների համար\n    extras: Լրացուցիչ նյութեր\n    general: Ընդհանուր\n    home: Տուն\n    icon_pack_editor: Պահված սրբապատկերներ\n    iconpack: Սրբապատկերներ\n    monitors: Մոնիտորներ\n    plugin: Plugins\n    resources: Ռեսուրսներ\n    shortcuts: Դյուրանցումներ\n    soundpack: Ձայնային փաթեթներ\n    specific_apps: Կարգավորումներ ըստ դիմումի\n    theme: Թեմաներ\n    virtual_desk: Վիրտուալ աշխատասեղաններ\n    wallpaper: Պաստառներ\n    widget: Widgets\nhome:\n  new_resources: Նոր ռեսուրսներ\ninherit: Ժառանգել\ninProgress: Ընթացքի մեջ է...\ninsert: Տեղադրել\nloading: Բեռնում ...\nmiscellaneous: Զանազան\nmonitors_configurations:\n  label: Մոնիտոր {{ինդեքս}\nmore: Ավելի շատ\n'no': Ոչ\nopen: Բաց անել\nquit: Թողնել\nremove: Հեռացնել\nreset_all_to_default: Վերագործարկեք բոլորին լռելյայն արժեքները\nreset_to_default: Վերագործարկեք կանխադրված արժեքը\nresources:\n  app_outdated: 'Այս ռեսուրսը պահանջում է Seelen UI- ի նոր տարբերակը `ճիշտ աշխատելու համար:'\n  corrupted_wallpaper: Չհաջողվեց հանել մանրապատկերը՝ վնասված կամ չաջակցվող տեսանյութի ձևաչափ\n  delete: Delete նջել ռեսուրսը\n  discover: Բացահայտեք ավելի շատ ռեսուրսներ\n  has_update: Թարմացումն առկա է\n  high_impact: Բարձր ազդեցություն կատարման վրա\n  import_wallpapers: Ներմուծեք տեղական պաստառներ\n  open_folder: Բացեք ռեսուրսների թղթապանակ\n  outdated: >-\n    Այս ռեսուրսը նախատեսված էր Seelen UI- ի ավելի հին տարբերակի համար եւ կարող է\n    պատշաճ կերպով չաշխատել:\n  see_on_website: Տես կայքում\nreview_request:\n  not_now: Ոչ հիմա\n  sure: Իհարկե!\n  title: 'Վայելո՞ւմ եք Seelen UI-ը:'\nsave: Խնայել\nsave_and_restart: Պահել եւ վերագործարկել\nsearch: Որոնում\nsee_more: Տեսնել ավելին\nshortcuts:\n  duplicate_error: Այս դյուրանցումը կրկնօրինակված է\n  enable: Միացնել ներկառուցված դյուրանցումների համակարգը\n  enable_tooltip: >-\n    Անջատեք, եթե դուք կիրականացնեք ձեր սեփական դյուրանցումների համակարգը\n    `օգտագործելով Seelen UI հաճախորդը\n  labels:\n    create_new_workspace: Ստեղծեք նոր աշխատանքային տարածք\n    cycle_stack_next: Ցիկլի կեռը հաջորդը\n    cycle_stack_prev: Cycle Stack Նախորդ\n    cycle_wallpaper_next: Փոխեք հաջորդ պաստառին\n    cycle_wallpaper_prev: Փոխեք նախորդ պաստառին\n    decrease_height: Նվազեցնել բարձրությունը\n    decrease_width: Լայնության նվազում\n    destroy_current_workspace: Քանդել ընթացիկ աշխատանքային տարածքը\n    focus_bottom: Կենտրոնացեք ներքեւից\n    focus_latest: Կենտրոնացեք վերջին\n    focus_left: Կենտրոնանալ ձախ\n    focus_right: Կենտրոնանալ ճիշտ\n    focus_top: Կենտրոնանալ գագաթին\n    increase_height: Բարձրացնել բարձրությունը\n    increase_width: Բարձրացնել լայնությունը\n    misc_force_quit: Ուժը թողնել\n    misc_force_restart: FORCE REARTART\n    misc_open_settings: Բաց պարամետրեր\n    misc_toggle_lock_tracing: Փոփոխեք կողպեքի հետեւում (տեղեկամատյաններ)\n    misc_toggle_win_event_tracing: Անջատեք շահեք իրադարձության հետագծումը (տեղեկամատյաններ)\n    move_to_workspace: Տեղափոխվել դեպի Workspace {0}\n    move_window_down: Տեղափոխեք պատուհանը դեպի ներքեւ\n    move_window_left: Տեղափոխեք պատուհանը ձախից\n    move_window_right: Տեղափոխեք պատուհանը աջից\n    move_window_up: Տեղափոխեք պատուհանը վերեւում\n    pause_tiling: Դադարեք սալիկապատման պատուհանի կառավարիչը\n    reserve_bottom: Ամրագրեք հատակը\n    reserve_float: Ամրագրեք բոց\n    reserve_left: Պահուստը մնացել է\n    reserve_right: Ամրագրեք աջ\n    reserve_stack: Պահեստավորված նյութեր\n    reserve_top: Ամրագրեք վերեւ\n    restore_sizes: Վերականգնել չափերը\n    send_to_workspace: Ուղարկել Workspace- ին {0}\n    start_weg_app: Կենտրոնանալ կամ սկսել դիմումը {{0}\n    switch_to_next_workspace: Անցեք հաջորդ աշխատանքային տարածքին\n    switch_to_previous_workspace: Անցեք նախորդ աշխատանքային տարածքին\n    switch_workspace: Անցեք Workspace- ին {{0}}\n    toggle_float: Անջատեք պատուհանի լողացող ռեժիմը\n    toggle_monocle: Փոփոխեք Workspace Monocle ռեժիմը\n  readonly_tooltip: Սա ընթերցված դյուրանցում է\n  reset: Վերականգնել կանխադրված\nsides:\n  bottom: Հատակից\n  left: Ձախ\n  right: Ճիշտ\n  top: Գագաթ\ntoolbar:\n  auto_hide: Ավտոմատ թաքցնել\n  delay_to_hide: Հետաձգել թաքցնելը\n  delay_to_show: Ցուցադրման ուշացում\n  dock_side: Դիրք\n  enable: Միացնել Fancy Toolbar- ը\n  hide_mode:\n    always: Միշտ\n    never: Երբեք\n    on_overlap: Համընկնման վրա\n  item_size: Նյութի չափը\n  label: Գործիքագոտու\n  margin: Լուսանցքի չափը\n  padding: Լցոնման չափը\n  placeholder: {}\nupdate:\n  available: 'Թարմացրեք մատչելի:'\n  channel: Թարմացրեք ալիքը\n  downloading: Ներբեռնում ...\nwall:\n  backgrounds: Պաստառներ\n  blur: Բութ\n  cancel: Չեղարկել\n  close: Փակել\n  collection_name: Հավաքածուի անվանումը\n  collections: Հավաքածուներ\n  contrast: Հակադրություն\n  corrupted_wallpapers_message: Կոռումպացված պաստառներ, մտածեք ջնջել դրանք.\n  create: Ստեղծել\n  create_collection: Ստեղծել Հավաքածու\n  default_collection: Կանխադրված հավաքածու\n  delete_collection: Ջնջել հավաքածուն\n  edit_collection: Խմբագրել հավաքածուն\n  enable: Միացրեք Պաստառների կառավարիչը\n  extend: Ընդլայնել առաջնային\n  fit:\n    contain: Պարունակել\n    cover: Ծածկոց\n    fill: Լցնել\n  flipHorizontal: Ծխնելույզ հորիզոնական\n  flipVertical: Մատով ուղղահայաց\n  generating_thumbnails: Պաստառի մանրապատկերների ստեղծում\n  hours: ժամ\n  interval: Ամեն ինչ փոխեք պաստառ\n  minutes: րոպե\n  monitor_collection: Մոնիտորների հավաքածու\n  multimonitor_behaviour: Մուլտիմոնիտորի վարքագիծ\n  muted: Անջատել վիդեո ձայնը\n  no_background: 'Դատարկ սլայդերի ցուցադրություն, փոխարենը օգտագործելով թեմայի ֆոնը:'\n  no_collections: 'Դեռևս ոչ մի հավաքածու չի ստեղծվել: Մեկը ստեղծելու համար սեղմեք + կոճակը:'\n  objectFit: Ֆոնային տեղավորումը\n  objectPosition: Ֆոնային դիրքը\n  overlayColor: Ծածկույթի գույն\n  overlayMixBlendMode: Ծածկույթի խառնուրդի խառնուրդի ռեժիմ\n  per_monitor: Մեկ մոնիտորի համար\n  playback: Նվագարկման արագություն\n  position:\n    bottom: Հատակից\n    center: Կենտրոն\n    left: Ձախ\n    right: Իրավունք\n    top: Գագաթ\n  processing_video: Տեսանյութի մշակում\n  random: Պատահականացրեք սլայդերի ցուցադրումը\n  saturation: Հագեցում\n  seconds: վայրկյան\n  select_collection: Ընտրեք Հավաքածու\n  thumbnail_generation_complete: Մանրապատկերների ստեղծումն ավարտված է\n  thumbnail_generation_finished: Մանրապատկերների ստեղծումը հաջողությամբ ավարտվեց\n  wallpaper_collection: Պաստառների հավաքածու\n  wallpaper_settings: Պաստառի կարգավորումներ\n  withOverlay: Ծածկով\n  workspace_collections: Աշխատանքային տարածքների հավաքածուներ\nweg:\n  auto_hide: Ավտոմատ թաքցնել\n  delay_to_hide: Հետաձգել թաքցնելը\n  delay_to_show: Ցուցադրման ուշացում\n  dock_side: Դիրք\n  enable: Միացնել Dock / Taskbar\n  filtering: Նյութի զտիչ\n  gap: Բացվածք\n  hide_mode:\n    always: Միշտ\n    never: Երբեք\n    on_overlap: Համընկնման վրա\n  items:\n    gap: Տարածություն իրերի միջեւ\n    label: Իրերը\n    pinned_visibility:\n      always: Միշտ\n      label: Ամրացված տարրերի տեսանելիություն\n      when_primary: Երբ մոնիտորը առաջնային է\n    show_instance_counter: Show ույց տալ բաց պատուհանների հաշվիչ\n    show_window_title: Show ույց տալ բաց պատուհանի վերնագիրը (միայն հորիզոնական)\n    size: Ապրանքի չափը\n    split_windows: Split Windows (մեկ տարր յուրաքանչյուր պատուհանի համար)\n    temporal_visibility:\n      all: Բոլորը\n      label: Չամրացված տարրերի տեսանելիություն\n      on_monitor: Մոնիտորի վրա\n    visible_separators: Տեսանելի տարանջատիչներ\n  label: Dock / Taskbar\n  margin: Լուսանցք\n  mode:\n    full_width: Ամբողջ էկրանի լայնությունը\n    min_content: Փոքր, որքան կարող է լինել\n  padding: Լիցք\n  show_end_task: Show ույց տալ ավարտի առաջադրանքը առաջադրանքի մեջ\n  width: Լայնություն\nwelcome:\n  give_a_review: Վերանայեք\n  message: >-\n    Seelen UI-ն անվճար և բաց կոդով աշխատասեղանի միջավայր է Windows-ի համար:\n    Այստեղ դուք լիովին վերահսկում եք ձեր աշխատասեղանի տեսքը և վարքագիծը, ինչը\n    թույլ է տալիս հարմարեցնել յուրաքանչյուր մանրուք՝ ձեր աշխատանքի ընթացքին և\n    ոճին համապատասխանելու համար:\n  ok: Սկսենք։\n  review: >-\n    Եթե ​​ձեզ դուր է գալիս Seelen UI-ի օգտագործումը, մտածեք խանութում ակնարկ\n    թողնելու մասին. ձեր կարծիքն օգնում է նախագծի զարգացմանն ու կատարելագործմանը:\n  title: 'Բարի գալուստ Seelen UI:'\nwidget:\n  enable: Միացրեք այս հարմարանքը\n  enable_for_monitor: Միացնել այս մոնիտորի վրա\n  instances: Դեպքեր\nwm:\n  animations:\n    duration: Անիմացիայի տեւողությունը (MS)\n    ease_function: Անիմացիայի թեթեւացման գործառույթը\n    enable: Միացնել պատուհանի անիմացիաները\n  author: Հեղինակ\n  border:\n    enable: Միացնել պատուհանի սահմանը\n    offset: Սահմանի օֆսեթ\n    width: Սահմանի լայնությունը\n  description: Նկարագրություն\n  drag_behavior: Քաշել վարքագիծը\n  drag_behavior_options:\n    sort: Տեսակավորել (վերադասավորել պատուհանները քաշելիս)\n    swap: Փոխանակում (փոխանակեք պատուհանները՝ քաշելու վերջում)\n  enable: Միացնել սալիկապատման պատուհանի կառավարիչը\n  layout: Դասավորություն\n  resize_delta: Չափափոխել դելտան (%)\n  space_between_containers: Տիեզերք բեռնարկղերի միջեւ\n  workspace_offset: Աշխատանքային տարածքների օֆսեթ (լուսանցքներ)\n  workspace_padding: Աշխատանքային տարածքների լիցքավորում\n'yes': Այո\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/id.yml",
    "content": "action:\n  confirm: Apa kamu yakin?\n  confirm_body: Tindakan ini tidak bisa dibatalkan.\napps_configurations:\n  app:\n    bindings: Mengikat (perhatikan kedua opsi itu diperlukan)\n    category: Kategori\n    category_placeholder: Tidak ada\n    monitor: Monitor\n    monitor_placeholder: Tidak ada\n    name: Nama\n    ok_create: Membuat\n    ok_edit: Memperbarui\n    ok_readonly: Edit sebagai baru\n    options:\n      NoInteractive: Tidak Ada Interaktif\n      VdPinned: Tampilkan di semua ruang kerja\n      WmFloat: Twm - Mulai Mengambang\n      WmForce: Twm - Paksa Kelola\n      WmUnmanage: Twm - Batalkan pengelolaan\n    options_label: Opsi tambahan\n    title_create: Membuat {{name}}\n    title_edit: Mengedit {{name}}\n    title_readonly: Melihat {{name}}\n    weg_options_label: Opsi Dock/Taskbar\n    wm_options_label: Opsi Manajer Jendela\n    workspace: Ruang kerja\n    workspace_placeholder: Tidak ada\n  bundled_msg: >-\n    Konfigurasi yang dibundel ini tidak dapat diedit dan dirancang untuk memberi\n    Anda pengalaman terbaik tanpa kustomisasi. Mereka secara otomatis\n    mengonfigurasi aplikasi yang paling umum untuk Anda.\n  bundled_title: Aplikasi Config dibundel dengan Seelen\n  confirm_delete: Anda yakin ingin menghapus konfigurasi ini?\n  confirm_delete_title: Konfirmasi hapus\n  delete: Menghapus\n  export: Ekspor\n  export_full: Pengaturan ekspor menurut aplikasi\n  extra_info: >-\n    Seelen UI Gunakan hanya satu pengidentifikasi per aplikasi (Pertandingan\n    Pertama Ditemukan) sehingga pesanan dalam bagaimana spesifisasi itu penting,\n    yang ditambahkan terbaru akan diprioritaskan, seperti yang dicatat tabel\n    diurutkan secara default dari yang terbaru ke yang lama.\n  identifier:\n    add_block: Tambahkan blok\n    and: DAN\n    id: Pengidentifikasi\n    kind: Identifikasi oleh\n    matching_strategy: Strategi pencocokan\n    matching_strategy_option:\n      contains: Berisi\n      ends_with: Berakhir dengan\n      equals: Setara\n      regex: Ekspresi reguler\n      starts_with: Dimulai dengan\n    negation: Meninggalkan pencocokan\n    or: ATAU\n    remove: Hapus blok\n    type:\n      class: Kelas\n      exe: Contoh\n      path: Jalur\n      title: Judul\n  import: Impor\n  import_full: Mengimpor pengaturan berdasarkan aplikasi\n  new: Baru\n  search: Mencari\n  swap: Menukar\ncancel: Membatalkan\nclose: Menutup\ndelete: Menghapus\ndevtools:\n  app_folders: Folder aplikasi\n  custom_config_file: Muat file konfigurasi khusus\n  data_folder: Folder Data\n  enable: Aktifkan alat pengembang\n  install_folder: Folder Instalasi\n  load: Memuat\n  settings_file: File Pengaturan\n  simulate_perm:\n    label: Simulasikan Permintaan Izin Widget\n    result_allowed: ✓ Izin diberikan\n    result_denied: ✗ Izin ditolak\n    trigger: Simulasikan\n    widget_id_placeholder: '@penulis/nama-widget'\nextras:\n  clear_icons: Menghapus Cache Ikon Sistem\n  clear_icons_tooltip: >-\n    Pengaktifan ulang mungkin diperlukan untuk menerapkan efek sepenuhnya pada\n    semua widget\n  exit: Berhenti/keluar\n  links: Tautan resmi\n  relaunch: Peluncuran kembali\n  version: 'Versi: kapan'\n  version_fixed: >-\n    Versi aplikasi dan WebView2 Runtime telah diperbaiki. Artinya aplikasi tidak\n    akan menerima pembaruan dan WebView2 Runtime tidak akan diperbarui secara\n    otomatis dengan pembaruan Windows.\ngeneral:\n  accent_color: Aksen warna\n  date_format: Format tanggal\n  date_format_how_to: Bagaimana cara menulis format tanggal?\n  hardware_acceleration: Akselerasi Perangkat Keras\n  hardware_acceleration_description: >-\n    Menonaktifkan akselerasi perangkat keras akan mengurangi penggunaan memori,\n    namun dapat menyebabkan masalah kinerja. Aman untuk dinonaktifkan jika Anda\n    tidak akan menggunakan wallpaper hidup.\n  icon_pack:\n    available: Paket Ikon yang Tersedia\n    selected: Paket Ikon Aktif\n  language: Bahasa\n  monday: Senin\n  performance_mode:\n    on_battery: Pada baterai\n    on_energy_saver: Tentang Penghemat Energi\n    options:\n      disabled: Dengan disabilitas\n      extreme: Ekstrim\n      minimal: Minimal\n    plugged: Terhubung atau pengisian daya\n  polling_interval: Interval pemungutan suara sistem\n  polling_interval_description: >-\n    Seberapa sering (dalam hitungan detik) Seelen UI memeriksa sumber daya\n    sistem seperti CPU, RAM, jaringan, dan aktivitas disk. Angka yang lebih\n    kecil berarti pembaruan yang lebih sering, namun penggunaan sumber daya\n    sedikit lebih tinggi.\n  saturday: Sabtu\n  start_of_week: Awal Minggu\n  startup: Jalankan saat startup?\n  sunday: Minggu\n  theme:\n    available: Tema yang Tersedia\n    selected: Tema Aktif\nheader:\n  labels:\n    config: Konfigurasi\n    developer: Untuk pengembang\n    extras: Ekstra\n    general: Umum\n    home: Rumah\n    icon_pack_editor: Ikon Cache\n    iconpack: Paket Ikon\n    monitors: Monitor\n    plugin: Plugin\n    resources: Sumber daya\n    shortcuts: Pintasan\n    soundpack: Paket Suara\n    specific_apps: Pengaturan berdasarkan Aplikasi\n    theme: Tema\n    virtual_desk: Desktop Virtual\n    wallpaper: Wallpaper\n    widget: Widget\nhome:\n  new_resources: Sumber Daya Baru\ninherit: Mewarisi\ninProgress: Sedang berlangsung...\ninsert: Menyisipkan\nloading: Memuat...\nmiscellaneous: Aneka ragam\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Lagi\n'no': TIDAK\nopen: Membuka\nquit: Berhenti\nremove: Menghapus\nreset_all_to_default: Atur ulang semua ke nilai default\nreset_to_default: Atur ulang ke nilai default\nresources:\n  app_outdated: >-\n    Sumber daya ini membutuhkan versi terbaru dari Seelen UI agar dapat\n    berfungsi dengan baik.\n  corrupted_wallpaper: Gagal mengekstrak thumbnail - format video rusak atau tidak didukung\n  delete: Hapus sumber daya\n  discover: Temukan lebih banyak sumber daya\n  has_update: Pembaruan tersedia\n  high_impact: Dampak tinggi pada kinerja\n  import_wallpapers: Impor wallpaper lokal\n  open_folder: Buka folder sumber daya\n  outdated: >-\n    Sumber daya ini dirancang untuk versi lama Seelen UI dan mungkin tidak\n    berfungsi dengan baik.\n  see_on_website: Lihat di situs web\nreview_request:\n  not_now: Tidak sekarang\n  sure: Tentu!\n  title: Menikmati Seelen UI?\nsave: Menyimpan\nsave_and_restart: Simpan & restart\nsearch: Mencari\nsee_more: Lihat Lebih Lanjut\nshortcuts:\n  duplicate_error: Pintasan ini diduplikasi\n  enable: Aktifkan sistem pintasan bawaan\n  enable_tooltip: >-\n    Nonaktifkan jika Anda akan mengimplementasikan sistem pintasan Anda sendiri\n    menggunakan klien Seelen UI\n  labels:\n    create_new_workspace: Buat ruang kerja baru\n    cycle_stack_next: Tumpukan Siklus Berikutnya\n    cycle_stack_prev: Tumpukan Siklus Sebelumnya\n    cycle_wallpaper_next: Ubah ke wallpaper berikutnya\n    cycle_wallpaper_prev: Ubah ke wallpaper sebelumnya\n    decrease_height: Tinggi penurunan\n    decrease_width: Penurunan lebar\n    destroy_current_workspace: Hancurkan ruang kerja saat ini\n    focus_bottom: Fokus dasar\n    focus_latest: Fokus Terbaru\n    focus_left: Fokus kiri\n    focus_right: Fokus benar\n    focus_top: Fokus atas\n    increase_height: Tingkatkan tinggi\n    increase_width: Meningkatkan lebar\n    misc_force_quit: Force berhenti\n    misc_force_restart: Memaksa Mulai Ulang\n    misc_open_settings: Buka Pengaturan\n    misc_toggle_lock_tracing: Toggle Lock Tracing (Log)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (Log)\n    move_to_workspace: Pindah ke Workspace {{0}}\n    move_window_down: Pindahkan jendela ke bawah\n    move_window_left: Pindahkan jendela ke kiri\n    move_window_right: Pindahkan jendela ke kanan\n    move_window_up: Pindahkan jendela ke atas\n    pause_tiling: Jeda Manajer Jendela Ubin\n    reserve_bottom: Cadangan Bawah\n    reserve_float: Cadangan float\n    reserve_left: Cadangan kiri\n    reserve_right: Cadangan hak\n    reserve_stack: Tumpukan Cadangan\n    reserve_top: Cadangan atasan\n    restore_sizes: Kembalikan ukuran\n    send_to_workspace: Kirim ke Workspace {{0}}\n    start_weg_app: Fokus atau Mulai Aplikasi {{0}}\n    switch_to_next_workspace: Beralih ke ruang kerja berikutnya\n    switch_to_previous_workspace: Beralih ke ruang kerja sebelumnya\n    switch_workspace: Beralih ke Workspace {{0}}\n    toggle_float: Toggle Window Float Mode\n    toggle_monocle: Sakelar Mode Monokel Workspace\n  readonly_tooltip: Ini adalah jalan pintas yang hanya membaca\n  reset: Reset ke default\nsides:\n  bottom: Dasar\n  left: Kiri\n  right: Benar\n  top: Atas\ntoolbar:\n  auto_hide: Persembunyian otomatis\n  delay_to_hide: Penundaan untuk bersembunyi\n  delay_to_show: Keterlambatan untuk ditampilkan\n  dock_side: Posisi\n  enable: Aktifkan toolbar mewah\n  hide_mode:\n    always: Selalu\n    never: Tidak pernah\n    on_overlap: Di tumpang tindih\n  item_size: Ukuran Barang\n  label: Toolbar\n  margin: Ukuran Margin\n  padding: Ukuran Bantalan\n  placeholder: {}\nupdate:\n  available: Perbarui Tersedia!\n  channel: Perbarui saluran\n  downloading: Mengunduh ...\nwall:\n  backgrounds: Wallpaper\n  blur: Blur\n  cancel: Membatalkan\n  close: Menutup\n  collection_name: Nama Koleksi\n  collections: Koleksi\n  contrast: Kontras\n  corrupted_wallpapers_message: 'Wallpaper rusak, pertimbangkan untuk menghapus ini:'\n  create: Membuat\n  create_collection: Buat Koleksi\n  default_collection: Koleksi Bawaan\n  delete_collection: Hapus Koleksi\n  edit_collection: Sunting Koleksi\n  enable: Aktifkan Manajer Wallpaper\n  extend: Perpanjang primer\n  fit:\n    contain: Berisi\n    cover: Penutup\n    fill: Isi\n  flipHorizontal: Balik Horizontal\n  flipVertical: Balik Vertikal\n  generating_thumbnails: Menghasilkan Gambar Mini Wallpaper\n  hours: jam\n  interval: Ubah wallpaper setiap\n  minutes: menit\n  monitor_collection: Koleksi Pantau\n  multimonitor_behaviour: Perilaku Multimonitor\n  muted: Bisukan audio video\n  no_background: Slideshow kosong, menggunakan latar belakang tema sebagai gantinya.\n  no_collections: Belum ada koleksi yang dibuat. Klik tombol + untuk membuatnya.\n  objectFit: Kesesuaian Latar Belakang\n  objectPosition: Posisi Latar Belakang\n  overlayColor: Warna Hamparan\n  overlayMixBlendMode: Mode Campuran Campuran Hamparan\n  per_monitor: Per monitor\n  playback: Kecepatan pemutaran\n  position:\n    bottom: Bawah\n    center: Pusat\n    left: Kiri\n    right: Benar.\n    top: Atas\n  processing_video: Memproses video\n  random: Mengacak tayangan slide\n  saturation: Kejenuhan\n  seconds: detik\n  select_collection: Pilih Koleksi\n  thumbnail_generation_complete: Pembuatan Gambar Kecil Selesai\n  thumbnail_generation_finished: Pembuatan thumbnail telah selesai dengan sukses\n  wallpaper_collection: Koleksi Kertas Dinding\n  wallpaper_settings: Pengaturan Wallpaper\n  withOverlay: Dengan Overlay\n  workspace_collections: Koleksi Ruang Kerja\nweg:\n  auto_hide: Sembunyi otomatis\n  delay_to_hide: Penundaan untuk bersembunyi\n  delay_to_show: Keterlambatan untuk ditampilkan\n  dock_side: Posisi\n  enable: Aktifkan Dock/Taskbar\n  filtering: Pemfilteran Item\n  gap: Celah\n  hide_mode:\n    always: Selalu\n    never: Tidak pernah\n    on_overlap: Di tumpang tindih\n  items:\n    gap: Ruang antar item\n    label: Item\n    pinned_visibility:\n      always: Selalu\n      label: Visibilitas Item yang Disematkan\n      when_primary: Saat monitor menjadi yang utama\n    show_instance_counter: Tampilkan penghitung jendela yang terbuka\n    show_window_title: Menampilkan judul jendela yang terbuka (hanya horizontal)\n    size: Ukuran item\n    split_windows: Split Windows (satu item per jendela)\n    temporal_visibility:\n      all: Semua\n      label: Visibilitas Item yang Tidak Disematkan\n      on_monitor: Di Monitor\n    visible_separators: Pemisah yang terlihat\n  label: Dock/Taskbar\n  margin: Batas\n  mode:\n    full_width: Lebar layar penuh\n    min_content: Sekecil mungkin\n  padding: Lapisan\n  show_end_task: Menampilkan tugas akhir di bilah tugas\n  width: Lebar\nwelcome:\n  give_a_review: Berikan Ulasan\n  message: >-\n    Seelen UI adalah lingkungan desktop sumber terbuka dan gratis untuk Windows.\n    Di sini Anda memiliki kendali penuh atas tampilan dan perilaku desktop Anda,\n    memungkinkan Anda menyesuaikan setiap detail agar sesuai dengan alur kerja\n    dan gaya Anda.\n  ok: Mari kita mulai!\n  review: >-\n    Jika Anda senang menggunakan Seelen UI, pertimbangkan untuk meninggalkan\n    ulasan di toko — masukan Anda membantu proyek tumbuh dan meningkat.\n  title: Selamat datang di Seelen UI!\nwidget:\n  enable: Aktifkan widget ini\n  enable_for_monitor: Aktifkan pada monitor ini\n  instances: Contoh\nwm:\n  animations:\n    duration: Durasi Animasi (MS)\n    ease_function: Fungsi pelonggaran animasi\n    enable: Aktifkan animasi Window\n  author: Pengarang\n  border:\n    enable: Aktifkan Perbatasan Jendela\n    offset: Offset perbatasan\n    width: Lebar perbatasan\n  description: Keterangan\n  drag_behavior: Perilaku Seret\n  drag_behavior_options:\n    sort: Sortir (urutkan ulang jendela sambil menyeret)\n    swap: Swap (menukar jendela pada drag end)\n  enable: Aktifkan Manajer Jendela Ubin\n  layout: Tata letak\n  resize_delta: Ubah Ubah Delta (%)\n  space_between_containers: Ruang antar wadah\n  workspace_offset: Offset ruang kerja (margin)\n  workspace_padding: Padding ruang kerja\n'yes': Ya\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/is.yml",
    "content": "action:\n  confirm: Ertu viss?\n  confirm_body: Ekki er hægt að afturkalla þessa aðgerð.\napps_configurations:\n  app:\n    bindings: Binding (athugaðu að báðir valkostirnir eru nauðsynlegir)\n    category: Flokkur\n    category_placeholder: Enginn\n    monitor: Fylgstu með\n    monitor_placeholder: Enginn\n    name: Nafn\n    ok_create: Búa til\n    ok_edit: UPDATE\n    ok_readonly: Breyta sem nýjum\n    options:\n      NoInteractive: Engin gagnvirk\n      VdPinned: Sýna á öllum vinnusvæðum\n      WmFloat: Twm - Byrjaðu að fljóta\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Stjórna ekki\n    options_label: Auka valkostir\n    title_create: Búa til {{nafn}}\n    title_edit: Breyting {{name}}\n    title_readonly: Skoða {{nafn}}\n    weg_options_label: Valkostir bryggju/verkefna\n    wm_options_label: Valkostir gluggastjóra\n    workspace: Vinnusvæði\n    workspace_placeholder: Enginn\n  bundled_msg: >-\n    Þessar búnt stillingar eru ekki breytanlegar og eru hannaðar til að veita\n    þér bestu upplifunina án aðlögunar. Þeir stilla sjálfkrafa algengustu\n    forritin fyrir þig.\n  bundled_title: App stillt saman með Seelen\n  confirm_delete: Ertu viss um að þú viljir eyða þessari stillingu/s?\n  confirm_delete_title: Staðfestu að eyða\n  delete: Eyða\n  export: Flytja út\n  export_full: Útflutningsstillingar eftir umsókn\n  extra_info: >-\n    Seelen UI Notaðu aðeins eitt auðkenni á hvert forrit (fyrsta samsvörun sem\n    fannst) þannig að röðin í því hvernig eru sértæk er mikilvæg, nýjasta bætt\n    við verður forgangsraðað, eins og athugið að töflunni er flokkað sjálfgefið\n    frá nýjasta til gamla.\n  identifier:\n    add_block: Bæta við blokk\n    and: Og\n    id: Auðkenni\n    kind: Þekkja með\n    matching_strategy: Samsvarandi stefna\n    matching_strategy_option:\n      contains: Inniheldur\n      ends_with: Endar með\n      equals: Jafnt\n      regex: Regluleg tjáning\n      starts_with: Byrjar með\n    negation: Afneitar samsvörun\n    or: Eða\n    remove: Eyða blokk\n    type:\n      class: bekk\n      exe: Exe\n      path: Slóð\n      title: Titill\n  import: Flytja inn\n  import_full: Innflutningsstillingar eftir umsókn\n  new: Nýtt\n  search: Leitaðu\n  swap: Skiptu um\ncancel: Hætta við\nclose: Lokaðu\ndelete: Eyða\ndevtools:\n  app_folders: App möppur\n  custom_config_file: Hlaða sérsniðna config skrá\n  data_folder: Gagnamappa\n  enable: Virkja verktaki verktaki\n  install_folder: Uppsetningarmöppu\n  load: Hlaða\n  settings_file: Stillingarskrá\n  simulate_perm:\n    label: Líktu eftir beiðni um leyfi fyrir græju\n    result_allowed: ✓ Leyfi veitt\n    result_denied: ✗ Leyfi hafnað\n    trigger: Herma eftir\n    widget_id_placeholder: '@höfundur/nafn búnaðar'\nextras:\n  clear_icons: Hreinsa kerfistákn skyndiminni\n  clear_icons_tooltip: Endurræsing gæti verið nauðsynleg til að taka gildi að fullu á allar búnaðir\n  exit: Hætta/hætta\n  links: Opinberir hlekkir\n  relaunch: Fylgdu aftur\n  version: Útgáfa\n  version_fixed: >-\n    Forritið og WebView2 Runtime útgáfur eru fastar. Þetta þýðir að forritið fær\n    ekki uppfærslur og WebView2 Runtime verður ekki uppfært sjálfkrafa með\n    Windows uppfærslum.\ngeneral:\n  accent_color: Hreim litur\n  date_format: Dagsetningarsnið\n  date_format_how_to: Hvernig á að skrifa dagsetningarsnið?\n  hardware_acceleration: Vélbúnaðarhröðun\n  hardware_acceleration_description: >-\n    Slökkt er á vélbúnaðarhröðun mun draga úr minnisnotkun en getur valdið\n    afköstum. Er óhætt að slökkva á því ef þú notar ekki lifandi veggfóður.\n  icon_pack:\n    available: Lausir táknpakkar\n    selected: Virkir táknpakkar\n  language: Tungumál\n  monday: mánudag\n  performance_mode:\n    on_battery: Á rafhlöðu\n    on_energy_saver: Á orkusparnað\n    options:\n      disabled: Óvirkur\n      extreme: Öfgafullt\n      minimal: Lágmarks\n    plugged: Tengt eða hleðsla\n  polling_interval: Tímabil kerfiskönnunar\n  polling_interval_description: >-\n    Hversu oft (í sekúndum) Seelen UI athugar kerfisauðlindir eins og örgjörva,\n    vinnsluminni, netkerfi og diskvirkni. Minni tala þýðir tíðari uppfærslur, en\n    aðeins meiri auðlindanotkun.\n  saturday: laugardag\n  start_of_week: Upphaf vikunnar\n  startup: Hlaupa í gangsetningu?\n  sunday: sunnudag\n  theme:\n    available: Þemu í boði\n    selected: Virk þemu\nheader:\n  labels:\n    config: Stillingar\n    developer: Fyrir verktaki\n    extras: Aukahlutir\n    general: Almennt\n    home: Heim\n    icon_pack_editor: Skyndimennd tákn\n    iconpack: Táknpakkar\n    monitors: Fylgist með\n    plugin: Viðbætur\n    resources: Auðlindir\n    shortcuts: Flýtileiðir\n    soundpack: Hljóðpakkar\n    specific_apps: Stillingar eftir umsókn\n    theme: Þemu\n    virtual_desk: Sýndarskjáborð\n    wallpaper: Veggfóður\n    widget: Búnaður\nhome:\n  new_resources: Ný úrræði\ninherit: Erfa\ninProgress: Í vinnslu...\ninsert: Settu inn\nloading: Hleðsla ...\nmiscellaneous: Ýmislegt\nmonitors_configurations:\n  label: Monitor {{Index}}\nmore: Meira\n'no': Nei\nopen: Opið\nquit: Hættu\nremove: Fjarlægðu\nreset_all_to_default: Endurstilla allt í sjálfgefin gildi\nreset_to_default: Endurstilla í sjálfgefið gildi\nresources:\n  app_outdated: Þessi auðlind krefst nýrrar útgáfu af Seelen HÍ til að virka rétt.\n  corrupted_wallpaper: Mistókst að draga út smámynd - skemmd eða óstudd myndbandssnið\n  delete: Eyða auðlindinni\n  discover: Uppgötvaðu fleiri úrræði\n  has_update: Uppfærsla er í boði\n  high_impact: Mikil áhrif á frammistöðu\n  import_wallpapers: Flytja inn staðbundið veggfóður\n  open_folder: Opin auðlindamappa\n  outdated: >-\n    Þessi úrræði var hönnuð fyrir eldri útgáfu af Seelen HÍ og gæti ekki virkað\n    rétt.\n  see_on_website: Sjá á heimasíðu\nreview_request:\n  not_now: Ekki núna\n  sure: Jú!\n  title: Hefurðu gaman af Seelen UI?\nsave: Vista\nsave_and_restart: Vista og endurræsa\nsearch: Leita\nsee_more: Sjáðu meira\nshortcuts:\n  duplicate_error: Þessi flýtileið er afrituð\n  enable: Virkja innbyggt flýtileiðkerfi\n  enable_tooltip: >-\n    Slökktu á ef þú munt innleiða þitt eigið flýtileiðkerfi með Seelen UI\n    viðskiptavininum\n  labels:\n    create_new_workspace: Búðu til nýja vinnusvæði\n    cycle_stack_next: Hringrás stafla næst\n    cycle_stack_prev: Hringrás stafla fyrri\n    cycle_wallpaper_next: Breyttu í næsta veggfóður\n    cycle_wallpaper_prev: Breytast í fyrra veggfóður\n    decrease_height: Minnka hæð\n    decrease_width: Minnka breidd\n    destroy_current_workspace: Eyðileggja núverandi vinnusvæði\n    focus_bottom: Fókus botn\n    focus_latest: Fókus síðast\n    focus_left: Einbeittu til vinstri\n    focus_right: Einbeittu þér rétt\n    focus_top: Fókus toppur\n    increase_height: Auka hæð\n    increase_width: Auka breidd\n    misc_force_quit: Force hætti\n    misc_force_restart: Force endurræsing\n    misc_open_settings: Opnar stillingar\n    misc_toggle_lock_tracing: Skiptu um rekja lás (logs)\n    misc_toggle_win_event_tracing: Skiptu um Win Event Tracing (logs)\n    move_to_workspace: Fara í vinnusvæði {{0}}\n    move_window_down: Færðu glugga til botns\n    move_window_left: Færðu glugga til vinstri\n    move_window_right: Færðu glugga til hægri\n    move_window_up: Færðu glugga efst\n    pause_tiling: Hlé flísar gluggastjóri\n    reserve_bottom: Panta botn\n    reserve_float: Varasjóður flot\n    reserve_left: Pantaðu til vinstri\n    reserve_right: Pantaðu rétt\n    reserve_stack: Panta stafla\n    reserve_top: Panta topp\n    restore_sizes: Endurheimta stærðir\n    send_to_workspace: Sendu á Workspace {{0}}\n    start_weg_app: Einbeittu eða byrjaðu forrit {{0}}\n    switch_to_next_workspace: Skiptu yfir í næsta vinnusvæði\n    switch_to_previous_workspace: Skiptu yfir í fyrri vinnusvæði\n    switch_workspace: Skiptu yfir í vinnusvæði {{0}}\n    toggle_float: Skiptu um glugga flotastillingu\n    toggle_monocle: Skiptu um vinnusvæði Monocle Mode\n  readonly_tooltip: Þetta er aðeins skrifvarinn\n  reset: Endurstilla í vanskil\nsides:\n  bottom: Botn\n  left: Vinstri\n  right: Ekki satt\n  top: Efst\ntoolbar:\n  auto_hide: Sjálfvirk fela\n  delay_to_hide: Seinkun á að fela\n  delay_to_show: Seinkun á sýningu\n  dock_side: Staða\n  enable: Virkja Fancy tækjastiku\n  hide_mode:\n    always: Alltaf\n    never: Aldrei\n    on_overlap: Á skörun\n  item_size: Atriðastærð\n  label: Tækjastikur\n  margin: Framlegð stærð\n  padding: Bólstrun Stærð\n  placeholder: {}\nupdate:\n  available: Uppfærsla í boði!\n  channel: Uppfæra rás\n  downloading: Niðurhal ...\nwall:\n  backgrounds: Veggfóður\n  blur: Þoka\n  cancel: Hætta við\n  close: Loka\n  collection_name: Nafn safns\n  collections: Söfn\n  contrast: Andstæður\n  corrupted_wallpapers_message: 'Skemmt veggfóður, íhugaðu að eyða þessum:'\n  create: Búa til\n  create_collection: Búðu til safn\n  default_collection: Sjálfgefið safn\n  delete_collection: Eyða safni\n  edit_collection: Breyta safni\n  enable: Virkja veggfóðursstjóra\n  extend: Framlengja prófkjör\n  fit:\n    contain: Inniheldur\n    cover: Cover\n    fill: Fylltu\n  flipHorizontal: Flip Lárétt\n  flipVertical: Flip lóðrétt\n  generating_thumbnails: Búa til veggfóðursmámyndir\n  hours: klukkustundir\n  interval: Skiptu um veggfóður hvert\n  minutes: mínútur\n  monitor_collection: Fylgjast með safni\n  multimonitor_behaviour: Fjölskjár hegðun\n  muted: Slökkva á myndhljóði\n  no_background: Tómar myndasýning, með því að nota bakgrunn þema í staðinn.\n  no_collections: Engin söfn búin til ennþá. Smelltu á + hnappinn til að búa til einn.\n  objectFit: Bakgrunnur passa\n  objectPosition: Bakgrunnsstaða\n  overlayColor: Yfirlag litur\n  overlayMixBlendMode: Yfirlagsblöndublöndunarstilling\n  per_monitor: Á hvern skjá\n  playback: Spilunarhraði\n  position:\n    bottom: Botn\n    center: Miðja\n    left: Vinstri\n    right: Ekki satt\n    top: Efst\n  processing_video: Vinnur myndband\n  random: Slendandi myndasýning\n  saturation: Mettun\n  seconds: sekúndur\n  select_collection: Veldu Safn\n  thumbnail_generation_complete: Smámyndagerð lokið\n  thumbnail_generation_finished: Smámyndagerð hefur lokið með góðum árangri\n  wallpaper_collection: Veggfóður safn\n  wallpaper_settings: Veggfóðurstillingar\n  withOverlay: Með yfirlagi\n  workspace_collections: Vinnusöfn\nweg:\n  auto_hide: Sjálfvirk fela\n  delay_to_hide: Seinkun á að fela\n  delay_to_show: Seinkun á sýningu\n  dock_side: Staða\n  enable: Virkja bryggju/verkefnastiku\n  filtering: Hlut síun\n  gap: Bil\n  hide_mode:\n    always: Alltaf\n    never: Aldrei\n    on_overlap: Á skörun\n  items:\n    gap: Rými milli atriða\n    label: Hlutir\n    pinned_visibility:\n      always: Alltaf\n      label: Sýnileiki festra hluta\n      when_primary: Þegar skjárinn er aðal\n    show_instance_counter: Sýna opinn Windows teljara\n    show_window_title: Sýna opinn glugga titil (aðeins lárétt)\n    size: Stærð hlutar\n    split_windows: Skiptir gluggar (einn hlutur í hverjum glugga)\n    temporal_visibility:\n      all: Allt\n      label: Sýnileiki ófestra hluta\n      on_monitor: Á Skjár\n    visible_separators: Sýnilegir skilju\n  label: Bryggju/verkefnabar\n  margin: Framlegð\n  mode:\n    full_width: Full skjábreidd\n    min_content: Lítið eins og hægt er\n  padding: Padding\n  show_end_task: Sýna lokverkefni í verkstikunni\n  width: Breidd\nwelcome:\n  give_a_review: Gefðu umsögn\n  message: >-\n    Seelen UI er ókeypis og opið skrifborðsumhverfi fyrir Windows. Hér hefur þú\n    fulla stjórn á því hvernig skjáborðið þitt lítur út og hegðar sér, sem gerir\n    þér kleift að sérsníða hvert smáatriði til að passa við vinnuflæði þitt og\n    stíl.\n  ok: Byrjum!\n  review: >-\n    Ef þú hefur gaman af því að nota Seelen UI skaltu íhuga að skilja eftir\n    umsögn um verslunina - athugasemdir þínar hjálpa verkefninu að vaxa og bæta.\n  title: Velkomin í Seelen HÍ!\nwidget:\n  enable: Virkja þennan búnað\n  enable_for_monitor: Virkja á þessum skjá\n  instances: Dæmi\nwm:\n  animations:\n    duration: Lengd hreyfimynda (MS)\n    ease_function: Leiðandi aðgerð\n    enable: Virkja teiknimyndir Window\n  author: Höfundur\n  border:\n    enable: Virkja landamæri glugga\n    offset: Landamæri offset\n    width: Breidd landamæra\n  description: Lýsing\n  drag_behavior: Draga hegðun\n  drag_behavior_options:\n    sort: Raða (endurraða gluggum á meðan þú dregur)\n    swap: Skipta (skipta um glugga á dragenda)\n  enable: Virkja flísagluggastjóra\n  layout: Skipulag\n  resize_delta: Breyta stærð delta (%)\n  space_between_containers: Rými milli gáma\n  workspace_offset: Vinnusvæði offset (framlegð)\n  workspace_padding: Vinnusvæði padding\n'yes': Já\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/it.yml",
    "content": "action:\n  confirm: Sei sicuro?\n  confirm_body: Questa azione non può essere annullata.\napps_configurations:\n  app:\n    bindings: Associazione (nota che entrambe le opzioni sono richieste)\n    category: Categoria\n    category_placeholder: Nessuna\n    monitor: Monitor\n    monitor_placeholder: Nessuno\n    name: Nome\n    ok_create: Crea\n    ok_edit: Aggiorna\n    ok_readonly: Modifica come Nuovo\n    options:\n      NoInteractive: Non interattivo\n      VdPinned: Mostra in tutte le aree di lavoro\n      WmFloat: Twm - Inizia a fluttuare\n      WmForce: Twm - Gestione della forza\n      WmUnmanage: Twm - Non gestibile\n    options_label: Opzioni Extra\n    title_create: Crea {{name}}\n    title_edit: Modifica {{name}}\n    title_readonly: Visualizzazione {{name}}\n    weg_options_label: Opzioni Dock/Barra delle Attività\n    wm_options_label: Opzioni Gestore delle Finestre\n    workspace: Workspace\n    workspace_placeholder: Nessuno\n  bundled_msg: >-\n    Queste configurazioni fornite non sono modificabili e sono progettate per\n    fornirvi la migliore esperienza senza personalizzazione. Configurano\n    automaticamente le applicazioni più comuni per voi.\n  bundled_title: Config App Fornite con Seelen\n  confirm_delete: Siete sicuri di voler eliminare questa/e configurazione/i?\n  confirm_delete_title: Conferma Eliminazione\n  delete: Elimina\n  export: Esporta\n  export_full: Esportazione delle impostazioni per applicazione\n  extra_info: >-\n    Seelen UI utilizza solo un identificatore per app (primo corrispondente\n    trovato), quindi l'ordine in cui sono specificate è importante; l'ultima\n    aggiunta avrà la priorità. Nota che la tabella è ordinata per impostazione\n    predefinita dalla più recente alla più vecchia.\n  identifier:\n    add_block: Aggiungi Blocco\n    and: E\n    id: Identificatore\n    kind: Identifica Per\n    matching_strategy: Strategia di Corrispondenza\n    matching_strategy_option:\n      contains: Contiene\n      ends_with: Termina con\n      equals: Uguali\n      regex: Espressione regolare\n      starts_with: Inizia con\n    negation: Negazione della Corrispondenza\n    or: O\n    remove: Elimina Blocco\n    type:\n      class: Classe\n      exe: Exe\n      path: Sentiero\n      title: Titolo\n  import: Importa\n  import_full: Importazione delle impostazioni per applicazione\n  new: Nuovo\n  search: Cerca\n  swap: Scambia\ncancel: Annulla\nclose: Chiudi\ndelete: Elimina\ndevtools:\n  app_folders: Cartelle App\n  custom_config_file: Carica File di Config Personalizzato\n  data_folder: Cartella Dati\n  enable: Abilita Strumenti Sviluppatore\n  install_folder: Cartella di Installazione\n  load: Carica\n  settings_file: File delle Impostazioni\n  simulate_perm:\n    label: Simulare la richiesta di autorizzazione del widget\n    result_allowed: ✓ Autorizzazione concessa\n    result_denied: ✗ Autorizzazione negata\n    trigger: Simulare\n    widget_id_placeholder: '@autore/nome-widget'\nextras:\n  clear_icons: Cancella la cache delle icone di sistema\n  clear_icons_tooltip: Potrebbe essere necessario un riavvio per avere effetto su tutti i widget.\n  exit: Esci\n  links: Link Ufficiali\n  relaunch: Riavvia\n  version: Versione\n  version_fixed: >-\n    Le versioni dell'applicazione e del runtime WebView2 sono fisse. Ciò\n    significa che l'applicazione non riceverà aggiornamenti e il runtime\n    WebView2 non verrà aggiornato automaticamente con gli aggiornamenti di\n    Windows.\ngeneral:\n  accent_color: Colore Accentuato\n  date_format: Formato Data\n  date_format_how_to: Come scrivere un formato data?\n  hardware_acceleration: Accelerazione hardware\n  hardware_acceleration_description: >-\n    La disabilitazione dell'accelerazione hardware ridurrà l'utilizzo della\n    memoria, ma può causare problemi di prestazioni. È sicuro disabilitarlo se\n    non utilizzerai sfondi live.\n  icon_pack:\n    available: Pacchetti di icone disponibili\n    selected: Pacchetti di icone attivi\n  language: Lingua\n  monday: Lunedi\n  performance_mode:\n    on_battery: Su batteria\n    on_energy_saver: Sull'energia risparmiando\n    options:\n      disabled: Disabile\n      extreme: Estremo\n      minimal: Minimo\n    plugged: Collegato o ricarica\n  polling_interval: Intervallo di polling del sistema\n  polling_interval_description: >-\n    La frequenza con cui (in secondi) Seelen UI controlla le risorse di sistema\n    come CPU, RAM, rete e attività del disco. Un numero inferiore indica\n    aggiornamenti più frequenti, ma un utilizzo delle risorse leggermente\n    superiore.\n  saturday: Sabato\n  start_of_week: Inizio settimana\n  startup: Eseguire all'avvio?\n  sunday: Domenica\n  theme:\n    available: Temi disponibili\n    selected: Temi attivi\nheader:\n  labels:\n    config: Configurazioni\n    developer: Per gli sviluppatori\n    extras: Extra\n    general: Generale\n    home: Home\n    icon_pack_editor: Icone in cache\n    iconpack: Pacchetti di icone\n    monitors: Monitor\n    plugin: Plugin\n    resources: Risorse\n    shortcuts: Scorciatoie\n    soundpack: Pacchetti di suoni\n    specific_apps: Impostazioni per applicazione\n    theme: Temi\n    virtual_desk: Desktop virtuali\n    wallpaper: Sfondi\n    widget: Widget\nhome:\n  new_resources: Nuove risorse\ninherit: Eredita\ninProgress: In corso...\ninsert: Inserisci\nloading: Caricamento...\nmiscellaneous: Varie\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Altro\n'no': 'NO'\nopen: Apri\nquit: Esci\nremove: Rimuovi\nreset_all_to_default: Ripristinare tutti i valori predefiniti\nreset_to_default: Ripristino del valore predefinito\nresources:\n  app_outdated: >-\n    Questa risorsa richiede una versione più recente di Seelen UI per funzionare\n    correttamente.\n  corrupted_wallpaper: >-\n    Impossibile estrarre la miniatura: formato video danneggiato o non\n    supportato\n  delete: Elimina risorsa\n  discover: Scoprire altre risorse\n  has_update: È disponibile un aggiornamento\n  high_impact: Elevato impatto sulle prestazioni\n  import_wallpapers: Importazione di sfondi locali\n  open_folder: Aprire la cartella delle risorse\n  outdated: >-\n    Questa risorsa è stata progettata per una versione precedente di Seelen UI e\n    potrebbe non funzionare correttamente.\n  see_on_website: Vedi sul sito web\nreview_request:\n  not_now: Non adesso\n  sure: Sicuro!\n  title: Ti piace l'interfaccia utente di Seelen?\nsave: Salva\nsave_and_restart: Salva e Riavvia\nsearch: Ricerca\nsee_more: Per saperne di più\nshortcuts:\n  duplicate_error: Questa scorciatoia è duplicata\n  enable: Abilita le scorciatoie integrate\n  enable_tooltip: >-\n    Disabilita se implementerai il tuo sistema di scorciatoie utilizzando il\n    client UI Seelen\n  labels:\n    create_new_workspace: Crea nuovo spazio di lavoro\n    cycle_stack_next: Cycle Stack Next\n    cycle_stack_prev: Cycle Stack precedente\n    cycle_wallpaper_next: Cambia allo sfondo successivo\n    cycle_wallpaper_prev: Passa allo sfondo precedente\n    decrease_height: Diminuire l'altezza\n    decrease_width: Diminuire la larghezza\n    destroy_current_workspace: Distruggi l'attuale spazio di lavoro\n    focus_bottom: Focus Bottom\n    focus_latest: Focus Ultime\n    focus_left: Focus a sinistra\n    focus_right: Concentrati bene\n    focus_top: Focus top\n    increase_height: Aumenta l'altezza\n    increase_width: Aumentare la larghezza\n    misc_force_quit: Force smettila\n    misc_force_restart: Riavvio forzato\n    misc_open_settings: Impostazioni aperte\n    misc_toggle_lock_tracing: Attiva il tracciamento del blocco (tronchi)\n    misc_toggle_win_event_tracing: Taggle Win Event Tracing (Logs)\n    move_to_workspace: Passa su Workspace {{0}}\n    move_window_down: Sposta la finestra sul fondo\n    move_window_left: Sposta la finestra a sinistra\n    move_window_right: Sposta la finestra a destra\n    move_window_up: Sposta la finestra in alto\n    pause_tiling: Pausa pausa manager delle finestre\n    reserve_bottom: Riserva Bottom\n    reserve_float: Float di riserva\n    reserve_left: Riserva a sinistra\n    reserve_right: Riserva a destra\n    reserve_stack: Stack di riserva\n    reserve_top: Riserva Top\n    restore_sizes: Ripristinare le dimensioni\n    send_to_workspace: Invia a Workspace {{0}}\n    start_weg_app: Focus o Start Application {{0}}\n    switch_to_next_workspace: Passa all'area di lavoro successiva\n    switch_to_previous_workspace: Passa all'area di lavoro precedente\n    switch_workspace: Passa a Workspace {{0}}\n    toggle_float: Attiva la modalità galleggiante della finestra\n    toggle_monocle: Attivare la modalità monoclo dell'area di lavoro\n  readonly_tooltip: Questo è un collegamento di sola lettura\n  reset: Ripristina le impostazioni predefinite\nsides:\n  bottom: Basso\n  left: Sinistra\n  right: Destra\n  top: Alto\ntoolbar:\n  auto_hide: Nascondi automaticamente\n  delay_to_hide: Ritardo per nascondere\n  delay_to_show: Ritardo per mostrare\n  dock_side: Posizione\n  enable: Abilita Barra Fancy\n  hide_mode:\n    always: Sempre\n    never: Mai\n    on_overlap: Sulla sovrapposizione\n  item_size: Dimensione dell'articolo\n  label: Barra\n  margin: Dimensione margine\n  padding: Dimensioni dell'imbottitura\n  placeholder: {}\nupdate:\n  available: Aggiornamento Disponibile!\n  channel: Canale di Aggiornamento\n  downloading: Download in corso...\nwall:\n  backgrounds: Sfondi\n  blur: Sfocatura\n  cancel: Cancellare\n  close: Vicina\n  collection_name: Nome della raccolta\n  collections: Collezioni\n  contrast: Contrasto\n  corrupted_wallpapers_message: 'Sfondi danneggiati, valuta la possibilità di eliminarli:'\n  create: Creare\n  create_collection: Crea raccolta\n  default_collection: Raccolta predefinita\n  delete_collection: Elimina raccolta\n  edit_collection: Modifica raccolta\n  enable: Abilita Gestore degli Sfondi\n  extend: Estendi primario\n  fit:\n    contain: Contenere\n    cover: Copertina\n    fill: Riempimento\n  flipHorizontal: Capovolgimento orizzontale\n  flipVertical: Flip verticale\n  generating_thumbnails: Generazione di miniature di sfondi\n  hours: ore\n  interval: Cambia sfondo ogni\n  minutes: minuti\n  monitor_collection: Monitorare la raccolta\n  multimonitor_behaviour: Comportamento multimonitor\n  muted: Disattiva l'audio del video\n  no_background: Slideshow vuoto, usa invece lo sfondo del tema.\n  no_collections: Nessuna raccolta creata ancora. Fare clic sul pulsante + per crearne uno.\n  objectFit: Sfondo adatto\n  objectPosition: Posizione di fondo\n  overlayColor: Colore di sovrapposizione\n  overlayMixBlendMode: Modalità di miscelazione della sovrapposizione\n  per_monitor: Per monitor\n  playback: Velocità di riproduzione\n  position:\n    bottom: Fondo\n    center: Centro\n    left: A sinistra\n    right: Diritto\n    top: In alto\n  processing_video: Elaborazione video\n  random: Slideshow casuale\n  saturation: Saturazione\n  seconds: secondi\n  select_collection: Seleziona Raccolta\n  thumbnail_generation_complete: Generazione delle miniature completata\n  thumbnail_generation_finished: La generazione delle miniature è stata completata correttamente\n  wallpaper_collection: Collezione di sfondi\n  wallpaper_settings: Impostazioni dello sfondo\n  withOverlay: Con sovrapposizione\n  workspace_collections: Raccolte di aree di lavoro\nweg:\n  auto_hide: Nascondi automaticamente\n  delay_to_hide: Ritardo per nascondere\n  delay_to_show: Ritardo per mostrare\n  dock_side: Posizione\n  enable: Abilita Dock/Barra delle Attività\n  filtering: Filtraggio degli articoli\n  gap: Spazio\n  hide_mode:\n    always: Sempre\n    never: Mai\n    on_overlap: Sulla sovrapposizione\n  items:\n    gap: Spazio tra gli Elementi\n    label: Elementi\n    pinned_visibility:\n      always: Sempre\n      label: Visibilità degli elementi appuntati\n      when_primary: Quando il monitor è primario\n    show_instance_counter: Mostra il contatore delle finestre aperte\n    show_window_title: Mostra il titolo della finestra aperta (solo in orizzontale)\n    size: Dimensione Elementi\n    split_windows: Finestre divise (un elemento per finestra)\n    temporal_visibility:\n      all: Tutto\n      label: Visibilità degli elementi non bloccati\n      on_monitor: Sul monitor\n    visible_separators: Separatori Visibili\n  label: Dock/Barra delle Attività\n  margin: Margine\n  mode:\n    full_width: Larghezza dello schermo intero\n    min_content: Piccolo quanto può essere\n  padding: Spaziatura\n  show_end_task: Mostra la fine dell'attività nella barra delle applicazioni\n  width: Larghezza\nwelcome:\n  give_a_review: Dai una recensione\n  message: >-\n    Seelen UI è un ambiente desktop gratuito e open source per Windows. Qui hai\n    il pieno controllo sull'aspetto e sul comportamento del tuo desktop,\n    permettendoti di personalizzare ogni dettaglio per adattarlo al tuo flusso\n    di lavoro e al tuo stile.\n  ok: Iniziamo!\n  review: >-\n    Se ti piace utilizzare l'interfaccia utente di Seelen, valuta la possibilità\n    di lasciare una recensione nello store: il tuo feedback aiuta il progetto a\n    crescere e migliorare.\n  title: Benvenuti nell'interfaccia utente di Seelen!\nwidget:\n  enable: Abilitare questo widget\n  enable_for_monitor: Abilita su questo monitor\n  instances: Istanze\nwm:\n  animations:\n    duration: Durata dell'animazione (MS)\n    ease_function: Funzione di allentamento dell'animazione\n    enable: Abilita le animazioni di Window\n  author: Autore\n  border:\n    enable: Abilita Bordo delle Finestre\n    offset: Offset Bordo\n    width: Larghezza Bordo\n  description: Descrizione\n  drag_behavior: Comportamento di trascinamento\n  drag_behavior_options:\n    sort: Ordina (riordina le finestre durante il trascinamento)\n    swap: Scambia (scambia le finestre alla fine del trascinamento)\n  enable: Abilita Gestore delle Finestre a Tiling\n  layout: Layout\n  resize_delta: Delta Ridimensionamento (percento)\n  space_between_containers: Spazio tra i Contenitori\n  workspace_offset: Offset dei Workspace (Margini)\n  workspace_padding: Spaziatura dei Workspace\n'yes': SÌ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ja.yml",
    "content": "action:\n  confirm: 操作を続行しますか？\n  confirm_body: このアクションを元に戻すことはできません。\napps_configurations:\n  app:\n    bindings: バインディング（両方のオプションが必要です）\n    category: カテゴリー\n    category_placeholder: なし\n    monitor: モニター\n    monitor_placeholder: なし\n    name: 名前\n    ok_create: 作成\n    ok_edit: 適用\n    ok_readonly: 新規設定を作成\n    options:\n      NoInteractive: インタラクティブなし\n      VdPinned: すべてのワークスペースに表示\n      WmFloat: Twm - フローティングを開始\n      WmForce: Twm - 強制管理\n      WmUnmanage: Twm - 管理解除\n    options_label: 追加の設定\n    title_create: '{{name}}を作成する'\n    title_edit: 編集{{name}}\n    title_readonly: 「{{name}}」を表示しています\n    weg_options_label: ドック/タスクバーの設定\n    wm_options_label: ウィンドウマネージャーの設定\n    workspace: ワークスペース\n    workspace_placeholder: なし\n  bundled_msg: >-\n    これらのバンドルされた構成は編集可能ではなく、カスタマイズなしで最高の体験を提供するように設計されています。\n    最も一般的なアプリケーションを自動的に構成します。\n  bundled_title: Seelenにバンドルされたアプリの構成\n  confirm_delete: この構成を削除しますか？\n  confirm_delete_title: 削除の確認\n  delete: 消去\n  export: エクスポート\n  export_full: アプリケーションごとに設定をエクスポート\n  extra_info: >-\n    SeelenUIは、アプリごとに1つの識別子のみを使用します（最初に一致したものが使用されます）。そのため、識別方法の順序が重要であり、最新の追加が優先されます。\n  identifier:\n    add_block: ブロックを追加\n    and: かつ\n    id: 識別子\n    kind: 識別方法\n    matching_strategy: マッチングの方法\n    matching_strategy_option:\n      contains: 含む\n      ends_with: で終わる\n      equals: 等しい\n      regex: 正規表現\n      starts_with: で始まる\n    negation: 否定\n    or: または\n    remove: 削除\n    type:\n      class: クラス\n      exe: EXE\n      path: パス\n      title: タイトル\n  import: インポート\n  import_full: アプリケーション別設定のインポート\n  new: 新規\n  search: 検索\n  swap: 入れ替え\ncancel: キャンセル\nclose: 閉じる\ndelete: 消去\ndevtools:\n  app_folders: アプリのフォルダー\n  custom_config_file: カスタム構成ファイルをロードする\n  data_folder: データのフォルダー\n  enable: 開発者ツールを有効にする\n  install_folder: アプリのフォルダー\n  load: ロード\n  settings_file: 設定ファイル\n  simulate_perm:\n    label: ウィジェットの許可リクエストをシミュレートする\n    result_allowed: ✓ 許可の付与\n    result_denied: ✗ 許可が拒否されました\n    trigger: シミュレートする\n    widget_id_placeholder: '@作成者/ウィジェット名'\nextras:\n  clear_icons: システムアイコンキャッシュをクリアする\n  clear_icons_tooltip: すべてのウィジェットに完全に反映させるには、再起動が必要な場合があります。\n  exit: SeelenUIを終了\n  links: リンク\n  relaunch: SeelenUIを再起動\n  version: バージョン\n  version_fixed: >-\n    アプリケーションとWebView2 Runtimeのバージョンは固定されています。これは、アプリケーションが更新を受信せず、WebView2\n    ランタイムが Windows 更新で自動的に更新されないことを意味します。\ngeneral:\n  accent_color: アクセントカラー\n  date_format: 日付形式\n  date_format_how_to: 日付の形式はどうやって書くのでしょうか？\n  hardware_acceleration: ハードウェアアクセラレーション\n  hardware_acceleration_description: >-\n    ハードウェア\n    アクセラレーションを無効にするとメモリの使用量は減りますが、パフォーマンスの問題が発生する可能性があります。ライブ壁紙を使用しない場合は無効にしても安全です。\n  icon_pack:\n    available: 利用可能なアイコンパック\n    selected: アクティブなアイコンパック\n  language: 言語\n  monday: 月曜日\n  performance_mode:\n    on_battery: バッテリー駆動時\n    on_energy_saver: 省電力モード時\n    options:\n      disabled: 無効\n      extreme: 極限\n      minimal: 最小限\n    plugged: 電源接続時\n  polling_interval: システムのポーリング間隔\n  polling_interval_description: >-\n    Seelen UI が CPU、RAM、ネットワーク、ディスク アクティビティなどのシステム リソースをチェックする頻度 (秒単位)。\n    数値が小さいほど、更新頻度は高くなりますが、リソース使用量がわずかに高くなります。\n  saturday: 土曜日\n  start_of_week: 週の始まり\n  startup: 起動時に実行する\n  sunday: 日曜日\n  theme:\n    available: 利用可能なテーマ\n    selected: アクティブなテーマ\nheader:\n  labels:\n    config: 設定\n    developer: 開発者向け\n    extras: エクストラ\n    general: 全般\n    home: ホーム\n    icon_pack_editor: キャッシュされたアイコン\n    iconpack: アイコンパック\n    monitors: モニター\n    plugin: プラグイン\n    resources: リソース\n    shortcuts: ショートカットキー\n    soundpack: サウンドパック\n    specific_apps: アプリケーション別設定\n    theme: テーマ\n    virtual_desk: 仮想デスクトップ\n    wallpaper: 壁紙\n    widget: ウィジェット\nhome:\n  new_resources: 新しいリソース\ninherit: 継承\ninProgress: 近日実装予定...\ninsert: 挿入\nloading: 読み込み中...\nmiscellaneous: その他\nmonitors_configurations:\n  label: モニター{{index}}\nmore: さらに表示\n'no': いいえ\nopen: 開く\nquit: 終了\nremove: 取り除く\nreset_all_to_default: すべての設定をデフォルトに戻す\nreset_to_default: デフォルトに戻す\nresources:\n  app_outdated: このリソースが正しく動作するためには、新しいバージョンのSeelen UIが必要です。\n  corrupted_wallpaper: サムネイルの抽出に失敗しました - 破損したビデオ形式またはサポートされていないビデオ形式です\n  delete: リソースを削除します\n  discover: その他のリソース\n  has_update: アップデートが利用可能です\n  high_impact: パフォーマンスへの大きな影響\n  import_wallpapers: ローカル壁紙のインポート\n  open_folder: リソースフォルダを開く\n  outdated: このリソースは古いバージョンのSeelen UI用に設計されており、正しく動作しない可能性があります。\n  see_on_website: ウェブサイトで見る\nreview_request:\n  not_now: また今度\n  sure: もちろん！\n  title: Seelen UI をお楽しみですか？\nsave: 保存\nsave_and_restart: 保存して再起動\nsearch: 検索\nsee_more: もっと見る\nshortcuts:\n  duplicate_error: このショートカットは重複しています\n  enable: ビルトインショートカットシステムを有効にします\n  enable_tooltip: Seelen UIクライアントを使用して独自のショートカットシステムを実装する場合は無効にします\n  labels:\n    create_new_workspace: 新しいワークスペースを作成\n    cycle_stack_next: 次のサイクルスタックに進む\n    cycle_stack_prev: 前のサイクルスタックに戻る\n    cycle_wallpaper_next: 次の壁紙に変更\n    cycle_wallpaper_prev: 前の壁紙に変更\n    decrease_height: 高さを減らす\n    decrease_width: 幅を減らす\n    destroy_current_workspace: 現在のワークスペースを破棄\n    focus_bottom: 下にフォーカス\n    focus_latest: 最新のウィンドウにフォーカス\n    focus_left: 左にフォーカス\n    focus_right: 右にフォーカス\n    focus_top: 上にフォーカス\n    increase_height: 高さを増やす\n    increase_width: 幅を増やす\n    misc_force_quit: 強制終了する\n    misc_force_restart: 強制再起動する\n    misc_open_settings: 設定を開く\n    misc_toggle_lock_tracing: ロックトレース（ログ）の切り替え\n    misc_toggle_win_event_tracing: ウィンイベントトレース（ログ）の切り替え\n    move_to_workspace: ワークスペースに移動{{0}}\n    move_window_down: ウィンドウを下に移動する\n    move_window_left: ウィンドウを左に移動する\n    move_window_right: ウィンドウを右に移動する\n    move_window_up: ウィンドウを上に移動する\n    pause_tiling: タイル張りウィンドウマネージャーを一時停止する\n    reserve_bottom: 下を予約する\n    reserve_float: フローティングを予約する\n    reserve_left: 左を予約する\n    reserve_right: 右を予約する\n    reserve_stack: スタックを予約する\n    reserve_top: 上を予約する\n    restore_sizes: サイズを復元する\n    send_to_workspace: ワークスペース {{0}} に送信\n    start_weg_app: アプリケーション {{0}} にフォーカスまたは開始\n    switch_to_next_workspace: 次のワークスペースに切り替え\n    switch_to_previous_workspace: 前のワークスペースに切り替え\n    switch_workspace: ワークスペースに切り替え{{0}}\n    toggle_float: ウィンドウフロートモードを切り替え\n    toggle_monocle: ワークスペースモノクルモードの切り替え\n  readonly_tooltip: 読み取り専用\n  reset: デフォルトにリセット\nsides:\n  bottom: 下\n  left: 左\n  right: 右\n  top: 上\ntoolbar:\n  auto_hide: 自動的に隠す\n  delay_to_hide: 隠れるまでの時間\n  delay_to_show: 表示されるまでの時間\n  dock_side: ポジション\n  enable: ツールバーを有効にする\n  hide_mode:\n    always: 常に\n    never: 無効\n    on_overlap: 重なり時\n  item_size: 商品サイズ\n  label: ツールバーの設定\n  margin: 余白サイズ\n  padding: パディングサイズ\n  placeholder: {}\nupdate:\n  available: 更新が利用可能です！\n  channel: 更新チャンネル\n  downloading: ダウンロード中...\nwall:\n  backgrounds: 壁紙\n  blur: ブラー\n  cancel: キャンセル\n  close: 閉じる\n  collection_name: コレクション名\n  collections: コレクション\n  contrast: コントラスト\n  corrupted_wallpapers_message: 壁紙が破損している場合は、削除することを検討してください。\n  create: 作成する\n  create_collection: コレクションを作成\n  default_collection: デフォルトのコレクション\n  delete_collection: コレクションの削除\n  edit_collection: コレクションの編集\n  enable: 壁紙マネージャーを有効にする\n  extend: 拡張\n  fit:\n    contain: コンテイン\n    cover: カバー\n    fill: フィル\n  flipHorizontal: 水平反転\n  flipVertical: 垂直反転\n  generating_thumbnails: 壁紙のサムネイルの生成\n  hours: 時間\n  interval: 壁紙変更の間隔\n  minutes: 分\n  monitor_collection: モニターコレクション\n  multimonitor_behaviour: マルチモニターの動作\n  muted: ビデオの音声をミュート\n  no_background: 壁紙に使用するファイルを選択\n  no_collections: まだコレクションは作成されていません。 + ボタンをクリックして作成します。\n  objectFit: 背景のフィット\n  objectPosition: 背景の位置\n  overlayColor: オーバーレイカラー\n  overlayMixBlendMode: オーバーレイミックスブレンドモード\n  per_monitor: モニターごと\n  playback: 再生速度\n  position:\n    bottom: 下\n    center: 中央\n    left: 左\n    right: 右\n    top: 上\n  processing_video: ビデオの処理\n  random: スライドショーをランダム化\n  saturation: 彩度\n  seconds: 秒\n  select_collection: コレクションの選択\n  thumbnail_generation_complete: サムネイルの生成が完了しました\n  thumbnail_generation_finished: サムネイルの生成が正常に終了しました\n  wallpaper_collection: 壁紙コレクション\n  wallpaper_settings: 壁紙の設定\n  withOverlay: オーバーレイ付き\n  workspace_collections: ワークスペースコレクション\nweg:\n  auto_hide: 自動的に隠す\n  delay_to_hide: 隠れるまでの時間\n  delay_to_show: 表示されるまでの時間\n  dock_side: 位置\n  enable: ドック（タスクバー）を有効にする\n  filtering: アイテムのフィルタリング\n  gap: アイテム間のスペース\n  hide_mode:\n    always: 常に\n    never: 無効\n    on_overlap: 重なり時\n  items:\n    gap: アイテム間のスペース\n    label: ドック上のアイテム\n    pinned_visibility:\n      always: 常に\n      label: ピン留めされたアイテムの表示設定\n      when_primary: モニターがプライマリの場合\n    show_instance_counter: 開いているウィンドウのカウンターを表示する\n    show_window_title: 開いているウィンドウのタイトルを表示する（水平方向のみ）\n    size: アイテムのサイズ\n    split_windows: ウィンドウの分割 (ウィンドウごとに 1 つのアイテム)\n    temporal_visibility:\n      all: 全て\n      label: 固定されていないアイテムの表示設定\n      on_monitor: モニター上\n    visible_separators: セパレーターを表示する\n  label: ドック（タスクバー）\n  margin: ドックの隙間\n  mode:\n    full_width: 全画面幅\n    min_content: できるだけ小さく\n  padding: 余白\n  show_end_task: タスクバーに終了ボタンを表示する\n  width: ドックの幅\nwelcome:\n  give_a_review: レビューを投稿する\n  message: >-\n    Seelen UI は、Windows 用の無料のオープンソース\n    デスクトップ環境です。ここでは、デスクトップの外観や動作を完全に制御でき、ワークフローやスタイルに合わせてあらゆる詳細をカスタマイズできます。\n  ok: 始めましょう！\n  review: >-\n    Seelen UI\n    の使用を楽しんでいる場合は、ストアにレビューを残すことを検討してください。あなたのフィードバックはプロジェクトの成長と改善に役立ちます。\n  title: Seelen UI へようこそ!\nwidget:\n  enable: このウィジェットを有効にする\n  enable_for_monitor: このモニターで有効にする\n  instances: インスタンス\nwm:\n  animations:\n    duration: アニメーション期間（MS）\n    ease_function: アニメーション緩和機能\n    enable: ウィンドウのアニメーションを有効にします\n  author: 製作者\n  border:\n    enable: ウィンドウの境界線を描画する\n    offset: 境界線の余白\n    width: 線幅\n  description: 説明\n  drag_behavior: ドラッグ動作\n  drag_behavior_options:\n    sort: 並べ替え (ドラッグしながらウィンドウの順序を変更)\n    swap: スワップ (ドラッグ終了時にウィンドウをスワップ)\n  enable: タイリングウィンドウマネージャーを有効にする\n  layout: レイアウト\n  resize_delta: リサイズ時の変化率（％）\n  space_between_containers: ウィンドウ間の間隔\n  workspace_offset: ワークスペース全体の間隔（マージン）\n  workspace_padding: ワークスペースの全体の間隔\n'yes': はい\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ka.yml",
    "content": "action:\n  confirm: დარწმუნებული ხარ?\n  confirm_body: ეს ქმედება შეუძლებელია.\napps_configurations:\n  app:\n    bindings: სავალდებულო (შენიშვნა საჭიროა ორივე ვარიანტი)\n    category: კატეგორია\n    category_placeholder: არაფერი\n    monitor: შემოწმება\n    monitor_placeholder: არაფერი\n    name: სახელი\n    ok_create: Შექმნა\n    ok_edit: განახლება\n    ok_readonly: რედაქტირება როგორც ახალი\n    options:\n      NoInteractive: არ არის ინტერაქტიული\n      VdPinned: ჩვენება ყველა სამუშაო სივრცეში\n      WmFloat: Twm - დაიწყე მცურავი\n      WmForce: Twm - ძალის მართვა\n      WmUnmanage: Twm - გაუქმება\n    options_label: დამატებითი პარამეტრები\n    title_create: '{{სახელის შექმნა}}'\n    title_edit: რედაქტირება {{სახელი}}\n    title_readonly: ნახვა {{სახელი}}\n    weg_options_label: დოკ/დავალების პანელის პარამეტრები\n    wm_options_label: ფანჯრის მენეჯერის პარამეტრები\n    workspace: სამუშაო ადგილი\n    workspace_placeholder: არაფერი\n  bundled_msg: >-\n    ეს შეფუთული კონფიგურაციები არ არის რედაქტირებული და შექმნილია იმისთვის, რომ\n    საუკეთესო გამოცდილება მოგაწოდოთ პერსონალიზაციის გარეშე. ისინი ავტომატურად\n    კონფიგურაციას უკეთებენ თქვენთვის ყველაზე გავრცელებულ პროგრამებს.\n  bundled_title: აპლიკაციის კონფიგურაცია შეფუთულია Seelen- ით\n  confirm_delete: დარწმუნებული ხართ, რომ გსურთ ამ კონფიგურაციის წაშლა?\n  confirm_delete_title: დაადასტურეთ წაშლა\n  delete: წაშლა\n  export: ექსპორტი\n  export_full: ექსპორტის პარამეტრები განაცხადით\n  extra_info: >-\n    Seelen ui გამოიყენეთ მხოლოდ ერთი იდენტიფიკატორი თითო აპლიკაციაში (პირველი\n    მატჩი ნაპოვნი), ასე რომ, თუ როგორ არის სპეციფიკურია სპეციფიკური, შეკვეთა,\n    უახლესი დამატებული პრიორიტეტული იქნება, რადგან შენიშვნა ცხრილი დალაგებულია\n    ნაგულისხმევი უახლესიდან ძველამდე.\n  identifier:\n    add_block: ბლოკის დამატება\n    and: და\n    id: პირადობა\n    kind: იდენტიფიცირება\n    matching_strategy: შესაბამისი სტრატეგია\n    matching_strategy_option:\n      contains: შეიცავს\n      ends_with: მთავრდება\n      equals: უდრის\n      regex: რეგულარული გამოხატულება\n      starts_with: იწყება იმით\n    negation: უარყოფითი შესატყვისი\n    or: ან\n    remove: წაშალეთ ბლოკი\n    type:\n      class: კლასი\n      exe: exe\n      path: ბილიკი\n      title: სათაური\n  import: იმპორტი\n  import_full: იმპორტის პარამეტრები განაცხადის საშუალებით\n  new: ახალი\n  search: ძებნა\n  swap: გაცვლა\ncancel: გაუქმება\nclose: ახლო\ndelete: წაშლა\ndevtools:\n  app_folders: აპლიკაციის საქაღალდეები\n  custom_config_file: ჩატვირთეთ პერსონალური კონფიგურაციის ფაილი\n  data_folder: მონაცემთა საქაღალდე\n  enable: ჩართეთ დეველოპერის ინსტრუმენტები\n  install_folder: ინსტალაციის საქაღალდე\n  load: დატვირთვა\n  settings_file: პარამეტრების ფაილი\n  simulate_perm:\n    label: ვიჯეტის ნებართვის მოთხოვნის სიმულაცია\n    result_allowed: ✓ გაცემულია ნებართვა\n    result_denied: ✗ ნებართვა უარყოფილია\n    trigger: სიმულაცია\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: სუფთა სისტემის ხატები ქეში\n  clear_icons_tooltip: გადატვირთვა შეიძლება საჭირო გახდეს, რომ სრულად იმოქმედოს ყველა ვიჯეტზე\n  exit: დატოვება/გასვლა\n  links: ოფიციალური ბმულები\n  relaunch: გაუქმება\n  version: ვერსია\n  version_fixed: >-\n    აპლიკაციისა და WebView2 Runtime ვერსიები დაფიქსირდა. ეს ნიშნავს, რომ\n    აპლიკაცია არ მიიღებს განახლებებს და WebView2 Runtime ავტომატურად არ\n    განახლდება Windows-ის განახლებებით.\ngeneral:\n  accent_color: აქცენტის ფერი\n  date_format: თარიღის ფორმატი\n  date_format_how_to: როგორ დავწეროთ თარიღის ფორმატი?\n  hardware_acceleration: აპარატურის აჩქარება\n  hardware_acceleration_description: >-\n    ტექნიკის აჩქარების გამორთვა შეამცირებს მეხსიერების გამოყენებას, მაგრამ\n    შეიძლება გამოიწვიოს მუშაობის პრობლემები. უსაფრთხოა გამორთვა, თუ არ\n    გამოიყენებთ ცოცხალ ფონებს.\n  icon_pack:\n    available: ხელმისაწვდომი ხატების პაკეტები\n    selected: აქტიური ხატების პაკეტები\n  language: Ენა\n  monday: ორშაბათი\n  performance_mode:\n    on_battery: ბატარეაზე\n    on_energy_saver: ენერგიის დაზოგვაზე\n    options:\n      disabled: უუნარო\n      extreme: უკიდურესი\n      minimal: მინიმალური\n    plugged: ჩართული ან დატენვა\n  polling_interval: სისტემის გამოკითხვის ინტერვალი\n  polling_interval_description: >-\n    რამდენად ხშირად (წამებში) Seelen UI ამოწმებს სისტემის რესურსებს, როგორიცაა\n    CPU, RAM, ქსელი და დისკის აქტივობა. უფრო მცირე რაოდენობა ნიშნავს უფრო ხშირ\n    განახლებებს, მაგრამ ოდნავ უფრო მეტ რესურსს.\n  saturday: შაბათი\n  start_of_week: კვირის დასაწყისი\n  startup: გაშვებით გაშვებით?\n  sunday: კვირა\n  theme:\n    available: ხელმისაწვდომი თემები\n    selected: აქტიური თემები\nheader:\n  labels:\n    config: კონფიგურაცია\n    developer: დეველოპერებისთვის\n    extras: ზედმეტი\n    general: გენერალი\n    home: სახლი\n    icon_pack_editor: დაცული ხატები\n    iconpack: ხატის პაკეტები\n    monitors: მონიტორები\n    plugin: ამხებო\n    resources: რესურსები\n    shortcuts: მალსახმობები\n    soundpack: ხმის პაკეტები\n    specific_apps: პარამეტრები აპლიკაციით\n    theme: თემები\n    virtual_desk: ვირტუალური სამუშაო მაგიდა\n    wallpaper: ფონები\n    widget: ვიჯეტები\nhome:\n  new_resources: ახალი რესურსები\ninherit: მემკვიდრეობით მიღება\ninProgress: Პროგრესირებს...\ninsert: ჩასმა\nloading: Ჩატვირთვა...\nmiscellaneous: შერეული\nmonitors_configurations:\n  label: მონიტორი {{ინდექსი}}\nmore: მეტი\n'no': არა\nopen: გახსნა\nquit: მიტოვება\nremove: ამოღება\nreset_all_to_default: ყველა ნაგულისხმევი მნიშვნელობებისთვის\nreset_to_default: ნაგულისხმევი მნიშვნელობის გადატვირთვა\nresources:\n  app_outdated: ეს რესურსი მოითხოვს Seelen UI– ს ახალ ვერსიას, რომ სწორად იმუშაოს.\n  corrupted_wallpaper: ესკიზის ამოღება ვერ მოხერხდა - ვიდეო ფორმატი დაზიანებულია ან მხარდაუჭერელია\n  delete: წაშალეთ რესურსი\n  discover: აღმოაჩინეთ მეტი რესურსი\n  has_update: განახლება ხელმისაწვდომია\n  high_impact: მაღალი გავლენა შესრულებაზე\n  import_wallpapers: ადგილობრივი ფონების იმპორტი\n  open_folder: ღია რესურსების საქაღალდე\n  outdated: >-\n    ეს რესურსი შეიქმნა Seelen UI- ს ძველი ვერსიისთვის და შესაძლოა არ იმუშაოს\n    სწორად.\n  see_on_website: იხილეთ საიტზე\nreview_request:\n  not_now: ახლა არა\n  sure: რა თქმა უნდა!\n  title: მოგწონთ Seelen UI?\nsave: Გადარჩენა\nsave_and_restart: შენახვა და გადატვირთვა\nsearch: ძიება\nsee_more: იხილეთ მეტი\nshortcuts:\n  duplicate_error: ეს მალსახმობი დუბლირებულია\n  enable: ჩართეთ ჩამონტაჟებული მალსახმობების სისტემა\n  enable_tooltip: >-\n    გამორთეთ, თუ თქვენ განახორციელებთ თქვენს მალსახმობების სისტემას Seelen UI\n    კლიენტის გამოყენებით\n  labels:\n    create_new_workspace: შექმენით ახალი სამუშაო ადგილი\n    cycle_stack_next: ციკლის დასტის შემდეგ\n    cycle_stack_prev: ციკლის დასტის წინა\n    cycle_wallpaper_next: შეცვალეთ შემდეგი ფონი\n    cycle_wallpaper_prev: წინა ფონიზე შეცვლა\n    decrease_height: სიმაღლის შემცირება\n    decrease_width: სიგანის შემცირება\n    destroy_current_workspace: გაანადგურე ამჟამინდელი სამუშაო ადგილი\n    focus_bottom: ფოკუსირება\n    focus_latest: ფოკუსირება უახლესი\n    focus_left: ფოკუსირება მარცხნივ\n    focus_right: ფოკუსირება სწორად\n    focus_top: ფოკუსირება\n    increase_height: სიმაღლის გაზრდა\n    increase_width: სიგანის გაზრდა\n    misc_force_quit: ძალის დატოვება\n    misc_force_restart: ძალის გადატვირთვა\n    misc_open_settings: ღია პარამეტრები\n    misc_toggle_lock_tracing: ჩაკეტილი საკეტის კვალი (ჟურნალი)\n    misc_toggle_win_event_tracing: გადარიცხეთ მოიგეთ ღონისძიების კვალი (ჟურნალი)\n    move_to_workspace: გადავიდეთ სამუშაო ადგილებზე {{0}}\n    move_window_down: ფანჯრის ქვედა ნაწილზე გადატანა\n    move_window_left: ფანჯრის მარცხნივ გადაადგილება\n    move_window_right: ფანჯრის მარჯვნივ გადაადგილება\n    move_window_up: ფანჯრის გადატანა ზემოდან\n    pause_tiling: პაუზის ჩამოსხმის ფანჯრის მენეჯერი\n    reserve_bottom: სარეზერვო ქვედა\n    reserve_float: სარეზერვო float\n    reserve_left: ნაკრძალი დარჩა\n    reserve_right: უფლება\n    reserve_stack: სარეზერვო დასტა\n    reserve_top: სარეზერვო ზედა\n    restore_sizes: აღადგინეთ ზომები\n    send_to_workspace: გაგზავნეთ სამუშაო სივრცეში {{0}}\n    start_weg_app: ფოკუსირება ან დაწყება განაცხადი {{0}}\n    switch_to_next_workspace: გადადით შემდეგ სამუშაო ადგილებზე\n    switch_to_previous_workspace: გადახვიდეთ წინა სამუშაო ადგილებზე\n    switch_workspace: გადადით სამუშაო ადგილებზე {{0}}\n    toggle_float: გადარიცხეთ ფანჯრის float რეჟიმი\n    toggle_monocle: გადართეთ სამუშაო ადგილის მონოკლის რეჟიმი\n  readonly_tooltip: ეს არის მხოლოდ წაკითხული მალსახმობი\n  reset: გადატვირთვა ნაგულისხმევებზე\nsides:\n  bottom: ძირი\n  left: მარცხენა\n  right: სწორი\n  top: თავი\ntoolbar:\n  auto_hide: ავტო დამალვა\n  delay_to_hide: დაგვიანებით დამალვა\n  delay_to_show: დაგვიანებით ჩვენება\n  dock_side: პოზიცია\n  enable: ჩართეთ ლამაზი პანელი\n  hide_mode:\n    always: ყოველთვის\n    never: არასოდეს\n    on_overlap: გადახურვაზე\n  item_size: ნივთის ზომა\n  label: ინსტრუმენტთა პანელი\n  margin: მარჟის ზომა\n  padding: ბალიშის ზომა\n  placeholder: {}\nupdate:\n  available: განახლება ხელმისაწვდომია!\n  channel: განაახლეთ არხი\n  downloading: ჩამოტვირთვა ...\nwall:\n  backgrounds: ფონები\n  blur: დაბენა\n  cancel: გაუქმება\n  close: დახურვა\n  collection_name: კოლექციის სახელი\n  collections: კოლექციები\n  contrast: წინააღმდეგობა\n  corrupted_wallpapers_message: 'დაზიანებული ფონები, განიხილეთ მათი წაშლა:'\n  create: შექმნა\n  create_collection: შექმენით კოლექცია\n  default_collection: ნაგულისხმევი კოლექცია\n  delete_collection: კოლექციის წაშლა\n  edit_collection: კოლექციის რედაქტირება\n  enable: ჩართეთ ფონი მენეჯერი\n  extend: გააფართოვეთ პირველადი\n  fit:\n    contain: დატევა\n    cover: სახურავი\n    fill: გავსება\n  flipHorizontal: Flip Horizontal\n  flipVertical: ფლიპ ვერტიკალური\n  generating_thumbnails: ფონის მინიატურების გენერირება\n  hours: საათი\n  interval: შეცვალეთ ფონი ყველა\n  minutes: წუთი\n  monitor_collection: მონიტორების კოლექცია\n  multimonitor_behaviour: მულტიმონიტორის ქცევა\n  muted: ვიდეო აუდიოს დადუმება\n  no_background: ცარიელი სლაიდშოუ, სამაგიეროდ თემის ფონის გამოყენებით.\n  no_collections: კოლექციები ჯერ არ არის შექმნილი. ერთის შესაქმნელად დააწკაპუნეთ + ღილაკს.\n  objectFit: ფონის შესაფერისი\n  objectPosition: ფონის პოზიცია\n  overlayColor: გადახურული ფერი\n  overlayMixBlendMode: Overlay Mix Blend Mode\n  per_monitor: თითო მონიტორზე\n  playback: აღწარმოების სიჩქარე\n  position:\n    bottom: ძირი\n    center: ცენტრი\n    left: მარცხენა\n    right: სწორი\n    top: თავი\n  processing_video: მიმდინარეობს ვიდეოს დამუშავება\n  random: რანდომიზაცია სლაიდშოუ\n  saturation: გაჯერება\n  seconds: წამები\n  select_collection: აირჩიეთ კოლექცია\n  thumbnail_generation_complete: მინიატურების გენერაცია დასრულებულია\n  thumbnail_generation_finished: ესკიზების შექმნა წარმატებით დასრულდა\n  wallpaper_collection: ფონების კოლექცია\n  wallpaper_settings: ფონი პარამეტრები\n  withOverlay: გადახურვით\n  workspace_collections: სამუშაო სივრცის კოლექციები\nweg:\n  auto_hide: Ავტომატური დამალვა\n  delay_to_hide: დაგვიანებით დამალვა\n  delay_to_show: დაგვიანებით ჩვენება\n  dock_side: პოზიცია\n  enable: ჩართეთ dock/taskbar\n  filtering: ნივთის ფილტრაცია\n  gap: ხარპი\n  hide_mode:\n    always: ყოველთვის\n    never: არასოდეს\n    on_overlap: გადახურვაზე\n  items:\n    gap: სივრცე ნივთებს შორის\n    label: ნივთები\n    pinned_visibility:\n      always: ყოველთვის\n      label: ჩამაგრებული ელემენტების ხილვადობა\n      when_primary: როცა მონიტორი პირველადია\n    show_instance_counter: აჩვენეთ ღია Windows Counter\n    show_window_title: აჩვენეთ ღია ფანჯრის სათაური (მხოლოდ ჰორიზონტალური)\n    size: ნივთის ზომა\n    split_windows: Windows-ის გაყოფა (თითო ელემენტი თითო ფანჯარაში)\n    temporal_visibility:\n      all: ყველა\n      label: ჩამაგრებული ერთეულების ხილვადობა\n      on_monitor: მონიტორზე\n    visible_separators: ხილული გამყოფი\n  label: დოკ/დავალების ზოლი\n  margin: ზღვარი\n  mode:\n    full_width: სრული ეკრანის სიგანე\n    min_content: რაც შეიძლება პატარა იყოს\n  padding: ბადება\n  show_end_task: აჩვენეთ საბოლოო დავალება დავალების ზოლში\n  width: სიგანე\nwelcome:\n  give_a_review: მიეცით მიმოხილვა\n  message: >-\n    Seelen UI არის უფასო და ღია კოდის დესკტოპის გარემო Windows-ისთვის. აქ თქვენ\n    გაქვთ სრული კონტროლი იმაზე, თუ როგორ გამოიყურება და იქცევა თქვენი დესკტოპი,\n    რაც საშუალებას გაძლევთ დააკონფიგურიროთ ყველა დეტალი თქვენს სამუშაო პროცესსა\n    და სტილში.\n  ok: დავიწყოთ!\n  review: >-\n    თუ გსურთ Seelen UI-ის გამოყენება, განიხილეთ მიმოხილვის დატოვება მაღაზიაში –\n    თქვენი გამოხმაურება ეხმარება პროექტს ზრდასა და გაუმჯობესებაში.\n  title: კეთილი იყოს თქვენი მობრძანება Seelen UI-ში!\nwidget:\n  enable: ჩართეთ ეს ვიჯეტი\n  enable_for_monitor: ჩართეთ ამ მონიტორზე\n  instances: შემთხვევები\nwm:\n  animations:\n    duration: ანიმაციის ხანგრძლივობა (MS)\n    ease_function: ანიმაციის შემსუბუქებული ფუნქცია\n    enable: ფანჯრის ანიმაციების ჩართვა\n  author: ავტორი\n  border:\n    enable: ფანჯრის საზღვრის ჩართვა\n    offset: სასაზღვრო ოფსეტური\n    width: საზღვრის სიგანე\n  description: აღწერილობა\n  drag_behavior: Drag Behavior\n  drag_behavior_options:\n    sort: სორტირება (გადააწყვეთ ფანჯრები გადატანისას)\n    swap: შეცვალეთ (გაცვალეთ ფანჯრები ჩავლებით)\n  enable: ჩართეთ ფილების ფანჯრის მენეჯერი\n  layout: გამოფენა\n  resize_delta: დელტას ზომის შეცვლა (%)\n  space_between_containers: სივრცე კონტეინერებს შორის\n  workspace_offset: სამუშაო ადგილები ოფსეტური (ზღვარი)\n  workspace_padding: სამუშაო ადგილები padding\n'yes': დიახ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/km.yml",
    "content": "action:\n  confirm: តើអ្នកប្រាកដទេ?\n  confirm_body: សកម្មភាពនេះមិនអាចធ្វើវិញបានទេ។\napps_configurations:\n  app:\n    bindings: ការចង (ចំណាំជម្រើសទាំងពីរត្រូវបានទាមទារ)\n    category: ប្រភេទ\n    category_placeholder: គ្មាន\n    monitor: ម៉ូនីទ័រ\n    monitor_placeholder: គ្មាន\n    name: ឈ្មោះ\n    ok_create: បង្កើត\n    ok_edit: ធ្វើបច្ចុប្បន្នភាព\n    ok_readonly: កែសម្រួលដូចថ្មី។\n    options:\n      NoInteractive: គ្មានអន្តរកម្ម\n      VdPinned: បង្ហាញនៅក្នុងកន្លែងធ្វើការទាំងអស់\n      WmFloat: TWM - ចាប់ផ្តើមអណ្តែត\n      WmForce: TWM - គ្រប់គ្រងកម្លាំង\n      WmUnmanage: TWM - Unmanage\n    options_label: ជម្រើសបន្ថែម\n    title_create: ការបង្កើត {{name}}\n    title_edit: ការកែសម្រួល {{ឈ្មោះ}}\n    title_readonly: កំពុងមើល {{name}}\n    weg_options_label: ជម្រើសចត / ភារកិច្ច\n    wm_options_label: ជម្រើសកម្មវិធីគ្រប់គ្រងបង្អួច\n    workspace: កន្លែងធ្វើការ\n    workspace_placeholder: គ្មាន\n  bundled_msg: >-\n    ការ​កំណត់​រចនាសម្ព័ន្ធ​ជា​កញ្ចប់​ទាំង​នេះ​មិន​អាច​កែ​សម្រួល​បាន​ទេ\n    ហើយ​ត្រូវ​បាន​រចនា​ឡើង​ដើម្បី​ផ្ដល់​ឱ្យ​អ្នក​នូវ​បទ​ពិសោធ​ដ៏​ល្អ​បំផុត​ដោយ​មិន​ចាំបាច់​ប្ដូរ​តាម​បំណង។\n    ពួកគេកំណត់រចនាសម្ព័ន្ធកម្មវិធីធម្មតាបំផុតសម្រាប់អ្នកដោយស្វ័យប្រវត្តិ។\n  bundled_title: កម្មវិធីកំណត់រចនាសម្ព័ន្ធបានរួមបញ្ចូលជាមួយ Seelen\n  confirm_delete: តើអ្នកប្រាកដថាចង់លុបការកំណត់រចនាសម្ព័ន្ធនេះទេ?\n  confirm_delete_title: បញ្ជាក់ការលុប\n  delete: លុប\n  export: នាំចេញ\n  export_full: ការនាំចេញការនាំចេញតាមពាក្យសុំ\n  extra_info: >-\n    Seeen UI ប្រើគ្រឿងសម្គាល់តែមួយប៉ុណ្ណោះក្នុងមួយកម្មវិធីមួយ\n    (រកឃើញការប្រកួតដំបូង)\n    ដូច្នេះការបញ្ជាទិញក្នុងរបៀបណាដែលមានលក្ខណៈសំខាន់គឺសំខាន់បំផុតនោះនៅពេលដែលការកត់ចំណាំតារាងត្រូវបានតម្រៀបតាមលំនាំដើមពីចុងក្រោយ។\n  identifier:\n    add_block: បន្ថែមប្លុក\n    and: និង\n    id: អ្នកកំណត់អត្តសញ្ញាណ\n    kind: កំណត់អត្តសញ្ញាណដោយ\n    matching_strategy: យុទ្ធសាស្ត្រផ្គូផ្គង\n    matching_strategy_option:\n      contains: មាន\n      ends_with: បញ្ចប់ដោយ\n      equals: ស្មើ\n      regex: ការបញ្ចេញមតិទៀងទាត់\n      starts_with: ចាប់ផ្តើមជាមួយ\n    negation: ការផ្គូផ្គងអវិជ្ជមាន\n    or: ឬ\n    remove: លុបប្លុក\n    type:\n      class: ថ្នាក់\n      exe: Exe\n      path: ផ្លូវ\n      title: ចំណងជើង\n  import: នាំចូល\n  import_full: ការកំណត់នាំចូលតាមពាក្យសុំ\n  new: ថ្មី។\n  search: ស្វែងរក\n  swap: ប្តូរ\ncancel: បោះបង់\nclose: បិត\ndelete: លុប\ndevtools:\n  app_folders: ថតកម្មវិធី\n  custom_config_file: ផ្ទុកឯកសារកំណត់រចនាសម្ព័ន្ធផ្ទាល់ខ្លួន\n  data_folder: ថតទិន្នន័យ\n  enable: បើកឧបករណ៍អ្នកអភិវឌ្ឍន៍\n  install_folder: ថតឯកសារដំឡើង\n  load: ផ្ទុក\n  settings_file: ឯកសារការកំណត់\n  simulate_perm:\n    label: ក្លែងធ្វើសំណើការអនុញ្ញាតធាតុក្រាហ្វិក\n    result_allowed: ✓មានការអនុញ្ញាត\n    result_denied: ✗ ការអនុញ្ញាតត្រូវបានបដិសេធ\n    trigger: ក្លែងធ្វើ\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: សម្អាតឃ្លាំងសម្ងាត់ប្រព័ន្ធ\n  clear_icons_tooltip: >-\n    ការចាប់ផ្តើមឡើងវិញអាចត្រូវបានតម្រូវឱ្យមានប្រសិទ្ធិភាពពេញលេញលើធាតុក្រាហ្វិកទាំងអស់\n  exit: ចេញ/ចេញ\n  links: តំណភ្ជាប់ផ្លូវការ\n  relaunch: បើកដំណើរការឡើងវិញ\n  version: កំណែ\n  version_fixed: >-\n    កំណែកម្មវិធី និង WebView2 Runtime ត្រូវបានជួសជុល។\n    នេះមានន័យថាកម្មវិធីនឹងមិនទទួលបានការអាប់ដេតទេ ហើយ WebView2 Runtime\n    នឹងមិនត្រូវបានអាប់ដេតដោយស្វ័យប្រវត្តិជាមួយនឹងការអាប់ដេតវីនដូទេ។\ngeneral:\n  accent_color: ពណ៌សង្កត់សំឡេង\n  date_format: ទ្រង់ទ្រាយកាលបរិច្ឆេទ\n  date_format_how_to: តើធ្វើដូចម្តេចដើម្បីសរសេរទម្រង់កាលបរិច្ឆេទ?\n  hardware_acceleration: ការបង្កើនល្បឿនផ្នែករឹង\n  hardware_acceleration_description: >-\n    ការបិទការបង្កើនល្បឿនផ្នែករឹងនឹងកាត់បន្ថយការប្រើប្រាស់អង្គចងចាំ\n    ប៉ុន្តែអាចបណ្តាលឱ្យមានបញ្ហាដំណើរការ។ មានសុវត្ថិភាពក្នុងការបិទ\n    ប្រសិនបើអ្នកមិនប្រើផ្ទាំងរូបភាពផ្ទាល់។\n  icon_pack:\n    available: កញ្ចប់រូបតំណាងដែលមាន\n    selected: កញ្ចប់រូបតំណាងសកម្ម\n  language: ភាសា\n  monday: ថ្ងៃច័ន្ទ\n  performance_mode:\n    on_battery: នៅលើថ្ម\n    on_energy_saver: នៅលើអ្នកសន្សំថាមពល\n    options:\n      disabled: កាយវិកលកម្ផ\n      extreme: ខ្លាមងបមផុត\n      minimal: ដេលអឹមតីម\n    plugged: ដោតឬសាក\n  polling_interval: ចន្លោះពេលបោះឆ្នោតប្រព័ន្ធ\n  polling_interval_description: >-\n    ញឹកញាប់ប៉ុណ្ណា (គិតជាវិនាទី) Seelen UI ពិនិត្យធនធានប្រព័ន្ធដូចជា CPU, RAM,\n    បណ្តាញ និងសកម្មភាពថាស។ ចំនួនតូចជាងនេះមានន័យថា\n    ការធ្វើបច្ចុប្បន្នភាពញឹកញាប់ជាងមុន ប៉ុន្តែការប្រើប្រាស់ធនធានខ្ពស់ជាងបន្តិច។\n  saturday: ថ្ងៃសៅរ៍\n  start_of_week: ការចាប់ផ្តើមនៃសប្តាហ៍\n  startup: ដំណើរការនៅពេលចាប់ផ្ដើម?\n  sunday: ថ្ងៃអាទិត្យ\n  theme:\n    available: ស្បែកដែលអាចប្រើបាន\n    selected: ស្បែកសកម្ម\nheader:\n  labels:\n    config: ការកំណត់រចនាសម្ព័ន្ធ\n    developer: សម្រាប់អ្នកអភិវឌ្ឍន៍\n    extras: ការបន្ថែម\n    general: ទូទៅ\n    home: ផ្ទហ\n    icon_pack_editor: រូបតំណាងឃ្លាំងសម្ងាត់\n    iconpack: រូបតំណាងកញ្ចប់\n    monitors: ម៉ូនីទ័រ\n    plugin: រអារ\n    resources: ធនធាន\n    shortcuts: ផ្លូវកាត់\n    soundpack: កញ្ចប់សំលេង\n    specific_apps: ការកំណត់តាមពាក្យសុំ\n    theme: ស្បែក\n    virtual_desk: ផ្ទៃតុនិម្មិត\n    wallpaper: ផាបផេច\n    widget: ធាតុក្រាហ្វិក\nhome:\n  new_resources: ធនធានថ្មី\ninherit: តតយលមរតក\ninProgress: កំពុង​ដំណើរការ...\ninsert: រចក\nloading: កំពុង​ផ្ទុក...\nmiscellaneous: ដេលមានផ្នេកខុសក្នា\nmonitors_configurations:\n  label: ម៉ូនីទ័រ {{សន្ទស្សន៍}}\nmore: រេចីនចាង\n'no': ដេលក្ផាន\nopen: បើក\nquit: ឈប់\nremove: រមកមិ avad ចេញ\nreset_all_to_default: កំណត់ឡើងវិញទាំងអស់ទៅតម្លៃលំនាំដើម\nreset_to_default: កំណត់ឡើងវិញទៅតម្លៃលំនាំដើម\nresources:\n  app_outdated: ធនធាននេះតម្រូវឱ្យមានកំណែថ្មីនៃ Seelen UI ដែលអាចធ្វើការបានត្រឹមត្រូវ។\n  corrupted_wallpaper: បរាជ័យក្នុងការទាញយករូបភាពតូច - ទ្រង់ទ្រាយវីដេអូខូច ឬមិនគាំទ្រ\n  delete: លុបធនធាន\n  discover: ស្វែងយល់ពីធនធានបន្ថែម\n  has_update: ការធ្វើបច្ចុប្បន្នភាពមាន\n  high_impact: ឥទ្ធិពលខ្ពស់លើការអនុវត្ត\n  import_wallpapers: នាំចូលផ្ទាំងរូបភាពក្នុងស្រុក\n  open_folder: បើកថតធនធាន\n  outdated: >-\n    ធនធាននេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់របស់ Seeen UI\n    ហើយប្រហែលជាមិនដំណើរការត្រឹមត្រូវទេ។\n  see_on_website: សូមមើលនៅលើគេហទំព័រ\nreview_request:\n  not_now: មិនមែនឥឡូវនេះទេ។\n  sure: ប្រាកដណាស់!\n  title: រីករាយជាមួយ Seelen UI?\nsave: រក្សាទុក\nsave_and_restart: រក្សាទុក & ចាប់ផ្តើមឡើងវិញ\nsearch: ស្វែងរក\nsee_more: មើលច្រើនទៀត\nshortcuts:\n  duplicate_error: ផ្លូវកាត់នេះត្រូវបានចម្លង\n  enable: បើកដំណើរការប្រព័ន្ធផ្លូវកាត់ដែលភ្ជាប់មកជាមួយ\n  enable_tooltip: >-\n    បិទប្រសិនបើអ្នកនឹងអនុវត្តប្រព័ន្ធផ្លូវកាត់ផ្ទាល់ខ្លួនរបស់អ្នកដោយប្រើម៉ាស៊ីនភ្ញៀវយូណេយូយូ\n  labels:\n    create_new_workspace: បង្កើតកន្លែងធ្វើការថ្មី\n    cycle_stack_next: វដ្តជង់បន្ទាប់បន្ទាប់\n    cycle_stack_prev: ជង់មុនវដ្តមុន\n    cycle_wallpaper_next: ប្តូរទៅផ្ទាំងរូបភាពបន្ទាប់\n    cycle_wallpaper_prev: ប្តូរទៅផ្ទាំងរូបភាពមុន\n    decrease_height: បន្ថយកម្ពស់\n    decrease_width: ថយចុះទទឹង\n    destroy_current_workspace: បំផ្លាញកន្លែងធ្វើការបច្ចុប្បន្ន\n    focus_bottom: ផ្តោតលើផ្នែកខាងក្រោម\n    focus_latest: ផ្តោតចុងក្រោយ\n    focus_left: ផ្តោតអារម្មណ៍ខាងឆ្វេង\n    focus_right: ផ្តោតសិទ្ធិត្រឹមត្រូវ\n    focus_top: ផ្តោតលើកំពូល\n    increase_height: បង្កើនកម្ពស់\n    increase_width: បង្កើនទទឹង\n    misc_force_quit: បង្ខំឱ្យឈប់\n    misc_force_restart: បង្ខំឱ្យចាប់ផ្តើមឡើងវិញ\n    misc_open_settings: បើកការកំណត់\n    misc_toggle_lock_tracing: បិទបើកការចាក់សោរ (កំណត់ហេតុ)\n    misc_toggle_win_event_tracing: បិទ / បើកព្រឹត្តិការណ៍ឈ្នះ (កំណត់ហេតុ)\n    move_to_workspace: ផ្លាស់ទីទៅកន្លែងធ្វើការ {{0}}\n    move_window_down: ផ្លាស់ទីបង្អួចទៅបាត\n    move_window_left: ផ្លាស់ទីបង្អួចទៅឆ្វេង\n    move_window_right: ផ្លាស់ទីបង្អួចទៅស្តាំ\n    move_window_up: ផ្លាស់ទីបង្អួចទៅខាងលើ\n    pause_tiling: ផ្អាកកម្មវិធីគ្រប់គ្រងបង្អួច\n    reserve_bottom: បាតបម្រុង\n    reserve_float: អណ្តែតអណ្តែត\n    reserve_left: បំរុងទុកខាងឆ្វេង\n    reserve_right: រក្សាសិទ្ធិ\n    reserve_stack: ជង់បម្រុង\n    reserve_top: បម្រុងខាងលើ\n    restore_sizes: ស្តារទំហំ\n    send_to_workspace: ផ្ញើទៅកន្លែងធ្វើការ {{0}}\n    start_weg_app: ផ្តោតអារម្មណ៍ឬចាប់ផ្តើមកម្មវិធី {{0}}\n    switch_to_next_workspace: ប្តូរទៅកន្លែងធ្វើការបន្ទាប់\n    switch_to_previous_workspace: ប្តូរទៅកន្លែងធ្វើការមុន\n    switch_workspace: ប្តូរទៅកន្លែងធ្វើការ {{0}}\n    toggle_float: បិទបើករបៀបអណ្តែតបង្អួច\n    toggle_monocle: បិទបើករបៀបម៉ូនីកឃែរ\n  readonly_tooltip: នេះគឺជាផ្លូវកាត់ដែលបានតែអាន\n  reset: កំណត់ឡើងវិញនូវលំនាំដើម\nsides:\n  bottom: បាត\n  left: ឆ្វេង\n  right: ត្រូវហើយ។\n  top: កំពូល\ntoolbar:\n  auto_hide: លាក់ដោយស្វ័យប្រវត្តិ\n  delay_to_hide: ពន្យារពេលដើម្បីលាក់\n  delay_to_show: ពន្យាពេលក្នុងការបង្ហាញ\n  dock_side: តីតាមង\n  enable: បើករបារឧបករណ៍ពុម្ពអក្សរក្បូរក្បាច់\n  hide_mode:\n    always: ជានិច្ច\n    never: មិនដែល\n    on_overlap: នៅលើការត្រួតស៊ីគ្នា។\n  item_size: ទំហំធាតុ\n  label: របាររបន៍របយ័ត្ន។ រប\n  margin: ទំហំរឹម\n  padding: ទំហំទ្រនាប់\n  placeholder: {}\nupdate:\n  available: មានបច្ចុប្បន្នភាព!\n  channel: ឆានែលធ្វើបច្ចុប្បន្នភាព\n  downloading: ទាញយក ...\nwall:\n  backgrounds: ផាបផេច\n  blur: ព្រមម៉ុន\n  cancel: បោះបង់\n  close: បិទ\n  collection_name: ឈ្មោះបណ្តុំ\n  collections: បណ្តុំ\n  contrast: ធេវីអាេយមានផាបផ្ទុយក្នា\n  corrupted_wallpapers_message: ផ្ទាំងរូបភាពខូច សូមពិចារណាលុបទាំងនេះ៖\n  create: បង្កើត\n  create_collection: បង្កើតបណ្តុំ\n  default_collection: ការប្រមូលលំនាំដើម\n  delete_collection: លុបបណ្តុំ\n  edit_collection: កែសម្រួលបណ្តុំ\n  enable: បើកដំណើរការកម្មវិធីគ្រប់គ្រងផ្ទាំងរូបភាព\n  extend: ពង្រីកបឋម\n  fit:\n    contain: មាន\n    cover: រកប\n    fill: ញាត់\n  flipHorizontal: ត្រឡប់ផ្ដេក\n  flipVertical: ត្រឡប់បញ្ឈរ\n  generating_thumbnails: ការបង្កើតរូបភាពតូចៗនៃផ្ទាំងរូបភាព\n  hours: ម៉ោង\n  interval: ផ្លាស់ប្តូរផ្ទាំងរូបភាពរៀងរាល់\n  minutes: បីតោ\n  monitor_collection: ការប្រមូលម៉ូនីទ័រ\n  multimonitor_behaviour: ឥរិយាបថពហុម៉ូនីទ័រ\n  muted: អូឌីយ៉ូវីដេអូបិទ\n  no_background: ការបញ្ចាំងស្លាយទទេដោយប្រើផ្ទៃខាងក្រោយរបស់ស្បែកជំនួសវិញ។\n  no_collections: មិនទាន់បានបង្កើតបណ្តុំនៅឡើយទេ។ ចុចប៊ូតុង + ដើម្បីបង្កើតមួយ។\n  objectFit: សមផ្ទៃខាងក្រោយសមស្រប\n  objectPosition: ទីតាំងផ្ទៃខាងក្រោយ\n  overlayColor: ពណ៌គម្រប\n  overlayMixBlendMode: លាបពណ៌លាយបញ្ចូលគ្នា\n  per_monitor: ក្នុងមួយម៉ូនីទ័រ\n  playback: ល្បឿនចាក់សារថ្មី\n  position:\n    bottom: ក្ដិត / បាត\n    center: ផ្ចិត\n    left: ឆេវង\n    right: ខាងស្ដាម\n    top: ឯផ្ទលើ\n  processing_video: ដំណើរការវីដេអូ\n  random: ការបញ្ចាំងស្លាយចៃដន្យដោយចៃដន្យ\n  saturation: ផាបថបតរ\n  seconds: វិនាទី\n  select_collection: ជ្រើសរើសបណ្តុំ\n  thumbnail_generation_complete: ការបង្កើតរូបភាពតូចបានបញ្ចប់\n  thumbnail_generation_finished: ការបង្កើតរូបភាពតូចបានបញ្ចប់ដោយជោគជ័យ\n  wallpaper_collection: ការប្រមូលផ្ទាំងរូបភាព\n  wallpaper_settings: ការកំណត់ផ្ទាំងរូបភាព\n  withOverlay: ជាមួយនឹងការត្រួតលើគ្នា\n  workspace_collections: ការប្រមូលកន្លែងធ្វើការ\nweg:\n  auto_hide: លាក់ដោយស្វ័យប្រវត្តិ\n  delay_to_hide: ពន្យារពេលដើម្បីលាក់\n  delay_to_show: ពន្យាពេលក្នុងការបង្ហាញ\n  dock_side: តីតាមង\n  enable: បើកដំណើរការ Dock/Taskbar\n  filtering: ការត្រងធាតុ\n  gap: គម្លាត\n  hide_mode:\n    always: ជានិច្ច\n    never: មិនដែល\n    on_overlap: នៅលើការត្រួតស៊ីគ្នា។\n  items:\n    gap: ចន្លោះរវាងធាតុ\n    label: ធាតុ\n    pinned_visibility:\n      always: ជានិច្ច\n      label: ភាពមើលឃើញធាតុដែលបានខ្ទាស់\n      when_primary: នៅពេលដែលម៉ូនីទ័រគឺបឋម\n    show_instance_counter: បង្ហាញការបើកខ្សែវីនដូ\n    show_window_title: បង្ហាញចំណងជើងបើកបង្អួច (មានតែផ្តេកប៉ុណ្ណោះ)\n    size: ទំហំធាតុ\n    split_windows: បំបែកវីនដូ (ធាតុមួយក្នុងមួយបង្អួច)\n    temporal_visibility:\n      all: ទាំងអស់។\n      label: ភាពមើលឃើញធាតុដែលមិនបានខ្ទាស់\n      on_monitor: នៅលើម៉ូនីទ័រ\n    visible_separators: សញ្ញាបំបែកដែលអាចមើលឃើញ\n  label: ចត/របារភារកិច្ច\n  margin: រឹម\n  mode:\n    full_width: ទទឹងពេញអេក្រង់\n    min_content: តូចតាមដែលអាចធ្វើបាន\n  padding: ទ្រនាប់\n  show_end_task: បង្ហាញភារកិច្ចបញ្ចប់នៅក្នុងរបារភារកិច្ច\n  width: ទទឹង\nwelcome:\n  give_a_review: ផ្តល់ការពិនិត្យឡើងវិញ\n  message: >-\n    Seelen UI គឺជាបរិស្ថានផ្ទៃតុឥតគិតថ្លៃ និងប្រភពបើកចំហសម្រាប់ Windows ។\n    នៅទីនេះអ្នកមានការគ្រប់គ្រងពេញលេញលើរបៀបដែលផ្ទៃតុរបស់អ្នកមើលទៅ និងឥរិយាបទ\n    ដែលអនុញ្ញាតឱ្យអ្នកកំណត់រាល់ព័ត៌មានលម្អិតដើម្បីផ្គូផ្គងលំហូរការងារ\n    និងរចនាប័ទ្មរបស់អ្នក។\n  ok: តោះចាប់ផ្តើម!\n  review: >-\n    ប្រសិនបើអ្នករីករាយនឹងការប្រើប្រាស់ Seelen UI\n    សូមពិចារណាចាកចេញពីការពិនិត្យឡើងវិញនៅលើហាង —\n    មតិកែលម្អរបស់អ្នកជួយឱ្យគម្រោងរីកចម្រើន និងប្រសើរឡើង។\n  title: សូមស្វាគមន៍មកកាន់ Seelen UI!\nwidget:\n  enable: បើកដំណើរការធាតុក្រាហ្វិកនេះ\n  enable_for_monitor: បើកដំណើរការម៉ូនីទ័រនេះ\n  instances: ករណីបុម\nwm:\n  animations:\n    duration: រយៈពេលចលនា (MS)\n    ease_function: មុខងារបន្ធូរបន្ថយចលនា\n    enable: បើកចលនាចលនារបស់បង្អួច\n  author: អ្នកនិពន្ធ\n  border:\n    enable: បើកស៊ុមបង្អួច\n    offset: ព្រំដែនអុហ្វសិត\n    width: ទទឹងព្រំដែន\n  description: ការពិពណ៌នា\n  drag_behavior: ឥរិយាបទអូស\n  drag_behavior_options:\n    sort: តម្រៀប (តម្រៀបបង្អួចឡើងវិញពេលកំពុងអូស)\n    swap: ប្តូរ (ប្តូរបង្អួចនៅលើចុងអូស)\n  enable: បើកដំណើរការកម្មវិធីគ្រប់គ្រងបង្អួច\n  layout: ប្លង់\n  resize_delta: ប្តូរទំហំដីសណ្តរ (%)\n  space_between_containers: ចន្លោះរវាងកុងតឺន័រ\n  workspace_offset: កន្លែងធ្វើការអុហ្វសិត (រឹម)\n  workspace_padding: ចន្លោះកន្លែងធ្វើការ\n'yes': បាត\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ko.yml",
    "content": "action:\n  confirm: 확실합니까?\n  confirm_body: 이 조치는 취소 할 수 없습니다.\napps_configurations:\n  app:\n    bindings: 바인딩 (두 옵션 모두 필요)\n    category: 카테고리\n    category_placeholder: 없음\n    monitor: 모니터\n    monitor_placeholder: 없음\n    name: 이름\n    ok_create: 만들기\n    ok_edit: 수정하기\n    ok_readonly: 복사본으로 편집\n    options:\n      NoInteractive: 대화형 없음\n      VdPinned: 모든 작업공간에 표시\n      WmFloat: Twm - 부동 시작\n      WmForce: Twm - 강제 관리\n      WmUnmanage: Twm - 관리 해제\n    options_label: 추가 옵션\n    title_create: '{{name}} 만들기'\n    title_edit: 편집 {{name}}\n    title_readonly: '{{name}} 보기'\n    weg_options_label: 독/작업 표시줄 옵션\n    wm_options_label: 윈도우 관리자 옵션\n    workspace: 작업 공간\n    workspace_placeholder: 없음\n  bundled_msg: >-\n    이러한 미리 구성된 설정은 편집할 수 없으며, 수정하지 않고 최상의 경험을 제공하도록 설계되었습니다. 자주 사용되는 프로그램을 자동으로\n    구성합니다.\n  bundled_title: Seelen에서 제공한 설정\n  confirm_delete: 정말로 이 설정을 삭제하시겠습니까?\n  confirm_delete_title: 삭제하시겠습니까?\n  delete: 삭제\n  export: 내보내기\n  export_full: 애플리케이션별 설정 내보내기\n  extra_info: >-\n    Seelen UI는 앱당 (가장 먼저 일치한) 하나의 식별자만 사용하므로 식별된 순서는 중요합니다. 가장 최근에 추가된 항목이 우선순위를\n    정합니다. 참고로 테이블은 기본적으로 최신순 부터 오래된 순서로 정렬됩니다.\n  identifier:\n    add_block: 블록 추가\n    and: 그리고\n    id: 식별자\n    kind: 식별 기준\n    matching_strategy: 식별 방법\n    matching_strategy_option:\n      contains: 포함\n      ends_with: 다음으로 끝남\n      equals: 같음\n      regex: 정규식\n      starts_with: 다음으로 시작\n    negation: 조건이 반대일때 식별하기\n    or: 또는\n    remove: 블록 삭제\n    type:\n      class: 수업\n      exe: exe\n      path: 길\n      title: 제목\n  import: 가져오기\n  import_full: 애플리케이션별 설정 가져오기\n  new: 신규\n  search: 검색\n  swap: 교체\ncancel: 취소\nclose: 닫기\ndelete: 삭제\ndevtools:\n  app_folders: 앱 폴더\n  custom_config_file: 사용자 정의 구성 파일 로드\n  data_folder: 데이터 폴더\n  enable: 개발자 도구 활성화\n  install_folder: 설치 폴더\n  load: 로드\n  settings_file: 설정 파일\n  simulate_perm:\n    label: 위젯 권한 요청 시뮬레이션\n    result_allowed: ✓ 권한이 부여되었습니다\n    result_denied: ✗ 권한이 거부되었습니다\n    trigger: 시뮬레이션\n    widget_id_placeholder: '@작성자/위젯 이름'\nextras:\n  clear_icons: 시스템 아이콘 캐시 지우기\n  clear_icons_tooltip: 모든 위젯에 완전히 적용하려면 다시 시작해야 할 수 있습니다.\n  exit: 종료/나가기\n  links: 공식 링크\n  relaunch: 다시 시작\n  version: 버전\n  version_fixed: >-\n    애플리케이션 및 WebView2 런타임 버전이 수정되었습니다. 이는 애플리케이션이 업데이트를 받지 않고 WebView2 Runtime이\n    Windows 업데이트로 자동 업데이트되지 않음을 의미합니다.\ngeneral:\n  accent_color: 강조 색상\n  date_format: 날짜 형식\n  date_format_how_to: 날짜 형식을 작성하는 방법은 무엇입니까?\n  hardware_acceleration: 하드웨어 가속\n  hardware_acceleration_description: >-\n    하드웨어 가속을 비활성화하면 메모리 사용량이 줄어들지만 성능 문제가 발생할 수 있습니다. 라이브 배경화면을 사용하지 않을 경우\n    비활성화하는 것이 안전합니다.\n  icon_pack:\n    available: 사용 가능한 아이콘 팩\n    selected: 현재 아이콘 팩\n  language: 언어\n  monday: 월요일\n  performance_mode:\n    on_battery: 배터리에\n    on_energy_saver: 에너지 세이버에서\n    options:\n      disabled: 장애가 있는\n      extreme: 극심한\n      minimal: 최소\n    plugged: 플러그 또는 충전\n  polling_interval: 시스템 폴링 간격\n  polling_interval_description: >-\n    Seelen UI가 CPU, RAM, 네트워크 및 디스크 활동과 같은 시스템 리소스를 확인하는 빈도(초)입니다. 숫자가 작을수록 업데이트\n    빈도가 높아지지만 리소스 사용량이 약간 높아집니다.\n  saturday: 토요일\n  start_of_week: 주의 시작\n  startup: 시작 시 실행\n  sunday: 일요일\n  theme:\n    available: 사용 가능한 테마\n    selected: 현재 테마\nheader:\n  labels:\n    config: 구성\n    developer: 개발자용\n    extras: 추가 정보\n    general: 일반\n    home: 홈\n    icon_pack_editor: 캐시된 아이콘\n    iconpack: 아이콘 팩\n    monitors: 모니터\n    plugin: 플러그인\n    resources: 리소스\n    shortcuts: 단축키\n    soundpack: 사운드 팩\n    specific_apps: 애플리케이션별 설정\n    theme: 테마\n    virtual_desk: 가상 데스크톱\n    wallpaper: 배경 화면\n    widget: 위젯\nhome:\n  new_resources: 새로운 리소스\ninherit: 상속\ninProgress: 진행 중...\ninsert: 추가\nloading: 로딩 중...\nmiscellaneous: 기타\nmonitors_configurations:\n  label: 모니터 {{index}}\nmore: 더보기\n'no': 아니요\nopen: 열기\nquit: 종료\nremove: 제거\nreset_all_to_default: 모두 기본값으로 초기화\nreset_to_default: 기본값으로 재설정\nresources:\n  app_outdated: 이 리소스가 제대로 작동하려면 최신 버전의 Seelen UI가 필요합니다.\n  corrupted_wallpaper: 썸네일 추출 실패 - 손상되었거나 지원되지 않는 비디오 형식\n  delete: 리소스 삭제\n  discover: 더 많은 리소스 살펴보기\n  has_update: 업데이트를 사용할 수 있습니다\n  high_impact: 성능에 큰 영향\n  import_wallpapers: 로컬 배경화면 가져오기\n  open_folder: 리소스 폴더 열기\n  outdated: 이 리소스는 이전 버전의 Seelen UI용으로 설계되었으며 제대로 작동하지 않을 수 있습니다.\n  see_on_website: 웹사이트에서 확인하세요\nreview_request:\n  not_now: 지금은 아님\n  sure: 확신하는!\n  title: Seelen UI를 즐기고 계시나요?\nsave: 저장\nsave_and_restart: 저장 및 재시작\nsearch: 찾다\nsee_more: 자세히 보기\nshortcuts:\n  duplicate_error: 이 바로가기가 중복되었습니다.\n  enable: 내장 단축키 시스템을 활성화하십시오\n  enable_tooltip: Seelen UI 클라이언트를 사용하여 자신의 바로 가기 시스템을 구현하는 경우 비활성화하십시오.\n  labels:\n    create_new_workspace: 새 작업 공간을 만듭니다\n    cycle_stack_next: 다음에 사이클 스택\n    cycle_stack_prev: 사이클 스택 이전\n    cycle_wallpaper_next: 다음 벽지로 변경하십시오\n    cycle_wallpaper_prev: 이전 벽지로 변경하십시오\n    decrease_height: 높이를 줄입니다\n    decrease_width: 너비를 줄입니다\n    destroy_current_workspace: 현재 작업 공간을 파괴하십시오\n    focus_bottom: 초점 바닥\n    focus_latest: 초점 최신\n    focus_left: 초점이 남았습니다\n    focus_right: 집중적으로\n    focus_top: 포커스 탑\n    increase_height: 높이를 늘리십시오\n    increase_width: 폭을 늘리십시오\n    misc_force_quit: 힘을 끊으십시오\n    misc_force_restart: 강제 재시작\n    misc_open_settings: 열기 설정\n    misc_toggle_lock_tracing: 토글 잠금 추적 (로그)\n    misc_toggle_win_event_tracing: 토글 윈 이벤트 추적 (로그)\n    move_to_workspace: 작업 공간으로 이동 {{0}}\n    move_window_down: 창문을 바닥으로 이동하십시오\n    move_window_left: 왼쪽으로 창을 이동하십시오\n    move_window_right: 창을 오른쪽으로 이동하십시오\n    move_window_up: 창을 맨 위로 이동하십시오\n    pause_tiling: 타일링 윈도우 관리자를 일시 중지합니다\n    reserve_bottom: 바닥을 예약하십시오\n    reserve_float: 플로트 예비\n    reserve_left: 왼쪽 예비\n    reserve_right: 오른쪽을 예약하십시오\n    reserve_stack: 예비 스택\n    reserve_top: 탑을 예약하십시오\n    restore_sizes: 크기를 복원하십시오\n    send_to_workspace: 작업 공간으로 보내 {{0}}\n    start_weg_app: 초점 또는 시작 응용 프로그램 {{0}}\n    switch_to_next_workspace: 다음 작업 공간으로 전환하십시오\n    switch_to_previous_workspace: 이전 작업 공간으로 전환하십시오\n    switch_workspace: 작업 공간으로 전환 {{0}}\n    toggle_float: 토글 윈도우 플로트 모드\n    toggle_monocle: 작업 공간 모노클 모드 토글\n  readonly_tooltip: 이것은 읽기 전용 단축키입니다\n  reset: 기본값으로 재설정하십시오\nsides:\n  bottom: 아래\n  left: 왼쪽\n  right: 오른쪽\n  top: 위\ntoolbar:\n  auto_hide: 자동 숨기기\n  delay_to_hide: 숨기기 지연\n  delay_to_show: 표시 지연\n  dock_side: 위치\n  enable: 툴바 활성화\n  hide_mode:\n    always: 항상\n    never: 안 함\n    on_overlap: 오버랩 시\n  item_size: 품목 크기\n  label: 도구 모음\n  margin: 여백 크기\n  padding: 패딩 크기\n  placeholder: {}\nupdate:\n  available: 사용 가능한 업데이트가 존재합니다!\n  channel: 업데이트 채널\n  downloading: 다운로드 중...\nwall:\n  backgrounds: 배경화면\n  blur: 블러\n  cancel: 취소\n  close: 닫다\n  collection_name: 컬렉션 이름\n  collections: 컬렉션\n  contrast: 대비\n  corrupted_wallpapers_message: '손상된 배경화면은 삭제하는 것이 좋습니다:'\n  create: 만들다\n  create_collection: 컬렉션 만들기\n  default_collection: 기본 컬렉션\n  delete_collection: 컬렉션 삭제\n  edit_collection: 컬렉션 편집\n  enable: 배경화면 관리자 활성화\n  extend: 기본 확장\n  fit:\n    contain: 포함\n    cover: 커버\n    fill: 채우기\n  flipHorizontal: 가로로 뒤집기\n  flipVertical: 세로로 뒤집기\n  generating_thumbnails: 배경화면 썸네일 생성\n  hours: 시간\n  interval: 배경화면 변경 주기\n  minutes: 분\n  monitor_collection: 모니터 컬렉션\n  multimonitor_behaviour: 다중 모니터 동작\n  muted: 비디오 오디오 음소거\n  no_background: 배경화면이 없으면, 테마에 내장된 배경화면을 대신 사용합니다.\n  no_collections: 아직 생성된 컬렉션이 없습니다. + 버튼을 클릭하여 생성하세요.\n  objectFit: 배경 맞춤\n  objectPosition: 배경 위치\n  overlayColor: 오버레이 색상\n  overlayMixBlendMode: 오버레이 믹스 블렌드 모드\n  per_monitor: 모니터당\n  playback: 재생 속도\n  position:\n    bottom: 하단\n    center: 센터\n    left: 왼쪽\n    right: 오른쪽\n    top: Top\n  processing_video: 비디오 처리 중\n  random: 슬라이드 쇼 무작위\n  saturation: 채도\n  seconds: 초\n  select_collection: 컬렉션 선택\n  thumbnail_generation_complete: 썸네일 생성 완료\n  thumbnail_generation_finished: 썸네일 생성이 성공적으로 완료되었습니다.\n  wallpaper_collection: 배경화면 모음\n  wallpaper_settings: 배경화면 설정\n  withOverlay: 오버레이 사용\n  workspace_collections: 작업공간 컬렉션\nweg:\n  auto_hide: 자동 숨기기\n  delay_to_hide: 숨기기 지연\n  delay_to_show: 표시 지연\n  dock_side: 위치\n  enable: 독/작업 표시줄 활성화\n  filtering: 항목 필터링\n  gap: 간격\n  hide_mode:\n    always: 항상\n    never: 안 함\n    on_overlap: 오버랩 시\n  items:\n    gap: 항목 간 간격\n    label: 항목\n    pinned_visibility:\n      always: 항상\n      label: 고정된 항목 가시성\n      when_primary: 모니터가 기본인 경우\n    show_instance_counter: 열린 창 카운터 표시\n    show_window_title: 열린 창 제목 표시(가로만)\n    size: 항목 크기\n    split_windows: 창 분할(창당 하나의 항목)\n    temporal_visibility:\n      all: 모두\n      label: 고정 해제된 항목 가시성\n      on_monitor: 모니터에서\n    visible_separators: 구분선 표시\n  label: 독/작업 표시줄\n  margin: 바깥 여백\n  mode:\n    full_width: 전체 화면 너비\n    min_content: 최대한 작게\n  padding: 안쪽 여백\n  show_end_task: 작업 표시줄에 종료 작업 표시\n  width: 너비\nwelcome:\n  give_a_review: 리뷰를 남겨주세요\n  message: >-\n    Seelen UI는 Windows용 무료 오픈 소스 데스크톱 환경입니다. 여기에서 데스크탑의 모양과 동작을 완전히 제어할 수 있으므로\n    작업 흐름과 스타일에 맞게 모든 세부 사항을 사용자 정의할 수 있습니다.\n  ok: 시작하자!\n  review: Seelen UI를 즐겨 사용하신다면 스토어에 리뷰를 남겨보세요. 여러분의 피드백은 프로젝트 성장과 개선에 도움이 됩니다.\n  title: Seelen UI에 오신 것을 환영합니다!\nwidget:\n  enable: 이 위젯 활성화\n  enable_for_monitor: 이 모니터에서 활성화\n  instances: 인스턴스\nwm:\n  animations:\n    duration: 애니메이션 지속 시간 (MS)\n    ease_function: 애니메이션 완화 기능\n    enable: 창의 애니메이션을 활성화합니다\n  author: 작성자\n  border:\n    enable: 창 테두리 활성화\n    offset: 테두리 여백\n    width: 테두리 너비\n  description: 설명\n  drag_behavior: 드래그 동작\n  drag_behavior_options:\n    sort: 정렬(드래그하는 동안 창 재정렬)\n    swap: 교환(드래그 끝에서 창 교환)\n  enable: 타일링 윈도우 관리자 활성화\n  layout: 레이아웃\n  resize_delta: 크기 조정 수치 (%)\n  space_between_containers: 컨테이너 간 간격\n  workspace_offset: 작업 공간 바깥 여백\n  workspace_padding: 작업 공간 안쪽 여백\n'yes': 예\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ku.yml",
    "content": "action:\n  confirm: Piştrastin?\n  confirm_body: Ev çalakî nikare were paşguh kirin.\napps_configurations:\n  app:\n    bindings: Binding (Nîşe her du vebijark hewce ne)\n    category: Liq\n    category_placeholder: Netû\n    monitor: Lê gûhdarkirin\n    monitor_placeholder: Netû\n    name: Nav\n    ok_create: Xûliqandin\n    ok_edit: UDÛDEPATE\n    ok_readonly: Wekî nû biguherînin\n    options:\n      NoInteractive: No Interactive\n      VdPinned: Di hemî cîhên xebatê de nîşan bide\n      WmFloat: Twm - Destpêkirina Floating\n      WmForce: Twm - Birêvebirina Hêzê\n      WmUnmanage: Twm - Birêvebirin\n    options_label: Vebijarkên zêde\n    title_create: Afirandina {NAME}\n    title_edit: Verastkirin {{name}}\n    title_readonly: Dîtin {{NAVEN}\n    weg_options_label: Vebijarkên Dock / Taskbar\n    wm_options_label: Vebijarkên rêveberê pencereyê\n    workspace: Workspace\n    workspace_placeholder: Netû\n  bundled_msg: >-\n    Van konfigurasyonên bundled ne edîtor in û têne çêkirin ku ji we re ezmûna\n    çêtirîn bêyî adetbûnê peyda bikin. Ew bixweber ji bo we serlêdanên herî\n    gelemperî mîheng dikin.\n  bundled_title: App Confic bi seelen\n  confirm_delete: Ma hûn guman dikin ku hûn dixwazin vê konfigurasyonê / s jêbirin?\n  confirm_delete_title: DELETE piştrast bikin\n  delete: Lûleêkirin\n  export: Eksport\n  export_full: Mîhengên bi serîlêdanê re bikin\n  extra_info: >-\n    Selen UI tenê yek nasnameyê bikar tînin (yekem maça ku tê dîtin) ji ber vê\n    yekê fermanê girîng e.\n  identifier:\n    add_block: Blok zêde bikin\n    and: Û\n    id: Nasî\n    kind: Ji hêla nas kirin\n    matching_strategy: Stratejiya hevberdanê\n    matching_strategy_option:\n      contains: Dihewîne\n      ends_with: Bi dawî dibe\n      equals: Wekhevî\n      regex: îfadeya bi rêkûpêk\n      starts_with: Bi dest pê dike\n    negation: Lihevhatina negotî\n    or: AN\n    remove: Blokê jêbirin\n    type:\n      class: Sinif\n      exe: Exe\n      path: Şop\n      title: Nav\n  import: Anîn\n  import_full: Mîhengên Import bi serîlêdanê\n  new: Nşh\n  search: Gerr\n  swap: Sofet\ncancel: Bişûndekirin\nclose: Nêzîkî\ndelete: Lûleêkirin\ndevtools:\n  app_folders: Pelên App\n  custom_config_file: Pelê Config-ê ya xwerû barkirin\n  data_folder: Peldanka daneyê\n  enable: Amûrên Pêşdebir çalak bikin\n  install_folder: Peldanka sazkirinê\n  load: Dawetkirin\n  settings_file: Pelê Mîhengan\n  simulate_perm:\n    label: Daxwaza Destûra Widgetê simule bikin\n    result_allowed: ✓ Destûr hat dayîn\n    result_denied: ✗ Destûr red kirin\n    trigger: Simulate\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Îkonên pergala zelal cache\n  clear_icons_tooltip: >-\n    Pêdivî ye ku ji nû ve destpêkirinê were xwestin ku bi tevahî li ser hemî\n    widgetan bandor bike\n  exit: Qut / derketin\n  links: Girêdanên fermî\n  relaunch: Berxwedêr\n  version: Awa\n  version_fixed: >-\n    Serlêdan û guhertoyên WebView2 Runtime têne rast kirin. Ev tê vê wateyê ku\n    serîlêdan dê nûvekirinan wernegire û WebView2 Runtime dê bixweber bi\n    nûvekirinên Windows-ê re neyê nûve kirin.\ngeneral:\n  accent_color: Rengê Accent\n  date_format: Formata Date\n  date_format_how_to: Meriv çawa formek tarîxê dinivîse?\n  hardware_acceleration: Hardware Acceleration\n  hardware_acceleration_description: >-\n    Neçalakkirina bilezkirina hardware dê karanîna bîranînê kêm bike, lê dikare\n    bibe sedema pirsgirêkên performansê. Ger hûn dîwarên zindî bikar neynin\n    neçalakkirina ewledar e.\n  icon_pack:\n    available: Pakêtên îkonê yên berdest\n    selected: Pakêtên Îkonên Çalak\n  language: Ziman\n  monday: Duşem\n  performance_mode:\n    on_battery: Li ser batterê\n    on_energy_saver: Li ser Saveravdêriya Enerjiyê\n    options:\n      disabled: Bêmecel\n      extreme: Bêfêhm zêde\n      minimal: Minimal\n    plugged: Plug kirin an drav kirin\n  polling_interval: Navbera dengdanê ya pergalê\n  polling_interval_description: >-\n    Çi caran (di çirkeyan de) Seelen UI çavkaniyên pergalê yên wekî CPU, RAM,\n    torê, û çalakiya dîskê kontrol dike. Hejmarek piçûktir tê wateya nûvekirinên\n    pir caran, lê karanîna çavkaniyê hinekî bilindtir e.\n  saturday: Şemî\n  start_of_week: Destpêka Hefteyê\n  startup: Li ser destpêka bisekinin?\n  sunday: Yekşem\n  theme:\n    available: Mijarên Berdest\n    selected: Mijarên Çalak\nheader:\n  labels:\n    config: Mîhengên\n    developer: Ji bo pêşdebiran\n    extras: Ekstras\n    general: Giştî\n    home: Xane\n    icon_pack_editor: Îkonên cached\n    iconpack: Packon icon\n    monitors: Çavdêr\n    plugin: Pêçanin\n    resources: Resourcesavkaniyên\n    shortcuts: Kurtefîlm\n    soundpack: Pakêtên deng\n    specific_apps: Mîhengên bi serîlêdanê\n    theme: Temas\n    virtual_desk: Desktopên Virtual\n    wallpaper: Wallpapers\n    widget: Widgets\nhome:\n  new_resources: Resourcesavkaniyên nû\ninherit: Mîratgirtin\ninProgress: Ez teslîm nabim...\ninsert: Lêzêdekirin\nloading: Loading ...\nmiscellaneous: Lihevket\nmonitors_configurations:\n  label: Monitor {{Index-}\nmore: Zêde\n'no': Na\nopen: Vekirî\nquit: Devjêberdan\nremove: Dûrxistin\nreset_all_to_default: Hemî nirxên xwerû resen bikin\nreset_to_default: Reset bi nirxa xwerû\nresources:\n  app_outdated: >-\n    Vê çavkaniyê guhertoyek nû ya Seelen UI hewce dike ku bi rengek baş\n    bixebite.\n  corrupted_wallpaper: >-\n    Derxistina wêneya piçûk bi ser neket - formata vîdyoyê ya xerabûyî an nayê\n    piştgirî kirin\n  delete: Avkaniyê jêbirin\n  discover: Zêdetir çavkaniyên kifş bikin\n  has_update: Nûvekirinek heye\n  high_impact: Bandora bilind li ser performansê\n  import_wallpapers: Dîwarên herêmî yên import bikin\n  open_folder: Peldanka çavkaniya vekirî\n  outdated: >-\n    Vê çavkaniyê ji bo guhertoyek kevn a Seelen UI hate çêkirin û dibe ku bi\n    rengek baş kar neke.\n  see_on_website: Li ser malperê bibînin\nreview_request:\n  not_now: Guhê xwe nedenê\n  sure: Emîn!\n  title: Ji Seelen UI-ê kêfxweş dibin?\nsave: Rizgarkirin\nsave_and_restart: Save & Restart\nsearch: Gerr\nsee_more: Bêtir bibînin\nshortcuts:\n  duplicate_error: Ev kurtebir hatiye dubarekirin\n  enable: Pergala kurtefîlmên çêkirî çalak bikin\n  enable_tooltip: >-\n    Heke hûn ê pergala kurteya xwe bi karanîna muwekîlê Seelen UI bikar bînin\n    bicîh bikin\n  labels:\n    create_new_workspace: Karê nû nû çêbikin\n    cycle_stack_next: Cycle stack next\n    cycle_stack_prev: Stacka Cycle ya berê\n    cycle_wallpaper_next: Guhertina Wallpaper-ê din\n    cycle_wallpaper_prev: Guhertina Wallpaper ya berê\n    decrease_height: Dirêjbûn\n    decrease_width: Width kêm bikin\n    destroy_current_workspace: Qada xebata heyî hilweşînin\n    focus_bottom: Focus Bottom\n    focus_latest: FOCUS Latest\n    focus_left: Focus çep\n    focus_right: Focus rast\n    focus_top: Focus Top\n    increase_height: Bilindbûnê zêde bikin\n    increase_width: Berfireh zêde bikin\n    misc_force_quit: Hêza Qedexe\n    misc_force_restart: Hêza Destpêkirin\n    misc_open_settings: Mîhengên vekirî\n    misc_toggle_lock_tracing: Toggle Lock Tracing (têketin)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (têketin)\n    move_to_workspace: Move to Workspace {{0}}\n    move_window_down: Pencereyê berbi jêr ve biçin\n    move_window_left: Pencereya çepê hilkişînin\n    move_window_right: Pencereya rastê hilkişînin\n    move_window_up: Pencereyê berbi jor ve biçin\n    pause_tiling: Rêvebirê pencereya tiliyê rawestînin\n    reserve_bottom: Binê binê\n    reserve_float: Float rezervan\n    reserve_left: Rezertê çepê\n    reserve_right: Rasterast rast bikin\n    reserve_stack: Stack Reserve\n    reserve_top: Top hilînin\n    restore_sizes: Mezinan sererast bikin\n    send_to_workspace: Ji bo Workspace {{0} bişînin.\n    start_weg_app: FOCUS an Destpêkirina Serîlêdanê {{0}}\n    switch_to_next_workspace: Veguherînin qada xebata next\n    switch_to_previous_workspace: Vegere qada xebata berê\n    switch_workspace: Switch to Workspace {{0}}\n    toggle_float: Mode Float Float Toggle\n    toggle_monocle: Mode Mode Monocle Workspace Toggle\n  readonly_tooltip: Ev kurteyek xwendinê ye\n  reset: Reset to Default\nsides:\n  bottom: Erd\n  left: Çep\n  right: Rast\n  top: Kop\ntoolbar:\n  auto_hide: Auto Hide\n  delay_to_hide: Dereng ji bo veşartinê\n  delay_to_show: Dereng nîşan bide\n  dock_side: Rewş\n  enable: Vebijêrkek Fancy Vebijêrin\n  hide_mode:\n    always: Herdem\n    never: Qet\n    on_overlap: Li ser hevgirtinê\n  item_size: Mezinahiya Babetê\n  label: Toolbar\n  margin: Mezinahiya Margin\n  padding: Padding Size\n  placeholder: {}\nupdate:\n  available: Nûvekirin!\n  channel: Kanala Nûve\n  downloading: Daxistin ...\nwall:\n  backgrounds: Wallpapers\n  blur: Şîn\n  cancel: Bişûndekirin\n  close: Nêzîkî\n  collection_name: Navê Koleksiyonê\n  collections: Collections\n  contrast: Dijîtî\n  corrupted_wallpapers_message: 'Dîwarên xerabûyî, bifikire ku van jêbirin:'\n  create: Xûliqandin\n  create_collection: Koleksiyonê çêbikin\n  default_collection: Berhevoka Bingehîn\n  delete_collection: Koleksiyonê jêbirin\n  edit_collection: Edit Collection\n  enable: Gerînendeyê Wallpaper çalak bike\n  extend: Seretayî dirêj bikin\n  fit:\n    contain: Bêdengman\n    cover: Lihêv\n    fill: Tijîkirin\n  flipHorizontal: Flip horizontî\n  flipVertical: Flip Vertical\n  generating_thumbnails: Çêkirina Thumbnails Wallpaper\n  hours: demjimêr\n  interval: Her kesê biguherînin\n  minutes: deqq\n  monitor_collection: Monitor Collection\n  multimonitor_behaviour: Behavior Multimonitor\n  muted: Dengê vîdyoyê qut bike\n  no_background: Slideshow vala, li şûna bikaranîna paşnavê mijarê.\n  no_collections: Hê berhevok nehatine çêkirin. Bişkojka + bikirtînin da ku yek çêbikin.\n  objectFit: Background Fit\n  objectPosition: Positiona paşîn\n  overlayColor: Rengê dorpêçkirî\n  overlayMixBlendMode: Overlay Mode Blend\n  per_monitor: Per monitor\n  playback: Leza Playback\n  position:\n    bottom: Erd\n    center: Navîne\n    left: Çep\n    right: Rast\n    top: Kop\n  processing_video: Vîdyoyê pêvajoyê\n  random: Slideshow Randomize\n  saturation: Satutionation\n  seconds: seconds\n  select_collection: Hilbijêre Collection\n  thumbnail_generation_complete: Nifşa Thumbnail Biqede\n  thumbnail_generation_finished: Çêkirina pîçikan bi serfirazî qediya\n  wallpaper_collection: Collection Wallpaper\n  wallpaper_settings: Mîhengên Wallpaper\n  withOverlay: Bi sernav\n  workspace_collections: Koleksiyonên Cihê Xebatê\nweg:\n  auto_hide: Auto Hide\n  delay_to_hide: Dereng ji bo veşartinê\n  delay_to_show: Dereng nîşan bide\n  dock_side: Rewş\n  enable: Dock / Taskbar çalak bikin\n  filtering: Filterkirina tiştê\n  gap: Qelîştok\n  hide_mode:\n    always: Herdem\n    never: Qet\n    on_overlap: Li ser hevgirtinê\n  items:\n    gap: Cîh di navbera tiştan de\n    label: Hevokên\n    pinned_visibility:\n      always: Herdem\n      label: Dîtina Tiştên Pînekirî\n      when_primary: Dema ku çavdêriya sereke ye\n    show_instance_counter: Counter Windows-ê vekirî nîşan bikin\n    show_window_title: Sernavê pencereyê vekirî nîşan bide (tenê horizontî)\n    size: Mezinahiya tiştê\n    split_windows: Windows-ê parçe bike (ji her pencereyê yek tişt)\n    temporal_visibility:\n      all: Gişt\n      label: Dîtina Tiştên Nepînekirî\n      on_monitor: Li ser Monitor\n    visible_separators: Dabeşên Visible\n  label: Dock / Taskbar\n  margin: Radihêj\n  mode:\n    full_width: Firehiya ekrana tevahî\n    min_content: Biçûk ku dikare bibe\n  padding: Padding\n  show_end_task: Di peywira peywirê de peywira dawiyê nîşan bide\n  width: Berî\nwelcome:\n  give_a_review: Bidin Review\n  message: >-\n    Seelen UI ji bo Windows-ê hawîrdorek sermaseya belaş û çavkaniya vekirî ye.\n    Li vir kontrola we ya bêkêmasî heye ka sermaseya we çawa xuya dike û\n    tevdigere, ku dihêle hûn her hûrgulî xweş bikin da ku li gorî xebata xwe û\n    şêwaza xwe tevbigerin.\n  ok: Ka em dest pê bikin!\n  review: >-\n    Heke hûn ji karanîna Seelen UI-yê hez dikin, bifikirin ku li ser firotgehê\n    nirxandinek bihêlin - nerînên we ji projeyê re dibe alîkar ku mezin bibe û\n    pêşve bibe.\n  title: Hûn bi xêr hatin Seelen UI!\nwidget:\n  enable: Vê widgetê çalak bikin\n  enable_for_monitor: Li ser vê çavdêriyê çalak bikin\n  instances: Mînak\nwm:\n  animations:\n    duration: Demjimêr anîmasyon (MS)\n    ease_function: Fonksiyona hêsankirina anîmasyonê\n    enable: Animasyonên Pencereyê çalak bikin\n  author: Nivîskar\n  border:\n    enable: Sînorê pencereyê çalak bike\n    offset: Berdewamiya sînor\n    width: Berfirehiya sînor\n  description: Terîf\n  drag_behavior: Drag Behavior\n  drag_behavior_options:\n    sort: Rêzkirin (dema dikişîne paceyan ji nû ve rêz bike)\n    swap: Swap (paceyên li ser dawiya kaşkirinê biguherînin)\n  enable: Gerînendeyê Windowê ya Tilî bikar bînin\n  layout: Rêz\n  resize_delta: Delta Resize (%)\n  space_between_containers: Cîhê di navbera konteyneran de\n  workspace_offset: Karûbarên Workspan (Margins)\n  workspace_padding: Karên xebitandinê\n'yes': Erê\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/lb.yml",
    "content": "action:\n  confirm: Bass du secher?\n  confirm_body: Dës Aktioun kann net annuléiert ginn.\napps_configurations:\n  app:\n    bindings: Bindend (Notiz béid Optiounen sinn erfuerderlech)\n    category: Gemengen\n    category_placeholder: Keen\n    monitor: Bonitor\n    monitor_placeholder: Keen\n    name: Numm vum Numm\n    ok_create: Erreeiert eng kreéieren\n    ok_edit: Olaangs\n    ok_readonly: Ännert als nei\n    options:\n      NoInteractive: Keng interaktiv\n      VdPinned: Show an all Aarbechtsberäicher\n      WmFloat: Twm - Start Floating\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Extra Optiounen\n    title_create: Erstellen {{Numm}}\n    title_edit: Ännert {{Numm}}\n    title_readonly: '{{{Numm}}'\n    weg_options_label: Dock / Taskbaroptiounen\n    wm_options_label: Fenster Manager Optiounen\n    workspace: Konschtpas\n    workspace_placeholder: Keen\n  bundled_msg: >-\n    Dës gebündelt Konfiguratiounen sinn net geännert a sinn entwéckelt fir Iech\n    déi bescht Erfahrung ouni Personnalisatioun ze ginn. Si konfiguréieren\n    automatesch déi heefegst Uwendungen fir Iech.\n  bundled_title: App con conflüchteg mat Seelen\n  confirm_delete: Sidd Dir sécher, datt Dir dës Configuratioun / s läscht braucht?\n  confirm_delete_title: Confirméieren Leedung\n  delete: Läschen\n  export: Export\n  export_full: Export Astellunge vun der Uwendung\n  extra_info: >-\n    Svelt UI benotzen nëmmen een identifizéierer pro App (Virdeel net fonnt),\n    sou wéi kënne bestëmmten onbestëmmt ass wichteg, well den Dësch bestëmmt\n    gëtt geschafaf, wéi Den Tabell aus alstoën ass, sou wéi den Dësch par\n    rapport ass.\n  identifier:\n    add_block: Füügt Block\n    and: An an\n    id: Identifizéierer\n    kind: Identifizéieren duerch\n    matching_strategy: Passende Strategie\n    matching_strategy_option:\n      contains: Enthält\n      ends_with: Enn mat\n      equals: Gläichzäiteg\n      regex: Regelméisseg Ausdrock\n      starts_with: Fänkt mat\n    negation: Negate passend\n    or: Oder\n    remove: Läschen Block\n    type:\n      class: Klass\n      exe: Exe\n      path: Wee\n      title: Titel\n  import: Importéieren\n  import_full: Import Astellunge per Uwendung\n  new: Nei\n  search: Sichen\n  swap: Zort\ncancel: Ofbriechen\nclose: Zoumaachen\ndelete: Läschen\ndevtools:\n  app_folders: App Classeure\n  custom_config_file: Luede personaliséiert Konfiguratiounsdatei\n  data_folder: Etabond Dossier\n  enable: Aktivéiert Entwéckler Tools\n  install_folder: Installatioun Dossier\n  load: Lueden\n  settings_file: Settytout-Datei\n  simulate_perm:\n    label: Simulatioun Widget Erlaabnis Ufro\n    result_allowed: ✓ Erlaabnis gëtt\n    result_denied: ✗ Erlaabnis refuséiert\n    trigger: Simuléieren\n    widget_id_placeholder: '@auteur/widget-name'\nextras:\n  clear_icons: Kloer System Symboler Cache\n  clear_icons_tooltip: E Restart konnt noutwendeg sinn fir ganz wéi all Widgets ze maachen\n  exit: Ophalen / erausfannen\n  links: Offiziell Linken\n  relaunch: Relatioun\n  version: Erëm\n  version_fixed: >-\n    D'Applikatioun an d'WebView2 Runtime Versioune si fixéiert. Dëst bedeit datt\n    d'Applikatioun keng Updates kritt an de WebView2 Runtime gëtt net\n    automatesch mat Windows Updates aktualiséiert.\ngeneral:\n  accent_color: Akzenter Faarf\n  date_format: Datum Format\n  date_format_how_to: Wéi schreiwen ech en Datumformat\n  hardware_acceleration: Hardware Beschleunegung\n  hardware_acceleration_description: >-\n    Desaktivéiere vun Hardware Beschleunegung reduzéiert d'Erënnerungsverbrauch,\n    awer kann Performanceprobleemer verursaachen. Ass sécher ze deaktivéieren\n    wann Dir keng Live Wallpaper benotzt.\n  icon_pack:\n    available: Verfügbar Ikon Packs\n    selected: Aktiv Ikon Packs\n  language: Sprooche\n  monday: Méindeg\n  performance_mode:\n    on_battery: Op Batterie\n    on_energy_saver: Op Energiespuer\n    options:\n      disabled: Behënnert\n      extreme: Extrem\n      minimal: Minimal\n    plugged: Ugeschloss oder opluede\n  polling_interval: System Ëmfro Intervall\n  polling_interval_description: >-\n    Wéi dacks (a Sekonnen) Seelen UI iwwerpréift Systemressourcen wéi CPU, RAM,\n    Netzwierk an Diskaktivitéit. Eng méi kleng Zuel bedeit méi heefeg Updates,\n    awer e bësse méi héich Ressourceverbrauch.\n  saturday: Samschdeg\n  start_of_week: Start vun der Woch\n  startup: Run op Startup?\n  sunday: Sonndeg\n  theme:\n    available: Verfügbar Themen\n    selected: Aktiv Themen\nheader:\n  labels:\n    config: Konfiguratioun\n    developer: Fir Entwéckler\n    extras: Extras\n    general: Generol\n    home: Doheem\n    icon_pack_editor: Cacked Symboler\n    iconpack: ICON Tass\n    monitors: Iwwerschwannegkeeten\n    plugin: Plugins\n    resources: Ressourcen\n    shortcuts: Ofkierpsen\n    soundpack: Tounpacken\n    specific_apps: Astellunge per Uwendung\n    theme: Themen\n    virtual_desk: Virtuell Desktops\n    wallpaper: Wallpapers\n    widget: Widgets\nhome:\n  new_resources: Nei Insidder\ninherit: Ontherrit\ninProgress: A Beaarbechtung...\ninsert: INor andafen\nloading: Lueden ...\nmiscellaneous: Erleeën\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Méi kleng\n'no': Nee\nopen: Ëmen e gratis\nquit: Ness\nremove: Ofwäschen\nreset_all_to_default: Reset all op Standardwäerter\nreset_to_default: Reset op Standardwäert\nresources:\n  app_outdated: Dës Ressource brauch eng nei Versioun vu Seelen Ui fir richteg ze schaffen.\n  corrupted_wallpaper: Ausgefall Miniatur Extrait - korrupt oder net ënnerstëtzt Video Format\n  delete: Läschen Ressource\n  discover: Entdeckt méi Ressourcen\n  has_update: En Update ass verfügbar\n  high_impact: Héich Impakt op Leeschtung\n  import_wallpapers: Import lokal Wallpapers\n  open_folder: Oppen Ressource Dossier\n  outdated: >-\n    Dës Ressource gouf fir eng méi al Versioun vu seelen UI entworf a sech net\n    richteg schaffen.\n  see_on_website: Gesinn op Websäit\nreview_request:\n  not_now: Net elo\n  sure: Sécher!\n  title: Genéisst Seelen UI?\nsave: Spuerwäit\nsave_and_restart: Spuert & nei starten\nsearch: Sichen\nsee_more: Méi gesinn\nshortcuts:\n  duplicate_error: Dës Ofkiirzung gëtt duplizéiert\n  enable: Aktivéiert agebaut-in Shortcuts System\n  enable_tooltip: >-\n    Desaktivéiere wann Dir Ären eegene Ofkierzungsystem implementéiert, mat dem\n    Segen UI Client\n  labels:\n    create_new_workspace: Neie Aarbechtsberäich erstellen\n    cycle_stack_next: Zyklus Stack nieft\n    cycle_stack_prev: Cycle Stack virdrun\n    cycle_wallpaper_next: Ännerung op den nächsten Tapeten\n    cycle_wallpaper_prev: Ännerung op déi virdrun Tapeten\n    decrease_height: Reduzéiert Héicht\n    decrease_width: Reduzéiert Breet\n    destroy_current_workspace: Zerstéiert aktuell Aarbechtsberäich\n    focus_bottom: Fokus ënnen\n    focus_latest: Fokusséieren dat lescht\n    focus_left: Fokusséiert lénks\n    focus_right: Fokusséiert richteg\n    focus_top: Fokusséieren Top\n    increase_height: Erhéijen Héicht\n    increase_width: Erhéijung vun der Breet\n    misc_force_quit: Kraaft opzehalen\n    misc_force_restart: Kraaft Restart\n    misc_open_settings: Oppen Astellungen\n    misc_toggle_lock_tracing: Toggle Lock Tracing (Logbicher)\n    misc_toggle_win_event_tracing: Toggle gewannen Event Tracing (Logbicher)\n    move_to_workspace: Plënnert op Aarbechtsberäich {{0}}\n    move_window_down: Géifen Fënster op ënnen\n    move_window_left: Giddfen Fënster op lénks\n    move_window_right: Réckelt Fënster op riets\n    move_window_up: Réckelt Fënster op Top\n    pause_tiling: Pause filt Fenstermanager\n    reserve_bottom: Reservéiert ënnen\n    reserve_float: Reservéiert Float\n    reserve_left: Reserve lénks\n    reserve_right: Reservéiert richteg\n    reserve_stack: Reserve Stack\n    reserve_top: Reserve Top\n    restore_sizes: Restauréiert Gréissten\n    send_to_workspace: Schéckt op Aarbechtsberäich {{0}}\n    start_weg_app: Fokusséieren oder ufänken Applikatioun {{0}}\n    switch_to_next_workspace: Wiesselt op d'nächst Aarbechtsberäich\n    switch_to_previous_workspace: Wiesselt op virdrun Aarbechtsberäich\n    switch_workspace: Schalt op Aarbechtsberäich {{0}}\n    toggle_float: Toggle Fenster Float Modus\n    toggle_monocle: Toggle Workspace Monocle Modus\n  readonly_tooltip: Dëst ass e Lies-nëmme Mangel\n  reset: Reset op Defaults\nsides:\n  bottom: Siwenn\n  left: Lénks\n  right: Riets\n  top: Uewen\ntoolbar:\n  auto_hide: Automatesch\n  delay_to_hide: Verspéidung ze verstoppen\n  delay_to_show: Verspéidung fir ze weisen\n  dock_side: Positioun un\n  enable: Aktivéiert Fore Toolbar\n  hide_mode:\n    always: Ëmmer\n    never: Ni\n    on_overlap: Op Iwwerlappung\n  item_size: Artikel Gréisst\n  label: TO-tcstar\n  margin: Margin Gréisst\n  padding: Padding Gréisst\n  placeholder: {}\nupdate:\n  available: Update verfügbar!\n  channel: Update Kanal\n  downloading: NËMMEN ...\nwall:\n  backgrounds: Wallpapers\n  blur: Blour\n  cancel: Ofbriechen\n  close: Zoumaachen\n  collection_name: Sammlung Numm\n  collections: Kollektiounen\n  contrast: Kontrad\n  corrupted_wallpapers_message: 'Korrupt Tapeten, betruecht dës ze läschen:'\n  create: Schafen\n  create_collection: Schafen Kollektioun\n  default_collection: Standard Kollektioun\n  delete_collection: Läschen Kollektioun\n  edit_collection: Kollektioun änneren\n  enable: Aktivéiert Wallpaper Manager\n  extend: Primär verlängeren\n  fit:\n    contain: Enthalen\n    cover: Zouzedoen\n    fill: Fëllt\n  flipHorizontal: Flip horizontal\n  flipVertical: Flip vertikal\n  generating_thumbnails: Generéiere Wallpaper Thumbnails\n  hours: Stonnen.\n  interval: Änneren Wallpaper all\n  minutes: Minutte vun der Minutten\n  monitor_collection: Monitor Kollektioun\n  multimonitor_behaviour: Multimonitor Behuelen\n  muted: Demp Video Audio\n  no_background: Eidel Slatswhow, benotzt den Thema vum Thema amplaz.\n  no_collections: >-\n    Keng Kollektiounen erstallt nach. Klickt op de + Knäppchen fir een ze\n    kreéieren.\n  objectFit: Hannergrond fit\n  objectPosition: Hannergrënn Positioun\n  overlayColor: Iwwerlauschteren Faarf\n  overlayMixBlendMode: Overlay Mix vermëschen Modus\n  per_monitor: Pro Monitor\n  playback: Spillversuch\n  position:\n    bottom: Siwenn\n    center: Center\n    left: Lénks\n    right: Riets\n    top: Uewen\n  processing_video: Video veraarbecht\n  random: Zoufälleg Slideshow\n  saturation: Saturatioun\n  seconds: Sekonnen\n  select_collection: Wielt Kollektioun\n  thumbnail_generation_complete: Thumbnail Generatioun Komplett\n  thumbnail_generation_finished: Thumbnail Generatioun ass erfollegräich ofgeschloss\n  wallpaper_collection: Wallpaper Kollektioun\n  wallpaper_settings: Wallpaper Astellunge\n  withOverlay: Mat Overlay\n  workspace_collections: Aarbechtsberäich Kollektiounen\nweg:\n  auto_hide: Automatesch\n  delay_to_hide: Verspéidung ze verstoppen\n  delay_to_show: Verspéidung fir ze weisen\n  dock_side: Positioun un\n  enable: Aktivéiert Dock / Taskbar\n  filtering: Item Filter\n  gap: Knënn\n  hide_mode:\n    always: Ëmmer\n    never: Ni\n    on_overlap: Op Iwwerlappung\n  items:\n    gap: Raum tëscht Artikelen\n    label: Artikelen\n    pinned_visibility:\n      always: Ëmmer\n      label: Pinned Elementer Visibilitéit\n      when_primary: Wann de Monitor primär ass\n    show_instance_counter: Show oppe Windows Konter\n    show_window_title: Show Open Fenster Titel (nëmmen horizontal)\n    size: Telefonsgrouss\n    split_windows: Split Windows (een Element pro Fënster)\n    temporal_visibility:\n      all: All\n      label: Unpinned Elementer Visibilitéit\n      on_monitor: Op Monitor\n    visible_separators: Sechsi seichtbar Seperure\n  label: Dock / Taskbar\n  margin: Den Jean-Margin\n  mode:\n    full_width: Voll Écran Breet\n    min_content: Kleng wéi méiglech\n  padding: Padding\n  show_end_task: Show Enn Aufgab an der Taskbar\n  width: Breet\nwelcome:\n  give_a_review: Gëff eng Iwwerpréiwung\n  message: >-\n    Seelen UI ass e gratis an Open Source Desktop-Ëmfeld fir Windows. Hei hutt\n    Dir voll Kontroll iwwer wéi Ären Desktop ausgesäit a behuelen, wat Iech\n    erlaabt all Detail ze personaliséieren fir Äre Workflow a Stil ze passen.\n  ok: Loosst eis ufänken!\n  review: >-\n    Wann Dir genéisst Seelen UI ze benotzen, betruecht eng Iwwerpréiwung am\n    Geschäft ze loossen - Äre Feedback hëlleft de Projet ze wuessen a\n    verbesseren.\n  title: Wëllkomm op Seelen UI!\nwidget:\n  enable: Aktivéiert dëse Widget\n  enable_for_monitor: Aktivéiert op dësem Monitor\n  instances: Instanzen\nwm:\n  animations:\n    duration: Animatioun Dauer (MS)\n    ease_function: Animatioun ausserhalb Funktioun\n    enable: Aktivéiert Fenster Animatiounen\n  author: Auteur\n  border:\n    enable: Aktivéiert Fenster Grenz\n    offset: Grenz Offäll\n    width: Grenz Breet\n  description: Broessdatsch\n  drag_behavior: Drag Behuelen\n  drag_behavior_options:\n    sort: Sortéieren (Fënstere nei bestellen beim zéien)\n    swap: Tauschen (Swap Fënsteren um Drag End)\n  enable: Aktivéiert Fenster-Manager\n  layout: Lueden\n  resize_delta: Resize Delta (%)\n  space_between_containers: Raum tëscht Behälter\n  workspace_offset: Workspaces Offset (Rand)\n  workspace_padding: Aarbechtsberäich padding\n'yes': Jo\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/lo.yml",
    "content": "action:\n  confirm: ທ່ານແນ່ໃຈບໍ່?\n  confirm_body: ການກະທໍານີ້ບໍ່ສາມາດເຮັດໄດ້.\napps_configurations:\n  app:\n    bindings: ການຜູກມັດ (ຫມາຍເຫດທັງສອງທາງເລືອກແມ່ນຈໍາເປັນ)\n    category: ປະເພດ\n    category_placeholder: ບໍ່ມີ\n    monitor: ຕິດຕາມກວດກາ\n    monitor_placeholder: ບໍ່ມີ\n    name: ຊື່\n    ok_create: ສ້າງ\n    ok_edit: ອັບເດດ\n    ok_readonly: ແກ້ໄຂເປັນໃໝ່\n    options:\n      NoInteractive: ບໍ່ມີການໂຕ້ຕອບກັນ\n      VdPinned: ສະແດງໃນທຸກບ່ອນເຮັດວຽກ\n      WmFloat: twm - ເລີ່ມຕົ້ນລອຍ\n      WmForce: twm - ກໍາລັງແຮງງານຄຸ້ມຄອງ\n      WmUnmanage: twm - unmanage\n    options_label: ທາງເລືອກພິເສດ\n    title_create: ການສ້າງ {{name}}\n    title_edit: ການແກ້ໄຂ {{ne name}}}\n    title_readonly: ກຳລັງເບິ່ງ {{name}}\n    weg_options_label: ທາງເລືອກ Dock / Taskasbar\n    wm_options_label: ຕົວເລືອກຜູ້ຈັດການ window Manager\n    workspace: ພື້ນທີ່ເຮັດວຽກ\n    workspace_placeholder: ບໍ່ມີ\n  bundled_msg: >-\n    ການຕັ້ງຄ່າຊຸດເຫຼົ່ານີ້ບໍ່ສາມາດແກ້ໄຂໄດ້\n    ແລະຖືກອອກແບບມາເພື່ອໃຫ້ປະສົບການທີ່ດີທີ່ສຸດແກ່ເຈົ້າໂດຍບໍ່ມີການປັບແຕ່ງ.\n    ພວກເຂົາຕັ້ງຄ່າແອັບພລິເຄຊັນທົ່ວໄປທີ່ສຸດສໍາລັບທ່ານໂດຍອັດຕະໂນມັດ.\n  bundled_title: App Config Bundled ກັບ Seelen\n  confirm_delete: ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບການຕັ້ງຄ່ານີ້?\n  confirm_delete_title: ຢືນຢັນການລຶບ\n  delete: ລຶບ\n  export: ສົ່ງອອກ\n  export_full: ການຕັ້ງຄ່າການສົ່ງອອກໂດຍສະຫມັກ\n  extra_info: >-\n    SEVENEN UI ໃຊ້ພຽງແຕ່ຕົວລະບຸຕົວລະບຸ 1 (ທີ່ພົບໃນລະດັບທໍາອິດ)\n    ສະນັ້ນຄໍາສັ່ງທີ່ມີຄວາມສໍາຄັນ, ເຊັ່ນວ່າຕາຕະລາງຖືກຈັດຮຽງຕາມລໍາດັບເດີມ\n  identifier:\n    add_block: ເພີ່ມ Block\n    and: ແລະ\n    id: ຕົວລະບຸ\n    kind: ກໍານົດໂດຍ\n    matching_strategy: ຍຸດທະສາດການຈັບຄູ່\n    matching_strategy_option:\n      contains: ປະກອບດ້ວຍ\n      ends_with: ສິ້ນສຸດດ້ວຍ\n      equals: ເທົ່າກັບ\n      regex: ການສະແດງອອກເປັນປົກກະຕິ\n      starts_with: ເລີ່ມຕົ້ນດ້ວຍ\n    negation: Negate ການຈັບຄູ່\n    or: ຫຼື\n    remove: ລຶບ Block\n    type:\n      class: ຫ້ອງຮຽນ\n      exe: exe\n      path: ເສັ້ນທາງ\n      title: ຫົວຂໍ້\n  import: ນໍາເຂົ້າ\n  import_full: ການຕັ້ງຄ່ານໍາເຂົ້າໂດຍການສະຫມັກ\n  new: ໃຫມ່\n  search: ຊອກຫາ\n  swap: ແລກປ່ຽນ\ncancel: ຍົກເລີກ\nclose: ປິດ\ndelete: ລຶບ\ndevtools:\n  app_folders: ໂຟນເດີ App\n  custom_config_file: ໂຫຼດໄຟລ໌ການຕັ້ງຄ່າແບບກຳນົດເອງ\n  data_folder: ແຟ້ມຂໍ້ມູນ\n  enable: ເປີດໃຊ້ເຄື່ອງມືນັກພັດທະນາ\n  install_folder: ໂຟນເດີການຕິດຕັ້ງ\n  load: ໂຫຼດ\n  settings_file: ໄຟລ໌ການຕັ້ງຄ່າ\n  simulate_perm:\n    label: ຈໍາລອງການຮ້ອງຂໍການອະນຸຍາດ Widget\n    result_allowed: ✓​ການ​ອະ​ນຸ​ຍາດ​ໃຫ້​\n    result_denied: ✗ ການ​ອະ​ນຸ​ຍາດ​ຖືກ​ປະ​ຕິ​ເສດ​\n    trigger: ຈຳລອງ\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: ລະບົບໄອຄອນລະບົບໄອຄອນ\n  clear_icons_tooltip: ການເລີ່ມຕົ້ນໃຫມ່ອາດຈະມີຜົນບັງຄັບໃຊ້ຢ່າງເຕັມສ່ວນໃນທຸກໆ widget\n  exit: ອອກ/ອອກ\n  links: ການເຊື່ອມຕໍ່ຢ່າງເປັນທາງການ\n  relaunch: ເປີດຄືນໃໝ່\n  version: ຮຸ່ນ\n  version_fixed: >-\n    ແອັບພລິເຄຊັນ ແລະ WebView2 Runtime ເວີຊັ່ນຖືກແກ້ໄຂແລ້ວ.\n    ນີ້ຫມາຍຄວາມວ່າແອັບພລິເຄຊັນຈະບໍ່ໄດ້ຮັບການປັບປຸງແລະ WebView2 Runtime\n    ຈະບໍ່ຖືກປັບປຸງໂດຍອັດຕະໂນມັດກັບການປັບປຸງ Windows.\ngeneral:\n  accent_color: ສີສໍານຽງ\n  date_format: ຮູບແບບວັນທີ\n  date_format_how_to: ວິທີການຂຽນຮູບແບບວັນທີ?\n  hardware_acceleration: ການເລັ່ງຮາດແວ\n  hardware_acceleration_description: >-\n    ການປິດການນຳໃຊ້ການເລັ່ງຮາດແວຈະຫຼຸດການນຳໃຊ້ໜ່ວຍຄວາມຈຳ,\n    ແຕ່ສາມາດເຮັດໃຫ້ເກີດບັນຫາປະສິດທິພາບໄດ້.\n    ແມ່ນປອດໄພທີ່ຈະປິດການໃຊ້ງານຖ້າທ່ານບໍ່ໃຊ້ຮູບວໍເປເປີສົດ.\n  icon_pack:\n    available: ໄອຄອນຊອງທີ່ມີໃຫ້\n    selected: ຊຸດໄອຄອນທີ່ໃຊ້ໄດ້\n  language: ພາສາ\n  monday: ວັນຈັນ\n  performance_mode:\n    on_battery: ສຸດແບັດເຕີຣີ\n    on_energy_saver: ກ່ຽວກັບປະຫຍັດພະລັງງານ\n    options:\n      disabled: ພິການ\n      extreme: ທີ່ຮ້າຍ\n      minimal: ຫນ້ອຍ\n    plugged: ສຽບຫລືສາກໄຟ\n  polling_interval: ໄລຍະການສຳຫຼວດລະບົບ\n  polling_interval_description: >-\n    ເລື້ອຍປານໃດ (ເປັນວິນາທີ) Seelen UI ກວດເບິ່ງຊັບພະຍາກອນລະບົບເຊັ່ນ CPU, RAM,\n    ເຄືອຂ່າຍ ແລະການເຄື່ອນໄຫວຂອງແຜ່ນ. ຕົວເລກທີ່ນ້ອຍກວ່າຫມາຍເຖິງການອັບເດດເລື້ອຍໆ,\n    ແຕ່ການໃຊ້ຊັບພະຍາກອນສູງກວ່າເລັກນ້ອຍ.\n  saturday: ວັນເສົາ\n  start_of_week: ເລີ່ມຕົ້ນອາທິດ\n  startup: ດໍາເນີນການກ່ຽວກັບການເລີ່ມຕົ້ນ?\n  sunday: ວັນອາທິດ\n  theme:\n    available: ຮູບແບບສີສັນທີ່ມີຢູ່\n    selected: ຮູບແບບການເຄື່ອນໄຫວ\nheader:\n  labels:\n    config: ການຕັ້ງຄ່າການຕັ້ງຄ່າ\n    developer: ສໍາລັບນັກພັດທະນາ\n    extras: ພິເສດ\n    general: ທົ່ວໄປ\n    home: ບ້ານ\n    icon_pack_editor: ຮູບສັນຍາລັກຂອງຖານ\n    iconpack: icon ຊອງ\n    monitors: ຈໍພາບ\n    plugin: Plugins\n    resources: ຊັບພະຍາກອນ\n    shortcuts: ທາງລັດ\n    soundpack: ຊອງສຽງ\n    specific_apps: ການຕັ້ງຄ່າໂດຍການສະຫມັກ\n    theme: ຫົວຂໍ້\n    virtual_desk: virtual desktops\n    wallpaper: ຮູບວໍເປເປີ\n    widget: widgets\nhome:\n  new_resources: ຊັບພະຍາກອນໃຫມ່\ninherit: ມູນມໍລະດົກ\ninProgress: ຢູ່ໃນຄວາມຄືບໜ້າ...\ninsert: ຊອນເຂົ້າ\nloading: ກຳລັງໂຫລດ...\nmiscellaneous: ຫຼາຢ\nmonitors_configurations:\n  label: Monitor {{{{ດັດສະນີ}}\nmore: ຫຼາຍ\n'no': ບໍ່\nopen: ເປີດ\nquit: ເຊົາ\nremove: ເອົາອອກ\nreset_all_to_default: ປັບຄ່າທັງຫມົດໃຫ້ກັບຄ່າເລີ່ມຕົ້ນ\nreset_to_default: ຕັ້ງຄ່າໃຫມ່ໃຫ້ກັບຄ່າເລີ່ມຕົ້ນ\nresources:\n  app_outdated: ຊັບພະຍາກອນນີ້ຮຽກຮ້ອງໃຫ້ມີ Searen ລຸ້ນໃຫມ່ UI ໃຫ້ເຮັດວຽກຢ່າງຖືກຕ້ອງ.\n  corrupted_wallpaper: ລົ້ມເຫລວໃນການສະກັດຮູບຫຍໍ້ - ຮູບແບບວິດີໂອເສຍຫາຍ ຫຼືບໍ່ຮອງຮັບ\n  delete: ລົບລ້າງຊັບພະຍາກອນ\n  discover: ຄົ້ນພົບຊັບພະຍາກອນເພີ່ມເຕີມ\n  has_update: ມີການປັບປຸງໃຫມ່\n  high_impact: ຜົນກະທົບສູງຕໍ່ການປະຕິບັດ\n  import_wallpapers: ນໍາເຂົ້າຮູບວໍເປເປີທ້ອງຖິ່ນ\n  open_folder: Open Opence Folder\n  outdated: >-\n    ຊັບພະຍາກອນນີ້ໄດ້ຖືກອອກແບບມາສໍາລັບ Apen ຂອງ Searen UI\n    ແລະອາດຈະບໍ່ເຮັດວຽກຢ່າງຖືກຕ້ອງ.\n  see_on_website: ເບິ່ງຢູ່ໃນເວັບໄຊທ໌\nreview_request:\n  not_now: ບໍ່ແມ່ນດຽວນີ້\n  sure: ແນ່ນອນ!\n  title: ເພີດເພີນໄປກັບ Seelen UI ບໍ?\nsave: ບັນທຶກ\nsave_and_restart: ບັນທຶກແລະ Restart\nsearch: ຊອກຫາ\nsee_more: ເບິ່ງເພີ່ມເຕີມ\nshortcuts:\n  duplicate_error: ທາງລັດນີ້ຊໍ້າກັນ\n  enable: ເປີດໃຊ້ລະບົບທາງລັດທີ່ມີຢູ່\n  enable_tooltip: ປິດການໃຊ້ງານຖ້າທ່ານຈະປະຕິບັດລະບົບທາງລັດຂອງທ່ານເອງໂດຍໃຊ້ລູກຄ້າ Setelen Ui\n  labels:\n    create_new_workspace: ສ້າງພື້ນທີ່ເຮັດວຽກໃຫມ່\n    cycle_stack_next: stack ຮອບວຽນຕໍ່ໄປ\n    cycle_stack_prev: stack ຮອບວຽນທີ່ຜ່ານມາ\n    cycle_wallpaper_next: ປ່ຽນເປັນຮູບວໍເປເປີຕໍ່ໄປ\n    cycle_wallpaper_prev: ປ່ຽນເປັນຮູບວໍເປເປີທີ່ຜ່ານມາ\n    decrease_height: ຫຼຸດລົງຄວາມສູງ\n    decrease_width: ຫຼຸດຜ່ອນຄວາມກວ້າງ\n    destroy_current_workspace: ທໍາລາຍພື້ນທີ່ເຮັດວຽກໃນປະຈຸບັນ\n    focus_bottom: ສຸມໃສ່ດ້ານລຸ່ມ\n    focus_latest: ສຸມສຸດທ້າຍ\n    focus_left: ສຸມໃສ່ຊ້າຍ\n    focus_right: ສຸມໃສ່ສິດ\n    focus_top: ສຸມໃສ່ດ້ານເທິງ\n    increase_height: ເພີ່ມຄວາມສູງ\n    increase_width: ເພີ່ມຄວາມກວ້າງ\n    misc_force_quit: ບັງຄັບໃຫ້ເຊົາ\n    misc_force_restart: ບັງຄັບໃຫ້ Restart\n    misc_open_settings: ເປີດການຕັ້ງຄ່າ\n    misc_toggle_lock_tracing: Toggle Lock Tracing (ບັນທຶກ)\n    misc_toggle_win_event_tracing: Toggle WIN ການແຂ່ງຂັນເຫດການ (ບັນທຶກ)\n    move_to_workspace: ຍ້າຍໄປເຮັດວຽກ {{0}}\n    move_window_down: ຍ້າຍປ່ອງຢ້ຽມໄປທາງລຸ່ມ\n    move_window_left: ຍ້າຍປ່ອງຢ້ຽມໄປທາງຊ້າຍ\n    move_window_right: ຍ້າຍປ່ອງຢ້ຽມໄປທາງຂວາ\n    move_window_up: ຍ້າຍປ່ອງຢ້ຽມໄປທາງເທີງ\n    pause_tiling: ຢຸດຊົ່ວຄາວຜູ້ຈັດການປ່ອງຢ້ຽມ\n    reserve_bottom: ສໍາຮອງລຸ່ມ\n    reserve_float: ເລື່ອນລອຍ\n    reserve_left: ສະຫງວນໄວ້\n    reserve_right: ສະຫງວນສິດ\n    reserve_stack: ສະຫງວນ\n    reserve_top: ສໍາຮອງສໍາລັບການ\n    restore_sizes: ຟື້ນຟູຂະຫນາດ\n    send_to_workspace: ສົ່ງໄປທີ່ Workspace {{0}}\n    start_weg_app: ຈຸດສຸມຫຼືການສະຫມັກສະຫມັກ {{0}}\n    switch_to_next_workspace: ປ່ຽນໄປເຮັດວຽກຢູ່ຫນ້າທໍາອິດ\n    switch_to_previous_workspace: ປ່ຽນເປັນບ່ອນເຮັດວຽກທີ່ຜ່ານມາ\n    switch_workspace: ປ່ຽນໄປເຮັດວຽກຢູ່ Workspace {{0}}\n    toggle_float: ຮູບແບບການເລື່ອນຂອງປ່ອງຢ້ຽມ\n    toggle_monocle: ຮູບແບບ Monecle Workspace\n  readonly_tooltip: ນີ້ແມ່ນທາງລັດທີ່ອ່ານເທົ່ານັ້ນ\n  reset: ຕັ້ງຄ່າຄືນໃຫມ່\nsides:\n  bottom: ລຸ່ມ\n  left: ຊ້າຍ\n  right: ສິດ\n  top: ເທິງ\ntoolbar:\n  auto_hide: ຊ່ອນອັດຕະໂນມັດ\n  delay_to_hide: ຊັກຊ້າເພື່ອຊ່ອນ\n  delay_to_show: ຊັກຊ້າໃນການສະແດງ\n  dock_side: ຕໍາແຫນ່ງ\n  enable: ເປີດໃຊ້ແຖບເຄື່ອງມື Fancy\n  hide_mode:\n    always: ສະເໝີ\n    never: ບໍ່ເຄີຍ\n    on_overlap: ທັບຊ້ອນກັນ\n  item_size: ຂະໜາດລາຍການ\n  label: ແຖບເຄື່ອງມື\n  margin: ຂະໜາດຂອບ\n  padding: ຂະໜາດແຜ່ນ\n  placeholder: {}\nupdate:\n  available: ປັບປຸງໃຫ້ທັນສະໄຫມ!\n  channel: ຊ່ອງທາງການປັບປຸງ\n  downloading: ການດາວໂຫລດ ...\nwall:\n  backgrounds: ຮູບວໍເປເປີ\n  blur: ມົວ\n  cancel: ຍົກເລີກ\n  close: ປິດ\n  collection_name: ຊື່ຄໍເລັກຊັນ\n  collections: ຄໍເລັກຊັນ\n  contrast: ຄວາມແຕກຕ່າງ\n  corrupted_wallpapers_message: 'ຮູບວໍເປເປີທີ່ເສຍຫາຍ, ພິຈາລະນາການລຶບສິ່ງເຫຼົ່ານີ້:'\n  create: ສ້າງ\n  create_collection: ສ້າງຄໍເລັກຊັນ\n  default_collection: ການເກັບຄ່າເລີ່ມຕົ້ນ\n  delete_collection: ລຶບຄໍເລັກຊັນ\n  edit_collection: ແກ້ໄຂຄໍເລັກຊັນ\n  enable: ເປີດໃຊ້ Wallpaper Manager\n  extend: ຂະຫຍາຍຂັ້ນຕົ້ນ\n  fit:\n    contain: ບັນຈຸ\n    cover: ຝາປິດ\n    fill: ຕື່ມຂໍ້ມູນ\n  flipHorizontal: flip ອອກຕາມລວງນອນ\n  flipVertical: ພິກແນວຕັ້ງ\n  generating_thumbnails: ກຳລັງສ້າງຮູບຫຍໍ້ຂອງວໍເປເປີ\n  hours: ຊົ່ວໂມງ\n  interval: ປ່ຽນຮູບວໍເປເປີທຸກ\n  minutes: ນາທີ\n  monitor_collection: ຕິດຕາມການເກັບກໍາ\n  multimonitor_behaviour: ພຶດຕິກຳ Multimonitor\n  muted: ສຽງ mute ສຽງສຽງ\n  no_background: ການສະໄລ້ທີ່ເປົ່າ, ໂດຍໃຊ້ພື້ນຫລັງຂອງຫົວຂໍ້ແທນ.\n  no_collections: ຍັງບໍ່ມີການສ້າງຄໍເລັກຊັນເທື່ອ. ຄລິກປຸ່ມ + ເພື່ອສ້າງອັນ.\n  objectFit: ປະຫວັດຄວາມເປັນມາ\n  objectPosition: ຕໍາແຫນ່ງພື້ນຫລັງ\n  overlayColor: ສີ Overlay\n  overlayMixBlendMode: ຮູບແບບການຜະສົມຜະສານແບບປະສົມ\n  per_monitor: ຕໍ່ຈໍພາບ\n  playback: ຄວາມໄວ PlaySback\n  position:\n    bottom: ດ້ານລຸ່ມ\n    center: ສູນກາງ\n    left: ກ່ໍາ\n    right: ຖືກຕ້ອງ\n    top: ທາງເທີງ\n  processing_video: ກຳລັງປະມວນຜົນວິດີໂອ\n  random: ແບບສະໄລດູນແບບສຸ່ມ\n  saturation: ອີ່ມຕົວ\n  seconds: ວິນາທີ\n  select_collection: ເລືອກຄໍເລັກຊັນ\n  thumbnail_generation_complete: ການສ້າງຮູບຕົວຢ່າງສໍາເລັດ\n  thumbnail_generation_finished: ການສ້າງຮູບຕົວຢ່າງສຳເລັດແລ້ວ\n  wallpaper_collection: ການເກັບພາບພື້ນຫຼັງ\n  wallpaper_settings: ການຕັ້ງຄ່າຮູບວໍເປເປີ\n  withOverlay: ດ້ວຍການຊ້ອນກັນ\n  workspace_collections: ການເກັບພື້ນທີ່ເຮັດວຽກ\nweg:\n  auto_hide: ເຊື່ອງອັດຕະໂນມັດ\n  delay_to_hide: ຊັກຊ້າເພື່ອຊ່ອນ\n  delay_to_show: ຊັກຊ້າໃນການສະແດງ\n  dock_side: ຕໍາແຫນ່ງ\n  enable: ເປີດໃຊ້ Dock/Taskbar\n  filtering: ການກັ່ນຕອງລາຍການ\n  gap: ຊ່ອງຫວ່າງ\n  hide_mode:\n    always: ສະເໝີ\n    never: ບໍ່ເຄີຍ\n    on_overlap: ທັບຊ້ອນກັນ\n  items:\n    gap: ຊ່ອງຫວ່າງລະຫວ່າງລາຍການ\n    label: ລາຍການ\n    pinned_visibility:\n      always: ສະເໝີ\n      label: ການເບິ່ງເຫັນລາຍການທີ່ປັກໝຸດໄວ້\n      when_primary: ເມື່ອຈໍພາບແມ່ນປະຖົມ\n    show_instance_counter: ສະແດງວຽກງານຕ້ານການເປີດ\n    show_window_title: ສະແດງຫົວຂໍ້ເປີດປ່ອງຢ້ຽມ (ພຽງແຕ່ແນວນອນ)\n    size: ຂະໜາດລາຍການ\n    split_windows: ແຍກ Windows (ຫນຶ່ງລາຍການຕໍ່ປ່ອງຢ້ຽມ)\n    temporal_visibility:\n      all: ທັງໝົດ\n      label: ການເບິ່ງເຫັນລາຍການທີ່ບໍ່ໄດ້ປັກໝຸດ\n      on_monitor: ໃນ Monitor\n    visible_separators: ຕົວແຍກທີ່ເບິ່ງເຫັນໄດ້\n  label: Dock/Taskbar\n  margin: ຂອບ\n  mode:\n    full_width: ຄວາມກວ້າງເຕັມຈໍ\n    min_content: ຂະຫນາດນ້ອຍເທົ່າທີ່ສາມາດເຮັດໄດ້\n  padding: padding\n  show_end_task: ສະແດງວຽກທີ່ສຸດໃນຫນ້າວຽກ\n  width: ກວ້າງ\nwelcome:\n  give_a_review: ໃຫ້ການທົບທວນຄືນ\n  message: >-\n    Seelen UI ແມ່ນສະພາບແວດລ້ອມເດັສທັອບທີ່ບໍ່ເສຍຄ່າ ແລະເປີດສໍາລັບ Windows.\n    ໃນທີ່ນີ້ທ່ານມີການຄວບຄຸມຢ່າງເຕັມທີ່ກ່ຽວກັບວິທີການ desktop\n    ຂອງທ່ານເບິ່ງແລະປະຕິບັດຕົວ,\n    ຊ່ວຍໃຫ້ທ່ານສາມາດປັບແຕ່ງທຸກລາຍລະອຽດເພື່ອໃຫ້ກົງກັບຂະບວນການເຮັດວຽກແລະຮູບແບບຂອງທ່ານ.\n  ok: ມາເລີ່ມກັນເລີຍ!\n  review: >-\n    ຖ້າທ່ານມັກໃຊ້ Seelen UI, ພິຈາລະນາອອກການທົບທວນຄືນໃນຮ້ານ —\n    ຄໍາຄິດເຫັນຂອງທ່ານຊ່ວຍໃຫ້ໂຄງການຂະຫຍາຍຕົວແລະປັບປຸງ.\n  title: ຍິນດີຕ້ອນຮັບສູ່ Seelen UI!\nwidget:\n  enable: ເປີດໃຊ້ widget ນີ້\n  enable_for_monitor: ເປີດໃຊ້ງານຕິດຕາມກວດການີ້\n  instances: ຖານະ\nwm:\n  animations:\n    duration: ໄລຍະເວລາທີ່ມີຊີວິດຊີວາ (MS)\n    ease_function: ຫນ້າທີ່ການທ່ອງທ່ຽວແບບເຄື່ອນໄຫວ\n    enable: ເປີດໃຊ້ພາບເຄື່ອນໄຫວຂອງ window\n  author: ຜູ້ຂຽນ\n  border:\n    enable: ເປີດໃຊ້ຂອບຂອງປ່ອງຢ້ຽມ\n    offset: Border Offset\n    width: ຄວາມກວ້າງຊາຍແດນ\n  description: ລາຍລະອຽດ\n  drag_behavior: ພຶດຕິກໍາການລາກ\n  drag_behavior_options:\n    sort: ຈັດຮຽງ (ຈັດຮຽງໜ້າຕ່າງໃໝ່ໃນຂະນະທີ່ລາກ)\n    swap: Swap (ສະຫຼັບປ່ອງຢ້ຽມຢູ່ປາຍລາກ)\n  enable: ເປີດໃຊ້ຜູ້ຈັດການປ່ອງຢ້ຽມກະເບື້ອງ\n  layout: ແຜນຜັງ\n  resize_delta: ປັບຂະໜາດ Delta (%)\n  space_between_containers: ຊ່ອງຫວ່າງລະຫວ່າງບັນຈຸ\n  workspace_offset: ພື້ນທີ່ເຮັດວຽກຊົດເຊີຍ (ຂອບ)\n  workspace_padding: ພື້ນທີ່ເຮັດວຽກ Padding\n'yes': ແລ້ວ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/lt.yml",
    "content": "action:\n  confirm: Ar tikrai?\n  confirm_body: Šis veiksmas negali būti panaikintas.\napps_configurations:\n  app:\n    bindings: Įrišimas (pastaba yra būtinos abi parinktys)\n    category: Kategorija\n    category_placeholder: Nė vienas\n    monitor: Monitorius\n    monitor_placeholder: Nė vienas\n    name: vardas\n    ok_create: Sukurti\n    ok_edit: Atnaujinimas\n    ok_readonly: Redaguoti kaip naują\n    options:\n      NoInteractive: Nėra interaktyvaus\n      VdPinned: Rodyti visose darbo srityse\n      WmFloat: Twm – pradėti plūduriuoti\n      WmForce: Twm – jėgos valdymas\n      WmUnmanage: Twm – nevaldyti\n    options_label: Papildomos galimybės\n    title_create: Kurti {{name}}\n    title_edit: Redagavimas {{name}}\n    title_readonly: Peržiūra {{name}}\n    weg_options_label: „Dock“/„Taskbar“ parinktys\n    wm_options_label: „Windows Manager“ parinktys\n    workspace: Darbo vieta\n    workspace_placeholder: Nė vienas\n  bundled_msg: >-\n    Šios sujungtos konfigūracijos nėra redaguojamos ir yra skirtos suteikti jums\n    geriausią patirtį be pritaikymo. Jie automatiškai sukonfigūruoja dažniausiai\n    jums.\n  bundled_title: APP CONFIC sujungta su „Seelen“\n  confirm_delete: Ar tikrai norite ištrinti šią konfigūraciją (-us)?\n  confirm_delete_title: Patvirtinkite ištrynti\n  delete: Ištrinti\n  export: Eksportas\n  export_full: Eksportuoti nustatymus pagal programą\n  extra_info: >-\n    „Seelen“ vartotojo sąsaja naudoja tik vieną identifikatorių vienai programai\n    (rasta pirmoji atitiktis), todėl svarbi tvarka, kaip nurodyta, bus teikiama\n    pirmenybė naujausiai pridėtai, nes pagal numatytuosius nustatymus lentelė\n    rūšiuojama nuo naujausios iki senesnės.\n  identifier:\n    add_block: Pridėkite bloką\n    and: Ir\n    id: Identifikatorius\n    kind: Identifikuoti\n    matching_strategy: Atitikimo strategija\n    matching_strategy_option:\n      contains: Sudėtyje yra\n      ends_with: Baigiasi su\n      equals: Lygu\n      regex: Reguliari išraiška\n      starts_with: Prasideda nuo\n    negation: Neigiamas\n    or: Arba\n    remove: Ištrinti bloką\n    type:\n      class: Klasė\n      exe: Exe\n      path: Kelias\n      title: Pavadinimas\n  import: Importuoti\n  import_full: Nustatymų importavimas pagal programą\n  new: Nauja\n  search: Paieška\n  swap: Apsikeitimas\ncancel: Atšaukti\nclose: Uždaryti\ndelete: Ištrinti\ndevtools:\n  app_folders: Programos aplankai\n  custom_config_file: Įkelkite pasirinktinį konfigūracijos failą\n  data_folder: Duomenų aplankas\n  enable: Įgalinkite kūrėjo įrankius\n  install_folder: Diegimo aplankas\n  load: Įkelti\n  settings_file: Nustatymų failas\n  simulate_perm:\n    label: Imituoti valdiklio leidimo užklausą\n    result_allowed: ✓ Leidimas suteiktas\n    result_denied: ✗ Leidimas atmestas\n    trigger: Imituoti\n    widget_id_placeholder: '@autorius/valdiklio pavadinimas'\nextras:\n  clear_icons: Išvalyti sistemos piktogramų talpyklą\n  clear_icons_tooltip: Gali prireikti iš naujo paleisti, kad visi valdikliai visiškai įsigaliotų.\n  exit: Mesti/išeiti\n  links: Oficialios nuorodos\n  relaunch: Atsikrauti\n  version: Versija\n  version_fixed: >-\n    Programos ir „WebView2 Runtime“ versijos yra fiksuotos. Tai reiškia, kad\n    programa negaus naujinimų ir „WebView2 Runtime“ nebus automatiškai\n    atnaujinta naudojant „Windows“ naujinimus.\ngeneral:\n  accent_color: Akcento spalva\n  date_format: Datos formatas\n  date_format_how_to: Kaip parašyti datos formatą?\n  hardware_acceleration: Aparatinės įrangos pagreitis\n  hardware_acceleration_description: >-\n    Išjungus aparatinės įrangos spartinimą sumažės atminties naudojimas, tačiau\n    gali kilti našumo problemų. Saugu išjungti, jei nenaudosite tiesioginių\n    tapetų.\n  icon_pack:\n    available: Galimi piktogramų paketai\n    selected: Aktyvūs piktogramų paketai\n  language: Kalba\n  monday: Pirmadienis\n  performance_mode:\n    on_battery: Ant akumuliatoriaus\n    on_energy_saver: Dėl energijos taupymo\n    options:\n      disabled: Neįgalus\n      extreme: Kraštutinumas\n      minimal: Minimalus\n    plugged: Prijungtas ar įkrovimas\n  polling_interval: Sistemos apklausos intervalas\n  polling_interval_description: >-\n    Kaip dažnai (sekundėmis) Seelen vartotojo sąsaja tikrina sistemos išteklius,\n    pvz., CPU, RAM, tinklo ir disko veiklą. Mažesnis skaičius reiškia dažnesnius\n    atnaujinimus, bet šiek tiek didesnį išteklių naudojimą.\n  saturday: šeštadienis\n  start_of_week: Savaitės pradžia\n  startup: Paleisti paleidimą?\n  sunday: sekmadienis\n  theme:\n    available: Galimos temos\n    selected: Aktyvios temos\nheader:\n  labels:\n    config: Konfigūracijos\n    developer: Kūrėjams\n    extras: Priedai\n    general: Bendrasis\n    home: Namai\n    icon_pack_editor: Ikonos talpyklose\n    iconpack: Ikonų paketai\n    monitors: Monitoriai\n    plugin: Įskiepiai\n    resources: Ištekliai\n    shortcuts: Nuorodos\n    soundpack: Garso paketai\n    specific_apps: Nustatymai pagal programą\n    theme: Temos\n    virtual_desk: Virtualūs darbalaukio kompiuteriai\n    wallpaper: Užsklandos\n    widget: Valdikliai\nhome:\n  new_resources: Nauji ištekliai\ninherit: Paveldėti\ninProgress: Vyksta ...\ninsert: Įterpti\nloading: Pakrovimas ...\nmiscellaneous: Įvairūs\nmonitors_configurations:\n  label: Monitorius {{index}}\nmore: Daugiau\n'no': Ne\nopen: Atviras\nquit: Mesti\nremove: Pašalinti\nreset_all_to_default: Iš naujo nustatyti visas numatytąsias vertes\nreset_to_default: Iš naujo nustatyti numatytąją vertę\nresources:\n  app_outdated: Kad šis išteklius veiktų tinkamai, reikia naujesnės \"Seelen UI\" versijos.\n  corrupted_wallpaper: >-\n    Nepavyko išskleisti miniatiūros – sugadintas arba nepalaikomas vaizdo įrašo\n    formatas\n  delete: Ištrinti šaltinį\n  discover: Atraskite daugiau išteklių\n  has_update: Galimas atnaujinimas\n  high_impact: Didelis poveikis našumui\n  import_wallpapers: Importuoti vietinius ekrano užsklandas\n  open_folder: Atidarykite išteklių aplanką\n  outdated: >-\n    Šis išteklius buvo sukurtas senesnei \"Seelen UI\" versijai ir gali neveikti\n    tinkamai.\n  see_on_website: Žiūrėti svetainėje\nreview_request:\n  not_now: Ne dabar\n  sure: Žinoma!\n  title: Mėgaukitės Seelen vartotojo sąsaja?\nsave: Sutaupyti\nsave_and_restart: Išsaugokite ir paleiskite iš naujo\nsearch: Ieškoti\nsee_more: Žiūrėti daugiau\nshortcuts:\n  duplicate_error: Šis spartusis klavišas pasikartoja\n  enable: Įgalinti įmontuotą nuorodų sistemą\n  enable_tooltip: Išjunkite, jei įdiegsite savo nuorodų sistemą naudodami „Seelen UI“ klientą\n  labels:\n    create_new_workspace: Sukurkite naują darbo vietą\n    cycle_stack_next: Ciklo kaminas Kitas\n    cycle_stack_prev: Ciklų kaminas ankstesnis\n    cycle_wallpaper_next: Pakeiskite į kitą tapetą\n    cycle_wallpaper_prev: Pakeisti ankstesnius tapetus\n    decrease_height: Sumažinti aukštį\n    decrease_width: Sumažinti plotį\n    destroy_current_workspace: Sunaikinkite dabartinę darbo vietą\n    focus_bottom: Fokusavimo dugnas\n    focus_latest: Sutelkite naujausią\n    focus_left: Fokusavimas kairėje\n    focus_right: Susitelkite teisingai\n    focus_top: Fokusavimo viršus\n    increase_height: Padidinti aukštį\n    increase_width: Padidinti plotį\n    misc_force_quit: Jėga mesti\n    misc_force_restart: Priverstinis paleidimas iš naujo\n    misc_open_settings: Atidaryti nustatymai\n    misc_toggle_lock_tracing: Perjunkite užrakto sekimą (žurnalai)\n    misc_toggle_win_event_tracing: Perjunkite „Win“ įvykių sekimą (žurnalai)\n    move_to_workspace: Pereikite į darbo vietą {{0}}\n    move_window_down: Perkelkite langą į apačią\n    move_window_left: Perkelkite langą į kairę\n    move_window_right: Perkelkite langą į dešinę\n    move_window_up: Perkelkite langą į viršų\n    pause_tiling: Pauzės plytelių langų tvarkyklė\n    reserve_bottom: Rezervo dugnas\n    reserve_float: Atsargos plūdė\n    reserve_left: Rezervas kairėje\n    reserve_right: Rezervuokite dešinę\n    reserve_stack: Rezervo kaminas\n    reserve_top: Rezervuokite viršų\n    restore_sizes: Atkurti dydžius\n    send_to_workspace: Siųsti į darbo vietą {{0}}\n    start_weg_app: Fokusuokite arba paleiskite programą {{0}}\n    switch_to_next_workspace: Perjunkite į kitą darbo vietą\n    switch_to_previous_workspace: Perjunkite į ankstesnę darbo vietą\n    switch_workspace: Perjunkite į darbo vietą {{0}}\n    toggle_float: Perjunkite lango plūdės režimą\n    toggle_monocle: Perjunkite darbo vietos monokle režimą\n  readonly_tooltip: Tai tik skaitomas nuoroda\n  reset: Iš naujo nustatykite pagal numatytuosius nustatymus\nsides:\n  bottom: Apačia\n  left: Kairėje\n  right: Teisingai\n  top: Viršus\ntoolbar:\n  auto_hide: Automatinis slėptuvė\n  delay_to_hide: Vėluoti paslėpti\n  delay_to_show: Delsimas parodyti\n  dock_side: Pozicija\n  enable: Įgalinkite išgalvotą įrankių juostą\n  hide_mode:\n    always: Visada\n    never: Niekada\n    on_overlap: Ant persidengimo\n  item_size: Prekės dydis\n  label: Įrankių juosta\n  margin: Maržos dydis\n  padding: Pamušalo dydis\n  placeholder: {}\nupdate:\n  available: Galima atnaujinti!\n  channel: Atnaujinkite kanalą\n  downloading: Atsisiuntimas ...\nwall:\n  backgrounds: Tapetai\n  blur: Neryškumas\n  cancel: Atšaukti\n  close: Uždaryti\n  collection_name: Kolekcijos pavadinimas\n  collections: Kolekcijos\n  contrast: Kontrastas\n  corrupted_wallpapers_message: 'Sugadinti fono paveikslėliai, apsvarstykite galimybę ištrinti šiuos:'\n  create: Sukurti\n  create_collection: Sukurti kolekciją\n  default_collection: Numatytoji kolekcija\n  delete_collection: Ištrinti kolekciją\n  edit_collection: Redaguoti kolekciją\n  enable: Įgalinkite tapetų tvarkytuvę\n  extend: Prailginti pirminį\n  fit:\n    contain: Sudėtis\n    cover: Viršelis\n    fill: Užpildykite\n  flipHorizontal: Apversti horizontaliai\n  flipVertical: Vertikalus apvertimas\n  generating_thumbnails: Fono paveikslėlių miniatiūrų generavimas\n  hours: valandos\n  interval: Keiskite tapetų kiekvieną\n  minutes: minutės\n  monitor_collection: Monitorių kolekcija\n  multimonitor_behaviour: Daugelio monitorių elgesys\n  muted: Nutildyti vaizdo garsą\n  no_background: Tuščias skaidrių demonstravimas, vietoje to, naudodamiesi temos fonu.\n  no_collections: Kol kas nesukurta jokių kolekcijų. Spustelėkite mygtuką +, kad sukurtumėte.\n  objectFit: Pagrindinė informacija Pritaikymas\n  objectPosition: Pagrindinė padėtis\n  overlayColor: Perdangos spalva\n  overlayMixBlendMode: Perdangos mišinio režimas\n  per_monitor: Vienam monitoriui\n  playback: Atkūrimo greitis\n  position:\n    bottom: Dugnas\n    center: Centras\n    left: Kairėje pusėje\n    right: Dešinė\n    top: Viršuje\n  processing_video: Apdorojamas vaizdo įrašas\n  random: Atsitiktinis skaidrių demonstravimas\n  saturation: Sodrumas\n  seconds: sekundės\n  select_collection: Pasirinkite Kolekcija\n  thumbnail_generation_complete: Miniatiūrų generavimas baigtas\n  thumbnail_generation_finished: Miniatiūrų generavimas sėkmingai baigtas\n  wallpaper_collection: Tapetų kolekcija\n  wallpaper_settings: Fono nustatymai\n  withOverlay: Su perdanga\n  workspace_collections: Darbo vietos kolekcijos\nweg:\n  auto_hide: Automatinis slėptuvė\n  delay_to_hide: Vėluoti paslėpti\n  delay_to_show: Delsimas parodyti\n  dock_side: Pozicija\n  enable: Įgalinti doką/užduočių juostą\n  filtering: Elementų filtravimas\n  gap: Tarpas\n  hide_mode:\n    always: Visada\n    never: Niekada\n    on_overlap: Ant persidengimo\n  items:\n    gap: Tarpas tarp daiktų\n    label: Daiktai\n    pinned_visibility:\n      always: Visada\n      label: Prisegtų elementų matomumas\n      when_primary: Kai monitorius yra pagrindinis\n    show_instance_counter: Rodyti atvirų langų skaitiklį\n    show_window_title: Rodyti atidaryto lango pavadinimą (tik horizontaliai)\n    size: Prekės dydis\n    split_windows: Padalinti langai (viename lange vienas elementas)\n    temporal_visibility:\n      all: Visi\n      label: Atsegtų elementų matomumas\n      on_monitor: Ant monitoriaus\n    visible_separators: Matomi separatoriai\n  label: „Dock“/„Taskbar“\n  margin: Paraštė\n  mode:\n    full_width: Visas ekrano plotis\n    min_content: Mažas kaip gali būti\n  padding: Paminkštinimas\n  show_end_task: Užduoties juostoje rodyti pabaigos užduotį\n  width: Plotis\nwelcome:\n  give_a_review: Pateikite apžvalgą\n  message: >-\n    Seelen UI yra nemokama atvirojo kodo darbalaukio aplinka, skirta Windows.\n    Čia galite visiškai valdyti, kaip atrodo ir veikia jūsų darbalaukis, todėl\n    galite tinkinti kiekvieną detalę, kad ji atitiktų jūsų darbo eigą ir stilių.\n  ok: Pradėkime!\n  review: >-\n    Jei jums patinka naudoti Seelen vartotojo sąsają, apsvarstykite galimybę\n    palikti atsiliepimą parduotuvėje – jūsų atsiliepimai padeda projektui augti\n    ir tobulėti.\n  title: Sveiki atvykę į Seelen vartotojo sąsają!\nwidget:\n  enable: Įjungti šį valdiklį\n  enable_for_monitor: Įjungti šiame monitoriuje\n  instances: Atvejai\nwm:\n  animations:\n    duration: Animacijos trukmė (MS)\n    ease_function: Animacijos palengvinimo funkcija\n    enable: Įgalinkite lango animacijas\n  author: Autorius\n  border:\n    enable: Įgalinti lango sieną\n    offset: Sienos poslinkis\n    width: Sienos plotis\n  description: apibūdinimas\n  drag_behavior: Vilkimo elgsena\n  drag_behavior_options:\n    sort: Rūšiuoti (pertvarkykite langus vilkdami)\n    swap: Sukeisti (keisti langus vilkimo pabaigoje)\n  enable: Įgalinkite plytelių langų tvarkyklę\n  layout: Išdėstymas\n  resize_delta: Pakeisti deltos dydį (%)\n  space_between_containers: Tarpas tarp konteinerių\n  workspace_offset: Darbo vietos poslinkis (paraštės)\n  workspace_padding: Darbo vietų pamušalas\n'yes': Taip\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/lv.yml",
    "content": "action:\n  confirm: Vai esat pārliecināts?\n  confirm_body: Šo darbību nevar atsaukt.\napps_configurations:\n  app:\n    bindings: Iesiešana (ņemiet vērā, ka ir vajadzīgas abas opcijas)\n    category: Kategorija\n    category_placeholder: Neviens\n    monitor: Kontrolēt\n    monitor_placeholder: Neviens\n    name: Nosaukt\n    ok_create: Radīt\n    ok_edit: Atjaunināt\n    ok_readonly: Rediģēt kā jaunu\n    options:\n      NoInteractive: Nav interaktīvu\n      VdPinned: Rādīt visās darbvietās\n      WmFloat: Twm — Sākt peldēt\n      WmForce: \"Twm\\_— piespiedu pārvaldība\"\n      WmUnmanage: \"Twm\\_— nepārvaldīt\"\n    options_label: Papildu iespējas\n    title_create: Izveidot {{name}}\n    title_edit: Rediģēšana {{name}}\n    title_readonly: Skatoties {{name}}\n    weg_options_label: Doka/uzdevumjoslas iespējas\n    wm_options_label: Logu pārvaldnieka opcijas\n    workspace: Darba vieta\n    workspace_placeholder: Neviens\n  bundled_msg: >-\n    Šīs komplektētās konfigurācijas nav rediģējamas un ir paredzētas, lai\n    sniegtu jums vislabāko pieredzi bez pielāgošanas. Tie automātiski konfigurē\n    jums visizplatītākās lietojumprogrammas.\n  bundled_title: Lietotnes konfigurācija, kas komplektēta ar Seelen\n  confirm_delete: Vai esat pārliecināts, ka vēlaties izdzēst šo konfigurāciju/-as?\n  confirm_delete_title: Apstipriniet izdzēst\n  delete: Dzēst\n  export: Eksportēt\n  export_full: Eksportēt iestatījumus pēc lietojumprogrammas\n  extra_info: >-\n    Seelen lietotāja saskarnē katrai lietotnei tiek izmantots tikai viens\n    identifikators (atrasta pirmā atbilstība), tāpēc ir svarīga secība, kādā tie\n    tiek norādīti, un jaunākajam pievienotajam tiks piešķirta prioritāte, jo\n    ņemiet vērā, ka tabula pēc noklusējuma tiek kārtota no jaunākā uz veco.\n  identifier:\n    add_block: Pievienot bloku\n    and: UN\n    id: Identifikators\n    kind: Identificēt ar\n    matching_strategy: Atbilstības stratēģija\n    matching_strategy_option:\n      contains: Satur\n      ends_with: Beidzas ar\n      equals: Vienāds\n      regex: Regulārā izteiksme\n      starts_with: Sākas ar\n    negation: Negatīva atbilstība\n    or: Vai\n    remove: Dzēst bloku\n    type:\n      class: Klase\n      exe: Exe\n      path: Ceļš\n      title: Nosaukums\n  import: Importēt\n  import_full: Iestatījumu importēšana pēc lietojumprogrammas\n  new: Jauns\n  search: Meklēt\n  swap: Apmaiņa\ncancel: Atcelt\nclose: Tuvs\ndelete: Dzēst\ndevtools:\n  app_folders: Lietotņu mapes\n  custom_config_file: Ielādēt pielāgotu konfigurācijas failu\n  data_folder: Datu mape\n  enable: Iespējot izstrādātāju rīkus\n  install_folder: Instalēšanas mape\n  load: Slodze\n  settings_file: Iestatīšanas fails\n  simulate_perm:\n    label: Simulēt logrīka atļaujas pieprasījumu\n    result_allowed: ✓ Atļauja piešķirta\n    result_denied: ✗ Atļauja liegta\n    trigger: Simulēt\n    widget_id_placeholder: '@autors/logrīka nosaukums'\nextras:\n  clear_icons: Iztīrīt sistēmas ikonu kešatmiņu\n  clear_icons_tooltip: >-\n    Varētu būt nepieciešams restartēt, lai tas pilnībā stātos spēkā visos\n    logrīkos.\n  exit: Atmest/iziet\n  links: Oficiālas saites\n  relaunch: Atsākšana\n  version: Versija\n  version_fixed: >-\n    Lietojumprogramma un WebView2 Runtime versijas ir fiksētas. Tas nozīmē, ka\n    lietojumprogramma nesaņems atjauninājumus un WebView2 Runtime netiks\n    automātiski atjaunināts ar Windows atjauninājumiem.\ngeneral:\n  accent_color: Akcents\n  date_format: Datuma formāts\n  date_format_how_to: Kā uzrakstīt datuma formātu?\n  hardware_acceleration: Aparatūras paātrinājums\n  hardware_acceleration_description: >-\n    Aparatūras paātrinājuma atspējošana samazinās atmiņas lietojumu, taču var\n    izraisīt veiktspējas problēmas. To ir droši atspējot, ja neizmantojat dzīvās\n    tapetes.\n  icon_pack:\n    available: Pieejamie ikonu komplekti\n    selected: Aktīvās ikonu pakotnes\n  language: Valoda\n  monday: pirmdiena\n  performance_mode:\n    on_battery: Uz akumulatora\n    on_energy_saver: Par enerģijas taupīšanu\n    options:\n      disabled: Nespējīgs\n      extreme: Galējs\n      minimal: Minimāls\n    plugged: Pievienots vai uzlādēts\n  polling_interval: Sistēmas aptaujas intervāls\n  polling_interval_description: >-\n    Cik bieži (sekundēs) Seelen UI pārbauda sistēmas resursus, piemēram, CPU,\n    RAM, tīkla un diska darbību. Mazāks skaitlis nozīmē biežākus atjauninājumus,\n    bet nedaudz lielāku resursu izmantošanu.\n  saturday: sestdiena\n  start_of_week: Nedēļas sākums\n  startup: Skriet startup?\n  sunday: svētdiena\n  theme:\n    available: Pieejamie motīvi\n    selected: Aktīvās tēmas\nheader:\n  labels:\n    config: Konfigurācijas\n    developer: Izstrādātājiem\n    extras: Papildaprīkojums\n    general: Ģenerāldirektors\n    home: Mājas\n    icon_pack_editor: Ikonas kešatmiņā\n    iconpack: Ikonu paketes\n    monitors: Monitori\n    plugin: Spraudņi\n    resources: Resursi\n    shortcuts: Saīsnes\n    soundpack: Skaņu paketes\n    specific_apps: Iestatījumi pēc lietojumprogrammas\n    theme: Tēmas\n    virtual_desk: Virtuālie darbvirsmas\n    wallpaper: Tapetes\n    widget: Logrīki\nhome:\n  new_resources: Jauni resursi\ninherit: Mantot\ninProgress: Notiek ...\ninsert: Ievietot\nloading: Iekraušana ...\nmiscellaneous: Dažāds\nmonitors_configurations:\n  label: Monitors {{indekss}}\nmore: Vairāk\n'no': Ne\nopen: Atvērt\nquit: Atmest\nremove: Noņemt\nreset_all_to_default: Visu noklusējuma vērtību atiestatīšana\nreset_to_default: Atiestatīt noklusējuma vērtību\nresources:\n  app_outdated: Lai šis resurss darbotos pareizi, nepieciešama jaunāka Seelen UI versija.\n  corrupted_wallpaper: \"Neizdevās izvilkt sīktēlu\\_— bojāts vai neatbalstīts video formāts\"\n  delete: Dzēst resursu\n  discover: Atklājiet vairāk resursu\n  has_update: Ir pieejams atjauninājums\n  high_impact: Liela ietekme uz veiktspēju\n  import_wallpapers: Importēt vietējās tapetes\n  open_folder: Atvērt resursu mapi\n  outdated: >-\n    Šis resurss ir izstrādāts vecākai Seelen UI versijai un var nedarboties\n    pareizi.\n  see_on_website: Skatīt vietnē\nreview_request:\n  not_now: Ne tagad\n  sure: Protams!\n  title: Vai jums patīk Seelen UI?\nsave: Ietaupīt\nsave_and_restart: Saglabāt un restartēt\nsearch: Meklēt\nsee_more: Skatīt vairāk\nshortcuts:\n  duplicate_error: Šī saīsne ir dublēta\n  enable: Iespējot iebūvēto saīsņu sistēmu\n  enable_tooltip: >-\n    Atspējot, ja jūs ieviesīsit savu saīsņu sistēmu, izmantojot Seelen UI\n    klientu\n  labels:\n    create_new_workspace: Izveidojiet jaunu darbvietu\n    cycle_stack_next: Nākamais cikla kaudze\n    cycle_stack_prev: Velosipēdu kaudze iepriekšējā\n    cycle_wallpaper_next: Mainiet uz nākamo fona attēlu\n    cycle_wallpaper_prev: Mainiet uz iepriekšējo fona attēlu\n    decrease_height: Samazināt augstumu\n    decrease_width: Samazināt platumu\n    destroy_current_workspace: Iznīcināt pašreizējo darbvietu\n    focus_bottom: Fokusēt apakšā\n    focus_latest: Fokuss jaunākais\n    focus_left: Fokusēt pa kreisi\n    focus_right: Fokusēt pa labi\n    focus_top: Fokusa tops\n    increase_height: Palielināt augstumu\n    increase_width: Palielināt platumu\n    misc_force_quit: Spēks atmest\n    misc_force_restart: Piespiedu restartēšana\n    misc_open_settings: Atvērti iestatījumi\n    misc_toggle_lock_tracing: Pārslēdziet slēdzenes izsekošanu (baļķi)\n    misc_toggle_win_event_tracing: Pārslēdziet uzvaru notikumu izsekošanu (žurnāli)\n    move_to_workspace: Pārvietojieties uz darbvietu {{0}}\n    move_window_down: Pārvietojiet logu uz leju\n    move_window_left: Pārvietojiet logu pa kreisi\n    move_window_right: Pārvietojiet logu uz labo pusi\n    move_window_up: Pārvietojiet logu uz augšu\n    pause_tiling: Pauzēt flīzes logu pārvaldnieku\n    reserve_bottom: Rezervēt apakšdaļu\n    reserve_float: Rezerves pludiņš\n    reserve_left: Rezervēt pa kreisi\n    reserve_right: Rezervēt pa labi\n    reserve_stack: Rezerves kaudze\n    reserve_top: Rezerves tops\n    restore_sizes: Atjaunot izmērus\n    send_to_workspace: Nosūtīt uz darbvietu {{0}}\n    start_weg_app: Fokusēt vai sākt lietojumprogrammu {{0}}\n    switch_to_next_workspace: Pārslēdzieties uz nākamo darbvietu\n    switch_to_previous_workspace: Pārslēdzieties uz iepriekšējo darbvietu\n    switch_workspace: Pārslēdzieties uz darbvietu {{0}}\n    toggle_float: Pārslēdziet loga pludiņa režīmu\n    toggle_monocle: Pārslēdziet darbvietas monokla režīmu\n  readonly_tooltip: Tas ir tikai lasāms saīsne\n  reset: Atiestatīt uz noklusējuma\nsides:\n  bottom: Dibens\n  left: Atstāts\n  right: Pa labi\n  top: Tops\ntoolbar:\n  auto_hide: Auto paslēpt\n  delay_to_hide: Aizkavē paslēpties\n  delay_to_show: Kavēšanās parādīt\n  dock_side: Pozīcija\n  enable: Iespējot izdomātu rīkjoslu\n  hide_mode:\n    always: Vienmēr\n    never: Nekad\n    on_overlap: Uz pārklāšanās\n  item_size: Preces izmērs\n  label: Rīkjosla\n  margin: Piemales lielums\n  padding: Polsterējuma izmērs\n  placeholder: {}\nupdate:\n  available: Pieejams atjauninājums!\n  channel: Atjaunināt kanālu\n  downloading: Lejupielāde ...\nwall:\n  backgrounds: Tapetes\n  blur: Blur\n  cancel: Atcelt\n  close: Aizvērt\n  collection_name: Kolekcijas nosaukums\n  collections: Kolekcijas\n  contrast: Kontrasts\n  corrupted_wallpapers_message: 'Bojātas tapetes, apsveriet iespēju dzēst šīs tapetes:'\n  create: Izveidot\n  create_collection: Izveidot kolekciju\n  default_collection: Noklusējuma kolekcija\n  delete_collection: Dzēst kolekciju\n  edit_collection: Rediģēt kolekciju\n  enable: Iespējot fona attēlu pārvaldnieku\n  extend: Pagarināt primāro\n  fit:\n    contain: Satur\n    cover: Vāks\n    fill: Aizpildiet\n  flipHorizontal: Flip horizontāli\n  flipVertical: Flip vertikālais\n  generating_thumbnails: Tapešu sīktēlu ģenerēšana\n  hours: laiks\n  interval: Mainiet fona attēlus katru\n  minutes: protokols\n  monitor_collection: Monitoru kolekcija\n  multimonitor_behaviour: Vairāku monitoru uzvedība\n  muted: Izslēgt video audio\n  no_background: Tukša slaidrāde, tā vietā izmantojot tēmas fonu.\n  no_collections: >-\n    Vēl nav izveidota neviena kolekcija. Noklikšķiniet uz pogas +, lai to\n    izveidotu.\n  objectFit: Fona fona atbilstība\n  objectPosition: Pamatinformācija Pozīcija\n  overlayColor: Pārklājuma krāsa\n  overlayMixBlendMode: Pārklājuma sajaukšanas režīms\n  per_monitor: Uz monitoru\n  playback: Atskaņošanas ātrums\n  position:\n    bottom: Apakšējā daļa\n    center: Centrs\n    left: Kreisā\n    right: Tiesības\n    top: Top\n  processing_video: Notiek video apstrāde\n  random: Randomizējiet slaidrādi\n  saturation: Piesātinājums\n  seconds: sekundes\n  select_collection: Atlasiet Kolekcija\n  thumbnail_generation_complete: Sīktēlu ģenerēšana ir pabeigta\n  thumbnail_generation_finished: Sīktēlu ģenerēšana ir veiksmīgi pabeigta\n  wallpaper_collection: Tapešu kolekcija\n  wallpaper_settings: Tapetes iestatījumi\n  withOverlay: Ar pārklājumu\n  workspace_collections: Darbvietu kolekcijas\nweg:\n  auto_hide: Auto paslēpt\n  delay_to_hide: Aizkavē paslēpties\n  delay_to_show: Kavēšanās parādīt\n  dock_side: Pozīcija\n  enable: Iespējot doku/uzdevumjoslu\n  filtering: Vienumu filtrēšana\n  gap: Plaisa\n  hide_mode:\n    always: Vienmēr\n    never: Nekad\n    on_overlap: Uz pārklāšanās\n  items:\n    gap: Vieta starp priekšmetiem\n    label: Priekšmeti\n    pinned_visibility:\n      always: Vienmēr\n      label: Piesprausto vienumu redzamība\n      when_primary: Kad monitors ir primārais\n    show_instance_counter: Rādīt atvērto logu skaitītāju\n    show_window_title: Rādīt atvērta loga virsrakstu (tikai horizontāli)\n    size: Vienuma izmērs\n    split_windows: Sadalīti logi (viens vienums katrā logā)\n    temporal_visibility:\n      all: Visi\n      label: Atsprausto vienumu redzamība\n      on_monitor: Uz monitora\n    visible_separators: Redzami atdalītāji\n  label: Doks/uzdevumjosla\n  margin: Robeža\n  mode:\n    full_width: Pilna ekrāna platums\n    min_content: Cik mazs var būt\n  padding: Polsterējums\n  show_end_task: Parādīt beigu uzdevumu uzdevumjoslā\n  width: Platums\nwelcome:\n  give_a_review: Sniedziet atsauksmi\n  message: >-\n    Seelen UI ir bezmaksas atvērtā koda darbvirsmas vide operētājsistēmai\n    Windows. Šeit jums ir pilnīga kontrole pār jūsu darbvirsmas izskatu un\n    darbību, ļaujot pielāgot katru detaļu, lai tā atbilstu jūsu darbplūsmai un\n    stilam.\n  ok: Sāksim!\n  review: \"Ja jums patīk izmantot Seelen UI, apsveriet iespēju atstāt atsauksmi par veikalu\\_— jūsu atsauksmes palīdz projektam augt un uzlaboties.\"\n  title: Laipni lūdzam Seelen lietotāja saskarnē!\nwidget:\n  enable: Ieslēgt šo logrīku\n  enable_for_monitor: Ieslēgt šajā monitorā\n  instances: Instances\nwm:\n  animations:\n    duration: Animācijas ilgums (MS)\n    ease_function: Animācijas atvieglošanas funkcija\n    enable: Iespējot loga animācijas\n  author: Autors\n  border:\n    enable: Iespējot loga robežu\n    offset: Robežas kompensācija\n    width: Apmales platums\n  description: Apraksts\n  drag_behavior: Vilkšanas uzvedība\n  drag_behavior_options:\n    sort: Kārtot (pārkārtot logus, velkot)\n    swap: Apmainīt (apmainīt logus vilkšanas beigās)\n  enable: Iespējot flīzēšanas logu pārvaldnieku\n  layout: Izkārtojums\n  resize_delta: Delta izmēra maiņa (%)\n  space_between_containers: Telpa starp konteineriem\n  workspace_offset: Darbvietu kompensācija (malas)\n  workspace_padding: Darbvietas polsterējums\n'yes': Jā\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/mk.yml",
    "content": "action:\n  confirm: Дали сте сигурни?\n  confirm_body: Оваа акција не може да се разоткрие.\napps_configurations:\n  app:\n    bindings: Обврзувачки (Забележете дека се потребни и двете опции)\n    category: Категорија\n    category_placeholder: Ништо\n    monitor: Монитор\n    monitor_placeholder: Ништо\n    name: Име\n    ok_create: Креирај\n    ok_edit: Ажурирање\n    ok_readonly: Уреди како ново\n    options:\n      NoInteractive: Нема интерактивна\n      VdPinned: Прикажи во сите работни простори\n      WmFloat: Twm - Почнете да лебдите\n      WmForce: Twm - Управување со сила\n      WmUnmanage: Twm - Не управувај\n    options_label: Дополнителни опции\n    title_create: Креирање {{име}}\n    title_edit: Уредување {{име}}\n    title_readonly: Гледање {{име}}\n    weg_options_label: Опции на пристаништето/лентата со задачи\n    wm_options_label: Опции за управување со прозорецот\n    workspace: Работен простор\n    workspace_placeholder: Ништо\n  bundled_msg: >-\n    Овие конфигурации во комплет не се уредни и се дизајнирани да ви обезбедат\n    најдобро искуство без прилагодување. Тие автоматски ги конфигурираат\n    најчестите апликации за вас.\n  bundled_title: Конфигурирана апликација во комплет со Селен\n  confirm_delete: Дали сте сигурни дека сакате да ја избришете оваа конфигурација/а?\n  confirm_delete_title: Потврдете го Избриши\n  delete: Избриши\n  export: Извоз\n  export_full: Извезувајте ги поставките по апликација\n  extra_info: >-\n    UI UI Користете само еден идентификатор по апликација (пронајден прв\n    натпревар), така што нарачката за тоа е важна е важна, најновиот додаден ќе\n    биде приоритет, бидејќи белешката табелата е сортирана стандардно од\n    најновата до стара.\n  identifier:\n    add_block: Додадете блок\n    and: И\n    id: Идентификатор\n    kind: Идентификувајте од\n    matching_strategy: Стратегија за појавување\n    matching_strategy_option:\n      contains: Содржи\n      ends_with: Завршува со\n      equals: Еднакви\n      regex: Редовно изразување\n      starts_with: Започнува со\n    negation: Негираат совпаѓање\n    or: Или\n    remove: Избриши блок\n    type:\n      class: Класа\n      exe: Exe\n      path: Пат\n      title: Наслов\n  import: Увоз\n  import_full: Увезете ги поставките по апликација\n  new: Ново\n  search: Пребарување\n  swap: Разменување\ncancel: Откажи\nclose: Затвори\ndelete: Избриши\ndevtools:\n  app_folders: Папки со апликации\n  custom_config_file: Вчитајте ја датотеката за прилагодена конфигурација\n  data_folder: Папка со податоци\n  enable: Овозможете алатки за развивачи\n  install_folder: Папка за инсталација\n  load: Оптоварување\n  settings_file: Датотека за поставки\n  simulate_perm:\n    label: Симулирајте барање за дозвола за виџети\n    result_allowed: ✓ Дадена е дозвола\n    result_denied: ✗ Дозволата е одбиена\n    trigger: Симулирајте\n    widget_id_placeholder: '@author/виџет-име'\nextras:\n  clear_icons: Јасна кеш за икони на системот\n  clear_icons_tooltip: >-\n    Може да се бара рестартирање за целосно да се стапат на сила на сите\n    додатоци\n  exit: Откажете/излез\n  links: Официјални врски\n  relaunch: Повторно започнување\n  version: Верзија\n  version_fixed: >-\n    Апликацијата и верзиите WebView2 Runtime се поправени. Ова значи дека\n    апликацијата нема да прима ажурирања и WebView2 Runtime нема автоматски да\n    се ажурира со ажурирања на Windows.\ngeneral:\n  accent_color: Боја на акцент\n  date_format: Формат на датум\n  date_format_how_to: Како да напишете формат на датум?\n  hardware_acceleration: Хардверско забрзување\n  hardware_acceleration_description: >-\n    Оневозможувањето на хардверското забрзување ќе ја намали употребата на\n    меморијата, но може да предизвика проблеми со перформансите. Безбедно е да\n    се оневозможи ако не користите живи позадини.\n  icon_pack:\n    available: Достапни пакети со икони\n    selected: Пакети со активни икони\n  language: Јазик\n  monday: понеделник\n  performance_mode:\n    on_battery: На батеријата\n    on_energy_saver: На заштеда на енергија\n    options:\n      disabled: Оневозможено\n      extreme: Екстремни\n      minimal: Минимално\n    plugged: Вклучен или полнење\n  polling_interval: Интервал на системски анкети\n  polling_interval_description: >-\n    Колку често (во секунди) Seelen UI ги проверува системските ресурси како\n    процесорот, RAM меморијата, мрежата и активноста на дискот. Помал број значи\n    почести ажурирања, но малку поголема употреба на ресурси.\n  saturday: сабота\n  start_of_week: Почеток на неделата\n  startup: Стартувам на стартување?\n  sunday: недела\n  theme:\n    available: Достапни теми\n    selected: Активни теми\nheader:\n  labels:\n    config: Конфигурации\n    developer: За развивачи\n    extras: Додатоци\n    general: Општо\n    home: Дома\n    icon_pack_editor: Зачувани икони\n    iconpack: Икони пакувања\n    monitors: Монитори\n    plugin: Приклучоци\n    resources: Ресурси\n    shortcuts: Кратенки\n    soundpack: Звучни пакувања\n    specific_apps: Поставки по апликација\n    theme: Теми\n    virtual_desk: Виртуелни работна површина\n    wallpaper: Позадини\n    widget: Додатоци\nhome:\n  new_resources: Нови ресурси\ninherit: Наследник\ninProgress: Во тек...\ninsert: Вметнете\nloading: Вчитување ...\nmiscellaneous: Различни\nmonitors_configurations:\n  label: Монитор {{индекс}}\nmore: Повеќе\n'no': Не\nopen: Отворено\nquit: Откажете се\nremove: Отстранете\nreset_all_to_default: Ресетирајте ги сите на стандардните вредности\nreset_to_default: Ресетирање на стандардна вредност\nresources:\n  app_outdated: Овој ресурс бара понова верзија на Seelen UI да работи правилно.\n  corrupted_wallpaper: Не успеа да се извлече сликичка - оштетен или неподдржан видео формат\n  delete: Избришете ресурс\n  discover: Откријте повеќе ресурси\n  has_update: Достапно е ажурирање\n  high_impact: Високо влијание врз перформансите\n  import_wallpapers: Увезете локални позадини\n  open_folder: Отворена папка со ресурси\n  outdated: >-\n    Овој ресурс беше дизајниран за постара верзија на Seelen UI и можеби нема да\n    работи правилно.\n  see_on_website: Видете на веб-страницата\nreview_request:\n  not_now: Не сега\n  sure: Секако!\n  title: Уживате во Seelen UI?\nsave: Зачувај\nsave_and_restart: Зачувај и рестартирај\nsearch: Пребарување\nsee_more: Погледнете повеќе\nshortcuts:\n  duplicate_error: Оваа кратенка е дупликат\n  enable: Овозможете вграден систем за кратенки\n  enable_tooltip: >-\n    Оневозможете ако го имплементирате вашиот сопствен систем на кратенки со\n    помош на клиентот Seelen UI\n  labels:\n    create_new_workspace: Создадете нов работен простор\n    cycle_stack_next: Оџак на циклус следно\n    cycle_stack_prev: Магаци за циклус Претходно\n    cycle_wallpaper_next: Промена на следната позадина\n    cycle_wallpaper_prev: Промена во претходната позадина\n    decrease_height: Намалување на висината\n    decrease_width: Намалување на ширината\n    destroy_current_workspace: Уништи сегашниот работен простор\n    focus_bottom: Фокус дното\n    focus_latest: Фокусирајте се најновите\n    focus_left: Фокусот лево\n    focus_right: Фокусирајте се десно\n    focus_top: Фокус врвот\n    increase_height: Зголемете ја висината\n    increase_width: Зголемете ја ширината\n    misc_force_quit: Сила откажана\n    misc_force_restart: Рестартирање на силата\n    misc_open_settings: Отворете ги поставките\n    misc_toggle_lock_tracing: Следење на заклучување на заклучување (логови)\n    misc_toggle_win_event_tracing: Вклучување\n    move_to_workspace: Преместете се на работниот простор {{0}}\n    move_window_down: Поместете го прозорецот до дното\n    move_window_left: Поместете го прозорецот кон лево\n    move_window_right: Поместете го прозорецот надесно\n    move_window_up: Поместете го прозорецот до врвот\n    pause_tiling: Пауза менаџер на прозорецот со плочки\n    reserve_bottom: Резервно дно\n    reserve_float: Резерва плови\n    reserve_left: Резерва лево\n    reserve_right: Резервирајте десно\n    reserve_stack: Резервен магацин\n    reserve_top: Резервен врв\n    restore_sizes: Врати големини\n    send_to_workspace: Испрати на работниот простор {{0}}\n    start_weg_app: Фокус или започнете апликација {{0}\n    switch_to_next_workspace: Префрлете се на следниот работен простор\n    switch_to_previous_workspace: Префрлете се на претходниот работен простор\n    switch_workspace: Префрлете се на работниот простор {{0}}\n    toggle_float: Вклучување на режимот на плови на прозорецот\n    toggle_monocle: Вклучете го режимот на монокет на работниот простор\n  readonly_tooltip: Ова е кратенка само за читање\n  reset: Ресетирање на стандардно\nsides:\n  bottom: Дно\n  left: Лево\n  right: Десно\n  top: Врв\ntoolbar:\n  auto_hide: Автоматско скриј\n  delay_to_hide: Одложување за сокривање\n  delay_to_show: Одложување за прикажување\n  dock_side: Позиција\n  enable: Овозможете фенси лента со алатки\n  hide_mode:\n    always: Секогаш\n    never: Никогаш\n    on_overlap: На преклопување\n  item_size: Големина на ставка\n  label: Лента со алатки\n  margin: Големина на маргина\n  padding: Големина на баласт\n  placeholder: {}\nupdate:\n  available: Ажурирање достапно!\n  channel: Ажурирање на каналот\n  downloading: Преземање ...\nwall:\n  backgrounds: Позадини\n  blur: Замаглување\n  cancel: Откажи\n  close: Затвори\n  collection_name: Име на колекцијата\n  collections: Колекции\n  contrast: Контраст\n  corrupted_wallpapers_message: 'Оштетени позадини, размислете да ги избришете овие:'\n  create: Креирај\n  create_collection: Креирај колекција\n  default_collection: Стандардна колекција\n  delete_collection: Избришете ја колекцијата\n  edit_collection: Уреди колекција\n  enable: Овозможете менаџер на тапети\n  extend: Прошири основно\n  fit:\n    contain: Содржат\n    cover: Покритие\n    fill: Пополнете\n  flipHorizontal: Флип хоризонтално\n  flipVertical: Флип вертикална\n  generating_thumbnails: Создавање сликички за позадина\n  hours: часови\n  interval: Променете ја позадината на секој\n  minutes: минути\n  monitor_collection: Колекција на монитори\n  multimonitor_behaviour: Однесување на мултимонитор\n  muted: Исклучете го звукот на видеото\n  no_background: Празен слајдшоу, наместо тоа, користејќи ја позадината на темата.\n  no_collections: Сè уште нема создадени колекции. Кликнете на копчето + за да креирате едно.\n  objectFit: Подготвување на позадината\n  objectPosition: Позиција во позадина\n  overlayColor: Боја на преклопување\n  overlayMixBlendMode: Режим на мешавина од мешавина од преклопување\n  per_monitor: По монитор\n  playback: Брзина на репродукција\n  position:\n    bottom: Дно\n    center: Центар\n    left: Лево\n    right: Десно\n    top: Горе\n  processing_video: Се обработува видео\n  random: Рандомизирајте го слајдшоу\n  saturation: Заситеност\n  seconds: секунди\n  select_collection: Изберете Колекција\n  thumbnail_generation_complete: Генерирањето сликички е завршено\n  thumbnail_generation_finished: Генерирањето сликички заврши успешно\n  wallpaper_collection: Колекција на тапети\n  wallpaper_settings: Поставки за позадина\n  withOverlay: Со преклоп\n  workspace_collections: Колекции на работниот простор\nweg:\n  auto_hide: Автоматско скриј\n  delay_to_hide: Одложување за сокривање\n  delay_to_show: Одложување за прикажување\n  dock_side: Позиција\n  enable: Овозможете пристаниште/лента со задачи\n  filtering: Филтрирање на артикли\n  gap: Јаз\n  hide_mode:\n    always: Секогаш\n    never: Никогаш\n    on_overlap: На преклопување\n  items:\n    gap: Простор помеѓу предмети\n    label: Предмети\n    pinned_visibility:\n      always: Секогаш\n      label: Видливост на закачени ставки\n      when_primary: Кога мониторот е примарен\n    show_instance_counter: Покажете бројач на отворени прозорци\n    show_window_title: Покажете го насловот на отворен прозорец (само хоризонтално)\n    size: Големина на артикалот\n    split_windows: Сплит Windows (една ставка по прозорец)\n    temporal_visibility:\n      all: Сите\n      label: Видливост на откачени ставки\n      on_monitor: На монитор\n    visible_separators: Видливи сепаратори\n  label: Док/лента со задачи\n  margin: Маргина\n  mode:\n    full_width: Ширина на цел екран\n    min_content: Мала колку што може\n  padding: Подлога\n  show_end_task: Покажете задача за крај во лентата со задачи\n  width: Ширина\nwelcome:\n  give_a_review: Дајте преглед\n  message: >-\n    Seelen UI е бесплатна и работна околина со отворен код за Windows. Овде\n    имате целосна контрола врз тоа како изгледа и се однесува вашиот десктоп,\n    што ви овозможува да го приспособите секој детал за да одговара на вашиот\n    работен тек и стил.\n  ok: Да почнеме!\n  review: >-\n    Ако уживате во користењето на Seelen UI, размислете да оставите преглед во\n    продавницата - вашите повратни информации помагаат проектот да расте и да се\n    подобри.\n  title: Добредојдовте во Seelen UI!\nwidget:\n  enable: Овозможете го овој додаток\n  enable_for_monitor: Овозможете на овој монитор\n  instances: Примери\nwm:\n  animations:\n    duration: Времетраење на анимација (МС)\n    ease_function: Функција за олеснување на анимацијата\n    enable: Овозможете ги анимациите на прозорецот\n  author: Автор\n  border:\n    enable: Овозможете ја границата на прозорецот\n    offset: Офсет за граница\n    width: Ширина на границата\n  description: Опис\n  drag_behavior: Повлечете однесување\n  drag_behavior_options:\n    sort: Подреди (прередете ги прозорците додека се влечете)\n    swap: Заменете (заменете ги прозорците на крајот на влечење)\n  enable: Овозможете менаџер на прозорецот за плочки\n  layout: Распоред\n  resize_delta: Променете ја големината на Делта (%)\n  space_between_containers: Простор помеѓу контејнери\n  workspace_offset: Работни места за неутрализирање (маргини)\n  workspace_padding: Подлога за работни места\n'yes': Да\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/mn.yml",
    "content": "action:\n  confirm: Та итгэлтэй байна уу?\n  confirm_body: Энэ үйлдлийг буцаах боломжгүй.\napps_configurations:\n  app:\n    bindings: Батлах (Анхааруулга хоёулаа сонголтыг оруулах шаардлагатай)\n    category: Зэрэглэл\n    category_placeholder: Хэн ч\n    monitor: Хянах\n    monitor_placeholder: Хэн ч\n    name: Нэр\n    ok_create: Шуүгиан дэгдээх\n    ok_edit: Шинэчлэх\n    ok_readonly: Шинэ болгон засах\n    options:\n      NoInteractive: Интерактив байхгүй\n      VdPinned: Бүх ажлын талбарт үзүүлэх\n      WmFloat: Twm - хөвөх эхлэл\n      WmForce: Twm - хүчээр удирдах\n      WmUnmanage: Twm - Нэхэвч\n    options_label: Нэмий сонголт\n    title_create: Үүсгэх {{нэр}}}\n    title_edit: Засварлах {{нэр}}}\n    title_readonly: Үзэх {{нэр}}\n    weg_options_label: Док / Taskbar сонголтууд\n    wm_options_label: Цонх менежерийн сонголтууд\n    workspace: Ауун тал\n    workspace_placeholder: Хэн ч\n  bundled_msg: >-\n    Эдгээр багцын тохиргоо нь засварлах боломжгүй бөгөөд танд тохируулга\n    хийхгүйгээр хамгийн сайн туршлага өгөх зорилготой юм. Тэд таны хувьд хамгийн\n    түгээмэл програмуудыг автоматаар тохируулдаг.\n  bundled_title: App Config Selen-тай багцлав\n  confirm_delete: Та энэ тохиргоог устгахыг хүсч байна уу?\n  confirm_delete_title: Устгахыг баталгаажуулна уу\n  delete: Эдгээх\n  export: Гадаадад гаргах\n  export_full: Програмаар экспортлох тохиргоо\n  extra_info: >-\n    Seelen ui нь App тутамд зөвхөн нэг танигчийг ашигладаг (эхний тоглолт\n    олдсон) нь хамгийн чухал нь хамгийн чухал бөгөөд хүснэгтийг хуучин\n    хувилбараас эхлээд эрэмбэлсэн байдлаар эрэмбэлэгдсэн байх ёстой.\n  identifier:\n    add_block: Нид нэмнэ\n    and: Ба\n    id: Өмнө тайлбар\n    kind: Тодорхойлох\n    matching_strategy: Тохирох стратеги\n    matching_strategy_option:\n      contains: агуулсан\n      ends_with: '-ээр төгсдөг'\n      equals: Тэнцүү\n      regex: Тогтмол илэрхийлэл\n      starts_with: '-ээр эхэлдэг'\n    negation: Тохирохыг үгүйсгэх\n    or: ЭСВЭЛ\n    remove: Блок устгах\n    type:\n      class: Анги\n      exe: Exe\n      path: Зам\n      title: Гарчиг\n  import: Импортлох\n  import_full: Програмаар тохиргоог импортлох\n  new: Шинэ\n  search: Эрэл хайгуул хийх\n  swap: Түлхэх\ncancel: Цуаах\nclose: Ойр дөхөм\ndelete: Эдгээх\ndevtools:\n  app_folders: Апп хавтас\n  custom_config_file: Custom Compigh файлыг ачаалах\n  data_folder: Д код хавтас\n  enable: Хөгжүүлэгч хэрэгслийг идэвхжүүлэх\n  install_folder: Суулгалах хүрээ\n  load: Ачих\n  settings_file: Тохиргооын нэр\n  simulate_perm:\n    label: Виджетийн зөвшөөрлийн хүсэлтийг дуурайлган хийх\n    result_allowed: ✓ Зөвшөөрөл олгосон\n    result_denied: ✗ Зөвшөөрөл татгалзсан\n    trigger: Дуурайх\n    widget_id_placeholder: '@зохиогч/виджетийн нэр'\nextras:\n  clear_icons: Системийн дүрсийг цэвэрлэх\n  clear_icons_tooltip: >-\n    Бүх виджет дээр хүчин төгөлдөр болохын тулд дахин эхлүүлэх шаардлагатай байж\n    болно\n  exit: Гарах / гарах\n  links: Албан ёсны холбоос\n  relaunch: OradAUN\n  version: Таамаглал\n  version_fixed: >-\n    Аппликешн болон WebView2 Runtime хувилбаруудыг зассан. Энэ нь програм нь\n    шинэчлэлт хүлээн авахгүй бөгөөд WebView2 Runtime нь Windows шинэчлэлтүүдээр\n    автоматаар шинэчлэгдэхгүй гэсэн үг юм.\ngeneral:\n  accent_color: Эргүүлэг\n  date_format: Огноо формат\n  date_format_how_to: Огнооны форматыг хэрхэн бичих вэ?\n  hardware_acceleration: Техник хангамжийн хурдатгал\n  hardware_acceleration_description: >-\n    Техник хангамжийн хурдатгалыг идэвхгүй болгох нь санах ойн хэрэглээг\n    багасгах боловч гүйцэтгэлийн асуудал үүсгэж болзошгүй. Хэрэв та амьд ханын\n    зураг ашиглахгүй бол идэвхгүй болгоход аюулгүй.\n  icon_pack:\n    available: Боломжтой Icon багцууд\n    selected: Идэвхтэй дүрсний багцууд\n  language: Хэл\n  monday: Даваа гараг\n  performance_mode:\n    on_battery: Зайтай\n    on_energy_saver: Эрчим хүч хэмнэгч дээр\n    options:\n      disabled: Эрэмдэг зэрэмдэг\n      extreme: Түйлын\n      minimal: Бага\n    plugged: Залгасан эсвэл цэнэглэж байна\n  polling_interval: Системийн санал авах интервал\n  polling_interval_description: >-\n    Seelen UI нь CPU, RAM, сүлжээ, дискний үйл ажиллагаа зэрэг системийн нөөцийг\n    хэр олон удаа (секундэд) шалгадаг. Бага тоо нь илүү олон удаа шинэчлэгдэх\n    боловч бага зэрэг өндөр нөөцийн хэрэглээ гэсэн үг юм.\n  saturday: Бямба гариг\n  start_of_week: Долоо хоногийн эхлэл\n  startup: Эхлэх үед гүйх үү?\n  sunday: Ням гараг\n  theme:\n    available: Боломжтой загварууд\n    selected: Идэвхтэй сэдэв\nheader:\n  labels:\n    config: Тохиргоо\n    developer: Хөгжүүлэгчдэд зориулсан\n    extras: Мөр өгөх\n    general: Жанжин\n    home: Гэр\n    icon_pack_editor: Кэшлэсэн дүрсүүд\n    iconpack: Icon багц\n    monitors: Хэлэлт авагч\n    plugin: Гарын үсэг\n    resources: Ажил\n    shortcuts: Хэв хаягдал\n    soundpack: Дууны багц\n    specific_apps: Програмын тохиргоо\n    theme: Сэдэвүүд\n    virtual_desk: Виржопын ширээний гутал\n    wallpaper: Ханын цаас\n    widget: Аялалын мэдээ\nhome:\n  new_resources: Шинэ нөөц\ninherit: Удамших\ninProgress: Явагдаж байна ...\ninsert: Оруулах\nloading: Ачаалах ...\nmiscellaneous: Олон төрлийн\nmonitors_configurations:\n  label: Хяналт {{индекс}}\nmore: Илүү / их олон\n'no': '-Гүй / -битгий'\nopen: Нээлттэй\nquit: Орхих\nremove: Зөөх\nreset_all_to_default: Бүгдийг нь анхдагч утгыг дахин тохируулна уу\nreset_to_default: Үндсэн утга руу дахин тохируулна уу\nresources:\n  app_outdated: Энэ нөөц нь Seelen Ui-ийн шинэ хувилбарыг зөв ажиллахыг шаарддаг.\n  corrupted_wallpaper: Өнгөц зургийг задалж чадсангүй - гэмтсэн эсвэл дэмжигдээгүй видео формат\n  delete: Нөөцийг устгах\n  discover: Илүү их нөөцийг олж мэдэх\n  has_update: Шинэчлэлт хийх боломжтой\n  high_impact: Гүйцэтгэлд өндөр нөлөө үзүүлдэг\n  import_wallpapers: Орон нутгийн ханын цаас импортлох\n  open_folder: Нээлттэй зөөврийн\n  outdated: >-\n    Энэ нөөц нь Seelen Ui-ийн хуучин хувилбарт зориулагдсан бөгөөд зөв\n    ажиллахгүй байж магадгүй юм.\n  see_on_website: Вэбсайтаас үзнэ үү\nreview_request:\n  not_now: Одоо биш\n  sure: Мэдээж!\n  title: Seelen UI таалагдаж байна уу?\nsave: Аврах\nsave_and_restart: Хадгалах & дахин эхлүүлнэ үү\nsearch: Хайх\nsee_more: Дэлгэрэнгүйг үзэх\nshortcuts:\n  duplicate_error: Энэ товчлол нь давхардсан байна\n  enable: Баригдсан товчлол системийг идэвхжүүлэх\n  enable_tooltip: >-\n    Хэрэв та Seelen UI Client ашиглан өөрийн товчлолыг ашиглан өөрийн товчлолыг\n    хэрэгжүүлэхийг идэвхгүй болго\n  labels:\n    create_new_workspace: Шинэ ажлын талбар үүсгэх\n    cycle_stack_next: Циклийн овоолго\n    cycle_stack_prev: Циклийн овоолго өмнөх\n    cycle_wallpaper_next: Дараагийн ханын зураг руу шилжих\n    cycle_wallpaper_prev: Өмнөх ханын зураг руу шилжих\n    decrease_height: Өндөр хэмжээг\n    decrease_width: Гарын хойш хөгжих\n    destroy_current_workspace: Одоогийн ажлын талбарыг устгах\n    focus_bottom: Дээр төвлөрүү\n    focus_latest: Фандик хамгийн сүүлийн үеийн\n    focus_left: Зүүн талд байна\n    focus_right: Домог\n    focus_top: Додуулах нь эсрэг\n    increase_height: Дээд хэмжээг нэмэгдүүлэх\n    increase_width: Өргөнийг нэмэгдүүлэх\n    misc_force_quit: Хүчний гарах\n    misc_force_restart: Хүчийг дахин эхлүүлэх\n    misc_open_settings: АВир танилцуулга\n    misc_toggle_lock_tracing: Түгжих Tracing Tracing (бүртгэл)\n    misc_toggle_win_event_tracing: Тогтмол үйл явдлын үеэр Togge To\n    move_to_workspace: Ажлын талбар руу шилжих {{0}}\n    move_window_down: Цонхыг доош нь шилжүүлэх\n    move_window_left: Цонхыг зүүн тийш шилжүүлэх\n    move_window_right: Цонхыг баруун тийш нь шилжүүлэх\n    move_window_up: Цонхыг дээд руу шилжүүлэх\n    pause_tiling: Цонхны менежерийг түр зогсоох\n    reserve_bottom: Нөөцийг нөөцлөх\n    reserve_float: Нөөцийн хөвөх\n    reserve_left: Зуух Зүүн\n    reserve_right: Нөөцийг зөв дарна уу\n    reserve_stack: Дурсамж авах\n    reserve_top: Нэр гаргах\n    restore_sizes: Хэмжээг сэргээх\n    send_to_workspace: Ажлын талбар руу илгээх {{0}}\n    start_weg_app: Фокус эсвэл програмыг эхлүүлэх {{0}}\n    switch_to_next_workspace: Дараагийн ажлын талбар руу шилжих\n    switch_to_previous_workspace: Өмнөх ажлын талбар руу шилжих\n    switch_workspace: Ажлын талбар руу шилжих {{0}}\n    toggle_float: Windows Hoad Mode-ийг сэлгэх\n    toggle_monocle: Ажлын талбар монокле горимыг сэлгэх\n  readonly_tooltip: Энэ бол зөвхөн унших товчлол юм\n  reset: Үндсэндээ тохируулах\nsides:\n  bottom: Ероол\n  left: Зүүн\n  right: Зөв байх\n  top: Таг\ntoolbar:\n  auto_hide: Авто гүнд\n  delay_to_hide: Нуухыг хойшлуулах\n  delay_to_show: Үзүүлэхийг хойшлуулсан\n  dock_side: Байрлал\n  enable: Ширээний хэрэгслийн самбарыг идэвхжүүлэх\n  hide_mode:\n    always: Үргэлж\n    never: Хэзээ ч үгүй\n    on_overlap: Давхардсан дээр\n  item_size: Барааны хэмжээ\n  label: Балтсний хайрцагны хэрэгсэл\n  margin: Маржин хэмжээ\n  padding: Дотор хэмжээ\n  placeholder: {}\nupdate:\n  available: Шинэчлэх боломжтой!\n  channel: Суваг шинэчлэх\n  downloading: Татаж авах ...\nwall:\n  backgrounds: Ханын цаас\n  blur: Тайлбарал\n  cancel: Цуцлах\n  close: Хаах\n  collection_name: Цуглуулгын нэр\n  collections: Цуглуулга\n  contrast: Эрс ялгаа\n  corrupted_wallpapers_message: 'Гэмтсэн ханын зураг, эдгээрийг устгана уу:'\n  create: Үүсгэх\n  create_collection: Цуглуулга үүсгэх\n  default_collection: Өгөгдмөл цуглуулга\n  delete_collection: Цуглуулгыг устгах\n  edit_collection: Цуглуулга засварлах\n  enable: Wallpaper менежерийг идэвхжүүлэх\n  extend: Үндсэн хэсгийг сунгах\n  fit:\n    contain: Агуулах\n    cover: Бутээлэг\n    fill: Дүүрэх\n  flipHorizontal: Хөндлөнгийн хэвтээ\n  flipVertical: Босоо босоо\n  generating_thumbnails: Дэлгэцийн өнгөц зураг үүсгэж байна\n  hours: цаг\n  interval: Ханын цаасыг өөрчлөх\n  minutes: хоёр хоног\n  monitor_collection: Мониторын цуглуулга\n  multimonitor_behaviour: Multimonitor Behavior\n  muted: Дуугүй видео аудио\n  no_background: Хоосон слайпешт, оронд нь.\n  no_collections: Одоогоор цуглуулгаа үүсгээгүй байна. + товчийг дарж нэгийг үүсгэнэ үү.\n  objectFit: Суурь тохирох\n  objectPosition: Арын байрлал\n  overlayColor: Давхардсан өнгө\n  overlayMixBlendMode: Холих холимог холимог горим\n  per_monitor: Монитор бүрт\n  playback: Тоглуулах хурд\n  position:\n    bottom: Ероол\n    center: Тов\n    left: Зүүн\n    right: Зөв байх\n    top: Таг\n  processing_video: Видеог боловсруулж байна\n  random: Санамсаргүй байдлаар Slideshes\n  saturation: Цэнгэхны бэлтгэл, төгсөг\n  seconds: хоёр цаг\n  select_collection: Цуглуулга сонгоно уу\n  thumbnail_generation_complete: Өнгөц зураг үүсгэж дууслаа\n  thumbnail_generation_finished: Өнгөц зураг үүсгэх ажиллагаа амжилттай дууслаа\n  wallpaper_collection: Дэлгэцийн зургийн цуглуулга\n  wallpaper_settings: Дэлгэцийн зурагны тохиргоо\n  withOverlay: Давхцаж байна\n  workspace_collections: Ажлын талбарын цуглуулга\nweg:\n  auto_hide: Авто гүнд\n  delay_to_hide: Нуухыг хойшлуулах\n  delay_to_show: Үзүүлэхийг хойшлуулсан\n  dock_side: Байрлал\n  enable: Док / Taskbar-г идэвхжүүлэх\n  filtering: Барааны шүүлтүүр\n  gap: Цонх\n  hide_mode:\n    always: Үргэлж\n    never: Хэзээ ч үгүй\n    on_overlap: Давхардсан дээр\n  items:\n    gap: Зүйлийн хоорондох зай\n    label: Зүйл\n    pinned_visibility:\n      always: Үргэлж\n      label: Тогтсон зүйлсийн харагдах байдал\n      when_primary: Монитор анхдагч байх үед\n    show_instance_counter: Нээлттэй Windows тоолуурыг харуул\n    show_window_title: Нээлттэй цонхны гарчигыг харуул (зөвхөн хэвтээ)\n    size: Зүйлийн хэмжээ\n    split_windows: Windows хуваах (цонх бүрт нэг зүйл)\n    temporal_visibility:\n      all: Бүгд\n      label: Тодруулаагүй зүйлсийн харагдах байдал\n      on_monitor: Монитор дээр\n    visible_separators: Харагдах тусгаарлагч\n  label: Док / Taskbar\n  margin: Саруулган өвс\n  mode:\n    full_width: Бүтэн дэлгэцийн өргөн\n    min_content: Аль болох жижиг\n  padding: Дэвсгэр\n  show_end_task: ТЕХНИКИЙН ТОНОГ ТӨХӨӨРӨМЖИЙН ТӨЛӨВЛӨГӨӨ\n  width: Өргөн\nwelcome:\n  give_a_review: Шүүмж өгөх\n  message: >-\n    Seelen UI нь Windows-д зориулсан үнэгүй, нээлттэй эхийн ширээний орчин юм.\n    Энд та ширээний компьютерээ хэрхэн харж, ажиллахыг бүрэн хянах боломжтой\n    бөгөөд энэ нь ажлын явц, хэв маягтаа тохируулан нарийн ширийн зүйл бүрийг\n    өөрчлөх боломжийг танд олгоно.\n  ok: Эхэлцгээе!\n  review: >-\n    Хэрэв та Seelen UI ашиглах дуртай бол дэлгүүрт сэтгэгдэл үлдээгээрэй - таны\n    санал хүсэлт төслийг хөгжүүлэх, сайжруулахад тусална.\n  title: Seelen UI-д тавтай морил!\nwidget:\n  enable: Энэ виджетийг идэвхжүүлэх\n  enable_for_monitor: Энэ монитор дээр идэвхжүүлэх\n  instances: Саналууд\nwm:\n  animations:\n    duration: Хөдөлгөөнт давталт (MS)\n    ease_function: Хөдөлгөөнт хялбаршуулах функц\n    enable: Цонхны анимацийг идэвхжүүлэх\n  author: Зохиолч\n  border:\n    enable: Цонхны хилийг идэвхжүүлэх\n    offset: Хилийн офсет\n    width: Хилийн өргөн\n  description: Тодорхойлолт / төрөл анги\n  drag_behavior: Чирэх зан үйл\n  drag_behavior_options:\n    sort: Эрэмбэлэх (чирэх үед цонхны дарааллыг өөрчлөх)\n    swap: Солих (цонхыг чирэх төгсгөлд солих)\n  enable: Цонхны менежерийг идэвхжүүлэх боломжийг олгох\n  layout: Төлөвлөгөө\n  resize_delta: Дельтааг өөрчлөх (%)\n  space_between_containers: Савны хоорондох зай\n  workspace_offset: Ажлын талбар нь офсет (Margins)\n  workspace_padding: Ажлын талбарууд\n'yes': Мон\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ms.yml",
    "content": "action:\n  confirm: Adakah anda pasti?\n  confirm_body: Tindakan ini tidak dapat dibatalkan.\napps_configurations:\n  app:\n    bindings: Mengikat (perhatikan kedua -dua pilihan diperlukan)\n    category: Kategori\n    category_placeholder: Tiada\n    monitor: Memantau\n    monitor_placeholder: Tiada\n    name: Nama\n    ok_create: Buat\n    ok_edit: Kemas kini\n    ok_readonly: Edit sebagai baru\n    options:\n      NoInteractive: Tiada interaktif\n      VdPinned: Tunjukkan di semua ruang kerja\n      WmFloat: TWM - Mula Terapung\n      WmForce: TWM - Mengurus daya\n      WmUnmanage: TWM - Unmanage\n    options_label: Pilihan tambahan\n    title_create: Menciptakan {{name}}\n    title_edit: Penyuntingan {{name}}\n    title_readonly: Melihat {{name}}\n    weg_options_label: Pilihan Dock/Taskbar\n    wm_options_label: Pilihan Pengurus Tetingkap\n    workspace: Ruang kerja\n    workspace_placeholder: Tiada\n  bundled_msg: >-\n    Konfigurasi yang dibundel ini tidak dapat diedit dan direka untuk memberi\n    anda pengalaman terbaik tanpa penyesuaian. Mereka secara automatik\n    mengkonfigurasi aplikasi yang paling biasa untuk anda.\n  bundled_title: Konfigurasi aplikasi dibundel dengan seelen\n  confirm_delete: Adakah anda pasti mahu memadamkan konfigurasi ini?\n  confirm_delete_title: Sahkan padam\n  delete: Padam\n  export: Eksport\n  export_full: Tetapan eksport melalui permohonan\n  extra_info: >-\n    UI Seelen hanya menggunakan satu pengecam bagi setiap apl (padanan pertama\n    ditemui) jadi susunan cara dikhususkan adalah penting, penambahan terkini\n    akan diutamakan, kerana ambil perhatian bahawa jadual diisih secara lalai\n    dari terkini ke lama.\n  identifier:\n    add_block: Tambah blok\n    and: Dan\n    id: Pengenalpastian\n    kind: Mengenal pasti oleh\n    matching_strategy: Strategi yang sepadan\n    matching_strategy_option:\n      contains: Mengandungi\n      ends_with: Berakhir dengan\n      equals: sama\n      regex: Ekspresi biasa\n      starts_with: Bermula dengan\n    negation: Menafikan padanan\n    or: Atau\n    remove: Padam blok\n    type:\n      class: Kelas\n      exe: Exe\n      path: Laluan\n      title: Tajuk\n  import: Import\n  import_full: Tetapan import melalui permohonan\n  new: Baru\n  search: Cari\n  swap: Bertukar\ncancel: Batalkan\nclose: Tutup\ndelete: Padam\ndevtools:\n  app_folders: Folder App\n  custom_config_file: Muatkan fail konfigurasi tersuai\n  data_folder: Folder Data\n  enable: Dayakan alat pemaju\n  install_folder: Folder pemasangan\n  load: Beban\n  settings_file: Fail tetapan\n  simulate_perm:\n    label: Simulasikan Permintaan Kebenaran Widget\n    result_allowed: ✓ Kebenaran diberikan\n    result_denied: ✗ Kebenaran ditolak\n    trigger: Simulasikan\n    widget_id_placeholder: '@nama pengarang/widget'\nextras:\n  clear_icons: Cacon Ikon Sistem Jelas\n  clear_icons_tooltip: Restart boleh dikehendaki sepenuhnya pada semua widget\n  exit: Berhenti/keluar\n  links: Pautan Rasmi\n  relaunch: Pelancaran semula\n  version: Versi\n  version_fixed: >-\n    Aplikasi dan versi WebView2 Runtime telah ditetapkan. Ini bermakna bahawa\n    aplikasi tidak akan menerima kemas kini dan WebView2 Runtime tidak akan\n    dikemas kini secara automatik dengan kemas kini Windows.\ngeneral:\n  accent_color: Warna aksen\n  date_format: Format tarikh\n  date_format_how_to: Bagaimana untuk menulis format tarikh?\n  hardware_acceleration: Pecutan Perkakasan\n  hardware_acceleration_description: >-\n    Melumpuhkan pecutan perkakasan akan mengurangkan penggunaan memori, tetapi\n    boleh menyebabkan masalah prestasi. Selamat untuk dilumpuhkan jika anda\n    tidak akan menggunakan kertas dinding langsung.\n  icon_pack:\n    available: Pek Ikon Tersedia\n    selected: Pek Ikon Aktif\n  language: Bahasa\n  monday: Isnin\n  performance_mode:\n    on_battery: Pada bateri\n    on_energy_saver: Pada Penjimat Tenaga\n    options:\n      disabled: Kurang upaya\n      extreme: Melampau\n      minimal: Minimum\n    plugged: Dipasang atau mengecas\n  polling_interval: Selang pengundian sistem\n  polling_interval_description: >-\n    Berapa kerap (dalam saat) UI Seelen menyemak sumber sistem seperti CPU, RAM,\n    rangkaian dan aktiviti cakera. Bilangan yang lebih kecil bermakna kemas kini\n    yang lebih kerap, tetapi penggunaan sumber yang lebih tinggi sedikit.\n  saturday: Sabtu\n  start_of_week: Permulaan Minggu\n  startup: Jalankan permulaan?\n  sunday: Ahad\n  theme:\n    available: Tema yang Tersedia\n    selected: Tema Aktif\nheader:\n  labels:\n    config: Konfigurasi\n    developer: Untuk pemaju\n    extras: Tambahan\n    general: Umum\n    home: Rumah\n    icon_pack_editor: Ikon cache\n    iconpack: Pek ikon\n    monitors: Monitor\n    plugin: Plugin\n    resources: Sumber\n    shortcuts: Pintasan\n    soundpack: Pek bunyi\n    specific_apps: Tetapan dengan permohonan\n    theme: Tema\n    virtual_desk: Desktop maya\n    wallpaper: Kertas dinding\n    widget: Widget\nhome:\n  new_resources: Sumber baru\ninherit: Mewarisi\ninProgress: Dalam proses ...\ninsert: Masukkan\nloading: Memuatkan ...\nmiscellaneous: Pelbagai\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Lebih\n'no': Tidak\nopen: Buka\nquit: Berhenti\nremove: Keluarkan\nreset_all_to_default: Tetapkan semula semua ke nilai lalai\nreset_to_default: Tetapkan semula ke nilai lalai\nresources:\n  app_outdated: >-\n    Sumber ini memerlukan versi Seelen UI yang lebih baru untuk berfungsi dengan\n    baik.\n  corrupted_wallpaper: Gagal mengeluarkan lakaran kecil - format video rosak atau tidak disokong\n  delete: Padam sumber\n  discover: Cari lebih banyak sumber\n  has_update: Kemas kini ada\n  high_impact: Kesan tinggi terhadap prestasi\n  import_wallpapers: Mengimport kertas dinding tempatan\n  open_folder: Buka Folder Sumber\n  outdated: >-\n    Sumber ini direka untuk versi lama Seelen UI dan mungkin tidak berfungsi\n    dengan baik.\n  see_on_website: Lihat di laman web\nreview_request:\n  not_now: bukan sekarang\n  sure: Pasti!\n  title: Menikmati UI Seelen?\nsave: Simpan\nsave_and_restart: Jimat & mulakan semula\nsearch: Cari\nsee_more: Lihat lagi\nshortcuts:\n  duplicate_error: Pintasan ini diduplikasi\n  enable: Dayakan sistem pintasan terbina dalam\n  enable_tooltip: >-\n    Lumpuhkan jika anda akan melaksanakan sistem pintasan anda sendiri\n    menggunakan pelanggan Seelen UI\n  labels:\n    create_new_workspace: Buat ruang kerja baru\n    cycle_stack_next: Stack kitaran seterusnya\n    cycle_stack_prev: Tumpukan kitaran sebelumnya\n    cycle_wallpaper_next: Tukar ke kertas dinding seterusnya\n    cycle_wallpaper_prev: Tukar ke kertas dinding sebelumnya\n    decrease_height: Mengurangkan ketinggian\n    decrease_width: Mengurangkan lebar\n    destroy_current_workspace: Memusnahkan ruang kerja semasa\n    focus_bottom: Fokus bawah\n    focus_latest: Fokus terkini\n    focus_left: Fokus kiri\n    focus_right: Fokus betul\n    focus_top: Fokus atas\n    increase_height: Meningkatkan ketinggian\n    increase_width: Meningkatkan lebar\n    misc_force_quit: Memaksa berhenti\n    misc_force_restart: Memaksa memulakan semula\n    misc_open_settings: Buka Tetapan\n    misc_toggle_lock_tracing: Togol mengesan kunci (log)\n    misc_toggle_win_event_tracing: Togol Menang Mengesan Acara (Log)\n    move_to_workspace: Pindah ke ruang kerja {{0}}\n    move_window_down: Pindahkan tetingkap ke bawah\n    move_window_left: Pindahkan tingkap ke kiri\n    move_window_right: Pindahkan tetingkap ke kanan\n    move_window_up: Pindahkan tetingkap ke bahagian atas\n    pause_tiling: Jeda Jeda Pengurus Tetingkap\n    reserve_bottom: Bawah simpanan\n    reserve_float: Rizab terapung\n    reserve_left: Rizab kiri\n    reserve_right: Rizab betul\n    reserve_stack: Timbunan simpanan\n    reserve_top: Rizab atas\n    restore_sizes: Pulihkan saiz\n    send_to_workspace: Hantar ke Workspace {{0}}\n    start_weg_app: Fokus atau mulakan aplikasi {{0}}\n    switch_to_next_workspace: Beralih ke ruang kerja seterusnya\n    switch_to_previous_workspace: Beralih ke ruang kerja sebelumnya\n    switch_workspace: Tukar ke ruang kerja {{0}}\n    toggle_float: Mod Terapung Togel Togel\n    toggle_monocle: Mod Monocle Workspace Togol\n  readonly_tooltip: Ini adalah jalan pintas baca sahaja\n  reset: Tetapkan semula ke lalai\nsides:\n  bottom: Bawah\n  left: Dibiarkan\n  right: Betul\n  top: Atas\ntoolbar:\n  auto_hide: Menyembunyikan auto\n  delay_to_hide: Bertangguh untuk bersembunyi\n  delay_to_show: Kelewatan untuk menunjukkan\n  dock_side: Kedudukan\n  enable: Dayakan bar alat mewah\n  hide_mode:\n    always: Sentiasa\n    never: tidak pernah\n    on_overlap: Bertindih\n  item_size: Saiz Barang\n  label: Bar alat\n  margin: Saiz Margin\n  padding: Saiz Padding\n  placeholder: {}\nupdate:\n  available: Kemas kini tersedia!\n  channel: Kemas kini saluran\n  downloading: Memuat turun ...\nwall:\n  backgrounds: Kertas dinding\n  blur: Kabur\n  cancel: Batal\n  close: tutup\n  collection_name: Nama Koleksi\n  collections: Koleksi\n  contrast: Kontras\n  corrupted_wallpapers_message: 'Kertas dinding rosak, pertimbangkan untuk memadam ini:'\n  create: Buat\n  create_collection: Buat Koleksi\n  default_collection: Koleksi Lalai\n  delete_collection: Padamkan Koleksi\n  edit_collection: Edit Koleksi\n  enable: Dayakan Pengurus Wallpaper\n  extend: Lanjutkan utama\n  fit:\n    contain: Mengandungi\n    cover: Penutup\n    fill: Mengisi\n  flipHorizontal: Flip mendatar\n  flipVertical: Flip menegak\n  generating_thumbnails: Menjana Gambar Kecil Kertas Dinding\n  hours: jam\n  interval: Tukar kertas dinding setiap\n  minutes: minit\n  monitor_collection: Koleksi Pantau\n  multimonitor_behaviour: Kelakuan Multimonitor\n  muted: Audio video bisu\n  no_background: Tayangan slaid kosong, menggunakan latar belakang tema.\n  no_collections: Tiada koleksi dibuat lagi. Klik butang + untuk mencipta satu.\n  objectFit: Latar belakang sesuai\n  objectPosition: Kedudukan latar belakang\n  overlayColor: Warna overlay\n  overlayMixBlendMode: Mod campuran campuran overlay\n  per_monitor: Setiap monitor\n  playback: Kelajuan main balik\n  position:\n    bottom: Bawah\n    center: Pusat\n    left: Kiri\n    right: Betul\n    top: Atas\n  processing_video: Memproses video\n  random: Rawak tayangan slaid\n  saturation: Ketepuan\n  seconds: saat\n  select_collection: Pilih Koleksi\n  thumbnail_generation_complete: Penjanaan Thumbnail Selesai\n  thumbnail_generation_finished: Penjanaan imej kecil telah berjaya diselesaikan\n  wallpaper_collection: Koleksi Kertas Dinding\n  wallpaper_settings: Tetapan Kertas Dinding\n  withOverlay: Dengan overlay\n  workspace_collections: Koleksi Ruang Kerja\nweg:\n  auto_hide: Menyembunyikan auto\n  delay_to_hide: Bertangguh untuk bersembunyi\n  delay_to_show: Kelewatan untuk menunjukkan\n  dock_side: Kedudukan\n  enable: Dayakan Dock/Taskbar\n  filtering: Penapisan item\n  gap: Jurang\n  hide_mode:\n    always: Sentiasa\n    never: tidak pernah\n    on_overlap: Bertindih\n  items:\n    gap: Ruang antara item\n    label: Item\n    pinned_visibility:\n      always: Sentiasa\n      label: Keterlihatan Item Disemat\n      when_primary: Apabila monitor adalah utama\n    show_instance_counter: Tunjukkan Kaunter Windows Terbuka\n    show_window_title: Tunjukkan tajuk tetingkap terbuka (hanya mendatar)\n    size: Saiz item\n    split_windows: Pisahkan Windows (satu item setiap tetingkap)\n    temporal_visibility:\n      all: Semua\n      label: Keterlihatan Item Dinyahsemat\n      on_monitor: Pada Monitor\n    visible_separators: Pemisah yang kelihatan\n  label: Dock/Taskbar\n  margin: Margin\n  mode:\n    full_width: Lebar skrin penuh\n    min_content: Kecil yang boleh\n  padding: Padding\n  show_end_task: Tunjukkan tugas akhir di bar tugas\n  width: Lebar\nwelcome:\n  give_a_review: Berikan Ulasan\n  message: >-\n    UI Seelen ialah persekitaran desktop sumber terbuka dan percuma untuk\n    Windows. Di sini anda mempunyai kawalan penuh ke atas rupa dan gelagat\n    desktop anda, membolehkan anda menyesuaikan setiap butiran agar sepadan\n    dengan aliran kerja dan gaya anda.\n  ok: Mari mulakan!\n  review: >-\n    Jika anda gemar menggunakan UI Seelen, pertimbangkan untuk meninggalkan\n    ulasan di kedai — maklum balas anda membantu projek berkembang dan bertambah\n    baik.\n  title: Selamat datang ke UI Seelen!\nwidget:\n  enable: Dayakan widget ini\n  enable_for_monitor: Dayakan di monitor ini\n  instances: Contoh\nwm:\n  animations:\n    duration: Tempoh Animasi (MS)\n    ease_function: Fungsi pelonggaran animasi\n    enable: Dayakan animasi tetingkap\n  author: Pengarang\n  border:\n    enable: Dayakan sempadan tetingkap\n    offset: Mengimbangi sempadan\n    width: Lebar sempadan\n  description: Penerangan\n  drag_behavior: Gelagat Seret\n  drag_behavior_options:\n    sort: Isih (susun semula tetingkap semasa menyeret)\n    swap: Tukar (tukar tetingkap pada hujung seret)\n  enable: Dayakan pengurus tetingkap jubin\n  layout: Susun atur\n  resize_delta: Saiz semula delta (%)\n  space_between_containers: Ruang antara bekas\n  workspace_offset: Ruang Kerja Offset (margin)\n  workspace_padding: Padding ruang kerja\n'yes': Ya\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/mt.yml",
    "content": "action:\n  confirm: Int żgur?\n  confirm_body: Din l-azzjoni ma tistax titneħħa.\napps_configurations:\n  app:\n    bindings: Torbot (innota ż-żewġ għażliet huma meħtieġa)\n    category: Kategorija\n    category_placeholder: Xejn\n    monitor: Tissorvelja\n    monitor_placeholder: Xejn\n    name: Isem\n    ok_create: Oħloq\n    ok_edit: Aġġornament\n    ok_readonly: Editja bħala ġdida\n    options:\n      NoInteractive: Nru Interattiv\n      VdPinned: Uri fl-ispazji tax-xogħol kollha\n      WmFloat: Twm - Ibda f'wiċċ l-ilma\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Għażliet żejda\n    title_create: Ħolqien ta '{{isem}}\n    title_edit: Editjar {{isem}}\n    title_readonly: Wiri {{isem}}\n    weg_options_label: Għażliet tal-baċir / taskbar\n    wm_options_label: Għażliet tal-Maniġer tat-Tieqa\n    workspace: Spazju ta 'xogħol\n    workspace_placeholder: Xejn\n  bundled_msg: >-\n    Dawn il-konfigurazzjonijiet miġbura mhumiex editabbli u huma mfassla biex\n    jipprovdulek l-aħjar esperjenza mingħajr personalizzazzjoni. Huma\n    awtomatikament jikkonfiguraw l-aktar applikazzjonijiet komuni għalik.\n  bundled_title: App konfigurazzjoni miġbura ma 'seelen\n  confirm_delete: Int żgur li trid tħassar din il-konfigurazzjoni / i?\n  confirm_delete_title: Ikkonferma Ħassar\n  delete: Ħassar\n  export: Esportazzjoni\n  export_full: Settings tal-Esportazzjoni skont l-Applikazzjoni\n  extra_info: >-\n    Seelen UI tuża identifikatur wieħed biss għal kull app (l-ewwel tqabbil\n    misjub) sabiex l-ordni ta 'kif huma speċifikati hija importanti, l-aħħar\n    miżjuda se tkun prijoritizzata, peress li nnota li t-tabella hija magħżula\n    b'mod awtomatiku mill-aktar tard għall-qadim.\n  identifier:\n    add_block: Żid blokka\n    and: U\n    id: Identifikatur\n    kind: Identifika minn\n    matching_strategy: Strateġija ta 'tqabbil\n    matching_strategy_option:\n      contains: Fih\n      ends_with: Tispiċċa bi\n      equals: Indaqs\n      regex: Espressjoni regolari\n      starts_with: Jibda bi\n    negation: Iċħad it-tqabbil\n    or: Jew\n    remove: Ħassar il-blokka\n    type:\n      class: Klassi\n      exe: Exe\n      path: Mogħdija\n      title: Titolu\n  import: Importa\n  import_full: Settings tal-importazzjoni skont l-applikazzjoni\n  new: Ġdid\n  search: Tfittxija\n  swap: Tpartit\ncancel: Ikkanċella\nclose: Viċin\ndelete: Ħassar\ndevtools:\n  app_folders: Folders tal-app\n  custom_config_file: Tagħbija Fajl tal-Konfigurazzjoni Custom\n  data_folder: Folder tad-dejta\n  enable: Ippermetti għodod għall-iżviluppaturi\n  install_folder: Folder tal-installazzjoni\n  load: Tagħbija\n  settings_file: Settings Fajl\n  simulate_perm:\n    label: Issimula Talba ta' Permess għal Widget\n    result_allowed: ✓ Permess mogħti\n    result_denied: ✗ Permess miċħud\n    trigger: Issimula\n    widget_id_placeholder: '@ awtur/widget-isem'\nextras:\n  clear_icons: Ikoni tas-sistema ċara cache\n  clear_icons_tooltip: >-\n    Jista 'jkun meħtieġ li jerġa' jkun meħtieġ biex jidħol fis-seħħ fuq\n    il-widgets kollha\n  exit: Nieqaf / ħruġ\n  links: Rabtiet uffiċjali\n  relaunch: Tnedija mill-ġdid\n  version: Verżjoni\n  version_fixed: >-\n    L-applikazzjoni u l-verżjonijiet tal-WebView2 Runtime huma ffissati. Dan\n    ifisser li l-applikazzjoni mhux se tirċievi aġġornamenti u l-WebView2\n    Runtime mhux se jiġi aġġornat awtomatikament bl-aġġornamenti tal-Windows.\ngeneral:\n  accent_color: Kulur tal-aċċent\n  date_format: Format tad-data\n  date_format_how_to: Kif tikteb format tad-data?\n  hardware_acceleration: Aċċelerazzjoni tal-Hardware\n  hardware_acceleration_description: >-\n    Id-diżattivazzjoni tal-aċċelerazzjoni tal-ħardwer tnaqqas l-użu tal-memorja,\n    iżda tista 'tikkawża problemi ta' prestazzjoni. Huwa sikur li tiddiżattiva\n    jekk mhux se tuża wallpapers ħajjin.\n  icon_pack:\n    available: Pakketti ta' Ikoni Disponibbli\n    selected: Pakketti tal-Ikoni Attivi\n  language: Lingwa\n  monday: it-Tnejn\n  performance_mode:\n    on_battery: Fuq batterija\n    on_energy_saver: Fuq l-enerġija li tiffranka\n    options:\n      disabled: B'diżabilità\n      extreme: Estrem\n      minimal: Minimu\n    plugged: Imqabbad jew iċċarġjar\n  polling_interval: Intervall tal-votazzjoni tas-sistema\n  polling_interval_description: >-\n    Kemm-il darba (f'sekondi) Seelen UI tiċċekkja r-riżorsi tas-sistema bħal\n    CPU, RAM, netwerk, u attività tad-disk. Numru iżgħar ifisser aġġornamenti\n    aktar frekwenti, iżda użu tar-riżorsi kemmxejn ogħla.\n  saturday: is-Sibt\n  start_of_week: Bidu tal-Ġimgħa\n  startup: Mexxi fuq l-istartjar?\n  sunday: il-Ħadd\n  theme:\n    available: Temi Disponibbli\n    selected: Temi Attivi\nheader:\n  labels:\n    config: Konfigurazzjonijiet\n    developer: Għall-iżviluppaturi\n    extras: Ekstras\n    general: Ġenerali\n    home: Id-dar\n    icon_pack_editor: Ikoni cache\n    iconpack: Pakketti tal-ikona\n    monitors: Monitors\n    plugin: Plugins\n    resources: Riżorsi\n    shortcuts: Shortcuts\n    soundpack: Pakketti tal-ħoss\n    specific_apps: Settings skont l-applikazzjoni\n    theme: Temi\n    virtual_desk: Desktops virtwali\n    wallpaper: Wallpapers\n    widget: Widgets\nhome:\n  new_resources: Riżorsi ġodda\ninherit: Jirtu\ninProgress: Fil-progress...\ninsert: Daħħal\nloading: Tagħbija...\nmiscellaneous: Varji\nmonitors_configurations:\n  label: Monitor {{indiċi}}\nmore: Aktar\n'no': LE\nopen: Miftuħa\nquit: Nieqaf\nremove: Neħħi\nreset_all_to_default: Irrisettja l-valuri kollha tal-inadempjenza\nreset_to_default: Irrisettja għall-valur awtomatiku\nresources:\n  app_outdated: Din ir-riżorsa teħtieġ verżjoni aktar ġdida ta 'Seelen UI biex taħdem sew.\n  corrupted_wallpaper: >-\n    Naqas milli jiġi estratt thumbnail - format tal-vidjo korrotta jew mhux\n    appoġġjata\n  delete: Ħassar ir-riżorsa\n  discover: Skopri aktar riżorsi\n  has_update: Aġġornament huwa disponibbli\n  high_impact: Impatt għoli fuq il-prestazzjoni\n  import_wallpapers: Importa wallpapers lokali\n  open_folder: Folder tar-Riżorsi Miftuħa\n  outdated: >-\n    Din ir-riżorsa kienet iddisinjata għal verżjoni aktar antika ta 'Seelen UI u\n    tista' ma taħdimx sew.\n  see_on_website: Ara fuq il-websajt\nreview_request:\n  not_now: Mhux issa\n  sure: Żgur!\n  title: Tgawdi Seelen UI?\nsave: Ħlief\nsave_and_restart: Issejvja u terġa 'tibda\nsearch: Fittex\nsee_more: Ara aktar\nshortcuts:\n  duplicate_error: Din is-shortcut hija duplikata\n  enable: Ippermetti s-sistema shortcuts inkorporati\n  enable_tooltip: >-\n    Itfi jekk timplimentax is-sistema shortcuts tiegħek stess billi tuża\n    l-klijent Seelen UI\n  labels:\n    create_new_workspace: Oħloq spazju ta 'xogħol ġdid\n    cycle_stack_next: Stack Cycle li jmiss\n    cycle_stack_prev: Munzell taċ-ċiklu preċedenti\n    cycle_wallpaper_next: Ibdel għall-Wallpaper li jmiss\n    cycle_wallpaper_prev: Bidla għal wallpaper preċedenti\n    decrease_height: Tnaqqas l-għoli\n    decrease_width: Tnaqqis il-wisa '\n    destroy_current_workspace: Jeqirdu l-ispazju ta 'xogħol attwali\n    focus_bottom: Fokus tal-qiegħ\n    focus_latest: Iffoka l-aħħar\n    focus_left: Fokus xellug\n    focus_right: Iffoka t-tajjeb\n    focus_top: Focus top\n    increase_height: Iżid l-għoli\n    increase_width: Iżżid il-wisa '\n    misc_force_quit: Forza nieqaf\n    misc_force_restart: Startjar mill-ġdid tal-forza\n    misc_open_settings: Settings miftuħa\n    misc_toggle_lock_tracing: Traċċar tal-Lock Toggle (zkuk)\n    misc_toggle_win_event_tracing: Toggle Win Avveniment Traċċar (zkuk)\n    move_to_workspace: Mexxi għal Workspace {{0}}\n    move_window_down: Mexxi tieqa għal isfel\n    move_window_left: Mexxi t-tieqa għax-xellug\n    move_window_right: Mexxi tieqa għal-lemin\n    move_window_up: Mexxi t-tieqa għal fuq\n    pause_tiling: Pawsa Maniġer tat-Tieqa tal-Madum\n    reserve_bottom: Riserva tal-qiegħ\n    reserve_float: Riżerva float\n    reserve_left: Riserva xellug\n    reserve_right: Riserva dritt\n    reserve_stack: Riżerva munzell\n    reserve_top: Riserva ta 'fuq\n    restore_sizes: Irrestawra d-daqsijiet\n    send_to_workspace: Ibgħat lil Workspace {{0}}\n    start_weg_app: Focus jew Ibda Applikazzjoni {{0}}\n    switch_to_next_workspace: Aqleb għall-Ispazju tax-Xogħol li jmiss\n    switch_to_previous_workspace: Aqleb għall-ispazju ta 'xogħol preċedenti\n    switch_workspace: Aqleb għal Workspace {{0}}\n    toggle_float: Toggle Window Float Mode\n    toggle_monocle: Toggle Workspace Monocle Mode\n  readonly_tooltip: Din hija shortcut li jinqara biss\n  reset: Irrisettja għal nuqqasijiet\nsides:\n  bottom: Qiegħ\n  left: Xellug\n  right: Dritt\n  top: Quċċata\ntoolbar:\n  auto_hide: Auto Hide\n  delay_to_hide: Dewmien biex jinħbew\n  delay_to_show: Dewmien biex juri\n  dock_side: Pożizzjoni\n  enable: Jippermetti toolbar tal-fancy\n  hide_mode:\n    always: Dejjem\n    never: Qatt\n    on_overlap: Fuq koinċidenza\n  item_size: Daqs tal-Oġġett\n  label: Toolbar\n  margin: Daqs tal-Marġini\n  padding: Daqs tal-ikkuttunar\n  placeholder: {}\nupdate:\n  available: Aġġornament disponibbli!\n  channel: Aġġorna l-kanal\n  downloading: Tniżżil ...\nwall:\n  backgrounds: Wallpapers\n  blur: Ċċajpar\n  cancel: Ikkanċella\n  close: Agħlaq\n  collection_name: Isem tal-Ġbir\n  collections: Kollezzjonijiet\n  contrast: Kuntrast\n  corrupted_wallpapers_message: 'Wallpapers korrotta, ikkunsidra li tħassar dawn:'\n  create: Oħloq\n  create_collection: Oħloq Ġbir\n  default_collection: Ġbir Default\n  delete_collection: Ħassar Ġbir\n  edit_collection: Edit Ġbir\n  enable: Enable Wallpaper Manager\n  extend: Estendi primarja\n  fit:\n    contain: Fihom\n    cover: Għata\n    fill: Imla\n  flipHorizontal: Flip orizzontali\n  flipVertical: Flip vertikali\n  generating_thumbnails: Ġenerazzjoni tal-Minjaturi tal-Wallpaper\n  hours: sigħat\n  interval: Ibdel il-wallpaper kull\n  minutes: minuti\n  monitor_collection: Ġbir Monitor\n  multimonitor_behaviour: Multimonitor Imġieba\n  muted: Mute video awdjo\n  no_background: Slideshow vojta, billi tuża l-isfond tat-tema minflok.\n  no_collections: S'issa ma nħolqotx kollezzjonijiet. Ikklikkja l-buttuna + biex toħloq waħda.\n  objectFit: L-isfond jaqbel\n  objectPosition: Pożizzjoni ta 'sfond\n  overlayColor: Kulur overlay\n  overlayMixBlendMode: Modalità ta 'taħlita ta' taħlita overlay\n  per_monitor: Għal kull monitor\n  playback: Veloċità tad-daqq\n  position:\n    bottom: Qiegħ\n    center: Ċentru\n    left: Xellug\n    right: Dritt\n    top: Quċċata\n  processing_video: Ipproċessar tal-vidjo\n  random: Randomize slideshow\n  saturation: Saturazzjoni\n  seconds: sekondi\n  select_collection: Agħżel Ġbir\n  thumbnail_generation_complete: Ġenerazzjoni Thumbnail Tlesti\n  thumbnail_generation_finished: Il-ġenerazzjoni ta' miniatures spiċċat b'suċċess\n  wallpaper_collection: Ġbir tal-wallpaper\n  wallpaper_settings: Settings tal-wallpaper\n  withOverlay: Bil-overlay\n  workspace_collections: Kollezzjonijiet tal-Ispazji tax-Xogħol\nweg:\n  auto_hide: Auto Hide\n  delay_to_hide: Dewmien biex jinħbew\n  delay_to_show: Dewmien biex juri\n  dock_side: Pożizzjoni\n  enable: Enable Dock / Taskbar\n  filtering: Iffiltrar tal-Oġġett\n  gap: Vojt\n  hide_mode:\n    always: Dejjem\n    never: Qatt\n    on_overlap: Fuq koinċidenza\n  items:\n    gap: Spazju bejn l-oġġetti\n    label: Oġġetti\n    pinned_visibility:\n      always: Dejjem\n      label: Oġġetti Pinnjati Viżibilità\n      when_primary: Meta l-monitor huwa primarju\n    show_instance_counter: Uri Open Windows Counter\n    show_window_title: Uri t-titlu tat-tieqa miftuħa (orizzontali biss)\n    size: Daqs tal-oġġett\n    split_windows: Twieqi maqsuma (oġġett wieħed għal kull tieqa)\n    temporal_visibility:\n      all: Kollha\n      label: Oġġetti Unpinned Viżibilità\n      on_monitor: Fuq Monitor\n    visible_separators: Separaturi viżibbli\n  label: Dock / Taskbar\n  margin: Marġni\n  mode:\n    full_width: Wisa 'tal-iskrin sħiħ\n    min_content: Żgħir kemm jista’ jkun\n  padding: Ikkuttunar\n  show_end_task: Uri l-kompitu finali fit-taskbar\n  width: Wisa '\nwelcome:\n  give_a_review: Agħti Reviżjoni\n  message: >-\n    Seelen UI huwa ambjent desktop b'xejn u open source għall-Windows. Hawnhekk\n    għandek kontroll sħiħ fuq kif id-desktop tiegħek jidher u jaġixxi, li\n    jippermettilek tippersonalizza kull dettall biex jaqbel mal-fluss tax-xogħol\n    u l-istil tiegħek.\n  ok: Nibdew!\n  review: >-\n    Jekk tieħu pjaċir tuża Seelen UI, ikkunsidra li tħalli reviżjoni fuq\n    il-maħżen — ir-rispons tiegħek jgħin lill-proġett jikber u jitjieb.\n  title: Merħba għal Seelen UI!\nwidget:\n  enable: Ippermetti dan il-widget\n  enable_for_monitor: Jippermetti fuq dan il-monitor\n  instances: Każijiet\nwm:\n  animations:\n    duration: Tul tal-Animazzjoni (MS)\n    ease_function: Funzjoni li tittaffa l-animazzjoni\n    enable: Ippermetti l-animazzjonijiet tat-tieqa\n  author: Awtur\n  border:\n    enable: Ippermetti l-fruntiera tat-tieqa\n    offset: Offset tal-fruntiera\n    width: Wisa 'tal-fruntiera\n  description: Deskrizzjoni\n  drag_behavior: Drag Imġieba\n  drag_behavior_options:\n    sort: Issortja (ordna mill-ġdid it-twieqi waqt li tkaxkar)\n    swap: Ibdel (tbiddel it-twieqi fit-tarf tat-tkaxkir)\n  enable: Ippermetti maniġer tat-tieqa tal-madum\n  layout: Tqassim\n  resize_delta: Daqs mill-ġdid Delta (%)\n  space_between_containers: Spazju bejn il-kontenituri\n  workspace_offset: Spazji tax-Xogħol Offset (Marġini)\n  workspace_padding: Padding tal-ispazji tax-xogħol\n'yes': IVA\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ne.yml",
    "content": "action:\n  confirm: के तपाइँ निश्चित हुनुहुन्छ?\n  confirm_body: यो कार्य पूर्ववत गर्न सकिदैन।\napps_configurations:\n  app:\n    bindings: बाध्यकारी (नोटहरू दुबै विकल्पहरू आवश्यक छन्)\n    category: श्रेणी\n    category_placeholder: केहि पनि होइन\n    monitor: मुल्यांकन गर्नु\n    monitor_placeholder: केहि पनि होइन\n    name: नाम\n    ok_create: सृष्टि गर्नु्\n    ok_edit: अद्यावधिक गर्नुहोस्\n    ok_readonly: नयाँ जस्तै सम्पादन गर्नुहोस्\n    options:\n      NoInteractive: कुनै अन्तरक्रियात्मक छैन\n      VdPinned: सबै कार्यस्थानहरूमा देखाउनुहोस्\n      WmFloat: Twm - फ्लोटिंग सुरु गर्नुहोस्\n      WmForce: Twm - बल व्यवस्थापन\n      WmUnmanage: Twm - अप्रबन्ध गर्नुहोस्\n    options_label: अतिरिक्त विकल्पहरू\n    title_create: '{{Neame नाम} सिर्जना गर्दै}'\n    title_edit: '{{{{नाम} ..'\n    title_readonly: '{{Name N}} अवलोकन}}'\n    weg_options_label: डक / टास्कबार विकल्पहरू विकल्पहरू\n    wm_options_label: विन्डो प्रबन्धक विकल्पहरू\n    workspace: कार्यक्षेत्र\n    workspace_placeholder: केहि पनि होइन\n  bundled_msg: >-\n    यी बन्डल कन्फिगरेसनहरू सम्पादन हुँदैन र तपाईंलाई अनुकूलन बिना उत्तम अनुभव\n    प्रदान गर्न डिजाइन गरिएको हो। तिनीहरू स्वचालित रूपमा तपाइँको लागि सबैभन्दा\n    सामान्य अनुप्रयोगहरू कन्फिगर गर्छन्।\n  bundled_title: अनुप्रयोग कन्फिगलेेनले भावनाको साथ बन्डल\n  confirm_delete: के तपाईं पक्का यो कन्फिगरेसन / एस मेटाउन चाहनुहुन्छ?\n  confirm_delete_title: पुष्टि गर्नुहोस्\n  delete: मेटाउन\n  export: निर्यात गर्नु\n  export_full: आवेदन द्वारा सेटिंग्स निर्यात गर्नुहोस्\n  extra_info: >-\n    Seelen UI ले प्रति एप एक मात्र पहिचानकर्ता प्रयोग गर्दछ (पहिलो मिल्दो फेला\n    पर्यो) त्यसैले कसरी निर्दिष्ट गरिएको छ भन्ने क्रम महत्त्वपूर्ण छ, पछिल्लो\n    थपिएकोलाई प्राथमिकता दिइनेछ, नोट गर्नुहोस् कि तालिकालाई पछिल्लोदेखि पुरानोमा\n    पूर्वनिर्धारित रूपमा क्रमबद्ध गरिएको छ।\n  identifier:\n    add_block: ब्लक थप्नुहोस्\n    and: र\n    id: पहिचानकर्ता\n    kind: द्वारा पहिचान\n    matching_strategy: मिलान रणनीति\n    matching_strategy_option:\n      contains: समावेश गर्दछ\n      ends_with: संग समाप्त हुन्छ\n      equals: बराबर हुन्छ\n      regex: नियमित अभिव्यक्ति\n      starts_with: बाट सुरु हुन्छ\n    negation: मिल्दो मिल्दो\n    or: अथवा\n    remove: ब्लक मेट्नुहोस्\n    type:\n      class: कक्षा\n      exe: Exe\n      path: बाटो\n      title: शीर्षक\n  import: आयात\n  import_full: अनुप्रयोग द्वारा सेटिंग्स आयात गर्नुहोस्\n  new: नंया\n  search: खोजी\n  swap: स्वप गर्नु\ncancel: रद्द गर्नु\nclose: घनिष्ट\ndelete: मेटाउन\ndevtools:\n  app_folders: निल्डर\n  custom_config_file: कस्टम कन्फिगरि फाईल लोड गर्नुहोस्\n  data_folder: डेटा फोल्डर\n  enable: विकासकर्ता उपकरणहरू सक्षम गर्नुहोस्\n  install_folder: स्थापना फोल्डर\n  load: बोभ्क\n  settings_file: सेटिंग्स फाइल\n  simulate_perm:\n    label: विजेट अनुमति अनुरोध सिमुलेट\n    result_allowed: ✓ अनुमति दिइएको छ\n    result_denied: ✗ अनुमति अस्वीकार गरियो\n    trigger: अनुकरण गर्नुहोस्\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: स्पष्ट प्रणाली आइकनहरू क्यास\n  clear_icons_tooltip: 'सबै विजेटहरूमा पूर्ण रूपमा प्रभाव लिनको लागि एक पुन: सुरू गर्न आवश्यक पर्दछ'\n  exit: छोड्नुहोस् / बाहिर निस्कनुहोस्\n  links: आधिकारिक लिंकहरू\n  relaunch: पुनःस्था\n  version: रुपान्तर\n  version_fixed: >-\n    अनुप्रयोग र WebView2 रनटाइम संस्करणहरू निश्चित छन्। यसको मतलब यो हो कि\n    अनुप्रयोगले अद्यावधिकहरू प्राप्त गर्दैन र WebView2 रनटाइम स्वचालित रूपमा\n    Windows अद्यावधिकहरूसँग अद्यावधिक हुनेछैन।\ngeneral:\n  accent_color: हिम्मता रंग\n  date_format: मिति ढाँचा\n  date_format_how_to: मिति ढाँचा कसरी लेख्ने?\n  hardware_acceleration: हार्डवेयर एक्सेलेरेशन\n  hardware_acceleration_description: >-\n    हार्डवेयर एक्सेलेरेशन असक्षम गर्नाले मेमोरीको प्रयोग घटाउनेछ, तर\n    कार्यसम्पादन समस्याहरू निम्त्याउन सक्छ। यदि तपाइँ लाइभ वालपेपर प्रयोग\n    गर्नुहुन्न भने असक्षम गर्न सुरक्षित छ।\n  icon_pack:\n    available: उपलब्ध आइकन प्याकहरू\n    selected: सक्रिय आइकन प्याकहरू\n  language: भाषा\n  monday: सोमबार\n  performance_mode:\n    on_battery: ब्याट्री मा\n    on_energy_saver: ऊर्जा सेभरमा\n    options:\n      disabled: पंगु\n      extreme: अत्यधिक\n      minimal: न्यूनतम\n    plugged: प्लग वा चार्ज गर्दै\n  polling_interval: प्रणाली मतदान अन्तराल\n  polling_interval_description: >-\n    कति पटक (सेकेन्डमा) Seelen UI ले CPU, RAM, नेटवर्क, र डिस्क गतिविधि जस्ता\n    प्रणाली स्रोतहरू जाँच गर्दछ। सानो संख्याको अर्थ धेरै बारम्बार अद्यावधिकहरू,\n    तर थोरै उच्च स्रोत उपयोग।\n  saturday: शनिबार\n  start_of_week: हप्ताको सुरुवात\n  startup: स्टार्टअपमा भाग्नुहोस्?\n  sunday: आइतबार\n  theme:\n    available: उपलब्ध विषयवस्तुहरू\n    selected: सक्रिय विषयवस्तुहरू\nheader:\n  labels:\n    config: कन्फिगरेसनहरू\n    developer: विकासकर्ताहरूको लागि\n    extras: अतिरिक्त\n    general: जर्नेल\n    home: घर\n    icon_pack_editor: Comed आईकनहरू\n    iconpack: आइकन प्याक\n    monitors: मोनिटरहरू\n    plugin: निशम्न\n    resources: वस्तुजार\n    shortcuts: सर्टकटहरू\n    soundpack: ध्वनि प्याक\n    specific_apps: अनुप्रयोग द्वारा सेटिंग्स\n    theme: विषयवस्तु\n    virtual_desk: भर्चुअल डेस्कटपहरू\n    wallpaper: वालपेपरहरू\n    widget: विजेटहरू\nhome:\n  new_resources: नयाँ स्रोतहरू\ninherit: बाबु बाजेबाट पाउनु\ninProgress: प्रगति हुदैछ...\ninsert: घुसाउनु\nloading: लोड गर्दै ...\nmiscellaneous: विविघ\nmonitors_configurations:\n  label: मोनिटर \\ inchtax}}\nmore: अरु\n'no': छैन\nopen: खोल्नु\nquit: छोड्नु\nremove: हटाउनु\nreset_all_to_default: सबै पूर्वनिर्धारित मानहरूमा रिसेट गर्नुहोस्\nreset_to_default: पूर्वनिर्धारित मानमा रिसेट गर्नुहोस्\nresources:\n  app_outdated: >-\n    यो स्रोतको आवश्यकता छ हेर्नुहोस् UI लाई राम्रोसँग काम गर्नको लागि नयाँ\n    संस्करण आवश्यक छ।\n  corrupted_wallpaper: थम्बनेल निकाल्न असफल भयो - भ्रष्ट वा असमर्थित भिडियो ढाँचा\n  delete: स्रोत मेट्नुहोस्\n  discover: अधिक स्रोतहरू पत्ता लगाउनुहोस्\n  has_update: एक अपडेट उपलब्ध छ\n  high_impact: प्रदर्शन मा उच्च प्रभाव\n  import_wallpapers: स्थानीय वालपेपर आयात गर्नुहोस्\n  open_folder: ओपन स्रोत फोल्डर\n  outdated: >-\n    यो स्रोत देख्न नहुने ज्ञात संस्करणको लागि डिजाइन गरिएको हो र राम्रोसँग काम\n    नगर्न सक्छ।\n  see_on_website: वेबसाइटमा हेर्नुहोस्\nreview_request:\n  not_now: अहिले होइन\n  sure: पक्का!\n  title: Seelen UI को मजा लिदै हुनुहुन्छ?\nsave: बचाउनु\nsave_and_restart: 'बचत गर्नुहोस् र पुन: सुरु गर्नुहोस्'\nsearch: खोज्नुहोस्\nsee_more: अझ हेर्नुहोस्\nshortcuts:\n  duplicate_error: यो सर्टकट दोहोरिएको छ\n  enable: निर्मित सर्टकट प्रणाली सक्षम गर्नुहोस्\n  enable_tooltip: >-\n    यदि तपाईं देखिने UI ग्राहक प्रयोग गरेर तपाईंको आफ्नै सर्टकट प्रणाली लागू\n    गर्नुहुन्छ भने असक्षम गर्नुहोस्\n  labels:\n    create_new_workspace: नयाँ कार्यस्थान सिर्जना गर्नुहोस्\n    cycle_stack_next: साइकल स्ट्याक अर्को\n    cycle_stack_prev: चक्र स्ट्याक अघिल्लो\n    cycle_wallpaper_next: अर्को वालपेपरमा परिवर्तन गर्नुहोस्\n    cycle_wallpaper_prev: अघिल्लो वालपेपरमा परिवर्तन गर्नुहोस्\n    decrease_height: उचाई घटाउनुहोस्\n    decrease_width: चौडाई कम\n    destroy_current_workspace: वर्तमान कार्यक्षेत्र नष्ट गर्नुहोस्\n    focus_bottom: फोकस तल\n    focus_latest: पछिल्लो ध्यान दिनुहोस्\n    focus_left: फर्काउनुहोस्\n    focus_right: ध्यान केन्द्रित गर्नुहोस्\n    focus_top: फोकस शीर्ष\n    increase_height: उचाइ बढाउनुहोस्\n    increase_width: चौडाई बढाउनुहोस्\n    misc_force_quit: बल छोडियो\n    misc_force_restart: साउसाट\n    misc_open_settings: खुला सेटिंग्स\n    misc_toggle_lock_tracing: टगल लक ट्रेसिंग (लग)\n    misc_toggle_win_event_tracing: टगल जीत घटना ट्रेसिंग (लग)\n    move_to_workspace: कार्यक्षेत्रमा जानुहोस् {0}}\n    move_window_down: विन्डो तल सार्नुहोस्\n    move_window_left: बाँयामा विन्डो सार्नुहोस्\n    move_window_right: विन्डोलाई दायाँ सार्नुहोस्\n    move_window_up: विन्डो शीर्षमा सार्नुहोस्\n    pause_tiling: टेल टेल विन्डो प्रबन्धक रोक्नुहोस्\n    reserve_bottom: रिजर्व तल\n    reserve_float: रिजर्भ फ्लोट\n    reserve_left: त्यागी बायाँ\n    reserve_right: रिजर्व सहि\n    reserve_stack: रिजर्व स्ट्याक\n    reserve_top: रिजर्व शीर्ष\n    restore_sizes: आकार पुनर्स्थापना गर्नुहोस्\n    send_to_workspace: कार्यक्षेत्रमा पठाउनुहोस् {0}}}\n    start_weg_app: फोकस वा सुरू गर्नुहोस् {0} 0}}\n    switch_to_next_workspace: अर्को कार्यक्षेत्रमा स्विच गर्नुहोस्\n    switch_to_previous_workspace: अघिल्लो कार्यक्षेत्रमा स्विच गर्नुहोस्\n    switch_workspace: कार्यक्षेत्रमा स्विच {0}}\n    toggle_float: टगलल विन्डो फ्लोट मोड\n    toggle_monocle: टगलस्प्लिप मोयर्सिल मोनोकल मोड\n  readonly_tooltip: यो एक पढ्न-मात्र सर्टकट हो\n  reset: पूर्वनिर्धारित गर्न रिसेट गर्नुहोस्\nsides:\n  bottom: पिंध\n  left: देब्रे\n  right: ठिक\n  top: टुप्पो\ntoolbar:\n  auto_hide: 'स्वत: लुकाउने'\n  delay_to_hide: लुकाउन ढिलाइ\n  delay_to_show: देखाउन ढिलाइ\n  dock_side: स्थिति\n  enable: फैन्सी उपकरणपट्टी सक्षम गर्नुहोस्\n  hide_mode:\n    always: सधैं\n    never: कहिल्यै\n    on_overlap: ओभरल्यापमा\n  item_size: वस्तु आकार\n  label: औजारपरो\n  margin: मार्जिन साइज\n  padding: प्याडिङ साइज\n  placeholder: {}\nupdate:\n  available: अपडेट उपलब्ध!\n  channel: अद्यावधिक च्यानल\n  downloading: डाउनलोड गर्दै ...\nwall:\n  backgrounds: वालपेपरहरू\n  blur: स्वरर्नु\n  cancel: रद्द गर्नुहोस्\n  close: बन्द गर्नुहोस्\n  collection_name: संग्रहको नाम\n  collections: सङ्ग्रहहरू\n  contrast: विषमता\n  corrupted_wallpapers_message: 'भ्रष्ट वालपेपरहरू, यी मेटाउने विचार गर्नुहोस्:'\n  create: सिर्जना गर्नुहोस्\n  create_collection: संग्रह सिर्जना गर्नुहोस्\n  default_collection: पूर्वनिर्धारित संग्रह\n  delete_collection: संग्रह मेटाउनुहोस्\n  edit_collection: सङ्ग्रह सम्पादन गर्नुहोस्\n  enable: वालपेपर प्रबन्धक सक्षम गर्नुहोस्\n  extend: प्राथमिक विस्तार गर्नुहोस्\n  fit:\n    contain: भित्र हुनु\n    cover: आवरण\n    fill: भर्नु\n  flipHorizontal: फ्लिप तेर्सो\n  flipVertical: फ्लिप ठाडो\n  generating_thumbnails: वालपेपर थम्बनेलहरू उत्पन्न गर्दै\n  hours: घण्टा\n  interval: वालपेपर परिवर्तन गर्नुहोस्\n  minutes: मिनेट\n  monitor_collection: मोनिटर संग्रह\n  multimonitor_behaviour: मल्टिमोनिटर व्यवहार\n  muted: भिडियो अडियो म्युट गर्नुहोस्\n  no_background: खाली स्लाइडजेशन, यसको सट्टा थिमको पृष्ठभूमि प्रयोग गर्दै।\n  no_collections: >-\n    अहिलेसम्म कुनै सङ्ग्रहहरू सिर्जना गरिएको छैन। एउटा सिर्जना गर्न + बटन क्लिक\n    गर्नुहोस्।\n  objectFit: पृष्ठभूमि फिट\n  objectPosition: पृष्ठभूमि स्थिति\n  overlayColor: ओभरलाइ रंग\n  overlayMixBlendMode: ओभरले मिक्स मिश्रण मोड\n  per_monitor: प्रति मोनिटर\n  playback: प्लेब्याक गति\n  position:\n    bottom: पिंध\n    center: केन्द्र\n    left: देब्रे\n    right: ठिक\n    top: टुप्पो\n  processing_video: भिडियो प्रशोधन गर्दै\n  random: अनियमित रूपमा स्लाइड्स स्लाइड\n  saturation: संतृप्ति\n  seconds: सेकेन्ड\n  select_collection: संग्रह चयन गर्नुहोस्\n  thumbnail_generation_complete: थम्बनेल जेनेरेसन पूरा भयो\n  thumbnail_generation_finished: थम्बनेल उत्पादन सफलतापूर्वक समाप्त भयो\n  wallpaper_collection: वालपेपर संग्रह\n  wallpaper_settings: वालपेपर सेटिङहरू\n  withOverlay: ओभरले साथ\n  workspace_collections: कार्यक्षेत्र संग्रहहरू\nweg:\n  auto_hide: 'स्वत: लुकाउने'\n  delay_to_hide: लुकाउन ढिलाइ\n  delay_to_show: देखाउन ढिलाइ\n  dock_side: स्थिति\n  enable: डक / टास्कबार सक्षम गर्नुहोस्\n  filtering: वस्तु फिल्टरिंग\n  gap: अन्तराल\n  hide_mode:\n    always: सधैं\n    never: कहिल्यै\n    on_overlap: ओभरल्यापमा\n  items:\n    gap: वस्तुहरू बीचको ठाउँ\n    label: वस्तुहरू\n    pinned_visibility:\n      always: सधैं\n      label: पिन गरिएका वस्तुहरू दृश्यता\n      when_primary: जब मनिटर प्राथमिक छ\n    show_instance_counter: खुला विन्डोज काउन्टर देखाउनुहोस्\n    show_window_title: खुला विन्डो शीर्षक देखाउनुहोस् (केवल क्षैतिज)\n    size: वस्तुको आकार\n    split_windows: विभाजन विन्डोज (प्रति विन्डो एक वस्तु)\n    temporal_visibility:\n      all: सबै\n      label: अनपिन गरिएका वस्तुहरूको दृश्यता\n      on_monitor: मोनिटरमा\n    visible_separators: दृश्य बिल्कुलरहरू\n  label: डक / टास्कबार\n  margin: स्पानलिन\n  mode:\n    full_width: पूर्ण स्क्रिन चौडाइ\n    min_content: हुनसक्ने सानो\n  padding: प्याडडि .्वन\n  show_end_task: कार्यबारमा अन्त कार्य देखाउनुहोस्\n  width: चौडाई\nwelcome:\n  give_a_review: एक समीक्षा दिनुहोस्\n  message: >-\n    Seelen UI Windows को लागी एक नि: शुल्क र खुला स्रोत डेस्कटप वातावरण हो। यहाँ\n    तपाइँ तपाइँको डेस्कटप कस्तो देखिन्छ र व्यवहार गर्छ भन्ने मा पूर्ण नियन्त्रण\n    छ, तपाइँ तपाइँको कार्यप्रवाह र शैली संग मेल खाने हरेक विवरण लाई अनुकूलित\n    गर्न अनुमति दिदै।\n  ok: सुरु गरौं!\n  review: >-\n    यदि तपाइँ Seelen UI प्रयोग गरेर रमाइलो गर्नुहुन्छ भने, स्टोरमा समीक्षा\n    छोड्ने विचार गर्नुहोस् — तपाइँको प्रतिक्रियाले परियोजनालाई बढ्न र सुधार गर्न\n    मद्दत गर्दछ।\n  title: Seelen UI मा स्वागत छ!\nwidget:\n  enable: यो विजेट सक्षम गर्नुहोस्\n  enable_for_monitor: यस मोनिटरमा सक्षम गर्नुहोस्\n  instances: संकेतहरू\nwm:\n  animations:\n    duration: एनिमेसन अवधि (एमएस)\n    ease_function: एनिमेसन स्टाइनिंग प्रकार्य\n    enable: विन्डोको एनिमेसन सक्षम गर्नुहोस्\n  author: लेखिका\n  border:\n    enable: विन्डोको सीमा सक्षम गर्नुहोस्\n    offset: सीमा अफसेट\n    width: सीमा चौडाई\n  description: वर्णन\n  drag_behavior: तान्नुहोस् व्यवहार\n  drag_behavior_options:\n    sort: 'क्रमबद्ध गर्नुहोस् (तान्दा विन्डोहरू पुन: क्रमबद्ध गर्नुहोस्)'\n    swap: स्वैप (ड्र्याग एन्डमा विन्डोज स्वैप)\n  enable: टलिंग विन्डो प्रबन्धक सक्षम गर्नुहोस्\n  layout: नक्शा\n  resize_delta: रिसाइज डेल्टा (%)\n  space_between_containers: कन्टेनर बीचको ठाउँ\n  workspace_offset: कार्यस्पेस अफसेट (मार्जिन)\n  workspace_padding: कार्यक्षेत्र प्याडिंग\n'yes': हुन्छ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/nl.yml",
    "content": "action:\n  confirm: Weet je het zeker?\n  confirm_body: Deze actie kan niet ongedaan worden gemaakt.\napps_configurations:\n  app:\n    bindings: Binding (beide opties zijn vereist)\n    category: Categorie\n    category_placeholder: Geen\n    monitor: Monitor\n    monitor_placeholder: Geen\n    name: Naam\n    ok_create: Creëren\n    ok_edit: Update\n    ok_readonly: Bewerken als nieuw\n    options:\n      NoInteractive: Geen interactief\n      VdPinned: Toon in alle werkruimten\n      WmFloat: Twm - Begin met zweven\n      WmForce: Twm - Forceer beheer\n      WmUnmanage: Twm - Niet beheren\n    options_label: Extra opties\n    title_create: Het maken van {{name}}\n    title_edit: Bewerken {{name}}\n    title_readonly: '{{{Name}} bekijken'\n    weg_options_label: Dock/taakbalkopties\n    wm_options_label: Opties voor vensterbeheerder\n    workspace: Werkruimte\n    workspace_placeholder: Geen\n  bundled_msg: >-\n    Deze gebundelde configuraties zijn niet bewerkbaar en zijn ontworpen om u de\n    beste ervaring zonder aanpassing te bieden. Ze configureren automatisch de\n    meest voorkomende toepassingen voor u.\n  bundled_title: App -configuratie gebundeld met Seelen\n  confirm_delete: Weet u zeker dat u deze configuratie(s) wilt verwijderen?\n  confirm_delete_title: Bevestig verwijderen\n  delete: Verwijderen\n  export: Exporteren\n  export_full: Instellingen exporteren per toepassing\n  extra_info: >-\n    Seelen UI gebruikt slechts één ID per app (eerste gevonden match), dus de\n    volgorde waarin ze worden gespecificeerd is belangrijk. De laatst\n    toegevoegde krijgt prioriteit, aangezien de tabel standaard wordt gesorteerd\n    van nieuwste naar oude.\n  identifier:\n    add_block: Block toevoegen\n    and: EN\n    id: Identificatie\n    kind: Identificeren door\n    matching_strategy: Matching -strategie\n    matching_strategy_option:\n      contains: Bevat\n      ends_with: Eindigt met\n      equals: Gelijk aan\n      regex: Reguliere expressie\n      starts_with: Begint met\n    negation: Ontkennen matching\n    or: OF\n    remove: Verwijder blok\n    type:\n      class: Klas\n      exe: Ex\n      path: Pad\n      title: Titel\n  import: Importeren\n  import_full: Instellingen importeren per toepassing\n  new: Nieuw\n  search: Zoekopdracht\n  swap: Ruilen\ncancel: Annuleren\nclose: Sluiten\ndelete: Verwijderen\ndevtools:\n  app_folders: App -mappen\n  custom_config_file: Laad het aangepaste configuratiebestand\n  data_folder: Gegevensmap\n  enable: Schakel ontwikkelaarstools in\n  install_folder: Installatiemap\n  load: Laden\n  settings_file: Instellingenbestand\n  simulate_perm:\n    label: Simuleer het verzoek om widgettoestemming\n    result_allowed: ✓ Toestemming verleend\n    result_denied: ✗ Toestemming geweigerd\n    trigger: Simuleren\n    widget_id_placeholder: '@auteur/widgetnaam'\nextras:\n  clear_icons: Systeempictogrammen cache wissen\n  clear_icons_tooltip: Er kan een herstart nodig zijn om volledig effect te hebben op alle widgets\n  exit: Stop/exit\n  links: Officiële links\n  relaunch: Opnieuw starten\n  version: Versie\n  version_fixed: >-\n    De applicatie- en WebView2 Runtime-versies staan ​​vast. Dit betekent dat de\n    applicatie geen updates ontvangt en dat de WebView2 Runtime niet automatisch\n    wordt bijgewerkt met Windows-updates.\ngeneral:\n  accent_color: Accent kleur\n  date_format: Datumformaat\n  date_format_how_to: Hoe schrijf je een datumformaat?\n  hardware_acceleration: Hardwareversnelling\n  hardware_acceleration_description: >-\n    Het uitschakelen van hardwareversnelling vermindert het geheugengebruik,\n    maar kan prestatieproblemen veroorzaken. Is veilig uit te schakelen als u\n    geen live achtergronden gebruikt.\n  icon_pack:\n    available: Beschikbare pictogrampakketten\n    selected: Actieve pictogrampakketten\n  language: Taal\n  monday: Maandag\n  performance_mode:\n    on_battery: Op de batterij\n    on_energy_saver: Op energiebespaarder\n    options:\n      disabled: Gehandicapt\n      extreme: Extreem\n      minimal: Minimaal\n    plugged: Aangesloten of opladen\n  polling_interval: Systeem polling-interval\n  polling_interval_description: >-\n    Hoe vaak (in seconden) Seelen UI systeembronnen zoals CPU, RAM, netwerk en\n    schijfactiviteit controleert. Een kleiner aantal betekent frequentere\n    updates, maar een iets hoger bronnengebruik.\n  saturday: Zaterdag\n  start_of_week: Begin van de week\n  startup: Uitvoeren bij het starten?\n  sunday: Zondag\n  theme:\n    available: Beschikbare thema's\n    selected: Actieve thema's\nheader:\n  labels:\n    config: Configuraties\n    developer: Voor ontwikkelaars\n    extras: Extra's\n    general: Algemeen\n    home: Thuis\n    icon_pack_editor: Gecachte pictogrammen\n    iconpack: Pictogrammenpakketten\n    monitors: Monitors\n    plugin: Plugins\n    resources: Bronnen\n    shortcuts: Snelkoppelingen\n    soundpack: Geluidspakketten\n    specific_apps: Instellingen per toepassing\n    theme: Thema's\n    virtual_desk: Virtuele desktops\n    wallpaper: Behang\n    widget: Widgets\nhome:\n  new_resources: Nieuwe bronnen\ninherit: Erven\ninProgress: Bezig...\ninsert: Invoegen\nloading: Bezig met laden...\nmiscellaneous: Gemengd\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Meer\n'no': Nee\nopen: Open\nquit: Afsluiten\nremove: Verwijderen\nreset_all_to_default: Alles resetten naar standaardwaarden\nreset_to_default: Terugzetten naar standaardwaarde\nresources:\n  app_outdated: Deze bron vereist een nieuwere versie van Seelen UI om goed te werken.\n  corrupted_wallpaper: Kan miniatuur niet extraheren - beschadigd of niet-ondersteund videoformaat\n  delete: Verwijder resource\n  discover: Ontdek meer bronnen\n  has_update: Er is een update beschikbaar\n  high_impact: Grote impact op de prestaties\n  import_wallpapers: Lokale achtergronden importeren\n  open_folder: Map met bronnen openen\n  outdated: >-\n    Deze bron is ontworpen voor een oudere versie van Seelen UI en werkt\n    mogelijk niet goed.\n  see_on_website: Zie op website\nreview_request:\n  not_now: Niet nu\n  sure: Zeker!\n  title: Geniet je van de Seelen-gebruikersinterface?\nsave: Opslaan\nsave_and_restart: Opslaan en opnieuw opstarten\nsearch: Zoekopdracht\nsee_more: Meer zien\nshortcuts:\n  duplicate_error: Deze snelkoppeling is gedupliceerd\n  enable: Schakel ingebouwd snelkoppelsysteem in\n  enable_tooltip: >-\n    Schakel uit of u uw eigen snelkoppelsysteem implementeert met behulp van de\n    Selen UI -client\n  labels:\n    create_new_workspace: Maak een nieuwe werkruimte\n    cycle_stack_next: Cycle Stack Volgende\n    cycle_stack_prev: Cyclusstapel vorige\n    cycle_wallpaper_next: Verander naar het volgende behang\n    cycle_wallpaper_prev: Verander naar het vorige achtergrond\n    decrease_height: De hoogte verminderen\n    decrease_width: Breedte afnemen\n    destroy_current_workspace: Vernietig de huidige werkruimte\n    focus_bottom: Focusbodem\n    focus_latest: Focus nieuwste\n    focus_left: Focus links\n    focus_right: Focus goed\n    focus_top: Focus Top\n    increase_height: Verhoog de hoogte\n    increase_width: Verhoog de breedte\n    misc_force_quit: Force stoppen\n    misc_force_restart: Opnieuw opstarten forceren\n    misc_open_settings: Open instellingen\n    misc_toggle_lock_tracing: Schakelvergrendeling traceren (logboeken)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (Logs)\n    move_to_workspace: Ga naar de werkruimte {{0}}\n    move_window_down: Verplaats venster naar beneden\n    move_window_left: Verplaats het venster naar links\n    move_window_right: Verplaats venster naar rechts\n    move_window_up: Verplaats het venster naar boven\n    pause_tiling: Pauzeer Tiling Window Manager\n    reserve_bottom: Reserve bodem\n    reserve_float: Reserve Float\n    reserve_left: Reserveer links\n    reserve_right: Reserve recht\n    reserve_stack: Reservetapel\n    reserve_top: Reserveer top\n    restore_sizes: Herstel de maten\n    send_to_workspace: Verzenden naar werkruimte {{0}}\n    start_weg_app: Focus of start applicatie {{0}}\n    switch_to_next_workspace: Schakel over naar de volgende werkruimte\n    switch_to_previous_workspace: Schakel over naar vorige werkruimte\n    switch_workspace: Schakel over naar werkruimte {{0}}\n    toggle_float: Schakelvenster Float -modus\n    toggle_monocle: Schakel de werkruimte Monocle -modus\n  readonly_tooltip: Dit is een alleen-lezen snelkoppeling\n  reset: Reset naar standaardwaarden\nsides:\n  bottom: Onderkant\n  left: Links\n  right: Rechts\n  top: Bovenkant\ntoolbar:\n  auto_hide: Automatisch verbergen\n  delay_to_hide: Vertraging om te verbergen\n  delay_to_show: Vertraging om te tonen\n  dock_side: Positie\n  enable: Schakel fancy werkbalk in\n  hide_mode:\n    always: Altijd\n    never: Nooit\n    on_overlap: Op overlap\n  item_size: Artikelgrootte\n  label: Werkbalk\n  margin: Margegrootte\n  padding: Maat vulling\n  placeholder: {}\nupdate:\n  available: Update beschikbaar!\n  channel: Updatekanaal\n  downloading: Downloaden ...\nwall:\n  backgrounds: Achtergronden\n  blur: Vervagen\n  cancel: Annuleren\n  close: Dichtbij\n  collection_name: Collectienaam\n  collections: Collecties\n  contrast: Contrast\n  corrupted_wallpapers_message: 'Beschadigde achtergronden, overweeg deze te verwijderen:'\n  create: Creëren\n  create_collection: Verzameling maken\n  default_collection: Standaardcollectie\n  delete_collection: Verzameling verwijderen\n  edit_collection: Verzameling bewerken\n  enable: Schakel wallpaper manager in\n  extend: Primair uitbreiden\n  fit:\n    contain: Bevat\n    cover: Omslag\n    fill: Vullen\n  flipHorizontal: Flip Horizontaal\n  flipVertical: Verticaal klappen\n  generating_thumbnails: Achtergrondminiaturen genereren\n  hours: uren\n  interval: Verander wallpaper elke\n  minutes: notulen\n  monitor_collection: Monitor-collectie\n  multimonitor_behaviour: Multimonitor-gedrag\n  muted: Video-audio dempen\n  no_background: >-\n    Lege diavoorstelling, gebruikt in plaats daarvan de achtergrond van het\n    thema.\n  no_collections: >-\n    Er zijn nog geen collecties aangemaakt. Klik op de knop + om er een te\n    maken.\n  objectFit: Achtergrond\n  objectPosition: Achtergrond Positie\n  overlayColor: Overlay Kleur\n  overlayMixBlendMode: Overlay Mix Mengmodus\n  per_monitor: Per monitor\n  playback: Afspeelsnelheid\n  position:\n    bottom: Bodem\n    center: Midden\n    left: Links\n    right: Rechts\n    top: Top\n  processing_video: Video verwerken\n  random: Randomize diavoorstelling\n  saturation: Verzadiging\n  seconds: seconden\n  select_collection: Selecteer Verzameling\n  thumbnail_generation_complete: Het genereren van miniaturen is voltooid\n  thumbnail_generation_finished: Het genereren van miniaturen is met succes voltooid\n  wallpaper_collection: Behangcollectie\n  wallpaper_settings: Achtergrondinstellingen\n  withOverlay: Met overlay\n  workspace_collections: Werkruimtecollecties\nweg:\n  auto_hide: Automatisch Verbergen\n  delay_to_hide: Vertraging om te verbergen\n  delay_to_show: Vertraging om te tonen\n  dock_side: Positie\n  enable: Schakel dock/taakbalk in\n  filtering: Item filteren\n  gap: Gat\n  hide_mode:\n    always: Altijd\n    never: Nooit\n    on_overlap: Op overlap\n  items:\n    gap: Ruimte tussen items\n    label: Items\n    pinned_visibility:\n      always: Altijd\n      label: Zichtbaarheid van vastgezette items\n      when_primary: Wanneer de monitor primair is\n    show_instance_counter: Toon open vensters teller\n    show_window_title: Titel van geopend venster tonen (alleen horizontaal)\n    size: Item grootte\n    split_windows: Vensters splitsen (één item per venster)\n    temporal_visibility:\n      all: Alle\n      label: Zichtbaarheid van losgemaakte items\n      on_monitor: Op monitor\n    visible_separators: Zichtbare scheiders\n  label: Dock/taakbalk\n  margin: Marge\n  mode:\n    full_width: Volledige schermbreedte\n    min_content: Klein als het maar kan zijn\n  padding: Vulling\n  show_end_task: Taak beëindigen in taakbalk weergeven\n  width: Breedte\nwelcome:\n  give_a_review: Geef een recensie\n  message: >-\n    Seelen UI is een gratis en open source desktopomgeving voor Windows. Hier\n    heeft u volledige controle over hoe uw bureaublad eruitziet en zich\n    gedraagt, zodat u elk detail kunt aanpassen aan uw workflow en stijl.\n  ok: Laten we beginnen!\n  review: >-\n    Als u de Seelen UI graag gebruikt, overweeg dan om een ​​recensie achter te\n    laten in de winkel. Uw feedback helpt het project te groeien en te\n    verbeteren.\n  title: Welkom bij de Seelen-gebruikersinterface!\nwidget:\n  enable: Deze widget inschakelen\n  enable_for_monitor: Inschakelen op deze monitor\n  instances: Instanties\nwm:\n  animations:\n    duration: Animatieduur (MS)\n    ease_function: Animatie -versoepelingsfunctie\n    enable: Schakel Window's animaties in\n  author: Auteur\n  border:\n    enable: Schakel Window's Border in\n    offset: Grens offset\n    width: Grensbreedte\n  description: Beschrijving\n  drag_behavior: Sleepgedrag\n  drag_behavior_options:\n    sort: Sorteren (vensters opnieuw ordenen tijdens slepen)\n    swap: Wisselen (vensters verwisselen aan het einde van het slepen)\n  enable: Schakel Tiling Window Manager in\n  layout: Lay -out\n  resize_delta: Wijzig de wijzers van delta (%)\n  space_between_containers: Ruimte tussen containers\n  workspace_offset: Workspaces Offset (marges)\n  workspace_padding: Workspaces -vulling\n'yes': Ja\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/no.yml",
    "content": "action:\n  confirm: Er du sikker?\n  confirm_body: Denne handlingen kan ikke angres.\napps_configurations:\n  app:\n    bindings: Binding (Merk begge alternativene er påkrevd)\n    category: Kategori\n    category_placeholder: Ingen\n    monitor: Observere\n    monitor_placeholder: Ingen\n    name: Navn\n    ok_create: Skape\n    ok_edit: Oppdater\n    ok_readonly: Rediger som ny\n    options:\n      NoInteractive: Ingen interaktiv\n      VdPinned: Vis i alle arbeidsområder\n      WmFloat: Twm - Start flytende\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Ekstra alternativer\n    title_create: Opprette {{name}}\n    title_edit: Redigering {{name}}\n    title_readonly: Visning {{name}}\n    weg_options_label: Dock/oppgavelinjealternativer\n    wm_options_label: Vinduslederalternativer\n    workspace: Arbeidsområde\n    workspace_placeholder: Ingen\n  bundled_msg: >-\n    Disse samlede konfigurasjonene er ikke redigerbare og er designet for å gi\n    deg den beste opplevelsen uten tilpasning. De konfigurerer automatisk de\n    vanligste applikasjonene for deg.\n  bundled_title: App Config samlet med Seelen\n  confirm_delete: Er du sikker på at du vil slette denne konfigurasjonen/S?\n  confirm_delete_title: Bekreft slett\n  delete: Slett\n  export: Eksport\n  export_full: Eksportinnstillinger etter søknad\n  extra_info: >-\n    Seelen-brukergrensesnittet bruker kun én identifikator per app (første treff\n    funnet), så rekkefølgen i hvordan spesifikasjonene er viktig, det siste lagt\n    til vil bli prioritert, da merk at tabellen er sortert som standard fra\n    nyeste til gammel.\n  identifier:\n    add_block: Legg til blokkering\n    and: OG\n    id: Identifikator\n    kind: Identifisere av\n    matching_strategy: Matchende strategi\n    matching_strategy_option:\n      contains: Inneholder\n      ends_with: Ender med\n      equals: Er lik\n      regex: Regelmessig uttrykk\n      starts_with: Starter med\n    negation: Negere matching\n    or: ELLER\n    remove: Slett blokkering\n    type:\n      class: Klasse\n      exe: Exe\n      path: Sti\n      title: Tittel\n  import: Import\n  import_full: Importer innstillinger etter søknad\n  new: Ny\n  search: Søk\n  swap: Bytte\ncancel: Avbryt\nclose: Lukke\ndelete: Slett\ndevtools:\n  app_folders: App -mapper\n  custom_config_file: Last inn tilpasset konfigurasjonsfil\n  data_folder: Datamappen\n  enable: Aktiver utviklerverktøy\n  install_folder: Installasjonsmappe\n  load: Laste\n  settings_file: Innstillingsfilen\n  simulate_perm:\n    label: Simuler forespørsel om widgettillatelse\n    result_allowed: ✓ Tillatelse gitt\n    result_denied: ✗ Tillatelse nektet\n    trigger: Simuler\n    widget_id_placeholder: '@forfatter/widgetnavn'\nextras:\n  clear_icons: Klare systemikoner cache\n  clear_icons_tooltip: Det kan kreves en omstart for å tre i kraft alle widgeter\n  exit: Avslutt/avslutte\n  links: Offisielle lenker\n  relaunch: Relanseres\n  version: Versjon\n  version_fixed: >-\n    Applikasjonen og WebView2 Runtime-versjonene er fikset. Dette betyr at\n    applikasjonen ikke vil motta oppdateringer og WebView2 Runtime vil ikke\n    automatisk oppdateres med Windows-oppdateringer.\ngeneral:\n  accent_color: Aksentfarge\n  date_format: Datoformat\n  date_format_how_to: Hvordan skrive et datoformat?\n  hardware_acceleration: Maskinvareakselerasjon\n  hardware_acceleration_description: >-\n    Deaktivering av maskinvareakselerasjon vil redusere minnebruken, men kan\n    forårsake ytelsesproblemer. Er trygt å deaktivere hvis du ikke vil bruke\n    levende bakgrunnsbilder.\n  icon_pack:\n    available: Tilgjengelige ikonpakker\n    selected: Aktive ikonpakker\n  language: Språk\n  monday: mandag\n  performance_mode:\n    on_battery: På batteri\n    on_energy_saver: På energisparer\n    options:\n      disabled: Funksjonshemmet\n      extreme: Ekstrem\n      minimal: Minimal\n    plugged: Plugget eller lading\n  polling_interval: Systemavstemningsintervall\n  polling_interval_description: >-\n    Hvor ofte (i sekunder) Seelen UI sjekker systemressurser som CPU, RAM,\n    nettverk og diskaktivitet. Et mindre antall betyr hyppigere oppdateringer,\n    men litt høyere ressursbruk.\n  saturday: lørdag\n  start_of_week: Ukestart\n  startup: Kjør ved oppstart?\n  sunday: søndag\n  theme:\n    available: Tilgjengelige temaer\n    selected: Aktive temaer\nheader:\n  labels:\n    config: Konfigurasjoner\n    developer: For utviklere\n    extras: Ekstrautstyr\n    general: Generell\n    home: Hjem\n    icon_pack_editor: Bufrede ikoner\n    iconpack: Ikonpakker\n    monitors: Skjermer\n    plugin: Plugins\n    resources: Ressurser\n    shortcuts: Snarveier\n    soundpack: Lydpakker\n    specific_apps: Innstillinger etter søknad\n    theme: Temaer\n    virtual_desk: Virtuelle stasjonære maskiner\n    wallpaper: Bakgrunnsbilder\n    widget: Widgets\nhome:\n  new_resources: Nye ressurser\ninherit: Arve\ninProgress: I prosess...\ninsert: Sett inn\nloading: Laster ...\nmiscellaneous: Diverse\nmonitors_configurations:\n  label: Overvåke {{indeks}}\nmore: Flere\n'no': Ingen\nopen: Åpen\nquit: Slutte\nremove: Fjerne\nreset_all_to_default: Tilbakestill alt til standardverdier\nreset_to_default: Tilbakestill til standardverdi\nresources:\n  app_outdated: >-\n    Denne ressursen krever en nyere versjon av Seelen UI for å fungere\n    ordentlig.\n  corrupted_wallpaper: Kunne ikke pakke ut miniatyrbildet – ødelagt eller ikke støttet videoformat\n  delete: Slett ressurs\n  discover: Oppdag flere ressurser\n  has_update: En oppdatering er tilgjengelig\n  high_impact: Høy innvirkning på ytelsen\n  import_wallpapers: Importer lokale bakgrunnsbilder\n  open_folder: Åpne ressursmappe\n  outdated: >-\n    Denne ressursen ble designet for en eldre versjon av Seelen UI og fungerer\n    kanskje ikke ordentlig.\n  see_on_website: Se på nettsiden\nreview_request:\n  not_now: Ikke nå\n  sure: Sikker!\n  title: Liker du Seelen UI?\nsave: Lagre\nsave_and_restart: Lagre og start på nytt\nsearch: Søk\nsee_more: Se mer\nshortcuts:\n  duplicate_error: Denne snarveien er duplisert\n  enable: Aktiver innebygd snarveisystem\n  enable_tooltip: >-\n    Deaktiver om du implementerer ditt eget snarveisystem ved hjelp av Seelen UI\n    -klienten\n  labels:\n    create_new_workspace: Lag nytt arbeidsområde\n    cycle_stack_next: Syklusstabel neste\n    cycle_stack_prev: Syklusstabel tidligere\n    cycle_wallpaper_next: Bytt til neste bakgrunn\n    cycle_wallpaper_prev: Endre til tidligere bakgrunn\n    decrease_height: Reduser høyden\n    decrease_width: Redusere bredden\n    destroy_current_workspace: Ødelegg gjeldende arbeidsområde\n    focus_bottom: Fokus bunnen\n    focus_latest: Fokusere siste\n    focus_left: Fokus igjen\n    focus_right: Fokuser riktig\n    focus_top: Fokus topp\n    increase_height: Øk høyden\n    increase_width: Øke bredden\n    misc_force_quit: Force slutter\n    misc_force_restart: Force omstart på nytt\n    misc_open_settings: Åpne innstillinger\n    misc_toggle_lock_tracing: Toggle Lock Tracing (logger)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (logger)\n    move_to_workspace: Flytt til arbeidsområdet {{0}}\n    move_window_down: Flytt vinduet til bunnen\n    move_window_left: Flytt vinduet til venstre\n    move_window_right: Flytt vinduet til høyre\n    move_window_up: Flytt vinduet til toppen\n    pause_tiling: Pause flislegging av vindusbehandler\n    reserve_bottom: Reserve bunn\n    reserve_float: Reserve flyter\n    reserve_left: Reserve igjen\n    reserve_right: Reservere riktig\n    reserve_stack: Reserve stack\n    reserve_top: Reserve top\n    restore_sizes: Gjenopprett størrelser\n    send_to_workspace: Send til arbeidsområdet {{0}}\n    start_weg_app: Fokus eller start applikasjon {{0}}\n    switch_to_next_workspace: Bytt til neste arbeidsområde\n    switch_to_previous_workspace: Bytt til tidligere arbeidsområde\n    switch_workspace: Bytt til arbeidsområdet {{0}}\n    toggle_float: Veksle vindu float -modus\n    toggle_monocle: Veksle arbeidsområde monokelmodus\n  readonly_tooltip: Dette er en leselinje-snarvei\n  reset: Tilbakestill til standardverdier\nsides:\n  bottom: Bunn\n  left: Venstre\n  right: Ikke sant\n  top: Topp\ntoolbar:\n  auto_hide: Auto Hide\n  delay_to_hide: Utsett å skjule\n  delay_to_show: Forsinkelse med visning\n  dock_side: Stilling\n  enable: Aktiver fancy verktøylinje\n  hide_mode:\n    always: Alltid\n    never: Aldri\n    on_overlap: På overlapping\n  item_size: Varestørrelse\n  label: Verktøylinje\n  margin: Margstørrelse\n  padding: Polstring Størrelse\n  placeholder: {}\nupdate:\n  available: Oppdatering tilgjengelig!\n  channel: Oppdateringskanal\n  downloading: Last ned ...\nwall:\n  backgrounds: Bakgrunnsbilder\n  blur: Uskarpe\n  cancel: Kansellere\n  close: Lukke\n  collection_name: Samlingens navn\n  collections: Samlinger\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Ødelagte bakgrunnsbilder, vurder å slette disse:'\n  create: Skape\n  create_collection: Opprett samling\n  default_collection: Standard samling\n  delete_collection: Slett samling\n  edit_collection: Rediger samling\n  enable: Aktiver bakgrunnssjef\n  extend: Utvid primær\n  fit:\n    contain: Inneholde\n    cover: Dekke\n    fill: Fylle\n  flipHorizontal: Flip horisontalt\n  flipVertical: Flip vertikal\n  generating_thumbnails: Generer bakgrunnsminiatyrbilder\n  hours: timer\n  interval: Endre bakgrunnsbilde hver\n  minutes: minutter\n  monitor_collection: Overvåk samling\n  multimonitor_behaviour: Multimonitor-atferd\n  muted: Demp videolyd\n  no_background: Tom lysbildefremvisning, ved å bruke temaets bakgrunn i stedet.\n  no_collections: Ingen samlinger opprettet ennå. Klikk på +-knappen for å opprette en.\n  objectFit: Bakgrunns passform\n  objectPosition: Bakgrunnsposisjon\n  overlayColor: Overleggsfarge\n  overlayMixBlendMode: Overlegg blandingsmodus\n  per_monitor: Per skjerm\n  playback: Avspillingshastighet\n  position:\n    bottom: Bunn\n    center: Senter\n    left: Igjen\n    right: Høyre\n    top: Topp\n  processing_video: Behandler video\n  random: Randomiser lysbildefremvisning\n  saturation: Metning\n  seconds: sekunder\n  select_collection: Velg Samling\n  thumbnail_generation_complete: Generering av miniatyrbilder er fullført\n  thumbnail_generation_finished: Generering av miniatyrbilder er fullført\n  wallpaper_collection: Tapet samling\n  wallpaper_settings: Bakgrunnsinnstillinger\n  withOverlay: Med overlegg\n  workspace_collections: Arbeidsområdesamlinger\nweg:\n  auto_hide: Skjul automatisk\n  delay_to_hide: Utsett å skjule\n  delay_to_show: Forsinkelse med visning\n  dock_side: Stilling\n  enable: Aktiver dock/oppgavelinje\n  filtering: Varen filtrering\n  gap: Mellomrom\n  hide_mode:\n    always: Alltid\n    never: Aldri\n    on_overlap: På overlapping\n  items:\n    gap: Plass mellom varene\n    label: Gjenstander\n    pinned_visibility:\n      always: Alltid\n      label: Festede elementers synlighet\n      when_primary: Når monitoren er primær\n    show_instance_counter: Vis åpne Windows -teller\n    show_window_title: Vis Open Window Title (bare horisontalt)\n    size: Varestørrelse\n    split_windows: Delte vinduer (ett element per vindu)\n    temporal_visibility:\n      all: Alle\n      label: Synlighet for frigjorte elementer\n      on_monitor: På monitor\n    visible_separators: Synlige separatorer\n  label: Dock/oppgavelinje\n  margin: Margin\n  mode:\n    full_width: Full skjermbredde\n    min_content: Liten som kan være\n  padding: Polstring\n  show_end_task: Vis sluttoppgave i oppgavelinjen\n  width: Bredde\nwelcome:\n  give_a_review: Gi en anmeldelse\n  message: >-\n    Seelen UI er et gratis skrivebordsmiljø med åpen kildekode for Windows. Her\n    har du full kontroll over hvordan skrivebordet ditt ser ut og oppfører seg,\n    slik at du kan tilpasse hver detalj for å matche arbeidsflyten og stilen\n    din.\n  ok: La oss begynne!\n  review: >-\n    Hvis du liker å bruke Seelen UI, bør du vurdere å legge igjen en anmeldelse\n    i butikken – tilbakemeldingen din hjelper prosjektet med å vokse og forbedre\n    seg.\n  title: Velkommen til Seelen UI!\nwidget:\n  enable: Aktiver denne widgeten\n  enable_for_monitor: Aktiver på denne skjermen\n  instances: Forekomster\nwm:\n  animations:\n    duration: Animasjonsvarighet (MS)\n    ease_function: Animasjon lettelsesfunksjon\n    enable: Aktiver vinduets animasjoner\n  author: Forfatter\n  border:\n    enable: Aktiver vinduets grense\n    offset: Grenseforskyvning\n    width: Grensebredde\n  description: Beskrivelse\n  drag_behavior: Dra-atferd\n  drag_behavior_options:\n    sort: Sorter (omorganiser vinduer mens du drar)\n    swap: Bytt (bytt vinduer på dra-enden)\n  enable: Aktiver flislegging av vindusleder\n  layout: Oppsett\n  resize_delta: Endre størrelse på Delta (%)\n  space_between_containers: Plass mellom containere\n  workspace_offset: Arbeidsområder forskyvning (marginer)\n  workspace_padding: Arbeidsområder polstring\n'yes': Ja\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/pa.yml",
    "content": "action:\n  confirm: ਤੁਹਾਨੂੰ ਪੂਰਾ ਵਿਸ਼ਵਾਸ ਹੈ?\n  confirm_body: ਇਸ ਕਿਰਿਆ ਨੂੰ ਵਾਪਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ.\napps_configurations:\n  app:\n    bindings: ਬਾਈਡਿੰਗ (ਨੋਟ ਕਰੋ ਕਿ ਦੋਵੇਂ ਵਿਕਲਪ ਲੋੜੀਂਦੇ ਹਨ)\n    category: ਸ਼੍ਰੇਣੀ\n    category_placeholder: ਕੋਈ ਨਹੀਂ\n    monitor: ਮਾਨੀਟਰ\n    monitor_placeholder: ਕੋਈ ਨਹੀਂ\n    name: ਨਾਮ\n    ok_create: ਬਣਾਓ\n    ok_edit: ਅਪਡੇਟ\n    ok_readonly: ਨਵੇਂ ਵਜੋਂ ਸੰਪਾਦਿਤ ਕਰੋ\n    options:\n      NoInteractive: ਕੋਈ ਇੰਟਰਐਕਟਿਵ ਨਹੀਂ\n      VdPinned: ਸਾਰੇ ਵਰਕਸਪੇਸ ਵਿੱਚ ਦਿਖਾਓ\n      WmFloat: Twm - ਫਲੋਟਿੰਗ ਸ਼ੁਰੂ ਕਰੋ\n      WmForce: Twm - ਫੋਰਸ ਪ੍ਰਬੰਧਨ\n      WmUnmanage: Twm - ਅਪ੍ਰਬੰਧਿਤ\n    options_label: ਵਾਧੂ ਵਿਕਲਪ\n    title_create: '{{name}} ਬਣਾਉਣਾ'\n    title_edit: ਸੰਪਾਦਨ {ime neame}\n    title_readonly: '{{name}} ਦੇਖ ਰਹੇ ਹਾਂ'\n    weg_options_label: ਡੌਕ / ਟਾਸਕਬਾਰ ਚੋਣਾਂ\n    wm_options_label: ਵਿੰਡੋ ਮੈਨੇਜਰ ਦੇ ਵਿਕਲਪ\n    workspace: ਵਰਕਸਪੇਸ\n    workspace_placeholder: ਕੋਈ ਨਹੀਂ\n  bundled_msg: >-\n    ਇਹ ਬੰਡਲ ਕੀਤੀਆਂ ਸੰਰਚਨਾਵਾਂ ਸੰਪਾਦਨਯੋਗ ਨਹੀਂ ਹਨ ਅਤੇ ਤੁਹਾਨੂੰ ਅਨੁਕੂਲਿਤ ਕੀਤੇ ਬਿਨਾਂ\n    ਵਧੀਆ ਅਨੁਭਵ ਪ੍ਰਦਾਨ ਕਰਨ ਲਈ ਤਿਆਰ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ। ਉਹ ਤੁਹਾਡੇ ਲਈ ਸਭ ਤੋਂ ਆਮ\n    ਐਪਲੀਕੇਸ਼ਨਾਂ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਕੌਂਫਿਗਰ ਕਰਦੇ ਹਨ।\n  bundled_title: ਐਪ ਕੌਂਫਿਗ ਸੀਲੇਨ ਨਾਲ ਬੰਡਲ ਕੀਤੀ ਗਈ\n  confirm_delete: ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ 'ਤੇ ਇਸ ਸੰਰਚਨਾ/s ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?\n  confirm_delete_title: ਮਿਟਾਉਣ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ\n  delete: ਮਿਟਾਓ\n  export: ਨਿਰਯਾਤ\n  export_full: ਐਪਲੀਕੇਸ਼ਨ ਦੁਆਰਾ ਸੈਟਿੰਗਜ਼ ਨਿਰਯਾਤ ਕਰੋ\n  extra_info: ਵੇਖਣਯੋਗ UI ਪ੍ਰਤੀ ਐਪ ਨੂੰ ਸਿਰਫ ਇਕ ਪਛਾਣਕਰਤਾ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹਨ\n  identifier:\n    add_block: ਬਲਾਕ ਸ਼ਾਮਲ ਕਰੋ\n    and: ਅਤੇ\n    id: ਪਛਾਣਕਰਤਾ\n    kind: ਦੁਆਰਾ ਪਛਾਣੋ\n    matching_strategy: ਮੈਚਿੰਗ ਰਣਨੀਤੀ\n    matching_strategy_option:\n      contains: ਸ਼ਾਮਿਲ ਹੈ\n      ends_with: ਨਾਲ ਖਤਮ ਹੁੰਦਾ ਹੈ\n      equals: ਬਰਾਬਰ ਹੈ\n      regex: ਨਿਯਮਤ ਸਮੀਕਰਨ\n      starts_with: ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ\n    negation: ਨੈਗੇਟ ਮੈਚਿੰਗ\n    or: ਜਾਂ\n    remove: ਬਲਾਕ ਮਿਟਾਓ\n    type:\n      class: ਕਲਾਸ\n      exe: ਐਕਸ\n      path: ਮਾਰਗ\n      title: ਸਿਰਲੇਖ\n  import: ਆਯਾਤ ਕਰੋ\n  import_full: ਐਪਲੀਕੇਸ਼ਨ ਦੁਆਰਾ ਸੈਟਿੰਗਾਂ ਆਯਾਤ ਕਰੋ\n  new: ਨਵਾਂ\n  search: ਖੋਜ\n  swap: ਸਵੈਪ\ncancel: ਰੱਦ ਕਰੋ\nclose: ਨੇੜੇ\ndelete: ਮਿਟਾਓ\ndevtools:\n  app_folders: ਐਪ ਫੋਲਡਰ\n  custom_config_file: ਕਸਟਮ ਕੌਂਫਿਗ ਫਾਈਲ ਲੋਡ ਕਰੋ\n  data_folder: ਡਾਟਾ ਫੋਲਡਰ\n  enable: ਡਿਵੈਲਪਰ ਟੂਲਸ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ\n  install_folder: ਇੰਸਟਾਲੇਸ਼ਨ ਫੋਲਡਰ\n  load: ਲੋਡ ਕਰੋ\n  settings_file: ਸੈਟਿੰਗਜ਼ ਫਾਈਲ\n  simulate_perm:\n    label: ਵਿਜੇਟ ਅਨੁਮਤੀ ਬੇਨਤੀ ਦੀ ਨਕਲ ਕਰੋ\n    result_allowed: ✓ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਗਈ\n    result_denied: ✗ ਇਜਾਜ਼ਤ ਅਸਵੀਕਾਰ ਕੀਤੀ ਗਈ\n    trigger: ਸਿਮੂਲੇਟ\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: ਸਿਸਟਮ ਆਈਕਾਨ ਕੈਚੇ ਸਾਫ਼ ਕਰੋ\n  clear_icons_tooltip: ਸਾਰੇ ਵਿਜੇਟਸ 'ਤੇ ਪੂਰੀ ਤਰ੍ਹਾਂ ਲਾਗੂ ਹੋਣ ਲਈ ਇੱਕ ਰੀਸਟਾਰਟ ਦੀ ਜ਼ਰੂਰਤ ਹੋ ਸਕਦੀ ਹੈ\n  exit: ਛੱਡੋ/ਬਾਹਰ ਨਿਕਲੋ\n  links: ਅਧਿਕਾਰਤ ਲਿੰਕ\n  relaunch: ਮੁੜ-ਲਾਂਚ ਕਰੋ\n  version: ਸੰਸਕਰਣ\n  version_fixed: >-\n    ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ WebView2 ਰਨਟਾਈਮ ਸੰਸਕਰਣ ਸਥਿਰ ਹਨ। ਇਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਐਪਲੀਕੇਸ਼ਨ\n    ਅੱਪਡੇਟ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰੇਗੀ ਅਤੇ WebView2 ਰਨਟਾਈਮ ਨੂੰ ਵਿੰਡੋਜ਼ ਅੱਪਡੇਟ ਨਾਲ ਆਪਣੇ ਆਪ\n    ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ।\ngeneral:\n  accent_color: ਲਹਿਜ਼ਾ ਰੰਗ ਦਾ ਰੰਗ\n  date_format: ਤਾਰੀਖ ਦਾ ਫਾਰਮੈਟ\n  date_format_how_to: ਇੱਕ ਮਿਤੀ ਫਾਰਮੈਟ ਕਿਵੇਂ ਲਿਖਣਾ ਹੈ?\n  hardware_acceleration: ਹਾਰਡਵੇਅਰ ਪ੍ਰਵੇਗ\n  hardware_acceleration_description: >-\n    ਹਾਰਡਵੇਅਰ ਪ੍ਰਵੇਗ ਨੂੰ ਅਸਮਰੱਥ ਕਰਨ ਨਾਲ ਮੈਮੋਰੀ ਦੀ ਵਰਤੋਂ ਘੱਟ ਜਾਵੇਗੀ, ਪਰ ਪ੍ਰਦਰਸ਼ਨ\n    ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਪੈਦਾ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਜੇਕਰ ਤੁਸੀਂ ਲਾਈਵ ਵਾਲਪੇਪਰਾਂ ਦੀ ਵਰਤੋਂ ਨਹੀਂ\n    ਕਰਦੇ ਹੋ ਤਾਂ ਅਯੋਗ ਕਰਨਾ ਸੁਰੱਖਿਅਤ ਹੈ।\n  icon_pack:\n    available: ਉਪਲਬਧ ਆਈਕਨ ਪੈਕ\n    selected: ਐਕਟਿਵ ਆਈਕਨ ਪੈਕ\n  language: ਭਾਸ਼ਾ\n  monday: ਸੋਮਵਾਰ\n  performance_mode:\n    on_battery: ਬੈਟਰੀ ਤੇ\n    on_energy_saver: Energy ਰਜਾ ਸੇਵਰ ਤੇ\n    options:\n      disabled: ਅਯੋਗ\n      extreme: ਅੱਤ\n      minimal: ਘੱਟੋ ਘੱਟ\n    plugged: ਪਲੱਗ ਕੀਤਾ ਜਾਂ ਚਾਰਜ ਕਰਨਾ\n  polling_interval: ਸਿਸਟਮ ਪੋਲਿੰਗ ਅੰਤਰਾਲ\n  polling_interval_description: >-\n    ਕਿੰਨੀ ਵਾਰ (ਸਕਿੰਟਾਂ ਵਿੱਚ) ਸੀਲੇਨ UI CPU, RAM, ਨੈੱਟਵਰਕ, ਅਤੇ ਡਿਸਕ ਗਤੀਵਿਧੀ ਵਰਗੇ\n    ਸਿਸਟਮ ਸਰੋਤਾਂ ਦੀ ਜਾਂਚ ਕਰਦਾ ਹੈ। ਇੱਕ ਛੋਟੀ ਸੰਖਿਆ ਦਾ ਮਤਲਬ ਹੈ ਵਧੇਰੇ ਵਾਰ-ਵਾਰ\n    ਅੱਪਡੇਟ, ਪਰ ਥੋੜਾ ਵੱਧ ਸਰੋਤ ਵਰਤੋਂ।\n  saturday: ਸ਼ਨੀਵਾਰ\n  start_of_week: ਹਫ਼ਤੇ ਦੀ ਸ਼ੁਰੂਆਤ\n  startup: ਸਟਾਰਟਅੱਪ 'ਤੇ ਚਲਾਉਣਾ?\n  sunday: ਐਤਵਾਰ\n  theme:\n    available: ਉਪਲਬਧ ਥੀਮ\n    selected: ਕਿਰਿਆਸ਼ੀਲ ਥੀਮ\nheader:\n  labels:\n    config: ਸੰਰਚਨਾ\n    developer: ਡਿਵੈਲਪਰਾਂ ਲਈ\n    extras: ਵਾਧੂ\n    general: ਜਨਰਲ\n    home: ਘਰ\n    icon_pack_editor: ਕੈਸ਼ਡ ਆਈਕਾਨ\n    iconpack: ਆਈਕਾਨ ਪੈਕ\n    monitors: ਨਿਗਰਾਨੀ ਕਰਦਾ ਹੈ\n    plugin: ਪਲੱਗਇਨ\n    resources: ਸਰੋਤ\n    shortcuts: ਸ਼ਾਰਟਕੱਟ\n    soundpack: ਧੁਨੀ ਪੈਕ\n    specific_apps: ਐਪਲੀਕੇਸ਼ਨ ਦੁਆਰਾ ਸੈਟਿੰਗਾਂ\n    theme: ਥੀਮ\n    virtual_desk: ਵਰਚੁਅਲ ਡੈਸਕਟਾਪ\n    wallpaper: ਵਾਲਪੇਪਰ\n    widget: ਵਿਜੇਟਸ\nhome:\n  new_resources: ਨਵੇਂ ਸਰੋਤ\ninherit: ਵਿਰਾਸਤ\ninProgress: ਤਰੱਕੀ ਹੋ ਰਹੀ ਹੈ...\ninsert: ਸੰਮਿਲਿਤ ਕਰੋ\nloading: ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\nmiscellaneous: ਫੁਟਕਲ\nmonitors_configurations:\n  label: ਨਿਗਰਾਨੀ {{ਇੰਡੈਕਸ}\nmore: ਹੋਰ\n'no': ਨਹੀਂ\nopen: ਖੋਲ੍ਹੋ\nquit: ਛੱਡੋ\nremove: ਹਟਾਓ\nreset_all_to_default: ਸਾਰੇ ਡਿਫੌਲਟ ਮੁੱਲਾਂ ਤੇ ਰੀਸੈਟ ਕਰੋ\nreset_to_default: ਡਿਫੌਲਟ ਵੈਲਯੂ ਤੇ ਰੀਸੈਟ ਕਰੋ\nresources:\n  app_outdated: ਇਸ ਸਰੋਤ ਲਈ ਸਹੀ ਤਰ੍ਹਾਂ ਕੰਮ ਕਰਨ ਲਈ ਇੱਕ ਨਵੇਂ ਸੰਸਕਰਣ ਦੀ ਜ਼ਰੂਰਤ ਹੈ.\n  corrupted_wallpaper: ਥੰਬਨੇਲ ਨੂੰ ਐਕਸਟਰੈਕਟ ਕਰਨ ਵਿੱਚ ਅਸਫਲ - ਖਰਾਬ ਜਾਂ ਅਸਮਰਥਿਤ ਵੀਡੀਓ ਫਾਰਮੈਟ\n  delete: ਸਰੋਤ ਨੂੰ ਮਿਟਾਓ\n  discover: ਹੋਰ ਸਰੋਤਾਂ ਦੀ ਖੋਜ ਕਰੋ\n  has_update: ਇੱਕ ਅਪਡੇਟ ਉਪਲਬਧ ਹੈ\n  high_impact: ਪ੍ਰਦਰਸ਼ਨ 'ਤੇ ਉੱਚ ਪ੍ਰਭਾਵ\n  import_wallpapers: ਸਥਾਨਕ ਵਾਲਪੇਪਰ ਆਯਾਤ ਕਰੋ\n  open_folder: ਓਪਨ ਸਰੋਤ ਫੋਲਡਰ\n  outdated: >-\n    ਇਹ ਸਰੋਤ ਸੰਸਥ UI ਦੇ ਪੁਰਾਣੇ ਸੰਸਕਰਣ ਲਈ ਤਿਆਰ ਕੀਤਾ ਗਿਆ ਸੀ ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਤਰ੍ਹਾਂ\n    ਕੰਮ ਨਾ ਕਰੇ.\n  see_on_website: ਵੈੱਬਸਾਈਟ 'ਤੇ ਦੇਖੋ\nreview_request:\n  not_now: ਹਾਲੇ ਨਹੀਂ\n  sure: ਯਕੀਨਨ!\n  title: ਸੀਲੇਨ UI ਦਾ ਆਨੰਦ ਮਾਣ ਰਹੇ ਹੋ?\nsave: ਸੇਵ ਕਰੋ\nsave_and_restart: ਸੇਵ ਅਤੇ ਰੀਸਟਾਰਟ\nsearch: ਖੋਜ\nsee_more: ਹੋਰ ਦੇਖੋ\nshortcuts:\n  duplicate_error: ਇਹ ਸ਼ਾਰਟਕੱਟ ਡੁਪਲੀਕੇਟ ਹੈ\n  enable: ਬਿਲਟ-ਇਨ ਸ਼ਾਰਟਕੱਟ ਸਿਸਟਮ ਨੂੰ ਸਮਰੱਥ ਕਰੋ\n  enable_tooltip: >-\n    ਅਯੋਗ ਕਰੋ ਜੇ ਤੁਸੀਂ ਵੇਖਿਆ UI ਕਲਾਇੰਟ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਆਪਣੇ ਖੁਦ ਦੇ ਸ਼ੌਰਟਕਟ ਸਿਸਟਮ\n    ਨੂੰ ਲਾਗੂ ਕਰੋਗੇ\n  labels:\n    create_new_workspace: ਨਵਾਂ ਵਰਕਸਪੇਸ ਬਣਾਓ\n    cycle_stack_next: ਸਾਈਕਲ ਸਟੈਕ ਅੱਗੇ\n    cycle_stack_prev: ਸਾਈਕਲ ਸਟੈਕ ਪਿਛਲੇ\n    cycle_wallpaper_next: ਅਗਲੇ ਵਾਲਪੇਪਰ ਵਿੱਚ ਬਦਲੋ\n    cycle_wallpaper_prev: ਪਿਛਲੇ ਵਾਲਪੇਪਰ ਵਿੱਚ ਬਦਲੋ\n    decrease_height: ਉਚਾਈ ਨੂੰ ਘਟਾਓ\n    decrease_width: ਚੌੜਾਈ\n    destroy_current_workspace: ਮੌਜੂਦਾ ਵਰਕਸਪੇਸ ਨੂੰ ਨਸ਼ਟ ਕਰੋ\n    focus_bottom: ਫੋਕਸ ਤਲ\n    focus_latest: ਤਾਜ਼ਾ ਫੋਕਸ\n    focus_left: ਖੱਬੇ ਧਿਆਨ ਰੱਖੋ\n    focus_right: ਸਹੀ ਫੋਕਸ\n    focus_top: ਫੋਕਸ ਚੋਟੀ ਦੇ\n    increase_height: ਉਚਾਈ ਵਧਾਓ\n    increase_width: ਚੌੜਾਈ ਵਧਾਓ\n    misc_force_quit: ਰੱਦ\n    misc_force_restart: ਫੋਰਸ ਚਾਲੂ\n    misc_open_settings: ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ\n    misc_toggle_lock_tracing: ਟੌਗਲ ਲਾਕ ਟਰੇਸਿੰਗ (ਲੌਗ)\n    misc_toggle_win_event_tracing: ਵਿਨ ਈਵੈਂਟ ਟਰੇਸਿੰਗ (ਲੌਗ) ਨੂੰ ਟੌਗਲ ਕਰੋ\n    move_to_workspace: ਵਰਕਸਪੇਸ ਵਿੱਚ ਜਾਓ {{0}}}\n    move_window_down: ਵਿੰਡੋ ਨੂੰ ਹੇਠਾਂ ਭੇਜੋ\n    move_window_left: ਵਿੰਡੋ ਨੂੰ ਖੱਬੇ ਵੱਲ ਭੇਜੋ\n    move_window_right: ਵਿੰਡੋ ਨੂੰ ਸੱਜੇ ਭੇਜੋ\n    move_window_up: ਵਿੰਡੋ ਨੂੰ ਸਿਖਰ ਤੇ ਲੈ ਜਾਓ\n    pause_tiling: ਵਿੰਡੋ ਮੈਨੇਜਰ ਨੂੰ ਰੋਕੋ\n    reserve_bottom: ਰਿਜ਼ਰਵ ਥੱਲੇ\n    reserve_float: ਰਿਜ਼ਰਵ ਫਲੋਟ\n    reserve_left: ਰਿਜ਼ਰਵ ਬਚਿਆ\n    reserve_right: ਰਿਜ਼ਰਵ ਸਹੀ\n    reserve_stack: ਰਿਜ਼ਰਵ ਸਟੈਕ\n    reserve_top: ਰਿਜ਼ਰਵ ਟਾਪ\n    restore_sizes: ਅਕਾਰ ਨੂੰ ਬਹਾਲ ਕਰੋ\n    send_to_workspace: ਵਰਕਸਪੇਸ ਨੂੰ ਭੇਜੋ {{0}}\n    start_weg_app: ਫੋਕਸ ਜਾਂ ਐਪਲੀਕੇਸ਼ਨ ਅਰੰਭ ਕਰੋ {{0}}\n    switch_to_next_workspace: ਅਗਲੇ ਵਰਕਸਪੇਸ ਤੇ ਜਾਓ\n    switch_to_previous_workspace: ਪਿਛਲੇ ਵਰਕਸਪੇਸ ਤੇ ਜਾਓ\n    switch_workspace: ਵਰਕਸਪੇਸ ਵਿੱਚ ਸਵਿੱਚ ਕਰੋ {{0}}}\n    toggle_float: ਵਿੰਡੋ ਫਲੋਟ ਮੋਡ ਬਦਲੋ\n    toggle_monocle: ਵਰਕਸਪੇਸ ਮੋਨੋਕਲ ਮੋਡ ਟੌਗਲ ਕਰੋ\n  readonly_tooltip: ਇਹ ਸਿਰਫ ਇਕੋ ਸ਼ਾਰਟਕੱਟ ਹੈ\n  reset: ਡਿਫੌਲਟਸ ਤੇ ਰੀਸੈਟ ਕਰੋ\nsides:\n  bottom: ਹੇਠਾਂ\n  left: ਖੱਬੇ\n  right: ਸੱਜਾ\n  top: ਸਿਖਰ\ntoolbar:\n  auto_hide: ਆਟੋ ਓਹਲੇ\n  delay_to_hide: ਛੁਪਾਉਣ ਲਈ ਦੇਰੀ\n  delay_to_show: ਦਿਖਾਉਣ ਵਿੱਚ ਦੇਰੀ\n  dock_side: ਸਥਿਤੀ\n  enable: ਫੈਂਸੀ ਟੂਲਬਾਰ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ\n  hide_mode:\n    always: ਹਮੇਸ਼ਾ\n    never: ਕਦੇ ਨਹੀਂ\n    on_overlap: ਓਵਰਲੈਪ 'ਤੇ\n  item_size: ਆਈਟਮ ਦਾ ਆਕਾਰ\n  label: ਟੂਲਬਾਰ\n  margin: ਹਾਸ਼ੀਏ ਦਾ ਆਕਾਰ\n  padding: ਪੈਡਿੰਗ ਆਕਾਰ\n  placeholder: {}\nupdate:\n  available: ਅਪਡੇਟ ਉਪਲਬਧ!\n  channel: ਚੈਨਲ ਅਪਡੇਟ ਕਰੋ\n  downloading: ਡਾ ing ਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ...\nwall:\n  backgrounds: ਵਾਲਪੇਪਰ\n  blur: ਧੁੰਦਲਾ\n  cancel: ਰੱਦ ਕਰੋ\n  close: ਬੰਦ ਕਰੋ\n  collection_name: ਸੰਗ੍ਰਹਿ ਦਾ ਨਾਮ\n  collections: ਸੰਗ੍ਰਹਿ\n  contrast: ਇਸ ਦੇ ਉਲਟ\n  corrupted_wallpapers_message: 'ਖਰਾਬ ਵਾਲਪੇਪਰ, ਇਹਨਾਂ ਨੂੰ ਮਿਟਾਉਣ ''ਤੇ ਵਿਚਾਰ ਕਰੋ:'\n  create: ਬਣਾਓ\n  create_collection: ਸੰਗ੍ਰਹਿ ਬਣਾਓ\n  default_collection: ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੰਗ੍ਰਹਿ\n  delete_collection: ਸੰਗ੍ਰਹਿ ਮਿਟਾਓ\n  edit_collection: ਸੰਗ੍ਰਹਿ ਦਾ ਸੰਪਾਦਨ ਕਰੋ\n  enable: ਵਾਲਪੇਪਰ ਮੈਨੇਜਰ ਨੂੰ ਸਮਰੱਥ ਕਰੋ\n  extend: ਪ੍ਰਾਇਮਰੀ ਵਧਾਓ\n  fit:\n    contain: ਸ਼ਾਮਲ\n    cover: ਕਵਰ\n    fill: ਭਰੋ\n  flipHorizontal: ਹਰੀਜੱਟਲ ਫਲਿੱਪ ਕਰੋ\n  flipVertical: ਲੰਬਕਾਰੀ ਫਲਿੱਪ ਕਰੋ\n  generating_thumbnails: ਵਾਲਪੇਪਰ ਥੰਬਨੇਲ ਤਿਆਰ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ\n  hours: ਘੰਟੇ\n  interval: ਵਾਲਪੇਪਰ ਬਦਲੋ\n  minutes: ਮਿੰਟ\n  monitor_collection: ਨਿਗਰਾਨ ਸੰਗ੍ਰਹਿ\n  multimonitor_behaviour: ਮਲਟੀਮੋਨੀਟਰ ਵਿਵਹਾਰ\n  muted: ਵੀਡੀਓ ਆਡੀਓ ਨੂੰ ਮਿਊਟ ਕਰੋ\n  no_background: ਇਸ ਦੀ ਬਜਾਏ ਥੀਮ ਦੇ ਬੈਕਗ੍ਰਾਉਂਡ ਦੀ ਵਰਤੋਂ ਕਰਦਿਆਂ, ਖਾਲੀ ਸਲਾਈਡ ਸ਼ੋਅ ਕਰੋ.\n  no_collections: ਅਜੇ ਤੱਕ ਕੋਈ ਸੰਗ੍ਰਹਿ ਨਹੀਂ ਬਣਾਇਆ ਗਿਆ। ਇੱਕ ਬਣਾਉਣ ਲਈ + ਬਟਨ 'ਤੇ ਕਲਿੱਕ ਕਰੋ।\n  objectFit: ਪਿਛੋਕੜ ਫਿਟ\n  objectPosition: ਪਿਛੋਕੜ ਦੀ ਸਥਿਤੀ\n  overlayColor: ਓਵਰਲੇ ਰੰਗ\n  overlayMixBlendMode: ਓਵਰਲੇਅ ਮਿਕਸ ਬਰੇਂਡ ਮੋਡ\n  per_monitor: ਪ੍ਰਤੀ ਮਾਨੀਟਰ\n  playback: ਪਲੇਬੈਕ ਸਪੀਡ\n  position:\n    bottom: ਤਲ\n    center: ਕੇਂਦਰ\n    left: ਖੱਬੇ\n    right: ਸੱਜੇ\n    top: ਸਿਖਰ\n  processing_video: ਵੀਡੀਓ 'ਤੇ ਪ੍ਰਕਿਰਿਆ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ\n  random: ਸਿਲਾਈਡ ਸਲਾਈਡ ਸ਼ੋਅ\n  saturation: ਸੰਤ੍ਰਿਪਤ\n  seconds: ਸਕਿੰਟ\n  select_collection: ਸੰਗ੍ਰਹਿ ਚੁਣੋ\n  thumbnail_generation_complete: ਥੰਬਨੇਲ ਜਨਰੇਸ਼ਨ ਪੂਰਾ ਹੋਇਆ\n  thumbnail_generation_finished: ਥੰਬਨੇਲ ਬਣਾਉਣਾ ਸਫਲਤਾਪੂਰਵਕ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ\n  wallpaper_collection: ਵਾਲਪੇਪਰ ਸੰਗ੍ਰਹਿ\n  wallpaper_settings: ਵਾਲਪੇਪਰ ਸੈਟਿੰਗਾਂ\n  withOverlay: ਓਵਰਲੇਅ ਦੇ ਨਾਲ\n  workspace_collections: ਵਰਕਸਪੇਸ ਸੰਗ੍ਰਹਿ\nweg:\n  auto_hide: ਆਟੋ ਓਹਲੇ\n  delay_to_hide: ਛੁਪਾਉਣ ਲਈ ਦੇਰੀ\n  delay_to_show: ਦਿਖਾਉਣ ਵਿੱਚ ਦੇਰੀ\n  dock_side: ਸਥਿਤੀ\n  enable: ਡੌਕ/ਟਾਸਕਬਾਰ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ\n  filtering: ਆਈਟਮ ਫਿਲਟਰਿੰਗ\n  gap: ਪਾੜਾ\n  hide_mode:\n    always: ਹਮੇਸ਼ਾ\n    never: ਕਦੇ ਨਹੀਂ\n    on_overlap: ਓਵਰਲੈਪ 'ਤੇ\n  items:\n    gap: ਆਈਟਮਾਂ ਵਿਚਕਾਰ ਸਪੇਸ\n    label: ਇਕਾਈ\n    pinned_visibility:\n      always: ਹਮੇਸ਼ਾ\n      label: ਪਿੰਨ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਦੀ ਦਿੱਖ\n      when_primary: ਜਦੋਂ ਮਾਨੀਟਰ ਪ੍ਰਾਇਮਰੀ ਹੁੰਦਾ ਹੈ\n    show_instance_counter: ਓਪਨ ਵਿੰਡੋਜ਼ ਕਾਉਂਟਰ ਦਿਖਾਓ\n    show_window_title: ਓਪਨ ਵਿੰਡੋ ਦਾ ਸਿਰਲੇਖ ਦਿਖਾਓ (ਸਿਰਫ ਖਿਤਿਜੀ)\n    size: ਆਈਟਮ ਦਾ ਆਕਾਰ\n    split_windows: ਵਿੰਡੋਜ਼ ਨੂੰ ਸਪਲਿਟ ਕਰੋ (ਪ੍ਰਤੀ ਵਿੰਡੋ ਇੱਕ ਆਈਟਮ)\n    temporal_visibility:\n      all: ਸਾਰੇ\n      label: ਅਨਪਿੰਨ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਦੀ ਦਿੱਖ\n      on_monitor: ਮਾਨੀਟਰ 'ਤੇ\n    visible_separators: ਦਿਖਣਯੋਗ ਵਿਭਾਜਕ\n  label: ਡੌਕ/ਟਾਸਕਬਾਰ\n  margin: ਹਾਸ਼ੀਏ\n  mode:\n    full_width: ਪੂਰੀ ਸਕ੍ਰੀਨ ਚੌੜਾਈ\n    min_content: ਛੋਟਾ ਜਿੰਨਾ ਹੋ ਸਕਦਾ ਹੈ\n  padding: ਪੈਡਿੰਗ\n  show_end_task: ਟਾਸਕਬਾਰ ਵਿੱਚ ਅੰਤ ਦਾ ਕੰਮ ਵੇਖੋ\n  width: ਚੌੜਾਈ\nwelcome:\n  give_a_review: ਇੱਕ ਸਮੀਖਿਆ ਦਿਓ\n  message: >-\n    Seelen UI ਵਿੰਡੋਜ਼ ਲਈ ਇੱਕ ਮੁਫਤ ਅਤੇ ਓਪਨ ਸੋਰਸ ਡੈਸਕਟਾਪ ਵਾਤਾਵਰਣ ਹੈ। ਇੱਥੇ ਤੁਹਾਡੇ\n    ਕੋਲ ਤੁਹਾਡੇ ਡੈਸਕਟੌਪ ਦੇ ਦਿੱਖ ਅਤੇ ਵਿਵਹਾਰ 'ਤੇ ਪੂਰਾ ਨਿਯੰਤਰਣ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ\n    ਆਪਣੇ ਵਰਕਫਲੋ ਅਤੇ ਸ਼ੈਲੀ ਨਾਲ ਮੇਲ ਕਰਨ ਲਈ ਹਰ ਵੇਰਵੇ ਨੂੰ ਅਨੁਕੂਲਿਤ ਕਰ ਸਕਦੇ ਹੋ।\n  ok: ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ!\n  review: >-\n    ਜੇਕਰ ਤੁਸੀਂ ਸੀਲੇਨ UI ਦੀ ਵਰਤੋਂ ਕਰਨ ਦਾ ਅਨੰਦ ਲੈਂਦੇ ਹੋ, ਤਾਂ ਸਟੋਰ 'ਤੇ ਇੱਕ ਸਮੀਖਿਆ\n    ਛੱਡਣ 'ਤੇ ਵਿਚਾਰ ਕਰੋ — ਤੁਹਾਡਾ ਫੀਡਬੈਕ ਪ੍ਰੋਜੈਕਟ ਨੂੰ ਵਧਣ ਅਤੇ ਬਿਹਤਰ ਬਣਾਉਣ ਵਿੱਚ ਮਦਦ\n    ਕਰਦਾ ਹੈ।\n  title: Seelen UI ਵਿੱਚ ਸੁਆਗਤ ਹੈ!\nwidget:\n  enable: ਇਸ ਵਿਦਜਿਟ ਨੂੰ ਸਮਰੱਥ ਕਰੋ\n  enable_for_monitor: ਇਸ ਮਾਨੀਟਰ ਨੂੰ ਸਮਰੱਥ ਕਰੋ\n  instances: ਉਦਾਹਰਣ\nwm:\n  animations:\n    duration: ਐਨੀਮੇਸ਼ਨ ਅੰਤਰਾਲ (ਐਮਐਸ)\n    ease_function: ਐਨੀਮੇਸ਼ਨ ਆਸਾਨ ਫੰਕਸ਼ਨ\n    enable: ਵਿੰਡੋ ਦੇ ਐਨੀਮੇਸ਼ਨ ਨੂੰ ਸਮਰੱਥ ਕਰੋ\n  author: ਲੇਖਕ\n  border:\n    enable: ਵਿੰਡੋ ਦੇ ਬਾਰਡਰ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ\n    offset: ਬਾਰਡਰ ਆਫਸੈੱਟ\n    width: ਬਾਰਡਰ ਚੌੜਾਈ\n  description: ਵਰਣਨ\n  drag_behavior: ਡਰੈਗ ਵਿਵਹਾਰ\n  drag_behavior_options:\n    sort: ਕ੍ਰਮਬੱਧ (ਖਿੱਚਣ ਵੇਲੇ ਵਿੰਡੋਜ਼ ਨੂੰ ਮੁੜ ਕ੍ਰਮਬੱਧ ਕਰੋ)\n    swap: ਸਵੈਪ (ਡਰੈਗ ਐਂਡ 'ਤੇ ਵਿੰਡੋਜ਼ ਨੂੰ ਸਵੈਪ ਕਰੋ)\n  enable: ਵਿੰਡੋ ਮੈਨੇਜਰ ਟਾਇਲਿੰਗ ਯੋਗ ਕਰੋ\n  layout: ਖਾਕਾ\n  resize_delta: ਡੈਲਟਾ ਦਾ ਆਕਾਰ ਬਦਲੋ (%)\n  space_between_containers: ਕੰਟੇਨਰਾਂ ਵਿਚਕਾਰ ਸਪੇਸ\n  workspace_offset: ਵਰਕਸਪੇਸ ਆਫਸੈੱਟ (ਮਾਰਜਿਨ)\n  workspace_padding: ਵਰਕਸਪੇਸ ਪੈਡਿੰਗ\n'yes': ਹਾਂ\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/pl.yml",
    "content": "action:\n  confirm: Jesteś pewien?\n  confirm_body: Tego działania nie można cofnąć.\napps_configurations:\n  app:\n    bindings: Skrót klawiszowy (wymagane są obie opcje)\n    category: Kategoria\n    category_placeholder: Nic\n    monitor: Ekran\n    monitor_placeholder: Nic\n    name: Nazwa\n    ok_create: Utwórz\n    ok_edit: Edytuj\n    ok_readonly: Edytuj jako nowy\n    options:\n      NoInteractive: Brak interaktywnego\n      VdPinned: Pokaż we wszystkich obszarach roboczych\n      WmFloat: Twm - Zacznij pływać\n      WmForce: Twm – Zarządzanie siłami\n      WmUnmanage: Twm — nie zarządzaj\n    options_label: Dodatkowe opcje\n    title_create: Tworzenie {{nazwa}}\n    title_edit: Edycja {{nazwa}}\n    title_readonly: Przeglądanie {{Nazwa}}\n    weg_options_label: Ustawienia docka/paska zadań\n    wm_options_label: Ustawienia menadżera okien\n    workspace: Obszar roboczy\n    workspace_placeholder: Nic\n  bundled_msg: >-\n    Te konfiguracje pakietowe nie są edytowalne i mają na celu zapewnienie\n    najlepszych wrażeń bez dostosowywania. Automatycznie konfigurują dla Ciebie\n    najczęściej używane aplikacje.\n  bundled_title: App Config w pakiecie z Seelen\n  confirm_delete: Czy na pewno chcesz usunąć tę konfigurację?\n  confirm_delete_title: Potwierdź usunięcie\n  delete: Usuń\n  export: Eksportuj\n  export_full: Eksport ustawień według aplikacji\n  extra_info: >-\n    Interfejs użytkownika Seelen używa tylko jednego identyfikatora na aplikację\n    (znaleziono pierwsze dopasowanie), dlatego ważna jest kolejność dodawania.\n    Priorytet będzie mieć najnowszy dodany, ponieważ tabela jest domyślnie\n    sortowana od najnowszego do najstarszego.\n  identifier:\n    add_block: Dodaj blok\n    and: I\n    id: Identyfikator\n    kind: Identyfikuj poprzez\n    matching_strategy: Sposób dopasowania\n    matching_strategy_option:\n      contains: Zawiera\n      ends_with: Kończy się na\n      equals: Równe\n      regex: Wyrażenie regularne\n      starts_with: Zaczyna się od\n    negation: Negate dopasowanie\n    or: LUB\n    remove: Usuń blok\n    type:\n      class: Klasa\n      exe: Ex\n      path: Ścieżka\n      title: Tytuł\n  import: Importuj\n  import_full: Importowanie ustawień według aplikacji\n  new: Nowy\n  search: Szukaj\n  swap: Zamień\ncancel: Anuluj\nclose: Zamknij\ndelete: Usuń\ndevtools:\n  app_folders: Foldery aplikacji\n  custom_config_file: Załaduj niestandardowy plik konfiguracyjny\n  data_folder: Folder danych\n  enable: Włącz narzędzia programistów\n  install_folder: Folder instalacyjny\n  load: Obciążenie\n  settings_file: Plik ustawień\n  simulate_perm:\n    label: Symuluj żądanie pozwolenia na widget\n    result_allowed: ✓ Zezwolenie udzielone\n    result_denied: ✗ Odmowa pozwolenia\n    trigger: Symulować\n    widget_id_placeholder: '@autor/nazwa-widgetu'\nextras:\n  clear_icons: Wyczyść pamięć podręczną ikon systemowych\n  clear_icons_tooltip: >-\n    Do poprawnego uruchomienia wszystkich widżetów może być wymagane ponowne\n    uruchomienie systemu\n  exit: Wyjdź\n  links: Oficjalne linki\n  relaunch: Wznów\n  version: Wersja\n  version_fixed: >-\n    Wersje aplikacji i środowiska wykonawczego WebView2 zostały naprawione.\n    Oznacza to, że aplikacja nie będzie otrzymywać aktualizacji, a środowisko\n    wykonawcze WebView2 nie będzie automatycznie aktualizowane aktualizacjami\n    systemu Windows.\ngeneral:\n  accent_color: Kolor akcentu\n  date_format: Format daty\n  date_format_how_to: Jak napisać format daty?\n  hardware_acceleration: Przyspieszenie sprzętowe\n  hardware_acceleration_description: >-\n    Wyłączenie akceleracji sprzętowej zmniejszy zużycie pamięci, ale może\n    spowodować problemy z wydajnością. Można bezpiecznie wyłączyć, jeśli nie\n    będziesz używać animowanych tapet.\n  icon_pack:\n    available: Dostępne pakiety ikon\n    selected: Aktywne pakiety ikon\n  language: Język\n  monday: Poniedziałek\n  performance_mode:\n    on_battery: Na baterii\n    on_energy_saver: Na oszczędności energii\n    options:\n      disabled: Wyłączony\n      extreme: Skrajny\n      minimal: Minimalny\n    plugged: Podłączone lub ładowanie\n  polling_interval: Interwał odpytywania systemu\n  polling_interval_description: >-\n    Jak często (w sekundach) interfejs użytkownika Seelen sprawdza zasoby\n    systemowe, takie jak procesor, pamięć RAM, sieć i aktywność dysku. Mniejsza\n    liczba oznacza częstsze aktualizacje, ale nieco większe zużycie zasobów.\n  saturday: Sobota\n  start_of_week: Początek tygodnia\n  startup: Uruchomić przy starcie systemu?\n  sunday: Niedziela\n  theme:\n    available: Dostępne motywy\n    selected: Aktywne motywy\nheader:\n  labels:\n    config: Konfiguracje\n    developer: Dla deweloperów\n    extras: Dodatki\n    general: Ogólne\n    home: Strona główna\n    icon_pack_editor: Ikony w pamięci podręcznej\n    iconpack: Pakiety ikon\n    monitors: Ekrany\n    plugin: Wtyczki\n    resources: Zasoby\n    shortcuts: Skróty klawiszowe\n    soundpack: Pakiety dźwiękowe\n    specific_apps: Ustawienia według aplikacji\n    theme: Tematy\n    virtual_desk: Wirtualne komputery stacjonarne\n    wallpaper: Tapety\n    widget: Widżety\nhome:\n  new_resources: Nowe zasoby\ninherit: Dziedziczyć\ninProgress: W trakcie...\ninsert: Wstawić\nloading: Ładowanie...\nmiscellaneous: Różnorodny\nmonitors_configurations:\n  label: Monitor {{indeks}}\nmore: Więcej\n'no': NIE\nopen: otwarty\nquit: Zrezygnować\nremove: Usunąć\nreset_all_to_default: Przywróć wartości domyślne\nreset_to_default: Przywróć wartość domyślną\nresources:\n  app_outdated: Ten zasób wymaga nowszej wersji Seelen UI do poprawnego działania.\n  corrupted_wallpaper: >-\n    Nie udało się wyodrębnić miniatury — uszkodzony lub nieobsługiwany format\n    wideo\n  delete: Usuń zasób\n  discover: Odkryj więcej zasobów\n  has_update: Aktualizacja jest dostępna\n  high_impact: Duży wpływ na wydajność\n  import_wallpapers: Import lokalnych tapet\n  open_folder: Otwórz folder zasobów\n  outdated: >-\n    Ten zasób został zaprojektowany dla starszej wersji Seelen UI i może nie\n    działać poprawnie.\n  see_on_website: Zobacz na stronie internetowej\nreview_request:\n  not_now: Nie teraz\n  sure: Jasne!\n  title: Podoba Ci się interfejs użytkownika Seelen?\nsave: Zastosuj\nsave_and_restart: Zastosuj i uruchom ponownie\nsearch: Szukaj\nsee_more: Zobacz więcej\nshortcuts:\n  duplicate_error: Ten skrót jest zduplikowany\n  enable: Włącz wbudowany system skrótów\n  enable_tooltip: >-\n    Wyłącz, jeśli zaimplementujesz własny system skrótów za pomocą klienta\n    Seelen UI\n  labels:\n    create_new_workspace: Utwórz nowy obszar roboczy\n    cycle_stack_next: Następnie stos cyklowy\n    cycle_stack_prev: Stos cyklu poprzedni\n    cycle_wallpaper_next: Zmień na następną tapetę\n    cycle_wallpaper_prev: Zmień na poprzednią tapetę\n    decrease_height: Zmniejszyć wysokość\n    decrease_width: Zmniejszyć szerokość\n    destroy_current_workspace: Zniszcz obecny obszar roboczy\n    focus_bottom: Focus Dim\n    focus_latest: Focus najnowszy\n    focus_left: Skup się na lewej\n    focus_right: Skup się dobrze\n    focus_top: Focus Top\n    increase_height: Zwiększyć wysokość\n    increase_width: Zwiększyć szerokość\n    misc_force_quit: Force zrezygnować\n    misc_force_restart: Wymuś restart\n    misc_open_settings: Otwórz ustawienia\n    misc_toggle_lock_tracing: Przełączanie śledzenia blokady (dzienniki)\n    misc_toggle_win_event_tracing: Przełączanie zdarzeń wygranych (dzienniki)\n    move_to_workspace: Przenieś się do przestrzeni roboczej {{0}}\n    move_window_down: Przenieś okno na dolny\n    move_window_left: Przesuń okno po lewej\n    move_window_right: Przenieś okno po prawej stronie\n    move_window_up: Przesuń okno na górę\n    pause_tiling: Zatrzymaj menedżer okien kafelków\n    reserve_bottom: Rezerwować dno\n    reserve_float: Rezerwować pływak\n    reserve_left: Rezerwa w lewo\n    reserve_right: Rezerwuj prawnie\n    reserve_stack: Stos rezerwowy\n    reserve_top: Rezerwuj top\n    restore_sizes: Przywróć rozmiary\n    send_to_workspace: Wyślij do przestrzeni roboczej {{0}}\n    start_weg_app: Focus lub uruchom aplikację {{0}}\n    switch_to_next_workspace: Przejdź na następny obszar roboczy\n    switch_to_previous_workspace: Przejdź na poprzedni obszar roboczy\n    switch_workspace: Przełącz na przestrzeń roboczą {{0}}\n    toggle_float: Tryb przełączania okna pływaka\n    toggle_monocle: Przełącz tryb monocle przestrzeni roboczej\n  readonly_tooltip: To jest skrót tylko do odczytu\n  reset: Zresetuj do domyślnych\nsides:\n  bottom: Dół\n  left: Lewo\n  right: Prawo\n  top: Góra\ntoolbar:\n  auto_hide: Automatyczne ukrywanie\n  delay_to_hide: Opóźnienie ukrycia\n  delay_to_show: Opóźnienie wysunięcia\n  dock_side: Pozycja\n  enable: Włącz pasek narzędzi\n  hide_mode:\n    always: Zawsze\n    never: Nigdy\n    on_overlap: Kiedy zasłania\n  item_size: Rozmiar przedmiotu\n  label: Pasek narzędzi\n  margin: Rozmiar marginesu\n  padding: Rozmiar wyściółki\n  placeholder: {}\nupdate:\n  available: Dostępna aktualizacja!\n  channel: Kanał aktualizacji\n  downloading: Pobieranie...\nwall:\n  backgrounds: Tapety\n  blur: Rozmycie\n  cancel: Anulować\n  close: Zamknąć\n  collection_name: Nazwa kolekcji\n  collections: Kolekcje\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Uszkodzone tapety. Rozważ ich usunięcie:'\n  create: Tworzyć\n  create_collection: Utwórz kolekcję\n  default_collection: Kolekcja domyślna\n  delete_collection: Usuń kolekcję\n  edit_collection: Edytuj kolekcję\n  enable: Włącz menedżer tapet\n  extend: Rozszerz podstawowy\n  fit:\n    contain: Zawierać\n    cover: Okładka\n    fill: Wypełnienie\n  flipHorizontal: Odwróć w poziomie\n  flipVertical: Flip Vertical\n  generating_thumbnails: Generowanie miniatur tapet\n  hours: godzin\n  interval: Zmień tapetę co\n  minutes: minut\n  monitor_collection: Kolekcja monitorów\n  multimonitor_behaviour: Zachowanie wielu monitorów\n  muted: Wycisz dźwięk wideo\n  no_background: Brak tapet, używane są tapety motywu systemu Windows.\n  no_collections: Nie utworzono jeszcze żadnych kolekcji. Kliknij przycisk +, aby go utworzyć.\n  objectFit: Dopasowanie tła\n  objectPosition: Pozycja w tle\n  overlayColor: Kolor nakładki\n  overlayMixBlendMode: Tryb mieszania nakładki\n  per_monitor: Na monitor\n  playback: Prędkość odtwarzania\n  position:\n    bottom: Dół\n    center: Centrum\n    left: Lewa\n    right: Prawo\n    top: Top\n  processing_video: Przetwarzanie wideo\n  random: Losowa kolejność\n  saturation: Nasycenie\n  seconds: sekund\n  select_collection: Wybierz opcję Kolekcja\n  thumbnail_generation_complete: Generowanie miniatur zostało ukończone\n  thumbnail_generation_finished: Generowanie miniatury zakończyło się pomyślnie\n  wallpaper_collection: Kolekcja tapet\n  wallpaper_settings: Ustawienia tapety\n  withOverlay: Z nakładką\n  workspace_collections: Kolekcje obszaru roboczego\nweg:\n  auto_hide: Automatyczne ukrywanie\n  delay_to_hide: Opóźnienie ukrycia\n  delay_to_show: Opóźnienie wysunięcia\n  dock_side: Pozycja\n  enable: Włącz dok/pasek zadań\n  filtering: Filtrowanie pozycji\n  gap: Przerwa\n  hide_mode:\n    always: Zawsze\n    never: Nigdy\n    on_overlap: Kiedy zasłania\n  items:\n    gap: Przerwa między elementami\n    label: Elementy\n    pinned_visibility:\n      always: Zawsze\n      label: Widoczność przypiętych elementów\n      when_primary: Główny ekran\n    show_instance_counter: Wyświetl liczbę otwartych okien\n    show_window_title: Wyświetl tytuł otwartego okna (tylko poziomo)\n    size: Rozmiar elementów\n    split_windows: Podziel okna (jeden element na okno)\n    temporal_visibility:\n      all: Wszystkie ekrany\n      label: Widoczność uruchomionych aplikacji\n      on_monitor: Jeden ekran\n    visible_separators: Widoczne separatory\n  label: Dock/Pasek zadań\n  margin: Margines\n  mode:\n    full_width: Pełna szerokość ekranu\n    min_content: Jak najmniejszy\n  padding: Wysokość tła\n  show_end_task: Pokaż zadanie końcowe na pasku zadań\n  width: Szerokość\nwelcome:\n  give_a_review: Wystaw recenzję\n  message: >-\n    Seelen UI to bezpłatne środowisko graficzne typu open source dla systemu\n    Windows. Tutaj masz pełną kontrolę nad wyglądem i zachowaniem pulpitu, co\n    pozwala dostosować każdy szczegół do Twojego przepływu pracy i stylu.\n  ok: Zacznijmy!\n  review: >-\n    Jeśli lubisz korzystać z interfejsu użytkownika Seelen, rozważ pozostawienie\n    recenzji w sklepie — Twoja opinia pomoże rozwijać i ulepszać projekt.\n  title: Witamy w interfejsie Seelen!\nwidget:\n  enable: Włącz ten widżet\n  enable_for_monitor: Włącz na tym monitorze\n  instances: Wystąpienia\nwm:\n  animations:\n    duration: Czas trwania animacji (MS)\n    ease_function: Funkcja łagodzenia animacji\n    enable: Włącz animacje okna\n  author: Autor\n  border:\n    enable: Włącz granicę okna\n    offset: Przesunięcie granic\n    width: Szerokość granicy\n  description: Opis\n  drag_behavior: Przeciągnij zachowanie\n  drag_behavior_options:\n    sort: Sortuj (zmieniaj kolejność okien podczas przeciągania)\n    swap: Zamień (zamień okna po przeciągnięciu)\n  enable: Włącz menedżer rozmieszczenia okien\n  layout: Układ\n  resize_delta: Delta rozmiaru (%)\n  space_between_containers: Przestrzeń między oknami\n  workspace_offset: Marginesy przestrzeni roboczych\n  workspace_padding: Wysokość tła przestrzeni roboczych\n'yes': Tak\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ps.yml",
    "content": "action:\n  confirm: ایا تاسو ډاډه یاست؟\n  confirm_body: دا عمل نشي خلاصولی.\napps_configurations:\n  app:\n    bindings: تړل (په یادداشت کې دواړه اختیارونه اړین دي)\n    category: کټګورۍ\n    category_placeholder: هیڅ نه\n    monitor: څارونکی\n    monitor_placeholder: هیڅ نه\n    name: نوم\n    ok_create: جوړول\n    ok_edit: تازه کول\n    ok_readonly: د نوي په توګه\n    options:\n      NoInteractive: نه متقابل عمل\n      VdPinned: په ټولو کاري ځایونو کې ښکاره کړئ\n      WmFloat: Twm - تیریدل پیل کړئ\n      WmForce: Twm - د ځواک اداره کول\n      WmUnmanage: Twm - بې نظمه کول\n    options_label: اضافي اختیارونه\n    title_create: '{{}} رامینځته کول'\n    title_edit: ترمیم. {{}}\n    title_readonly: لیدنه. یو {علی}}\n    weg_options_label: دوک / ټاسک بار انتخابونه\n    wm_options_label: د کړکۍ مدیر اختیارونه\n    workspace: کاري ځای\n    workspace_placeholder: هیڅ نه\n  bundled_msg: >-\n    دا بنډل تشکیلات د ترمیم وړ ندي او ډیزاین شوي چې تاسو ته د دودیز کولو پرته\n    غوره تجربه چمتو کړي. دوی په اوتومات ډول ستاسو لپاره خورا عام غوښتنلیکونه\n    تنظیموي.\n  bundled_title: د اپلیکیشن سره ایپ بنډل شوی\n  confirm_delete: ایا ته باوري یې چې دا جوړښتونه ړنګول غواړې؟\n  confirm_delete_title: ړنګول تایید کړئ\n  delete: حذف کول\n  export: صادرات\n  export_full: د غوښتنلیک له مخې د صادراتو ترتیبات\n  extra_info: >-\n    سیلین UI په هر اپلیکیشن کې یوازې یو پیژندونکی کاروي (لومړی میچ وموندل شو) نو\n    د مشخص کولو څرنګوالي ترتیب مهم دی ، وروستي اضافه شوي به لومړیتوب ورکړل شي ،\n    ځکه چې یادونه وکړئ جدول له وروستي څخه زاړه ته په ډیفالټ ډول ترتیب شوی.\n  identifier:\n    add_block: بلاک اضافه کړئ\n    and: او\n    id: پېژنيزه\n    kind: د\n    matching_strategy: برابرول\n    matching_strategy_option:\n      contains: لري\n      ends_with: سره پای ته رسیږي\n      equals: مساوي\n      regex: منظم بیان\n      starts_with: سره پیل کیږي\n    negation: غفلت کول\n    or: یا\n    remove: بلاک ړنګ کړئ\n    type:\n      class: ټولګي\n      exe: Exe\n      path: لاره\n      title: عنوان\n  import: واردول\n  import_full: د غوښتنلیک لخوا د وارداتو تنظیمات\n  new: نوی\n  search: لټون\n  swap: سویپ\ncancel: لغوه کول\nclose: بندول\ndelete: حذف کول\ndevtools:\n  app_folders: د ایپ فولډرونه\n  custom_config_file: د دودیز ترتیب فایل پورته کړئ\n  data_folder: د معلوماتو فولډر\n  enable: د پراختیا کونکي وسیلې فعال کړئ\n  install_folder: لګول فولډر\n  load: بار\n  settings_file: د امستنې دوتنه\n  simulate_perm:\n    label: د ویجټ اجازې غوښتنه سمول\n    result_allowed: ✓ اجازه ورکړل شوه\n    result_denied: ✗ اجازه رد شوه\n    trigger: سمول\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: د ایکسین عکس کې کیچ\n  clear_icons_tooltip: یو چالان ته اړتیا لیدل کیدی شي چې په بشپړ پوړ کې په بشپړ ډول پلي شي\n  exit: پریښودل / وتل\n  links: رسمي اړیکې\n  relaunch: بیا پیل کول\n  version: نسخه\n  version_fixed: >-\n    د غوښتنلیک او WebView2 د چلولو نسخه ثابته شوې. دا پدې مانا ده چې غوښتنلیک به\n    تازه معلومات ترلاسه نکړي او د WebView2 Runtime به په اوتومات ډول د وینډوز\n    تازه معلوماتو سره تازه نشي.\ngeneral:\n  accent_color: تیزس رنګ\n  date_format: د نیټې ب .ه\n  date_format_how_to: د نیټې بڼه څنګه لیکل کیږي؟\n  hardware_acceleration: د هارډویر سرعت\n  hardware_acceleration_description: >-\n    د هارډویر سرعت غیر فعال کول به د حافظې کارول کم کړي ، مګر د فعالیت مسلو لامل\n    کیدی شي. غیر فعال کول خوندي دي که تاسو ژوندی وال پیپرونه ونه کاروئ.\n  icon_pack:\n    available: د آیکون کڅوړې شتون لري\n    selected: د فعال آیکون بسته\n  language: ژبه\n  monday: دوشنبه\n  performance_mode:\n    on_battery: په بیټرۍ کې\n    on_energy_saver: د انرژي سیور باندې\n    options:\n      disabled: ناتوان شوی\n      extreme: ډیر\n      minimal: لږترلږه\n    plugged: فلګ یا چارج کول\n  polling_interval: د رای ورکولو سیسټم وقفه\n  polling_interval_description: >-\n    څو ځله (په ثانیو کې) سیلین UI د سیسټم سرچینې لکه CPU، RAM، شبکه، او ډیسک\n    فعالیت چک کوي. یو کوچنی شمیر د ډیرو پرله پسې تازه معلوماتو معنی لري، مګر د\n    سرچینو کارول یو څه لوړ دي.\n  saturday: شنبه\n  start_of_week: د اونۍ پیل\n  startup: په پیل کې چلول؟\n  sunday: یک شنبه\n  theme:\n    available: شته موضوعات\n    selected: فعال موضوعات\nheader:\n  labels:\n    config: تشکیلات\n    developer: د پراختیا کونکو لپاره\n    extras: اضافي\n    general: عمومي\n    home: کور\n    icon_pack_editor: د\n    iconpack: آیکون کڅوړه\n    monitors: څارونکي\n    plugin: فلګونه\n    resources: سرچینې\n    shortcuts: لنډوکونه\n    soundpack: غږ کڅوړه\n    specific_apps: د غوښتنلیک له مخې تنظیمات\n    theme: موضوعات\n    virtual_desk: مجازی ډیسټاپونه\n    wallpaper: والپيپرونه\n    widget: ویجټونه\nhome:\n  new_resources: نوې سرچينې\ninherit: میراث\ninProgress: د پرمختګ په حال کې...\ninsert: دننه کړئ\nloading: بارول ...\nmiscellaneous: متفرقه\nmonitors_configurations:\n  label: د {{{پلس} نظارت وکړئ}\nmore: نور\n'no': نه\nopen: خلاص\nquit: پرېښودل\nremove: لرې کول\nreset_all_to_default: ټول ډیفالټ ارزښتونو ته تنظیم کړئ\nreset_to_default: د ډیفالټ ارزښت ته بیا تنظیمول\nresources:\n  app_outdated: دا سرچینه د سم کار کولو لپاره یو نوي نسخه ته اړتیا لري.\n  corrupted_wallpaper: د تمبنیل په ایستلو کې پاتې راغلی - فاسد یا غیر ملاتړ شوی ویډیو بڼه\n  delete: سرچینه حذف کړئ\n  discover: ډیرې سرچینې ومومئ\n  has_update: یو اوسمهال شتون لري\n  high_impact: په فعالیت باندې لوړه اغیزه\n  import_wallpapers: ځایی وال پیپرونه وارد کړئ\n  open_folder: د سرچینو پوښۍ خلاصه کړئ\n  outdated: دا سرچینه د ښایسته UI زاړه نسخه لپاره ډیزاین شوې وه او شاید سم کار ونه کړي.\n  see_on_website: په ویب پاڼه کې وګورئ\nreview_request:\n  not_now: اوس نه\n  sure: ډاډه!\n  title: د Seelen UI څخه خوند اخلئ؟\nsave: خوندي کړئ\nsave_and_restart: خوندي کول او بیا پیل کړئ\nsearch: لټون\nsee_more: نور وګوره\nshortcuts:\n  duplicate_error: دا شارټ کټ نقل شوی دی\n  enable: د جوړ شوي لنډیز سیسټم فعال کړئ\n  enable_tooltip: >-\n    غیر فعال کړئ که تاسو د اصلي UI پیرودونکي په کارولو سره خپل د لنډیو سیسټم پلي\n    کړئ\n  labels:\n    create_new_workspace: نوی کارځای جوړ کړئ\n    cycle_stack_next: د دوران سټیک\n    cycle_stack_prev: دور مخکینی\n    cycle_wallpaper_next: راتلونکي وال پیپر ته بدل کړئ\n    cycle_wallpaper_prev: پخواني وال پیپر ته بدل کړئ\n    decrease_height: لوړوالی کمول\n    decrease_width: د سوری کمول\n    destroy_current_workspace: اوسني کاري ځای ویجاړ کړئ\n    focus_bottom: ښکته\n    focus_latest: وروستی تمرکز وکړئ\n    focus_left: کي left ه تمرکز\n    focus_right: سم تمرکز\n    focus_top: فوکس سر\n    increase_height: قد زیاتول\n    increase_width: پلنوالی زیات کړئ\n    misc_force_quit: ځواک پرېښودل\n    misc_force_restart: ځواک بیاچالانول\n    misc_open_settings: خلاص امستنې\n    misc_toggle_lock_tracing: د غلا کولو لاک تعقیب (ونې)\n    misc_toggle_win_event_tracing: د لید لید پاراګ (ونې)\n    move_to_workspace: د کارځای ته لاړ شئ {0 0}}\n    move_window_down: د کړکۍ لاندې ته واړوئ\n    move_window_left: د کی left اړخ ته لاړشئ\n    move_window_right: د کړکۍ سمې ته لاړشئ\n    move_window_up: کړکۍ سر ته واړوئ\n    pause_tiling: د وقفې ټیینګ کړکۍ مدیر\n    reserve_bottom: د\n    reserve_float: ریزرو فلوټ\n    reserve_left: ساتل شوی\n    reserve_right: سم ساتل\n    reserve_stack: ریزرو سټیک\n    reserve_top: خوندي پټی\n    restore_sizes: اندازونه بحال کړئ\n    send_to_workspace: کارځای ته واستوئ {{0}}\n    start_weg_app: فوکس یا د تطبیق غوښتنلیک {{0}}\n    switch_to_next_workspace: راتلونکی کاري ځای ته لاړشئ\n    switch_to_previous_workspace: پخوانیو ورکشاپ ته واړوئ\n    switch_workspace: د کارځای ته لاړشئ {0 0}}\n    toggle_float: د کړکۍ د فلوټ حالت\n    toggle_monocle: د رواپیز دستیس مونو سپیکشن حالت\n  readonly_tooltip: دا یو بشپړ لنډ لنډ پړاو دی\n  reset: د افلاسیون لپاره بیا تنظیمول\nsides:\n  bottom: ښکته\n  left: کیڼ\n  right: ښي\n  top: سر\ntoolbar:\n  auto_hide: آٹو پټول\n  delay_to_hide: د پټولو لپاره ځنډ\n  delay_to_show: د ښودلو لپاره ځنډ\n  dock_side: موقعیت\n  enable: د فینسي تورې پټه وړتیا وړ کړئ\n  hide_mode:\n    always: تل\n    never: هیڅکله نه\n    on_overlap: پر اوورلیپ\n  item_size: د توکي اندازه\n  label: توکپټه\n  margin: د حاشیې اندازه\n  padding: د پیډینګ اندازه\n  placeholder: {}\nupdate:\n  available: اوسمهالول شتون لري!\n  channel: چینل تازه کول\n  downloading: دانلود ...\nwall:\n  backgrounds: والپيپرونه\n  blur: روښانه\n  cancel: لغوه کړئ\n  close: تړل\n  collection_name: د راټولولو نوم\n  collections: راټولونه\n  contrast: برعکس\n  corrupted_wallpapers_message: 'فاسد وال پیپرونه، د دې حذف کولو په اړه غور وکړئ:'\n  create: جوړ کړئ\n  create_collection: ټولګه جوړه کړئ\n  default_collection: ډیفالټ ټولګه\n  delete_collection: ټولګه ړنګه کړئ\n  edit_collection: ټولګه ترمیم کړئ\n  enable: د وال پیپر مدیر فعال کړئ\n  extend: لومړني پراخ کړئ\n  fit:\n    contain: پکې شامل دي\n    cover: پوښ\n    fill: ډکول\n  flipHorizontal: فلیپ افقي\n  flipVertical: فلیپ عمودي\n  generating_thumbnails: د وال پیپر تمبیلونه پیدا کول\n  hours: ساعتونه\n  interval: په وال پیپر بدل کړئ\n  minutes: دقیقې\n  monitor_collection: د څارنې ټولګه\n  multimonitor_behaviour: ملټي مانیټر چلند\n  muted: د ویډیو آډیو خاموش کړئ\n  no_background: پرځای یې خالي سلایډونه، پرځای د موضوع شالید په کارولو سره.\n  no_collections: تراوسه هیڅ ټولګه نه ده جوړه شوې. د جوړولو لپاره + تڼۍ کلیک وکړئ.\n  objectFit: شالید فټ\n  objectPosition: شالید وضعیت\n  overlayColor: ډیر رنګ رنګ\n  overlayMixBlendMode: د انکشکې مخلوط حالت\n  per_monitor: د څارنې لپاره\n  playback: د بیا غږولو سرعت\n  position:\n    bottom: ښکته\n    center: مرکز\n    left: کیڼ\n    right: ښي\n    top: سر\n  processing_video: د ویډیو پروسس کول\n  random: تصادفي سلایډ\n  saturation: تړل\n  seconds: ثانیې\n  select_collection: ټولګه وټاکئ\n  thumbnail_generation_complete: د تمبیل تولید بشپړ شو\n  thumbnail_generation_finished: د تمبیل تولید په بریالیتوب سره پای ته ورسید\n  wallpaper_collection: د وال پیپر ټولګه\n  wallpaper_settings: د وال پیپر ترتیبات\n  withOverlay: د\n  workspace_collections: د کار ځای ټولګه\nweg:\n  auto_hide: آٹو پټول\n  delay_to_hide: د پټولو لپاره ځنډ\n  delay_to_show: د ښودلو لپاره ځنډ\n  dock_side: موقعیت\n  enable: دوک / ټاسک بار فعال کړئ\n  filtering: توکی فلټر کول\n  gap: تشې\n  hide_mode:\n    always: تل\n    never: هیڅکله نه\n    on_overlap: پر اوورلیپ\n  items:\n    gap: د توکو تر مینځ فضا\n    label: توکي\n    pinned_visibility:\n      always: تل\n      label: د پینډ شوي توکو لید\n      when_primary: کله چې څارونکی لومړنی وي\n    show_instance_counter: د خلاصې وینډوز کاونټر وښایاست\n    show_window_title: د خلاص کړکۍ سرلیک وښایاست (یوازې افقی)\n    size: د توکي اندازه\n    split_windows: وینډوز تقسیم کړئ (په هره کړکۍ کې یو توکي)\n    temporal_visibility:\n      all: ټول\n      label: د ناپاک شوي توکو لید لید\n      on_monitor: په مانیټر کې\n    visible_separators: لیدلي جلا کونکي\n  label: ډیک / ټاسک بار\n  margin: حاشیه\n  mode:\n    full_width: د بشپړ سکرین پلنوالی\n    min_content: کوچني لکه څنګه چې کیدی شي\n  padding: غالۍ\n  show_end_task: په ټاسکبار کې د پای دنده ښودل\n  width: پلنوالی\nwelcome:\n  give_a_review: بیاکتنه ورکړئ\n  message: >-\n    سیلین UI د وینډوز لپاره وړیا او خلاص سرچینه ډیسټاپ چاپیریال دی. دلته تاسو\n    بشپړ کنټرول لرئ چې ستاسو ډیسټاپ څنګه ښکاري او چلند کوي، تاسو ته اجازه درکوي\n    چې ستاسو د کاري فلو او سټایل سره سمون لپاره هر توضیحات تنظیم کړئ.\n  ok: راځئ چې پیل وکړو!\n  review: >-\n    که تاسو د سیلین UI کارولو څخه خوند اخلئ ، په پلورنځي کې بیاکتنه پریږدئ -\n    ستاسو نظر د پروژې وده او وده کې مرسته کوي.\n  title: Seelen UI ته ښه راغلاست!\nwidget:\n  enable: دې ویجیټ وړ کړئ\n  enable_for_monitor: پدې څارنه کې وړ کړئ\n  instances: مثالونه\nwm:\n  animations:\n    duration: د انیمیشن موده (MS)\n    ease_function: د انیمیشن ډیک فعالیت\n    enable: د کړکۍ انکار کول فعال کړئ\n  author: لیکوال\n  border:\n    enable: د کړکۍ پوله فعال کړئ\n    offset: پوله\n    width: د بريد سور\n  description: تفصیل\n  drag_behavior: د چلولو چلند\n  drag_behavior_options:\n    sort: ترتیب کړئ (د ځړولو پرمهال کړکۍ بیا تنظیم کړئ)\n    swap: تبادله (د ډریګ په پای کې کړکۍ بدلول)\n  enable: د ټی بایټس کړکۍ مدیر فعال کړئ\n  layout: ترتیب\n  resize_delta: د ډیلټا (٪) ریموټ کړئ\n  space_between_containers: د کانتینرونو ترمینځ ځای\n  workspace_offset: د کاري ځای ځایونه (مارجینز)\n  workspace_padding: د کارډا ریسس\n'yes': هو\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/pt-BR.yml",
    "content": "action:\n  confirm: Tem certeza?\n  confirm_body: Esta ação não pode ser desfeita.\napps_configurations:\n  app:\n    bindings: Vinculação (ambas as opções são obrigatórias)\n    category: Categoria\n    category_placeholder: Nenhum\n    monitor: Monitor\n    monitor_placeholder: Nenhum\n    name: Nome\n    ok_create: Criar\n    ok_edit: Atualizar\n    ok_readonly: Editar como novo\n    options:\n      NoInteractive: Não interativo\n      VdPinned: Mostrar em todos os espaços de trabalho\n      WmFloat: Twm - Comece a flutuar\n      WmForce: Twm - Forçar Gerenciamento\n      WmUnmanage: Twm - Não gerenciar\n    options_label: Opções extras\n    title_create: Criando {{name}}\n    title_edit: Editando {{name}}\n    title_readonly: Visualizando {{name}}\n    weg_options_label: Opções da dock/barra de tarefas\n    wm_options_label: Opções do gerenciador de janelas\n    workspace: Área de trabalho\n    workspace_placeholder: Nenhum\n  bundled_msg: >-\n    Essas configurações agrupadas não são editáveis e foram projetadas para\n    fornecer a melhor experiência sem personalização. Elas configuram\n    automaticamente os aplicativos mais comuns para você.\n  bundled_title: Configurações de Aplicativos incluídas no Seelen\n  confirm_delete: Tem certeza de que deseja excluir esta(s) configuração(ões)?\n  confirm_delete_title: Confirmar exclusão\n  delete: Excluir\n  export: Exportar\n  export_full: Exportar configurações por aplicativo\n  extra_info: >-\n    A Seelen UI usa apenas um identificador por aplicativo (primeira\n    correspondência encontrada); portanto, a ordem é importante. O último\n    adicionado tem prioridade. A tabela é ordenada do mais recente ao mais\n    antigo por padrão.\n  identifier:\n    add_block: Adicionar bloco\n    and: E\n    id: Identificador\n    kind: Identificar por\n    matching_strategy: Estratégia de correspondência\n    matching_strategy_option:\n      contains: Contém\n      ends_with: Termina com\n      equals: Igual\n      regex: Expressão regular\n      starts_with: Começa com\n    negation: Negar correspondência\n    or: OU\n    remove: Excluir bloco\n    type:\n      class: Classe\n      exe: Exe\n      path: Caminho\n      title: Título\n  import: Importar\n  import_full: Importar configurações por aplicativo\n  new: Novo\n  search: Pesquisar\n  swap: Trocar\ncancel: Cancelar\nclose: Fechar\ndelete: Excluir\ndevtools:\n  app_folders: Pastas de Aplicativos\n  custom_config_file: Carregar Arquivo de Configuração Personalizado\n  data_folder: Pasta de Dados\n  enable: Ativar Ferramentas de Desenvolvedor\n  install_folder: Pasta de Instalação\n  load: Carregar\n  settings_file: Arquivo de Configurações\n  simulate_perm:\n    label: Simular solicitação de permissão de widget\n    result_allowed: ✓ Permissão concedida\n    result_denied: ✗ Permissão negada\n    trigger: Simular\n    widget_id_placeholder: '@autor/nome do widget'\nextras:\n  clear_icons: Limpar cache de ícones do sistema\n  clear_icons_tooltip: Pode ser necessário reiniciar para aplicar a todos os widgets\n  exit: Sair\n  links: Links Oficiais\n  relaunch: Reiniciar\n  version: Versão\n  version_fixed: >-\n    As versões do aplicativo e do WebView2 Runtime foram corrigidas. Isso\n    significa que o aplicativo não receberá atualizações e o WebView2 Runtime\n    não será atualizado automaticamente com as atualizações do Windows.\ngeneral:\n  accent_color: Cor de destaque\n  date_format: Formato de data\n  date_format_how_to: Como escrever um formato de data?\n  hardware_acceleration: Aceleração de Hardware\n  hardware_acceleration_description: >-\n    Desativar a aceleração de hardware reduzirá o uso de memória, mas poderá\n    causar problemas de desempenho. É seguro desativar se você não usar papéis\n    de parede animados.\n  icon_pack:\n    available: Pacotes de ícones disponíveis\n    selected: Pacotes de ícones ativos\n  language: Idioma\n  monday: Segunda-feira\n  performance_mode:\n    on_battery: Na bateria\n    on_energy_saver: Em economia de energia\n    options:\n      disabled: Desabilitado\n      extreme: Extremo\n      minimal: Mínimo\n    plugged: Conectado ou carregamento\n  polling_interval: Intervalo de pesquisa do sistema\n  polling_interval_description: >-\n    Com que frequência (em segundos) o Seelen UI verifica os recursos do\n    sistema, como CPU, RAM, rede e atividade de disco. Um número menor significa\n    atualizações mais frequentes, mas um uso de recursos um pouco maior.\n  saturday: Sábado\n  start_of_week: Início da semana\n  startup: Executar na inicialização?\n  sunday: Domingo\n  theme:\n    available: Temas disponíveis\n    selected: Temas ativos\nheader:\n  labels:\n    config: Configurações\n    developer: Para desenvolvedores\n    extras: Extras\n    general: Geral\n    home: Início\n    icon_pack_editor: Ícones em cache\n    iconpack: Pacotes de Ícones\n    monitors: Monitores\n    plugin: Plugins\n    resources: Recursos\n    shortcuts: Atalhos\n    soundpack: Pacotes de Som\n    specific_apps: Configurações por Aplicativo\n    theme: Temas\n    virtual_desk: Áreas de Trabalho Virtuais\n    wallpaper: Papéis de Parede\n    widget: Widgets\nhome:\n  new_resources: Novos Recursos\ninherit: Herdar\ninProgress: Em andamento...\ninsert: Inserir\nloading: Carregando...\nmiscellaneous: Miscelânea\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Mais\n'no': Não\nopen: Abrir\nquit: Desistir\nremove: Remover\nreset_all_to_default: Redefinir tudo para os valores padrão\nreset_to_default: Redefinir para o valor padrão\nresources:\n  app_outdated: >-\n    Esse recurso requer uma versão mais recente da interface do usuário da\n    Seelen para funcionar corretamente.\n  corrupted_wallpaper: Falha ao extrair miniatura – formato de vídeo corrompido ou incompatível\n  delete: Excluir recursos\n  discover: Descubra mais recursos\n  has_update: Uma atualização está disponível\n  high_impact: Alto impacto no desempenho\n  import_wallpapers: Importar papéis de parede locais\n  open_folder: Abrir a pasta de recursos\n  outdated: >-\n    Esse recurso foi projetado para uma versão mais antiga da interface do\n    usuário da Seelen e pode não funcionar corretamente.\n  see_on_website: Veja no site\nreview_request:\n  not_now: Agora não\n  sure: Clara!\n  title: Gostando da interface do Seelen?\nsave: Salvar\nsave_and_restart: Salve e reinicie\nsearch: Procurar\nsee_more: Veja mais\nshortcuts:\n  duplicate_error: Este atalho está duplicado\n  enable: Habilitar o sistema de atalhos embutidos\n  enable_tooltip: >-\n    Desative se você implementar seu próprio sistema de atalhos usando o Seelen\n    UI Client\n  labels:\n    create_new_workspace: Crie um novo espaço de trabalho\n    cycle_stack_next: Pilha de ciclo a seguir\n    cycle_stack_prev: Pilha de ciclo anterior\n    cycle_wallpaper_next: Mudar para o próximo papel de parede\n    cycle_wallpaper_prev: Mudar para papel de parede anterior\n    decrease_height: Diminuir a altura\n    decrease_width: Diminuir a largura\n    destroy_current_workspace: Destrua o espaço de trabalho atual\n    focus_bottom: Foco no fundo\n    focus_latest: Foco mais recente\n    focus_left: Foco restante\n    focus_right: Foco certo\n    focus_top: Focus Top\n    increase_height: Aumentar a altura\n    increase_width: Aumentar a largura\n    misc_force_quit: Force parou\n    misc_force_restart: Forçar reinicialização\n    misc_open_settings: Abra as configurações\n    misc_toggle_lock_tracing: Traçar o traçado de trava (logs)\n    misc_toggle_win_event_tracing: Alternar o rastreamento do evento Win (logs)\n    move_to_workspace: Mova para o espaço de trabalho {{0}}\n    move_window_down: Mova a janela para o fundo\n    move_window_left: Mova a janela para a esquerda\n    move_window_right: Mova a janela para a direita\n    move_window_up: Mova a janela para o topo\n    pause_tiling: PAUSA PAUSE JANEGENTE DE JANDA TILING\n    reserve_bottom: Fundo de reserva\n    reserve_float: Reserve flutuar\n    reserve_left: Reserve à esquerda\n    reserve_right: Reserve certo\n    reserve_stack: Pilha de reserva\n    reserve_top: Reserve Top\n    restore_sizes: Restaurar tamanhos\n    send_to_workspace: Enviar para o espaço de trabalho {{0}}\n    start_weg_app: Concentre ou inicie o aplicativo {{0}}\n    switch_to_next_workspace: Mude para o próximo espaço de trabalho\n    switch_to_previous_workspace: Mudar para o espaço de trabalho anterior\n    switch_workspace: Mudar para o espaço de trabalho {{0}}\n    toggle_float: Alterne o modo de flutuação da janela\n    toggle_monocle: Alternar o modo de monocle na área de trabalho\n  readonly_tooltip: Este é um atalho somente leitura\n  reset: Redefinir para padrões\nsides:\n  bottom: Abaixo\n  left: Esquerda\n  right: Direita\n  top: Topo\ntoolbar:\n  auto_hide: Esconder automaticamente\n  delay_to_hide: Demora para esconder\n  delay_to_show: Demora para mostrar\n  dock_side: Posição\n  enable: Ativar barra de ferramentas sofisticada\n  hide_mode:\n    always: Sempre\n    never: Nunca\n    on_overlap: Na sobreposição\n  item_size: Tamanho do item\n  label: Barra de ferramentas\n  margin: Tamanho da margem\n  padding: Tamanho do preenchimento\n  placeholder: {}\nupdate:\n  available: Atualização disponível!\n  channel: Atualizar canal\n  downloading: Download ...\nwall:\n  backgrounds: Papéis de parede\n  blur: Desfoque\n  cancel: Cancelar\n  close: Fechar\n  collection_name: Nome da coleção\n  collections: Coleções\n  contrast: Contraste\n  corrupted_wallpapers_message: 'Papéis de parede corrompidos, considere excluí-los:'\n  create: Criar\n  create_collection: Criar coleção\n  default_collection: Coleção padrão\n  delete_collection: Excluir coleção\n  edit_collection: Editar coleção\n  enable: Ativar gerente de papel de parede\n  extend: Estender primário\n  fit:\n    contain: Conter\n    cover: Capa\n    fill: Preencher\n  flipHorizontal: Virar na horizontal\n  flipVertical: Virar vertical\n  generating_thumbnails: Gerando miniaturas de papel de parede\n  hours: horas\n  interval: Troque o papel de parede todos\n  minutes: minutos\n  monitor_collection: Coleção de monitores\n  multimonitor_behaviour: Comportamento multimonitor\n  muted: Silenciar áudio de vídeo\n  no_background: A apresentação de slides vazia, usando o plano de fundo do tema.\n  no_collections: Nenhuma coleção criada ainda. Clique no botão + para criar um.\n  objectFit: Ajuste do plano de fundo\n  objectPosition: Posição de fundo\n  overlayColor: Cor da sobreposição\n  overlayMixBlendMode: Overlay Mix Blend Mode (Modo de mistura de sobreposição)\n  per_monitor: Por monitor\n  playback: Velocidade de reprodução\n  position:\n    bottom: Parte inferior\n    center: Centro\n    left: Esquerda\n    right: Certo\n    top: Topo\n  processing_video: Processando vídeo\n  random: Randomize Slideshow\n  saturation: Saturação\n  seconds: segundas\n  select_collection: Selecione a coleção\n  thumbnail_generation_complete: Geração de miniaturas concluída\n  thumbnail_generation_finished: A geração da miniatura foi concluída com sucesso\n  wallpaper_collection: Coleção de papéis de parede\n  wallpaper_settings: Configurações de papel de parede\n  withOverlay: Com sobreposição\n  workspace_collections: Coleções de espaço de trabalho\nweg:\n  auto_hide: Ocultar automaticamente\n  delay_to_hide: Demora para esconder\n  delay_to_show: Demora para mostrar\n  dock_side: Posição\n  enable: Habilitar Dock/Barra de Tarefas\n  filtering: Filtragem de itens\n  gap: Espaçamento\n  hide_mode:\n    always: Sempre\n    never: Nunca\n    on_overlap: Na sobreposição\n  items:\n    gap: Espaço entre itens\n    label: Item\n    pinned_visibility:\n      always: Sempre\n      label: Visibilidade dos itens fixados\n      when_primary: Quando o monitor é primário\n    show_instance_counter: Mostre o balcão do Windows aberto\n    show_window_title: Mostrar título de janela aberta (apenas horizontal)\n    size: Tamanho do item\n    split_windows: Dividir janelas (um item por janela)\n    temporal_visibility:\n      all: Todos\n      label: Visibilidade de itens não fixados\n      on_monitor: No monitor\n    visible_separators: Separadores Visíveis\n  label: Dock/barra de tarefas\n  margin: Margem\n  mode:\n    full_width: Largura de tela inteira\n    min_content: Pequeno como pode ser\n  padding: Preenchimento\n  show_end_task: Mostrar tarefa final na barra de tarefas\n  width: Largura\nwelcome:\n  give_a_review: Dê uma avaliação\n  message: >-\n    Seelen UI é um ambiente de desktop gratuito e de código aberto para Windows.\n    Aqui você tem controle total sobre a aparência e o comportamento de sua área\n    de trabalho, permitindo personalizar cada detalhe para combinar com seu\n    fluxo de trabalho e estilo.\n  ok: Vamos começar!\n  review: >-\n    Se você gosta de usar a UI Seelen, considere deixar um comentário na loja –\n    seu feedback ajuda o projeto a crescer e melhorar.\n  title: Bem-vindo à UI da Seelen!\nwidget:\n  enable: Ativar esse widget\n  enable_for_monitor: Ativar neste monitor\n  instances: Instâncias\nwm:\n  animations:\n    duration: Duração da animação (MS)\n    ease_function: Função de flexibilização da animação\n    enable: Ativar animações da Window\n  author: Autor\n  border:\n    enable: Habilitar borda da janela\n    offset: Deslocamento de borda\n    width: Largura da borda\n  description: Descrição\n  drag_behavior: Comportamento de arrastar\n  drag_behavior_options:\n    sort: Classificar (reordenar janelas enquanto arrasta)\n    swap: Trocar (trocar janelas ao arrastar)\n  enable: Habilitar gerenciador de janela de ladrilhos\n  layout: Disposição\n  resize_delta: Delta de redimensionamento (%)\n  space_between_containers: Espaço entre contêineres\n  workspace_offset: Deslocamento de espaços de trabalho (margens)\n  workspace_padding: Preenchimento de espaços de trabalho\n'yes': Sim\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/pt-PT.yml",
    "content": "action:\n  confirm: Tem certeza?\n  confirm_body: Esta ação não pode ser desfeita.\napps_configurations:\n  app:\n    bindings: Vinculação (observe que ambas as opções são necessárias)\n    category: Categoria\n    category_placeholder: Nenhum\n    monitor: Monitor\n    monitor_placeholder: Nenhum\n    name: Nome\n    ok_create: Criar\n    ok_edit: Atualização\n    ok_readonly: Editar como novo\n    options:\n      NoInteractive: Não interativo\n      VdPinned: Mostrar em todos os espaços de trabalho\n      WmFloat: Twm - Comece a flutuar\n      WmForce: Twm - Forçar Gerenciamento\n      WmUnmanage: Twm - Não gerenciar\n    options_label: Opções extras\n    title_create: Criando {{nome}}\n    title_edit: Edição {{name}}\n    title_readonly: Visualizando {{nome}}\n    weg_options_label: Opções de doca/barra de tarefas\n    wm_options_label: Opções do gerenciador de janelas\n    workspace: Espaço de trabalho\n    workspace_placeholder: Nenhum\n  bundled_msg: >-\n    Essas configurações agrupadas não são editáveis e foram projetadas para\n    fornecer a melhor experiência sem personalização. Eles configuram\n    automaticamente os aplicativos mais comuns para você.\n  bundled_title: App Config pacote com Seelen\n  confirm_delete: Tem certeza de que deseja excluir esta configuração/s?\n  confirm_delete_title: Confirme excluir\n  delete: Eliminar\n  export: Exportar\n  export_full: Configurações de exportação por aplicação\n  extra_info: >-\n    A interface do usuário da Seelen usa apenas um identificador por aplicativo\n    (primeira partida encontrada); portanto, o pedido em como são específicos é\n    importante, o mais recente adicionado será priorizado, pois note que a\n    tabela é classificada por padrão do mais recente ao antigo.\n  identifier:\n    add_block: Adicione o bloco\n    and: E\n    id: Identificador\n    kind: Identificar por\n    matching_strategy: Estratégia correspondente\n    matching_strategy_option:\n      contains: Contém\n      ends_with: Termina com\n      equals: Igual a\n      regex: Expressão regular\n      starts_with: Começa com\n    negation: Negar a correspondência\n    or: OU\n    remove: Excluir bloco\n    type:\n      class: Classe\n      exe: Exe\n      path: Percurso\n      title: Título\n  import: Importação\n  import_full: Importar configurações por aplicação\n  new: Nova\n  search: Pesquisa\n  swap: Permuta\ncancel: Cancelar\nclose: Perto\ndelete: Eliminar\ndevtools:\n  app_folders: Pastas de aplicativos\n  custom_config_file: Carregar o arquivo de configuração personalizado\n  data_folder: Pasta de dados\n  enable: Ativar ferramentas de desenvolvedor\n  install_folder: Pasta de instalação\n  load: Carga\n  settings_file: Arquivo de configurações\n  simulate_perm:\n    label: Simular pedido de permissão de widget\n    result_allowed: ✓ Permissão concedida\n    result_denied: ✗ Permissão negada\n    trigger: Simular\n    widget_id_placeholder: '@autor/nome do widget'\nextras:\n  clear_icons: Ícones do sistema limpo cache\n  clear_icons_tooltip: Pode ser necessário reinício para entrar em vigor em todos os widgets\n  exit: Desistir/sair\n  links: Links oficiais\n  relaunch: Relançar\n  version: Versão\n  version_fixed: >-\n    As versões do aplicativo e do WebView2 Runtime foram corrigidas. Isso\n    significa que o aplicativo não receberá atualizações e o WebView2 Runtime\n    não será atualizado automaticamente com as atualizações do Windows.\ngeneral:\n  accent_color: Cor de destaque\n  date_format: Formato de data\n  date_format_how_to: Como escrever um formato de data?\n  hardware_acceleration: Aceleração de Hardware\n  hardware_acceleration_description: >-\n    Desativar a aceleração de hardware reduzirá a utilização de memória, mas\n    poderá causar problemas de desempenho. É seguro desativar se não utilizar\n    papéis de parede animados.\n  icon_pack:\n    available: Pacotes de ícones disponíveis\n    selected: Pacotes de ícones ativos\n  language: Idioma\n  monday: Segunda-feira\n  performance_mode:\n    on_battery: Na bateria\n    on_energy_saver: Em economia de energia\n    options:\n      disabled: Desativado\n      extreme: Extremo\n      minimal: Mínimo\n    plugged: Conectado ou carregamento\n  polling_interval: Intervalo de pesquisa do sistema\n  polling_interval_description: >-\n    Com que frequência (em segundos) o Seelen UI verifica os recursos do\n    sistema, como CPU, RAM, rede e atividade de disco. Um número mais reduzido\n    significa atualizações mais frequentes, mas uma utilização de recursos\n    ligeiramente maior.\n  saturday: Sábado\n  start_of_week: Início da semana\n  startup: Correr na inicialização?\n  sunday: Domingo\n  theme:\n    available: Temas disponíveis\n    selected: Temas ativos\nheader:\n  labels:\n    config: Configurações\n    developer: Para desenvolvedores\n    extras: Extras\n    general: General\n    home: Casa\n    icon_pack_editor: Ícones em cache\n    iconpack: Pacotes de ícones\n    monitors: Monitores\n    plugin: Plugins\n    resources: Recursos\n    shortcuts: Atalhos\n    soundpack: Pacotes de som\n    specific_apps: Configurações por aplicação\n    theme: Temas\n    virtual_desk: Desktops virtuais\n    wallpaper: Papéis de parede\n    widget: Widgets\nhome:\n  new_resources: Novos recursos\ninherit: Herdar\ninProgress: Em andamento...\ninsert: Inserir\nloading: Carregando...\nmiscellaneous: Diversas\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Mais\n'no': Não\nopen: Aberto\nquit: Despedir-se\nremove: Remover\nreset_all_to_default: Redefina tudo para valores padrão\nreset_to_default: Redefinir para o valor padrão\nresources:\n  app_outdated: >-\n    Este recurso exige que uma versão mais recente da UI Seelen funcione\n    corretamente.\n  corrupted_wallpaper: Falha ao extrair miniatura – formato de vídeo corrompido ou incompatível\n  delete: Excluir recursos\n  discover: Descubra mais recursos\n  has_update: Uma atualização está disponível\n  high_impact: Alto impacto no desempenho\n  import_wallpapers: Importar papéis de parede locais\n  open_folder: Pasta de recursos aberta\n  outdated: >-\n    Esse recurso foi projetado para uma versão mais antiga da UI Seelen e pode\n    não funcionar corretamente.\n  see_on_website: Veja no site\nreview_request:\n  not_now: Agora não\n  sure: Clara!\n  title: Gostando da interface do Seelen?\nsave: Guardar\nsave_and_restart: Salve e reinicie\nsearch: Pesquisa\nsee_more: Veja mais\nshortcuts:\n  duplicate_error: Este atalho está duplicado\n  enable: Habilitar o sistema de atalhos embutidos\n  enable_tooltip: >-\n    Desative se você implementar seu próprio sistema de atalhos usando o Seelen\n    UI Client\n  labels:\n    create_new_workspace: Crie um novo espaço de trabalho\n    cycle_stack_next: Pilha de ciclo a seguir\n    cycle_stack_prev: Pilha de ciclo anterior\n    cycle_wallpaper_next: Mudar para o próximo papel de parede\n    cycle_wallpaper_prev: Mudar para papel de parede anterior\n    decrease_height: Diminuir a altura\n    decrease_width: Diminuir a largura\n    destroy_current_workspace: Destrua o espaço de trabalho atual\n    focus_bottom: Foco no fundo\n    focus_latest: Foco mais recente\n    focus_left: Foco restante\n    focus_right: Foco certo\n    focus_top: Focus Top\n    increase_height: Aumentar a altura\n    increase_width: Aumentar a largura\n    misc_force_quit: Force parou\n    misc_force_restart: Reiniciar força\n    misc_open_settings: Abra as configurações\n    misc_toggle_lock_tracing: Traçar o traçado de trava (logs)\n    misc_toggle_win_event_tracing: Alternar o rastreamento do evento Win (logs)\n    move_to_workspace: Mova para o espaço de trabalho {{0}}\n    move_window_down: Mova a janela para o fundo\n    move_window_left: Mova a janela para a esquerda\n    move_window_right: Mova a janela para a direita\n    move_window_up: Mova a janela para o topo\n    pause_tiling: PAUSA PAUSE JANEGENTE DE JANDA TILING\n    reserve_bottom: Fundo de reserva\n    reserve_float: Reserve flutuar\n    reserve_left: Reserve à esquerda\n    reserve_right: Reserve certo\n    reserve_stack: Pilha de reserva\n    reserve_top: Reserve Top\n    restore_sizes: Restaurar tamanhos\n    send_to_workspace: Enviar para o espaço de trabalho {{0}}\n    start_weg_app: Concentre ou inicie o aplicativo {{0}}\n    switch_to_next_workspace: Mude para o próximo espaço de trabalho\n    switch_to_previous_workspace: Mudar para o espaço de trabalho anterior\n    switch_workspace: Mudar para o espaço de trabalho {{0}}\n    toggle_float: Alterne o modo de flutuação da janela\n    toggle_monocle: Alternar o modo de monocle na área de trabalho\n  readonly_tooltip: Este é um atalho somente leitura\n  reset: Redefinir para padrões\nsides:\n  bottom: Fundo\n  left: Esquerda\n  right: Certa\n  top: Topo\ntoolbar:\n  auto_hide: Esconda automática\n  delay_to_hide: Atraso para se esconder\n  delay_to_show: Atraso para mostrar\n  dock_side: Posição\n  enable: Ativar barra de ferramentas sofisticada\n  hide_mode:\n    always: Sempre\n    never: Nunca\n    on_overlap: Em sobreposição\n  item_size: Tamanho do artigo\n  label: Barra de ferramentas\n  margin: Tamanho da margem\n  padding: Tamanho do preenchimento\n  placeholder: {}\nupdate:\n  available: Atualização disponível!\n  channel: Atualizar canal\n  downloading: Download ...\nwall:\n  backgrounds: Papéis de parede\n  blur: Borrão\n  cancel: Cancelar\n  close: Perto\n  collection_name: Nome da coleção\n  collections: Coleções\n  contrast: Contraste\n  corrupted_wallpapers_message: 'Papéis de parede corrompidos, considere excluí-los:'\n  create: Criar\n  create_collection: Criar coleção\n  default_collection: Coleção padrão\n  delete_collection: Excluir coleção\n  edit_collection: Editar coleção\n  enable: Ativar gerente de papel de parede\n  extend: Estender primário\n  fit:\n    contain: Conter\n    cover: Cobertura\n    fill: Preencher\n  flipHorizontal: Flip Horizontal\n  flipVertical: Flip vertical\n  generating_thumbnails: Gerando miniaturas de papel de parede\n  hours: horas\n  interval: Troque o papel de parede todos\n  minutes: minutos\n  monitor_collection: Coleção de monitores\n  multimonitor_behaviour: Comportamento multimonitor\n  muted: Silenciar áudio de vídeo\n  no_background: A apresentação de slides vazia, usando o plano de fundo do tema.\n  no_collections: Nenhuma coleção criada ainda. Clique no botão + para criar um.\n  objectFit: Ajuste de fundo\n  objectPosition: Posição de fundo\n  overlayColor: Cor de sobreposição\n  overlayMixBlendMode: Modo de mistura de sobreposição\n  per_monitor: Por monitor\n  playback: Velocidade de reprodução\n  position:\n    bottom: Fundo\n    center: Centro\n    left: Esquerda\n    right: Certa\n    top: Topo\n  processing_video: Processando vídeo\n  random: Randomize Slideshow\n  saturation: Saturação\n  seconds: segundas\n  select_collection: Selecione a coleção\n  thumbnail_generation_complete: Geração de miniaturas concluída\n  thumbnail_generation_finished: A geração da miniatura foi concluída com sucesso\n  wallpaper_collection: Coleção de papéis de parede\n  wallpaper_settings: Configurações de papel de parede\n  withOverlay: Com sobreposição\n  workspace_collections: Coleções de espaço de trabalho\nweg:\n  auto_hide: Esconda automática\n  delay_to_hide: Atraso para se esconder\n  delay_to_show: Atraso para mostrar\n  dock_side: Posição\n  enable: Habilitar dock/barra de tarefas\n  filtering: Filtragem de itens\n  gap: Lacuna\n  hide_mode:\n    always: Sempre\n    never: Nunca\n    on_overlap: Em sobreposição\n  items:\n    gap: Espaço entre itens\n    label: Artigos\n    pinned_visibility:\n      always: Sempre\n      label: Visibilidade dos itens fixados\n      when_primary: Quando o monitor é primário\n    show_instance_counter: Mostre o balcão do Windows aberto\n    show_window_title: Mostrar título de janela aberta (apenas horizontal)\n    size: Tamanho do item\n    split_windows: Dividir janelas (um item por janela)\n    temporal_visibility:\n      all: Tudo\n      label: Visibilidade dos itens não ingênicos\n      on_monitor: No monitor\n    visible_separators: Separadores visíveis\n  label: Dock/Task Bar\n  margin: Margem\n  mode:\n    full_width: Largura da tela inteira\n    min_content: Pequeno como pode ser\n  padding: Preenchimento\n  show_end_task: Mostre a tarefa final na barra de tarefas\n  width: Largura\nwelcome:\n  give_a_review: Dê uma avaliação\n  message: >-\n    Seelen UI é um ambiente de desktop gratuito e de código aberto para Windows.\n    Aqui você tem controle total sobre a aparência e o comportamento de sua área\n    de trabalho, permitindo personalizar cada detalhe para combinar com seu\n    fluxo de trabalho e estilo.\n  ok: Vamos começar!\n  review: >-\n    Se você gosta de usar a UI Seelen, considere deixar um comentário na loja –\n    seu feedback ajuda o projeto a crescer e melhorar.\n  title: Bem-vindo à UI da Seelen!\nwidget:\n  enable: Ative este widget\n  enable_for_monitor: Ativar neste monitor\n  instances: Instâncias\nwm:\n  animations:\n    duration: Duração da animação (MS)\n    ease_function: Função de flexibilização da animação\n    enable: Ativar animações da Window\n  author: Autora\n  border:\n    enable: Ativar a fronteira da janela\n    offset: Offset de borda\n    width: Largura da fronteira\n  description: Descrição\n  drag_behavior: Comportamento de arrastar\n  drag_behavior_options:\n    sort: Classificar (reordenar janelas enquanto arrasta)\n    swap: Trocar (trocar janelas ao arrastar)\n  enable: Habilitar gerente de janela de ladrilhos\n  layout: Esquema\n  resize_delta: Redimensionar delta (%)\n  space_between_containers: Espaço entre recipientes\n  workspace_offset: Espaços de trabalho Offset (margens)\n  workspace_padding: Espaços de trabalho preenchem\n'yes': Sim\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ro.yml",
    "content": "action:\n  confirm: Ești sigur?\n  confirm_body: Această acțiune nu poate fi anulată.\napps_configurations:\n  app:\n    bindings: Legare (notă ambele opțiuni sunt necesare)\n    category: Categorie\n    category_placeholder: Nici unul\n    monitor: Monitor\n    monitor_placeholder: Nici unul\n    name: Nume\n    ok_create: Crea\n    ok_edit: Actualizați\n    ok_readonly: Editați ca nou\n    options:\n      NoInteractive: Nu interactiv\n      VdPinned: Afișați în toate spațiile de lucru\n      WmFloat: Twm - Începeți să plutească\n      WmForce: Twm - Gestionarea forței\n      WmUnmanage: Twm - Unmanage\n    options_label: Opțiuni suplimentare\n    title_create: Crearea {{nume}}\n    title_edit: Editare {{nume}}\n    title_readonly: Vizualizare {{nume}}\n    weg_options_label: Opțiuni de doc/bara de activități\n    wm_options_label: Opțiuni manager de ferestre\n    workspace: Spațiu de lucru\n    workspace_placeholder: Nici unul\n  bundled_msg: >-\n    Aceste configurații în pachet nu sunt editabile și sunt concepute pentru a\n    vă oferi cea mai bună experiență fără personalizare. Acestea configurează\n    automat cele mai frecvente aplicații pentru dvs.\n  bundled_title: App Config Bundled cu Seelen\n  confirm_delete: Ești sigur că vrei să ștergi această configurație/s?\n  confirm_delete_title: Confirmă ștergerea\n  delete: Șterge\n  export: Export\n  export_full: Exportul setărilor în funcție de aplicație\n  extra_info: >-\n    Seelen UI folosește un singur identificator pe aplicație (prima potrivire\n    găsită), astfel încât ordinea în modul în care este specificată este\n    importantă, cea mai recentă adăugată va fi prioritară, deoarece notă tabelul\n    este sortat în mod implicit de la cel mai recent la vechi.\n  identifier:\n    add_block: Adăugați bloc\n    and: ȘI\n    id: Identificator\n    kind: Identifică de\n    matching_strategy: Strategia de potrivire\n    matching_strategy_option:\n      contains: Conține\n      ends_with: Se termină cu\n      equals: Egal\n      regex: Expresia regulată\n      starts_with: Începe cu\n    negation: Negate potrivire\n    or: SAU\n    remove: Ștergeți blocul\n    type:\n      class: Clasă\n      exe: Exe\n      path: Cale\n      title: Titlu\n  import: Import\n  import_full: Importul setărilor în funcție de aplicație\n  new: Nou\n  search: Căutare\n  swap: Swap\ncancel: Anulare\nclose: Aproape\ndelete: Șterge\ndevtools:\n  app_folders: Foldere de aplicații\n  custom_config_file: Încărcați fișierul de configurare personalizat\n  data_folder: Folder de date\n  enable: Activați instrumentele pentru dezvoltatori\n  install_folder: Folder de instalare\n  load: Sarcină\n  settings_file: Fișier de setări\n  simulate_perm:\n    label: Simulați cererea de permisiune pentru widget\n    result_allowed: ✓ Permisiune acordată\n    result_denied: ✗ Permisiune refuzată\n    trigger: Simula\n    widget_id_placeholder: '@autor/nume-widget'\nextras:\n  clear_icons: Ștergeți memoria cache a pictogramelor de sistem\n  clear_icons_tooltip: >-\n    Ar putea fi necesară o repornire pentru ca toate widget-urile să intre în\n    vigoare\n  exit: Renunță/ieșire\n  links: Link -uri oficiale\n  relaunch: Relansare\n  version: Versiune\n  version_fixed: >-\n    Aplicația și versiunile WebView2 Runtime sunt remediate. Aceasta înseamnă că\n    aplicația nu va primi actualizări și WebView2 Runtime nu va fi actualizat\n    automat cu actualizări Windows.\ngeneral:\n  accent_color: Culoare de accent\n  date_format: Formatul datei\n  date_format_how_to: Cum se scrie un format de dată?\n  hardware_acceleration: Accelerație hardware\n  hardware_acceleration_description: >-\n    Dezactivarea accelerației hardware va reduce utilizarea memoriei, dar poate\n    cauza probleme de performanță. Este sigur de dezactivat dacă nu folosiți\n    imagini de fundal live.\n  icon_pack:\n    available: Pachete de pictograme disponibile\n    selected: Pachete de pictograme active\n  language: Limba\n  monday: luni\n  performance_mode:\n    on_battery: Pe baterie\n    on_energy_saver: Pe economia de energie\n    options:\n      disabled: Dezactivat\n      extreme: Extrem\n      minimal: Minim\n    plugged: Conectat sau încărcat\n  polling_interval: Interval de interogare a sistemului\n  polling_interval_description: >-\n    Cât de des (în secunde) Seelen UI verifică resursele sistemului, cum ar fi\n    CPU, RAM, rețea și activitatea discului. Un număr mai mic înseamnă\n    actualizări mai frecvente, dar o utilizare ușor mai mare a resurselor.\n  saturday: sâmbătă\n  start_of_week: Începutul săptămânii\n  startup: Rulați la pornire?\n  sunday: duminică\n  theme:\n    available: Teme disponibile\n    selected: Teme active\nheader:\n  labels:\n    config: Configurații\n    developer: Pentru dezvoltatori\n    extras: Extras\n    general: General\n    home: Acasă\n    icon_pack_editor: Icoane în cache\n    iconpack: Pachete de pictograme\n    monitors: Monitoare\n    plugin: Plugin-uri\n    resources: Resurse\n    shortcuts: Comitete rapide\n    soundpack: Pachete de sunet\n    specific_apps: Setări în funcție de aplicație\n    theme: Teme\n    virtual_desk: Birouri virtuale\n    wallpaper: Imagini de fundal\n    widget: Widget-uri\nhome:\n  new_resources: Resurse noi\ninherit: Moşteni\ninProgress: În curs...\ninsert: Introduce\nloading: Se încarcă...\nmiscellaneous: Diverse\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Mai mult\n'no': Nu\nopen: Deschis\nquit: Părăsi\nremove: Elimina\nreset_all_to_default: Resetați toate la valorile implicite\nreset_to_default: Resetare la valoarea implicită\nresources:\n  app_outdated: >-\n    Această resursă necesită o versiune mai recentă a Seelen UI pentru a\n    funcționa corect.\n  corrupted_wallpaper: Nu s-a extras miniatura - format video corupt sau neacceptat\n  delete: Ștergeți resursa\n  discover: Descoperiți mai multe resurse\n  has_update: O actualizare este disponibilă\n  high_impact: Impact mare asupra performanței\n  import_wallpapers: Importați imagini de fundal locale\n  open_folder: Deschideți folderul de resurse\n  outdated: >-\n    Această resursă a fost proiectată pentru o versiune mai veche a Seelen UI și\n    este posibil să nu funcționeze corect.\n  see_on_website: Vezi pe site\nreview_request:\n  not_now: Nu acum\n  sure: Sigur!\n  title: Vă bucurați de Seelen UI?\nsave: Salvați\nsave_and_restart: Salvați și reporniți\nsearch: Căutare\nsee_more: A se vedea mai mult\nshortcuts:\n  duplicate_error: Această comandă rapidă este duplicată\n  enable: Activați sistemul de comenzi rapide încorporate\n  enable_tooltip: >-\n    Dezactivați dacă veți implementa propriul sistem de comenzi rapide folosind\n    clientul UI Seelen\n  labels:\n    create_new_workspace: Creați un spațiu de lucru nou\n    cycle_stack_next: Ciclism Stack în continuare\n    cycle_stack_prev: Stack cycle anterior\n    cycle_wallpaper_next: Schimbați la următorul tapet\n    cycle_wallpaper_prev: Schimbați la tapetul anterior\n    decrease_height: Scăderea înălțimii\n    decrease_width: Scăderea lățimii\n    destroy_current_workspace: Distrugeți spațiul de lucru actual\n    focus_bottom: Focus Fund\n    focus_latest: Concentrați -vă cel mai recent\n    focus_left: Concentrați -vă la stânga\n    focus_right: Concentrați -vă corect\n    focus_top: Focus top\n    increase_height: Crește înălțimea\n    increase_width: Creșteți lățimea\n    misc_force_quit: Forța a renunțat\n    misc_force_restart: Forțați repornirea\n    misc_open_settings: Setări deschise\n    misc_toggle_lock_tracing: Comutarea blocării de blocare (jurnalele)\n    misc_toggle_win_event_tracing: Toggle Win Win Tracing (Jurnals)\n    move_to_workspace: Treceți la Workspace {{0}}\n    move_window_down: Mutați fereastra în jos\n    move_window_left: Mutați fereastra în stânga\n    move_window_right: Mutați fereastra spre dreapta\n    move_window_up: Mutați fereastra în partea de sus\n    pause_tiling: Pauză manager de ferestre de gresie\n    reserve_bottom: Rezervați fundul\n    reserve_float: Rezervați plutitorul\n    reserve_left: Rezerva stânga\n    reserve_right: Rezervați drept\n    reserve_stack: Rezerva stivă\n    reserve_top: Rezervați vârful\n    restore_sizes: Restaurați dimensiunile\n    send_to_workspace: Trimiteți la Workspace {{0}}\n    start_weg_app: Focus sau Start Application {{0}}\n    switch_to_next_workspace: Treceți la următorul spațiu de lucru\n    switch_to_previous_workspace: Treceți la spațiul de lucru anterior\n    switch_workspace: Treceți la Workspace {{0}}\n    toggle_float: Comutați modul de plutire a ferestrei\n    toggle_monocle: Comutați modul monocle al spațiului de lucru\n  readonly_tooltip: Aceasta este o scurtătură numai pentru citire\n  reset: Resetați la valorile implicite\nsides:\n  bottom: Fund\n  left: Stânga\n  right: Dreapta\n  top: Top\ntoolbar:\n  auto_hide: Ascunde auto\n  delay_to_hide: Întârzie să te ascunzi\n  delay_to_show: Întârzieți afișarea\n  dock_side: Poziția\n  enable: Activați bara de instrumente fantezistă\n  hide_mode:\n    always: Întotdeauna\n    never: Nu\n    on_overlap: Pe suprapunere\n  item_size: Dimensiunea articolului\n  label: Bara de instrumente\n  margin: Dimensiunea marginii\n  padding: Dimensiunea căptușelii\n  placeholder: {}\nupdate:\n  available: Actualizare disponibilă!\n  channel: Actualizare canal\n  downloading: Descărcare ...\nwall:\n  backgrounds: Imagini de fundal\n  blur: Ceață\n  cancel: Anula\n  close: Aproape\n  collection_name: Numele colecției\n  collections: Colecții\n  contrast: Contrast\n  corrupted_wallpapers_message: 'Imagini de fundal corupte, luați în considerare ștergerea acestora:'\n  create: Crea\n  create_collection: Creați o colecție\n  default_collection: Colecție implicită\n  delete_collection: Șterge colecția\n  edit_collection: Editați colecția\n  enable: Activați manager de tapet\n  extend: Extindeți primarul\n  fit:\n    contain: Conține\n    cover: Acoperire\n    fill: Umpleți\n  flipHorizontal: Flip orizontal\n  flipVertical: Flip vertical\n  generating_thumbnails: Generarea miniaturilor de fundal\n  hours: ore\n  interval: Schimbați tapet fiecare\n  minutes: minute\n  monitor_collection: Colecția de monitor\n  multimonitor_behaviour: Comportamentul multimonitor\n  muted: Dezactivați sunetul video\n  no_background: Slideshow gol, folosind în schimb fundalul temei.\n  no_collections: Nicio colecție încă creată. Faceți clic pe butonul + pentru a crea unul.\n  objectFit: Potrivire în fundal\n  objectPosition: Poziția de fundal\n  overlayColor: Culoare suprapunere\n  overlayMixBlendMode: Mod de amestecare suprapunere Mix\n  per_monitor: Per monitor\n  playback: Viteza de redare\n  position:\n    bottom: Fund\n    center: Centrul\n    left: Stânga\n    right: Corect\n    top: Sus\n  processing_video: Se procesează videoclipul\n  random: Prezentare de diapozitive aleatorie\n  saturation: Saturație\n  seconds: secunde\n  select_collection: Selectați Colecție\n  thumbnail_generation_complete: Generarea miniaturii finalizată\n  thumbnail_generation_finished: Generarea miniaturilor s-a încheiat cu succes\n  wallpaper_collection: Colecția de imagini de fundal\n  wallpaper_settings: Setări de fundal\n  withOverlay: Cu suprapunere\n  workspace_collections: Colecții de spațiu de lucru\nweg:\n  auto_hide: Ascundere automată\n  delay_to_hide: Întârzie să te ascunzi\n  delay_to_show: Întârzieți afișarea\n  dock_side: Poziţie\n  enable: Activați Dock/Bara de activități\n  filtering: Filtrarea elementelor\n  gap: Decalaj\n  hide_mode:\n    always: Întotdeauna\n    never: Nu\n    on_overlap: Pe suprapunere\n  items:\n    gap: Spațiu între articole\n    label: Articole\n    pinned_visibility:\n      always: Întotdeauna\n      label: Vizibilitatea elementelor fixate\n      when_primary: Când monitorul este principal\n    show_instance_counter: Afișați contorul ferestrelor deschise\n    show_window_title: Afișează titlul ferestrei deschise (numai orizontal)\n    size: Dimensiunea articolului\n    split_windows: Divizarea ferestrelor (un element pe fereastră)\n    temporal_visibility:\n      all: Toate\n      label: Vizibilitatea elementelor nefixate\n      on_monitor: Pe monitor\n    visible_separators: Separatoare vizibile\n  label: Dock/Bara de activități\n  margin: Marjă\n  mode:\n    full_width: Lățimea ecranului complet\n    min_content: Cât de mic poate fi\n  padding: Căptușeală\n  show_end_task: Afișarea sarcinii finale în bara de activități\n  width: Lăţime\nwelcome:\n  give_a_review: Dă o recenzie\n  message: >-\n    Seelen UI este un mediu desktop gratuit și open source pentru Windows. Aici\n    aveți control deplin asupra modului în care arată și se comportă desktopul,\n    permițându-vă să personalizați fiecare detaliu pentru a se potrivi cu fluxul\n    și stilul dvs. de lucru.\n  ok: Să începem!\n  review: >-\n    Dacă vă place să utilizați Seelen UI, luați în considerare să lăsați o\n    recenzie în magazin - feedbackul dvs. ajută proiectul să crească și să se\n    îmbunătățească.\n  title: Bun venit la Seelen UI!\nwidget:\n  enable: Activați acest widget\n  enable_for_monitor: Activați pe acest monitor\n  instances: Instanțe\nwm:\n  animations:\n    duration: Durata de animație (MS)\n    ease_function: Funcția de ușurare a animației\n    enable: Activați animațiile Window\n  author: Autor\n  border:\n    enable: Activați granița ferestrei\n    offset: Offset de frontieră\n    width: Lățimea graniței\n  description: Descriere\n  drag_behavior: Comportament de tragere\n  drag_behavior_options:\n    sort: Sortare (reordonează ferestrele în timp ce trageți)\n    swap: Schimbați (schimbați ferestrele la capătul de tragere)\n  enable: Activați managerul de ferestre de gresie\n  layout: Aspect\n  resize_delta: Redimensionați delta (%)\n  space_between_containers: Spațiu între containere\n  workspace_offset: Spații de lucru Offset (marje)\n  workspace_padding: Padding spații de lucru\n'yes': Da\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ru.yml",
    "content": "action:\n  confirm: Вы уверены?\n  confirm_body: Это действие не может быть отменено.\napps_configurations:\n  app:\n    bindings: Привязка (оба параметра обязательны)\n    category: Категория\n    category_placeholder: Нет\n    monitor: Монитор\n    monitor_placeholder: Нет\n    name: Название\n    ok_create: Создать\n    ok_edit: Обновить\n    ok_readonly: Изменить как новое\n    options:\n      NoInteractive: Нет интерактивности\n      VdPinned: Показать во всех рабочих областях\n      WmFloat: Twm – Начни плавать\n      WmForce: Twm — принудительное управление\n      WmUnmanage: Twm – отменить управление\n    options_label: Дополнительные параметры\n    title_create: Создание {{name}}\n    title_edit: Редактирование {{name}}\n    title_readonly: Просмотр {{name}}\n    weg_options_label: Параметры панели задач\n    wm_options_label: Параметры управления окном\n    workspace: Рабочая область\n    workspace_placeholder: Нет\n  bundled_msg: >-\n    Эти встроенные конфигурации не подлежат редактированию и созданы для того,\n    чтобы обеспечить вам максимальное удобство без настройки. Они автоматически\n    настраивают для вас наиболее распространенные приложения.\n  bundled_title: Конфигурация приложения в комплекте с Seelen\n  confirm_delete: Удалить эту конфигурацию?\n  confirm_delete_title: Подтверждение удаления\n  delete: Удалить\n  export: Экспорт\n  export_full: Экспорт настроек по приложениям\n  extra_info: >-\n    Пользовательский интерфейс SEELEN Используйте только один идентификатор на\n    приложение (первое совпадение), поэтому важен порядок в том, как\n    специфический, последнее добавление будет приоритет, так как примечание.\n    Таблица сортируется по умолчанию от последних до старых.\n  identifier:\n    add_block: Добавить блок\n    and: И\n    id: Идентификатор\n    kind: Идентифицировать по\n    matching_strategy: Стратегия подбора\n    matching_strategy_option:\n      contains: Содержит\n      ends_with: Заканчивается на\n      equals: Равно\n      regex: Регулярное выражение\n      starts_with: Начинается с\n    negation: Отрицательный подбор\n    or: ИЛИ\n    remove: Удалить блок\n    type:\n      class: Класс окна\n      exe: Exe\n      path: Путь\n      title: Заголовок\n  import: Импорт\n  import_full: Импорт настроек по приложениям\n  new: Новый\n  search: Поиск\n  swap: Сменить\ncancel: Отмена\nclose: Закрыть\ndelete: Удалить\ndevtools:\n  app_folders: Папки приложения\n  custom_config_file: Загрузить пользовательский файл конфигурации\n  data_folder: Папка данных\n  enable: Включить инструменты разработчика\n  install_folder: Папка установки\n  load: Загрузить\n  settings_file: Файл настроек\n  simulate_perm:\n    label: Имитировать запрос разрешения виджета\n    result_allowed: ✓ Разрешение получено\n    result_denied: ✗ Разрешение отклонено\n    trigger: Имитировать\n    widget_id_placeholder: '@автор/имя-виджета'\nextras:\n  clear_icons: Очистить кэш системных значков\n  clear_icons_tooltip: >-\n    Для полного вступления в силу всех виджетов может потребоваться\n    перезагрузка.\n  exit: Выйти\n  links: Официальные ссылки\n  relaunch: Перезапустить\n  version: Версия\n  version_fixed: >-\n    Версии приложения и среды выполнения WebView2 исправлены. Это означает, что\n    приложение не будет получать обновления, а среда выполнения WebView2 не\n    будет автоматически обновляться обновлениями Windows.\ngeneral:\n  accent_color: Акцентный цвет\n  date_format: Формат даты\n  date_format_how_to: Как написать формат даты?\n  hardware_acceleration: Аппаратное ускорение\n  hardware_acceleration_description: >-\n    Отключение аппаратного ускорения уменьшит использование памяти, но может\n    вызвать проблемы с производительностью. Безопасно отключить, если вы не\n    будете использовать живые обои.\n  icon_pack:\n    available: Доступные пакеты значков\n    selected: Активные пакеты значков\n  language: Язык\n  monday: Понедельник\n  performance_mode:\n    on_battery: На батареи\n    on_energy_saver: На энергетическую экономию\n    options:\n      disabled: Неполноценный\n      extreme: Крайний\n      minimal: Минимальный\n    plugged: Подключен или зарядка\n  polling_interval: Интервал опроса системы\n  polling_interval_description: >-\n    Как часто (в секундах) Seelen UI проверяет системные ресурсы, такие как ЦП,\n    ОЗУ, сеть и активность диска. Меньшее число означает более частые\n    обновления, но немного большее использование ресурсов.\n  saturday: Суббота\n  start_of_week: Начало недели\n  startup: Автозапуск при включении системы\n  sunday: Воскресенье\n  theme:\n    available: Доступные темы\n    selected: Активные темы\nheader:\n  labels:\n    config: Конфигурации\n    developer: Для разработчиков\n    extras: Доп. информация\n    general: Общее\n    home: Главная страница\n    icon_pack_editor: Кэшированные иконки\n    iconpack: Пакеты иконок\n    monitors: Мониторы\n    plugin: Плагины\n    resources: Ресурсы\n    shortcuts: Сочетания клавиш\n    soundpack: Звуковые пакеты\n    specific_apps: Настройки по приложениям\n    theme: Темы\n    virtual_desk: Виртуальные рабочие столы\n    wallpaper: Обои\n    widget: Виджеты\nhome:\n  new_resources: Новые ресурсы\ninherit: Наследовать\ninProgress: В процессе...\ninsert: Вставить\nloading: Загрузка...\nmiscellaneous: Разное\nmonitors_configurations:\n  label: Монитор {{index}}\nmore: Подробнее\n'no': Нет\nopen: Открыть\nquit: Закрыть\nremove: Удалить\nreset_all_to_default: Сброс всех значений по умолчанию\nreset_to_default: Сброс на значение по умолчанию\nresources:\n  app_outdated: Для корректной работы этого ресурса требуется более новая версия Seelen UI.\n  corrupted_wallpaper: 'Не удалось извлечь миниатюру: формат видео поврежден или не поддерживается.'\n  delete: Удалить ресурс\n  discover: Откройте для себя больше ресурсов\n  has_update: Доступно обновление\n  high_impact: Высокое влияние на производительность\n  import_wallpapers: Импорт местных обоев\n  open_folder: Откройте папку с ресурсами\n  outdated: >-\n    Этот ресурс был разработан для старой версии Seelen UI и может работать\n    некорректно.\n  see_on_website: Смотрите на сайте\nreview_request:\n  not_now: Не сейчас\n  sure: Конечно!\n  title: Нравится интерфейс Seelen?\nsave: Сохранить\nsave_and_restart: Сохранить и перезапустить\nsearch: Поиск\nsee_more: См. подробнее\nshortcuts:\n  duplicate_error: Этот ярлык дублируется\n  enable: Включить встроенную систему ярлыков\n  enable_tooltip: >-\n    Отключите, если вы будете реализовать свою собственную систему ярлыков,\n    используя клиент UI SEELEN\n  labels:\n    create_new_workspace: Создайте новое рабочее пространство\n    cycle_stack_next: Велосипедный стек следующий\n    cycle_stack_prev: Велосипедный стек предыдущий\n    cycle_wallpaper_next: Переодеться в следующие обои\n    cycle_wallpaper_prev: Переход на предыдущие обои\n    decrease_height: Уменьшить высоту\n    decrease_width: Уменьшить ширину\n    destroy_current_workspace: Уничтожить текущее рабочее пространство\n    focus_bottom: Фокус снизу\n    focus_latest: Сосредоточиться на последнем\n    focus_left: Фокус остался\n    focus_right: Фокус правильно\n    focus_top: Фокус Топ\n    increase_height: Увеличить высоту\n    increase_width: Увеличить ширину\n    misc_force_quit: Сила ухода\n    misc_force_restart: Принудительный перезапуск\n    misc_open_settings: Откройте настройки\n    misc_toggle_lock_tracing: Перевернуть отслеживание блокировки (журналы)\n    misc_toggle_win_event_tracing: Toggle Win Tracing Event (журналы)\n    move_to_workspace: Перейти в рабочую пространство {{0}}\n    move_window_down: Переместить окно внизу\n    move_window_left: Переместить окно налево\n    move_window_right: Переместить окно вправо\n    move_window_up: Переместить окно вверх\n    pause_tiling: Пауза менеджер по плиточному окнам\n    reserve_bottom: Резерв дно\n    reserve_float: Резервировать поплавок\n    reserve_left: Резерв остался\n    reserve_right: Резервировать справа\n    reserve_stack: Резервный стек\n    reserve_top: Резерв вершина\n    restore_sizes: Восстановить размеры\n    send_to_workspace: Отправить в рабочее пространство {{0}}\n    start_weg_app: Focus или Start Application {{0}}\n    switch_to_next_workspace: Переключиться на следующее рабочее пространство\n    switch_to_previous_workspace: Переключиться на предыдущее рабочее пространство\n    switch_workspace: Переключить на рабочее пространство {{0}}\n    toggle_float: Переключать режим плавания окна\n    toggle_monocle: Toggle Workspace Monocle режим\n  readonly_tooltip: Это ярлык только для чтения\n  reset: Сбросить по умолчанию\nsides:\n  bottom: Снизу\n  left: Слева\n  right: Справа\n  top: Сверху\ntoolbar:\n  auto_hide: Автоскрытие\n  delay_to_hide: Задержка, чтобы скрыть\n  delay_to_show: Задержка показа\n  dock_side: Позиция\n  enable: Включить\n  hide_mode:\n    always: Всегда\n    never: Никогда\n    on_overlap: При перекрытии\n  item_size: Размер товара\n  label: Верхняя панель\n  margin: Размер поля\n  padding: Размер заполнения\n  placeholder: {}\nupdate:\n  available: Обновление доступно!\n  channel: Обновить канал\n  downloading: Загрузка ...\nwall:\n  backgrounds: Обои\n  blur: Пятно\n  cancel: Отмена\n  close: Закрывать\n  collection_name: Название коллекции\n  collections: Коллекции\n  contrast: Контраст\n  corrupted_wallpapers_message: 'Поврежденные обои, рассмотрите возможность их удаления:'\n  create: Создавать\n  create_collection: Создать коллекцию\n  default_collection: Коллекция по умолчанию\n  delete_collection: Удалить коллекцию\n  edit_collection: Редактировать коллекцию\n  enable: Включить менеджер обоев\n  extend: Продлить основной\n  fit:\n    contain: Содержите\n    cover: Обложка\n    fill: Наполнение\n  flipHorizontal: Переворот по горизонтали\n  flipVertical: Флип вертикальный\n  generating_thumbnails: Создание миниатюр обоев\n  hours: часы\n  interval: Менять обои через\n  minutes: минуты\n  monitor_collection: Коллекция мониторов\n  multimonitor_behaviour: Поведение нескольких мониторов\n  muted: Отключить звук видео\n  no_background: Пустое слайд-шоу (используется фон темы)\n  no_collections: Коллекции пока не созданы. Нажмите кнопку +, чтобы создать его.\n  objectFit: Фоновая подгонка\n  objectPosition: Фоновая позиция\n  overlayColor: Цвет наложения\n  overlayMixBlendMode: Режим наложения \"Перекрытие\n  per_monitor: На монитор\n  playback: Скорость воспроизведения\n  position:\n    bottom: Дно\n    center: Центр\n    left: Слева\n    right: Справа\n    top: Топ\n  processing_video: Обработка видео\n  random: Случайный порядок\n  saturation: Насыщенность\n  seconds: секунды\n  select_collection: Выбрать коллекцию\n  thumbnail_generation_complete: Создание миниатюр завершено\n  thumbnail_generation_finished: Создание миниатюр успешно завершено\n  wallpaper_collection: Коллекция обоев\n  wallpaper_settings: Настройки обоев\n  withOverlay: С накладкой\n  workspace_collections: Коллекции рабочих областей\nweg:\n  auto_hide: Автоскрытие\n  delay_to_hide: Задержка, чтобы скрыть\n  delay_to_show: Задержка показа\n  dock_side: Расположение\n  enable: Включить\n  filtering: Фильтрация элементов\n  gap: Расстояние\n  hide_mode:\n    always: Всегда\n    never: Никогда\n    on_overlap: При перекрытии\n  items:\n    gap: Расстояние между иконок\n    label: Иконки\n    pinned_visibility:\n      always: Всегда\n      label: Видимость закрепленных элементов\n      when_primary: Если монитор основной\n    show_instance_counter: Показать счетчик открытых окон\n    show_window_title: Показать заголовок открытого окна (только горизонтально)\n    size: Размер иконок\n    split_windows: Разделение окон (по одному элементу в окне)\n    temporal_visibility:\n      all: Все\n      label: Видимость незакрепленных элементов\n      on_monitor: На мониторе\n    visible_separators: Разделители\n  label: Панель задач\n  margin: Внешний отступ\n  mode:\n    full_width: По ширине экрана\n    min_content: Минимальная\n  padding: Внутренний отступ\n  show_end_task: Показать завершение задачи на панели задач\n  width: Ширина\nwelcome:\n  give_a_review: Оставить отзыв\n  message: >-\n    Seelen UI — это бесплатная среда рабочего стола с открытым исходным кодом\n    для Windows. Здесь у вас есть полный контроль над тем, как выглядит и ведет\n    себя ваш рабочий стол, что позволяет вам настроить каждую деталь в\n    соответствии с вашим рабочим процессом и стилем.\n  ok: Начнем!\n  review: >-\n    Если вам нравится использовать Seelen UI, оставьте отзыв в магазине — ваш\n    отзыв поможет проекту расти и совершенствоваться.\n  title: Добро пожаловать в интерфейс Seelen!\nwidget:\n  enable: Включите этот виджет\n  enable_for_monitor: Включить на этом мониторе\n  instances: Экземпляры\nwm:\n  animations:\n    duration: Продолжительность анимации (MS)\n    ease_function: Функция облегчения анимации\n    enable: Включить анимацию Window\n  author: Автор\n  border:\n    enable: Включить границу окон\n    offset: Смещение границы\n    width: Ширина границы\n  description: Описание\n  drag_behavior: Поведение перетаскивания\n  drag_behavior_options:\n    sort: Сортировка (изменение порядка окон при перетаскивании)\n    swap: Swap (поменять окна на конце перетаскивания)\n  enable: Включить менеджер по плитке окна\n  layout: Макет\n  resize_delta: Шаг изменения размера (%)\n  space_between_containers: Пространство между контейнерами\n  workspace_offset: Внешний отступ рабочего пространства\n  workspace_padding: Внутренний отступ рабочего пространства\n'yes': Да\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/si.yml",
    "content": "action:\n  confirm: ඔයාට විශ්වාස ද?\n  confirm_body: මෙම ක්රියාව අහෝසි කළ නොහැක.\napps_configurations:\n  app:\n    bindings: බැඳීම (විකල්ප දෙකම අවශ්‍ය බව සලකන්න)\n    category: වර්ගය\n    category_placeholder: කිසිවක් නැත\n    monitor: නිරීක්ෂණය කරන්න\n    monitor_placeholder: කිසිවක් නැත\n    name: නම\n    ok_create: නිර්මාණය කරන්න\n    ok_edit: යාවත්කාලීන කරන්න\n    ok_readonly: නව ලෙස සංස්කරණය කරන්න\n    options:\n      NoInteractive: අන්තර්ක්‍රියාකාරී නැත\n      VdPinned: සියලුම වැඩබිම්වල පෙන්වන්න\n      WmFloat: Twm - පාවෙන ආරම්භ කරන්න\n      WmForce: Twm - බල කළමනාකරණය\n      WmUnmanage: Twm - කළමනාකරණය කරන්න\n    options_label: අමතර විකල්ප\n    title_create: '{{name}} නිර්මාණය කරමින්'\n    title_edit: සංස්කරණය {{name}}\n    title_readonly: '{{name}} බලමින්'\n    weg_options_label: තටාකය / කාර්ය තීරුව විකල්ප\n    wm_options_label: කවුළු කළමනාකරුගේ විකල්ප\n    workspace: වැඩබිම\n    workspace_placeholder: කිසිවක් නැත\n  bundled_msg: >-\n    මෙම මිටි වින්‍යාසයන් සංස්කරණය කළ නොහැකි අතර අභිරුචිකරණයකින් තොරව ඔබට හොඳම\n    අත්දැකීම ලබා දීමට සැලසුම් කර ඇත. ඔවුන් ඔබ සඳහා වඩාත් පොදු යෙදුම්\n    ස්වයංක්‍රීයව වින්‍යාස කරයි.\n  bundled_title: යෙදුම් වින්‍යාසය සීලන් සමඟ බණ්ඩල් කර ඇත\n  confirm_delete: ඔබට මෙම වින්‍යාසය/s මැකීමට අවශ්‍ය බව විශ්වාසද?\n  confirm_delete_title: මකා දැමීම තහවුරු කරන්න\n  delete: මකන්න\n  export: අපනයන\n  export_full: අයදුම්පත මගින් සැකසුම් අපනයනය කරන්න\n  extra_info: >-\n    Applow Applay Apan Applen UI යෙදුමට එක් අනන්යතාවයක් භාවිතා කරන්න (පළමු තරගය\n    හමු විය) එබැවින් නිශ්චිතව දක්වන අනුපිළිවෙල වැදගත් ය, නවතම එකතු කරන ලද නවතම\n    එකතු කරනු ලැබේ, නවතම නම නවතම නවතම දක්වා පෙරනිමියෙන්.\n  identifier:\n    add_block: බ්ලොක් එක් කරන්න\n    and: සහ\n    id: හඳුනාගැනීම\n    kind: විසින් හඳුනා ගන්න\n    matching_strategy: ගැලපුම් උපාය මාර්ගය\n    matching_strategy_option:\n      contains: අඩංගු වේ\n      ends_with: සමඟ අවසන් වේ\n      equals: සමාන වේ\n      regex: නිතිපතා ප්රකාශනය\n      starts_with: සමඟ ආරම්භ වේ\n    negation: ගැලපීම නිෂේධනය කරන්න\n    or: හෝ\n    remove: අවහිර කිරීම මකන්න\n    type:\n      class: පන්තිය\n      exe: Exe\n      path: මාර්ගය\n      title: මාතෘකාව\n  import: ආනයන\n  import_full: අයදුම්පත අනුව සැකසුම් ආයාත කරන්න\n  new: අලුත්\n  search: සෙවීම\n  swap: හුවමාරු කරන්න\ncancel: අවලංගු කරන්න\nclose: වසන්න\ndelete: මකන්න\ndevtools:\n  app_folders: යෙදුම් ෆෝල්ඩර\n  custom_config_file: අභිරුචි වින්‍යාස ගොනුව පූරණය කරන්න\n  data_folder: දත්ත ෆෝල්ඩරය\n  enable: සංවර්ධක මෙවලම් සබල කරන්න\n  install_folder: ස්ථාපන ෆෝල්ඩරය\n  load: පැටවීම\n  settings_file: සැකසුම් ගොනුව\n  simulate_perm:\n    label: විජට් අවසර ඉල්ලීම අනුකරණය කරන්න\n    result_allowed: ✓ අවසරය ලබා දී ඇත\n    result_denied: ✗ අවසරය ප්‍රතික්ෂේප විය\n    trigger: අනුකරණය කරන්න\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: පැහැදිලි පද්ධති අයිකන හැඹිලිය\n  clear_icons_tooltip: >-\n    සියලුම විජට් සඳහා සම්පූර්ණයෙන්ම බලාත්මක වීමට නැවත ආරම්භ කිරීම අවශ්ය විය\n    හැකිය\n  exit: පිටවීම/පිටවීම\n  links: නිල සබැඳි\n  relaunch: නැවත දියත් කරන්න\n  version: පිටපත\n  version_fixed: >-\n    යෙදුම සහ WebView2 ධාවන කාල අනුවාද සවි කර ඇත. මෙයින් අදහස් වන්නේ යෙදුමට\n    යාවත්කාලීන නොලැබෙන අතර WebView2 ධාවන කාලය වින්ඩෝස් යාවත්කාලීන සමඟ\n    ස්වයංක්‍රීයව යාවත්කාලීන නොවන බවයි.\ngeneral:\n  accent_color: උච්චාරණ වර්ණය\n  date_format: දිනය ආකෘතිය\n  date_format_how_to: දින ආකෘතියක් ලියන්නේ කෙසේද?\n  hardware_acceleration: දෘඪාංග ත්වරණය\n  hardware_acceleration_description: >-\n    දෘඪාංග ත්වරණය අක්‍රිය කිරීම මතක භාවිතය අඩු කරයි, නමුත් කාර්ය සාධන ගැටළු ඇති\n    කළ හැක. ඔබ සජීවී බිතුපත් භාවිතා නොකරන්නේ නම් අබල කිරීම ආරක්ෂිත වේ.\n  icon_pack:\n    available: පවතින අයිකන ඇසුරුම්\n    selected: ක්‍රියාකාරී අයිකන ඇසුරුම්\n  language: භාෂාව\n  monday: සඳුදා\n  performance_mode:\n    on_battery: බැටරි මත\n    on_energy_saver: බලශක්ති ඉතිරිය\n    options:\n      disabled: ආබාධිත\n      extreme: අන්ත\n      minimal: අවම\n    plugged: ප්ලග් හෝ ආරෝපණය\n  polling_interval: පද්ධති ඡන්ද පරතරය\n  polling_interval_description: >-\n    සීලෙන් යූඅයි කොපමණ වාරයක් (තත්පර වලින්) CPU, RAM, ජාල, සහ තැටි ක්‍රියාකාරකම්\n    වැනි පද්ධති සම්පත් පරීක්ෂා කරයි. කුඩා සංඛ්‍යාවක් යන්නෙන් අදහස් වන්නේ නිතර\n    යාවත්කාලීන කිරීම්, නමුත් සම්පත් භාවිතය තරමක් ඉහළ ය.\n  saturday: සෙනසුරාදා\n  start_of_week: සතියේ ආරම්භය\n  startup: ආරම්භයේදී ධාවනය කරන්නද?\n  sunday: ඉරිදා\n  theme:\n    available: පවතින තේමා\n    selected: ක්රියාකාරී තේමාවන්\nheader:\n  labels:\n    config: වින්යාසයන්\n    developer: සංවර්ධකයින් සඳහා\n    extras: අමතර\n    general: ජනරාල්\n    home: මුල් පිටුව\n    icon_pack_editor: හැඹිලි අයිකන\n    iconpack: අයිකන ඇසුරුම්\n    monitors: නිරීක්ෂකයින්\n    plugin: ප්ලගීන\n    resources: සම්පත්\n    shortcuts: කෙටිමං\n    soundpack: ශබ්ද ඇසුරුම්\n    specific_apps: අයදුම්පත අනුව සැකසුම්\n    theme: තේමාවන්\n    virtual_desk: අතථ්ය ඩෙස්ක්ටොප්\n    wallpaper: බිතුපත්\n    widget: විජට්\nhome:\n  new_resources: නව සම්පත්\ninherit: උරුම වේ\ninProgress: ප්රගතියේ...\ninsert: ඇතුළු කරන්න\nloading: පූරණය වෙමින්...\nmiscellaneous: විවිධ\nmonitors_configurations:\n  label: '{{දර්ශකය} u අධීක්ෂණය කරන්න'\nmore: තව\n'no': නැත\nopen: විවෘත\nquit: ඉවත්\nremove: ඉවත් කරන්න\nreset_all_to_default: සියල්ල පෙරනිමි අගයන් වෙත නැවත සකසන්න\nreset_to_default: පෙරනිමි වටිනාකමට නැවත සකසන්න\nresources:\n  app_outdated: මෙම සම්පතට සින්ලන් යූඅයි හි නවතම අනුවාදයක් නිසියාකාරව වැඩ කිරීමට අවශ්ය වේ.\n  corrupted_wallpaper: සිඟිති රුව උපුටා ගැනීමට අසමත් විය - දූෂිත හෝ සහාය නොදක්වන වීඩියෝ ආකෘතිය\n  delete: සම්පත මකන්න\n  discover: තවත් සම්පත් සොයා ගන්න\n  has_update: යාවත්කාලීන කිරීමක් තිබේ\n  high_impact: කාර්ය සාධනය කෙරෙහි ඉහළ බලපෑමක්\n  import_wallpapers: දේශීය බිතුපත් ආනයනය කරන්න\n  open_folder: සම්පත් ෆෝල්ඩරය විවෘත කරන්න\n  outdated: >-\n    මෙම සම්පත නිර්මාණය කර ඇත්තේ සීලන් UI හි පැරණි අනුවාදයක් සඳහා වන අතර නිසි ලෙස\n    ක්රියා නොකරනු ඇත.\n  see_on_website: වෙබ් අඩවියේ බලන්න\nreview_request:\n  not_now: දැන් නොවේ\n  sure: සහතිකයි!\n  title: Seelen UI භුක්ති විඳිනවාද?\nsave: සුරකින්න\nsave_and_restart: සුරකින්න සහ නැවත ආරම්භ කරන්න\nsearch: සොයන්න\nsee_more: තවත් බලන්න\nshortcuts:\n  duplicate_error: මෙම කෙටි මග අනුපිටපත් කර ඇත\n  enable: සාදන ලද කෙටිමං පද්ධතියක් සක්රීය කරන්න\n  enable_tooltip: >-\n    ඔබ සීලංගා යූ සේවාදායකයා භාවිතා කරමින් ඔබේම කෙටිමං පද්ධතියක් ක්රියාත්මක\n    කරන්නේ නම් අක්රීය කරන්න\n  labels:\n    create_new_workspace: නව වැඩබිම සාදන්න\n    cycle_stack_next: ඊළඟට චක්රීය තොගය\n    cycle_stack_prev: පෙර චක්රීය තොගය\n    cycle_wallpaper_next: ඊළඟ බිතුපතෙහි වෙනස් කිරීම\n    cycle_wallpaper_prev: පෙර බිතුපතෙහි වෙනස් කිරීම\n    decrease_height: උස අඩු කිරීම\n    decrease_width: පළල අඩු කරන්න\n    destroy_current_workspace: වත්මන් වැඩබිම විනාශ කරන්න\n    focus_bottom: පහළට අවධානය යොමු කරන්න\n    focus_latest: නවතම අවධානයට යොමු කරන්න\n    focus_left: වමට අවධානය යොමු කරන්න\n    focus_right: දකුණට හරි\n    focus_top: ඉහළට අවධානය යොමු කරන්න\n    increase_height: උස වැඩි කරන්න\n    increase_width: පළල වැඩි කිරීම\n    misc_force_quit: බලය ඉවත්වීම\n    misc_force_restart: බලය නැවත ආරම්භ කරන්න\n    misc_open_settings: විවෘත සැකසුම්\n    misc_toggle_lock_tracing: ටොගල් අගුල ලුහුබැඳීම (ල logs ු-සටහන්)\n    misc_toggle_win_event_tracing: ටොගල් ජයග්රාහී සිදුවීම් ලුහුබැඳීම (ල logs ු-සටහන්)\n    move_to_workspace: වැඩපොළ වෙත යන්න {{0}}\n    move_window_down: කවුළුව පහළට ගෙනයන්න\n    move_window_left: කවුළුව වමට ගෙනයන්න\n    move_window_right: කවුළුව දකුණට ගෙනයන්න\n    move_window_up: කවුළුව ඉහළට ගෙන යන්න\n    pause_tiling: ටයිල් කිරීමේ කවුළු කළමනාකරු විරාමයක් තබන්න\n    reserve_bottom: සංචිත පතුල\n    reserve_float: සංචිත පාවීම\n    reserve_left: සංචිත වමට\n    reserve_right: සංචිත අයිතිය\n    reserve_stack: සංචිත තොගය\n    reserve_top: සංවචනය\n    restore_sizes: ප්රමාණ ප්රතිස්ථාපනය කරන්න\n    send_to_workspace: වැඩපොළ වෙත යවන්න {{0}}\n    start_weg_app: අවධානය යොමු කිරීම හෝ ආරම්භ කිරීම {{0}}\n    switch_to_next_workspace: ඊළඟ වැඩබිම වෙත මාරු වන්න\n    switch_to_previous_workspace: පෙර වැඩබිම වෙත මාරු වන්න\n    switch_workspace: වැඩබිම් වෙත මාරු වන්න {{0}}\n    toggle_float: කවුළුව පාවෙන ප්රකාරය ටොගල කරන්න\n    toggle_monocle: වැඩබිම මෝනොකැක් මාදිලිය ටොගල කරන්න\n  readonly_tooltip: මෙය කියවීමට පමණක් කෙටිමඟකි\n  reset: පෙරනිමි වෙත නැවත සකසන්න\nsides:\n  bottom: පහළ\n  left: වම\n  right: හරි\n  top: ඉහල\ntoolbar:\n  auto_hide: ස්වයංක්රීය සැඟවීම\n  delay_to_hide: සැඟවීමට ප්රමාද\n  delay_to_show: පෙන්වීමට ප්‍රමාදය\n  dock_side: තනතුර\n  enable: විසිතුරු මෙවලම් තීරුව සබල කරන්න\n  hide_mode:\n    always: සෑම විටම\n    never: කවදාවත් නැහැ\n    on_overlap: අතිච්ඡාදනය මත\n  item_size: අයිතමයේ ප්රමාණය\n  label: මෙවලම් තීරුව\n  margin: ආන්තික ප්රමාණය\n  padding: පිරවුම් ප්රමාණය\n  placeholder: {}\nupdate:\n  available: යාවත්කාලීන කිරීම!\n  channel: නාලිකාව යාවත්කාලීන කරන්න\n  downloading: බාගත කිරීම ...\nwall:\n  backgrounds: බිතුපත්\n  blur: බ්ලර්\n  cancel: අවලංගු කරන්න\n  close: වසන්න\n  collection_name: එකතුවේ නම\n  collections: එකතු කිරීම්\n  contrast: වෙනස\n  corrupted_wallpapers_message: 'දූෂිත බිතුපත්, මේවා මකා දැමීමට සලකා බලන්න:'\n  create: නිර්මාණය කරන්න\n  create_collection: එකතුවක් සාදන්න\n  default_collection: පෙරනිමි එකතුව\n  delete_collection: එකතුව මකන්න\n  edit_collection: එකතුව සංස්කරණය කරන්න\n  enable: බිතුපත් කළමනාකරු සක්රීය කරන්න\n  extend: මූලික දිගු කරන්න\n  fit:\n    contain: අඩංගු\n    cover: ආවරණය\n    fill: පුරවන්න\n  flipHorizontal: ෆ්ලිප් හොරයිසොන්ටමල්\n  flipVertical: සිරස් රෝග\n  generating_thumbnails: Wallpaper Thumbnails උත්පාදනය කිරීම\n  hours: පැය\n  interval: සෑම කෙනෙකුම බිතුපත වෙනස් කරන්න\n  minutes: මිනිත්තු\n  monitor_collection: නිරීක්ෂණ එකතුව\n  multimonitor_behaviour: බහු නිරීක්ෂක හැසිරීම\n  muted: වීඩියෝ ශ්‍රව්‍ය නිශ්ශබ්ද කරන්න\n  no_background: හිස් විනිවිදක දර්ශනය, ඒ වෙනුවට තේමාවේ පසුබිම භාවිතා කිරීම.\n  no_collections: තවම එකතුවක් සාදා නැත. එකක් සෑදීමට + බොත්තම ක්ලික් කරන්න.\n  objectFit: පසුබිම් යෝග්යතාවය\n  objectPosition: පසුබිම් පිහිටීම\n  overlayColor: ආවරණයේ වර්ණය\n  overlayMixBlendMode: උඩිස් මිශ්ර මිශ්ර කිරීමේ මාදිලිය\n  per_monitor: මොනිටරයකට\n  playback: ප්ලේබැක් වේගය\n  position:\n    bottom: පහළ\n    center: මධ්යස්ථානය\n    left: වමට\n    right: අයිතිය\n    top: ඉහළට\n  processing_video: වීඩියෝ සැකසීම\n  random: සසම්භාවී විනිවිදක දර්ශනය\n  saturation: සන්තෘප්තිය\n  seconds: තත්පර\n  select_collection: එකතුව තෝරන්න\n  thumbnail_generation_complete: සිඟිති රූ උත්පාදනය සම්පූර්ණයි\n  thumbnail_generation_finished: සිඟිති රූ උත්පාදනය සාර්ථකව නිම කර ඇත\n  wallpaper_collection: බිතුපත් එකතුව\n  wallpaper_settings: බිතුපත් සැකසීම්\n  withOverlay: ආවරණය සමඟ\n  workspace_collections: වැඩ ඉඩ එකතු කිරීම්\nweg:\n  auto_hide: ස්වයංක්‍රීය සඟවන්න\n  delay_to_hide: සැඟවීමට ප්රමාද\n  delay_to_show: පෙන්වීමට ප්‍රමාදය\n  dock_side: තනතුර\n  enable: Dock/Taskbar සබල කරන්න\n  filtering: අයිතම පෙරීම\n  gap: පරතරය\n  hide_mode:\n    always: සෑම විටම\n    never: කවදාවත් නැහැ\n    on_overlap: අතිච්ඡාදනය මත\n  items:\n    gap: අයිතම අතර අවකාශය\n    label: අයිතම\n    pinned_visibility:\n      always: සෑම විටම\n      label: අමුණන ලද අයිතම දෘශ්‍යතාව\n      when_primary: මොනිටරය ප්රාථමික වන විට\n    show_instance_counter: විවෘත වින්ඩෝස් කවුන්ටරය පෙන්වන්න\n    show_window_title: විවෘත කවුළු මාතෘකාව පෙන්වන්න (තිරස් පමණක්)\n    size: අයිතමයේ ප්රමාණය\n    split_windows: වින්ඩෝස් බෙදන්න (කවුළුවකට එක් අයිතමයක්)\n    temporal_visibility:\n      all: සියල්ල\n      label: ඇමුණුම් නොකළ අයිතම දෘශ්‍යතාව\n      on_monitor: මොනිටරය මත\n    visible_separators: දෘශ්‍ය බෙදුම්කරුවන්\n  label: ඩොක්/කාර්ය තීරුව\n  margin: ආන්තිකය\n  mode:\n    full_width: සම්පූර්ණ තිර පළල\n    min_content: හැකි තරම් කුඩා\n  padding: පෑඩිං\n  show_end_task: කාර්ය තීරුවේ අවසන් කාර්යය පෙන්වන්න\n  width: පළල\nwelcome:\n  give_a_review: Review එකක් දෙන්න\n  message: >-\n    Seelen UI යනු Windows සඳහා නිදහස් සහ විවෘත මූලාශ්‍ර ඩෙස්ක්ටොප් පරිසරයකි.\n    මෙහිදී ඔබට ඔබේ වැඩ ප්‍රවාහයට සහ විලාසයට ගැලපෙන පරිදි සෑම විස්තරයක්ම\n    අභිරුචිකරණය කිරීමට ඉඩ සලසමින් ඔබේ ඩෙස්ක්ටොප් පෙනුම සහ හැසිරෙන ආකාරය පිළිබඳ\n    පූර්ණ පාලනය ඔබට ඇත.\n  ok: අපි පටන් ගනිමු!\n  review: >-\n    ඔබ Seelen UI භාවිතා කිරීමට කැමති නම්, ගබඩාවේ සමාලෝචනයක් තැබීමට සලකා බලන්න -\n    ඔබේ ප්‍රතිපෝෂණය ව්‍යාපෘතිය වර්ධනය වීමට සහ වැඩිදියුණු කිරීමට උපකාරී වේ.\n  title: Seelen UI වෙත සාදරයෙන් පිළිගනිමු!\nwidget:\n  enable: මෙම විජට් සක්රීය කරන්න\n  enable_for_monitor: මෙම මොනිටරය මත සක්රීය කරන්න\n  instances: අවස්ථා\nwm:\n  animations:\n    duration: සජීවිකරණ කාලය (MS)\n    ease_function: සජීවිකරණ ලිහිල් කිරීමේ ක්රියාකාරිත්වය\n    enable: කවුළුවේ සජීවිකරණ සක්රීය කරන්න\n  author: කර්තෘ\n  border:\n    enable: කවුළුවේ මායිම සක්රිය කරන්න\n    offset: මායිම් ඕෆ්සෙට්\n    width: මායිම් පළල\n  description: විස්තර\n  drag_behavior: ඇදගෙන යාමේ හැසිරීම\n  drag_behavior_options:\n    sort: වර්ග කරන්න (ඇදගෙන යන අතරතුර කවුළු නැවත සකසන්න)\n    swap: Swap (drag end මත windows swap)\n  enable: ටයිල්ලිං කවුළු කළමනාකරු සක්රීය කරන්න\n  layout: පිරිසැලසුම\n  resize_delta: ඩෙල්ටා ප්‍රමාණය වෙනස් කරන්න (%)\n  space_between_containers: බහාලුම් අතර අවකාශය\n  workspace_offset: වැඩබිම් ඕෆ්සෙට් (ආන්තික)\n  workspace_padding: වැඩබිම් පිරවුම\n'yes': ඔව්\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/sk.yml",
    "content": "action:\n  confirm: Si si istý?\n  confirm_body: Táto akcia sa nedá vrátiť späť.\napps_configurations:\n  app:\n    bindings: Viazanie (POZNÁMKA sú potrebné obe možnosti)\n    category: Kategória\n    category_placeholder: Žiadny\n    monitor: Monitor\n    monitor_placeholder: Žiadny\n    name: názov\n    ok_create: Vytvárať\n    ok_edit: Aktualizácia\n    ok_readonly: Upravovať ako nové\n    options:\n      NoInteractive: Žiadna interaktívna\n      VdPinned: Zobraziť vo všetkých pracovných priestoroch\n      WmFloat: Twm - Začať plávať\n      WmForce: Twm - Force Manage\n      WmUnmanage: Twm - Unmanage\n    options_label: Ďalšie možnosti\n    title_create: Vytváranie {{name}}\n    title_edit: Úpravy {{name}}\n    title_readonly: Prezeranie {{name}}\n    weg_options_label: Možnosti doku/panela úloh\n    wm_options_label: Možnosti správcu okien\n    workspace: Pracovný priestor\n    workspace_placeholder: Žiadny\n  bundled_msg: >-\n    Tieto zviazané konfigurácie nie sú upraviteľné a sú navrhnuté tak, aby vám\n    poskytli najlepší zážitok bez prispôsobenia. Automaticky nakonfigurujú pre\n    vás najbežnejšie aplikácie.\n  bundled_title: Aplikácia konfigurácia zviazaná s Seelen\n  confirm_delete: Ste si istí, že chcete túto konfiguráciu vymazať?\n  confirm_delete_title: Potvrďte odstránenie\n  delete: Vymazať\n  export: Export\n  export_full: Export nastavení podľa aplikácie\n  extra_info: >-\n    Používateľské rozhranie Seelen používa iba jeden identifikátor na aplikáciu\n    (prvá nájdená zhoda), takže poradie, v akom sú špecifikované, je dôležité.\n    Najnovšie pridané budú mať prioritu, pretože tabuľka je predvolene zoradená\n    od najnovšieho po staré.\n  identifier:\n    add_block: Pridať blok\n    and: A\n    id: Identifikátor\n    kind: Určiť\n    matching_strategy: Stratégia\n    matching_strategy_option:\n      contains: Obsahuje\n      ends_with: Končí s\n      equals: Rovná sa\n      regex: Regulárny výraz\n      starts_with: Začína s\n    negation: Negovať zladenie\n    or: Alebo\n    remove: Vymazať blok\n    type:\n      class: triedy\n      exe: Exe\n      path: Cesta\n      title: Názov\n  import: Dovoz\n  import_full: Import nastavení podľa aplikácie\n  new: Nový\n  search: Vyhľadávanie\n  swap: Vymeniť\ncancel: Zrušiť\nclose: Zatvoriť\ndelete: Vymazať\ndevtools:\n  app_folders: Priečinky\n  custom_config_file: Načítať vlastný konfiguračný súbor\n  data_folder: Priečinok\n  enable: Povoľte vývojárskym nástrojom\n  install_folder: Inštalačný priečinok\n  load: Naložiť\n  settings_file: Nastavovací súbor\n  simulate_perm:\n    label: Simulovať žiadosť o povolenie miniaplikácie\n    result_allowed: ✓ Povolenie udelené\n    result_denied: ✗ Povolenie zamietnuté\n    trigger: Simulovať\n    widget_id_placeholder: '@autor/názov-widgetu'\nextras:\n  clear_icons: Vymazanie vyrovnávacej pamäte systémových ikon\n  clear_icons_tooltip: Aby sa všetky widgety plne prejavili, môže byť potrebný reštart.\n  exit: Prestať\n  links: Oficiálne odkazy\n  relaunch: Reštart\n  version: Verzia\n  version_fixed: >-\n    Verzie aplikácie a WebView2 Runtime sú opravené. To znamená, že aplikácia\n    nebude dostávať aktualizácie a WebView2 Runtime sa nebude automaticky\n    aktualizovať aktualizáciami systému Windows.\ngeneral:\n  accent_color: Prízvuk\n  date_format: Formát\n  date_format_how_to: Ako napísať formát dátumu?\n  hardware_acceleration: Hardvérová akcelerácia\n  hardware_acceleration_description: >-\n    Vypnutie hardvérovej akcelerácie zníži využitie pamäte, ale môže spôsobiť\n    problémy s výkonom. Je bezpečné ho zakázať, ak nebudete používať živé\n    tapety.\n  icon_pack:\n    available: Dostupné balíčky ikon\n    selected: Balíky aktívnych ikon\n  language: Jazyk\n  monday: pondelok\n  performance_mode:\n    on_battery: Batéria\n    on_energy_saver: Na šetrič energie\n    options:\n      disabled: Postihnutý\n      extreme: Extrémny\n      minimal: Minimálny\n    plugged: Zapojené alebo nabíjanie\n  polling_interval: Interval dotazovania systému\n  polling_interval_description: >-\n    Ako často (v sekundách) používateľské rozhranie Seelen kontroluje systémové\n    zdroje, ako je CPU, RAM, sieť a aktivita disku. Menší počet znamená\n    častejšie aktualizácie, ale o niečo vyššie využitie zdrojov.\n  saturday: sobota\n  start_of_week: Začiatok týždňa\n  startup: Spustiť pri štarte?\n  sunday: nedeľa\n  theme:\n    available: Dostupné témy\n    selected: Aktívne témy\nheader:\n  labels:\n    config: Konfigurácie\n    developer: Pre vývojárov\n    extras: Dodatky\n    general: Všeobecný\n    home: Domov\n    icon_pack_editor: Ikony v medzipamäti\n    iconpack: Balíky ikon\n    monitors: Monitorovať\n    plugin: Zásuvné moduly\n    resources: Zdroje\n    shortcuts: Skratky\n    soundpack: Zvukové balíky\n    specific_apps: Nastavenia podľa aplikácie\n    theme: Témy\n    virtual_desk: Virtuálne pracovné plochy\n    wallpaper: Tapety na plochu\n    widget: Widgety\nhome:\n  new_resources: Nové zdroje\ninherit: Zdediť\ninProgress: Prebieha ...\ninsert: Vložiť\nloading: Načítava...\nmiscellaneous: Rôznorodý\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Viac\n'no': Nie\nopen: OTVORENÉ\nquit: Prestať\nremove: Odložiť\nreset_all_to_default: Obnovenie všetkých predvolených hodnôt\nreset_to_default: Obnovenie predvolenej hodnoty\nresources:\n  app_outdated: >-\n    Tento zdroj si na správne fungovanie vyžaduje novšiu verziu používateľského\n    rozhrania Seelen.\n  corrupted_wallpaper: >-\n    Nepodarilo sa extrahovať miniatúru – poškodený alebo nepodporovaný formát\n    videa\n  delete: Vymazať zdroj\n  discover: Objavte ďalšie zdroje\n  has_update: K dispozícii je aktualizácia\n  high_impact: Vysoký vplyv na výkon\n  import_wallpapers: Import miestnych tapiet\n  open_folder: Otvorenie priečinka so zdrojmi\n  outdated: >-\n    Tento zdroj bol navrhnutý pre staršiu verziu používateľského rozhrania\n    Seelen a nemusí fungovať správne.\n  see_on_website: Pozri na webovej stránke\nreview_request:\n  not_now: Teraz nie\n  sure: Jasné!\n  title: Páči sa vám používateľské rozhranie Seelen?\nsave: Uložiť\nsave_and_restart: Uložiť a reštartovať\nsearch: Hľadať\nsee_more: Pozri viac\nshortcuts:\n  duplicate_error: Táto skratka je duplikovaná\n  enable: Povoliť vstavaný systém skratiek\n  enable_tooltip: >-\n    Zakážte, ak budete implementovať svoj vlastný systém skratiek pomocou\n    klienta Seelen UI klienta\n  labels:\n    create_new_workspace: Vytvorte nový pracovný priestor\n    cycle_stack_next: Stack Cycle Ďalej\n    cycle_stack_prev: Cyklus predchádzajúci\n    cycle_wallpaper_next: Zmena na ďalšiu tapetu\n    cycle_wallpaper_prev: Zmena na predchádzajúcu tapetu\n    decrease_height: Zníženie výšky\n    decrease_width: Znížiť šírku\n    destroy_current_workspace: Zničiť súčasný pracovný priestor\n    focus_bottom: Zaostrenie\n    focus_latest: Najnovší\n    focus_left: Vľavo\n    focus_right: Zamerať sa\n    focus_top: Zaostrenie\n    increase_height: Výška\n    increase_width: Zväčšenie\n    misc_force_quit: Vynútiť prestať\n    misc_force_restart: Vynútiť reštart\n    misc_open_settings: Otvorené nastavenia\n    misc_toggle_lock_tracing: Prepnite sledovanie zámku (protokoly)\n    misc_toggle_win_event_tracing: Prepnúť sledovanie udalostí Win (protokoly)\n    move_to_workspace: Presunúť do pracovného priestoru {{0}}\n    move_window_down: Presuňte okno dole\n    move_window_left: Presuňte okno doľava\n    move_window_right: Presuňte okno doprava\n    move_window_up: Presuňte okno na vrchol\n    pause_tiling: Pauza obkladací správca okien\n    reserve_bottom: Dno\n    reserve_float: Plavák\n    reserve_left: Vľavo\n    reserve_right: Oprávnenie\n    reserve_stack: Zásobník\n    reserve_top: Horná časť\n    restore_sizes: Obnoviť veľkosti\n    send_to_workspace: Odoslať do Workspace {{0}}\n    start_weg_app: Zamerajte alebo spustiť aplikáciu {{0}}\n    switch_to_next_workspace: Prepnite na ďalší pracovný priestor\n    switch_to_previous_workspace: Prepnite na predchádzajúci pracovný priestor\n    switch_workspace: Prepnite na pracovný priestor {{0}}\n    toggle_float: Prepínanie plavákového režimu okna\n    toggle_monocle: Prepnite režim Monocle Workspace\n  readonly_tooltip: Toto je skratka iba na čítanie\n  reset: Reset na predvolené hodnoty\nsides:\n  bottom: Spodná časť\n  left: Vľavo\n  right: Správny\n  top: Vrchol\ntoolbar:\n  auto_hide: Automaticky skryť\n  delay_to_hide: Odložiť skryť\n  delay_to_show: Oneskorenie zobrazenia\n  dock_side: Pozícia\n  enable: Povoliť ozdobný panel s nástrojmi\n  hide_mode:\n    always: Vždy\n    never: Nikdy\n    on_overlap: Na prekrytí\n  item_size: Veľkosť položky\n  label: Panel s nástrojmi\n  margin: Veľkosť okraja\n  padding: Veľkosť výplne\n  placeholder: {}\nupdate:\n  available: Aktualizácia k dispozícii!\n  channel: Kanál\n  downloading: Sťahovanie ...\nwall:\n  backgrounds: Tapety\n  blur: Rozmazanie\n  cancel: Zrušiť\n  close: Zavrieť\n  collection_name: Názov zbierky\n  collections: zbierky\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Poškodené tapety, zvážte ich odstránenie:'\n  create: Vytvorte\n  create_collection: Vytvoriť zbierku\n  default_collection: Predvolená kolekcia\n  delete_collection: Odstrániť zbierku\n  edit_collection: Upraviť kolekciu\n  enable: Povoliť manažéra tapiet\n  extend: Predĺžte primárne\n  fit:\n    contain: Obsahuje\n    cover: Obal\n    fill: Naplňte stránku\n  flipHorizontal: Flip Horizontálne\n  flipVertical: Flip Vertical\n  generating_thumbnails: Generovanie miniatúr tapety\n  hours: hodiny\n  interval: Vymeňte tapetu každý\n  minutes: minúta\n  monitor_collection: Zbierka monitorov\n  multimonitor_behaviour: Multimonitorové správanie\n  muted: Stlmiť zvuk videa\n  no_background: Empty SlideShow, namiesto toho pomocou pozadia témy.\n  no_collections: >-\n    Zatiaľ nie sú vytvorené žiadne zbierky. Kliknutím na tlačidlo + ho\n    vytvoríte.\n  objectFit: Pozadie Fit\n  objectPosition: Pozadie Pozícia\n  overlayColor: Farba prekrytia\n  overlayMixBlendMode: Režim prekrývania Mix\n  per_monitor: Na jeden monitor\n  playback: Rýchlosť prehrávania\n  position:\n    bottom: Spodná časť\n    center: Stredisko\n    left: Vľavo\n    right: Vpravo\n    top: Top\n  processing_video: Spracovanie videa\n  random: Randomizovať prezentáciu\n  saturation: Nasýtenie\n  seconds: sekundy\n  select_collection: Vyberte položku Zbierka\n  thumbnail_generation_complete: Generovanie miniatúr je dokončené\n  thumbnail_generation_finished: Generovanie miniatúr bolo úspešne ukončené\n  wallpaper_collection: Kolekcia tapiet\n  wallpaper_settings: Nastavenia tapety\n  withOverlay: S prekrytím\n  workspace_collections: Kolekcie pracovného priestoru\nweg:\n  auto_hide: Automaticky skryť\n  delay_to_hide: Odložiť skryť\n  delay_to_show: Oneskorenie zobrazenia\n  dock_side: Pozícia\n  enable: Povoliť prístavisko/panel úloh\n  filtering: Filtrovanie položiek\n  gap: Priepasť\n  hide_mode:\n    always: Vždy\n    never: Nikdy\n    on_overlap: Na prekrytí\n  items:\n    gap: Priestor medzi položkami\n    label: Predmety\n    pinned_visibility:\n      always: Vždy\n      label: Viditeľnosť pripnutých položiek\n      when_primary: Keď je monitor primárny\n    show_instance_counter: Zobrazenie počítadla otvorených okien\n    show_window_title: Zobrazenie názvu otvoreného okna (len horizontálne)\n    size: Veľkosť položky\n    split_windows: Rozdeliť okná (jedna položka na okno)\n    temporal_visibility:\n      all: Všetky\n      label: Viditeľnosť nepripnutých položiek\n      on_monitor: Na monitore\n    visible_separators: Viditeľné oddeľovače\n  label: Prístavný panel\n  margin: Okraj\n  mode:\n    full_width: Celá šírka obrazovky\n    min_content: Malý ako len môže byť\n  padding: Vypchávka\n  show_end_task: Zobrazenie koncovej úlohy na paneli úloh\n  width: Šírka\nwelcome:\n  give_a_review: Dajte recenziu\n  message: >-\n    Seelen UI je bezplatné a open source desktopové prostredie pre Windows. Tu\n    máte plnú kontrolu nad tým, ako vaša pracovná plocha vyzerá a ako sa správa,\n    čo vám umožňuje prispôsobiť každý detail tak, aby zodpovedal vášmu\n    pracovnému postupu a štýlu.\n  ok: Začnime!\n  review: >-\n    Ak radi používate používateľské rozhranie Seelen, zvážte zanechanie recenzie\n    v obchode – vaša spätná väzba pomáha projektu rásť a zlepšovať sa.\n  title: Vitajte v používateľskom rozhraní Seelen!\nwidget:\n  enable: Povolenie tohto widgetu\n  enable_for_monitor: Povolenie na tomto monitore\n  instances: Inštancie\nwm:\n  animations:\n    duration: Trvanie animácie (MS)\n    ease_function: Animačná uvoľňovanie funkcie\n    enable: Povoľte animácie okien\n  author: Autor\n  border:\n    enable: Povoľte hranicu okna\n    offset: Vyrovnanie\n    width: Šírka hranice\n  description: Opis\n  drag_behavior: Správanie ťahania\n  drag_behavior_options:\n    sort: Triediť (zmeniť poradie okien počas ťahania)\n    swap: Vymeniť (vymeniť okná na konci ťahania)\n  enable: Povoľte správcu okien obkladov\n  layout: Usporiadanie\n  resize_delta: Zmeňte veľkosť delty (%)\n  space_between_containers: Priestor medzi nádobami\n  workspace_offset: Offsety Workspaces (marže)\n  workspace_padding: Čalúnenie pracovných priestorov\n'yes': Áno\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/so.yml",
    "content": "action:\n  confirm: Ma hubtaa?\n  confirm_body: Ficilkan lama beddeli karo.\napps_configurations:\n  app:\n    bindings: Xirashada (Xusuusin labada ikhtiyaar ayaa loo baahan yahay)\n    category: Dabaqad\n    category_placeholder: Midna\n    monitor: Kaabba-galaas\n    monitor_placeholder: Midna\n    name: Magac\n    ok_create: Abuurid\n    ok_edit: Cusboonaysiin\n    ok_readonly: Tafatir sida cusub\n    options:\n      NoInteractive: Is dhexgalka\n      VdPinned: Muuji dhammaan goobta shaqada\n      WmFloat: TWM - Bilow sabuuradda\n      WmForce: TWM - Xoog Maamulka\n      WmUnmanage: TWM - oo aan si qabad lahayn\n    options_label: Fursadaha dheeraadka ah\n    title_create: Abuuritaanka {{Magaca {Magaca}\n    title_edit: Tafatirka {{magaca}}\n    title_readonly: Daawashada {{,}\n    weg_options_label: Dock / Fursadaha Hawlgalka\n    wm_options_label: Xulashooyinka Maamulaha daaqada\n    workspace: Shaqo-joojin\n    workspace_placeholder: Midna\n  bundled_msg: >-\n    Qaabeyntan xirmada ma aha wax aan la taabtaan oo loogu talagalay inay ku\n    siiyaan khibradda ugu fiican ee aan la helin. Waxay si otomaatig ah u\n    habeeyaan codsiyada ugu caansan adiga.\n  bundled_title: App COLOND SOLDED SELEN\n  confirm_delete: Ma hubtaa inaad rabto inaad tirtirto qaabeyntan / s?\n  confirm_delete_title: Xaqiiji Delete\n  delete: Tirtirid\n  export: Dhoofin\n  export_full: Dejinta Dhoofinta Codsiga\n  extra_info: >-\n    Seelen UI waxay isticmaashaa hal aqoonsi oo app kasta ah (ciyaartii ugu\n    horaysay ee la helay) markaa siday u kala horreeyaan sida loo cayimay ayaa\n    muhiim ah, waxa ugu dambeeya ee lagu daray ayaa la kala hormarin doonaa,\n    sida la ogsoon yahay in miiska si caadi ah loo soocay kii u dambeeyay ilaa\n    kii hore.\n  identifier:\n    add_block: Ku dar xannibaadda\n    and: Iyo\n    id: Aqoonsiga\n    kind: Aqoonso\n    matching_strategy: Istaraatiijiyadda iswaafajinta\n    matching_strategy_option:\n      contains: Waxa ku jira\n      ends_with: Ku dhamaata\n      equals: U dhigma\n      regex: Odhaah joogto ah\n      starts_with: Waxay ku bilaabataa\n    negation: Ciriiri u dhigma\n    or: Ama\n    remove: Tirtir xannibaadda\n    type:\n      class: Fasalka\n      exe: Ex\n      path: Jidka\n      title: Ciwaanka\n  import: Soo dejin\n  import_full: Soo dejinta goobaha codsashada\n  new: Cusub\n  search: Raadin\n  swap: Isku beddelo\ncancel: Burin\nclose: Xirid\ndelete: Tirtirid\ndevtools:\n  app_folders: Faylka app\n  custom_config_file: Xamuul faylka caadiga ah ee config\n  data_folder: Faylka xogta\n  enable: U oggolow qalabka horumarinta\n  install_folder: Xeerka rakibaadda\n  load: Xammuul\n  settings_file: Faylka Dejinta\n  simulate_perm:\n    label: Isku day Codsiga Ogolaanshaha Widget\n    result_allowed: Ogolaanshaha waa la bixiyay\n    result_denied: Ogolaanshaha waa la diiday\n    trigger: Isku day\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Calaamadaha Calaamadaha Camis Cast\n  clear_icons_tooltip: >-\n    Dib-u-bilaabi kara ayaa loo baahan karaa inuu si buuxda u dhaqan geliyo\n    dhammaan waxyaabaha widgets\n  exit: Jooji / bixitaan\n  links: Xiriirinta rasmiga ah\n  relaunch: Dib u beer\n  version: Werin\n  version_fixed: >-\n    Codsiga iyo nooca Runtime WebView2 waa go'an. Tani waxay ka dhigan tahay in\n    codsigu aanu heli doonin wax cusub oo WebView2 Runtime aan si toos ah loogu\n    cusboonaysiin doonin cusbooneysiinta Windows.\ngeneral:\n  accent_color: Midab caqabad\n  date_format: Taariikh format\n  date_format_how_to: Sidee loo qoraa qaabka taariikhda?\n  hardware_acceleration: Dardargelinta Qalabka\n  hardware_acceleration_description: >-\n    Deminta dardargelinta qalabku waxay yarayn doontaa isticmaalka xusuusta,\n    laakiin waxay sababi kartaa arrimo waxqabadka. Waa badbaado in la joojiyo\n    haddii aadan isticmaali doonin gidaarada tooska ah.\n  icon_pack:\n    available: Xirmooyinka Icon ee la heli karo\n    selected: Xirmooyinka Astaanta Firfircoon\n  language: Af\n  monday: Isniin\n  performance_mode:\n    on_battery: Batteriga\n    on_energy_saver: Ku Saabsan Tamarta Saver\n    options:\n      disabled: Itaal-daran\n      extreme: Xad-dhaaf ah\n      minimal: Aan yahayeexan\n    plugged: Xirxiran ama ku dallac\n  polling_interval: Inta u dhaxaysa nidaamka cod bixinta\n  polling_interval_description: >-\n    Intee jeer (ilbiriqsi gudahood) Seelen UI waxay hubisaa ilaha nidaamka sida\n    CPU, RAM, network, iyo dhaqdhaqaaqa diskka. Nambar ka yar macnaheedu waa\n    cusbooneysiin badan oo soo noqnoqda, laakiin isticmaalka kheyraadka wax yar\n    ka sarreeya.\n  saturday: Sabti\n  start_of_week: Bilawga Todobaadka\n  startup: Ku orod bilowga?\n  sunday: Axad\n  theme:\n    available: Mawduucyada la heli karo\n    selected: Mawduucyada Firfircoon\nheader:\n  labels:\n    config: Qaabaynta\n    developer: Loogu talagalay horumarka\n    extras: Ka duwan daro\n    general: Guud\n    home: Guri\n    icon_pack_editor: Calaamadaha lagu dhejiyay\n    iconpack: Xirmooyinka icon\n    monitors: Kormeerayaasha\n    plugin: Pluins\n    resources: Kheyraadka\n    shortcuts: Gaaban\n    soundpack: Xirmooyinka dhawaaqa\n    specific_apps: Dejinta Codsi\n    theme: Mowduucyada\n    virtual_desk: Desktops Virtual\n    wallpaper: Darf-biyoodka\n    widget: Widgetts\nhome:\n  new_resources: Kheyraadka cusub\ninherit: Dhaxlid\ninProgress: Horusocod ...\ninsert: Gelin\nloading: Loading ...\nmiscellaneous: Kala duduwan\nmonitors_configurations:\n  label: Kormeer {{tus ind ind}}\nmore: Ka badan\n'no': Maya\nopen: Furid\nquit: Joojin\nremove: Ka qaadid\nreset_all_to_default: Dib u dejiso dhammaan qiimayaasha caadiga ah\nreset_to_default: Dib u dejiso qiimaha caadiga ah\nresources:\n  app_outdated: >-\n    Kheyraadkaani wuxuu u baahan yahay nooc cusub oo ah Selelel Ui oo si sax ah\n    u shaqeynaya.\n  corrupted_wallpaper: >-\n    Ku guuldareystay in la soo saaro thumbnail - qaab muuqaal ah oo xumaaday ama\n    aan la taageerin\n  delete: Tirtir Kheyraadka\n  discover: Soo hel ilo badan\n  has_update: Cusboonaysiin ayaa la heli karaa\n  high_impact: Saamaynta sare ee waxqabadka\n  import_wallpapers: Soo dejiso waraaqaha boodhka ee maxalliga ah\n  open_folder: Fur faylka furan\n  outdated: >-\n    Kheyraadkan waxaa loogu talagalay in nooc ka weyn oo ah Selelel Ui oo laga\n    yaabo inuu si sax ah u shaqeyn.\n  see_on_website: Ka eeg bogga internetka\nreview_request:\n  not_now: Hadda maaha\n  sure: Hubaal!\n  title: Ku raaxaysiga Seelen UI?\nsave: Badbaadin\nsave_and_restart: Badbaadin & Dib u bilow\nsearch: Raadi\nsee_more: Arag in dheeraad ah\nshortcuts:\n  duplicate_error: Jidka gaaban waa la koobiyay\n  enable: U fududee nidaamka qaab dhismeedka gaagaaban\n  enable_tooltip: >-\n    Disable Haddii aad hirgelin doontid nidaamkaaga gaagaaban ee aad adigu iska\n    leedahay adiga oo adeegsanaya macmiilka SELEN UI\n  labels:\n    create_new_workspace: Abuuro shaqooyin cusub\n    cycle_stack_next: Wareegga xidhmada\n    cycle_stack_prev: Wareegga hore\n    cycle_wallpaper_next: U beddelo warqadaha xiga\n    cycle_wallpaper_prev: U beddelo xaashiyaha hore\n    decrease_height: Hoos u dhig dhererka\n    decrease_width: Ballaadhinta ballac\n    destroy_current_workspace: Burburinta goobta shaqada ee hadda jirta\n    focus_bottom: Hoos udhaca hoose\n    focus_latest: Diirad saar\n    focus_left: Ahrive bidix\n    focus_right: Diiradda saara\n    focus_top: Xoogga saar dusha sare\n    increase_height: Kordhi dhererka\n    increase_width: Kordhi ballaadhan\n    misc_force_quit: Xoog ku jooji\n    misc_force_restart: Xoogga dib u bilow\n    misc_open_settings: Meel furan\n    misc_toggle_lock_tracing: Ku wareeji raadinta qufulka (logs)\n    misc_toggle_win_event_tracing: Ku guuleysta raadinta dhacdada dhacdada (logs)\n    move_to_workspace: U dhaqaaq goobta shaqada {0}}\n    move_window_down: Daaqadda u dhaqaaq ilaa hoose\n    move_window_left: Daaqadda u dhaqaaq ilaa bidix\n    move_window_right: Daaqadda u dhaqaaq dhanka midig\n    move_window_up: Daaqadda u dhaqaaq ilaa sare\n    pause_tiling: Jooji Maareeyaha Daaqadda Daaqadda\n    reserve_bottom: Kaydsanaanta hoose\n    reserve_float: Keydka sabuuradda\n    reserve_left: Kaydsanaanta bidix\n    reserve_right: Xaq u dhig\n    reserve_stack: Reebs Rap\n    reserve_top: Keydka sare\n    restore_sizes: Soo celinta cabirka\n    send_to_workspace: U dir goobta shaqada {{0}}\n    start_weg_app: Diiradda ama Bilaabashada Arjiga Codsiga {0-}}\n    switch_to_next_workspace: U beddelo goobta shaqada ee xiga\n    switch_to_previous_workspace: U beddelo shaqooyin hore\n    switch_workspace: U beddelo goobta shaqada {{0}}\n    toggle_float: Ku wareeji xarkaha suuqa\n    toggle_monocle: TOGGLE MODOCLE MoCOCLE Mode\n  readonly_tooltip: Tani waa gaagaaban oo keliya oo keliya\n  reset: Dib u dejiso defafyada\nsides:\n  bottom: Hoose\n  left: Bidix\n  right: Sax\n  top: Dusha kore\ntoolbar:\n  auto_hide: Auto qaraha\n  delay_to_hide: Dib u dhig si loo qariyo\n  delay_to_show: Dib u dhig si loo muujiyo\n  dock_side: Meel\n  enable: Keeniyaan Qalabka Fancy Toolbar\n  hide_mode:\n    always: Had iyo jeer\n    never: Marna\n    on_overlap: Isku dhafka\n  item_size: Cabbirka Shayga\n  label: Qalab argamiye\n  margin: Cabirka Margin\n  padding: Cabbirka suufka\n  placeholder: {}\nupdate:\n  available: Cusboonaysiinta la heli karo!\n  channel: Cusboonaysiinta Kanaalka\n  downloading: Soo dejinta ...\nwall:\n  backgrounds: Darf-biyoodka\n  blur: Jilicsan\n  cancel: Jooji\n  close: Xir\n  collection_name: Magaca Ururinta\n  collections: Ururinta\n  contrast: Is barbar dhigidda\n  corrupted_wallpapers_message: 'Warqadaha gidaarka ah ee jaban, tixgeli inaad tirtirto kuwan:'\n  create: Abuur\n  create_collection: Abuur Ururinta\n  default_collection: Ururinta asalka ah\n  delete_collection: Tirtir Ururinta\n  edit_collection: Wax ka beddel Ururinta\n  enable: Awood u yeelo maamulaha derbiga\n  extend: Kordhi aasaasiga\n  fit:\n    contain: Ka koobasho\n    cover: Daboolid\n    fill: Buuxin\n  flipHorizontal: Flip siman\n  flipVertical: Flip vertical\n  generating_thumbnails: Soo saarida Sawir-gacmeedka Waraaqda\n  hours: saacadood\n  interval: Beddel wadooyinka si kasta\n  minutes: daqiiqo\n  monitor_collection: La Soco Ururinta\n  multimonitor_behaviour: Habdhaqanka Kormeeraha Badan\n  muted: Muuqaalka Muuqaalka Mate\n  no_background: Slideshow madhan, adoo adeegsanaya asalka mawduuca.\n  no_collections: Weli wax ururin ah lama abuurin Guji badhanka + si aad mid u abuurto.\n  objectFit: Asalka taam\n  objectPosition: Booska asalka ah\n  overlayColor: Midab ka sarreeya\n  overlayMixBlendMode: Isku-darka iskudarka isku-darka\n  per_monitor: Kormeere kasta\n  playback: Xawaaraha ciyaarta\n  position:\n    bottom: Hoos\n    center: Dhex\n    left: Bidix\n    right: Sax\n    top: Dusha kore\n  processing_video: Hagaajinta muuqaalka\n  random: Kala sooc slideshow\n  saturation: SATION\n  seconds: sekan\n  select_collection: Dooro Ururinta\n  thumbnail_generation_complete: Jiilka thumbnail oo Dhamaystiran\n  thumbnail_generation_finished: Jiilkii thumbnail waxa uu u dhamaaday si guul leh\n  wallpaper_collection: Ururinta Warqadaha\n  wallpaper_settings: Dejinta Warqadaha\n  withOverlay: Iyadoo lagu daboolay\n  workspace_collections: Ururinta Goobta Shaqada\nweg:\n  auto_hide: Auto qaraha\n  delay_to_hide: Dib u dhig si loo qariyo\n  delay_to_show: Dib u dhig si loo muujiyo\n  dock_side: Meel\n  enable: Kartida DOCK / Turniinka\n  filtering: Shaandhaynta sheyga\n  gap: Dalool\n  hide_mode:\n    always: Had iyo jeer\n    never: Marna\n    on_overlap: Isku dhafka\n  items:\n    gap: Meel udhaxeysa waxyaabaha\n    label: Waxyaabaha\n    pinned_visibility:\n      always: Had iyo jeer\n      label: Shayga Ku dheggan Muuqaal\n      when_primary: Marka kormeeruhu uu yahay aasaasiga\n    show_instance_counter: Muuji miiska daaqadaha furan\n    show_window_title: Muuji cinwaanka daaqadda furan (oo kaliya toosan)\n    size: Shayga cabirka\n    split_windows: Kala qaybso Windows (hal shay daaqad kasta)\n    temporal_visibility:\n      all: Dhammaan\n      label: Aragtida Shayada Furan\n      on_monitor: On Monitor\n    visible_separators: Kala-sooca muuqda\n  label: Dock / Tootshar\n  margin: Margin\n  mode:\n    full_width: Ballaca shaashadda oo dhan\n    min_content: Yaryar sida ay noqon karto\n  padding: Suur\n  show_end_task: Muuji shaqada dhamaadka ee Tooska\n  width: Ballac\nwelcome:\n  give_a_review: Dib u eegis sii\n  message: >-\n    Seelen UI waa goob desktop il bilaash ah oo furan oo loogu talagalay\n    Windows. Halkan waxa aad si buuxda gacanta ugu haysaa sida uu desktop-kaagu\n    u eg yahay una dhaqmo, taas oo kuu ogolaanaysa in aad habayso tafaasiil\n    kasta si uu u waafajiyo socodka shaqadaada iyo qaabkaaga.\n  ok: Aan bilowno!\n  review: >-\n    Haddii aad ku raaxaysato isticmaalka Seelen UI, tixgeli inaad dib u eegis\n    kaga tagto dukaanka - jawaab celintaadu waxay caawisaa mashruuca inuu koro\n    oo horumaro.\n  title: Ku soo dhawoow Seelen UI!\nwidget:\n  enable: U oggolow widget-kaan\n  enable_for_monitor: U oggolow kormeerkan\n  instances: Tusaca\nwm:\n  animations:\n    duration: Duration animation (MS)\n    ease_function: Animation fududeynta shaqada\n    enable: Karti animations daaqadaha\n  author: Qoraa\n  border:\n    enable: Karti xadka daaqadaha\n    offset: Xudduud xadka\n    width: Xadka ballaca\n  description: Sifo\n  drag_behavior: Jiid Dhaqanka\n  drag_behavior_options:\n    sort: Kala sooc (dib u hagaaji daaqadaha markaad jiidanayso)\n    swap: Isku beddel (ku beddel daaqadaha dhamaadka jiida)\n  enable: U oggolow maamulaha daaqadda daaqadda\n  layout: Khariidad\n  resize_delta: Dib u dhig Delta (%)\n  space_between_containers: Meel bannaan oo u dhexeeya weelka\n  workspace_offset: Goobaha shaqada ayaa ka dhigtay\n  workspace_padding: Goobta Goobta Goobta\n'yes': Haa\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/sr.yml",
    "content": "action:\n  confirm: Јесте ли сигурни?\n  confirm_body: Ова радња се не може поништити.\napps_configurations:\n  app:\n    bindings: Везивање (белешка су обе опције потребне)\n    category: Категорија\n    category_placeholder: Ниједан\n    monitor: Монитор\n    monitor_placeholder: Ниједан\n    name: Назив\n    ok_create: Креирај\n    ok_edit: ажурирање\n    ok_readonly: Уреди као ново\n    options:\n      NoInteractive: Но Интерацтиве\n      VdPinned: Прикажи у свим радним просторима\n      WmFloat: Твм - Почни да плуташ\n      WmForce: Твм - Форце Манаге\n      WmUnmanage: Твм - Унманаге\n    options_label: Додатне опције\n    title_create: Стварање {{наме}}\n    title_edit: Уређивање {{наме}}\n    title_readonly: Гледајући {{наме}}\n    weg_options_label: Опције прикључке / траке задатака\n    wm_options_label: Опције Виндов Манагер опције\n    workspace: Радни простор\n    workspace_placeholder: Ниједан\n  bundled_msg: >-\n    Ове пакетне конфигурације нису уређене и дизајниране су да вам пруже најбоље\n    искуство без прилагођавања. Они аутоматски конфигуришу најчешће апликације\n    за вас.\n  bundled_title: Апликација конфигурација у пакету са СЕЕЛЕН-ом\n  confirm_delete: Јесте ли сигурни да желите да избришете ову конфигурацију / с?\n  confirm_delete_title: Потврдити брисање\n  delete: Избрисати\n  export: Извоз\n  export_full: Подешавања извоза по апликацији\n  extra_info: >-\n    СЕЕЛЕН УИ Користите само један идентификатор по апликацији (први меч\n    пронађен), тако да је налог у начину на који је специфичан, приоритет ће\n    бити приоритетно, јер је назначено да је табела сортирана подразумевано из\n    Старе.\n  identifier:\n    add_block: Додај блок\n    and: И\n    id: Идентификатор\n    kind: Идентификовати\n    matching_strategy: Стратегија подударања\n    matching_strategy_option:\n      contains: Садржи\n      ends_with: Завршава са\n      equals: Једнако\n      regex: Регуларни израз\n      starts_with: Почиње са\n    negation: Негирајте подударање\n    or: Или\n    remove: Избрисати блок\n    type:\n      class: Класа\n      exe: Еке\n      path: Пут\n      title: Наслов\n  import: Увоз\n  import_full: Подешавања увоза по апликацији\n  new: Нова\n  search: Претрага\n  swap: Замена\ncancel: Поништити, отказати\nclose: Затворити\ndelete: Избрисати\ndevtools:\n  app_folders: Мапе за апликације\n  custom_config_file: Учитајте прилагођену конфигуријску датотеку\n  data_folder: Мапа података\n  enable: Омогућите алате за програмере\n  install_folder: Инсталациона мапа\n  load: Оптеретити\n  settings_file: Подешавања\n  simulate_perm:\n    label: Симулирајте захтев за дозволу за виџет\n    result_allowed: ✓ Дозвола је дата\n    result_denied: ✗ Дозвола одбијена\n    trigger: Симулирајте\n    widget_id_placeholder: '@аутхор/видгет-наме'\nextras:\n  clear_icons: Цлеар Систем Ицонс Цацхе\n  clear_icons_tooltip: >-\n    Могло би се поновно покретање моћи да у потпуности ступи на снагу на све\n    видгете\n  exit: Престанак / излаз\n  links: Званичне везе\n  relaunch: Поновити\n  version: Верзија\n  version_fixed: >-\n    Верзије апликације и ВебВиев2 Рунтиме су поправљене. То значи да апликација\n    неће примати ажурирања и да ВебВиев2 Рунтиме неће бити аутоматски ажуриран\n    ажурирањима за Виндовс.\ngeneral:\n  accent_color: Боја нагласности\n  date_format: Формат датума\n  date_format_how_to: Како написати формат датума?\n  hardware_acceleration: Хардверско убрзање\n  hardware_acceleration_description: >-\n    Онемогућавање хардверског убрзања ће смањити употребу меморије, али може\n    изазвати проблеме са перформансама. Безбедно је онемогућити ако не желите да\n    користите живе позадине.\n  icon_pack:\n    available: Доступни пакети икона\n    selected: Активни пакети икона\n  language: Језик\n  monday: понедељак\n  performance_mode:\n    on_battery: На батерији\n    on_energy_saver: На уштеду енергије\n    options:\n      disabled: Инвалидски\n      extreme: Екстремни\n      minimal: Минималан\n    plugged: Прикључен или пуњење\n  polling_interval: Интервал анкетирања система\n  polling_interval_description: >-\n    Колико често (у секундама) Сеелен УИ проверава системске ресурсе као што су\n    ЦПУ, РАМ, мрежа и активност диска. Мањи број значи чешће ажурирање, али\n    нешто већу употребу ресурса.\n  saturday: Субота\n  start_of_week: Почетак недеље\n  startup: Ради на покретању?\n  sunday: недеља\n  theme:\n    available: Доступне теме\n    selected: Активне теме\nheader:\n  labels:\n    config: Конфигурације\n    developer: За програмере\n    extras: Додаци\n    general: Генерал\n    home: Дом\n    icon_pack_editor: Кеширане иконе\n    iconpack: Паковање икона\n    monitors: Монитори\n    plugin: Додаци\n    resources: Ресурси\n    shortcuts: Пречице\n    soundpack: Звучна паковања\n    specific_apps: Подешавања по апликацији\n    theme: Тема\n    virtual_desk: Виртуелни радне површине\n    wallpaper: Позадине\n    widget: Видгети\nhome:\n  new_resources: Нови ресурси\ninherit: Наследити\ninProgress: У току...\ninsert: Уметнути\nloading: Учитавање ...\nmiscellaneous: Остало\nmonitors_configurations:\n  label: Монитор {{Индек}}\nmore: Више\n'no': Не\nopen: Отворен\nquit: Одустати\nremove: Скинути\nreset_all_to_default: Ресетујте све за подразумеване вредности\nreset_to_default: Ресетујте на подразумевану вредност\nresources:\n  app_outdated: Овај ресурс захтева новију верзију СЕЕЛЕН-а да ради правилно.\n  corrupted_wallpaper: Екстраховање сличице није успело – оштећен или неподржан видео формат\n  delete: Избриши ресурс\n  discover: Откријте више ресурса\n  has_update: Доступно је ажурирање\n  high_impact: Велики утицај на перформансе\n  import_wallpapers: Увоз локалне позадине\n  open_folder: Отворите мапу ресурса\n  outdated: >-\n    Овај ресурс је дизајниран за старију верзију СЕЕЛЕН УИ и можда неће радити\n    исправно.\n  see_on_website: Погледајте на веб страници\nreview_request:\n  not_now: Не сада\n  sure: Наравно!\n  title: Уживате у корисничком интерфејсу Сеелен?\nsave: сачувати\nsave_and_restart: Сачувај и поново покрените\nsearch: Тражи\nsee_more: Видети више\nshortcuts:\n  duplicate_error: Ова пречица је дуплирана\n  enable: Омогућите уграђене системе пречаца\n  enable_tooltip: >-\n    Онемогућите ако ћете имплементирати сопствени систем пречаца помоћу СЕЕЛЕН\n    УИ клијента\n  labels:\n    create_new_workspace: Креирајте нови радни простор\n    cycle_stack_next: Следећи положај циклуса\n    cycle_stack_prev: Цицле Стацк Претходно\n    cycle_wallpaper_next: Промените се на следећу позадину\n    cycle_wallpaper_prev: Промена у претходне позадине\n    decrease_height: Смањите висину\n    decrease_width: Смањите ширину\n    destroy_current_workspace: Уништите тренутни радни простор\n    focus_bottom: На дно фокуса\n    focus_latest: Фокусирати најновије\n    focus_left: Фокус је остављен\n    focus_right: Фокусирати право\n    focus_top: Фокусирати врх\n    increase_height: Повећати висину\n    increase_width: Повећати ширину\n    misc_force_quit: Престанак\n    misc_force_restart: Поново покренути\n    misc_open_settings: Отвори подешавања\n    misc_toggle_lock_tracing: Укључите тражење закључавања (трупци)\n    misc_toggle_win_event_tracing: Пребацивање тражења догађаја Вин (Дневци)\n    move_to_workspace: Пређите на радни простор {{0}}\n    move_window_down: Померите прозор до дна\n    move_window_left: Померите прозор на лево\n    move_window_right: Померите прозор удесно\n    move_window_up: Померите прозор на врх\n    pause_tiling: Паузирајте менаџер прозора\n    reserve_bottom: Резервисати дно\n    reserve_float: Резерват\n    reserve_left: Резерват лево\n    reserve_right: Резервисати право\n    reserve_stack: Резервни сноп\n    reserve_top: Резервисати врх\n    restore_sizes: Вратите величине\n    send_to_workspace: Пошаљи на радни простор {{0}}\n    start_weg_app: Фокусирајте или започните апликацију {{0}}\n    switch_to_next_workspace: Пређите на следећи радни простор\n    switch_to_previous_workspace: Пређите на претходни радни простор\n    switch_workspace: Пређите на радни простор {{0}}\n    toggle_float: Укључивање прозора Флоат режим\n    toggle_monocle: Укључите режим Монокла Моноцле Воркспаце\n  readonly_tooltip: Ово је пречица само за читање\n  reset: Ресетујте на подразумеване вредности\nsides:\n  bottom: Дно\n  left: Лево\n  right: Јел тако\n  top: На врху\ntoolbar:\n  auto_hide: Аутоматска кожа\n  delay_to_hide: Одложите да се сакријете\n  delay_to_show: Одлагање приказивања\n  dock_side: Положај\n  enable: Омогућите фанци траку са алаткама\n  hide_mode:\n    always: Увек\n    never: Никада\n    on_overlap: На преклапање\n  item_size: Величина артикла\n  label: Алатна трака\n  margin: Величина маргине\n  padding: Паддинг Сизе\n  placeholder: {}\nupdate:\n  available: Ажурирање доступно!\n  channel: Ажурирај канал\n  downloading: Преузимање ...\nwall:\n  backgrounds: Позадине\n  blur: Замутити\n  cancel: Откажи\n  close: Затвори\n  collection_name: Назив колекције\n  collections: Збирке\n  contrast: Контраст\n  corrupted_wallpapers_message: 'Оштећене позадине, размислите о брисању ових:'\n  create: Креирај\n  create_collection: Направите колекцију\n  default_collection: Подразумевана колекција\n  delete_collection: Избриши колекцију\n  edit_collection: Уреди колекцију\n  enable: Омогућите менаџера позадина\n  extend: Продужите примарни\n  fit:\n    contain: Садрзати\n    cover: Покривати\n    fill: Нанети\n  flipHorizontal: Флип хоризонтално\n  flipVertical: Вертикални\n  generating_thumbnails: Генерисање сличица позадине\n  hours: сати\n  interval: Промените позадину сваком\n  minutes: минут\n  monitor_collection: Монитор Цоллецтион\n  multimonitor_behaviour: Мултимонитор Бехавиор\n  muted: Искључите звук видео записа\n  no_background: Празан презентацију, уместо тога користећи прибор за тему.\n  no_collections: >-\n    Још увек није направљена ниједна збирка. Кликните на дугме + да бисте га\n    креирали.\n  objectFit: Позадина погодна\n  objectPosition: Позадински положај\n  overlayColor: Прекривање боје\n  overlayMixBlendMode: Режим прекривене мешавине помешања\n  per_monitor: По монитору\n  playback: Брзина репродукције\n  position:\n    bottom: Дно\n    center: Центер\n    left: Лево\n    right: У праву\n    top: На врху\n  processing_video: Обрада видео записа\n  random: Рандомизе СлидеСхов\n  saturation: Засићење\n  seconds: секунди\n  select_collection: Изаберите Колекција\n  thumbnail_generation_complete: Генерисање сличица је завршено\n  thumbnail_generation_finished: Генерисање сличица је успешно завршено\n  wallpaper_collection: Колекција тапета\n  wallpaper_settings: Подешавања позадине\n  withOverlay: Са прекривањем\n  workspace_collections: Збирке радног простора\nweg:\n  auto_hide: Аутоматска кожа\n  delay_to_hide: Одложите да бисте се сакрили\n  delay_to_show: Одлагање приказивања\n  dock_side: Положај\n  enable: Омогући прикључак / траку задатака\n  filtering: Филтрирање предмета\n  gap: Празнина\n  hide_mode:\n    always: Увек\n    never: Никада\n    on_overlap: На преклапање\n  items:\n    gap: Простор између предмета\n    label: Предмети\n    pinned_visibility:\n      always: Увек\n      label: Видљивост закачених ставки\n      when_primary: Када је монитор примарни\n    show_instance_counter: Прикажи отворите прозори\n    show_window_title: Прикажи назив отвореног прозора (само хоризонтално)\n    size: Величина предмета\n    split_windows: Подели прозоре (једна ставка по прозору)\n    temporal_visibility:\n      all: Све\n      label: Видљивост откачених ставки\n      on_monitor: На монитору\n    visible_separators: Видљиви сепаратори\n  label: Доцк / трака задатака\n  margin: Маргина\n  mode:\n    full_width: Пуна ширина екрана\n    min_content: Мала колико може бити\n  padding: Подлокнути\n  show_end_task: Прикажи крајњи задатак на траци задатака\n  width: Ширина\nwelcome:\n  give_a_review: Дајте рецензију\n  message: >-\n    Сеелен УИ је бесплатно десктоп окружење отвореног кода за Виндовс. Овде\n    имате потпуну контролу над изгледом и понашањем ваше радне површине, што вам\n    омогућава да прилагодите сваки детаљ како би одговарао вашем току рада и\n    стилу.\n  ok: Почнимо!\n  review: >-\n    Ако уживате у коришћењу корисничког интерфејса Сеелен, размислите о томе да\n    оставите рецензију у продавници — ваше повратне информације помажу да\n    пројекат расте и унапређује се.\n  title: Добродошли у Сеелен УИ!\nwidget:\n  enable: Омогућите овај виџет\n  enable_for_monitor: Омогући на овом монитору\n  instances: Случајеви\nwm:\n  animations:\n    duration: Трајање анимације (МС)\n    ease_function: Олакшања анимације\n    enable: Омогућите анимације прозора\n  author: Аутор\n  border:\n    enable: Омогућите границу прозора\n    offset: Помак на границу\n    width: Ширина границе\n  description: Опис\n  drag_behavior: Драг Бехавиор\n  drag_behavior_options:\n    sort: Сортирај (промени редослед прозора током превлачења)\n    swap: Замени (замени прозоре на крају превлачења)\n  enable: Омогућите менаџера прозора\n  layout: Изглед\n  resize_delta: Промените величину Делта (%)\n  space_between_containers: Простор између контејнера\n  workspace_offset: Оффсет радних простора (маржи)\n  workspace_padding: Облога за радне просторе\n'yes': Да\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/sv.yml",
    "content": "action:\n  confirm: Är du säker?\n  confirm_body: Denna åtgärd kan inte ångras.\napps_configurations:\n  app:\n    bindings: Bindning (Observera att båda alternativen krävs)\n    category: Kategori\n    category_placeholder: Ingen\n    monitor: Övervaka\n    monitor_placeholder: Ingen\n    name: namn\n    ok_create: Skapa\n    ok_edit: Uppdatering\n    ok_readonly: Redigera som ny\n    options:\n      NoInteractive: Ingen interaktiv\n      VdPinned: Visa i alla arbetsytor\n      WmFloat: Twm - Börja flyta\n      WmForce: Twm - Force Hantera\n      WmUnmanage: Twm - Unmanage\n    options_label: Extra alternativ\n    title_create: Skapa {{name}}\n    title_edit: Redigera {{name}}\n    title_readonly: Visa {{name}}\n    weg_options_label: Dock/Aktivitetsfältalternativ\n    wm_options_label: Fönsterchefalternativ\n    workspace: Arbetsyta\n    workspace_placeholder: Ingen\n  bundled_msg: >-\n    Dessa bundna konfigurationer är inte redigerbara och är utformade för att ge\n    dig den bästa upplevelsen utan anpassning. De konfigurerar automatiskt de\n    vanligaste applikationerna för dig.\n  bundled_title: App config bunded med seelen\n  confirm_delete: Är du säker på att du vill ta bort den här konfigurationen/s?\n  confirm_delete_title: Bekräfta radering\n  delete: Radera\n  export: Exportera\n  export_full: Exportera inställningar per applikation\n  extra_info: >-\n    Seelen UI använder endast en identifierare per app (första matchningen\n    hittas) så ordningen i hur de specificeras är viktig, det senast tillagda\n    kommer att prioriteras, som observera att tabellen sorteras som standard\n    från senaste till gammal.\n  identifier:\n    add_block: Lägga till\n    and: OCH\n    id: Identifierare\n    kind: Identifiera sig genom\n    matching_strategy: Matchningsstrategi\n    matching_strategy_option:\n      contains: Innehåller\n      ends_with: Slutar med\n      equals: Lika\n      regex: Reguljärt uttryck\n      starts_with: Börjar med\n    negation: Negera matchning\n    or: ELLER\n    remove: Radera block\n    type:\n      class: Klass\n      exe: Exe\n      path: Väg\n      title: Titel\n  import: Importera\n  import_full: Importera inställningar per applikation\n  new: Ny\n  search: Sök\n  swap: Byta\ncancel: Annullera\nclose: Nära\ndelete: Radera\ndevtools:\n  app_folders: Appmappar\n  custom_config_file: Ladda anpassad konfigurationsfil\n  data_folder: Datarmapp\n  enable: Aktivera utvecklarverktyg\n  install_folder: Installationsmapp\n  load: Ladda\n  settings_file: Inställningsfil\n  simulate_perm:\n    label: Simulera widgettillståndsbegäran\n    result_allowed: ✓ Tillstånd beviljat\n    result_denied: ✗ Tillstånd nekad\n    trigger: Simulera\n    widget_id_placeholder: '@författare/widget-namn'\nextras:\n  clear_icons: Rensa cachen för systemikoner\n  clear_icons_tooltip: En omstart kan krävas för att få full effekt på alla widgets\n  exit: Sluta/avsluta\n  links: Officiella länkar\n  relaunch: Omstart\n  version: Version\n  version_fixed: >-\n    Applikationen och WebView2 Runtime-versionerna är fixade. Detta innebär att\n    applikationen inte kommer att ta emot uppdateringar och WebView2 Runtime\n    kommer inte att uppdateras automatiskt med Windows-uppdateringar.\ngeneral:\n  accent_color: Accentfärg\n  date_format: Datumformat\n  date_format_how_to: Hur skriver man ett datumformat?\n  hardware_acceleration: Hårdvaruacceleration\n  hardware_acceleration_description: >-\n    Om du inaktiverar hårdvaruacceleration minskar minnesanvändningen, men kan\n    orsaka prestandaproblem. Är säkert att inaktivera om du inte använder\n    levande bakgrundsbilder.\n  icon_pack:\n    available: Tillgängliga ikonpaket\n    selected: Aktiva ikonpaket\n  language: Språk\n  monday: måndag\n  performance_mode:\n    on_battery: På batteri\n    on_energy_saver: På energisparare\n    options:\n      disabled: Funktionshindrad\n      extreme: Extrem\n      minimal: Minimal\n    plugged: Ansluten eller laddning\n  polling_interval: Systemavfrågningsintervall\n  polling_interval_description: >-\n    Hur ofta (i sekunder) Seelen UI kontrollerar systemresurser som CPU, RAM,\n    nätverk och diskaktivitet. Ett mindre antal innebär oftare uppdateringar,\n    men något högre resursanvändning.\n  saturday: lördag\n  start_of_week: Veckans början\n  startup: Köras vid start?\n  sunday: söndag\n  theme:\n    available: Tillgängliga teman\n    selected: Aktiva teman\nheader:\n  labels:\n    config: Konfigurationer\n    developer: För utvecklare\n    extras: Extramaterial\n    general: Allmän\n    home: Hem\n    icon_pack_editor: Cachade ikoner\n    iconpack: Ikonpaket\n    monitors: Monitorer\n    plugin: Plugins\n    resources: Resurser\n    shortcuts: Genvägar\n    soundpack: Ljudpaket\n    specific_apps: Inställningar per applikation\n    theme: Teman\n    virtual_desk: Virtuella skrivbord\n    wallpaper: Bakgrundsbilder\n    widget: Widgetar\nhome:\n  new_resources: Nya resurser\ninherit: Ärva\ninProgress: Pågående...\ninsert: Infoga\nloading: Läser in...\nmiscellaneous: Diverse\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Mer\n'no': Inga\nopen: Öppen\nquit: Sluta\nremove: Ta bort\nreset_all_to_default: Återställ alla till standardvärden\nreset_to_default: Återställ till standardvärde\nresources:\n  app_outdated: Denna resurs kräver en nyare version av Seelen UI för att fungera korrekt.\n  corrupted_wallpaper: >-\n    Det gick inte att extrahera miniatyrbilden - skadat eller videoformat som\n    inte stöds\n  delete: Radera resurs\n  discover: Upptäck fler resurser\n  has_update: En uppdatering är tillgänglig\n  high_impact: Hög inverkan på prestanda\n  import_wallpapers: Importera lokala bakgrundsbilder\n  open_folder: Öppna resursmappen\n  outdated: >-\n    Den här resursen är utformad för en äldre version av Seelen UI och kanske\n    inte fungerar korrekt.\n  see_on_website: Se på hemsidan\nreview_request:\n  not_now: Inte nu\n  sure: Säker!\n  title: Gillar du Seelen UI?\nsave: Spara\nsave_and_restart: Spara och starta om\nsearch: Söka\nsee_more: Se mer\nshortcuts:\n  duplicate_error: Den här genvägen är duplicerad\n  enable: Aktivera inbyggda genvägssystem\n  enable_tooltip: >-\n    Inaktivera om du kommer att implementera ditt eget genvägssystem med Seelen\n    UI -klienten\n  labels:\n    create_new_workspace: Skapa en ny arbetsyta\n    cycle_stack_next: Cykelstack nästa\n    cycle_stack_prev: Cykelstack föregående\n    cycle_wallpaper_next: Byt till nästa tapet\n    cycle_wallpaper_prev: Ändra till tidigare tapeter\n    decrease_height: Minskande höjd\n    decrease_width: Minskande bredd\n    destroy_current_workspace: Förstör den aktuella arbetsytan\n    focus_bottom: Fokusera botten\n    focus_latest: Fokus senaste\n    focus_left: Fokus vänster\n    focus_right: Fokusera rätt\n    focus_top: Focus topp\n    increase_height: Öka höjden\n    increase_width: Öka bredd\n    misc_force_quit: Straffa\n    misc_force_restart: Tvinga omstart\n    misc_open_settings: Öppna inställningar\n    misc_toggle_lock_tracing: TOGGLE LOCK TRACING (loggar)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (loggar)\n    move_to_workspace: Flytta till Workspace {{0}}\n    move_window_down: Flytta fönster till botten\n    move_window_left: Flytta fönstret till vänster\n    move_window_right: Flytta fönstret till höger\n    move_window_up: Flytta fönstret till toppen\n    pause_tiling: Pausskakelfönsterhanterare\n    reserve_bottom: Reserv botten\n    reserve_float: Reservflott\n    reserve_left: Reservera vänster\n    reserve_right: Reservera höger\n    reserve_stack: Reservstack\n    reserve_top: Reservtopp\n    restore_sizes: Återställ storleken\n    send_to_workspace: Skicka till Workspace {{0}}\n    start_weg_app: Fokus eller starta applikationen {{0}}\n    switch_to_next_workspace: Byt till nästa arbetsyta\n    switch_to_previous_workspace: Byt till tidigare arbetsyta\n    switch_workspace: Byt till Workspace {{0}}\n    toggle_float: Växla fönsterflödesläge\n    toggle_monocle: Växla arbetsyta Monocle -läge\n  readonly_tooltip: Detta är en skrivskyddad genväg\n  reset: Återställas till standardvärden\nsides:\n  bottom: Botten\n  left: Vänster\n  right: Höger\n  top: Topp\ntoolbar:\n  auto_hide: Auto Hide\n  delay_to_hide: Dröjsmål att gömma sig\n  delay_to_show: Fördröjning att visa\n  dock_side: Position\n  enable: Aktivera snyggt verktygsfält\n  hide_mode:\n    always: Alltid\n    never: Aldrig\n    on_overlap: På överlappning\n  item_size: Objektstorlek\n  label: Verktygsfält\n  margin: Marginalstorlek\n  padding: Vadderingsstorlek\n  placeholder: {}\nupdate:\n  available: Uppdatering tillgänglig!\n  channel: Uppdateringskanal\n  downloading: Nedladdning ...\nwall:\n  backgrounds: Tapeter\n  blur: Blur\n  cancel: Avboka\n  close: Nära\n  collection_name: Samlingens namn\n  collections: Samlingar\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Skadade bakgrundsbilder, överväg att ta bort dessa:'\n  create: Skapa\n  create_collection: Skapa samling\n  default_collection: Standardsamling\n  delete_collection: Ta bort samling\n  edit_collection: Redigera samling\n  enable: Aktivera tapetchef\n  extend: Förläng primär\n  fit:\n    contain: Innehåller\n    cover: Omslag\n    fill: Fylla\n  flipHorizontal: Vänd horisontellt\n  flipVertical: Vänd vertikal\n  generating_thumbnails: Generera bakgrundsminiatyrer\n  hours: timme\n  interval: Ändra tapeter varje\n  minutes: minuter\n  monitor_collection: Övervaka samling\n  multimonitor_behaviour: Multimonitors beteende\n  muted: Stäng av videoljud\n  no_background: Tomt bildspel, med temas bakgrund istället.\n  no_collections: Inga samlingar har skapats ännu. Klicka på knappen + för att skapa en.\n  objectFit: Bakgrund Passform\n  objectPosition: Bakgrund Position\n  overlayColor: Färg på överlägg\n  overlayMixBlendMode: Overlay Mix Blandningsläge\n  per_monitor: Per bildskärm\n  playback: Uppspelningshastighet\n  position:\n    bottom: Botten\n    center: Centrum\n    left: Vänster\n    right: Rätt\n    top: Topp\n  processing_video: Bearbetar video\n  random: Slumpmässig bildspel\n  saturation: Mättnad\n  seconds: sekunder\n  select_collection: Välj Samling\n  thumbnail_generation_complete: Generering av miniatyrbilder är klar\n  thumbnail_generation_finished: Generering av miniatyrbilder har slutförts framgångsrikt\n  wallpaper_collection: Tapetsamling\n  wallpaper_settings: Bakgrundsinställningar\n  withOverlay: Med överlägg\n  workspace_collections: Arbetsyta samlingar\nweg:\n  auto_hide: Auto Hide\n  delay_to_hide: Dröjsmål att gömma sig\n  delay_to_show: Fördröjning att visa\n  dock_side: Placera\n  enable: Aktivera bryggor/aktivitetsfält\n  filtering: Filtrering av objekt\n  gap: Glipa\n  hide_mode:\n    always: Alltid\n    never: Aldrig\n    on_overlap: På överlappning\n  items:\n    gap: Utrymme mellan föremål\n    label: Föremål\n    pinned_visibility:\n      always: Alltid\n      label: Synlighet för fästa objekt\n      when_primary: När monitorn är primär\n    show_instance_counter: Visa räknare för öppna fönster\n    show_window_title: Visa titel på öppet fönster (endast horisontellt)\n    size: Objektstorlek\n    split_windows: Delade fönster (ett objekt per fönster)\n    temporal_visibility:\n      all: Alla\n      label: Synlighet för lossade objekt\n      on_monitor: På bildskärm\n    visible_separators: Synliga separatorer\n  label: Docka/arbetsfält\n  margin: Marginal\n  mode:\n    full_width: Helskärmsbredd\n    min_content: Liten som kan vara\n  padding: Stoppning\n  show_end_task: Visa slutuppgift i aktivitetsfältet\n  width: Bredd\nwelcome:\n  give_a_review: Ge en recension\n  message: >-\n    Seelen UI är en gratis skrivbordsmiljö med öppen källkod för Windows. Här\n    har du full kontroll över hur ditt skrivbord ser ut och beter sig, så att du\n    kan anpassa varje detalj för att matcha ditt arbetsflöde och din stil.\n  ok: Låt oss börja!\n  review: >-\n    Om du gillar att använda Seelen UI, överväg att lämna en recension i butiken\n    – din feedback hjälper projektet att växa och förbättras.\n  title: Välkommen till Seelen UI!\nwidget:\n  enable: Aktivera denna widget\n  enable_for_monitor: Aktivera på denna bildskärm\n  instances: Instanser\nwm:\n  animations:\n    duration: Animationens varaktighet (MS)\n    ease_function: Animationslättningsfunktion\n    enable: Aktivera Windows animationer\n  author: Författare\n  border:\n    enable: Aktivera fönstergränsen\n    offset: Gränsförskjutning\n    width: Gränsbredd\n  description: Beskrivning\n  drag_behavior: Drabeteende\n  drag_behavior_options:\n    sort: Sortera (ordna om fönster medan du drar)\n    swap: Byt (byta fönster på dragänden)\n  enable: Aktivera kakelfönsterhanterare\n  layout: Layout\n  resize_delta: Ändra ändra delta (%)\n  space_between_containers: Utrymme mellan containrar\n  workspace_offset: Arbetsytor Offset (marginaler)\n  workspace_padding: Arbetsytor\n'yes': Ja\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/sw.yml",
    "content": "action:\n  confirm: Una uhakika?\n  confirm_body: Kitendo hiki hakiwezi kufutwa.\napps_configurations:\n  app:\n    bindings: Kufunga (kumbuka chaguzi zote mbili zinahitajika)\n    category: Jamii\n    category_placeholder: Hakuna\n    monitor: Kufuatilia\n    monitor_placeholder: Hakuna\n    name: Jina\n    ok_create: Unda\n    ok_edit: Sasisha\n    ok_readonly: Hariri kama mpya\n    options:\n      NoInteractive: Hakuna maingiliano\n      VdPinned: Onyesha katika nafasi zote za kazi\n      WmFloat: TWM - anza kuelea\n      WmForce: TWM - Nguvu Kusimamia\n      WmUnmanage: TWM - UnManage\n    options_label: Chaguzi za ziada\n    title_create: Kuunda {{jina}}\n    title_edit: Kuhariri {{jina}}\n    title_readonly: Kuangalia {{jina}}\n    weg_options_label: Chaguzi za Dock/Taskbar\n    wm_options_label: Chaguzi za Meneja wa Dirisha\n    workspace: Nafasi ya kazi\n    workspace_placeholder: Hakuna\n  bundled_msg: >-\n    Usanidi huu uliowekwa wazi hauwezi kuhaririwa na umeundwa kukupa uzoefu bora\n    bila ubinafsishaji. Wao husanidi moja kwa moja programu za kawaida kwako.\n  bundled_title: Programu ya kusanidi na Seelen\n  confirm_delete: Je! Una uhakika unataka kufuta usanidi huu/s?\n  confirm_delete_title: Thibitisha Futa\n  delete: Futa\n  export: Kuuza nje\n  export_full: Mipangilio ya kuuza nje na Maombi\n  extra_info: >-\n    Seelen UI hutumia kitambulisho kimoja tu kwa kila programu (kwanza\n    kupatikana) kwa hivyo mpangilio wa jinsi ulivyobainishwa ni muhimu, ya hivi\n    punde zaidi yatapewa kipaumbele, kwani kumbuka jedwali hupangwa kwa\n    chaguo-msingi kutoka kwa hivi karibuni hadi vya zamani.\n  identifier:\n    add_block: Ongeza block\n    and: Na\n    id: Kitambulisho\n    kind: Tambua na\n    matching_strategy: Mkakati wa kulinganisha\n    matching_strategy_option:\n      contains: Ina\n      ends_with: Inaisha na\n      equals: Sawa\n      regex: Kujieleza mara kwa mara\n      starts_with: Huanza na\n    negation: Negate kulinganisha\n    or: Au\n    remove: Futa block\n    type:\n      class: Darasa\n      exe: Mfano\n      path: Njia\n      title: Kichwa\n  import: Kuagiza\n  import_full: Ingiza Mipangilio na Maombi\n  new: Mpya\n  search: Tafuta\n  swap: Kubadilishana\ncancel: Ghairi\nclose: Karibu\ndelete: Futa\ndevtools:\n  app_folders: Folda za programu\n  custom_config_file: Pakia faili ya usanidi wa kawaida\n  data_folder: Folda ya data\n  enable: Wezesha zana za msanidi programu\n  install_folder: Folda ya usanikishaji\n  load: Mzigo\n  settings_file: Faili ya mipangilio\n  simulate_perm:\n    label: Iga Ombi la Ruhusa ya Wijeti\n    result_allowed: ✓ Ruhusa imetolewa\n    result_denied: ✗ Ruhusa imekataliwa\n    trigger: Iga\n    widget_id_placeholder: '@mwandishi/wijeti-jina'\nextras:\n  clear_icons: Kashe ya icons za mfumo wazi\n  clear_icons_tooltip: Kuanza tena kunaweza kuhitajika kuchukua kikamilifu kwa vilivyoandikwa vyote\n  exit: Acha/exit\n  links: Viungo rasmi\n  relaunch: Kuzindua tena\n  version: Toleo\n  version_fixed: >-\n    Programu na matoleo ya WebView2 Runtime yamerekebishwa. Hii ina maana kwamba\n    programu haitapokea masasisho na Muda wa Uendeshaji wa WebView2\n    hautasasishwa kiotomatiki na masasisho ya Windows.\ngeneral:\n  accent_color: Rangi ya lafudhi\n  date_format: Muundo wa tarehe\n  date_format_how_to: Jinsi ya kuandika muundo wa tarehe?\n  hardware_acceleration: Kuongeza kasi ya vifaa\n  hardware_acceleration_description: >-\n    Kuzima uongezaji kasi wa maunzi kutapunguza matumizi ya kumbukumbu, lakini\n    kunaweza kusababisha matatizo ya utendaji. Ni salama kuzima ikiwa hutatumia\n    mandhari hai.\n  icon_pack:\n    available: Vifurushi vya ikoni vinavyopatikana\n    selected: Vifurushi vya ikoni zinazotumika\n  language: Lugha\n  monday: Jumatatu\n  performance_mode:\n    on_battery: Kwenye betri\n    on_energy_saver: Kwenye Saver ya Nishati\n    options:\n      disabled: Walemavu\n      extreme: Uliokithiri\n      minimal: Ndogo\n    plugged: Kuziba au kuchaji\n  polling_interval: Muda wa upigaji kura wa mfumo\n  polling_interval_description: >-\n    Ni mara ngapi (kwa sekunde) Seelen UI hukagua rasilimali za mfumo kama vile\n    CPU, RAM, mtandao na shughuli za diski. Nambari ndogo inamaanisha masasisho\n    ya mara kwa mara, lakini matumizi ya juu kidogo ya rasilimali.\n  saturday: Jumamosi\n  start_of_week: Mwanzo wa Wiki\n  startup: Kuendesha kuanza?\n  sunday: Jumapili\n  theme:\n    available: Mandhari Zinazopatikana\n    selected: Mandhari Inayotumika\nheader:\n  labels:\n    config: Usanidi\n    developer: Kwa watengenezaji\n    extras: Ziada\n    general: Mkuu\n    home: Nyumbani\n    icon_pack_editor: Icons zilizowekwa\n    iconpack: Pakiti za ikoni\n    monitors: Wachunguzi\n    plugin: Programu -jalizi\n    resources: Rasilimali\n    shortcuts: Njia za mkato\n    soundpack: Pakiti za sauti\n    specific_apps: Mipangilio na Maombi\n    theme: Mada\n    virtual_desk: Dawati za kweli\n    wallpaper: Wallpapers\n    widget: Vilivyoandikwa\nhome:\n  new_resources: Rasilimali mpya\ninherit: Urithi\ninProgress: Inaendelea...\ninsert: Ingiza\nloading: Inapakia ...\nmiscellaneous: Miscellaneous\nmonitors_configurations:\n  label: Monitor {{index}}\nmore: Zaidi\n'no': Hapana\nopen: Wazi\nquit: Acha\nremove: Ondoa\nreset_all_to_default: Rudisha yote kwa maadili ya msingi\nreset_to_default: Rudisha kwa thamani ya msingi\nresources:\n  app_outdated: Rasilimali hii inahitaji toleo jipya la Seelen UI kufanya kazi vizuri.\n  corrupted_wallpaper: Imeshindwa kutoa kijipicha - umbizo la video mbovu au ambalo halitumiki\n  delete: Futa rasilimali\n  discover: Gundua rasilimali zaidi\n  has_update: Sasisho linapatikana\n  high_impact: Athari kubwa juu ya utendaji\n  import_wallpapers: Ingiza wallpapers za mitaa\n  open_folder: Fungua folda ya rasilimali\n  outdated: >-\n    Rasilimali hii ilibuniwa kwa toleo la zamani la Seelen UI na linaweza\n    kufanya kazi vizuri.\n  see_on_website: Tazama kwenye tovuti\nreview_request:\n  not_now: Si sasa\n  sure: Hakika!\n  title: Je, unafurahia UI ya Seelen?\nsave: Kuokoa\nsave_and_restart: Okoa na uanze tena\nsearch: Tafuta\nsee_more: Tazama zaidi\nshortcuts:\n  duplicate_error: Njia hii ya mkato imerudiwa\n  enable: Wezesha mfumo wa mkato uliojengwa ndani\n  enable_tooltip: >-\n    Lemaza ikiwa utatumia mfumo wako wa njia za mkato kwa kutumia mteja wa\n    Seelen UI\n  labels:\n    create_new_workspace: Unda nafasi mpya ya kazi\n    cycle_stack_next: Mzunguko wa mzunguko unaofuata\n    cycle_stack_prev: Starehe ya mzunguko uliopita\n    cycle_wallpaper_next: Badilisha kwa Ukuta unaofuata\n    cycle_wallpaper_prev: Badilisha kwa Ukuta uliopita\n    decrease_height: Kupungua urefu\n    decrease_width: Punguza upana\n    destroy_current_workspace: Kuharibu nafasi ya kazi ya sasa\n    focus_bottom: Kuzingatia chini\n    focus_latest: Kuzingatia hivi karibuni\n    focus_left: Kuzingatia kushoto\n    focus_right: Zingatia kulia\n    focus_top: Kuzingatia juu\n    increase_height: Ongeza urefu\n    increase_width: Ongeza upana\n    misc_force_quit: Nguvu kuacha\n    misc_force_restart: Nguvu Anzisha tena\n    misc_open_settings: Mipangilio ya Fungua\n    misc_toggle_lock_tracing: Kubadilisha Ufuatiliaji wa Kufunga (Magogo)\n    misc_toggle_win_event_tracing: Kubadilisha Win Tukio Kufuatilia (Magogo)\n    move_to_workspace: Hoja kwa nafasi ya kazi {{0}}\n    move_window_down: Sogeza dirisha hadi chini\n    move_window_left: Sogeza dirisha kushoto\n    move_window_right: Sogeza dirisha kwenda kulia\n    move_window_up: Sogeza dirisha juu\n    pause_tiling: Sitisha meneja wa windows\n    reserve_bottom: Hifadhi chini\n    reserve_float: Hifadhi kuelea\n    reserve_left: Hifadhi kushoto\n    reserve_right: Hifadhi haki\n    reserve_stack: Hifadhi ya Hifadhi\n    reserve_top: Hifadhi juu\n    restore_sizes: Kurejesha ukubwa\n    send_to_workspace: Tuma kwa nafasi ya kazi {{0}}\n    start_weg_app: Kuzingatia au Anza Maombi {{0}}\n    switch_to_next_workspace: Badilisha kwenye nafasi ya kazi inayofuata\n    switch_to_previous_workspace: Badilisha kwenye nafasi ya kazi ya hapo awali\n    switch_workspace: Badilika kwa nafasi ya kazi {{0}}\n    toggle_float: Badilisha modi ya kuelea ya dirisha\n    toggle_monocle: Badilisha modi ya nafasi ya kazi\n  readonly_tooltip: Hii ni njia ya mkato ya kusoma tu\n  reset: Rudisha kwa defaults\nsides:\n  bottom: Chini\n  left: Kushoto\n  right: Haki\n  top: Juu\ntoolbar:\n  auto_hide: Kujificha kiotomatiki\n  delay_to_hide: Kuchelewa kujificha\n  delay_to_show: Kuchelewa kuonyesha\n  dock_side: Msimamo\n  enable: Wezesha zana ya dhana\n  hide_mode:\n    always: Daima\n    never: Kamwe\n    on_overlap: Juu ya mwingiliano\n  item_size: Ukubwa wa Kipengee\n  label: Zana\n  margin: Ukubwa wa Pambizo\n  padding: Ukubwa wa Padding\n  placeholder: {}\nupdate:\n  available: Sasisha inapatikana!\n  channel: Sasisha kituo\n  downloading: Kupakua ...\nwall:\n  backgrounds: Wallpapers\n  blur: Blur\n  cancel: Ghairi\n  close: Funga\n  collection_name: Jina la Mkusanyiko\n  collections: Mikusanyiko\n  contrast: Tofauti\n  corrupted_wallpapers_message: 'Mandhari zilizoharibika, zingatia kufuta hizi:'\n  create: Unda\n  create_collection: Unda Mkusanyiko\n  default_collection: Mkusanyiko Chaguomsingi\n  delete_collection: Futa Mkusanyiko\n  edit_collection: Hariri Mkusanyiko\n  enable: Washa Meneja wa Ukuta\n  extend: Ongeza msingi\n  fit:\n    contain: Kuwa na\n    cover: Funika\n    fill: Jaza\n  flipHorizontal: Flip usawa\n  flipVertical: Flip wima\n  generating_thumbnails: Inazalisha Vijipicha vya Mandhari\n  hours: masaa\n  interval: Badilisha Ukuta kila\n  minutes: dakika\n  monitor_collection: Fuatilia Mkusanyiko\n  multimonitor_behaviour: Tabia ya Multimonitor\n  muted: Sauti ya Video ya Bubu\n  no_background: Slideshow tupu, kwa kutumia mandharinyuma ya mandhari badala yake.\n  no_collections: Bado hakuna mikusanyiko iliyoundwa. Bofya kitufe cha + ili kuunda moja.\n  objectFit: Msingi wa kifafa\n  objectPosition: Msimamo wa nyuma\n  overlayColor: Rangi ya juu\n  overlayMixBlendMode: Njia ya mchanganyiko wa mchanganyiko\n  per_monitor: Kwa mfuatiliaji\n  playback: Kasi ya kucheza\n  position:\n    bottom: Chini\n    center: Kituo\n    left: Kushoto\n    right: Kulia\n    top: Juu\n  processing_video: Inachakata video\n  random: Sisitiza slideshow\n  saturation: Kueneza\n  seconds: sekunde\n  select_collection: Chagua Mkusanyiko\n  thumbnail_generation_complete: Uzalishaji wa Kijipicha Umekamilika\n  thumbnail_generation_finished: Uzalishaji wa kijipicha umekamilika\n  wallpaper_collection: Ukusanyaji wa Mandhari\n  wallpaper_settings: Mipangilio ya Mandhari\n  withOverlay: Na overlay\n  workspace_collections: Mikusanyiko ya Nafasi ya Kazi\nweg:\n  auto_hide: Kujificha kiotomatiki\n  delay_to_hide: Kuchelewa kujificha\n  delay_to_show: Kuchelewa kuonyesha\n  dock_side: Msimamo\n  enable: Wezesha Dock/Taskbar\n  filtering: Kuchuja kwa bidhaa\n  gap: Pengo\n  hide_mode:\n    always: Daima\n    never: Kamwe\n    on_overlap: Juu ya mwingiliano\n  items:\n    gap: Nafasi kati ya vitu\n    label: Vitu\n    pinned_visibility:\n      always: Daima\n      label: Mwonekano wa Vipengee Vilivyobandikwa\n      when_primary: Wakati kufuatilia ni msingi\n    show_instance_counter: Onyesha wazi Windows Counter\n    show_window_title: Onyesha kichwa wazi cha windows (usawa tu)\n    size: Saizi ya bidhaa\n    split_windows: Gawanya Windows (kipengee kimoja kwa kila dirisha)\n    temporal_visibility:\n      all: Wote\n      label: Mwonekano wa Vipengee Vilivyobanduliwa\n      on_monitor: Kwenye Monitor\n    visible_separators: Watenganisho unaoonekana\n  label: Dock/Taskbar\n  margin: Margin\n  mode:\n    full_width: Upana kamili wa skrini\n    min_content: Ndogo kama inaweza kuwa\n  padding: Padding\n  show_end_task: Onyesha kazi ya mwisho katika Taskbar\n  width: Upana\nwelcome:\n  give_a_review: Toa Uhakiki\n  message: >-\n    Seelen UI ni mazingira ya bure na ya wazi ya eneo-kazi kwa Windows. Hapa una\n    udhibiti kamili wa jinsi eneo-kazi lako linavyoonekana na kufanya kazi,\n    hivyo kukuruhusu kubinafsisha kila undani ili kuendana na utendakazi na\n    mtindo wako.\n  ok: Hebu tuanze!\n  review: >-\n    Ikiwa unafurahia kutumia Seelen UI, zingatia kuacha ukaguzi kwenye duka -\n    maoni yako husaidia mradi kukua na kuboreka.\n  title: Karibu kwenye Seelen UI!\nwidget:\n  enable: Wezesha widget hii\n  enable_for_monitor: Wezesha kwenye mfuatiliaji huu\n  instances: Matukio\nwm:\n  animations:\n    duration: Muda wa Uhuishaji (MS)\n    ease_function: Uhuishaji wa Kuongeza Uhuishaji\n    enable: Wezesha michoro za Window\n  author: Mwandishi\n  border:\n    enable: Wezesha mpaka wa dirisha\n    offset: Kukabiliana na mpaka\n    width: Upana wa mpaka\n  description: Maelezo\n  drag_behavior: Tabia ya Kuburuta\n  drag_behavior_options:\n    sort: Panga (panga upya madirisha huku ukiburuta)\n    swap: Badili (badilisha madirisha kwenye mwisho wa buruta)\n  enable: Washa Meneja wa Dirisha la Tiling\n  layout: Mpangilio\n  resize_delta: Rudisha Delta (%)\n  space_between_containers: Nafasi kati ya vyombo\n  workspace_offset: Nafasi za kazi za kukabiliana (pembezoni)\n  workspace_padding: Nafasi za kazi\n'yes': Ndio\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ta.yml",
    "content": "action:\n  confirm: நீங்கள் உறுதியாக இருக்கிறீர்களா?\n  confirm_body: இந்த செயலை செயல்தவிர்க்க முடியாது.\napps_configurations:\n  app:\n    bindings: பிணைப்பு (இரண்டு விருப்பங்களும் தேவை என்பதை நினைவில் கொள்ளவும்)\n    category: வகை\n    category_placeholder: இல்லை\n    monitor: கண்காணிக்கவும்\n    monitor_placeholder: இல்லை\n    name: பெயர்\n    ok_create: உருவாக்கு\n    ok_edit: புதுப்பிக்கவும்\n    ok_readonly: புதியதாக திருத்தவும்\n    options:\n      NoInteractive: ஊடாடுதல் இல்லை\n      VdPinned: அனைத்து பணியிடங்களிலும் காட்டு\n      WmFloat: Twm - மிதக்கத் தொடங்குங்கள்\n      WmForce: Twm - படை நிர்வகி\n      WmUnmanage: Twm - நிர்வகிக்கா\n    options_label: கூடுதல் விருப்பங்கள்\n    title_create: '{{name}} உருவாக்குகிறது'\n    title_edit: எடிட்டிங் {{பெயர்}}\n    title_readonly: '{{name}} பார்க்கிறது'\n    weg_options_label: கப்பல்துறை/பணிப்பட்டு விருப்பங்கள்\n    wm_options_label: சாளர மேலாளர் விருப்பங்கள்\n    workspace: பணியிடம்\n    workspace_placeholder: இல்லை\n  bundled_msg: >-\n    இந்தத் தொகுக்கப்பட்ட உள்ளமைவுகளைத் திருத்த முடியாது மற்றும் தனிப்பயனாக்கம்\n    இல்லாமல் சிறந்த அனுபவத்தை உங்களுக்கு வழங்கும் வகையில் வடிவமைக்கப்பட்டுள்ளது.\n    உங்களுக்கான பொதுவான பயன்பாடுகளை அவை தானாகவே கட்டமைக்கும்.\n  bundled_title: பயன்பாட்டு கட்டமைப்பு சீலனுடன் தொகுக்கப்பட்டுள்ளது\n  confirm_delete: இந்த உள்ளமைவு/களை நிச்சயமாக நீக்க விரும்புகிறீர்களா?\n  confirm_delete_title: நீக்குவதை உறுதிப்படுத்தவும்\n  delete: அழி\n  export: ஏற்றுமதி\n  export_full: விண்ணப்பத்தின் மூலம் அமைப்புகளை ஏற்றுமதி செய்யுங்கள்\n  extra_info: >-\n    Seelen UI ஆனது ஒரு பயன்பாட்டிற்கு ஒரே ஒரு அடையாளங்காட்டியை மட்டுமே\n    பயன்படுத்துகிறது (முதல் பொருத்தம் கண்டறியப்பட்டது) எனவே எப்படி\n    குறிப்பிடப்படுகிறது என்பதற்கான வரிசை முக்கியமானது, சமீபத்திய சேர்க்கைக்கு\n    முன்னுரிமை அளிக்கப்படும், ஏனெனில் அட்டவணையானது சமீபத்தியது முதல் பழையது வரை\n    இயல்புநிலையாக வரிசைப்படுத்தப்படும்.\n  identifier:\n    add_block: தொகுதியைச் சேர்க்கவும்\n    and: மற்றும்\n    id: அடையாளங்காட்டி\n    kind: மூலம் அடையாளம் காணவும்\n    matching_strategy: பொருந்தும் உத்தி\n    matching_strategy_option:\n      contains: கொண்டுள்ளது\n      ends_with: உடன் முடிகிறது\n      equals: சமம்\n      regex: வழக்கமான வெளிப்பாடு\n      starts_with: உடன் தொடங்குகிறது\n    negation: பொருத்தத்தை மறுக்கவும்\n    or: அல்லது\n    remove: தடுப்பை நீக்கு\n    type:\n      class: வகுப்பு\n      exe: Exe\n      path: பாதை\n      title: தலைப்பு\n  import: இறக்குமதி\n  import_full: பயன்பாட்டின் மூலம் அமைப்புகளை இறக்குமதி செய்யுங்கள்\n  new: புதியது\n  search: தேடு\n  swap: இடமாற்று\ncancel: ரத்து செய்\nclose: மூடு\ndelete: அழி\ndevtools:\n  app_folders: பயன்பாட்டு கோப்புறைகள்\n  custom_config_file: தனிப்பயன் கட்டமைப்பு கோப்பை ஏற்றவும்\n  data_folder: தரவு கோப்புறை\n  enable: டெவலப்பர் கருவிகளை இயக்கு\n  install_folder: நிறுவல் கோப்புறை\n  load: ஏற்றவும்\n  settings_file: அமைப்புகள் கோப்பு\n  simulate_perm:\n    label: விட்ஜெட் அனுமதி கோரிக்கையை உருவகப்படுத்தவும்\n    result_allowed: ✓ அனுமதி வழங்கப்பட்டது\n    result_denied: ┇ அனுமதி மறுக்கப்பட்டது\n    trigger: உருவகப்படுத்து\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: கணினி ஐகான்கள் தற்காலிக சேமிப்பை அழிக்கவும்\n  clear_icons_tooltip: அனைத்து விட்ஜெட்களிலும் முழுமையாக நடைமுறைக்கு வர மறுதொடக்கம் தேவைப்படலாம்\n  exit: வெளியேறு/வெளியேறு\n  links: அதிகாரப்பூர்வ இணைப்புகள்\n  relaunch: மறுதொடக்கம்\n  version: பதிப்பு\n  version_fixed: >-\n    பயன்பாடு மற்றும் WebView2 இயக்க நேர பதிப்புகள் சரி செய்யப்பட்டுள்ளன. இதன்\n    பொருள், பயன்பாடு புதுப்பிப்புகளைப் பெறாது மற்றும் WebView2 இயக்க நேரம்\n    தானாகவே Windows புதுப்பிப்புகளுடன் புதுப்பிக்கப்படாது.\ngeneral:\n  accent_color: உச்சரிப்பு நிறம்\n  date_format: தேதி வடிவம்\n  date_format_how_to: தேதி வடிவமைப்பை எழுதுவது எப்படி?\n  hardware_acceleration: வன்பொருள் முடுக்கம்\n  hardware_acceleration_description: >-\n    வன்பொருள் முடுக்கத்தை முடக்குவது நினைவக பயன்பாட்டைக் குறைக்கும், ஆனால்\n    செயல்திறன் சிக்கல்களை ஏற்படுத்தும். நீங்கள் நேரடி வால்பேப்பர்களைப்\n    பயன்படுத்தாவிட்டால் முடக்குவது பாதுகாப்பானது.\n  icon_pack:\n    available: கிடைக்கும் ஐகான் பேக்குகள்\n    selected: செயலில் உள்ள ஐகான் பேக்குகள்\n  language: மொழி\n  monday: திங்கட்கிழமை\n  performance_mode:\n    on_battery: பேட்டரியில்\n    on_energy_saver: ஆற்றல் சேமிப்பாளரில்\n    options:\n      disabled: முடக்கப்பட்டது\n      extreme: எக்ஸ்ட்ரீம்\n      minimal: குறைந்தபட்ச\n    plugged: செருகப்பட்ட அல்லது சார்ஜ்\n  polling_interval: கணினி வாக்குப்பதிவு இடைவெளி\n  polling_interval_description: >-\n    சீலன் UI எவ்வளவு அடிக்கடி (வினாடிகளில்) CPU, RAM, நெட்வொர்க் மற்றும் வட்டு\n    செயல்பாடு போன்ற கணினி ஆதாரங்களைச் சரிபார்க்கிறது. ஒரு சிறிய எண் என்பது\n    அடிக்கடி புதுப்பித்தல்கள், ஆனால் சற்று அதிகமான வள பயன்பாடு.\n  saturday: சனிக்கிழமை\n  start_of_week: வாரத்தின் தொடக்கம்\n  startup: தொடக்கத்தில் இயக்கவா?\n  sunday: ஞாயிறு\n  theme:\n    available: கிடைக்கும் தீம்கள்\n    selected: செயலில் உள்ள தீம்கள்\nheader:\n  labels:\n    config: உள்ளமைவுகள்\n    developer: டெவலப்பர்களுக்கு\n    extras: கூடுதல்\n    general: பொது\n    home: வீடு\n    icon_pack_editor: தற்காலிக சேமிப்பு சின்னங்கள்\n    iconpack: ஐகான் பொதிகள்\n    monitors: கண்காணிப்பாளர்கள்\n    plugin: செருகுநிரல்கள்\n    resources: வளங்கள்\n    shortcuts: குறுக்குவழிகள்\n    soundpack: ஒலி பொதிகள்\n    specific_apps: பயன்பாட்டின் மூலம் அமைப்புகள்\n    theme: கருப்பொருள்கள்\n    virtual_desk: மெய்நிகர் டெஸ்க்டாப்புகள்\n    wallpaper: வால்பேப்பர்கள்\n    widget: விட்ஜெட்டுகள்\nhome:\n  new_resources: புதிய வளங்கள்\ninherit: மரபு\ninProgress: செயல்பாட்டில் உள்ளது...\ninsert: செருகவும்\nloading: ஏற்றுகிறது...\nmiscellaneous: இதர\nmonitors_configurations:\n  label: கண்காணிக்கவும் {{குறியீட்டு}}}\nmore: மேலும்\n'no': இல்லை\nopen: திற\nquit: விட்டுவிட\nremove: அகற்று\nreset_all_to_default: இயல்புநிலை மதிப்புகளுக்கு அனைத்தையும் மீட்டமைக்கவும்\nreset_to_default: இயல்புநிலை மதிப்புக்கு மீட்டமைக்கவும்\nresources:\n  app_outdated: இந்த வளத்திற்கு சரியாக வேலை செய்ய சீலன் UI இன் புதிய பதிப்பு தேவைப்படுகிறது.\n  corrupted_wallpaper: >-\n    சிறுபடத்தைப் பிரித்தெடுப்பதில் தோல்வி - சிதைந்த அல்லது ஆதரிக்கப்படாத வீடியோ\n    வடிவம்\n  delete: வளத்தை நீக்கு\n  discover: மேலும் ஆதாரங்களைக் கண்டறியவும்\n  has_update: புதுப்பிப்பு கிடைக்கிறது\n  high_impact: செயல்திறனில் அதிக தாக்கம்\n  import_wallpapers: உள்ளூர் வால்பேப்பர்களை இறக்குமதி செய்யுங்கள்\n  open_folder: திறந்த வள கோப்புறை\n  outdated: >-\n    இந்த ஆதாரம் சீலன் UI இன் பழைய பதிப்பிற்காக வடிவமைக்கப்பட்டுள்ளது, மேலும்\n    சரியாக வேலை செய்யாது.\n  see_on_website: இணையதளத்தில் பார்க்கவும்\nreview_request:\n  not_now: இப்போது இல்லை\n  sure: நிச்சயமாக!\n  title: சீலன் UIஐ அனுபவிக்கிறீர்களா?\nsave: சேமிக்கவும்\nsave_and_restart: சேமிக்கவும் மறுதொடக்கம் செய்யவும்\nsearch: தேடு\nsee_more: மேலும் காண்க\nshortcuts:\n  duplicate_error: இந்த குறுக்குவழி நகலெடுக்கப்பட்டது\n  enable: உள்ளமைக்கப்பட்ட குறுக்குவழிகள் அமைப்பை இயக்கவும்\n  enable_tooltip: >-\n    சீலன் UI கிளையண்டைப் பயன்படுத்தி உங்கள் சொந்த குறுக்குவழி அமைப்பை நீங்கள்\n    செயல்படுத்தினால் முடக்கு\n  labels:\n    create_new_workspace: புதிய பணியிடத்தை உருவாக்கவும்\n    cycle_stack_next: அடுத்த சுழற்சி அடுக்கு\n    cycle_stack_prev: முந்தைய சுழற்சி அடுக்கு\n    cycle_wallpaper_next: அடுத்த வால்பேப்பருக்கு மாற்றவும்\n    cycle_wallpaper_prev: முந்தைய வால்பேப்பருக்கு மாற்றவும்\n    decrease_height: உயரத்தைக் குறைக்கவும்\n    decrease_width: அகலத்தைக் குறைக்கவும்\n    destroy_current_workspace: தற்போதைய பணியிடத்தை அழிக்கவும்\n    focus_bottom: கீழே கவனம் செலுத்துங்கள்\n    focus_latest: சமீபத்திய கவனம்\n    focus_left: மீதமுள்ள கவனம்\n    focus_right: சரியாக கவனம் செலுத்துங்கள்\n    focus_top: மேலே கவனம் செலுத்துங்கள்\n    increase_height: உயரத்தை அதிகரிக்கவும்\n    increase_width: அகலத்தை அதிகரிக்கவும்\n    misc_force_quit: படை வெளியேறுதல்\n    misc_force_restart: படை மறுதொடக்கம்\n    misc_open_settings: திறந்த அமைப்புகள்\n    misc_toggle_lock_tracing: பூட்டு தடமறிதல் (பதிவுகள்)\n    misc_toggle_win_event_tracing: வெற்றி நிகழ்வு தடமறிதல் (பதிவுகள்)\n    move_to_workspace: பணியிடத்திற்கு நகர்த்தவும் {{0}}\n    move_window_down: சாளரத்தை கீழே நகர்த்தவும்\n    move_window_left: சாளரத்தை இடதுபுறமாக நகர்த்தவும்\n    move_window_right: சாளரத்தை வலதுபுறமாக நகர்த்தவும்\n    move_window_up: சாளரத்தை மேலே நகர்த்தவும்\n    pause_tiling: இடைநிறுத்த டைலிங் சாளர மேலாளர்\n    reserve_bottom: முன்பதிவு கீழே\n    reserve_float: ரிசர்வ் மிதவை\n    reserve_left: ரிசர்வ் இடது\n    reserve_right: சரியான உரிமை\n    reserve_stack: ரிசர்வ் ஸ்டேக்\n    reserve_top: ரிசர்வ் டாப்\n    restore_sizes: அளவுகளை மீட்டமை\n    send_to_workspace: பணியிடத்திற்கு அனுப்பவும் {{0}}\n    start_weg_app: பயன்பாட்டை கவனம் செலுத்துங்கள் அல்லது தொடங்கவும் {{0}}\n    switch_to_next_workspace: அடுத்த பணியிடத்திற்கு மாறவும்\n    switch_to_previous_workspace: முந்தைய பணியிடத்திற்கு மாறவும்\n    switch_workspace: பணியிடத்திற்கு மாறவும் {{0}}\n    toggle_float: சாளர மிதவை பயன்முறையை மாற்றவும்\n    toggle_monocle: பணியிட மோனோக்கிள் பயன்முறையை மாற்றவும்\n  readonly_tooltip: இது படிக்க மட்டும் குறுக்குவழி\n  reset: இயல்புநிலைகளுக்கு மீட்டமைக்கவும்\nsides:\n  bottom: கீழே\n  left: விட்டு\n  right: சரி\n  top: மேல்\ntoolbar:\n  auto_hide: ஆட்டோ மறை\n  delay_to_hide: மறைக்க தாமதம்\n  delay_to_show: காட்ட தாமதம்\n  dock_side: நிலை\n  enable: ஆடம்பரமான கருவிப்பட்டியை இயக்கவும்\n  hide_mode:\n    always: எப்போதும்\n    never: ஒருபோதும் இல்லை\n    on_overlap: ஒன்றுடன் ஒன்று\n  item_size: பொருளின் அளவு\n  label: கருவிப்பட்டி\n  margin: விளிம்பு அளவு\n  padding: திணிப்பு அளவு\n  placeholder: {}\nupdate:\n  available: புதுப்பிப்பு கிடைக்கிறது!\n  channel: சேனலைப் புதுப்பிக்கவும்\n  downloading: பதிவிறக்கம் ...\nwall:\n  backgrounds: வால்பேப்பர்கள்\n  blur: மங்கலானது\n  cancel: ரத்து செய்\n  close: மூடு\n  collection_name: சேகரிப்பு பெயர்\n  collections: தொகுப்புகள்\n  contrast: மாறுபாடு\n  corrupted_wallpapers_message: 'சிதைந்த வால்பேப்பர்கள், இவற்றை நீக்குவதைக் கவனியுங்கள்:'\n  create: உருவாக்கு\n  create_collection: சேகரிப்பை உருவாக்கவும்\n  default_collection: இயல்புநிலை சேகரிப்பு\n  delete_collection: சேகரிப்பை நீக்கு\n  edit_collection: தொகுப்பைத் திருத்து\n  enable: வால்பேப்பர் மேலாளரை இயக்கவும்\n  extend: முதன்மையை நீட்டவும்\n  fit:\n    contain: கட்டுப்படுத்தவும்\n    cover: கவர்\n    fill: நிரப்பவும்\n  flipHorizontal: கிடைமட்டமாக புரட்டவும்\n  flipVertical: செங்குத்து புரட்டவும்\n  generating_thumbnails: வால்பேப்பர் சிறு உருவங்களை உருவாக்குகிறது\n  hours: மணி\n  interval: ஒவ்வொரு வால்பேப்பரையும் மாற்றவும்\n  minutes: நிமிடங்கள்\n  monitor_collection: சேகரிப்பைக் கண்காணிக்கவும்\n  multimonitor_behaviour: மல்டிமோனிட்டர் நடத்தை\n  muted: வீடியோ ஆடியோவை முடக்கு\n  no_background: வெற்று ஸ்லைடுஷோ, அதற்கு பதிலாக கருப்பொருளின் பின்னணியைப் பயன்படுத்துகிறது.\n  no_collections: >-\n    இதுவரை தொகுப்புகள் எதுவும் உருவாக்கப்படவில்லை. ஒன்றை உருவாக்க + பொத்தானைக்\n    கிளிக் செய்யவும்.\n  objectFit: பின்னணி பொருத்தம்\n  objectPosition: பின்னணி நிலை\n  overlayColor: மேலடுக்கு நிறம்\n  overlayMixBlendMode: மேலடுக்கு கலவை கலவை பயன்முறை\n  per_monitor: ஒரு மானிட்டருக்கு\n  playback: பின்னணி வேகம்\n  position:\n    bottom: கீழே\n    center: மையம்\n    left: இடது\n    right: சரி\n    top: மேல்\n  processing_video: வீடியோவை செயலாக்குகிறது\n  random: ஸ்லைடுஷோவை சீரற்றதாக்குங்கள்\n  saturation: செறிவு\n  seconds: விநாடிகள்\n  select_collection: சேகரிப்பைத் தேர்ந்தெடுக்கவும்\n  thumbnail_generation_complete: சிறுபட உருவாக்கம் முடிந்தது\n  thumbnail_generation_finished: சிறுபட உருவாக்கம் வெற்றிகரமாக முடிந்தது\n  wallpaper_collection: வால்பேப்பர் சேகரிப்பு\n  wallpaper_settings: வால்பேப்பர் அமைப்புகள்\n  withOverlay: மேலடுக்கு\n  workspace_collections: பணியிட தொகுப்புகள்\nweg:\n  auto_hide: தானாக மறை\n  delay_to_hide: மறைக்க தாமதம்\n  delay_to_show: காட்ட தாமதம்\n  dock_side: நிலை\n  enable: டாக்/டாஸ்க்பாரை இயக்கவும்\n  filtering: பொருள் வடிகட்டுதல்\n  gap: இடைவெளி\n  hide_mode:\n    always: எப்போதும்\n    never: ஒருபோதும் இல்லை\n    on_overlap: ஒன்றுடன் ஒன்று\n  items:\n    gap: பொருட்களுக்கு இடையே உள்ள இடம்\n    label: பொருட்களை\n    pinned_visibility:\n      always: எப்போதும்\n      label: பின் செய்யப்பட்ட பொருட்களின் தெரிவுநிலை\n      when_primary: மானிட்டர் முதன்மையாக இருக்கும்போது\n    show_instance_counter: திறந்த விண்டோஸ் கவுண்டரைக் காட்டு\n    show_window_title: திறந்த சாளர தலைப்பைக் காட்டு (கிடைமட்டமாக மட்டுமே)\n    size: பொருளின் அளவு\n    split_windows: விண்டோஸைப் பிரிக்கவும் (ஒரு சாளரத்திற்கு ஒரு உருப்படி)\n    temporal_visibility:\n      all: அனைத்து\n      label: அன்பின் செய்யப்பட்ட உருப்படிகளின் தெரிவுநிலை\n      on_monitor: மானிட்டரில்\n    visible_separators: காணக்கூடிய பிரிப்பான்கள்\n  label: கப்பல்துறை/பணிப்பட்டி\n  margin: விளிம்பு\n  mode:\n    full_width: முழு திரை அகலம்\n    min_content: முடிந்தவரை சிறியது\n  padding: திணிப்பு\n  show_end_task: பணிப்பட்டியில் இறுதி பணியைக் காட்டு\n  width: அகலம்\nwelcome:\n  give_a_review: விமர்சனம் கொடுங்கள்\n  message: >-\n    சீலன் யுஐ என்பது விண்டோஸிற்கான இலவச மற்றும் திறந்த மூல டெஸ்க்டாப் சூழல்.\n    உங்கள் டெஸ்க்டாப் எவ்வாறு தோற்றமளிக்கிறது மற்றும் நடந்துகொள்கிறது என்பதில்\n    உங்களுக்கு முழுக் கட்டுப்பாடு உள்ளது, இது உங்கள் பணிப்பாய்வு மற்றும்\n    பாணியுடன் பொருந்தக்கூடிய ஒவ்வொரு விவரத்தையும் தனிப்பயனாக்க அனுமதிக்கிறது.\n  ok: ஆரம்பிப்போம்!\n  review: >-\n    நீங்கள் Seelen UIஐப் பயன்படுத்துவதை விரும்புகிறீர்கள் என்றால், ஸ்டோரில் ஒரு\n    மதிப்பாய்வைத் தரவும் - உங்கள் கருத்து திட்டம் வளரவும் மேம்படுத்தவும்\n    உதவுகிறது.\n  title: Seelen UIக்கு வரவேற்கிறோம்!\nwidget:\n  enable: இந்த விட்ஜெட்டை இயக்கவும்\n  enable_for_monitor: இந்த மானிட்டரில் இயக்கவும்\n  instances: நிகழ்வுகள்\nwm:\n  animations:\n    duration: அனிமேஷன் காலம் (எம்.எஸ்)\n    ease_function: அனிமேஷன் எளிதாக்கும் செயல்பாடு\n    enable: சாளரத்தின் அனிமேஷன்களை இயக்கவும்\n  author: நூலாசிரியர்\n  border:\n    enable: சாளரத்தின் எல்லையை இயக்கு\n    offset: பார்டர் ஆஃப்செட்\n    width: பார்டர் அகலம்\n  description: விளக்கம்\n  drag_behavior: இழுத்தல் நடத்தை\n  drag_behavior_options:\n    sort: வரிசைப்படுத்தவும் (இழுக்கும்போது சாளரங்களை மறுவரிசைப்படுத்தவும்)\n    swap: இடமாற்று (இழுக்கும் முனையில் சாளரங்களை மாற்றவும்)\n  enable: டைலிங் சாளர மேலாளரை இயக்கவும்\n  layout: தளவமைப்பு\n  resize_delta: டெல்டாவின் அளவை மாற்று (%)\n  space_between_containers: கொள்கலன்களுக்கு இடையில் இடைவெளி\n  workspace_offset: பணியிடங்கள் ஆஃப்செட் (விளிம்புகள்)\n  workspace_padding: பணியிடங்கள் திணிப்பு\n'yes': ஆம்\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/te.yml",
    "content": "action:\n  confirm: మీకు ఖచ్చితంగా తెలుసా?\n  confirm_body: ఈ చర్యను రద్దు చేయలేము.\napps_configurations:\n  app:\n    bindings: బైండింగ్ (రెండు ఎంపికలు అవసరమని గమనించండి)\n    category: వర్గం\n    category_placeholder: ఏదీ లేదు\n    monitor: మానిటర్\n    monitor_placeholder: ఏదీ లేదు\n    name: పేరు\n    ok_create: సృష్టించు\n    ok_edit: నవీకరించు\n    ok_readonly: కొత్తదిగా సవరించండి\n    options:\n      NoInteractive: ఇంటరాక్టివ్ లేదు\n      VdPinned: అన్ని వర్క్‌స్పేస్‌లలో చూపించు\n      WmFloat: Twm - ఫ్లోటింగ్ ప్రారంభించండి\n      WmForce: Twm - బలవంతంగా నిర్వహించండి\n      WmUnmanage: Twm - నిర్వహించవద్దు\n    options_label: అదనపు ఎంపికలు\n    title_create: '{{name}}ని సృష్టిస్తోంది'\n    title_edit: సవరణ {{పేరు}}\n    title_readonly: '{{name}} వీక్షిస్తోంది'\n    weg_options_label: డాక్/టాస్క్‌బార్ ఎంపికలు\n    wm_options_label: విండో మేనేజర్ ఎంపికలు\n    workspace: కార్యస్థలం\n    workspace_placeholder: ఏదీ లేదు\n  bundled_msg: >-\n    ఈ బండిల్ కాన్ఫిగరేషన్‌లు సవరించదగినవి కావు మరియు అనుకూలీకరణ లేకుండా మీకు\n    ఉత్తమ అనుభవాన్ని అందించడానికి రూపొందించబడ్డాయి. వారు మీ కోసం అత్యంత సాధారణ\n    అప్లికేషన్‌లను స్వయంచాలకంగా కాన్ఫిగర్ చేస్తారు.\n  bundled_title: యాప్ కాన్ఫిగర్ సీలెన్‌తో బండిల్ చేయబడింది\n  confirm_delete: మీరు ఈ కాన్ఫిగరేషన్/లని ఖచ్చితంగా తొలగించాలనుకుంటున్నారా?\n  confirm_delete_title: తొలగించడాన్ని నిర్ధారించండి\n  delete: తొలగించు\n  export: ఎగుమతి చేయండి\n  export_full: అప్లికేషన్ ద్వారా సెట్టింగులు ఎగుమతి\n  extra_info: >-\n    సీలెన్ UI ఒక అనువర్తనానికి ఒక ఐడెంటిఫైయర్‌ను మాత్రమే ఉపయోగిస్తుంది (మొదటి\n    మ్యాచ్ కనుగొనబడింది) కాబట్టి ఎలా పేర్కొనబడింది అనే క్రమం ముఖ్యమైనది, తాజా\n    జోడించిన వాటికి ప్రాధాన్యత ఇవ్వబడుతుంది, ఎందుకంటే పట్టిక డిఫాల్ట్‌గా తాజా\n    నుండి పాత వరకు క్రమబద్ధీకరించబడుతుంది.\n  identifier:\n    add_block: బ్లాక్ జోడించండి\n    and: మరియు\n    id: ఐడెంటిఫైయర్\n    kind: ద్వారా గుర్తించండి\n    matching_strategy: మ్యాచింగ్ స్ట్రాటజీ\n    matching_strategy_option:\n      contains: కలిగి ఉంది\n      ends_with: తో ముగుస్తుంది\n      equals: సమానం\n      regex: రెగ్యులర్ వ్యక్తీకరణ\n      starts_with: తో మొదలవుతుంది\n    negation: సరిపోలికను తిరస్కరించండి\n    or: లేదా\n    remove: బ్లాక్‌ని తొలగించండి\n    type:\n      class: తరగతి\n      exe: Exe\n      path: మార్గం\n      title: శీర్షిక\n  import: దిగుమతి\n  import_full: అప్లికేషన్ ద్వారా సెట్టింగ్‌లను దిగుమతి చేయండి\n  new: కొత్తది\n  search: వెతకండి\n  swap: మార్పిడి\ncancel: రద్దు చేయండి\nclose: దగ్గరగా\ndelete: తొలగించు\ndevtools:\n  app_folders: యాప్ ఫోల్డర్‌లు\n  custom_config_file: కస్టమ్ కాన్ఫిగర్ ఫైల్‌ను లోడ్ చేయండి\n  data_folder: డేటా ఫోల్డర్\n  enable: డెవలపర్ సాధనాలను ప్రారంభించండి\n  install_folder: ఇన్‌స్టాలేషన్ ఫోల్డర్\n  load: లోడ్ చేయండి\n  settings_file: సెట్టింగుల ఫైల్\n  simulate_perm:\n    label: విడ్జెట్ అనుమతి అభ్యర్థనను అనుకరించండి\n    result_allowed: ✓ అనుమతి మంజూరు చేయబడింది\n    result_denied: ✗ అనుమతి నిరాకరించబడింది\n    trigger: అనుకరించు\n    widget_id_placeholder: '@రచయిత/విడ్జెట్-పేరు'\nextras:\n  clear_icons: క్లియర్ సిస్టమ్ చిహ్నాలు కాష్\n  clear_icons_tooltip: అన్ని విడ్జెట్లపై పూర్తిగా అమలులోకి రావడానికి పున art ప్రారంభం అవసరం\n  exit: నిష్క్రమించు/నిష్క్రమించు\n  links: అధికారిక లింకులు\n  relaunch: పునఃప్రారంభించండి\n  version: 'సంస్కరణ: Telugu'\n  version_fixed: >-\n    అప్లికేషన్ మరియు WebView2 రన్‌టైమ్ వెర్షన్‌లు పరిష్కరించబడ్డాయి. దీని అర్థం\n    అప్లికేషన్ నవీకరణలను స్వీకరించదు మరియు Windows నవీకరణలతో WebView2 రన్‌టైమ్\n    స్వయంచాలకంగా నవీకరించబడదు.\ngeneral:\n  accent_color: యాస రంగు\n  date_format: తేదీ ఆకృతి\n  date_format_how_to: తేదీ ఆకృతిని ఎలా వ్రాయాలి?\n  hardware_acceleration: హార్డ్‌వేర్ త్వరణం\n  hardware_acceleration_description: >-\n    హార్డ్‌వేర్ త్వరణాన్ని నిలిపివేయడం మెమరీ వినియోగాన్ని తగ్గిస్తుంది, కానీ\n    పనితీరు సమస్యలను కలిగిస్తుంది. మీరు లైవ్ వాల్‌పేపర్‌లను ఉపయోగించకపోతే\n    డిజేబుల్ చేయడం సురక్షితం.\n  icon_pack:\n    available: అందుబాటులో ఉన్న ఐకాన్ ప్యాక్‌లు\n    selected: సక్రియ ఐకాన్ ప్యాక్‌లు\n  language: భాష\n  monday: సోమవారం\n  performance_mode:\n    on_battery: బ్యాటరీపై\n    on_energy_saver: ఎనర్జీ సేవర్‌పై\n    options:\n      disabled: నిలిపివేయబడింది\n      extreme: ఎక్స్‌ట్రీమ్\n      minimal: కనిష్ట\n    plugged: ప్లగ్డ్ లేదా ఛార్జింగ్\n  polling_interval: సిస్టమ్ పోలింగ్ విరామం\n  polling_interval_description: >-\n    సీలెన్ UI ఎంత తరచుగా (సెకన్లలో) CPU, RAM, నెట్‌వర్క్ మరియు డిస్క్ కార్యాచరణ\n    వంటి సిస్టమ్ వనరులను తనిఖీ చేస్తుంది. చిన్న సంఖ్య అంటే మరింత తరచుగా\n    అప్‌డేట్‌లు, కానీ కొంచెం ఎక్కువ వనరుల వినియోగం.\n  saturday: శనివారం\n  start_of_week: వారం ప్రారంభం\n  startup: స్టార్టప్‌లో అమలు చేయాలా?\n  sunday: ఆదివారం\n  theme:\n    available: అందుబాటులో ఉన్న థీమ్‌లు\n    selected: క్రియాశీల థీమ్‌లు\nheader:\n  labels:\n    config: ఆకృతీకరణలు\n    developer: డెవలపర్‌ల కోసం\n    extras: ఎక్స్‌ట్రాలు\n    general: జనరల్\n    home: హోమ్\n    icon_pack_editor: కాష్ చేసిన చిహ్నాలు\n    iconpack: ఐకాన్ ప్యాక్‌లు\n    monitors: మానిటర్లు\n    plugin: ప్లగిన్లు\n    resources: వనరులు\n    shortcuts: సత్వరమార్గాలు\n    soundpack: సౌండ్ ప్యాక్‌లు\n    specific_apps: అప్లికేషన్ ద్వారా సెట్టింగులు\n    theme: థీమ్స్\n    virtual_desk: వర్చువల్ డెస్క్‌టాప్‌లు\n    wallpaper: వాల్‌పేపర్లు\n    widget: విడ్జెట్లు\nhome:\n  new_resources: కొత్త వనరులు\ninherit: వారసత్వంగా\ninProgress: పురోగతిలో ఉంది...\ninsert: చొప్పించండి\nloading: లోడ్...\nmiscellaneous: ఇతరాలు\nmonitors_configurations:\n  label: మానిటర్ {{సూచిక}}\nmore: మరిన్ని\n'no': లేదు\nopen: తెరవండి\nquit: నిష్క్రమించు\nremove: తొలగించండి\nreset_all_to_default: అన్ని డిఫాల్ట్ విలువలకు రీసెట్ చేయండి\nreset_to_default: డిఫాల్ట్ విలువకు రీసెట్ చేయండి\nresources:\n  app_outdated: ఈ వనరు సరిగ్గా పనిచేయడానికి సీలెన్ UI యొక్క క్రొత్త వెర్షన్ అవసరం.\n  corrupted_wallpaper: >-\n    సూక్ష్మచిత్రాన్ని సంగ్రహించడంలో విఫలమైంది - పాడైన లేదా మద్దతు లేని వీడియో\n    ఫార్మాట్\n  delete: వనరును తొలగించండి\n  discover: మరిన్ని వనరులను కనుగొనండి\n  has_update: నవీకరణ అందుబాటులో ఉంది\n  high_impact: పనితీరుపై అధిక ప్రభావం\n  import_wallpapers: స్థానిక వాల్‌పేపర్‌లను దిగుమతి చేయండి\n  open_folder: ఓపెన్ రిసోర్స్ ఫోల్డర్\n  outdated: >-\n    ఈ వనరు సీలెన్ UI యొక్క పాత వెర్షన్ కోసం రూపొందించబడింది మరియు సరిగ్గా\n    పనిచేయకపోవచ్చు.\n  see_on_website: వెబ్‌సైట్‌లో చూడండి\nreview_request:\n  not_now: ఇప్పుడు కాదు\n  sure: తప్పకుండా!\n  title: సీలెన్ UIని ఆస్వాదిస్తున్నారా?\nsave: సేవ్ చేయండి\nsave_and_restart: సేవ్ & పున art ప్రారంభించండి\nsearch: శోధించండి\nsee_more: మరిన్ని చూడండి\nshortcuts:\n  duplicate_error: ఈ సత్వరమార్గం నకిలీ చేయబడింది\n  enable: అంతర్నిర్మిత సత్వరమార్గాల వ్యవస్థను ప్రారంభించండి\n  enable_tooltip: >-\n    మీరు సీలెన్ UI క్లయింట్ ఉపయోగించి మీ స్వంత సత్వరమార్గాల వ్యవస్థను అమలు\n    చేస్తే నిలిపివేయండి\n  labels:\n    create_new_workspace: క్రొత్త వర్క్‌స్పేస్‌ను సృష్టించండి\n    cycle_stack_next: తరువాత సైకిల్ స్టాక్\n    cycle_stack_prev: సైకిల్ స్టాక్ మునుపటి\n    cycle_wallpaper_next: తదుపరి వాల్‌పేపర్‌కు మార్చండి\n    cycle_wallpaper_prev: మునుపటి వాల్‌పేపర్‌కు మార్చండి\n    decrease_height: ఎత్తు తగ్గుతుంది\n    decrease_width: వెడల్పు తగ్గుతుంది\n    destroy_current_workspace: ప్రస్తుత వర్క్‌స్పేస్‌ను నాశనం చేయండి\n    focus_bottom: దిగువ ఫోకస్\n    focus_latest: తాజా ఫోకస్\n    focus_left: ఫోకస్ ఎడమ\n    focus_right: సరిగ్గా దృష్టి పెట్టండి\n    focus_top: ఫోకస్ టాప్\n    increase_height: ఎత్తును పెంచండి\n    increase_width: వెడల్పును పెంచండి\n    misc_force_quit: ఫోర్స్ క్విట్\n    misc_force_restart: ఫోర్స్ పున art ప్రారంభం\n    misc_open_settings: సెట్టింగులు తెరవండి\n    misc_toggle_lock_tracing: లాక్ ట్రేసింగ్ (లాగ్‌లు) టోగుల్ చేయండి\n    misc_toggle_win_event_tracing: విన్ ఈవెంట్ ట్రేసింగ్ (లాగ్స్) ను టోగుల్ చేయండి\n    move_to_workspace: వర్క్‌స్పేస్‌కు తరలించండి {{0}}\n    move_window_down: విండోను దిగువకు తరలించండి\n    move_window_left: విండోను ఎడమ వైపుకు తరలించండి\n    move_window_right: విండోను కుడి వైపుకు తరలించండి\n    move_window_up: విండోను పైకి తరలించండి\n    pause_tiling: టైలింగ్ విండో మేనేజర్ పాజ్\n    reserve_bottom: దిగువ రిజర్వ్\n    reserve_float: రిజర్వ్ ఫ్లోట్\n    reserve_left: రిజర్వ్ ఎడమ\n    reserve_right: కుడి రిజర్వ్\n    reserve_stack: రిజర్వ్ స్టాక్\n    reserve_top: రిజర్వ్ టాప్\n    restore_sizes: పరిమాణాలను పునరుద్ధరించండి\n    send_to_workspace: వర్క్‌స్పేస్‌కు పంపండి {{0}}\n    start_weg_app: ఫోకస్ చేయండి లేదా అప్లికేషన్ ప్రారంభించండి {{0}}\n    switch_to_next_workspace: తదుపరి వర్క్‌స్పేస్‌కు మారండి\n    switch_to_previous_workspace: మునుపటి వర్క్‌స్పేస్‌కు మారండి\n    switch_workspace: వర్క్‌స్పేస్‌కు మారండి {{0}}\n    toggle_float: విండో ఫ్లోట్ మోడ్‌ను టోగుల్ చేయండి\n    toggle_monocle: వర్క్‌స్పేస్ మోనోకిల్ మోడ్‌ను టోగుల్ చేయండి\n  readonly_tooltip: ఇది చదవడానికి మాత్రమే సత్వరమార్గం\n  reset: డిఫాల్ట్‌లకు రీసెట్ చేయండి\nsides:\n  bottom: దిగువన\n  left: ఎడమ\n  right: కుడి\n  top: టాప్\ntoolbar:\n  auto_hide: ఆటో దాచు\n  delay_to_hide: దాచడానికి ఆలస్యం\n  delay_to_show: చూపించడానికి ఆలస్యం\n  dock_side: స్థానం\n  enable: ఫ్యాన్సీ టూల్‌బార్‌ని ప్రారంభించండి\n  hide_mode:\n    always: ఎల్లప్పుడూ\n    never: ఎప్పుడూ\n    on_overlap: అతివ్యాప్తిపై\n  item_size: అంశం పరిమాణం\n  label: టూల్‌బార్\n  margin: మార్జిన్ పరిమాణం\n  padding: పాడింగ్ పరిమాణం\n  placeholder: {}\nupdate:\n  available: నవీకరణ అందుబాటులో ఉంది!\n  channel: ఛానెల్‌ను నవీకరించండి\n  downloading: డౌన్‌లోడ్ ...\nwall:\n  backgrounds: వాల్‌పేపర్లు\n  blur: బ్లర్\n  cancel: రద్దు చేయి\n  close: మూసివేయి\n  collection_name: సేకరణ పేరు\n  collections: సేకరణలు\n  contrast: దీనికి విరుద్ధంగా\n  corrupted_wallpapers_message: 'పాడైన వాల్‌పేపర్‌లు, వీటిని తొలగించడాన్ని పరిగణించండి:'\n  create: సృష్టించు\n  create_collection: సేకరణను సృష్టించండి\n  default_collection: డిఫాల్ట్ సేకరణ\n  delete_collection: సేకరణను తొలగించండి\n  edit_collection: సేకరణను సవరించండి\n  enable: వాల్పేపర్ మేనేజర్‌ను ప్రారంభించండి\n  extend: ప్రాథమికంగా విస్తరించండి\n  fit:\n    contain: కలిగి\n    cover: కవర్\n    fill: పూరించండి\n  flipHorizontal: క్షితిజ సమాంతరంగా తిప్పండి\n  flipVertical: ఫ్లిప్ నిలువు\n  generating_thumbnails: వాల్‌పేపర్ థంబ్‌నెయిల్‌లను రూపొందిస్తోంది\n  hours: గంటలు\n  interval: ప్రతి వాల్‌పేపర్‌ను మార్చండి\n  minutes: నిమిషాలు\n  monitor_collection: సేకరణను పర్యవేక్షించండి\n  multimonitor_behaviour: మల్టీమోనిటర్ బిహేవియర్\n  muted: వీడియో ఆడియోను మ్యూట్ చేయండి\n  no_background: ఖాళీ స్లైడ్‌షో, బదులుగా థీమ్ యొక్క నేపథ్యాన్ని ఉపయోగించి.\n  no_collections: >-\n    ఇంకా సేకరణలు ఏవీ సృష్టించబడలేదు. ఒకదాన్ని సృష్టించడానికి + బటన్‌ను క్లిక్\n    చేయండి.\n  objectFit: నేపథ్య సరిపోతుంది\n  objectPosition: నేపథ్య స్థానం\n  overlayColor: అతివ్యాప్తి రంగు\n  overlayMixBlendMode: అతివ్యాప్తి మిక్స్ బ్లెండ్ మోడ్\n  per_monitor: ప్రతి మానిటర్\n  playback: ప్లేబ్యాక్ వేగం\n  position:\n    bottom: దిగువ\n    center: కేంద్రం\n    left: ఎడమ\n    right: కుడి\n    top: టాప్\n  processing_video: వీడియోను ప్రాసెస్ చేస్తోంది\n  random: రాండమైజ్ స్లైడ్‌షో\n  saturation: సంతృప్తత\n  seconds: సెకన్లు\n  select_collection: సేకరణను ఎంచుకోండి\n  thumbnail_generation_complete: థంబ్‌నెయిల్ జనరేషన్ పూర్తయింది\n  thumbnail_generation_finished: థంబ్‌నెయిల్ ఉత్పత్తి విజయవంతంగా ముగిసింది\n  wallpaper_collection: వాల్‌పేపర్ కలెక్షన్\n  wallpaper_settings: వాల్‌పేపర్ సెట్టింగ్‌లు\n  withOverlay: అతివ్యాప్తితో\n  workspace_collections: కార్యస్థల సేకరణలు\nweg:\n  auto_hide: ఆటో దాచు\n  delay_to_hide: దాచడానికి ఆలస్యం\n  delay_to_show: చూపించడానికి ఆలస్యం\n  dock_side: స్థానం\n  enable: డాక్/టాస్క్‌బార్‌ని ప్రారంభించండి\n  filtering: అంశం వడపోత\n  gap: గ్యాప్\n  hide_mode:\n    always: ఎల్లప్పుడూ\n    never: ఎప్పుడూ\n    on_overlap: అతివ్యాప్తిపై\n  items:\n    gap: వస్తువుల మధ్య ఖాళీ\n    label: వస్తువులు\n    pinned_visibility:\n      always: ఎల్లప్పుడూ\n      label: పిన్ చేయబడిన అంశాల దృశ్యమానత\n      when_primary: మానిటర్ ప్రాథమికంగా ఉన్నప్పుడు\n    show_instance_counter: ఓపెన్ విండోస్ కౌంటర్ చూపించు\n    show_window_title: ఓపెన్ విండో శీర్షికను చూపించు (క్షితిజ సమాంతర మాత్రమే)\n    size: అంశం పరిమాణం\n    split_windows: స్ప్లిట్ విండోస్ (ఒక విండోకు ఒక అంశం)\n    temporal_visibility:\n      all: అన్నీ\n      label: అన్‌పిన్ చేయబడిన అంశాల దృశ్యమానత\n      on_monitor: మానిటర్‌లో\n    visible_separators: కనిపించే సెపరేటర్లు\n  label: డాక్/టాస్క్‌బార్\n  margin: మార్జిన్\n  mode:\n    full_width: పూర్తి స్క్రీన్ వెడల్పు\n    min_content: వీలైనంత చిన్నది\n  padding: పాడింగ్\n  show_end_task: టాస్క్‌బార్‌లో ముగింపు పనిని చూపించు\n  width: వెడల్పు\nwelcome:\n  give_a_review: రివ్యూ ఇవ్వండి\n  message: >-\n    సీలెన్ UI అనేది Windows కోసం ఉచిత మరియు ఓపెన్ సోర్స్ డెస్క్‌టాప్ వాతావరణం.\n    ఇక్కడ మీ డెస్క్‌టాప్ ఎలా కనిపిస్తుంది మరియు ప్రవర్తిస్తుంది అనే దానిపై మీకు\n    పూర్తి నియంత్రణ ఉంటుంది, ఇది మీ వర్క్‌ఫ్లో మరియు స్టైల్‌కు సరిపోయేలా ప్రతి\n    వివరాలను అనుకూలీకరించడానికి మిమ్మల్ని అనుమతిస్తుంది.\n  ok: ప్రారంభిద్దాం!\n  review: >-\n    మీరు సీలెన్ UIని ఉపయోగించడం ఆనందించినట్లయితే, స్టోర్‌లో సమీక్షను\n    అందించడాన్ని పరిగణించండి - మీ అభిప్రాయం ప్రాజెక్ట్ అభివృద్ధి చెందడానికి\n    మరియు మెరుగుపరచడంలో సహాయపడుతుంది.\n  title: Seelen UIకి స్వాగతం!\nwidget:\n  enable: ఈ విడ్జెట్‌ను ప్రారంభించండి\n  enable_for_monitor: ఈ మానిటర్‌లో ప్రారంభించండి\n  instances: ఉదాహరణలు\nwm:\n  animations:\n    duration: యానిమేషన్ వ్యవధి (ఎంఎస్)\n    ease_function: యానిమేషన్ సడలింపు ఫంక్షన్\n    enable: విండో యొక్క యానిమేషన్లను ప్రారంభించండి\n  author: రచయిత\n  border:\n    enable: విండో అంచుని ప్రారంభించండి\n    offset: సరిహద్దు ఆఫ్‌సెట్\n    width: అంచు వెడల్పు\n  description: వివరణ\n  drag_behavior: డ్రాగ్ బిహేవియర్\n  drag_behavior_options:\n    sort: క్రమబద్ధీకరించు (డ్రాగ్ చేస్తున్నప్పుడు విండోలను క్రమాన్ని మార్చండి)\n    swap: స్వాప్ (డ్రాగ్ ఎండ్‌లో విండోలను స్వాప్ చేయండి)\n  enable: టైలింగ్ విండో మేనేజర్‌ను ప్రారంభించండి\n  layout: లేఅవుట్\n  resize_delta: డెల్టా (%) పరిమాణాన్ని మార్చండి\n  space_between_containers: కంటైనర్ల మధ్య ఖాళీ\n  workspace_offset: వర్క్‌స్పేస్ ఆఫ్‌సెట్ (మార్జిన్‌లు)\n  workspace_padding: కార్యస్థలాల పాడింగ్\n'yes': అవును\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/tg.yml",
    "content": "action:\n  confirm: Шумо мутмаъин ҳастед?\n  confirm_body: Ин амал бекор карда намешавад.\napps_configurations:\n  app:\n    bindings: Бодед\n    category: Категория\n    category_placeholder: Ҳеь\n    monitor: Назорат\n    monitor_placeholder: Ҳеь\n    name: Ном\n    ok_create: Сохтан\n    ok_edit: Навсозӣ\n    ok_readonly: Ҳамчун нав\n    options:\n      NoInteractive: Ягон интерактивӣ нест\n      VdPinned: Дар ҳама ҷойҳои корӣ нишон диҳед\n      WmFloat: Дугоник - Оғози шино\n      WmForce: Twr - Идоракунии қувва\n      WmUnmanage: Twrm - Noweragage\n    options_label: Имконоти иловагӣ\n    title_create: Эҷоди {{НОМИ}}\n    title_edit: Таҳрир {{НОМ}}\n    title_readonly: Дидани {{НОМ}}\n    weg_options_label: Параметрҳои Dock / Pitibar\n    wm_options_label: Имконоти мудири равзана\n    workspace: Корӣ\n    workspace_placeholder: Ҳеь\n  bundled_msg: >-\n    Ин конфигуратсияи маҷмӯӣ таҳрир карда намешавад ва барои таъмини таҷрибаи\n    беҳтарин бидуни фармоишӣ пешбинӣ шудаанд. Онҳо ба таври худкор барномаҳои\n    маъмултарини шуморо барои шумо танзим мекунанд.\n  bundled_title: App Confile бо seelen\n  confirm_delete: Шумо мутмаин ҳастед, ки мехоҳед ин конфигуратсия / S-ро нест кунед?\n  confirm_delete_title: Тасдиқро тасдиқ кунед\n  delete: Нест\n  export: Содирот\n  export_full: Танзимоти содиротиро тавассути ариза\n  extra_info: >-\n    Seelen UI дар як барнома танҳо як идентификаторро истифода мебарад (мувофиқи\n    аввал пайдо шуд), аз ин рӯ тартиби муайян кардани он муҳим аст, ба охирин\n    иловашуда авлавият дода мешавад, зеро қайд кунед, ки ҷадвал аз рӯи нобаёнӣ\n    аз охирин то кӯҳна мураттаб карда мешавад.\n  identifier:\n    add_block: Блокро илова кунед\n    and: Ва\n    id: Муайянкунанда\n    kind: Муайян мекунад\n    matching_strategy: Стратегияи мувофиқ\n    matching_strategy_option:\n      contains: Дар бар мегирад\n      ends_with: Бо\n      equals: Баробар\n      regex: Ифодаи муқаррарӣ\n      starts_with: Бо оғоз меёбад\n    negation: Мувофиқати манфӣ\n    or: Ё\n    remove: Нест кардани блок\n    type:\n      class: Синф\n      exe: Парвандаи Иҷрокунанда\n      path: Роҳ\n      title: Унвон\n  import: Воридот\n  import_full: Танзимоти воридот тавассути ариза\n  new: Нав\n  search: Кофтуков\n  swap: Своп\ncancel: Манъ кардан\nclose: Наздик\ndelete: Нест\ndevtools:\n  app_folders: Папкаҳои барнома\n  custom_config_file: Файли танзимоти танзими фармоишӣ\n  data_folder: Папкаи додаҳо\n  enable: Воситаҳои таҳиягарро фаъол кунед\n  install_folder: Феҳристи насб\n  load: Сарборӣ\n  settings_file: Файлҳои танзимот\n  simulate_perm:\n    label: Дархости иҷозати виджетро тақлид кунед\n    result_allowed: ✓ Иҷозат дода шудааст\n    result_denied: ✗ Иҷозат рад карда шуд\n    trigger: Тақлид кардан\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Нишондиҳандаи системаи тозагӣ\n  clear_icons_tooltip: Бозоғоз кардан мумкин аст, ки ба ҳама виджет пурра таъсир расонад\n  exit: Курат / баромадан\n  links: Истеҳсоли расмӣ\n  relaunch: Радкунӣ\n  version: Версия\n  version_fixed: >-\n    Версияҳои барнома ва WebView2 Runtime собит шудаанд. Ин маънои онро дорад,\n    ки барнома навсозиҳоро қабул намекунад ва WebView2 Runtime бо навсозиҳои\n    Windows ба таври худкор нав карда намешавад.\ngeneral:\n  accent_color: Ранги ҳисоб\n  date_format: Шакли сана\n  date_format_how_to: Формати санаро чӣ гуна бояд нависед?\n  hardware_acceleration: Шитоби сахтафзор\n  hardware_acceleration_description: >-\n    Хомӯш кардани суръатбахшии сахтафзор истифодаи хотираро коҳиш медиҳад, аммо\n    метавонад боиси мушкилоти иҷроиш гардад. Агар шумо обои зиндаро истифода\n    набаред, хомӯш кардан бехатар аст.\n  icon_pack:\n    available: Бастаҳои нишонаи дастрас\n    selected: Бастаҳои нишонаи фаъол\n  language: Забон\n  monday: Душанбе\n  performance_mode:\n    on_battery: Аз рӯи батарея\n    on_energy_saver: Оид ба сарфкунандаи энергия\n    options:\n      disabled: Маъюб\n      extreme: Шадид\n      minimal: Ҳадди аққал\n    plugged: Васл кардан ё пур кардани барқ\n  polling_interval: Фосилаи овоздиҳии система\n  polling_interval_description: >-\n    Чанд маротиба (дар сонияҳо) Seelen UI захираҳои системаро ба монанди CPU,\n    RAM, шабака ва фаъолияти диск тафтиш мекунад. Шумораи камтар маънои навсозии\n    зуд-зуд дорад, аммо истифодаи каме бештари захираҳо.\n  saturday: шанбе\n  start_of_week: Оғози ҳафта\n  startup: Оғози кор?\n  sunday: Якшанбе\n  theme:\n    available: Мавзӯъҳои дастрас\n    selected: Мавзӯъҳои фаъол\nheader:\n  labels:\n    config: Конфигуратсия\n    developer: Барои таҳиягарон\n    extras: Иловаҳо\n    general: Генерал\n    home: Хона\n    icon_pack_editor: Нишони кэш\n    iconpack: ICon бастаҳои\n    monitors: Мониторҳо\n    plugin: Плагинҳо\n    resources: Захираҳо\n    shortcuts: Миёнбурҳо\n    soundpack: Пойафзолҳои садо\n    specific_apps: Танзимот тавассути барнома\n    theme: Мавзӯъҳо\n    virtual_desk: Мизи кории виртуалӣ\n    wallpaper: Обои обу\n    widget: Виджетҳо\nhome:\n  new_resources: Захираҳои нав\ninherit: Мерос гирифтан\ninProgress: Дар ҷараён...\ninsert: Илова кардан\nloading: Боркунӣ ...\nmiscellaneous: Дигар\nmonitors_configurations:\n  label: Монитор {Индекс}}\nmore: Бештар\n'no': Нест\nopen: Кушодан\nquit: Баромадан\nremove: Дур кардан\nreset_all_to_default: Ҳама ба арзишҳои пешфарз\nreset_to_default: Аз нав танзимкунии арзиши пешфарз\nresources:\n  app_outdated: Ин манбаъ як версияи нави seelen-ро талаб мекунад, то дуруст кор кунам.\n  corrupted_wallpaper: Истихроҷи ангораи видео - формати вайроншуда ё дастгирӣнашаванда\n  delete: Манбаъро нест мекунад\n  discover: Нахуст захираҳои бештарро кашф кунед\n  has_update: Навсозӣ дастрас аст\n  high_impact: Таъсири баланд ба иҷроиш\n  import_wallpapers: Объекти маҳаллӣ\n  open_folder: Папкаи захираҳо\n  outdated: >-\n    Ин манбаъ барои нусхаи кӯҳна аз YELEINS тарҳрезӣ шудааст ва метавонад дуруст\n    кор кунад.\n  see_on_website: Дар вебсайт нигаред\nreview_request:\n  not_now: Ҳозир не\n  sure: Албатта!\n  title: Аз Seelen UI лаззат мебаред?\nsave: Пул захира кардан\nsave_and_restart: Наҷот ва бозоғоз\nsearch: Ҷустуҷӯ\nsee_more: Бештар нигаред\nshortcuts:\n  duplicate_error: Ин миёнабур такрор карда шудааст\n  enable: Системаи миёнабурҳои обиро фаъол созед\n  enable_tooltip: >-\n    Агар шумо системаи миёнабурҳои худро бо истифода аз мизоҷи Yoelen истифода\n    баред\n  labels:\n    create_new_workspace: Эҷоди фазои нав\n    cycle_stack_next: Стрипка дар паҳлӯи он\n    cycle_stack_prev: Стритинг қаблан\n    cycle_wallpaper_next: Тағирот ба обои нав\n    cycle_wallpaper_prev: Тағирот ба обои қаблӣ\n    decrease_height: Баландиро кам мекунад\n    decrease_width: Пойафзол коҳиш диҳед\n    destroy_current_workspace: Ҷоизаҳои ҷориро нест кунед\n    focus_bottom: Фаъолахш\n    focus_latest: Тамаркуз охирин\n    focus_left: Тамаркуз ба чап\n    focus_right: Фокус ба\n    focus_top: Фокус ба боло\n    increase_height: Баланд бардоштани баландӣ\n    increase_width: Паҳнои васеъ\n    misc_force_quit: Қувваи корӣ\n    misc_force_restart: Бастори қувват\n    misc_open_settings: Танзимоти кушода\n    misc_toggle_lock_tracing: Todge Cound Tracking (гузоришҳо)\n    misc_toggle_win_event_tracing: Гузариш ба ғолиб Tweacing (гузоришҳо)\n    move_to_workspace: Ба кории {{0}} гузаред\n    move_window_down: Тирезаро ба поён ҳаракат кунед\n    move_window_left: Тиреза ба чап\n    move_window_right: Тирезаро ба рост ҳаракат кунед\n    move_window_up: Тиреза ба боло\n    pause_tiling: Менеҷери тирезаи Temple\n    reserve_bottom: Поёни захиравӣ\n    reserve_float: Шино мекунанд\n    reserve_left: Захира\n    reserve_right: Зарф аз тарафи\n    reserve_stack: Стекер захира\n    reserve_top: Болои боло\n    restore_sizes: Барқарор кардан\n    send_to_workspace: Ба кории rowspace {{0}}\n    start_weg_app: Тамаркуз ё оғози дархост {{0}}\n    switch_to_next_workspace: Гузариш ба фазои оянда\n    switch_to_previous_workspace: Ба фазои пешина гузаред\n    switch_workspace: Гузаштан ба роҳҳои корӣ {{0}}\n    toggle_float: Ҳолати шинокунандаи шино\n    toggle_monocle: Реҷаи монокзори роҳ\n  readonly_tooltip: Ин як миёнабурҳои ягона аст\n  reset: Аз нав танзимкунии пешфарз\nsides:\n  bottom: Тагпо\n  left: Бесоҳибмонда\n  right: Рост\n  top: Боло\ntoolbar:\n  auto_hide: Худкор Пинҳон кардани\n  delay_to_hide: Таъхир барои пинҳон кардан\n  delay_to_show: Таъхир барои нишон додан\n  dock_side: Вазъият\n  enable: Дастрас кардани панели футболи\n  hide_mode:\n    always: Ҳамеша\n    never: Ҳеҷ гоҳ\n    on_overlap: Дар такрори\n  item_size: Андозаи ашё\n  label: Асбобу\n  margin: Андозаи маржа\n  padding: Андозаи пуркунӣ\n  placeholder: {}\nupdate:\n  available: Навсозӣ!\n  channel: Навсозӣ канал\n  downloading: Зеркашӣ кунед ...\nwall:\n  backgrounds: Обои обу\n  blur: Гул\n  cancel: Бекор кардан\n  close: Пӯшед\n  collection_name: Номи коллексия\n  collections: Маҷмӯаҳо\n  contrast: Зиддият\n  corrupted_wallpapers_message: 'Обои вайроншуда, тоза кардани инҳоро баррасӣ кунед:'\n  create: Эҷод кунед\n  create_collection: Маҷмӯа эҷод кунед\n  default_collection: Коллексияи пешфарз\n  delete_collection: Маҷмӯаро нест кунед\n  edit_collection: Маҷмӯаро таҳрир кунед\n  enable: Фаъолсозии менеҷери обои\n  extend: Давом додани ибтидоӣ\n  fit:\n    contain: Нигоҳубин кардан\n    cover: Пӯшидан\n    fill: Пур кардан\n  flipHorizontal: Flip уфуқ\n  flipVertical: Флип амудӣ\n  generating_thumbnails: Эҷоди ангораи обои\n  hours: соат\n  interval: Тағйироти ҳар як\n  minutes: дақиқа\n  monitor_collection: Маҷмӯаи монитор\n  multimonitor_behaviour: Рафтори мултимониторӣ\n  muted: Аудити видеоӣ\n  no_background: Слайдҳои холӣ, бо истифодаи заминаи мавзӯъҳо.\n  no_collections: >-\n    Ҳанӯз коллексия эҷод нашудааст. Барои сохтани як тугма тугмаи + -ро клик\n    кунед.\n  objectFit: Замина\n  objectPosition: Мавқеи заминавӣ\n  overlayColor: Ранги пурраи\n  overlayMixBlendMode: Реҷаи омехтаи пурраи омехта\n  per_monitor: Ба як монитор\n  playback: Суръати бозӣ\n  position:\n    bottom: Тагпо\n    center: Марказ\n    left: Бесоҳибмонда\n    right: Рост\n    top: Боло\n  processing_video: Коркарди видео\n  random: Велосипедро тасодуфан\n  saturation: Соф\n  seconds: сонияҳо\n  select_collection: Коллексияро интихоб кунед\n  thumbnail_generation_complete: Насли тасвири ангора анҷом ёфт\n  thumbnail_generation_finished: Насли эскиз бомуваффақият анҷом ёфт\n  wallpaper_collection: Маҷмӯаи обои\n  wallpaper_settings: Танзимоти обои\n  withOverlay: Бо overlay\n  workspace_collections: Коллексияҳои фазои корӣ\nweg:\n  auto_hide: Худкор Пинҳон кардани\n  delay_to_hide: Таъхир барои пинҳон кардан\n  delay_to_show: Таъхир барои нишон додан\n  dock_side: Вазъият\n  enable: Dock / Travelar\n  filtering: Фортинги ашё\n  gap: Фосила\n  hide_mode:\n    always: Ҳамеша\n    never: Ҳеҷ гоҳ\n    on_overlap: Дар такрори\n  items:\n    gap: Фосила байни ашё\n    label: Ашё\n    pinned_visibility:\n      always: Ҳамеша\n      label: Намоиши ашёҳои маҳкамшуда\n      when_primary: Вақте ки монитор ибтидоӣ аст\n    show_instance_counter: Counter Counter Counter\n    show_window_title: Сарлавҳаи тирезаро нишон диҳед (танҳо уфуқӣ)\n    size: Андозаи ашё\n    split_windows: Windows-ро тақсим кунед (як адад дар як тиреза)\n    temporal_visibility:\n      all: Ҳама\n      label: Намоиши ҷузъҳои ҷудонашуда\n      on_monitor: Дар Монитор\n    visible_separators: Бандиаткунандагони намоён\n  label: Dock / супоришҳо\n  margin: Маржа\n  mode:\n    full_width: Паҳнои пурраи экран\n    min_content: То ҳадди имкон хурд\n  padding: Тахтача\n  show_end_task: Вазифаи хотимаро дар панели супоришҳо нишон диҳед\n  width: Васеъ\nwelcome:\n  give_a_review: Барраси кунед\n  message: >-\n    Seelen UI як муҳити мизи кории ройгон ва кушодаасос барои Windows мебошад.\n    Дар ин ҷо шумо назорати пурраи намуди мизи кории шумо доред ва ба шумо имкон\n    медиҳад, ки ҳар як ҷузъиётро мувофиқи ҷараёни кор ва услуби худ танзим\n    кунед.\n  ok: Биёед оғоз кунем!\n  review: >-\n    Агар шумо аз истифодаи Seelen UI лаззат мебаред, фикр кунед, ки дар мағоза\n    шарҳ гузоред - фикру мулоҳизаҳои шумо ба рушд ва такмил додани лоиҳа кӯмак\n    мекунанд.\n  title: Хуш омадед ба Seelen UI!\nwidget:\n  enable: Ин виджетро фаъол кунед\n  enable_for_monitor: Дар ин монитор фаъол кунед\n  instances: Ҳолатҳо\nwm:\n  animations:\n    duration: Давомнокии аниматсия (MS)\n    ease_function: Имтиёзҳои осон\n    enable: Фаъолсозии аниматсияҳои тирезаро фаъол кунед\n  author: Муаллиф\n  border:\n    enable: Сарҳади тирезаро фаъол созед\n    offset: Ҳафтаи сарҳад\n    width: Паҳнои сарҳад\n  description: Тасвирӣ\n  drag_behavior: Рафтори кашолакунӣ\n  drag_behavior_options:\n    sort: Мураттаб кардан (аз нав тартиби тирезаҳо ҳангоми кашолакунӣ)\n    swap: Своп (иваз кардани тирезаҳо дар охири кашолакунӣ)\n  enable: Фаъолсозии мудири тиреза\n  layout: Тарҳ\n  resize_delta: Татбиқи Delta (%)\n  space_between_containers: Фосила байни контейнер\n  workspace_offset: Ҷойгиршавӣ\n  workspace_padding: Worlpaces Padding\n'yes': Ҳа\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/th.yml",
    "content": "action:\n  confirm: คุณแน่ใจหรือไม่?\n  confirm_body: การกระทำนี้ไม่สามารถยกเลิกได้\napps_configurations:\n  app:\n    bindings: การเชื่อม (หมายเหตุทั้งสองตัวเลือกเป็นสิ่งจำเป็น)\n    category: หมวดหมู่\n    category_placeholder: ไม่มี\n    monitor: เฝ้าสังเกต\n    monitor_placeholder: ไม่มี\n    name: ชื่อ\n    ok_create: สร้าง\n    ok_edit: อัปเดต\n    ok_readonly: แก้ไขเป็นใหม่\n    options:\n      NoInteractive: ไม่มีการโต้ตอบ\n      VdPinned: แสดงในพื้นที่ทำงานทั้งหมด\n      WmFloat: Twm - เริ่มลอยตัว\n      WmForce: Twm - บังคับจัดการ\n      WmUnmanage: Twm - ยกเลิกการจัดการ\n    options_label: ตัวเลือกพิเศษ\n    title_create: การสร้าง {{ชื่อ}}\n    title_edit: การแก้ไข {{ชื่อ}}\n    title_readonly: การดู {{ชื่อ}}\n    weg_options_label: ตัวเลือกท่าเทียบเรือ/บาร์\n    wm_options_label: ตัวเลือกตัวจัดการหน้าต่าง\n    workspace: พื้นที่ทำงาน\n    workspace_placeholder: ไม่มี\n  bundled_msg: >-\n    การกำหนดค่าแบบรวมเหล่านี้ไม่สามารถแก้ไขได้และได้รับการออกแบบมาเพื่อให้คุณได้รับประสบการณ์ที่ดีที่สุดโดยไม่ต้องปรับแต่ง\n    พวกเขากำหนดค่าแอปพลิเคชันที่พบบ่อยที่สุดสำหรับคุณโดยอัตโนมัติ\n  bundled_title: แอพกำหนดค่ามาพร้อมกับ Seelen\n  confirm_delete: แน่ใจหรือว่าต้องการลบการกำหนดค่านี้/s?\n  confirm_delete_title: ยืนยันการลบ\n  delete: ลบ\n  export: ส่งออก\n  export_full: การตั้งค่าการส่งออกโดยแอปพลิเคชัน\n  extra_info: >-\n    SEELEN UI ใช้ตัวระบุเพียงหนึ่งตัวต่อแอป (พบนัดแรก)\n    ดังนั้นคำสั่งซื้อเฉพาะนั้นมีความสำคัญการเพิ่มล่าสุดจะได้รับการจัดลำดับความสำคัญเนื่องจากหมายเหตุตารางจะถูกเรียงลำดับโดยค่าเริ่มต้นจากล่าสุดถึงเก่า\n  identifier:\n    add_block: เพิ่มบล็อก\n    and: และ\n    id: ตัวระบุ\n    kind: ระบุโดย\n    matching_strategy: กลยุทธ์การจับคู่\n    matching_strategy_option:\n      contains: ประกอบด้วย\n      ends_with: ปิดท้ายด้วย\n      equals: เท่ากับ\n      regex: การแสดงออกปกติ\n      starts_with: เริ่มต้นด้วย\n    negation: คัดค้านการจับคู่\n    or: หรือ\n    remove: ลบบล็อก\n    type:\n      class: ระดับ\n      exe: เอ็กซ์\n      path: เส้นทาง\n      title: ชื่อ\n  import: นำเข้า\n  import_full: นำเข้าการตั้งค่าโดยแอปพลิเคชัน\n  new: ใหม่\n  search: ค้นหา\n  swap: แลกเปลี่ยน\ncancel: ยกเลิก\nclose: ปิด\ndelete: ลบ\ndevtools:\n  app_folders: โฟลเดอร์แอพ\n  custom_config_file: โหลดไฟล์กำหนดค่าที่กำหนดเอง\n  data_folder: โฟลเดอร์ข้อมูล\n  enable: เปิดใช้งานเครื่องมือนักพัฒนาซอฟต์แวร์\n  install_folder: โฟลเดอร์การติดตั้ง\n  load: โหลด\n  settings_file: ไฟล์การตั้งค่า\n  simulate_perm:\n    label: จำลองคำขออนุญาตวิดเจ็ต\n    result_allowed: ✓ ได้รับอนุญาตแล้ว\n    result_denied: ✗ การอนุญาตถูกปฏิเสธ\n    trigger: จำลอง\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: ล้างไอคอนระบบแคช\n  clear_icons_tooltip: การรีสตาร์ทอาจจำเป็นต้องมีผลอย่างเต็มที่กับวิดเจ็ตทั้งหมด\n  exit: ออก/ออก\n  links: ลิงค์อย่างเป็นทางการ\n  relaunch: เปิดใหม่\n  version: รุ่น\n  version_fixed: >-\n    เวอร์ชันของแอปพลิเคชันและ WebView2 Runtime ได้รับการแก้ไขแล้ว\n    ซึ่งหมายความว่าแอปพลิเคชันจะไม่ได้รับการอัพเดตและ WebView2 Runtime\n    จะไม่ได้รับการอัปเดตโดยอัตโนมัติด้วยการอัปเดต Windows\ngeneral:\n  accent_color: สีเน้น\n  date_format: รูปแบบวันที่\n  date_format_how_to: จะเขียนรูปแบบวันที่ได้อย่างไร?\n  hardware_acceleration: การเร่งความเร็วด้วยฮาร์ดแวร์\n  hardware_acceleration_description: >-\n    การปิดใช้งานการเร่งด้วยฮาร์ดแวร์จะลดการใช้หน่วยความจำ\n    แต่อาจทำให้เกิดปัญหาด้านประสิทธิภาพได้\n    ปลอดภัยในการปิดใช้งานหากคุณจะไม่ใช้วอลเปเปอร์เคลื่อนไหว\n  icon_pack:\n    available: ชุดไอคอนที่มีอยู่\n    selected: ชุดไอคอนที่ใช้งานอยู่\n  language: ภาษา\n  monday: วันจันทร์\n  performance_mode:\n    on_battery: บนแบตเตอรี่\n    on_energy_saver: ประหยัดพลังงาน\n    options:\n      disabled: พิการ\n      extreme: สุดขีด\n      minimal: น้อยที่สุด\n    plugged: เสียบหรือชาร์จ\n  polling_interval: ช่วงเวลาการสำรวจระบบ\n  polling_interval_description: >-\n    Seelen UI ตรวจสอบทรัพยากรระบบ เช่น CPU, RAM, เครือข่าย\n    และกิจกรรมดิสก์บ่อยแค่ไหน (เป็นวินาที)\n    จำนวนที่น้อยกว่าหมายถึงการอัปเดตบ่อยขึ้น แต่การใช้ทรัพยากรสูงขึ้นเล็กน้อย\n  saturday: วันเสาร์\n  start_of_week: เริ่มต้นสัปดาห์\n  startup: วิ่งเมื่อเริ่มต้น?\n  sunday: วันอาทิตย์\n  theme:\n    available: ธีมที่มีจำหน่าย\n    selected: ธีมที่ใช้งานอยู่\nheader:\n  labels:\n    config: การกำหนดค่า\n    developer: สำหรับนักพัฒนา\n    extras: ความพิเศษ\n    general: ทั่วไป\n    home: บ้าน\n    icon_pack_editor: ไอคอนแคช\n    iconpack: ไอคอนแพ็ค\n    monitors: จอภาพ\n    plugin: ปลั๊กอิน\n    resources: ทรัพยากร\n    shortcuts: ทางลัด\n    soundpack: ชุดเสียง\n    specific_apps: การตั้งค่าโดยแอปพลิเคชัน\n    theme: ธีม\n    virtual_desk: เดสก์ท็อปเสมือนจริง\n    wallpaper: วอลเปเปอร์\n    widget: วิดเจ็ต\nhome:\n  new_resources: ทรัพยากรใหม่\ninherit: สืบทอด\ninProgress: กำลังดำเนินการ...\ninsert: แทรก\nloading: กำลังโหลด ...\nmiscellaneous: เบ็ดเตล็ด\nmonitors_configurations:\n  label: ตรวจสอบ {{index}}\nmore: มากกว่า\n'no': เลขที่\nopen: เปิด\nquit: ล้มเลิก\nremove: ลบ\nreset_all_to_default: รีเซ็ตเป็นค่าเริ่มต้นทั้งหมด\nreset_to_default: รีเซ็ตเป็นค่าเริ่มต้น\nresources:\n  app_outdated: ทรัพยากรนี้ต้องการ Seelen UI รุ่นใหม่กว่าเพื่อทำงานอย่างถูกต้อง\n  corrupted_wallpaper: ไม่สามารถแยกภาพขนาดย่อได้ - รูปแบบวิดีโอเสียหายหรือไม่รองรับ\n  delete: ลบทรัพยากร\n  discover: ค้นพบทรัพยากรเพิ่มเติม\n  has_update: มีการอัปเดต\n  high_impact: มีผลกระทบสูงต่อประสิทธิภาพ\n  import_wallpapers: นำเข้าวอลเปเปอร์ในท้องถิ่น\n  open_folder: เปิดโฟลเดอร์ทรัพยากร\n  outdated: ทรัพยากรนี้ได้รับการออกแบบมาสำหรับ Seelen UI รุ่นเก่าและอาจทำงานไม่ถูกต้อง\n  see_on_website: ดูบนเว็บไซต์\nreview_request:\n  not_now: ไม่ใช่ตอนนี้\n  sure: แน่นอน!\n  title: เพลิดเพลินกับ Seelen UI หรือไม่?\nsave: บันทึก\nsave_and_restart: บันทึกและรีสตาร์ท\nsearch: ค้นหา\nsee_more: ดูเพิ่มเติม\nshortcuts:\n  duplicate_error: ทางลัดนี้ซ้ำกัน\n  enable: เปิดใช้งานระบบทางลัดในตัว\n  enable_tooltip: ปิดการใช้งานหากคุณจะใช้ระบบทางลัดของคุณเองโดยใช้ไคลเอนต์ SEELEN UI\n  labels:\n    create_new_workspace: สร้างพื้นที่ทำงานใหม่\n    cycle_stack_next: วงจรสแต็คถัดไป\n    cycle_stack_prev: Cycle Stack ก่อนหน้านี้\n    cycle_wallpaper_next: เปลี่ยนเป็นวอลล์เปเปอร์ถัดไป\n    cycle_wallpaper_prev: เปลี่ยนเป็นวอลล์เปเปอร์ก่อนหน้า\n    decrease_height: ลดความสูง\n    decrease_width: ลดความกว้าง\n    destroy_current_workspace: ทำลายพื้นที่ทำงานปัจจุบัน\n    focus_bottom: โฟกัสด้านล่าง\n    focus_latest: โฟกัสล่าสุด\n    focus_left: โฟกัสซ้าย\n    focus_right: โฟกัสขวา\n    focus_top: โฟกัสด้านบน\n    increase_height: เพิ่มความสูง\n    increase_width: เพิ่มความกว้าง\n    misc_force_quit: บังคับให้เลิก\n    misc_force_restart: บังคับให้รีสตาร์ท\n    misc_open_settings: เปิดการตั้งค่า\n    misc_toggle_lock_tracing: สลับการติดตามล็อค (บันทึก)\n    misc_toggle_win_event_tracing: สลับการติดตามเหตุการณ์ (บันทึก)\n    move_to_workspace: ย้ายไปที่ WorksPace {{0}}\n    move_window_down: ย้ายหน้าต่างไปด้านล่าง\n    move_window_left: ย้ายหน้าต่างไปทางซ้าย\n    move_window_right: ย้ายหน้าต่างไปทางขวา\n    move_window_up: ย้ายหน้าต่างไปด้านบน\n    pause_tiling: หยุดชั่วคราวตัวจัดการหน้าต่างปูกระเบื้อง\n    reserve_bottom: สำรองด้านล่าง\n    reserve_float: สำรองลอย\n    reserve_left: สำรองซ้าย\n    reserve_right: สำรองสิทธิ\n    reserve_stack: กองสำรอง\n    reserve_top: สำรองด้านบน\n    restore_sizes: คืนค่าขนาด\n    send_to_workspace: ส่งไปยัง WorksPace {{0}}\n    start_weg_app: โฟกัสหรือเริ่มแอปพลิเคชัน {{0}}\n    switch_to_next_workspace: เปลี่ยนเป็นพื้นที่ทำงานถัดไป\n    switch_to_previous_workspace: เปลี่ยนไปใช้พื้นที่ทำงานก่อนหน้า\n    switch_workspace: สลับไปที่ WorksPace {{0}}\n    toggle_float: สลับโหมดลอยหน้าต่าง\n    toggle_monocle: สลับโหมดเวิร์กสเปซ Monocle\n  readonly_tooltip: นี่คือทางลัดแบบอ่านอย่างเดียว\n  reset: รีเซ็ตเป็นค่าเริ่มต้น\nsides:\n  bottom: ด้านล่าง\n  left: ซ้าย\n  right: ขวา\n  top: สูงสุด\ntoolbar:\n  auto_hide: ซ่อนอัตโนมัติ\n  delay_to_hide: ล่าช้าในการซ่อน\n  delay_to_show: ล่าช้าในการแสดง\n  dock_side: ตำแหน่ง\n  enable: เปิดใช้งานแถบเครื่องมือแฟนซี\n  hide_mode:\n    always: เสมอ\n    never: ไม่เคย\n    on_overlap: เมื่อทับซ้อนกัน\n  item_size: ขนาดรายการ\n  label: แถบเครื่องมือ\n  margin: ขนาดระยะขอบ\n  padding: ขนาดบุนวม\n  placeholder: {}\nupdate:\n  available: อัปเดตพร้อมใช้งาน!\n  channel: อัปเดตช่อง\n  downloading: ดาวน์โหลด ...\nwall:\n  backgrounds: วอลเปเปอร์\n  blur: เบลอ\n  cancel: ยกเลิก\n  close: ปิด\n  collection_name: ชื่อคอลเลกชัน\n  collections: คอลเลกชัน\n  contrast: ตัดกัน\n  corrupted_wallpapers_message: 'วอลเปเปอร์ที่เสียหาย โปรดพิจารณาลบสิ่งเหล่านี้:'\n  create: สร้าง\n  create_collection: สร้างคอลเลกชัน\n  default_collection: คอลเลกชันเริ่มต้น\n  delete_collection: ลบคอลเลกชั่น\n  edit_collection: แก้ไขคอลเลกชัน\n  enable: เปิดใช้งานผู้จัดการวอลล์เปเปอร์\n  extend: ขยายหลัก\n  fit:\n    contain: บรรจุ\n    cover: ปิดบัง\n    fill: เติม\n  flipHorizontal: พลิกแนวนอน\n  flipVertical: พลิกแนวตั้ง\n  generating_thumbnails: กำลังสร้างรูปขนาดย่อของวอลเปเปอร์\n  hours: ชั่วโมง\n  interval: เปลี่ยนวอลล์เปเปอร์ทุก ๆ\n  minutes: นาที\n  monitor_collection: ตรวจสอบคอลเลกชัน\n  multimonitor_behaviour: พฤติกรรมของจอภาพหลายจอ\n  muted: ปิดเสียงวิดีโอ\n  no_background: สไลด์โชว์เปล่าโดยใช้พื้นหลังของธีมแทน\n  no_collections: ยังไม่มีการสร้างคอลเลกชัน คลิกปุ่ม + เพื่อสร้าง\n  objectFit: พื้นหลังพอดี\n  objectPosition: ตำแหน่งพื้นหลัง\n  overlayColor: สีซ้อนทับ\n  overlayMixBlendMode: โหมดผสมมิกซ์ซ้อนทับ\n  per_monitor: ต่อจอภาพ\n  playback: ความเร็วในการเล่น\n  position:\n    bottom: ด้านล่าง\n    center: ศูนย์\n    left: ซ้าย\n    right: ขวา\n    top: สูงสุด\n  processing_video: กำลังประมวลผลวิดีโอ\n  random: สุ่มสไลด์โชว์\n  saturation: ความอิ่มตัว\n  seconds: ไม่กี่วินาที\n  select_collection: เลือกคอลเลกชัน\n  thumbnail_generation_complete: การสร้างภาพขนาดย่อเสร็จสมบูรณ์\n  thumbnail_generation_finished: การสร้างภาพขนาดย่อเสร็จสิ้นเรียบร้อยแล้ว\n  wallpaper_collection: คอลเลกชันวอลล์เปเปอร์\n  wallpaper_settings: การตั้งค่าวอลเปเปอร์\n  withOverlay: ด้วยการซ้อนทับ\n  workspace_collections: คอลเลกชันพื้นที่ทำงาน\nweg:\n  auto_hide: ซ่อนอัตโนมัติ\n  delay_to_hide: ล่าช้าในการซ่อน\n  delay_to_show: ล่าช้าในการแสดง\n  dock_side: ตำแหน่ง\n  enable: เปิดใช้งาน Dock/Task -Babar\n  filtering: การกรองรายการ\n  gap: ช่องว่าง\n  hide_mode:\n    always: เสมอ\n    never: ไม่เคย\n    on_overlap: เมื่อทับซ้อนกัน\n  items:\n    gap: ช่องว่างระหว่างรายการ\n    label: รายการ\n    pinned_visibility:\n      always: เสมอ\n      label: การมองเห็นรายการที่ปักหมุด\n      when_primary: เมื่อมอนิเตอร์เป็นเครื่องหลัก\n    show_instance_counter: แสดงตัวนับ Windows Open\n    show_window_title: แสดงชื่อเปิดหน้าต่าง (แนวนอนเท่านั้น)\n    size: ขนาดรายการ\n    split_windows: แยก Windows (หนึ่งรายการต่อหน้าต่าง)\n    temporal_visibility:\n      all: ทั้งหมด\n      label: การมองเห็นรายการที่เลิกปักหมุด\n      on_monitor: บนจอภาพ\n    visible_separators: ตัวแยกที่มองเห็นได้\n  label: แท่นวาง/บาร์\n  margin: ระยะขอบ\n  mode:\n    full_width: ความกว้างเต็มหน้าจอ\n    min_content: เล็กเท่าที่จะทำได้\n  padding: การขยายความ\n  show_end_task: แสดงงานสิ้นสุดในแถบงาน\n  width: ความกว้าง\nwelcome:\n  give_a_review: ให้รีวิว\n  message: >-\n    Seelen UI เป็นสภาพแวดล้อมเดสก์ท็อปแบบโอเพ่นซอร์สฟรีสำหรับ Windows ที่นี่\n    คุณสามารถควบคุมลักษณะและลักษณะการทำงานของเดสก์ท็อปของคุณได้อย่างเต็มที่\n    ช่วยให้คุณสามารถปรับแต่งทุกรายละเอียดให้ตรงกับขั้นตอนการทำงานและสไตล์ของคุณ\n  ok: เริ่มกันเลย!\n  review: >-\n    หากคุณสนุกกับการใช้ UI ของ Seelen ลองเขียนรีวิวไว้ที่ร้านค้า\n    ความคิดเห็นของคุณจะช่วยให้โปรเจ็กต์เติบโตและปรับปรุงได้\n  title: ยินดีต้อนรับสู่ Seelen UI!\nwidget:\n  enable: เปิดใช้งานวิดเจ็ตนี้\n  enable_for_monitor: เปิดใช้งานบนจอภาพนี้\n  instances: อินสแตนซ์\nwm:\n  animations:\n    duration: ระยะเวลาการเคลื่อนไหว (MS)\n    ease_function: ฟังก์ชั่นการผ่อนคลายภาพเคลื่อนไหว\n    enable: เปิดใช้งานภาพเคลื่อนไหวของหน้าต่าง\n  author: ผู้เขียน\n  border:\n    enable: เปิดใช้งานเส้นขอบของหน้าต่าง\n    offset: ชดเชยพรมแดน\n    width: ความกว้างชายแดน\n  description: คำอธิบาย\n  drag_behavior: พฤติกรรมการลาก\n  drag_behavior_options:\n    sort: จัดเรียง (เรียงลำดับหน้าต่างใหม่ขณะลาก)\n    swap: สลับ (สลับหน้าต่างที่ปลายลาก)\n  enable: เปิดใช้งานตัวจัดการหน้าต่างปูกระเบื้อง\n  layout: เค้าโครง\n  resize_delta: ปรับขนาดเดลต้า (%)\n  space_between_containers: ช่องว่างระหว่างภาชนะบรรจุ\n  workspace_offset: พื้นที่ทำงานออฟเซ็ต (ระยะขอบ)\n  workspace_padding: ช่องว่างพื้นที่ทำงาน\n'yes': ใช่\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/tl.yml",
    "content": "action:\n  confirm: Sigurado ka ba?\n  confirm_body: Ang pagkilos na ito ay hindi maaaring magawa.\napps_configurations:\n  app:\n    bindings: Ang pagbubuklod (tandaan ang parehong mga pagpipilian ay kinakailangan)\n    category: Kategorya\n    category_placeholder: Wala\n    monitor: Subaybayan\n    monitor_placeholder: Wala\n    name: Pangalan\n    ok_create: Lumikha\n    ok_edit: I -update\n    ok_readonly: I -edit bilang bago\n    options:\n      NoInteractive: Walang interactive\n      VdPinned: Ipakita sa lahat ng mga lugar ng trabaho\n      WmFloat: TWM - Simulan ang lumulutang\n      WmForce: TWM - Pamahalaan ang Pamahalaan\n      WmUnmanage: TWM - hindi namamahala\n    options_label: Dagdag na mga pagpipilian\n    title_create: Lumilikha ng {{pangalan}}\n    title_edit: Pag -edit {{pangalan}}\n    title_readonly: Pagtingin {{pangalan}}\n    weg_options_label: Mga pagpipilian sa pantalan/taskbar\n    wm_options_label: Mga pagpipilian sa Window Manager\n    workspace: Workspace\n    workspace_placeholder: Wala\n  bundled_msg: >-\n    Ang mga naka -bundle na mga pagsasaayos ay hindi mai -edit at idinisenyo\n    upang mabigyan ka ng pinakamahusay na karanasan nang walang pagpapasadya.\n    Awtomatiko nilang i -configure ang pinaka -karaniwang mga aplikasyon para sa\n    iyo.\n  bundled_title: Ang config ng app na naka -bundle kay Seelen\n  confirm_delete: Sigurado ka bang nais mong tanggalin ang pagsasaayos na ito/s?\n  confirm_delete_title: Kumpirmahin ang Tanggalin\n  delete: Tanggalin\n  export: I -export\n  export_full: Mga setting ng pag -export sa pamamagitan ng aplikasyon\n  extra_info: >-\n    Ang Seelen UI ay gumagamit lamang ng isang identifier bawat app (unang tugma\n    na natagpuan) kaya ang pagkakasunud -sunod sa kung paano ang tinukoy ay\n    mahalaga, ang pinakabagong idinagdag ay unahin, dahil ang tala ang\n    talahanayan ay pinagsunod -sunod ng default mula sa pinakabagong hanggang sa\n    luma.\n  identifier:\n    add_block: Magdagdag ng block\n    and: At\n    id: Identifier\n    kind: Kilalanin ng\n    matching_strategy: Diskarte sa pagtutugma\n    matching_strategy_option:\n      contains: Naglalaman\n      ends_with: Nagtatapos sa\n      equals: katumbas\n      regex: Regular na pagpapahayag\n      starts_with: Nagsisimula sa\n    negation: Negate pagtutugma\n    or: O\n    remove: Tanggalin ang bloke\n    type:\n      class: Klase\n      exe: Exe\n      path: Daan\n      title: Pamagat\n  import: Angkat\n  import_full: Mga setting ng pag -import sa pamamagitan ng application\n  new: Bago\n  search: Maghanap\n  swap: Ipagpalit\ncancel: Kanselahin\nclose: Malapit\ndelete: Tanggalin\ndevtools:\n  app_folders: Mga folder ng app\n  custom_config_file: I -load ang pasadyang config file\n  data_folder: Folder ng data\n  enable: Paganahin ang mga tool ng developer\n  install_folder: Folder ng pag -install\n  load: Mag -load\n  settings_file: File ng mga setting\n  simulate_perm:\n    label: Gayahin ang Kahilingan ng Pahintulot sa Widget\n    result_allowed: ✓ Ipinagkaloob ang pahintulot\n    result_denied: ✗ Tinanggihan ang pahintulot\n    trigger: Gayahin\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: I -clear ang Mga Icon ng System Cache\n  clear_icons_tooltip: >-\n    Ang isang pag -restart ay maaaring kailanganin upang ganap na maisakatuparan\n    ang lahat ng mga widget\n  exit: Huminto/exit\n  links: Opisyal na mga link\n  relaunch: Muling pagsasaayos\n  version: Bersyon\n  version_fixed: >-\n    Ang mga bersyon ng application at WebView2 Runtime ay naayos na.\n    Nangangahulugan ito na ang application ay hindi makakatanggap ng mga update\n    at ang WebView2 Runtime ay hindi awtomatikong maa-update sa Windows update.\ngeneral:\n  accent_color: Kulay ng accent\n  date_format: Format ng petsa\n  date_format_how_to: Paano magsulat ng format ng petsa?\n  hardware_acceleration: Pagpapabilis ng Hardware\n  hardware_acceleration_description: >-\n    Ang hindi pagpapagana ng hardware acceleration ay makakabawas sa paggamit ng\n    memory, ngunit maaaring magdulot ng mga isyu sa performance. Ligtas na hindi\n    paganahin kung hindi ka gagamit ng mga live na wallpaper.\n  icon_pack:\n    available: Magagamit na Mga Icon Pack\n    selected: Mga Aktibong Icon Pack\n  language: Wika\n  monday: Lunes\n  performance_mode:\n    on_battery: Sa baterya\n    on_energy_saver: Sa enerhiya saver\n    options:\n      disabled: Hindi pinagana\n      extreme: Matinding\n      minimal: Minimal\n    plugged: Naka -plug o singilin\n  polling_interval: Agwat ng botohan ng system\n  polling_interval_description: >-\n    Gaano kadalas (sa mga segundo) sinusuri ng Seelen UI ang mga mapagkukunan ng\n    system tulad ng CPU, RAM, network, at aktibidad ng disk. Ang mas maliit na\n    bilang ay nangangahulugan ng mas madalas na pag-update, ngunit bahagyang mas\n    mataas ang paggamit ng mapagkukunan.\n  saturday: Sabado\n  start_of_week: Simula ng Linggo\n  startup: Patakbuhin sa Startup?\n  sunday: Linggo\n  theme:\n    available: Magagamit na Mga Tema\n    selected: Mga Aktibong Tema\nheader:\n  labels:\n    config: Mga pagsasaayos\n    developer: Para sa mga developer\n    extras: Extras\n    general: Pangkalahatan\n    home: Home\n    icon_pack_editor: Mga icon na naka -cache\n    iconpack: Icon pack\n    monitors: Monitor\n    plugin: Plugins\n    resources: Mga mapagkukunan\n    shortcuts: Mga shortcut\n    soundpack: Mga tunog pack\n    specific_apps: Mga setting sa pamamagitan ng application\n    theme: Mga tema\n    virtual_desk: Mga Virtual Desktop\n    wallpaper: Wallpaper\n    widget: Mga widget\nhome:\n  new_resources: Mga bagong mapagkukunan\ninherit: Magmana\ninProgress: Sa pag -unlad ...\ninsert: Ipasok\nloading: Naglo -load ...\nmiscellaneous: Miscellaneous\nmonitors_configurations:\n  label: Subaybayan {{index}}\nmore: Higit pa\n'no': Hindi\nopen: Buksan\nquit: Huminto ka\nremove: Alisin\nreset_all_to_default: I -reset ang lahat sa mga default na halaga\nreset_to_default: I -reset sa default na halaga\nresources:\n  app_outdated: >-\n    Ang mapagkukunang ito ay nangangailangan ng isang mas bagong bersyon ng\n    Seelen UI upang gumana nang maayos.\n  corrupted_wallpaper: >-\n    Nabigong i-extract ang thumbnail - sira o hindi sinusuportahang format ng\n    video\n  delete: Tanggalin ang mapagkukunan\n  discover: Tuklasin ang higit pang mga mapagkukunan\n  has_update: Magagamit ang isang pag -update\n  high_impact: Mataas na epekto sa pagganap\n  import_wallpapers: Mag -import ng mga lokal na wallpaper\n  open_folder: Buksan ang folder ng mapagkukunan\n  outdated: >-\n    Ang mapagkukunang ito ay dinisenyo para sa isang mas lumang bersyon ng\n    Seelen UI at maaaring hindi gumana nang maayos.\n  see_on_website: Tingnan sa website\nreview_request:\n  not_now: Hindi ngayon\n  sure: Oo naman!\n  title: Nasisiyahan sa Seelen UI?\nsave: I -save\nsave_and_restart: I -save at i -restart\nsearch: Maghanap\nsee_more: Makita pa\nshortcuts:\n  duplicate_error: Ang shortcut na ito ay nadoble\n  enable: Paganahin ang built-in na mga shortcut system\n  enable_tooltip: >-\n    Huwag paganahin kung ipatutupad mo ang iyong sariling sistema ng mga\n    shortcut gamit ang Seelen UI Client\n  labels:\n    create_new_workspace: Lumikha ng bagong workspace\n    cycle_stack_next: Susunod na Stack Stack\n    cycle_stack_prev: Cycle Stack Nakaraan\n    cycle_wallpaper_next: Baguhin sa susunod na wallpaper\n    cycle_wallpaper_prev: Baguhin sa nakaraang wallpaper\n    decrease_height: Bawasan ang taas\n    decrease_width: Bawasan ang lapad\n    destroy_current_workspace: Wasakin ang kasalukuyang workspace\n    focus_bottom: Pokus sa ilalim\n    focus_latest: Pinakabagong ituon\n    focus_left: Kaliwa ng Focus\n    focus_right: Tama ang ituon\n    focus_top: Tumuon sa itaas\n    increase_height: Dagdagan ang taas\n    increase_width: Dagdagan ang lapad\n    misc_force_quit: Puwersa na huminto\n    misc_force_restart: Force restart\n    misc_open_settings: Buksan ang mga setting\n    misc_toggle_lock_tracing: Toggle lock tracing (log)\n    misc_toggle_win_event_tracing: Toggle Win Event Tracing (log)\n    move_to_workspace: Lumipat sa workspace {{0}}\n    move_window_down: Ilipat ang window sa ibaba\n    move_window_left: Ilipat ang window sa kaliwa\n    move_window_right: Ilipat ang window sa kanan\n    move_window_up: Ilipat ang window sa itaas\n    pause_tiling: I -pause ang manager ng window window\n    reserve_bottom: Reserve Bottom\n    reserve_float: Reserve float\n    reserve_left: Reserve Kaliwa\n    reserve_right: Tama ang reserba\n    reserve_stack: Reserve stack\n    reserve_top: Reserve top\n    restore_sizes: Ibalik ang mga sukat\n    send_to_workspace: Ipadala sa Workspace {{0}}\n    start_weg_app: Ituon o simulan ang application {{0}}\n    switch_to_next_workspace: Lumipat sa Susunod na Workspace\n    switch_to_previous_workspace: Lumipat sa nakaraang workspace\n    switch_workspace: Lumipat sa workspace {{0}}\n    toggle_float: Toggle window float mode\n    toggle_monocle: Toggle workspace monocle mode\n  readonly_tooltip: Ito ay isang shortcut na basahin lamang\n  reset: I -reset sa mga default\nsides:\n  bottom: Ilalim\n  left: Kaliwa\n  right: Tama\n  top: Tuktok\ntoolbar:\n  auto_hide: Auto itago\n  delay_to_hide: Delay sa pagtatago\n  delay_to_show: Pagkaantala sa pagpapakita\n  dock_side: Posisyon\n  enable: Paganahin ang magarbong toolbar\n  hide_mode:\n    always: Laging\n    never: Hindi kailanman\n    on_overlap: Sa overlap\n  item_size: Laki ng Item\n  label: Toolbar\n  margin: Laki ng Margin\n  padding: Laki ng Padding\n  placeholder: {}\nupdate:\n  available: Mag -update Magagamit!\n  channel: I -update ang channel\n  downloading: Pag -download ...\nwall:\n  backgrounds: Wallpaper\n  blur: Malabo\n  cancel: Kanselahin\n  close: Isara\n  collection_name: Pangalan ng Koleksyon\n  collections: Mga koleksyon\n  contrast: Kaibahan\n  corrupted_wallpapers_message: 'Mga sirang wallpaper, isaalang-alang ang pagtanggal ng mga ito:'\n  create: Lumikha\n  create_collection: Lumikha ng Koleksyon\n  default_collection: Default na Koleksyon\n  delete_collection: Tanggalin ang Koleksyon\n  edit_collection: I-edit ang Koleksyon\n  enable: Paganahin ang manager ng wallpaper\n  extend: Palawakin ang pangunahin\n  fit:\n    contain: Naglalaman\n    cover: Takpan\n    fill: Punan\n  flipHorizontal: I -flip ang pahalang\n  flipVertical: Flip vertical\n  generating_thumbnails: Pagbuo ng Mga Thumbnail ng Wallpaper\n  hours: oras\n  interval: Baguhin ang wallpaper bawat\n  minutes: minuto\n  monitor_collection: Monitor Collection\n  multimonitor_behaviour: Pag-uugali ng Multimonitor\n  muted: Mute Video Audio\n  no_background: Walang laman na slideshow, gamit ang background ng tema sa halip.\n  no_collections: Wala pang nagawang koleksyon. I-click ang + button para gumawa ng isa.\n  objectFit: Pagkasyahin sa background\n  objectPosition: Posisyon sa background\n  overlayColor: Kulay ng overlay\n  overlayMixBlendMode: Overlay Mix Blend Mode\n  per_monitor: Bawat monitor\n  playback: Bilis ng pag -playback\n  position:\n    bottom: Ilalim\n    center: Sentro\n    left: Kaliwa\n    right: Tama\n    top: Tuktok\n  processing_video: Pinoproseso ang video\n  random: Randomize slideshow\n  saturation: Saturation\n  seconds: segundo\n  select_collection: Piliin ang Koleksyon\n  thumbnail_generation_complete: Kumpleto na ang Pagbuo ng Thumbnail\n  thumbnail_generation_finished: Matagumpay na natapos ang pagbuo ng thumbnail\n  wallpaper_collection: Koleksyon ng Wallpaper\n  wallpaper_settings: Mga Setting ng Wallpaper\n  withOverlay: Na may overlay\n  workspace_collections: Mga Koleksyon ng Workspace\nweg:\n  auto_hide: Auto itago\n  delay_to_hide: Delay sa pagtatago\n  delay_to_show: Pagkaantala sa pagpapakita\n  dock_side: Posisyon\n  enable: Paganahin ang pantalan/taskbar\n  filtering: Pag -filter ng item\n  gap: Gap\n  hide_mode:\n    always: Laging\n    never: Hindi kailanman\n    on_overlap: Sa overlap\n  items:\n    gap: Puwang sa pagitan ng mga item\n    label: Mga item\n    pinned_visibility:\n      always: Laging\n      label: Visibility ng Mga Naka-pin na Item\n      when_primary: Kapag ang monitor ay pangunahin\n    show_instance_counter: Ipakita ang bukas na counter ng windows\n    show_window_title: Ipakita ang Buksan ang Pamagat ng Window (Pahalang lamang)\n    size: Laki ng item\n    split_windows: Hatiin ang Windows (isang item sa bawat window)\n    temporal_visibility:\n      all: Lahat\n      label: Visibility ng Mga Na-unpin na Item\n      on_monitor: Sa Monitor\n    visible_separators: Visible Separator\n  label: Dock/Taskbar\n  margin: Margin\n  mode:\n    full_width: Buong lapad ng screen\n    min_content: Maliit hangga't maaari\n  padding: Padding\n  show_end_task: Ipakita ang pagtatapos ng gawain sa taskbar\n  width: Lapad\nwelcome:\n  give_a_review: Magbigay ng Review\n  message: >-\n    Ang Seelen UI ay isang libre at open source na desktop environment para sa\n    Windows. Dito mayroon kang ganap na kontrol sa hitsura at pag-uugali ng\n    iyong desktop, na nagbibigay-daan sa iyong i-customize ang bawat detalye\n    upang tumugma sa iyong daloy ng trabaho at istilo.\n  ok: Magsimula na tayo!\n  review: >-\n    Kung masisiyahan ka sa paggamit ng Seelen UI, isaalang-alang ang pag-iwan ng\n    review sa tindahan — ang iyong feedback ay nakakatulong sa proyekto na\n    lumago at umunlad.\n  title: Maligayang pagdating sa Seelen UI!\nwidget:\n  enable: Paganahin ang widget na ito\n  enable_for_monitor: Paganahin sa monitor na ito\n  instances: Mga pagkakataon\nwm:\n  animations:\n    duration: Tagal ng Animation (MS)\n    ease_function: Pag -andar ng pag -easing ng animation\n    enable: Paganahin ang mga animation ng window\n  author: May -akda\n  border:\n    enable: Paganahin ang hangganan ng window\n    offset: Border Offset\n    width: Lapad ng hangganan\n  description: Paglalarawan\n  drag_behavior: I-drag ang Gawi\n  drag_behavior_options:\n    sort: Pagbukud-bukurin (muling ayusin ang mga bintana habang nagda-drag)\n    swap: Magpalit (magpalit ng mga bintana sa dulo ng pag-drag)\n  enable: Paganahin ang manager ng window window\n  layout: Layout\n  resize_delta: Baguhin ang laki ng Delta (%)\n  space_between_containers: Puwang sa pagitan ng mga lalagyan\n  workspace_offset: Workspaces Offset (Margin)\n  workspace_padding: Mga workspaces padding\n'yes': Oo\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/tr.yml",
    "content": "action:\n  confirm: Emin misin?\n  confirm_body: Bu eylem geri alınamaz.\napps_configurations:\n  app:\n    bindings: Tuş Ataması (her iki seçenek de gereklidir)\n    category: Kategori\n    category_placeholder: Yok\n    monitor: Monitör\n    monitor_placeholder: Yok\n    name: İsim\n    ok_create: Oluştur\n    ok_edit: Düzenle\n    ok_readonly: Yeni Olarak Düzenle\n    options:\n      NoInteractive: Etkileşimli Değil\n      VdPinned: Tüm çalışma alanlarında göster\n      WmFloat: Twm - Kaymaya Başla\n      WmForce: Twm - Yönetmeyi Zorla\n      WmUnmanage: Twm - Yönetimi kaldır\n    options_label: Ekstra Seçenekler\n    title_create: '{{Name}} Oluşturuluyor'\n    title_edit: '{{name}} Düzenleniyor'\n    title_readonly: '{{name}} Görüntüleniyor'\n    weg_options_label: Dock/Görev Çubuğu Seçenekleri\n    wm_options_label: Pencere Yöneticisi Seçenekleri\n    workspace: Çalışma Alanı\n    workspace_placeholder: Yok\n  bundled_msg: >-\n    Bu paketlenmiş yapılandırmalar düzenlenemez ve özelleştirme olmadan size en\n    iyi deneyimi sağlamak için tasarlanmıştır. En yaygın uygulamaları otomatik\n    olarak yapılandırır.\n  bundled_title: Seelen ile Birlikte Gelen Uygulama Yapılandırması\n  confirm_delete: Bu yapılandırmayı silmek istediğinizden emin misiniz?\n  confirm_delete_title: Silmeyi Onayla\n  delete: Sil\n  export: Dışa Aktar\n  export_full: Ayarları uygulamaya göre dışa aktar\n  extra_info: >-\n    Seelen UI, uygulama başına yalnızca bir tanımlayıcı kullanır (ilk eşleşme\n    bulundu), bu nedenle nasıl belirlendiği sırası önemlidir. Tablonun\n    varsayılan olarak en yeniden eskiye doğru sıralandığını unutmayın, en son\n    eklenenlere öncelik verilecektir.\n  identifier:\n    add_block: Blok Ekle\n    and: VE\n    id: Tanımlayıcı\n    kind: Tanımla\n    matching_strategy: Eşleştirme Stratejisi\n    matching_strategy_option:\n      contains: İçerir\n      ends_with: Şununla biter\n      equals: Eşittir\n      regex: Düzenli ifade\n      starts_with: Şununla başlar\n    negation: Eşleşmeyi Geçersiz Kıl\n    or: VEYA\n    remove: Bloğu Sil\n    type:\n      class: Sınıf\n      exe: Exe\n      path: Yol\n      title: Başlık\n  import: İçe Aktar\n  import_full: Ayarları uygulamaya göre içe aktar\n  new: Yeni\n  search: Ara\n  swap: Değiştir\ncancel: İptal Et\nclose: Kapat\ndelete: Sil\ndevtools:\n  app_folders: Uygulama Klasörleri\n  custom_config_file: Özel Yapılandırma Dosyası Yükle\n  data_folder: Veri Klasörü\n  enable: Geliştirici Araçlarını Etkinleştir\n  install_folder: Kurulum Klasörü\n  load: Yükle\n  settings_file: Ayarlar Dosyası\n  simulate_perm:\n    label: Widget İzin İsteğini Simüle Et\n    result_allowed: ✓ İzin verildi\n    result_denied: ✗ İzin reddedildi\n    trigger: Benzetmek\n    widget_id_placeholder: '@yazar/widget-adı'\nextras:\n  clear_icons: Sistem Simgeleri Önbelleğini Temizle\n  clear_icons_tooltip: Tüm widget'larda tam olarak etkili olması için yeniden başlatma gerekebilir\n  exit: Çık\n  links: Resmi Bağlantılar\n  relaunch: Yeniden Başlat\n  version: Sürüm\n  version_fixed: >-\n    Uygulama ve WebView2 Çalışma Zamanı sürümleri düzeltildi. Bu, uygulamanın\n    güncellemeleri almayacağı ve WebView2 Çalışma Zamanının Windows\n    güncellemeleriyle otomatik olarak güncellenmeyeceği anlamına gelir.\ngeneral:\n  accent_color: Vurgu Rengi\n  date_format: Tarih Biçimi\n  date_format_how_to: Tarih formatı nasıl yazılır?\n  hardware_acceleration: Donanım Hızlandırma\n  hardware_acceleration_description: >-\n    Donanım hızlandırmayı devre dışı bırakmak bellek kullanımını azaltır ancak\n    performans sorunlarına neden olabilir. Canlı duvar kağıtları\n    kullanmayacaksanız devre dışı bırakmak güvenlidir.\n  icon_pack:\n    available: Mevcut Simge Paketleri\n    selected: Aktif Simge Paketleri\n  language: Dil\n  monday: Pazartesi\n  performance_mode:\n    on_battery: Aküde\n    on_energy_saver: Enerji tasarrufu üzerine\n    options:\n      disabled: Engelli\n      extreme: Aşırı\n      minimal: Minimal\n    plugged: Tıkalı veya şarj\n  polling_interval: Sistem yoklama aralığı\n  polling_interval_description: >-\n    Seelen UI'nin CPU, RAM, ağ ve disk etkinliği gibi sistem kaynaklarını ne\n    sıklıkta (saniye cinsinden) kontrol ettiği. Daha küçük bir sayı, daha sık\n    güncelleme ancak biraz daha yüksek kaynak kullanımı anlamına gelir.\n  saturday: Cumartesi\n  start_of_week: Hafta Başı\n  startup: Başlangıçta çalıştır\n  sunday: Pazar\n  theme:\n    available: Mevcut Temalar\n    selected: Aktif Temalar\nheader:\n  labels:\n    config: Konfigürasyonlar\n    developer: Geliştiriciler için\n    extras: Ekstralar\n    general: Genel\n    home: Ana Sayfa\n    icon_pack_editor: Önbelleğe Alınmış Simgeler\n    iconpack: Simge Paketleri\n    monitors: Monitörler\n    plugin: Eklentiler\n    resources: Kaynaklar\n    shortcuts: Kısayollar\n    soundpack: Ses Paketleri\n    specific_apps: Uygulamaya Göre Ayarlar\n    theme: Temalar\n    virtual_desk: Sanal Masaüstleri\n    wallpaper: Duvar Kağıtları\n    widget: Widget'lar\nhome:\n  new_resources: Yeni Kaynaklar\ninherit: Devral\ninProgress: Devam Ediyor...\ninsert: Ekle\nloading: Yükleniyor...\nmiscellaneous: Çeşitli\nmonitors_configurations:\n  label: Monitör {{index}}\nmore: Daha Fazla\n'no': HAYIR\nopen: Aç\nquit: Çıkış\nremove: Kaldır\nreset_all_to_default: Tümünü varsayılan değerlere sıfırla\nreset_to_default: Varsayılan değere sıfırla\nresources:\n  app_outdated: >-\n    Bu kaynağın düzgün çalışması için Seelen UI'nin daha yeni bir sürümü\n    gerekir.\n  corrupted_wallpaper: Küçük resim çıkarılamadı - bozuk veya desteklenmeyen video biçimi\n  delete: Kaynağı Sil\n  discover: Daha fazla kaynak keşfedin\n  has_update: Bir güncelleme mevcuttur\n  high_impact: Performans üzerinde yüksek etki\n  import_wallpapers: Yerel duvar kağıtlarını içe aktarma\n  open_folder: Kaynak klasörünü aç\n  outdated: >-\n    Bu kaynak Seelen UI'nin eski bir sürümü için tasarlanmıştır ve düzgün\n    çalışmayabilir.\n  see_on_website: Web sitesinde gör\nreview_request:\n  not_now: Şimdi değil\n  sure: Elbette!\n  title: Seelen kullanıcı arayüzünden memnun musunuz?\nsave: Kaydet\nsave_and_restart: Kaydet ve Yeniden Başlat\nsearch: Aramak\nsee_more: Daha Fazla Gör\nshortcuts:\n  duplicate_error: Bu kısayol kopyalandı\n  enable: Dahili kısayol sistemini etkinleştirin\n  enable_tooltip: >-\n    Seelen UI istemcisini kullanarak kendi kısayol sisteminizi uygulayacaksanız\n    devre dışı bırakın\n  labels:\n    create_new_workspace: Yeni çalışma alanı oluşturun\n    cycle_stack_next: Bisiklet yığını sonraki\n    cycle_stack_prev: Önceki bisiklet yığını\n    cycle_wallpaper_next: Sonraki Duvar Kağıdına Değiştir\n    cycle_wallpaper_prev: Önceki duvar kağıdına geç\n    decrease_height: Yüksekliği Azalt\n    decrease_width: Genişliği Azalt\n    destroy_current_workspace: Mevcut çalışma alanını yok et\n    focus_bottom: Dip\n    focus_latest: En son odak\n    focus_left: Sola odaklanmak\n    focus_right: Doğru odaklanmak\n    focus_top: Focus tepe\n    increase_height: Yüksekliği arttırmak\n    increase_width: Genişliği Artırın\n    misc_force_quit: Çıkarma\n    misc_force_restart: Yeniden Başlatmaya Zorla\n    misc_open_settings: Ayarlar Aç\n    misc_toggle_lock_tracing: Kilit izleme (kütükler)\n    misc_toggle_win_event_tracing: Win olay izleme (günlükler) değiştirme\n    move_to_workspace: Çalışma Alanına Taşın {{0}}\n    move_window_down: Pencereyi dibe taşıyın\n    move_window_left: Pencereyi sola taşıyın\n    move_window_right: Pencereyi sağa taşıyın\n    move_window_up: Pencereyi üstüne taşıyın\n    pause_tiling: Duraklatma döşeme pencere yöneticisi\n    reserve_bottom: Dip\n    reserve_float: Yüzmek\n    reserve_left: Sola gitme\n    reserve_right: Doğru ayırtmak\n    reserve_stack: Yedek yığın\n    reserve_top: Üst kısım\n    restore_sizes: Boyutları Geri Yükle\n    send_to_workspace: Çalışma Alanına Gönder {{0}}\n    start_weg_app: Odak veya Başlat Uygulaması {{0}}\n    switch_to_next_workspace: Sonraki çalışma alanına geçin\n    switch_to_previous_workspace: Önceki çalışma alanına geçin\n    switch_workspace: Çalışma alanına geç {{0}}\n    toggle_float: Pencere şamandıra modunu geçiş\n    toggle_monocle: Çalışma Alanı Monokle Modunu Değiştirin\n  readonly_tooltip: Bu salt okunur bir kısayol\n  reset: Varsayılanlara sıfırlayın\nsides:\n  bottom: Alt\n  left: Sol\n  right: Sağ\n  top: Üst\ntoolbar:\n  auto_hide: Otomatik Gizle\n  delay_to_hide: Gizleme gecikmesi\n  delay_to_show: Gösterme gecikmesi\n  dock_side: Pozisyon\n  enable: Araç Çubuğunu Etkinleştir\n  hide_mode:\n    always: Her zaman\n    never: Asla\n    on_overlap: Üst üste binme durumunda\n  item_size: Ürün Boyutu\n  label: Araç Çubuğu\n  margin: Kenar Boşluğu Boyutu\n  padding: Dolgu Boyutu\n  placeholder: {}\nupdate:\n  available: Güncelleme Mevcut!\n  channel: Güncelleme kanalı\n  downloading: İndiriliyor...\nwall:\n  backgrounds: Duvar Kağıtları\n  blur: Bulanıklık\n  cancel: İptal etmek\n  close: Kapalı\n  collection_name: Koleksiyon Adı\n  collections: Koleksiyonlar\n  contrast: Kontrast\n  corrupted_wallpapers_message: 'Bozuk duvar kağıtları, şunları silmeyi düşünün:'\n  create: Yaratmak\n  create_collection: Koleksiyon Oluştur\n  default_collection: Varsayılan Koleksiyon\n  delete_collection: Koleksiyonu Sil\n  edit_collection: Koleksiyonu Düzenle\n  enable: Duvar Kağıdı Yöneticisini Etkinleştir\n  extend: Birincil'i genişlet\n  fit:\n    contain: Konteyner\n    cover: Kapak\n    fill: Doldur\n  flipHorizontal: Yatay Çevirme\n  flipVertical: Dikey Çevirme\n  generating_thumbnails: Duvar Kağıdı Küçük Resimleri Oluşturma\n  hours: saat\n  interval: Duvar kağıdı değiştirme aralığı\n  minutes: dakika\n  monitor_collection: Monitör Koleksiyonu\n  multimonitor_behaviour: Çoklu Monitör Davranışı\n  muted: Video sesini kapat\n  no_background: Boş slayt gösterisi, tema arka planı kullanılıyor.\n  no_collections: >-\n    Henüz koleksiyon oluşturulmadı. Bir tane oluşturmak için + düğmesine\n    tıklayın.\n  objectFit: Arka Plan Uyumu\n  objectPosition: Arka Plan Pozisyonu\n  overlayColor: Kaplama Rengi\n  overlayMixBlendMode: Kaplama Karışım Karışım Modu\n  per_monitor: Monitör başına\n  playback: Oynatma hızı\n  position:\n    bottom: Alt\n    center: Merkez\n    left: Sol\n    right: Doğru.\n    top: Üst\n  processing_video: Video işleniyor\n  random: Rastgele slayt gösterisi\n  saturation: Doygunluk\n  seconds: saniye\n  select_collection: Koleksiyon Seç\n  thumbnail_generation_complete: Küçük Resim Oluşturma Tamamlandı\n  thumbnail_generation_finished: Küçük resim oluşturma başarıyla tamamlandı\n  wallpaper_collection: Duvar Kağıdı Koleksiyonu\n  wallpaper_settings: Duvar Kağıdı Ayarları\n  withOverlay: Kaplama ile\n  workspace_collections: Çalışma Alanı Koleksiyonları\nweg:\n  auto_hide: Otomatik Gizle\n  delay_to_hide: Gizleme gecikmesi\n  delay_to_show: Gösterme gecikmesi\n  dock_side: Konum\n  enable: Dock/Görev Çubuğunu Etkinleştir\n  filtering: Öğe Filtreleme\n  gap: Boşluk\n  hide_mode:\n    always: Her zaman\n    never: Asla\n    on_overlap: Üst üste binme durumunda\n  items:\n    gap: Öğeler Arası Boşluk\n    label: Öğeler\n    pinned_visibility:\n      always: Her zaman\n      label: Sabitlenmiş Öğelerin Görünürlüğü\n      when_primary: Monitör birincil olduğunda\n    show_instance_counter: Açık pencere sayacını göster\n    show_window_title: Açık pencere başlığını göster (sadece yatay)\n    size: Öğe Boyutu\n    split_windows: Pencereleri Böl (pencere başına bir öğe)\n    temporal_visibility:\n      all: Tümü\n      label: Sabitlenmemiş Öğelerin Görünürlüğü\n      on_monitor: Monitörde\n    visible_separators: Görünür Ayırıcılar\n  label: Dock/Görev Çubuğu\n  margin: Kenar Boşluğu\n  mode:\n    full_width: Tam ekran genişliği\n    min_content: Olabildiğince küçük\n  padding: İç Boşluk\n  show_end_task: Görev çubuğunda görevi sonlandırmayı göster\n  width: Genişlik\nwelcome:\n  give_a_review: Yorum Yapın\n  message: >-\n    Seelen UI, Windows için ücretsiz ve açık kaynaklı bir masaüstü ortamıdır.\n    Burada masaüstünüzün nasıl görüneceği ve nasıl davranacağı üzerinde tam\n    kontrole sahip olursunuz; böylece her ayrıntıyı iş akışınıza ve stilinize\n    uyacak şekilde özelleştirebilirsiniz.\n  ok: Haydi başlayalım!\n  review: >-\n    Seelen kullanıcı arayüzünü kullanmaktan hoşlanıyorsanız mağazaya bir\n    inceleme bırakmayı düşünün; geri bildiriminiz projenin büyümesine ve\n    gelişmesine yardımcı olur.\n  title: Seelen kullanıcı arayüzüne hoş geldiniz!\nwidget:\n  enable: Bu widget'ı etkinleştir\n  enable_for_monitor: Bu monitörde etkinleştir\n  instances: Örnekler\nwm:\n  animations:\n    duration: Animasyon Süresi (MS)\n    ease_function: Animasyon kolaylaştırma işlevi\n    enable: Pencerenin animasyonlarını etkinleştirin\n  author: Yazar\n  border:\n    enable: Pencere Kenarlığını Etkinleştir\n    offset: Kenar Ofseti\n    width: Kenar Genişliği\n  description: Açıklama\n  drag_behavior: Sürükleme Davranışı\n  drag_behavior_options:\n    sort: Sırala (sürüklerken pencereleri yeniden sıralayın)\n    swap: Değiştir (sürükleme ucunda pencereleri değiştir)\n  enable: Döşeme Pencere Yöneticisini Etkinleştir\n  layout: Düzen\n  resize_delta: Yeniden Boyutlandırma Değişimi (%)\n  space_between_containers: Konteynerler Arası Boşluk\n  workspace_offset: Çalışma Alanları Uzaklıkları (Kenar Boşlukları)\n  workspace_padding: Çalışma Alanları İç Boşluğu\n'yes': Evet\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/uk.yml",
    "content": "action:\n  confirm: Ви впевнені?\n  confirm_body: Цю дію не можна скасувати.\napps_configurations:\n  app:\n    bindings: Прив'язка (візьміть до уваги, що обидва варіанти потрібні)\n    category: Категорія\n    category_placeholder: Ніяка\n    monitor: Монітор\n    monitor_placeholder: Ніякий\n    name: Назва\n    ok_create: Створити\n    ok_edit: Оновити\n    ok_readonly: Редагувати як нове\n    options:\n      NoInteractive: Немає інтерактиву\n      VdPinned: Показати в усіх робочих областях\n      WmFloat: Twm - почати плавати\n      WmForce: Twm – примусове керування\n      WmUnmanage: Twm - Скасувати керування\n    options_label: Додаткові варіанти\n    title_create: Створення {{name}}\n    title_edit: Редагування {{name}}\n    title_readonly: Перегляд {{name}}\n    weg_options_label: Параметри Dock/панелі задач\n    wm_options_label: Параметри менеджера вікон\n    workspace: Робочий простір\n    workspace_placeholder: Ніякий\n  bundled_msg: >-\n    Ці готові конфігурації не підлягають редагуванню і створені для того, щоб\n    забезпечити вам найкращий досвід роботи без кастомізації. Вони автоматично\n    налаштовують найпоширеніші програми для вас.\n  bundled_title: Запропонована конфігурація додатка Seelen\n  confirm_delete: Ви впевнені, що хочете видалити конфігурацію?\n  confirm_delete_title: Підтвердити видалення\n  delete: Видалити\n  export: Експорт\n  export_full: Експорт налаштувань за додатками\n  extra_info: >-\n    Seelen UI використовує лише один ідентифікатор для кожного додатка (перший\n    знайдений збіг), тому важливим є порядок, у якому вони вказуються. Останні\n    додані додатки матимуть пріоритет. Зауважте, що за замовчуванням таблиця\n    відсортована від найновішого до найстарішого.\n  identifier:\n    add_block: Додати блок\n    and: І\n    id: Ідентифікатор\n    kind: Ідентифікувати по\n    matching_strategy: Стратегія відповідності\n    matching_strategy_option:\n      contains: Містить\n      ends_with: Закінчується на\n      equals: Дорівнює\n      regex: Регулярний вираз\n      starts_with: Починається з\n    negation: Заперечувати відповідність\n    or: АБО\n    remove: Видалити блок\n    type:\n      class: Клас\n      exe: Виконуваний файл\n      path: Шлях\n      title: Назва\n  import: Імпорт\n  import_full: Імпорт налаштувань за додатками\n  new: Новий\n  search: Пошук\n  swap: Замінити\ncancel: Скасувати\nclose: Закрити\ndelete: Видалити\ndevtools:\n  app_folders: Папки додатків\n  custom_config_file: Імпортувати файл конфігурації\n  data_folder: Папка даних\n  enable: Увімкнути інструменти розробника\n  install_folder: Папка встановлення\n  load: Відкрити\n  settings_file: Файл налаштувань\n  simulate_perm:\n    label: Симуляція запиту на дозвіл віджета\n    result_allowed: ✓ Дозвіл надано\n    result_denied: ✗ У дозволі відмовлено\n    trigger: Симулювати\n    widget_id_placeholder: '@author/назва-віджета'\nextras:\n  clear_icons: Очистити кеш системних іконок\n  clear_icons_tooltip: Може знадобитися перезапуск для повного набрання чинності для всіх віджетів\n  exit: Закрити програму\n  links: Офіційні посилання\n  relaunch: Перезапустити\n  version: Версія\n  version_fixed: >-\n    Виправлено версії програми та WebView2 Runtime. Це означає, що програма не\n    отримуватиме оновлення, а WebView2 Runtime не буде автоматично оновлюватись\n    оновленнями Windows.\ngeneral:\n  accent_color: Основний колір\n  date_format: Формат дати та часу\n  date_format_how_to: Як написати формат дати?\n  hardware_acceleration: Апаратне прискорення\n  hardware_acceleration_description: >-\n    Вимкнення апаратного прискорення зменшить використання пам’яті, але може\n    спричинити проблеми з продуктивністю. Безпечно вимкнути, якщо ви не хочете\n    використовувати живі шпалери.\n  icon_pack:\n    available: Доступні пакети іконок\n    selected: Активні пакети іконки\n  language: Мова\n  monday: понеділок\n  performance_mode:\n    on_battery: На акумулятор\n    on_energy_saver: На енергозбереженні\n    options:\n      disabled: Інвалід\n      extreme: Екстремальний\n      minimal: Мінімальний\n    plugged: Підключений або заряджається\n  polling_interval: Інтервал опитування системи\n  polling_interval_description: >-\n    Як часто (у секундах) Seelen UI перевіряє системні ресурси, такі як ЦП,\n    оперативна пам’ять, мережа та активність диска. Менше число означає частіші\n    оновлення, але трохи більше використання ресурсів.\n  saturday: Субота\n  start_of_week: Початок тижня\n  startup: Запускати разом з Windows?\n  sunday: неділя\n  theme:\n    available: Доступні теми\n    selected: Активні теми\nheader:\n  labels:\n    config: Конфігурації\n    developer: Для розробників\n    extras: Додатково\n    general: Загальні налаштування\n    home: Домашня сторінка\n    icon_pack_editor: Кешовані іконки\n    iconpack: Пакети іконок\n    monitors: Монітори\n    plugin: Плагіни\n    resources: Ресурси\n    shortcuts: Сполучення клавіш\n    soundpack: Звукові пакети\n    specific_apps: Налаштування за додатками\n    theme: Теми\n    virtual_desk: Віртуальні робочі столи\n    wallpaper: Шпалери\n    widget: Віджети\nhome:\n  new_resources: Нові ресурси\ninherit: Успадкувати\ninProgress: В процесі...\ninsert: Вставити\nloading: Завантаження...\nmiscellaneous: Різне\nmonitors_configurations:\n  label: Монітор {{index}}\nmore: Більше\n'no': Ні\nopen: Відкрити\nquit: Вийти\nremove: Видалити\nreset_all_to_default: Скинути все до значень за замовчуванням\nreset_to_default: Скидання до значення за замовчуванням\nresources:\n  app_outdated: Для коректної роботи цього ресурсу потрібна новіша версія інтерфейсу Seelen.\n  corrupted_wallpaper: >-\n    Не вдалося видобути мініатюру – формат відео пошкоджений або не\n    підтримується\n  delete: Видалити ресурс\n  discover: Відкрийте для себе більше ресурсів\n  has_update: Доступне оновлення\n  high_impact: Великий вплив на продуктивність\n  import_wallpapers: Імпортувати місцеві шпалери\n  open_folder: Відкрийте папку з ресурсами\n  outdated: >-\n    Цей ресурс був розроблений для старої версії інтерфейсу Seelen і може не\n    працювати належним чином.\n  see_on_website: Дивіться на сайті\nreview_request:\n  not_now: Не зараз\n  sure: звичайно!\n  title: Подобається інтерфейс користувача Seelen?\nsave: Зберегти\nsave_and_restart: Зберегти та перезапустити\nsearch: Пошук\nsee_more: Побачити більше\nshortcuts:\n  duplicate_error: Цей ярлик дублюється\n  enable: Увімкнути вбудовану систему ярликів\n  enable_tooltip: >-\n    Вимкніть, якщо ви реалізуєте власну систему ярликів за допомогою клієнта\n    інтерфейсу SEELEN\n  labels:\n    create_new_workspace: Створіть нову робочу область\n    cycle_stack_next: Цикл стек далі\n    cycle_stack_prev: Стек циклу попереднього\n    cycle_wallpaper_next: Зміна на наступні шпалери\n    cycle_wallpaper_prev: Зміна на попередні шпалери\n    decrease_height: Зниження висоти\n    decrease_width: Зменшення ширини\n    destroy_current_workspace: Знищити поточну робочу область\n    focus_bottom: Фокус дно\n    focus_latest: Фокус найновіший\n    focus_left: Фокус ліворуч\n    focus_right: Фокус правильно\n    focus_top: Фокус\n    increase_height: Збільшити висоту\n    increase_width: Збільшити ширину\n    misc_force_quit: Сила кинути\n    misc_force_restart: Примусовий перезапуск\n    misc_open_settings: Відкриті налаштування\n    misc_toggle_lock_tracing: Перемикання відстеження блокування (журнали)\n    misc_toggle_win_event_tracing: Toggle Win Tracing події (журнали)\n    move_to_workspace: Перемістіть до робочої області {{0}}\n    move_window_down: Перемістіть вікно до знизу\n    move_window_left: Перемістіть вікно вліво\n    move_window_right: Перемістіть вікно вправо\n    move_window_up: Перемістіть вікно вгорі\n    pause_tiling: Пауза плитка Менеджер вікон\n    reserve_bottom: ЗАБЕЗПЕЧЕННЯ\n    reserve_float: Резервне плавання\n    reserve_left: Резерв зліва\n    reserve_right: Резервуйте праворуч\n    reserve_stack: Резерв\n    reserve_top: Резервувати топ\n    restore_sizes: Відновити розміри\n    send_to_workspace: Надіслати на робочу область {{0}}\n    start_weg_app: Фокус або запуск програми {{0}}\n    switch_to_next_workspace: Перейдіть на наступну робочу область\n    switch_to_previous_workspace: Перехід на попередню робочу область\n    switch_workspace: Переключення на робочу область {{0}}\n    toggle_float: Перемикайте режим віконного поплавця\n    toggle_monocle: Перемикайте режим монокльної роботи робочої області\n  readonly_tooltip: Це ярлик лише для читання\n  reset: Скидання за замовчуванням\nsides:\n  bottom: Низ\n  left: Ліво\n  right: Право\n  top: Верх\ntoolbar:\n  auto_hide: Автоматично приховувати\n  delay_to_hide: Затримка приховання\n  delay_to_show: Затримка показу\n  dock_side: Посада\n  enable: Увімкнути панель інструментів\n  hide_mode:\n    always: Завжди\n    never: Ніколи\n    on_overlap: При перекритті\n  item_size: Розмір елемента\n  label: Панель інструментів\n  margin: Розмір маржі\n  padding: Розмір прокладки\n  placeholder: {}\nupdate:\n  available: Доступне оновлення!\n  channel: Канал оновлення\n  downloading: Завантаження...\nwall:\n  backgrounds: Шпалери\n  blur: Плямистість.\n  cancel: Скасувати\n  close: Закрити\n  collection_name: Назва колекції\n  collections: Колекції\n  contrast: Контраст\n  corrupted_wallpapers_message: 'Пошкоджені шпалери, видаліть ці:'\n  create: Створити\n  create_collection: Створити колекцію\n  default_collection: Колекція за замовчуванням\n  delete_collection: Видалити колекцію\n  edit_collection: Редагувати колекцію\n  enable: Увімкнути менеджер шпалер\n  extend: Подовжити основний\n  fit:\n    contain: Містити\n    cover: Обкладинка\n    fill: Заповніть.\n  flipHorizontal: Горизонтальне перевертання\n  flipVertical: Фліп Вертикальний\n  generating_thumbnails: Створення мініатюр шпалер\n  hours: години\n  interval: Зміна шпалер кожні\n  minutes: хвилини\n  monitor_collection: Колекція моніторів\n  multimonitor_behaviour: Багатомоніторна поведінка\n  muted: Вимкнути звук відео\n  no_background: Слайдів не знайдено, натомість застосовано фон теми.\n  no_collections: Ще немає створених колекцій. Натисніть кнопку +, щоб створити його.\n  objectFit: Підгонка фону\n  objectPosition: Вихідна позиція\n  overlayColor: Колір накладання\n  overlayMixBlendMode: Режим змішування з накладанням\n  per_monitor: На монітор\n  playback: Швидкість відтворення\n  position:\n    bottom: Дно.\n    center: Центр\n    left: Ліворуч.\n    right: Так.\n    top: Верхня частина\n  processing_video: Обробка відео\n  random: Випадковий порядок слайд-шоу\n  saturation: Насиченість\n  seconds: секунди\n  select_collection: Виберіть Колекція\n  thumbnail_generation_complete: Генерація ескізів завершена\n  thumbnail_generation_finished: Створення мініатюри успішно завершено\n  wallpaper_collection: Колекція шпалер\n  wallpaper_settings: Налаштування шпалер\n  withOverlay: З накладанням\n  workspace_collections: Колекції робочої області\nweg:\n  auto_hide: Автоматично приховувати\n  delay_to_hide: Затримка приховання\n  delay_to_show: Затримка показу\n  dock_side: Позиція панелі\n  enable: Увімкнути панель завдань\n  filtering: Фільтрація елементів\n  gap: Відступ від краю\n  hide_mode:\n    always: Завжди\n    never: Ніколи\n    on_overlap: При перекритті\n  items:\n    gap: Проміжок між піктограмами\n    label: Піктограми\n    pinned_visibility:\n      always: Завжди\n      label: Видимість закріплених елементів\n      when_primary: Коли монітор є основним\n    show_instance_counter: Показати лічильник відкритих вікон\n    show_window_title: Показувати заголовок відкритого вікна (тільки горизонтально)\n    size: Розмір піктограм\n    split_windows: Розділені вікна (один елемент на вікно)\n    temporal_visibility:\n      all: всі\n      label: Видимість незакріплених елементів\n      on_monitor: На моніторі\n    visible_separators: Видимі розділювачі\n  label: Панель завдань\n  margin: Зовнішній відступ\n  mode:\n    full_width: На всю ширину екрана\n    min_content: Якнайменший\n  padding: Внутрішній відступ\n  show_end_task: Показати завершене завдання на панелі завдань\n  width: Ширина\nwelcome:\n  give_a_review: Дайте відгук\n  message: >-\n    Seelen UI — це безкоштовне робоче середовище з відкритим кодом для Windows.\n    Тут ви маєте повний контроль над тим, як виглядає та поводиться ваш робочий\n    стіл, що дозволяє налаштувати кожну деталь відповідно до вашого робочого\n    процесу та стилю.\n  ok: Починаємо!\n  review: >-\n    Якщо вам подобається використовувати Seelen UI, подумайте про те, щоб\n    залишити відгук у магазині — ваш відгук допоможе проекту розвиватися та\n    вдосконалюватися.\n  title: Ласкаво просимо до Seelen UI!\nwidget:\n  enable: Увімкніть цей віджет\n  enable_for_monitor: Увімкнути на цьому моніторі\n  instances: Екземпляри\nwm:\n  animations:\n    duration: Тривалість анімації (MS)\n    ease_function: Функція ослаблення анімації\n    enable: Увімкнути анімацію вікна\n  author: Автор\n  border:\n    enable: Увімкнути кордон вікна\n    offset: Зміщення кордону\n    width: Ширина кордону\n  description: Опис\n  drag_behavior: Поведінка перетягування\n  drag_behavior_options:\n    sort: Сортування (зміна порядку вікон під час перетягування)\n    swap: Swap (міняти місцями вікна на кінці перетягування)\n  enable: Увімкнути менеджер вікон\n  layout: Макет\n  resize_delta: Змінити розмір дельти (%)\n  space_between_containers: Простір між вікнами\n  workspace_offset: Зсув робочих просторів (Зовнішні поля)\n  workspace_padding: Внутрішній відступ робочих просторів\n'yes': Так\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/ur.yml",
    "content": "action:\n  confirm: کیا آپ کو یقین ہے؟\n  confirm_body: اس عمل کو ختم نہیں کیا جاسکتا۔\napps_configurations:\n  app:\n    bindings: بائنڈنگ (نوٹ دونوں اختیارات کی ضرورت ہے)\n    category: قسم\n    category_placeholder: کوئی نہیں\n    monitor: مانیٹر\n    monitor_placeholder: کوئی نہیں\n    name: نام\n    ok_create: بنانا\n    ok_edit: اپ ڈیٹ\n    ok_readonly: نئے کے طور پر ترمیم کریں\n    options:\n      NoInteractive: کوئی انٹرایکٹو نہیں\n      VdPinned: تمام ورک اسپیس میں دکھائیں\n      WmFloat: TWM - تیرنا شروع کریں\n      WmForce: TWM - فورس کا انتظام کریں\n      WmUnmanage: TWM - غیر منظم\n    options_label: اضافی اختیارات\n    title_create: '{{نام}} بنانا'\n    title_edit: ترمیم {{نام}}\n    title_readonly: '{{نام}} دیکھ رہا ہے'\n    weg_options_label: گودی/ٹاسک بار کے اختیارات\n    wm_options_label: ونڈو مینیجر کے اختیارات\n    workspace: ورک اسپیس\n    workspace_placeholder: کوئی نہیں\n  bundled_msg: >-\n    یہ بنڈل تشکیلات قابل تدوین نہیں ہیں اور آپ کو حسب ضرورت کے بغیر بہترین تجربہ\n    فراہم کرنے کے لئے ڈیزائن کی گئی ہیں۔ وہ خود بخود آپ کے لئے سب سے عام ایپلی\n    کیشنز کو تشکیل دیتے ہیں۔\n  bundled_title: ایپ کنفیگ سیلن کے ساتھ بنڈل\n  confirm_delete: کیا آپ واقعی اس ترتیب کو حذف کرنا چاہتے ہیں؟\n  confirm_delete_title: حذف کرنے کی تصدیق کریں\n  delete: حذف کریں\n  export: برآمد\n  export_full: درخواست کے ذریعہ ترتیبات برآمد کریں\n  extra_info: >-\n    Seelen UI فی ایپ صرف ایک شناخت کنندہ کا استعمال کرتا ہے (پہلا میچ ملا) لہذا\n    ترتیب کس طرح مخصوص کی گئی ہے اہم ہے، تازہ ترین شامل کو ترجیح دی جائے گی،\n    کیونکہ نوٹ کریں کہ جدول کو ڈیفالٹ سے تازہ ترین سے پرانے تک ترتیب دیا جاتا\n    ہے۔\n  identifier:\n    add_block: بلاک شامل کریں\n    and: اور\n    id: شناخت کنندہ\n    kind: بذریعہ شناخت کریں\n    matching_strategy: مماثل حکمت عملی\n    matching_strategy_option:\n      contains: پر مشتمل ہے۔\n      ends_with: کے ساتھ ختم ہوتا ہے۔\n      equals: برابر ہے۔\n      regex: باقاعدہ اظہار\n      starts_with: سے شروع ہوتا ہے۔\n    negation: نفی مماثل\n    or: یا\n    remove: بلاک کو حذف کریں\n    type:\n      class: کلاس\n      exe: Exe\n      path: راستہ\n      title: عنوان\n  import: درآمد\n  import_full: درخواست کے ذریعہ ترتیبات درآمد کریں\n  new: نئی\n  search: تلاش\n  swap: تبادلہ\ncancel: منسوخ کریں\nclose: بند کریں\ndelete: حذف کریں\ndevtools:\n  app_folders: ایپ فولڈرز\n  custom_config_file: کسٹم کنفیگ فائل لوڈ کریں\n  data_folder: ڈیٹا فولڈر\n  enable: ڈویلپر ٹولز کو فعال کریں\n  install_folder: انسٹالیشن فولڈر\n  load: لوڈ\n  settings_file: سیٹنگ فاعل\n  simulate_perm:\n    label: ویجیٹ کی اجازت کی درخواست کی تقلید کریں۔\n    result_allowed: ✓ اجازت دی گئی۔\n    result_denied: ✗ اجازت مسترد کر دی گئی۔\n    trigger: نقل کرنا\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: صاف نظام شبیہیں کیشے\n  clear_icons_tooltip: دوبارہ شروع کرنے کے لئے تمام ویجٹوں پر مکمل اثر ڈالنے کی ضرورت ہوسکتی ہے\n  exit: چھوڑ دیں/باہر نکلیں\n  links: لنکس\n  relaunch: دوبارہ لانچ\n  version: ورژن\n  version_fixed: >-\n    ایپلی کیشن اور ویب ویو 2 رن ٹائم ورژن طے شدہ ہیں۔ اس کا مطلب یہ ہے کہ ایپلی\n    کیشن کو اپ ڈیٹس موصول نہیں ہوں گے اور ویب ویو 2 رن ٹائم کو ونڈوز کی تازہ\n    کاریوں کے ساتھ خود بخود اپ ڈیٹ نہیں کیا جائے گا۔\ngeneral:\n  accent_color: رنگ\n  date_format: تاریخ کی شکل\n  date_format_how_to: تاریخ کی شکل کیسے لکھیں؟\n  hardware_acceleration: ہارڈ ویئر ایکسلریشن\n  hardware_acceleration_description: >-\n    ہارڈویئر ایکسلریشن کو غیر فعال کرنے سے میموری کا استعمال کم ہو جائے گا، لیکن\n    کارکردگی کے مسائل پیدا ہو سکتے ہیں۔ اگر آپ لائیو وال پیپر استعمال نہیں کریں\n    گے تو اسے غیر فعال کرنا محفوظ ہے۔\n  icon_pack:\n    available: دستیاب آئیکن پیک\n    selected: ایکٹو آئیکن پیک\n  language: زبان\n  monday: پیر\n  performance_mode:\n    on_battery: بیٹری پر\n    on_energy_saver: انرجی سیور پر\n    options:\n      disabled: غیر فعال\n      extreme: انتہائی\n      minimal: کم سے کم\n    plugged: پلگ ان یا چارجنگ\n  polling_interval: سسٹم پولنگ وقفہ\n  polling_interval_description: >-\n    سیلین UI کتنی بار (سیکنڈوں میں) سسٹم کے وسائل جیسے CPU، RAM، نیٹ ورک، اور\n    ڈسک کی سرگرمی کو چیک کرتا ہے۔ ایک چھوٹی تعداد کا مطلب ہے زیادہ بار بار اپ\n    ڈیٹس، لیکن وسائل کا تھوڑا سا زیادہ استعمال۔\n  saturday: ہفتہ\n  start_of_week: ہفتے کا آغاز\n  startup: اسٹارٹ اپ پر چلائیں؟\n  sunday: اتوار\n  theme:\n    available: دستیاب تھیمز\n    selected: ایکٹو تھیمز\nheader:\n  labels:\n    config: تشکیلات\n    developer: ڈویلپرز کے لئے\n    extras: ایکسٹرا\n    general: جنرل\n    home: گھر\n    icon_pack_editor: کیشڈ شبیہیں\n    iconpack: آئیکن پیک\n    monitors: مانیٹر\n    plugin: پلگ ان\n    resources: وسائل\n    shortcuts: شارٹ کٹ\n    soundpack: ساؤنڈ پیک\n    specific_apps: درخواست کے ذریعہ ترتیبات\n    theme: موضوعات\n    virtual_desk: ورچوئل ڈیسک ٹاپس\n    wallpaper: وال پیپر\n    widget: ویجٹ\nhome:\n  new_resources: نئے وسائل\ninherit: وراثت\ninProgress: کام جاری ہے...\ninsert: داخل کریں\nloading: لوڈنگ ...\nmiscellaneous: متفرق\nmonitors_configurations:\n  label: مانیٹر {{انڈیکس}}\nmore: زیادہ\n'no': نہیں\nopen: کھلا\nquit: چھوڑ دیں\nremove: ہٹا دیں\nreset_all_to_default: سب کو پہلے سے طے شدہ اقدار پر دوبارہ ترتیب دیں\nreset_to_default: ڈیفالٹ ویلیو پر دوبارہ سیٹ کریں\nresources:\n  app_outdated: >-\n    اس وسیلہ کے لئے مناسب طریقے سے کام کرنے کے لئے سیلین UI کا ایک نیا ورژن\n    درکار ہے۔\n  corrupted_wallpaper: تھمب نیل نکالنے میں ناکام - خراب یا غیر تعاون یافتہ ویڈیو فارمیٹ\n  delete: وسائل کو حذف کریں\n  discover: مزید وسائل دریافت کریں\n  has_update: ایک تازہ کاری دستیاب ہے\n  high_impact: کارکردگی پر زیادہ اثر\n  import_wallpapers: مقامی وال پیپر درآمد کریں\n  open_folder: اوپن ریسورس فولڈر\n  outdated: >-\n    یہ وسیلہ سیلین UI کے پرانے ورژن کے لئے ڈیزائن کیا گیا تھا اور ہوسکتا ہے کہ\n    وہ صحیح طریقے سے کام نہ کرے۔\n  see_on_website: ویب سائٹ پر دیکھیں\nreview_request:\n  not_now: اب نہیں\n  sure: یقینا!\n  title: سیلین UI سے لطف اندوز ہو رہے ہو؟\nsave: محفوظ کریں\nsave_and_restart: محفوظ کریں اور دوبارہ شروع کریں\nsearch: تلاش کریں۔\nsee_more: مزید دیکھیں\nshortcuts:\n  duplicate_error: اس شارٹ کٹ کو نقل کیا گیا ہے\n  enable: بلٹ ان شارٹ کٹ سسٹم کو فعال کریں\n  enable_tooltip: >-\n    اگر آپ سیلین UI کلائنٹ کا استعمال کرتے ہوئے اپنے شارٹ کٹ سسٹم کو نافذ کریں\n    گے تو غیر فعال کریں\n  labels:\n    create_new_workspace: نیا ورک اسپیس بنائیں\n    cycle_stack_next: سائیکل اسٹیک اگلا\n    cycle_stack_prev: سائیکل اسٹیک پچھلا\n    cycle_wallpaper_next: اگلے وال پیپر میں تبدیل کریں\n    cycle_wallpaper_prev: پچھلے وال پیپر میں تبدیل کریں\n    decrease_height: اونچائی میں کمی\n    decrease_width: چوڑائی کو کم کرنا\n    destroy_current_workspace: موجودہ ورک اسپیس کو ختم کریں\n    focus_bottom: فوکس نیچے\n    focus_latest: تازہ ترین توجہ مرکوز کریں\n    focus_left: فوکس بائیں\n    focus_right: صحیح توجہ مرکوز کریں\n    focus_top: فوکس ٹاپ\n    increase_height: اونچائی میں اضافہ\n    increase_width: چوڑائی میں اضافہ\n    misc_force_quit: فورس چھوڑ دیں\n    misc_force_restart: زبردستی دوبارہ شروع کریں\n    misc_open_settings: کھلی ترتیبات\n    misc_toggle_lock_tracing: ٹوگل لاک ٹریسنگ (نوشتہ جات)\n    misc_toggle_win_event_tracing: ٹوگل ون ایونٹ ٹریسنگ (لاگ)\n    move_to_workspace: ورک اسپیس {{0}} میں منتقل کریں\n    move_window_down: ونڈو کو نیچے منتقل کریں\n    move_window_left: ونڈو کو بائیں طرف منتقل کریں\n    move_window_right: ونڈو کو دائیں طرف منتقل کریں\n    move_window_up: ونڈو کو اوپر منتقل کریں\n    pause_tiling: ٹائلنگ ونڈو مینیجر کو روکیں\n    reserve_bottom: ریزرو نیچے\n    reserve_float: ریزرو فلوٹ\n    reserve_left: ریزرو بائیں\n    reserve_right: ٹھیک ہے\n    reserve_stack: ریزرو اسٹیک\n    reserve_top: ریزرو ٹاپ\n    restore_sizes: سائز کو بحال کریں\n    send_to_workspace: ورک اسپیس کو بھیجیں {{0}}\n    start_weg_app: توجہ مرکوز کریں یا درخواست شروع کریں {{0}}\n    switch_to_next_workspace: اگلی ورک اسپیس پر سوئچ کریں\n    switch_to_previous_workspace: پچھلے ورک اسپیس پر سوئچ کریں\n    switch_workspace: ورک اسپیس {{0}} پر سوئچ کریں\n    toggle_float: ٹوگل ونڈو فلوٹ موڈ\n    toggle_monocle: ٹوگل ورک اسپیس مونوکل وضع\n  readonly_tooltip: یہ صرف ایک پڑھنے والا شارٹ کٹ ہے\n  reset: ڈیفالٹس کو دوبارہ ترتیب دیں\nsides:\n  bottom: نیچے\n  left: بائیں\n  right: دائیں\n  top: اوپر\ntoolbar:\n  auto_hide: آٹو چھپائیں\n  delay_to_hide: چھپانے میں تاخیر\n  delay_to_show: دکھانے میں تاخیر\n  dock_side: پوزیشن\n  enable: فینسی ٹول بار کو فعال کریں\n  hide_mode:\n    always: ہمیشہ\n    never: کبھی نہیں\n    on_overlap: اوورلیپ پر\n  item_size: آئٹم کا سائز\n  label: ٹول بار\n  margin: مارجن سائز\n  padding: پیڈنگ سائز\n  placeholder: {}\nupdate:\n  available: تازہ کاری دستیاب!\n  channel: چینل کو اپ ڈیٹ کریں\n  downloading: ڈاؤن لوڈ کرنا ...\nwall:\n  backgrounds: وال پیپر\n  blur: دھندلا\n  cancel: منسوخ کریں\n  close: بند کریں\n  collection_name: جمع کرنے کا نام\n  collections: مجموعے\n  contrast: اس کے برعکس\n  corrupted_wallpapers_message: 'خراب وال پیپرز ، ان کو حذف کرنے پر غور کریں:'\n  create: تخلیق کریں\n  create_collection: مجموعہ بنائیں\n  default_collection: پہلے سے طے شدہ مجموعہ\n  delete_collection: مجموعہ حذف کریں\n  edit_collection: مجموعہ میں ترمیم کریں\n  enable: وال پیپر مینیجر کو فعال کریں\n  extend: پرائمری میں توسیع کریں\n  fit:\n    contain: مشتمل\n    cover: کور\n    fill: پُر کریں\n  flipHorizontal: افقی پلٹائیں\n  flipVertical: عمودی پلٹائیں\n  generating_thumbnails: وال پیپر تھمب نیل تیار کرنا\n  hours: گھنٹے\n  interval: ہر ایک وال پیپر کو تبدیل کریں\n  minutes: منٹ\n  monitor_collection: مانیٹر کلیکشن\n  multimonitor_behaviour: کثیر الجہتی سلوک\n  muted: خاموش ویڈیو آڈیو\n  no_background: اس کے بجائے تھیم کے پس منظر کا استعمال کرتے ہوئے ، خالی سلائیڈ شو۔\n  no_collections: ابھی تک کوئی مجموعہ پیدا نہیں ہوا۔ ایک بنانے کے لئے + بٹن پر کلک کریں۔\n  objectFit: پس منظر فٹ\n  objectPosition: پس منظر کی پوزیشن\n  overlayColor: اوورلے رنگ\n  overlayMixBlendMode: اوورلے مکس مرکب موڈ\n  per_monitor: فی مانیٹر\n  playback: پلے بیک کی رفتار\n  position:\n    bottom: نیچے\n    center: مرکز\n    left: بائیں\n    right: دائیں\n    top: اوپر\n  processing_video: پروسیسنگ ویڈیو\n  random: سلائیڈ شو کو بے ترتیب بنائیں\n  saturation: سنترپتی\n  seconds: سیکنڈ\n  select_collection: مجموعہ منتخب کریں\n  thumbnail_generation_complete: تھمب نیل جنریشن مکمل\n  thumbnail_generation_finished: تھمب نیل جنریشن نے کامیابی کے ساتھ ختم کیا ہے\n  wallpaper_collection: وال پیپر مجموعہ\n  wallpaper_settings: وال پیپر کی ترتیبات\n  withOverlay: اوورلے کے ساتھ\n  workspace_collections: ورک اسپیس کلیکشن\nweg:\n  auto_hide: خود بخود چھپ جانا\n  delay_to_hide: چھپانے میں تاخیر\n  delay_to_show: دکھانے میں تاخیر\n  dock_side: پوزیشن\n  enable: ٹاسک بار کو فعال کریں\n  filtering: آئٹم فلٹرنگ\n  gap: گیپ\n  hide_mode:\n    always: ہمیشہ\n    never: کبھی نہیں\n    on_overlap: اوورلیپ پر\n  items:\n    gap: اشیاء کے درمیان جگہ\n    label: اشیاء\n    pinned_visibility:\n      always: ہمیشہ\n      label: پن کی ہوئی اشیاء کی مرئیت\n      when_primary: جب مانیٹر پرائمری ہو۔\n    show_instance_counter: کھلی ونڈوز کاؤنٹر دکھائیں\n    show_window_title: کھلی ونڈو ٹائٹل دکھائیں (صرف افقی)\n    size: آئٹم کا سائز\n    split_windows: اسپلٹ ونڈوز (ایک آئٹم فی ونڈو)\n    temporal_visibility:\n      all: تمام\n      label: ان پن کردہ آئٹمز کی مرئیت\n      on_monitor: مانیٹر پر\n    visible_separators: مرئی جداکار\n  label: ٹاسک بار\n  margin: مارجن\n  mode:\n    full_width: پوری سکرین کی چوڑائی\n    min_content: جتنا چھوٹا ہو سکتا ہے۔\n  padding: بھرتی\n  show_end_task: ٹاسک بار میں اختتامی کام دکھائیں\n  width: چوڑائی\nwelcome:\n  give_a_review: ایک جائزہ دیں\n  message: >-\n    سیلن UI ونڈوز کے لئے ایک مفت اور اوپن سورس ڈیسک ٹاپ ماحول ہے۔ یہاں آپ کا\n    مکمل کنٹرول ہے کہ آپ کا ڈیسک ٹاپ کس طرح نظر آتا ہے اور برتاؤ کرتا ہے ، جس سے\n    آپ اپنے ورک فلو اور انداز سے ملنے کے لئے ہر تفصیل کو اپنی مرضی کے مطابق\n    بناتے ہیں۔\n  ok: آئیے شروع کریں!\n  review: >-\n    اگر آپ سیلین UI کے استعمال سے لطف اندوز ہوتے ہیں تو ، اسٹور پر جائزہ لینے پر\n    غور کریں - آپ کی رائے منصوبے کو بڑھنے اور بہتر بنانے میں مدد دیتی ہے۔\n  title: سیلین UI میں خوش آمدید!\nwidget:\n  enable: اس ویجیٹ کو فعال کریں\n  enable_for_monitor: اس مانیٹر کو قابل بنائیں\n  instances: مثال کے طور پر\nwm:\n  animations:\n    duration: حرکت پذیری کا دورانیہ (ایم ایس)\n    ease_function: حرکت پذیری میں آسانی پیدا کرنا\n    enable: ونڈو کے متحرک تصاویر کو فعال کریں\n  author: مصنف\n  border:\n    enable: ونڈو کی بارڈر کو فعال کریں\n    offset: بارڈر آفسیٹ\n    width: بارڈر کی چوڑائی\n  description: تفصیل\n  drag_behavior: ڈریگ سلوک\n  drag_behavior_options:\n    sort: ترتیب دیں (گھسیٹتے وقت ونڈوز کو دوبارہ ترتیب دیں)\n    swap: تبادلہ (ڈریگ اینڈ پر ونڈوز تبدیل)\n  enable: ٹائلنگ ونڈو مینیجر کو فعال کریں\n  layout: ترتیب\n  resize_delta: ڈیلٹا کا سائز (٪)\n  space_between_containers: کنٹینرز کے درمیان جگہ\n  workspace_offset: ورک اسپیس آفسیٹ (مارجن)\n  workspace_padding: ورک اسپیس پیڈنگ\n'yes': ہاں\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/uz.yml",
    "content": "action:\n  confirm: Ishonasizmi?\n  confirm_body: Ushbu harakatni bekor qilib bo'lmaydi.\napps_configurations:\n  app:\n    bindings: Bog'lash (eslatma ikkalasi ham talab qilinadi)\n    category: Tili\n    category_placeholder: Hech biri\n    monitor: Monitor\n    monitor_placeholder: Hech biri\n    name: Ism\n    ok_create: Yaratmoq\n    ok_edit: Yangilamoq\n    ok_readonly: Yangi sifatida tahrirlash\n    options:\n      NoInteractive: Interaktiv interfaol yo'q\n      VdPinned: Barcha ish joylarida ko'rsatish\n      WmFloat: TWM - suzishni boshlang\n      WmForce: TWM - kuchni boshqarish\n      WmUnmanage: TWM - Ungelaj\n    options_label: Qo'shimcha variantlar\n    title_create: '{{Ism}} yaratish'\n    title_edit: '{{Ismi}} tahrirlash'\n    title_readonly: Ko'rish {{ism}}\n    weg_options_label: Dok / vazifalar paneli\n    wm_options_label: Oyna boshqaruvchisi variantlari\n    workspace: Ish maydoni\n    workspace_placeholder: Hech biri\n  bundled_msg: >-\n    Ushbu yig'ilgan konfiguratsiyalar tahrirlanmaydi va sizga eng yaxshi\n    tajribani taqdim etishsiz taqdim etish uchun mo'ljallangan. Ular avtomatik\n    ravishda siz uchun eng keng tarqalgan dasturlarni sozlaydi.\n  bundled_title: Dastur konfigurasi Seelen bilan bog'langan\n  confirm_delete: Ushbu konfiguratsiyani o'chirishni xohlayotganingizga ishonchingiz komilmi?\n  confirm_delete_title: O'chirishni tasdiqlang\n  delete: O'chirmoq\n  export: Eksport\n  export_full: Ilova orqali eksport sozlamalari\n  extra_info: >-\n    Seelen UI har bir ilovada faqat bitta identifikatordan foydalanadi (birinchi\n    moslik topildi), shuning uchun qanday belgilanish tartibi muhim, oxirgi\n    qoʻshilganlar ustuvor boʻladi, chunki jadval sukut boʻyicha oxirgidan\n    eskisiga saralanganligini unutmang.\n  identifier:\n    add_block: Blokni qo'shish\n    and: Va\n    id: Identifikator\n    kind: Aniqlang\n    matching_strategy: Mos keladigan strategiya\n    matching_strategy_option:\n      contains: Tarkibida\n      ends_with: bilan tugaydi\n      equals: Teng\n      regex: Muntazam ifoda\n      starts_with: bilan boshlanadi\n    negation: Mos kelmaslik\n    or: Yoki\n    remove: Blokni o'chirish\n    type:\n      class: Sinf\n      exe: Exe\n      path: Yo'l\n      title: Sarlavha\n  import: Import\n  import_full: Ilova orqali import sozlamalari\n  new: Yangi\n  search: Qidirmoq\n  swap: Almashmoq\ncancel: Bekor qilmoq\nclose: Yaqin\ndelete: O'chirmoq\ndevtools:\n  app_folders: Ilova papkalari\n  custom_config_file: Custom Config faylini yuklang\n  data_folder: Ma'lumotlar papkasi\n  enable: Ishlab chiqaruvchi vositalarni yoqish\n  install_folder: O'rnatish papkasini o'rnatish\n  load: Yuklamoq\n  settings_file: Sozlamalar fayli\n  simulate_perm:\n    label: Vidjetga ruxsat so‘rovini simulyatsiya qilish\n    result_allowed: ✓ Ruxsat berilgan\n    result_denied: ✗ Ruxsat rad etildi\n    trigger: Simulyatsiya qilish\n    widget_id_placeholder: '@muallif/vidjet nomi'\nextras:\n  clear_icons: Tozalash tizimi piktogrammalari kesh\n  clear_icons_tooltip: >-\n    Barcha vidjetlarga to'liq ta'sir qilish uchun qayta boshlash talab qilinishi\n    mumkin\n  exit: Chiqish / chiqish\n  links: Rasmiy havolalar\n  relaunch: Qolma\n  version: Versiya\n  version_fixed: >-\n    Ilova va WebView2 Runtime versiyalari aniqlangan. Bu shuni anglatadiki,\n    ilova yangilanishlarni olmaydi va WebView2 Runtime Windows yangilanishlari\n    bilan avtomatik ravishda yangilanmaydi.\ngeneral:\n  accent_color: Urg'u rangi\n  date_format: Sana formati\n  date_format_how_to: Sana formatini qanday yozish kerak?\n  hardware_acceleration: Uskuna tezlashuvi\n  hardware_acceleration_description: >-\n    Uskuna tezlashtirishni o'chirib qo'yish xotiradan foydalanishni kamaytiradi,\n    lekin ishlash bilan bog'liq muammolarga olib kelishi mumkin. Jonli fon\n    rasmlarini ishlatmasangiz, o'chirib qo'yish xavfsiz.\n  icon_pack:\n    available: Mavjud piktogramma to'plamlari\n    selected: Faol piktogramma to'plamlari\n  language: Til\n  monday: dushanba\n  performance_mode:\n    on_battery: Batareyada\n    on_energy_saver: Energiya tejash bo'yicha\n    options:\n      disabled: Nogiron\n      extreme: Haddan tashqari\n      minimal: Minimal\n    plugged: Ulangan yoki zaryadlash\n  polling_interval: Tizim so'rovi oralig'i\n  polling_interval_description: >-\n    Seelen UI protsessor, operativ xotira, tarmoq va disk faoliyati kabi tizim\n    resurslarini qanchalik tez-tez tekshiradi (soniyalarda). Kichikroq raqam\n    tez-tez yangilanishlarni anglatadi, lekin biroz yuqoriroq resurslardan\n    foydalanish.\n  saturday: shanba\n  start_of_week: Hafta boshi\n  startup: Ishga tushirishda ishlaydimi?\n  sunday: yakshanba\n  theme:\n    available: Mavjud mavzular\n    selected: Faol mavzular\nheader:\n  labels:\n    config: Konfiguratsiya\n    developer: Ishlab chiquvchilar uchun\n    extras: Ekstraza\n    general: Umumiy\n    home: Uy\n    icon_pack_editor: Keshlangan piktogrammalar\n    iconpack: Ikonka paketlari\n    monitors: Monitorlar\n    plugin: Plaginlar\n    resources: Resurslar\n    shortcuts: Yorliqlar\n    soundpack: Ovozli paketlar\n    specific_apps: Ilova orqali sozlash\n    theme: Mavzular\n    virtual_desk: Virtual ish stollari\n    wallpaper: Fon rasmlari\n    widget: Vidjetlar\nhome:\n  new_resources: Yangi manbalar\ninherit: Meros qilib olmoq\ninProgress: Jarayonda...\ninsert: Qo'shmoq\nloading: Yuklash ...\nmiscellaneous: Har xil\nmonitors_configurations:\n  label: '{{Indeks}}'\nmore: Ko'proq\n'no': Yo'q\nopen: Ochiq\nquit: Ketmoq\nremove: Olib tashlamoq\nreset_all_to_default: Barcha standart qiymatlarni qayta o'rnating\nreset_to_default: Standart qiymatni qayta o'rnating\nresources:\n  app_outdated: Ushbu resurs Seelen UIning yangi versiyasini kerakli ishlashga talab qiladi.\n  corrupted_wallpaper: >-\n    Eskizni chiqarib boʻlmadi - buzilgan yoki qoʻllab-quvvatlanmaydigan video\n    formati\n  delete: Resurslarni o'chirish\n  discover: Ko'proq resurslarni kashf eting\n  has_update: Yangilanish mavjud\n  high_impact: Ishlashning yuqori ta'siri\n  import_wallpapers: Mahalliy fon rasmlarini import qilish\n  open_folder: Resurslar jildini oching\n  outdated: >-\n    Ushbu manba Seelen UIning eski versiyasi uchun mo'ljallangan va to'g'ri\n    ishlamasligi mumkin.\n  see_on_website: Veb-saytda ko'ring\nreview_request:\n  not_now: Hozir emas\n  sure: Albatta!\n  title: Seelen UI yoqyapsizmi?\nsave: Tejash\nsave_and_restart: Saqlang va qayta yoqing\nsearch: Qidiruv\nsee_more: Ko'proq ko'rish\nshortcuts:\n  duplicate_error: Bu yorliq takrorlanadi\n  enable: Ichki yorliqlar tizimini yoqing\n  enable_tooltip: >-\n    Seelen Ui mijozidan foydalanib o'zingizning yorliq tizimingizni amalga\n    oshirsangiz, o'chiring\n  labels:\n    create_new_workspace: Yangi ish maydonini yarating\n    cycle_stack_next: Keyingi tsikl stakan\n    cycle_stack_prev: Oldingi tsikl stakan\n    cycle_wallpaper_next: Keyingi devor qog'ozi o'zgartiring\n    cycle_wallpaper_prev: Oldingi fon rasmiga o'zgartirish\n    decrease_height: Balandlikni kamaytirish\n    decrease_width: Kenglikni kamaytirish\n    destroy_current_workspace: Hozirgi ish maydonini yo'q qiling\n    focus_bottom: Fokus suvi\n    focus_latest: Eng so'nggi\n    focus_left: Fince chap\n    focus_right: Joylashtirish\n    focus_top: Face Top\n    increase_height: Balandlikni oshirish\n    increase_width: Kenglikni oshirish\n    misc_force_quit: Kuch berish\n    misc_force_restart: Kuchni qayta boshlang\n    misc_open_settings: Ochiq sozlamalar\n    misc_toggle_lock_tracing: Qulfni qaytarish (jurnallar)\n    misc_toggle_win_event_tracing: Tap The The The The Trecing (jurnallar)\n    move_to_workspace: Ish maydoni uchun {{0}}\n    move_window_down: Oynani pastga siljiting\n    move_window_left: Oynani chapga siljiting\n    move_window_right: Oynani o'ngga siljiting\n    move_window_up: Oynani yuqoriga siljiting\n    pause_tiling: Tiling oynasi menejeri\n    reserve_bottom: Zaxira pastki\n    reserve_float: Suzmoq\n    reserve_left: Zaxira chap\n    reserve_right: Qo'riqxona\n    reserve_stack: Zaxira to'plami\n    reserve_top: Zaxira tepasi\n    restore_sizes: O'lchamlarini tiklash\n    send_to_workspace: Ish maydoni uchun {{0}}\n    start_weg_app: Fokus yoki dasturni boshlash {{0}}\n    switch_to_next_workspace: Keyingi ish joyiga o'tish\n    switch_to_previous_workspace: Oldingi ish joyiga o'tish\n    switch_workspace: Ish maydoni {{0}}\n    toggle_float: Oynaning sath rejimini o'zgartiring\n    toggle_monocle: Ish maydoni Monokle rejimini o'zgartiring\n  readonly_tooltip: Bu faqat o'qish faqat yorliqidir\n  reset: Standartlarga qayta o'rnating\nsides:\n  bottom: Pastki\n  left: Chapda\n  right: To'g'ri\n  top: Eng yuqori\ntoolbar:\n  auto_hide: Avtomatik yashirish\n  delay_to_hide: Yashirish uchun kechikish\n  delay_to_show: Ko'rsatish uchun kechikish\n  dock_side: Pozitsiya\n  enable: Fashr asboblar panelini yoqing\n  hide_mode:\n    always: Har doim\n    never: Hech qachon\n    on_overlap: Bir-biriga yopishgan holda\n  item_size: Element hajmi\n  label: Asboblar paneli\n  margin: Marja hajmi\n  padding: To'ldirish hajmi\n  placeholder: {}\nupdate:\n  available: Yangilanish mavjud!\n  channel: Kanalni yangilash\n  downloading: Yuklab olish ...\nwall:\n  backgrounds: Fon rasmlari\n  blur: Puflamoq\n  cancel: Bekor qilish\n  close: Yopish\n  collection_name: To'plam nomi\n  collections: To'plamlar\n  contrast: Qarama-qarshilik\n  corrupted_wallpapers_message: 'Buzilgan fon rasmlari, ularni o''chirishni o''ylab ko''ring:'\n  create: Yaratish\n  create_collection: To'plam yaratish\n  default_collection: Birlamchi toʻplam\n  delete_collection: To'plamni o'chirish\n  edit_collection: To'plamni tahrirlash\n  enable: Fon rasmi menejerini yoqing\n  extend: Birlamchini kengaytiring\n  fit:\n    contain: O'z ichiga olgan\n    cover: Qopqoq\n    fill: To'ldirmoq\n  flipHorizontal: Gorizontalental\n  flipVertical: Vertikal vertikal\n  generating_thumbnails: Fon rasmi eskizlari yaratilmoqda\n  hours: soat\n  interval: Har bir devor rasmini o'zgartiring\n  minutes: daqiqa\n  monitor_collection: Monitor to'plami\n  multimonitor_behaviour: Multimonitor xatti-harakati\n  muted: Ovozni o'chirish\n  no_background: O'rniga slaydshown, ularning o'rniga formatdan foydalanib.\n  no_collections: >-\n    Hozircha hech qanday kolleksiya yaratilmagan. Uni yaratish uchun + tugmasini\n    bosing.\n  objectFit: Fon mos\n  objectPosition: Fon holati\n  overlayColor: Overlay rang\n  overlayMixBlendMode: Ortiqcha aralashtirish rejimini aralashtiring\n  per_monitor: Monitor uchun\n  playback: Qayta tinglash tezligi\n  position:\n    bottom: Pastki\n    center: Markaz\n    left: Chapda\n    right: To'g'ri\n    top: Eng yuqori\n  processing_video: Videoga ishlov berilmoqda\n  random: Tasodifiy slayd-shou\n  saturation: To'yinganlik\n  seconds: soniya\n  select_collection: To'plamni tanlang\n  thumbnail_generation_complete: Eskiz yaratish tugallandi\n  thumbnail_generation_finished: Eskiz yaratish muvaffaqiyatli yakunlandi\n  wallpaper_collection: Fon rasmi to'plami\n  wallpaper_settings: Fon rasmi sozlamalari\n  withOverlay: Qopqoq bilan\n  workspace_collections: Ish maydoni to'plamlari\nweg:\n  auto_hide: Avtomatik yashirish\n  delay_to_hide: Yashirish uchun kechikish\n  delay_to_show: Ko'rsatish uchun kechikish\n  dock_side: Pozitsiya\n  enable: Dock / vazifalar panelini yoqing\n  filtering: Mahsulotni filtrlash\n  gap: Bo'shliq\n  hide_mode:\n    always: Har doim\n    never: Hech qachon\n    on_overlap: Bir-biriga yopishgan holda\n  items:\n    gap: Buyumlar orasidagi bo'shliq\n    label: Narsalar\n    pinned_visibility:\n      always: Har doim\n      label: Belgilangan elementlarning ko'rinishi\n      when_primary: Monitor asosiy bo'lganda\n    show_instance_counter: Ochiq Windows hisoblagichini ko'rsatish\n    show_window_title: Ochiq deraza nomini ko'rsatish (faqat gorizontal)\n    size: Element hajmi\n    split_windows: Windowsni ajratish (har bir oynada bitta element)\n    temporal_visibility:\n      all: Hammasi\n      label: Ochilmagan elementlarning ko'rinishi\n      on_monitor: Monitorda\n    visible_separators: Ko'rinadigan ajratuvchi\n  label: Dok / vazifalar paneli\n  margin: Marja\n  mode:\n    full_width: To'liq ekran kengligi\n    min_content: Iloji boricha kichik\n  padding: Plomba\n  show_end_task: Vazifalar panelida tugatish vazifasini ko'rsating\n  width: Kenglik\nwelcome:\n  give_a_review: Sharh bering\n  message: >-\n    Seelen UI Windows uchun bepul va ochiq manbali ish stoli muhitidir. Bu yerda\n    siz ish stolingiz qanday koʻrinishi va oʻzini tutishini toʻliq nazorat qila\n    olasiz, bu sizga har bir detalni ish jarayoni va uslubingizga mos ravishda\n    sozlash imkonini beradi.\n  ok: Boshlaymiz!\n  review: >-\n    Agar siz Seelen UI-dan foydalanishni yoqtirsangiz, do'konda sharh qoldiring\n    - fikr-mulohazalaringiz loyihani rivojlantirish va yaxshilashga yordam\n    beradi.\n  title: Seelen UI-ga xush kelibsiz!\nwidget:\n  enable: Bu vidjetni yoqing\n  enable_for_monitor: Ushbu monitorga yoqish\n  instances: Mustaqil holatlar\nwm:\n  animations:\n    duration: Animatsiya muddati (MS)\n    ease_function: Animatsiya engillashtirish funktsiyasi\n    enable: Oyna animatsiyalarini yoqing\n  author: Muallif\n  border:\n    enable: Oynaning chegarasini yoqing\n    offset: Chegara ofset\n    width: Chegara kengligi\n  description: Tavsif\n  drag_behavior: Harakatni tortish\n  drag_behavior_options:\n    sort: Saralash (tortish paytida oynalarni qayta tartiblash)\n    swap: Almashtirish (drag oxirida oynalarni almashtirish)\n  enable: Tiling oyna boshqaruvchisini yoqing\n  layout: Tartib\n  resize_delta: Delta (%) ni o'zgartiring\n  space_between_containers: Idishlar orasidagi bo'shliq\n  workspace_offset: Ishxonalar ofset (marjalar)\n  workspace_padding: Ish plakding\n'yes': Ha\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/vi.yml",
    "content": "action:\n  confirm: Bạn có chắc không?\n  confirm_body: Hành động này không thể được hoàn tác.\napps_configurations:\n  app:\n    bindings: Binding (Lưu ý cả hai tùy chọn đều được yêu cầu)\n    category: Loại\n    category_placeholder: Không có\n    monitor: Màn hình\n    monitor_placeholder: Không có\n    name: Tên\n    ok_create: Tạo nên\n    ok_edit: Cập nhật\n    ok_readonly: Chỉnh sửa như mới\n    options:\n      NoInteractive: Không tương tác\n      VdPinned: Hiển thị trong tất cả không gian làm việc\n      WmFloat: Twm - Bắt đầu nổi\n      WmForce: Twm - Quản lý lực lượng\n      WmUnmanage: Twm - Bỏ quản lý\n    options_label: Tùy chọn bổ sung\n    title_create: Tạo {{name}}\n    title_edit: Chỉnh sửa {{name}}\n    title_readonly: Xem {{name}}\n    weg_options_label: Tùy chọn Thanh điều khiển/Thanh nhiệm vụ\n    wm_options_label: Tùy chọn Trình quản lý cửa sổ\n    workspace: Không gian làm việc\n    workspace_placeholder: Không có\n  bundled_msg: >-\n    Các cấu hình đi kèm này không thể chỉnh sửa và được thiết kế để cung cấp cho\n    bạn trải nghiệm tốt nhất mà không cần tùy chỉnh. Chúng tự động định cấu hình\n    các ứng dụng phổ biến nhất cho bạn.\n  bundled_title: Ứng dụng Cấu hình đi kèm với Seelen\n  confirm_delete: Bạn có chắc bạn muốn xóa cấu hình/s này không?\n  confirm_delete_title: Xác nhận xóa\n  delete: Xóa bỏ\n  export: Xuất khẩu\n  export_full: Cài đặt xuất theo ứng dụng\n  extra_info: >-\n    Seelen UI chỉ sử dụng một định danh cho mỗi ứng dụng (tìm thấy trận đấu đầu\n    tiên) vì vậy thứ tự được cụ thể là quan trọng, phần được thêm mới nhất sẽ\n    được ưu tiên, vì ghi chú được sắp xếp theo mặc định từ mới nhất đến cũ.\n  identifier:\n    add_block: Thêm khối\n    and: VÀ\n    id: Định danh\n    kind: Xác định bởi\n    matching_strategy: Chiến lược phù hợp\n    matching_strategy_option:\n      contains: Chứa\n      ends_with: Kết thúc bằng\n      equals: Bằng\n      regex: Biểu thức chính quy\n      starts_with: Bắt đầu với\n    negation: Phủ nhận sự phù hợp\n    or: HOẶC\n    remove: Xóa khối\n    type:\n      class: Lớp học\n      exe: Exe\n      path: Con đường\n      title: Tiêu đề\n  import: Nhập khẩu\n  import_full: Nhập cài đặt theo ứng dụng\n  new: Mới\n  search: Tìm kiếm\n  swap: Tráo đổi\ncancel: Hủy bỏ\nclose: Đóng\ndelete: Xóa bỏ\ndevtools:\n  app_folders: Thư mục ứng dụng\n  custom_config_file: Tải tệp cấu hình tùy chỉnh\n  data_folder: Thư mục dữ liệu\n  enable: Kích hoạt các công cụ nhà phát triển\n  install_folder: Thư mục cài đặt\n  load: Trọng tải\n  settings_file: Cài đặt tập tin\n  simulate_perm:\n    label: Mô phỏng yêu cầu cấp phép widget\n    result_allowed: ✓ Đã được cấp phép\n    result_denied: ✗ Quyền bị từ chối\n    trigger: mô phỏng\n    widget_id_placeholder: '@tác giả/tên widget'\nextras:\n  clear_icons: Xóa bộ đệm biểu tượng hệ thống\n  clear_icons_tooltip: >-\n    Khởi động lại có thể được yêu cầu để có hiệu lực hoàn toàn trên tất cả các\n    tiện ích\n  exit: Thoát/Thoát\n  links: Liên kết chính thức\n  relaunch: Khởi động lại\n  version: Phiên bản\n  version_fixed: >-\n    Ứng dụng và phiên bản WebView2 Runtime đã được sửa. Điều này có nghĩa là ứng\n    dụng sẽ không nhận được bản cập nhật và WebView2 Runtime sẽ không được tự\n    động cập nhật với các bản cập nhật Windows.\ngeneral:\n  accent_color: Màu nhấn\n  date_format: Định dạng ngày\n  date_format_how_to: Làm thế nào để viết một định dạng ngày?\n  hardware_acceleration: Tăng tốc phần cứng\n  hardware_acceleration_description: >-\n    Việc tắt tính năng tăng tốc phần cứng sẽ giảm mức sử dụng bộ nhớ nhưng có\n    thể gây ra các vấn đề về hiệu suất. Có thể tắt an toàn nếu bạn không sử dụng\n    hình nền động.\n  icon_pack:\n    available: Gói biểu tượng có sẵn\n    selected: Gói biểu tượng hoạt động\n  language: Ngôn ngữ\n  monday: Thứ hai\n  performance_mode:\n    on_battery: Trên pin\n    on_energy_saver: Trên tiết kiệm năng lượng\n    options:\n      disabled: Tàn tật\n      extreme: Vô cùng\n      minimal: Tối thiểu\n    plugged: Cắm hoặc sạc\n  polling_interval: Khoảng thời gian thăm dò hệ thống\n  polling_interval_description: >-\n    Tần suất (tính bằng giây) Seelen UI kiểm tra các tài nguyên hệ thống như\n    hoạt động của CPU, RAM, mạng và ổ đĩa. Số lượng nhỏ hơn có nghĩa là cập nhật\n    thường xuyên hơn nhưng mức sử dụng tài nguyên cao hơn một chút.\n  saturday: Thứ bảy\n  start_of_week: Đầu tuần\n  startup: Chạy khi khởi động?\n  sunday: Chủ nhật\n  theme:\n    available: Chủ đề có sẵn\n    selected: Chủ đề đang hoạt động\nheader:\n  labels:\n    config: Cấu hình\n    developer: Cho các nhà phát triển\n    extras: Bổ sung\n    general: Tổng quan\n    home: Trang chủ\n    icon_pack_editor: Biểu tượng bộ nhớ cache\n    iconpack: Gói biểu tượng\n    monitors: Màn hình\n    plugin: Plugin\n    resources: Tài nguyên\n    shortcuts: Phím tắt\n    soundpack: Gói âm thanh\n    specific_apps: Cài đặt theo ứng dụng\n    theme: Chủ đề\n    virtual_desk: Máy tính để bàn ảo\n    wallpaper: Hình nền\n    widget: Widgets\nhome:\n  new_resources: Tài nguyên mới\ninherit: Kế thừa\ninProgress: Trong tiến trình...\ninsert: Chèn\nloading: Đang tải...\nmiscellaneous: Linh tinh\nmonitors_configurations:\n  label: Màn hình {{index}}\nmore: Hơn\n'no': KHÔNG\nopen: Mở\nquit: Từ bỏ\nremove: Di dời\nreset_all_to_default: Đặt lại tất cả về giá trị mặc định\nreset_to_default: Đặt lại về giá trị mặc định\nresources:\n  app_outdated: Tài nguyên này yêu cầu phiên bản UI Seelen mới hơn hoạt động đúng.\n  corrupted_wallpaper: >-\n    Không thể trích xuất hình thu nhỏ - định dạng video bị hỏng hoặc không được\n    hỗ trợ\n  delete: Xóa tài nguyên\n  discover: Khám phá thêm tài nguyên\n  has_update: Một bản cập nhật có sẵn\n  high_impact: Tác động cao đến hiệu suất\n  import_wallpapers: Nhập hình nền địa phương\n  open_folder: Mở thư mục tài nguyên\n  outdated: >-\n    Tài nguyên này được thiết kế cho phiên bản cũ của SEELEN UI và có thể không\n    hoạt động đúng.\n  see_on_website: Xem trên trang web\nreview_request:\n  not_now: Không phải bây giờ\n  sure: Chắc chắn!\n  title: Bạn thích giao diện người dùng Seelen?\nsave: Cứu\nsave_and_restart: Lưu & Khởi động lại\nsearch: Tìm kiếm\nsee_more: Xem thêm\nshortcuts:\n  duplicate_error: Phím tắt này bị trùng lặp\n  enable: Bật hệ thống phím tắt tích hợp\n  enable_tooltip: >-\n    Tắt nếu bạn sẽ triển khai hệ thống phím tắt của riêng mình bằng cách sử dụng\n    ứng dụng UI Seelen\n  labels:\n    create_new_workspace: Tạo không gian làm việc mới\n    cycle_stack_next: Chu kỳ ngăn xếp tiếp theo\n    cycle_stack_prev: Chu kỳ ngăn xếp trước\n    cycle_wallpaper_next: Thay đổi hình nền tiếp theo\n    cycle_wallpaper_prev: Thay đổi thành hình nền trước\n    decrease_height: Giảm chiều cao\n    decrease_width: Giảm chiều rộng\n    destroy_current_workspace: Phá hủy không gian làm việc hiện tại\n    focus_bottom: Focus bottom\n    focus_latest: Tập trung mới nhất\n    focus_left: Tập trung trái\n    focus_right: Tập trung đúng\n    focus_top: Tập trung hàng đầu\n    increase_height: Tăng chiều cao\n    increase_width: Tăng chiều rộng\n    misc_force_quit: BẮT ĐẦU\n    misc_force_restart: Bắt đầu lại\n    misc_open_settings: Mở cài đặt\n    misc_toggle_lock_tracing: Chuyển đổi dấu vết khóa (nhật ký)\n    misc_toggle_win_event_tracing: Chuyển đổi theo dõi sự kiện (nhật ký)\n    move_to_workspace: Di chuyển đến không gian làm việc {{0}}\n    move_window_down: Di chuyển cửa sổ xuống dưới\n    move_window_left: Di chuyển cửa sổ sang trái\n    move_window_right: Di chuyển cửa sổ sang phải\n    move_window_up: Chuyển cửa sổ sang đầu\n    pause_tiling: Tạm dừng Trình quản lý cửa sổ ốp lát\n    reserve_bottom: Dự trữ đáy\n    reserve_float: Dự trữ nổi\n    reserve_left: Dự trữ trái\n    reserve_right: Đặt trước đúng\n    reserve_stack: Ngân hàng dự trữ\n    reserve_top: Dự trữ hàng đầu\n    restore_sizes: Khôi phục kích thước\n    send_to_workspace: Gửi đến không gian làm việc {{0}}\n    start_weg_app: Ứng dụng tập trung hoặc bắt đầu {{0}}\n    switch_to_next_workspace: Chuyển sang không gian làm việc tiếp theo\n    switch_to_previous_workspace: Chuyển sang không gian làm việc trước đó\n    switch_workspace: Chuyển sang không gian làm việc {{0}}\n    toggle_float: Chuyển đổi chế độ nổi của cửa sổ\n    toggle_monocle: Chuyển đổi chế độ Monocle không gian làm việc\n  readonly_tooltip: Đây là một lối tắt chỉ đọc\n  reset: Đặt lại về mặc định\nsides:\n  bottom: Đáy\n  left: Bên trái\n  right: Phải\n  top: Đứng đầu\ntoolbar:\n  auto_hide: Tự động ẩn\n  delay_to_hide: Trì hoãn việc ẩn giấu\n  delay_to_show: Trì hoãn hiển thị\n  dock_side: Chức vụ\n  enable: Kích hoạt thanh công cụ ưa thích\n  hide_mode:\n    always: Luôn luôn\n    never: Không bao giờ\n    on_overlap: Trên sự chồng chéo\n  item_size: Kích thước mục\n  label: Thanh công cụ\n  margin: Kích thước lề\n  padding: Kích thước đệm\n  placeholder: {}\nupdate:\n  available: Cập nhật có sẵn!\n  channel: Cập nhật kênh\n  downloading: Tải xuống ...\nwall:\n  backgrounds: Hình nền\n  blur: Mờ\n  cancel: Hủy bỏ\n  close: Đóng\n  collection_name: Tên bộ sưu tập\n  collections: Bộ sưu tập\n  contrast: Sự tương phản\n  corrupted_wallpapers_message: 'Hình nền bị hỏng, hãy cân nhắc việc xóa những hình nền này:'\n  create: Tạo nên\n  create_collection: Tạo bộ sưu tập\n  default_collection: Bộ sưu tập mặc định\n  delete_collection: Xóa bộ sưu tập\n  edit_collection: Chỉnh sửa bộ sưu tập\n  enable: Bật trình quản lý hình nền\n  extend: Mở rộng chính\n  fit:\n    contain: Bao gồm\n    cover: Che phủ\n    fill: Đổ đầy\n  flipHorizontal: Lật ngang\n  flipVertical: Lật dọc\n  generating_thumbnails: Tạo hình thu nhỏ hình nền\n  hours: giờ\n  interval: Thay đổi hình nền mỗi\n  minutes: phút\n  monitor_collection: Bộ sưu tập giám sát\n  multimonitor_behaviour: Hành vi đa màn hình\n  muted: Tắt âm thanh video\n  no_background: Thay vào đó, trình chiếu trống, sử dụng nền của chủ đề.\n  no_collections: Chưa có bộ sưu tập nào được tạo. Nhấp vào nút + để tạo một cái.\n  objectFit: Phù hợp với nền\n  objectPosition: Vị trí nền\n  overlayColor: Màu lớp phủ\n  overlayMixBlendMode: Chế độ trộn trộn lớp phủ\n  per_monitor: Mỗi màn hình\n  playback: Tốc độ phát lại\n  position:\n    bottom: Đáy\n    center: Trung tâm\n    left: Bên trái\n    right: Phải\n    top: Đứng đầu\n  processing_video: Đang xử lý video\n  random: Slideshow ngẫu nhiên\n  saturation: Bão hòa\n  seconds: giây\n  select_collection: Chọn Bộ sưu tập\n  thumbnail_generation_complete: Hoàn tất việc tạo hình thu nhỏ\n  thumbnail_generation_finished: Việc tạo hình thu nhỏ đã kết thúc thành công\n  wallpaper_collection: Bộ sưu tập hình nền\n  wallpaper_settings: Cài đặt hình nền\n  withOverlay: Với lớp phủ\n  workspace_collections: Bộ sưu tập không gian làm việc\nweg:\n  auto_hide: Tự động ẩn\n  delay_to_hide: Trì hoãn việc ẩn giấu\n  delay_to_show: Trì hoãn hiển thị\n  dock_side: Chức vụ\n  enable: Bật Dock/Thanh tác vụ\n  filtering: Lọc vật phẩm\n  gap: Khoảng cách\n  hide_mode:\n    always: Luôn luôn\n    never: Không bao giờ\n    on_overlap: Trên sự chồng chéo\n  items:\n    gap: Không gian giữa các mặt hàng\n    label: Mặt hàng\n    pinned_visibility:\n      always: Luôn luôn\n      label: Hiển thị các mục được ghim\n      when_primary: Khi màn hình là chính\n    show_instance_counter: Hiển thị bộ đếm Windows mở\n    show_window_title: Hiển thị tiêu đề cửa sổ mở (chỉ ngang)\n    size: Kích thước mục\n    split_windows: Chia Windows (một mục trên mỗi cửa sổ)\n    temporal_visibility:\n      all: Tất cả\n      label: Hiển thị các mục đã được bỏ ghim\n      on_monitor: Trên màn hình\n    visible_separators: Phân tách có thể nhìn thấy\n  label: Dock/Thanh tác vụ\n  margin: Lề\n  mode:\n    full_width: Chiều rộng toàn màn hình\n    min_content: Nhỏ nhất có thể\n  padding: Đệm\n  show_end_task: Hiển thị nhiệm vụ kết thúc trong thanh tác vụ\n  width: Chiều rộng\nwelcome:\n  give_a_review: Đưa ra đánh giá\n  message: >-\n    Seelen UI là môi trường máy tính để bàn nguồn mở và miễn phí dành cho\n    Windows. Tại đây, bạn có toàn quyền kiểm soát giao diện và hoạt động của màn\n    hình, cho phép bạn tùy chỉnh mọi chi tiết để phù hợp với quy trình làm việc\n    và phong cách của mình.\n  ok: Hãy bắt đầu!\n  review: >-\n    Nếu bạn thích sử dụng Seelen UI, hãy cân nhắc để lại đánh giá trên cửa hàng\n    - phản hồi của bạn sẽ giúp dự án phát triển và cải thiện.\n  title: Chào mừng bạn đến với giao diện người dùng Seelen!\nwidget:\n  enable: Kích hoạt tiện ích này\n  enable_for_monitor: Bật trên màn hình này\n  instances: Trường hợp\nwm:\n  animations:\n    duration: Thời lượng hoạt hình (MS)\n    ease_function: Hoạt hình nới lỏng chức năng\n    enable: Bật hình ảnh động của Window\n  author: Tác giả\n  border:\n    enable: Bật biên giới của cửa sổ\n    offset: Biên giới bù đắp\n    width: Chiều rộng biên giới\n  description: Sự miêu tả\n  drag_behavior: Hành vi kéo\n  drag_behavior_options:\n    sort: Sắp xếp (sắp xếp lại các cửa sổ trong khi kéo)\n    swap: Hoán đổi (hoán đổi cửa sổ ở đầu kéo)\n  enable: Bật trình quản lý cửa sổ ốp lát\n  layout: Cách trình bày\n  resize_delta: Thay đổi kích thước delta (%)\n  space_between_containers: Không gian giữa các container\n  workspace_offset: Không gian làm việc bù (lề)\n  workspace_padding: Không gian làm việc đệm\n'yes': Đúng\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/yo.yml",
    "content": "action:\n  confirm: Ṣe o da ọ loju?\n  confirm_body: Iṣe yii ko le ṣe atunṣe.\napps_configurations:\n  app:\n    bindings: Dide (Ṣe akiyesi awọn aṣayan mejeeji ni a nilo)\n    category: Ẹya\n    category_placeholder: Ko si\n    monitor: Aṣabojuto\n    monitor_placeholder: Ko si\n    name: Orukọ\n    ok_create: Lati ṣẹda\n    ok_edit: Imudojuiwọn\n    ok_readonly: Satunkọ bi tuntun\n    options:\n      NoInteractive: Ko si ohun-ini\n      VdPinned: Fihan ninu gbogbo awọn ibi-iṣẹ\n      WmFloat: TWM - Bẹrẹ lilefoofo loju omi\n      WmForce: TWM - Ṣakoso ipa\n      WmUnmanage: TWM - aigbagbọ\n    options_label: Awọn aṣayan afikun\n    title_create: Ṣiṣẹda {Orukọ}}\n    title_edit: Ṣiṣatunṣe {{Orukọ}}\n    title_readonly: Wiwo {{Orukọ}}\n    weg_options_label: Dock / Awọn aṣayan Iṣẹ-ṣiṣe\n    wm_options_label: Awọn aṣayan Iṣẹ Iṣẹ window\n    workspace: Ibi-iṣẹ\n    workspace_placeholder: Ko si\n  bundled_msg: >-\n    Awọn atunto ti a dapọ mọ ko ṣe deede ati pe a ṣe apẹrẹ lati fun ọ ni iriri\n    ti o dara julọ laisi isọdi. Wọn tunto awọn ohun elo ti o wọpọ julọ fun ọ.\n  bundled_title: Ohun elo app ṣepọ pẹlu Selen\n  confirm_delete: Ṣe o da ọ loju pe o fẹ lati pa iṣeto rẹ / s?\n  confirm_delete_title: Jẹrisi paarẹ\n  delete: Paarẹ\n  export: Firanṣẹ si ilẹ okeere\n  export_full: Awọn eto okeere nipasẹ ohun elo\n  extra_info: >-\n    Sisen UI Nikan idakan kan fun app (ere akọkọ ti a rii) nitorinaa ni alaye ni\n    pataki, bi a ṣe le ṣe akiyesi tabili ti o wa ni lẹsẹsẹ nipasẹ aiyipada si\n    atijọ.\n  identifier:\n    add_block: Fi dègbé\n    and: Ati\n    id: Idamo\n    kind: Ṣe idanimọ nipasẹ\n    matching_strategy: Ibaamu iṣiro\n    matching_strategy_option:\n      contains: Ni ninu\n      ends_with: O pari pẹlu\n      equals: O dọgba\n      regex: Ikosile deede\n      starts_with: Bẹrẹ pẹlu\n    negation: Ibaramu tuntun\n    or: Tabi\n    remove: Paarẹ Brow\n    type:\n      class: Kilasi\n      exe: Exe\n      path: Ona\n      title: Akọle\n  import: Aṣagbewọle lati ilẹ okeere\n  import_full: Awọn Eto Wọle sii nipasẹ ohun elo\n  new: Tuntun\n  search: Ṣewadii\n  swap: Eepo\ncancel: Fagilee\nclose: Sunmọ\ndelete: Paarẹ\ndevtools:\n  app_folders: Awọn folda App\n  custom_config_file: Fifuye faili atunto aṣa\n  data_folder: Folda data\n  enable: Mu awọn irinṣẹ Oláàmúdà ṣiṣẹ\n  install_folder: Filese Fifi sori ẹrọ\n  load: Ẹru\n  settings_file: Faili eto\n  simulate_perm:\n    label: Ṣe afiwe Ibeere Gbigbanilaaye ẹrọ ailorukọ\n    result_allowed: ✓ Gbigbanilaaye\n    result_denied: ✗ Ti kọ igbanilaaye\n    trigger: Ṣe afarawe\n    widget_id_placeholder: '@author / ailorukọ-orukọ'\nextras:\n  clear_icons: Ko awọn ilana eto kuro\n  clear_icons_tooltip: A le nilo atunbere lati ṣe ipa ni kikun lori gbogbo awọn ẹrọ ailorukọ\n  exit: Duro / Jade\n  links: Awọn ọna asopọ osise\n  relaunch: Ṣadi\n  version: Ẹya\n  version_fixed: >-\n    Ohun elo naa ati awọn ẹya asiko ṣiṣe WebView2 jẹ ti o wa titi. Eyi tumọ si\n    pe ohun elo naa kii yoo gba awọn imudojuiwọn ati WebView2 Runtime kii yoo ni\n    imudojuiwọn laifọwọyi pẹlu awọn imudojuiwọn Windows.\ngeneral:\n  accent_color: Awọ ara\n  date_format: Ọna kika ọjọ\n  date_format_how_to: Bawo ni lati kọ ọna kika ọjọ kan?\n  hardware_acceleration: Hardware isare\n  hardware_acceleration_description: >-\n    Pa ohun elo imudara yoo dinku lilo iranti, ṣugbọn o le fa awọn ọran iṣẹ. Ṣe\n    ailewu lati mu ṣiṣẹ ti o ko ba lo iṣẹṣọ ogiri laaye.\n  icon_pack:\n    available: Awọn akopọ Aami ti o wa\n    selected: Awọn akopọ Aami ti nṣiṣe lọwọ\n  language: Ede\n  monday: Monday\n  performance_mode:\n    on_battery: Lori batiri\n    on_energy_saver: Lori Ipamọ Agbara\n    options:\n      disabled: Abirun\n      extreme: Ipẹkun\n      minimal: Ayọkuro\n    plugged: Pulọọgi tabi gbigba agbara\n  polling_interval: Aarin idibo eto\n  polling_interval_description: >-\n    Igba melo (ni iṣẹju-aaya) Seelen UI ṣe ayẹwo awọn orisun eto bii Sipiyu,\n    Ramu, nẹtiwọọki, ati iṣẹ disk. Nọmba ti o kere julọ tumọ si awọn imudojuiwọn\n    loorekoore, ṣugbọn lilo awọn orisun diẹ ga julọ.\n  saturday: Satidee\n  start_of_week: Ibẹrẹ Ọsẹ\n  startup: Ṣiṣe lori ibẹrẹ?\n  sunday: Sunday\n  theme:\n    available: Awọn akori to wa\n    selected: Awọn akori ti nṣiṣe lọwọ\nheader:\n  labels:\n    config: Awọn atunto\n    developer: Fun Awọn Difelopa\n    extras: Imurarẹ\n    general: Gbogboogbo\n    home: Ile\n    icon_pack_editor: Awọn aami ti a kọ silẹ\n    iconpack: Awọn akopọ aami\n    monitors: Awọn diirarito\n    plugin: Afikun\n    resources: Awọn orisun\n    shortcuts: Awọn ọna abuja\n    soundpack: Awọn akopọ ohun\n    specific_apps: Eto nipasẹ ohun elo\n    theme: Awọn akori\n    virtual_desk: Awọn orisun itẹwe foju\n    wallpaper: Ogiri ogiri\n    widget: Ẹrọ ẹrọ\nhome:\n  new_resources: Awọn orisun Tuntun\ninherit: Jogun\ninProgress: Ni ilọsiwaju ...\ninsert: Fisi\nloading: Loading ...\nmiscellaneous: Oriṣiriṣi\nmonitors_configurations:\n  label: Adirẹsi {{Ṣe atọka}}\nmore: Diẹ sii\n'no': Kọ\nopen: Ṣii\nquit: Lọ kuro\nremove: Yọkuro\nreset_all_to_default: Tun gbogbo rẹ si awọn iye aifọwọyi\nreset_to_default: Tunto si iye aiyipada\nresources:\n  app_outdated: Afikun yii nilo ẹya tuntun ti Sisen UI lati ṣiṣẹ daradara.\n  corrupted_wallpaper: >-\n    Kuna lati jade eekanna atanpako - ibajẹ tabi ọna kika fidio ti ko ṣe\n    atilẹyin\n  delete: Paarẹ awọn olu\n  discover: Ṣawari awọn orisun diẹ sii\n  has_update: Imudojuiwọn wa\n  high_impact: Ipa giga lori iṣẹ ṣiṣe\n  import_wallpapers: Gbe awọn iṣẹṣọ ogiri agbegbe wọle\n  open_folder: Ṣii folda Ipilẹṣẹ\n  outdated: A ṣe orisun yii fun ẹya atijọ ti Silen Ui ati pe ko le ma ṣiṣẹ daradara.\n  see_on_website: Wo lori aaye ayelujara\nreview_request:\n  not_now: Ko bayi\n  sure: Daju!\n  title: Ngbadun Seelen UI?\nsave: Fipamọ\nsave_and_restart: Fipamọ & Tun bẹrẹ\nsearch: Wa\nsee_more: Wo diẹ sii\nshortcuts:\n  duplicate_error: Ọna abuja yii jẹ pidánpidán\n  enable: Mu ṣiṣẹ eto awọn ọna abuja\n  enable_tooltip: Muu Ti o ba yoo ṣe eto awọn ọna ọna abuja tirẹ nipa lilo alajọṣepọ UI\n  labels:\n    create_new_workspace: Ṣẹda ibi-iṣẹ tuntun\n    cycle_stack_next: Awọn akopọ iyipo atẹle\n    cycle_stack_prev: Awọn akopọ ọmọ ni iṣaaju\n    cycle_wallpaper_next: Yipada si iṣẹṣọ ogiri atẹle\n    cycle_wallpaper_prev: Yipada si iṣẹṣọ ogiri iṣaaju\n    decrease_height: Dinku iga\n    decrease_width: DIVEELE WA\n    destroy_current_workspace: Pa ip [lọwọlọwọ run\n    focus_bottom: Idojukọ Idojukọ\n    focus_latest: Idojukọ\n    focus_left: Idojukọ osi\n    focus_right: Dojukọ ọtun\n    focus_top: Dojukọ oke\n    increase_height: Igasoke giga\n    increase_width: Gbooro\n    misc_force_quit: Fi agbara mu\n    misc_force_restart: Fi ipa mu\n    misc_open_settings: Ṣi Eto\n    misc_toggle_lock_tracing: Titiipa titiipa (awọn àkọọlẹ)\n    misc_toggle_win_event_tracing: Tongo Win Iṣẹlẹ iṣẹlẹ (awọn àkọọlẹ)\n    move_to_workspace: Gbe lọ si ibi-iṣẹ {{0}\n    move_window_down: Gbe window si isalẹ\n    move_window_left: Gbe window si osi\n    move_window_right: Gbe window si ọtun\n    move_window_up: Gbe window si oke\n    pause_tiling: Sinmi Took Window Manace\n    reserve_bottom: Isalẹ ifipamọ\n    reserve_float: Dase leefofo loju omi\n    reserve_left: Reserve osi\n    reserve_right: Reserve ọtun\n    reserve_stack: Ipilẹ akopọ\n    reserve_top: Reserve oke\n    restore_sizes: Pada awọn titobi\n    send_to_workspace: Firanṣẹ si Accepace {{0}\n    start_weg_app: Idojukọ tabi bẹrẹ ohun elo {0}}\n    switch_to_next_workspace: Yipada si ibi-iṣẹ t'okan\n    switch_to_previous_workspace: Yipada si ibi-iṣẹ iṣaaju\n    switch_workspace: Yipada si ibi-iṣẹ {0}\n    toggle_float: Oniropin window leefofo loju omi\n    toggle_monocle: Toggle Servicepace Ipo Monocle\n  readonly_tooltip: Eyi jẹ ọna abuja-kika\n  reset: Tun si awọn aseku\nsides:\n  bottom: Isalẹ\n  left: Osi\n  right: Titọ\n  top: Oke\ntoolbar:\n  auto_hide: Ibi ipamọ aifọwọyi\n  delay_to_hide: Idaduro lati tọju\n  delay_to_show: Idaduro lati ṣafihan\n  dock_side: Ipo\n  enable: Mu ṣiṣẹ irinṣẹ irinṣẹ Fancy ṣiṣẹ\n  hide_mode:\n    always: Nigbagbogbo\n    never: Kò\n    on_overlap: Lori agbekọja\n  item_size: Iwọn Nkan\n  label: Ọpa irinṣẹ\n  margin: Ala Iwon\n  padding: Padding Iwon\n  placeholder: {}\nupdate:\n  available: Imudojuiwọn wa!\n  channel: Ohun elo imudojuiwọn\n  downloading: Gbigba lati ayelujara ...\nwall:\n  backgrounds: Ogiri ogiri\n  blur: Abẹ\n  cancel: Fagilee\n  close: Sunmọ\n  collection_name: Orukọ gbigba\n  collections: Awọn akojọpọ\n  contrast: Ifiwera\n  corrupted_wallpapers_message: 'Awọn iṣẹṣọ ogiri ti bajẹ, ronu piparẹ awọn wọnyi:'\n  create: Ṣẹda\n  create_collection: Ṣẹda Gbigba\n  default_collection: Gbigba aiyipada\n  delete_collection: Pa Gbigba\n  edit_collection: Ṣatunkọ Gbigba\n  enable: Mu oluṣakoso ogiri ṣiṣẹ\n  extend: Fa akọkọ\n  fit:\n    contain: Ni ninu\n    cover: Ideri\n    fill: Kun\n  flipHorizontal: Isipade pejọ\n  flipVertical: Yi inaro\n  generating_thumbnails: Awọn eekanna atanpako Iṣẹṣọ ogiri\n  hours: wakati\n  interval: Ayipada ogiri ni gbogbo\n  minutes: iṣẹju\n  monitor_collection: Atẹle Gbigba\n  multimonitor_behaviour: Multimonitor Ihuwasi\n  muted: Free Voio fidio\n  no_background: Sifo Yisile, lilo abẹlẹ idile dipo.\n  no_collections: Ko si awọn akojọpọ ti a ṣẹda sibẹsibẹ. Tẹ bọtini + lati ṣẹda ọkan.\n  objectFit: Abẹla abẹlẹ\n  objectPosition: Ipo abẹlẹ\n  overlayColor: Awọ botay\n  overlayMixBlendMode: Igbẹpọ dapọ Ipo Ipa\n  per_monitor: Fun atẹle\n  playback: Iyara ṣiṣiṣẹsẹhin\n  position:\n    bottom: Isalẹ\n    center: Ile-iṣẹ\n    left: Osi\n    right: Titọ\n    top: Oke\n  processing_video: Fidio ti n ṣiṣẹ\n  random: Afikun ifaworanhan\n  saturation: Itẹlọrun\n  seconds: aaya\n  select_collection: Yan Gbigba\n  thumbnail_generation_complete: Ipilẹ eekanna atanpako Pari\n  thumbnail_generation_finished: Iran eekanna atanpako ti pari ni aṣeyọri\n  wallpaper_collection: Gbigba ogiri\n  wallpaper_settings: Eto Iṣẹṣọ ogiri\n  withOverlay: Pẹlu apọju\n  workspace_collections: Awọn akojọpọ aaye iṣẹ\nweg:\n  auto_hide: Ibi ipamọ aifọwọyi\n  delay_to_hide: Idaduro lati tọju\n  delay_to_show: Idaduro lati ṣafihan\n  dock_side: Ipo\n  enable: Muu dock / forkbar\n  filtering: Sisẹ Nkan\n  gap: Alafo\n  hide_mode:\n    always: Nigbagbogbo\n    never: Kò\n    on_overlap: Lori agbekọja\n  items:\n    gap: Aaye laarin awọn ohun kan\n    label: Awọn ohun\n    pinned_visibility:\n      always: Nigbagbogbo\n      label: Pini Awọn nkan Hihan\n      when_primary: Nigbati atẹle jẹ akọkọ\n    show_instance_counter: Fihan ṣiṣi Windows\n    show_window_title: Ṣe afihan akọle window (petele nikan)\n    size: Iwọn nkan\n    split_windows: Pipin Windows (ohun kan fun window)\n    temporal_visibility:\n      all: Gbogbo\n      label: Hihan Awọn nkan ti a ko pin\n      on_monitor: Lori Atẹle\n    visible_separators: Awọn iyatọ ti o han\n  label: Dock / forkbar\n  margin: Ẹlẹsin\n  mode:\n    full_width: Iboju kikun iwọn\n    min_content: Kekere bi o ti le jẹ\n  padding: Fidigọdi silẹ\n  show_end_task: Fihan iṣẹ ṣiṣe ni iṣẹ-ṣiṣe\n  width: Fifẹ\nwelcome:\n  give_a_review: Fun Atunwo\n  message: >-\n    Seelen UI jẹ ọfẹ ati agbegbe tabili orisun ṣiṣi fun Windows. Nibi o ni\n    iṣakoso ni kikun lori bii tabili tabili rẹ ṣe n wo ati ihuwasi, gbigba ọ\n    laaye lati ṣe akanṣe gbogbo alaye lati baamu iṣan-iṣẹ ati aṣa rẹ.\n  ok: Jẹ ká bẹrẹ!\n  review: >-\n    Ti o ba gbadun lilo Seelen UI, ronu fifi atunyẹwo silẹ lori ile itaja - esi\n    rẹ ṣe iranlọwọ fun iṣẹ akanṣe lati dagba ati ilọsiwaju.\n  title: Kaabọ si Seelen UI!\nwidget:\n  enable: Mu ẹrọ ailorukọ yii ṣiṣẹ\n  enable_for_monitor: Mu ṣiṣẹ lori atẹle yii\n  instances: Awọn iṣẹlẹ\nwm:\n  animations:\n    duration: Akoko Idaraya (MS)\n    ease_function: Iṣẹ to dara pupọ\n    enable: Jeki awọn ohun idanilaraya window\n  author: Onkọwe ọkunrin\n  border:\n    enable: Jeki aala window\n    offset: Iparun aala\n    width: Iwọn aala\n  description: Isapejuwe\n  drag_behavior: Fa Iwa\n  drag_behavior_options:\n    sort: Too (tunto awọn window lakoko ti o nfa)\n    swap: Paṣipaa (paṣipaa awọn window ni ipari fifa)\n  enable: Jeki oludari window\n  layout: Agbekalẹ\n  resize_delta: Tun ṣe Delta (%)\n  space_between_containers: Aaye laarin awọn apoti\n  workspace_offset: Awọn iṣẹ Iṣẹ-iṣẹ (awọn ala)\n  workspace_padding: Awọn iṣẹ itasi\n'yes': Bẹẹni\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/zh-CN.yml",
    "content": "action:\n  confirm: 你确定吗？\n  confirm_body: 此操作无法撤销。\napps_configurations:\n  app:\n    bindings: 绑定（注意两个选项均为必填项）\n    category: 类别\n    category_placeholder: 无\n    monitor: 显示器\n    monitor_placeholder: 无\n    name: 名称\n    ok_create: 创建\n    ok_edit: 更新\n    ok_readonly: 另存为新配置\n    options:\n      NoInteractive: 没有互动\n      VdPinned: 在所有工作区中显示\n      WmFloat: Twm - 开始浮动\n      WmForce: Twm - 强制管理\n      WmUnmanage: Twm - 取消管理\n    options_label: 其他\n    title_create: 创建 {{name}}\n    title_edit: 编辑 {{name}}\n    title_readonly: 查看 {{name}}\n    weg_options_label: Dock / 任务栏选项\n    wm_options_label: 窗口管理器选项\n    workspace: 工作区\n    workspace_placeholder: 无\n  bundled_msg: 这些捆绑配置不可编辑，旨在为您提供无需自定义的最佳体验。它们会自动为您配置最常见的应用程序。\n  bundled_title: Seelen 捆绑的应用配置\n  confirm_delete: 确定要删除此配置吗？\n  confirm_delete_title: 确认删除\n  delete: 删除\n  export: 导出\n  export_full: 按应用程序导出设置\n  extra_info: Seelen UI 对每个应用仅使用一个标识符（找到的第一个匹配项），因此指定的顺序很重要，最新添加的将优先处理，请注意表格默认按从新到旧排序。\n  identifier:\n    add_block: 添加块\n    and: 与\n    id: 标识符\n    kind: 识别方式\n    matching_strategy: 匹配策略\n    matching_strategy_option:\n      contains: 包含\n      ends_with: 结束于\n      equals: 等于\n      regex: 正则表达式\n      starts_with: 开始于\n    negation: 否定匹配\n    or: 或\n    remove: 删除块\n    type:\n      class: 类\n      exe: 可执行文件\n      path: 路径\n      title: 标题\n  import: 导入\n  import_full: 按应用程序导入设置\n  new: 新建\n  search: 搜索\n  swap: 交换\ncancel: 取消\nclose: 关闭\ndelete: 删除\ndevtools:\n  app_folders: 应用文件夹\n  custom_config_file: 加载自定义配置文件\n  data_folder: 数据文件夹\n  enable: 启用开发者工具\n  install_folder: 安装文件夹\n  load: 加载\n  settings_file: 设置文件\n  simulate_perm:\n    label: 模拟Widget权限请求\n    result_allowed: ✓ 已获得许可\n    result_denied: ✗ 权限被拒绝\n    trigger: 模拟\n    widget_id_placeholder: '@作者/小部件名称'\nextras:\n  clear_icons: 清除系统图标缓存\n  clear_icons_tooltip: 可能需要重启才能在所有小部件上完全生效\n  exit: 退出\n  links: 官方链接\n  relaunch: 重启应用\n  version: 版本\n  version_fixed: 应用程序和 WebView2 运行时版本已修复。这意味着应用程序将不会接收更新，并且 WebView2 运行时也不会随 Windows 更新自动更新。\ngeneral:\n  accent_color: 主色调\n  date_format: 日期格式\n  date_format_how_to: 日期格式怎么写？\n  hardware_acceleration: 硬件加速\n  hardware_acceleration_description: 禁用硬件加速会减少内存使用量，但可能会导致性能问题。 如果您不使用动态壁纸，可以安全禁用。\n  icon_pack:\n    available: 可用图标包\n    selected: 当前图标包\n  language: 语言\n  monday: 周一\n  performance_mode:\n    on_battery: 使用电池时\n    on_energy_saver: 节能模式下\n    options:\n      disabled: 禁用\n      extreme: 极致\n      minimal: 最小\n    plugged: 插电或充电时\n  polling_interval: 系统轮询间隔\n  polling_interval_description: Seelen UI 检查 CPU、RAM、网络和磁盘活动等系统资源的频率（以秒为单位）。 数字越小意味着更新越频繁，但资源使用率会稍高。\n  saturday: 周六\n  start_of_week: 一周的开始\n  startup: 开机自启\n  sunday: 星期日\n  theme:\n    available: 可用主题\n    selected: 当前主题\nheader:\n  labels:\n    config: 配置\n    developer: 开发者选项\n    extras: 附加功能\n    general: 通用设置\n    home: 首页\n    icon_pack_editor: 缓存图标\n    iconpack: 图标包\n    monitors: 显示器\n    plugin: 插件\n    resources: 资源\n    shortcuts: 快捷键\n    soundpack: 音效包\n    specific_apps: 按应用设置\n    theme: 主题\n    virtual_desk: 虚拟桌面\n    wallpaper: 壁纸\n    widget: 小部件\nhome:\n  new_resources: 新资源\ninherit: 继承\ninProgress: 进行中...\ninsert: 插入\nloading: 加载中...\nmiscellaneous: 杂项\nmonitors_configurations:\n  label: 显示器 {{index}}\nmore: 更多\n'no': 否\nopen: 打开\nquit: 退出\nremove: 移除\nreset_all_to_default: 重置所有为默认值\nreset_to_default: 重置为默认值\nresources:\n  app_outdated: 该资源需要较新版本的 Seelen UI 才能正常使用。\n  corrupted_wallpaper: 无法提取缩略图 - 视频格式已损坏或不受支持\n  delete: 删除资源\n  discover: 发现更多资源\n  has_update: 有更新可用\n  high_impact: 对性能影响很大\n  import_wallpapers: 导入本地壁纸\n  open_folder: 打开资源文件夹\n  outdated: 此资源是为旧版本的 Seelen UI 设计的，可能无法正常使用。\n  see_on_website: 在网站中查看\nreview_request:\n  not_now: 暂时不是\n  sure: 当然！\n  title: 喜欢 Seelen UI 吗？\nsave: 保存\nsave_and_restart: 保存并重启\nsearch: 搜索\nsee_more: 查看更多\nshortcuts:\n  duplicate_error: 该快捷键重复\n  enable: 启用内置快捷键系统\n  enable_tooltip: 如果您使用 Seelen UI 客户端实现自己的快捷键系统，那么请禁用此项。\n  labels:\n    create_new_workspace: 创建工作区\n    cycle_stack_next: 循环堆叠下一个\n    cycle_stack_prev: 循环堆叠上一个\n    cycle_wallpaper_next: 切换到下一张壁纸\n    cycle_wallpaper_prev: 切换到上一张壁纸\n    decrease_height: 减少高度\n    decrease_width: 减少宽度\n    destroy_current_workspace: 销毁当前工作区\n    focus_bottom: 聚焦底部\n    focus_latest: 聚焦最新窗口\n    focus_left: 聚焦左侧\n    focus_right: 聚焦右侧\n    focus_top: 聚焦上方\n    increase_height: 增加高度\n    increase_width: 增加宽度\n    misc_force_quit: 强制退出\n    misc_force_restart: 强制重启\n    misc_open_settings: 打开设置\n    misc_toggle_lock_tracing: 切换锁定追踪（日志）\n    misc_toggle_win_event_tracing: 切换 Win 事件追踪（日志）\n    move_to_workspace: 移动到工作区 {{0}}\n    move_window_down: 将窗口移至下方\n    move_window_left: 将窗口移至左侧\n    move_window_right: 将窗口移至右侧\n    move_window_up: 将窗口移至上方\n    pause_tiling: 暂停平铺窗口管理器\n    reserve_bottom: 保留下方空间\n    reserve_float: 保留浮动空间\n    reserve_left: 保留左侧空间\n    reserve_right: 保留右侧空间\n    reserve_stack: 保留堆叠空间\n    reserve_top: 保留上方空间\n    restore_sizes: 恢复尺寸\n    send_to_workspace: 发送到工作区 {{0}}\n    start_weg_app: 聚焦或启动应用 {{0}}\n    switch_to_next_workspace: 切换到下一个工作区\n    switch_to_previous_workspace: 切换到先前的工作区\n    switch_workspace: 切换到工作区 {{0}}\n    toggle_float: 切换窗口浮动模式\n    toggle_monocle: 切换工作区独占模式\n  readonly_tooltip: 此为只读的快捷键\n  reset: 重置为默认值\nsides:\n  bottom: 下方\n  left: 左侧\n  right: 右侧\n  top: 顶部\ntoolbar:\n  auto_hide: 自动隐藏\n  delay_to_hide: 延迟隐藏\n  delay_to_show: 延迟显示\n  dock_side: 职位\n  enable: 启用精美工具栏\n  hide_mode:\n    always: 总是\n    never: 从不\n    on_overlap: 重叠时\n  item_size: 商品尺寸\n  label: 工具栏\n  margin: 边距大小\n  padding: 填充尺寸\n  placeholder: {}\nupdate:\n  available: 有可用更新！\n  channel: 更新通道\n  downloading: 下载中...\nwall:\n  backgrounds: 壁纸\n  blur: 模糊度\n  cancel: 取消\n  close: 关闭\n  collection_name: 收藏名称\n  collections: 收藏\n  contrast: 对比度\n  corrupted_wallpapers_message: 已损坏的壁纸，请考虑删除这些：\n  create: 创建\n  create_collection: 创建集合\n  default_collection: 默认集合\n  delete_collection: 删除集合\n  edit_collection: 编辑集合\n  enable: 启用壁纸管理器\n  extend: 扩展主屏幕\n  fit:\n    contain: 适应\n    cover: 覆盖\n    fill: 填充\n  flipHorizontal: 水平翻转\n  flipVertical: 垂直翻转\n  generating_thumbnails: 生成壁纸缩略图\n  hours: 小时\n  interval: 更换壁纸的时间间隔\n  minutes: 分钟\n  monitor_collection: 监控集合\n  multimonitor_behaviour: 多显示器行为\n  muted: 将视频音频静音\n  no_background: 幻灯片为空，将使用主题背景替代。\n  no_collections: 尚未创建任何集合。单击 “+” 按钮创建一个。\n  objectFit: 背景适应方式\n  objectPosition: 背景位置\n  overlayColor: 叠加颜色\n  overlayMixBlendMode: 叠加混合混合模式\n  per_monitor: 每台显示器\n  playback: 播放速度\n  position:\n    bottom: 底部\n    center: 居中\n    left: 左侧\n    right: 右侧\n    top: 顶部\n  processing_video: 处理视频\n  random: 随机更改壁纸\n  saturation: 饱和度\n  seconds: 秒\n  select_collection: 选择集合\n  thumbnail_generation_complete: 缩略图生成完成\n  thumbnail_generation_finished: 缩略图生成已成功完成\n  wallpaper_collection: 壁纸集合\n  wallpaper_settings: 壁纸设置\n  withOverlay: 启用叠加层\n  workspace_collections: 工作区集合\nweg:\n  auto_hide: 自动隐藏\n  delay_to_hide: 隐藏延迟\n  delay_to_show: 显示延迟\n  dock_side: 位置\n  enable: 启用 Dock / 任务栏\n  filtering: 项目过滤\n  gap: 间距\n  hide_mode:\n    always: 总是\n    never: 从不\n    on_overlap: 重叠时\n  items:\n    gap: 项目间间距\n    label: 项目\n    pinned_visibility:\n      always: 总是\n      label: 固定项目可见性\n      when_primary: 当显示器为主显示器时\n    show_instance_counter: 显示打开窗口计数\n    show_window_title: 显示打开窗口标题（仅水平方向）\n    size: 项目大小\n    split_windows: 拆分窗口（每个窗口一项）\n    temporal_visibility:\n      all: 全部\n      label: 未固定项目的可见性\n      on_monitor: 在监视器上\n    visible_separators: 可见分隔符\n  label: Dock / 任务栏\n  margin: 边距\n  mode:\n    full_width: 全屏宽度\n    min_content: 最小化内容\n  padding: 内边距\n  show_end_task: 在任务栏中显示结束任务\n  width: 宽度\nwelcome:\n  give_a_review: 发表评论\n  message: >-\n    Seelen UI 是一个适用于 Windows 的免费开源桌面环境。\n    在这里，您可以完全控制桌面的外观和行为，允许您自定义每个细节以匹配您的工作流程和风格。\n  ok: 让我们开始吧！\n  review: 如果您喜欢使用 Seelen UI，请考虑在商店中留下评论 - 您的反馈有助于项目的成长和改进。\n  title: 欢迎来到Seelen UI！\nwidget:\n  enable: 启用此小部件\n  enable_for_monitor: 在此显示器上启用\n  instances: 实例\nwm:\n  animations:\n    duration: 动画时长（毫秒）\n    ease_function: 动画缓动函数\n    enable: 启用窗口动画\n  author: 作者\n  border:\n    enable: 启用窗口边框\n    offset: 边框偏移\n    width: 边框宽度\n  description: 描述\n  drag_behavior: 拖动行为\n  drag_behavior_options:\n    sort: 排序（拖动时重新排序窗口）\n    swap: 交换（在拖动端交换窗口）\n  enable: 启用平铺窗口管理器\n  layout: 布局\n  resize_delta: 调整大小增量（%）\n  space_between_containers: 容器间间距\n  workspace_offset: 工作区偏移（边距）\n  workspace_padding: 工作区内边距\n'yes': 是\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/zh-TW.yml",
    "content": "action:\n  confirm: 你確定嗎？\n  confirm_body: 該動作不能撤消。\napps_configurations:\n  app:\n    bindings: 綁定（需要兩個選項）\n    category: 類別\n    category_placeholder: 沒有任何\n    monitor: 監視器\n    monitor_placeholder: 沒有任何\n    name: 姓名\n    ok_create: 創造\n    ok_edit: 更新\n    ok_readonly: 編輯為新\n    options:\n      NoInteractive: 沒有互動\n      VdPinned: 在所有工作區中顯示\n      WmFloat: Twm - 開始浮動\n      WmForce: Twm - 強制管理\n      WmUnmanage: Twm - 取消管理\n    options_label: 額外的選擇\n    title_create: 創建{{name}}\n    title_edit: 編輯{{name}}\n    title_readonly: 查看{{name}}\n    weg_options_label: 碼頭/任務欄選項\n    wm_options_label: 窗口管理器選項\n    workspace: 工作區\n    workspace_placeholder: 沒有任何\n  bundled_msg: 這些捆綁的配置不可編輯，旨在為您提供最佳體驗而沒有自定義。 他們會自動為您配置最常見的應用程序。\n  bundled_title: App Config與Seelen捆綁在一起\n  confirm_delete: 您確定要刪除此配置嗎？\n  confirm_delete_title: 確認刪除\n  delete: 刪除\n  export: 出口\n  export_full: 按應用程序導出設置\n  extra_info: >-\n    SEELEN\n    UI僅使用每個應用程序使用一個標識符（第一次匹配），因此“如何指定”的順序很重要，最新添加的添加將得到優先級，因為註意表的默認情況下是從最新到舊的。\n  identifier:\n    add_block: 添加塊\n    and: 和\n    id: 標識符\n    kind: 識別\n    matching_strategy: 匹配策略\n    matching_strategy_option:\n      contains: 包含\n      ends_with: 以\n      equals: 平等\n      regex: 正則表達式\n      starts_with: 從\n    negation: 負匹配\n    or: 或者\n    remove: 刪除塊\n    type:\n      class: 班級\n      exe: EXE文件\n      path: 小路\n      title: 標題\n  import: 進口\n  import_full: 通過應用程序導入設置\n  new: 新的\n  search: 搜尋\n  swap: 交換\ncancel: 取消\nclose: 關閉\ndelete: 刪除\ndevtools:\n  app_folders: 應用文件夾\n  custom_config_file: 加載自定義配置文件\n  data_folder: 數據文件夾\n  enable: 啟用開發人員工具\n  install_folder: 安裝文件夾\n  load: 載入\n  settings_file: 設置文件\n  simulate_perm:\n    label: 模擬Widget權限請求\n    result_allowed: ✓ 已取得許可\n    result_denied: ✗ 權限被拒絕\n    trigger: 模擬\n    widget_id_placeholder: '@作者/小部件名稱'\nextras:\n  clear_icons: 清除系統圖標緩存\n  clear_icons_tooltip: 可能需要重新啟動才能完全生效所有小部件\n  exit: 退出/退出\n  links: 官方鏈接\n  relaunch: 重新啟動\n  version: 版本\n  version_fixed: 應用程序和 WebView2 運行時版本已修復。這意味著應用程序將不會接收更新，並且 WebView2 運行時也不會隨 Windows 更新自動更新。\ngeneral:\n  accent_color: 口音顏色\n  date_format: 日期格式\n  date_format_how_to: 日期格式怎麼寫？\n  hardware_acceleration: 硬體加速\n  hardware_acceleration_description: 停用硬體加速會減少記憶體使用量，但可能會導致效能問題。 如果您不使用動態壁紙，可以安全地停用。\n  icon_pack:\n    available: 可用的圖標包\n    selected: 活動圖標包\n  language: 語言\n  monday: 週一\n  performance_mode:\n    on_battery: 在電池上\n    on_energy_saver: 在能量儲蓄方面\n    options:\n      disabled: 禁用\n      extreme: 極端\n      minimal: 最小\n    plugged: 插入或充電\n  polling_interval: 系統輪詢間隔\n  polling_interval_description: Seelen UI 檢查 CPU、RAM、網路和磁碟活動等系統資源的頻率（以秒為單位）。 數字越小意味著更新越頻繁，但資源使用率會稍高。\n  saturday: 週六\n  start_of_week: 一周開始\n  startup: 在啟動上運行？\n  sunday: 星期日\n  theme:\n    available: 可用的主題\n    selected: 主題主題\nheader:\n  labels:\n    config: 配置\n    developer: 對於開發人員\n    extras: 額外\n    general: 一般的\n    home: 家\n    icon_pack_editor: 緩存圖標\n    iconpack: 圖標包\n    monitors: 監視器\n    plugin: 插件\n    resources: 資源\n    shortcuts: 捷徑\n    soundpack: 聲音包\n    specific_apps: 按應用程序設置\n    theme: 主題\n    virtual_desk: 虛擬桌面\n    wallpaper: 壁紙\n    widget: 小部件\nhome:\n  new_resources: 新資源\ninherit: 繼承\ninProgress: 進行中...\ninsert: 插入\nloading: 載入中...\nmiscellaneous: 各種各樣的\nmonitors_configurations:\n  label: 監視{{index}}\nmore: 更多的\n'no': 不\nopen: 打開\nquit: 辭職\nremove: 消除\nreset_all_to_default: 重置全部默認值\nreset_to_default: 重置為默認值\nresources:\n  app_outdated: 此資源要求Seelen UI的較新版本才能正常工作。\n  corrupted_wallpaper: 無法提取縮略圖 - 視頻格式已損壞或不受支持\n  delete: 刪除資源\n  discover: 發現更多資源\n  has_update: 有更新可用\n  high_impact: 對性能影響很大\n  import_wallpapers: 導入本地壁紙\n  open_folder: 打開資源文件夾\n  outdated: 該資源是為Seelen UI的較舊版本而設計的，可能無法正常工作。\n  see_on_website: 見網站\nreview_request:\n  not_now: 現在不要\n  sure: 當然！\n  title: 喜歡 Seelen UI 嗎？\nsave: 節省\nsave_and_restart: 保存和重新啟動\nsearch: 搜尋\nsee_more: 查看更多\nshortcuts:\n  duplicate_error: 該快捷方式重複\n  enable: 啟用內置捷徑系統\n  enable_tooltip: 禁用您使用SEELEN UI客戶端實現自己的快捷方式系統\n  labels:\n    create_new_workspace: 創建新工作區\n    cycle_stack_next: 循環堆棧接下來\n    cycle_stack_prev: 循環堆棧以前\n    cycle_wallpaper_next: 更改為下一個牆紙\n    cycle_wallpaper_prev: 更改為以前的牆紙\n    decrease_height: 降低高度\n    decrease_width: 減小寬度\n    destroy_current_workspace: 破壞當前工作區\n    focus_bottom: 聚焦底部\n    focus_latest: 重點最新\n    focus_left: 焦點左\n    focus_right: 重點正確\n    focus_top: 聚焦頂部\n    increase_height: 增加身高\n    increase_width: 增加寬度\n    misc_force_quit: 武力退出\n    misc_force_restart: 強制重新啟動\n    misc_open_settings: 打開設置\n    misc_toggle_lock_tracing: 切換鎖定跟踪（日誌）\n    misc_toggle_win_event_tracing: 切換Win事件跟踪（日誌）\n    move_to_workspace: 移至工作區{{0}}\n    move_window_down: 將窗口移至底部\n    move_window_left: 向左移動窗口\n    move_window_right: 向右移動窗口\n    move_window_up: 將窗口移至頂部\n    pause_tiling: 暫停瓷磚窗口管理器\n    reserve_bottom: 儲備底部\n    reserve_float: 儲備浮子\n    reserve_left: 保留剩下\n    reserve_right: 保留權\n    reserve_stack: 儲備堆\n    reserve_top: 儲備頂\n    restore_sizes: 還原尺寸\n    send_to_workspace: 發送到工作區{{0}}\n    start_weg_app: 聚焦或啟動應用程序{{0}}\n    switch_to_next_workspace: 切換到下一個工作區\n    switch_to_previous_workspace: 切換到以前的工作區\n    switch_workspace: 切換到工作區{{0}}\n    toggle_float: 切換窗口浮點模式\n    toggle_monocle: 切換工作區單片模式\n  readonly_tooltip: 這是只讀的捷徑\n  reset: 重置為默認值\nsides:\n  bottom: 底部\n  left: 左邊\n  right: 正確的\n  top: 頂部\ntoolbar:\n  auto_hide: 自動隱藏\n  delay_to_hide: 延遲隱藏\n  delay_to_show: 延遲顯示\n  dock_side: 位置\n  enable: 啟用高檔工具欄\n  hide_mode:\n    always: 總是\n    never: 絕不\n    on_overlap: 在重疊\n  item_size: 商品尺寸\n  label: 工具欄\n  margin: 邊距大小\n  padding: 填充尺寸\n  placeholder: {}\nupdate:\n  available: 更新可用！\n  channel: 更新頻道\n  downloading: 下載...\nwall:\n  backgrounds: 壁紙\n  blur: 模糊\n  cancel: 取消\n  close: 關閉\n  collection_name: 收藏名稱\n  collections: 收藏\n  contrast: 對比\n  corrupted_wallpapers_message: 損壞的壁紙，請考慮刪除這些：\n  create: 創造\n  create_collection: 創建收藏\n  default_collection: 默認集合\n  delete_collection: 刪除集合\n  edit_collection: 編輯收藏\n  enable: 啟用壁紙經理\n  extend: 延長小學\n  fit:\n    contain: 包含\n    cover: 覆蓋\n    fill: 充滿\n  flipHorizontal: 翻轉水平\n  flipVertical: 翻轉垂直\n  generating_thumbnails: 生成壁紙縮略圖\n  hours: 小時\n  interval: 每次更改牆紙\n  minutes: 分鐘\n  monitor_collection: 監控集合\n  multimonitor_behaviour: 多顯示器行為\n  muted: 將視頻音頻靜音\n  no_background: 使用主題的背景為空幻燈片。\n  no_collections: 尚未創建任何集合。 單擊 + 按鈕創建一個。\n  objectFit: 背景擬合\n  objectPosition: 背景位置\n  overlayColor: 覆蓋顏色\n  overlayMixBlendMode: 疊加混合混合模式\n  per_monitor: 每台顯示器\n  playback: 播放速度\n  position:\n    bottom: 底部\n    center: 中心\n    left: 左邊\n    right: 正確的\n    top: 頂部\n  processing_video: 處理視頻\n  random: 隨機化幻燈片\n  saturation: 飽和\n  seconds: 秒\n  select_collection: 選擇系列\n  thumbnail_generation_complete: 縮略圖生成完成\n  thumbnail_generation_finished: 縮略圖生成已成功完成\n  wallpaper_collection: 壁紙合集\n  wallpaper_settings: 壁紙設置\n  withOverlay: 使用覆蓋\n  workspace_collections: 工作區集合\nweg:\n  auto_hide: 自動隱藏\n  delay_to_hide: 延遲隱藏\n  delay_to_show: 延遲顯示\n  dock_side: 位置\n  enable: 啟用碼頭/任務欄\n  filtering: 項目過濾\n  gap: 差距\n  hide_mode:\n    always: 總是\n    never: 絕不\n    on_overlap: 在重疊\n  items:\n    gap: 項目之間的空間\n    label: 專案\n    pinned_visibility:\n      always: 總是\n      label: 固定物品可見性\n      when_primary: 當監視器是主要的\n    show_instance_counter: 顯示打開的Windows計數器\n    show_window_title: 顯示打開的窗口標題（僅水平）\n    size: 項目大小\n    split_windows: 拆分視窗（每個視窗一項）\n    temporal_visibility:\n      all: 全部\n      label: 未鎖定的物品可見性\n      on_monitor: 在顯示器上\n    visible_separators: 可見的分離器\n  label: 碼頭/任務欄\n  margin: 利潤\n  mode:\n    full_width: 全屏寬度\n    min_content: 很小\n  padding: 填充\n  show_end_task: 在任務欄中顯示最終任務\n  width: 寬度\nwelcome:\n  give_a_review: 發表評論\n  message: >-\n    Seelen UI 是一個適用於 Windows 的免費開源桌面環境。\n    在這裡，您可以完全控制桌面的外觀和行為，允許您自定義每個細節以匹配您的工作流程和風格。\n  ok: 讓我們開始吧！\n  review: 如果您喜歡使用 Seelen UI，請考慮在商店中留下評論 - 您的反饋有助於項目的成長和改進。\n  title: 歡迎來到Seelen UI！\nwidget:\n  enable: 啟用這個小部件\n  enable_for_monitor: 在此顯示器上啟用\n  instances: 實例\nwm:\n  animations:\n    duration: 動畫持續時間（MS）\n    ease_function: 動畫放鬆功能\n    enable: 啟用Window的動畫\n  author: 作者\n  border:\n    enable: 啟用Window的邊框\n    offset: 邊界偏移\n    width: 邊界寬度\n  description: 描述\n  drag_behavior: 拖動行為\n  drag_behavior_options:\n    sort: 排序（拖動時重新排序窗口）\n    swap: 交換（在拖動端交換窗口）\n  enable: 啟用平鋪窗口管理器\n  layout: 佈局\n  resize_delta: 調整三角洲大小（％）\n  space_between_containers: 容器之間的空間\n  workspace_offset: 工作區偏移（邊距）\n  workspace_padding: 工作區填充\n'yes': 是的\n"
  },
  {
    "path": "src/ui/react/settings/i18n/translations/zu.yml",
    "content": "action:\n  confirm: Uqinisekile?\n  confirm_body: Lesi senzo asinakungcola.\napps_configurations:\n  app:\n    bindings: Ukubopha (inothi zombili izinketho ziyadingeka)\n    category: Uhlobo\n    category_placeholder: Namunye\n    monitor: Qapha\n    monitor_placeholder: Namunye\n    name: Ibizo\n    ok_create: Dala\n    ok_edit: Buyekeza\n    ok_readonly: Hlela njengomusha\n    options:\n      NoInteractive: Akukho okusebenzayo\n      VdPinned: Khombisa kuzo zonke izindawo zokusebenza\n      WmFloat: I-TWM - Qala ukuntanta\n      WmForce: I-TWM - Force Phatha\n      WmUnmanage: I-TWM - UNANMARAGE\n    options_label: Izinketho Ezengeziwe\n    title_create: Ukudala {{igama}}\n    title_edit: Ukuhlela {{igama}}\n    title_readonly: Ukubuka {{igama}}\n    weg_options_label: Izinketho ze-Dock / Taskbar\n    wm_options_label: Izinketho ze-Window Manager\n    workspace: Indawo\n    workspace_placeholder: Namunye\n  bundled_msg: >-\n    Lokhu kulungiswa okuhlanganisiwe akuhlelekile futhi kuklanyelwe ukukunikeza\n    okuhlangenwe nakho okuhle kakhulu ngaphandle kokwenza ngokwezifiso.\n    Balungiselela ngokuzenzakalelayo izicelo ezivame kakhulu kuwe.\n  bundled_title: I-App Config ehlanganiswe ne-Seleen\n  confirm_delete: Uqinisekile ukuthi ufuna ukususa lolu hlelo / s?\n  confirm_delete_title: Qinisekisa ukususa\n  delete: Cisha\n  export: Thumela kwamanye amazwe\n  export_full: Thumela Amasethingi okuthumela ngaphandle ngesicelo\n  extra_info: >-\n    I-Seelen UI isebenzisa kuphela isikhombi esisodwa sohlelo lokusebenza\n    ngalunye (umdlalo wokuqala otholakala) ngakho-ke i-oda lokuthi libalulekile\n    kanjani, njengoba itafula lakamuva lizobekwa phambili, njengoba inothi\n    lihlelwe ngokuzenzakalela kusuka kwakamuva kuya kwakamuva kuya kwakamuva.\n  identifier:\n    add_block: Faka ibhlokhi\n    and: Na-\n    id: Isithombe esithi\n    kind: Khomba ngu\n    matching_strategy: Isu lokuqondanisa\n    matching_strategy_option:\n      contains: Iqukethe\n      ends_with: Iphetha ngokuthi\n      equals: Kuyalingana\n      regex: Inkulumo evamile\n      starts_with: Iqala ngokuthi\n    negation: Hlukana nokufanisa\n    or: Noma\n    remove: Susa ibhulokhi\n    type:\n      class: Ikilasi\n      exe: Exe\n      path: Indlela\n      title: Isihloko\n  import: Ngenisa ezweni\n  import_full: Ngenisa amasethingi ngohlelo lokusebenza\n  new: '-Kwanamuhla'\n  search: Sesha\n  swap: Shintshela\ncancel: Hlikihla\nclose: Ngokuminyanisa\ndelete: Cisha\ndevtools:\n  app_folders: Amafolda wohlelo lokusebenza\n  custom_config_file: Layisha ifayela le-config yangokwezifiso\n  data_folder: Ifolda yedatha\n  enable: Nika amandla amathuluzi onjiniyela\n  install_folder: Ifolda yokufaka\n  load: Thwala\n  settings_file: Ifayela lezilungiselelo\n  simulate_perm:\n    label: Lingisa Isicelo Semvume Yewijethi\n    result_allowed: ✓ Imvume inikiwe\n    result_denied: ✗ Imvume inqatshiwe\n    trigger: Lingisa\n    widget_id_placeholder: '@author/widget-name'\nextras:\n  clear_icons: Sula i-Cold yesistimu ye-System\n  clear_icons_tooltip: >-\n    Ukuqalisa kabusha kungadingeka ukuthi kusebenze ngokuphelele kuwo wonke\n    amawijethi\n  exit: Quit / Phuma\n  links: Izixhumanisi ezisemthethweni\n  relaunch: Qondisa amandla ehlehliseyo\n  version: Ukuhumushela\n  version_fixed: >-\n    Uhlelo lokusebenza kanye nezinguqulo ze-WebView2 Runtime zilungisiwe. Lokhu\n    kusho ukuthi uhlelo lokusebenza ngeke luthole ukubuyekezwa futhi Isikhathi\n    sokusebenza seWebView2 ngeke sibuyekezwe ngokuzenzakalelayo ngezibuyekezo\n    ze-Windows.\ngeneral:\n  accent_color: Umbala we-Accent\n  date_format: Ifomethi yosuku\n  date_format_how_to: Ibhalwa kanjani ifomethi yedethi?\n  hardware_acceleration: I-Hardware Acceleration\n  hardware_acceleration_description: >-\n    Ukukhubaza ukusheshisa kwehadiwe kuzonciphisa ukusetshenziswa kwememori,\n    kodwa kungabangela izinkinga zokusebenza. Kuphephile ukukhubaza uma ungeke\n    usebenzise amaphephadonga abukhoma.\n  icon_pack:\n    available: Amaphekhi esithonjana atholakalayo\n    selected: Amaphekhi Wesithonjana Asebenzayo\n  language: Ulimi\n  monday: UMsombuluko\n  performance_mode:\n    on_battery: Ebhethri\n    on_energy_saver: Ku-Sergy Saver\n    options:\n      disabled: '-Gogekile'\n      extreme: '-Dlulele'\n      minimal: '-Ncilula'\n    plugged: Ixhunyiwe noma ishaja\n  polling_interval: Isikhathi sokuvota sesistimu\n  polling_interval_description: >-\n    Kukangaki (ngemizuzwana) i-Seelen UI ihlola izinsiza zesistimu njenge-CPU,\n    i-RAM, inethiwekhi, nomsebenzi wediski. Inombolo encane isho izibuyekezo\n    ezivame kakhulu, kodwa ukusetshenziswa kwensiza ephakeme kancane.\n  saturday: NgoMgqibelo\n  start_of_week: Ukuqala Kweviki\n  startup: Gijima ekuqaleni?\n  sunday: NgeSonto\n  theme:\n    available: Izingqikithi Ezitholakalayo\n    selected: Izindikimba ezisebenzayo\nheader:\n  labels:\n    config: Ukulungiswa\n    developer: Obathuthukisi\n    extras: Okungatha\n    general: Jwayelekile\n    home: Ikhaya\n    icon_pack_editor: Izithonjana ezihlodliwe\n    iconpack: I-Icon Packs\n    monitors: Ukuqapha\n    plugin: Amaplithi\n    resources: Izinsizakusebenza\n    shortcuts: Amashoshothi\n    soundpack: Amaphakethe omsindo\n    specific_apps: Izilungiselelo ngesicelo\n    theme: Izingqizi\n    virtual_desk: Ama-Virtual Desktops\n    wallpaper: Ama-Wallpaper\n    widget: Amawijethi\nhome:\n  new_resources: Izinsizakusebenza ezintsha\ninherit: Dla ifa\ninProgress: Kuyaqhubeka...\ninsert: Faka\nloading: Iyalayisha ...\nmiscellaneous: '-Nhlobonhlobo'\nmonitors_configurations:\n  label: Qapha {{inkomba}}\nmore: Okuningi\n'no': '-Nokuba'\nopen: Vula\nquit: Yeka\nremove: Susa\nreset_all_to_default: Setha kabusha konke kumanani azenzakalelayo\nreset_to_default: Setha kabusha ngenani elizenzakalelayo\nresources:\n  app_outdated: Lesi sisetshenziswa sidinga uhlobo olusha lweSeelen UI ukusebenza kahle.\n  corrupted_wallpaper: >-\n    Yehlulekile ukukhipha isithonjana - ifomethi yevidiyo eyonakele noma\n    engasekelwe\n  delete: Susa insiza\n  discover: Thola izinsiza ezengeziwe\n  has_update: Isibuyekezo siyatholakala\n  high_impact: Umthelela ophezulu ekusebenzeni\n  import_wallpapers: Ngenisa amaphephadonga endawo\n  open_folder: Ifolda Yezisetshenziswa\n  outdated: >-\n    Lo mthombo wenzelwe inguqulo endala yeSeelen UI futhi ingahle isebenze\n    kahle.\n  see_on_website: Bona kuwebhusayithi\nreview_request:\n  not_now: Hhayi manje\n  sure: Impela!\n  title: Ujabulela i-Seelen UI?\nsave: Hlenga\nsave_and_restart: Gcina futhi Qalisa kabusha\nsearch: Sesha\nsee_more: Buka Okuningi\nshortcuts:\n  duplicate_error: Lesi sinqamuleli siyimpinda\n  enable: Nika amandla uhlelo lwezinqamuleli ezakhelwe ngaphakathi\n  enable_tooltip: >-\n    Khubaza uma uzosebenzisa uhlelo lwakho lwezinqamuleli usebenzisa iklayenti\n    le-Seelen UI\n  labels:\n    create_new_workspace: Dala indawo yokusebenza entsha\n    cycle_stack_next: Umjikelezo isitaki olandelayo\n    cycle_stack_prev: Umjikelezo ubona okwedlule\n    cycle_wallpaper_next: Shintshela ekhasini elilandelayo\n    cycle_wallpaper_prev: Shintshela ku-Wallpaper eyedlule\n    decrease_height: Yehlisa ukuphakama\n    decrease_width: Yehlisa Ububanzi\n    destroy_current_workspace: Ukubhubhisa indawo yokusebenza yamanje\n    focus_bottom: Gxila phansi\n    focus_latest: Gxila kwakamuva\n    focus_left: Gxila kwesokunxele\n    focus_right: Gxila kwesokudla\n    focus_top: Gxila phezulu\n    increase_height: Khuphula ukuphakama\n    increase_width: Khulisa Ububanzi\n    misc_force_quit: Amandla ayeke\n    misc_force_restart: Force Qala kabusha\n    misc_open_settings: Vula izilungiselelo\n    misc_toggle_lock_tracing: Guqula ukukhiya ukulandelela (izingodo)\n    misc_toggle_win_event_tracing: Guqula u-Win Traceng (izingodo)\n    move_to_workspace: Hambisa ku-Workpace {{0}}\n    move_window_down: Hambisa iwindi phansi\n    move_window_left: Hambisa iwindi ukushiya kwesokunxele\n    move_window_right: Hambisa iwindi ukuze kwesokudla\n    move_window_up: Hambisa iwindi phezulu\n    pause_tiling: Misa okwesikhashana Imenenja yewindows\n    reserve_bottom: Ukubhuka phansi\n    reserve_float: Indawo yokuntanta\n    reserve_left: Indawo esele\n    reserve_right: Ukubhuka kwesokudla\n    reserve_stack: Isitaki\n    reserve_top: Beka phezulu\n    restore_sizes: Buyisela osayizi\n    send_to_workspace: Thumela kwi-Workpace {{0}}\n    start_weg_app: Gxila noma qala uhlelo lokusebenza {{0}}\n    switch_to_next_workspace: Shintshela endaweni yokusebenzela elandelayo\n    switch_to_previous_workspace: Shintshela endaweni yokusebenza edlule\n    switch_workspace: Shintshela endaweni yokusebenza {{0}}\n    toggle_float: Imodi yewindows Float Float\n    toggle_monocle: Guqula imodi ye-monocle ye-workspace\n  readonly_tooltip: Lesi isinqamuleli esifundwayo kuphela\n  reset: Setha kabusha okuzenzakalelayo\nsides:\n  bottom: Esinqeni\n  left: '-Bunxele'\n  right: Ngakwesokudla\n  top: Isihloko\ntoolbar:\n  auto_hide: Fihla okuzenzakalelayo\n  delay_to_hide: Libazise ukucasha\n  delay_to_show: Libazisa ukubonisa\n  dock_side: Ukuma\n  enable: Nika amandla amathuluzi e-fancy\n  hide_mode:\n    always: Njalo\n    never: Ungalokothi\n    on_overlap: Ngokugqagqana\n  item_size: Usayizi Wento\n  label: Indawo yamathuluzi\n  margin: Usayizi Wemajini\n  padding: Usayizi wePadding\n  placeholder: {}\nupdate:\n  available: Ukuvuselelwa Kuyatholakala!\n  channel: Isiteshi sokuvuselela\n  downloading: Ukulanda ...\nwall:\n  backgrounds: Ama-Wallpaper\n  blur: Impumpula\n  cancel: Khansela\n  close: Vala\n  collection_name: Igama Leqoqo\n  collections: Amaqoqo\n  contrast: Ukuqhathanisa\n  corrupted_wallpapers_message: 'Izithombe zangemuva ezonakele, cabanga ukususa lezi:'\n  create: Dala\n  create_collection: Dala Iqoqo\n  default_collection: Iqoqo elizenzakalelayo\n  delete_collection: Susa Iqoqo\n  edit_collection: Hlela Iqoqo\n  enable: Nika amandla umphathi wangemuva\n  extend: Nweba okuyinhloko\n  fit:\n    contain: Qukethe\n    cover: Mbathisa\n    fill: Gcwalisa\n  flipHorizontal: Flip ovundlile\n  flipVertical: Flip mpo\n  generating_thumbnails: Ikhiqiza Izithonjana zangemuva\n  hours: amahora\n  interval: Shintsha iphepha lokunamathisela odongeni\n  minutes: imizuzu\n  monitor_collection: Gada Iqoqo\n  multimonitor_behaviour: Ukuziphatha kwe-Multimonitor\n  muted: Thulisa Video Audio\n  no_background: >-\n    Umbukiso wesilayidi esingenalutho, usebenzisa ingemuva le-theme\n    esikhundleni.\n  no_collections: >-\n    Awekho amaqoqo adaliwe okwamanje. Chofoza inkinobho ethi + ukuze udale\n    eyodwa.\n  objectFit: Isendlalelo semuva\n  objectPosition: Isikhundla sangemuva\n  overlayColor: Umbala obheke phambili\n  overlayMixBlendMode: Hlanganisa imodi ehlanganisiwe\n  per_monitor: Ngemonitha ngayinye\n  playback: Ijubane lokudlala\n  position:\n    bottom: Esinqeni\n    center: Indawo yangaphakathi impela\n    left: '-Bunxele'\n    right: Ngakwesokudla\n    top: Isihloko\n  processing_video: Icubungula ividiyo\n  random: Nge-slideshow engahleliwe\n  saturation: Ukutacwaba\n  seconds: imisa\n  select_collection: Khetha Iqoqo\n  thumbnail_generation_complete: Ukukhiqizwa Kwesithonjana Kuqediwe\n  thumbnail_generation_finished: Ukwenziwa kwezithonjana kuqede ngempumelelo\n  wallpaper_collection: Ukuqoqwa kwamaphephadonga\n  wallpaper_settings: Izilungiselelo zangemuva\n  withOverlay: Ngokumbondelana\n  workspace_collections: Amaqoqo wendawo yokusebenza\nweg:\n  auto_hide: Fihla okuzenzakalelayo\n  delay_to_hide: Libazise ukucasha\n  delay_to_show: Libazisa ukubonisa\n  dock_side: Ukuma\n  enable: Nika amandla i-Dock / Taskbar\n  filtering: Ukuhlunga kwento\n  gap: Insungubezi\n  hide_mode:\n    always: Njalo\n    never: Ungalokothi\n    on_overlap: Ngokugqagqana\n  items:\n    gap: Isikhala phakathi kwezinto\n    label: Into\n    pinned_visibility:\n      always: Njalo\n      label: Ukubonakala Kwezinto Eziphiniwe\n      when_primary: Lapho imonitha iyinhloko\n    show_instance_counter: Khombisa i-Windows Counter\n    show_window_title: Khombisa isihloko esivulekile sewindi (okuvundlile)\n    size: Usayizi wento\n    split_windows: Hlukanisa iWindows (into eyodwa ngewindi ngalinye)\n    temporal_visibility:\n      all: Konke\n      label: Ukubonakala Kwezinto Ezingaphiniwe\n      on_monitor: Ku-Monitor\n    visible_separators: Abahlukanisi ababonakalayo\n  label: Dock / taskbar\n  margin: Image\n  mode:\n    full_width: Ububanzi besikrini esigcwele\n    min_content: Kuncane njengoba kungaba\n  padding: Idilidi\n  show_end_task: Khombisa umsebenzi wokugcina kubha yomsebenzi\n  width: Ububanzi\nwelcome:\n  give_a_review: Nikeza Isibuyekezo\n  message: >-\n    I-Seelen UI iyindawo yamahhala nevulekile yedeskithophu yeWindows. Lapha\n    unokulawula okugcwele kokuthi ideskithophu yakho ibukeka futhi iziphatha\n    kanjani, okukuvumela ukuthi wenze ngendlela oyifisayo yonke imininingwane\n    ukuze ihambisane nokuhamba komsebenzi wakho nesitayela.\n  ok: Ake siqale!\n  review: >-\n    Uma uthanda ukusebenzisa i-Seelen UI, cabanga ukushiya isibuyekezo esitolo —\n    impendulo yakho isiza iphrojekthi ukuthi ikhule futhi ithuthuke.\n  title: Siyakwamukela ku-Seelen UI!\nwidget:\n  enable: Nika amandla lewijethi\n  enable_for_monitor: Nika amandla kulo mqaphi\n  instances: Izimo\nwm:\n  animations:\n    duration: Isikhathi se-animation (MS)\n    ease_function: Ukugqwayiza kwe-animation function\n    enable: Nika amandla izithombe zewindi\n  author: Umlobi\n  border:\n    enable: Nika amandla umngcele wewindi\n    offset: Umngcele we-Offset\n    width: Ububanzi bomngcele\n  description: Ukufanisa\n  drag_behavior: Hudula Ukuziphatha\n  drag_behavior_options:\n    sort: Hlunga (hlela kabusha amawindi ngenkathi uhudula)\n    swap: Shintsha (shintshanisa amawindi ekugcineni kokuhudula)\n  enable: Nika amandla umphathi wewindi elijabulisayo\n  layout: Isakhiwo\n  resize_delta: Shintsha usayizi DEDTA (%)\n  space_between_containers: Isikhala phakathi kweziqukathi\n  workspace_offset: Izindawo zokusebenzela (Margins)\n  workspace_padding: I-Workespashe Padding\n'yes': Yebo\n"
  },
  {
    "path": "src/ui/react/settings/index.tsx",
    "content": "import { getRootContainer } from \"libs/ui/react/utils/index.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { I18nextProvider } from \"react-i18next\";\nimport { HashRouter } from \"react-router\";\nimport { Widget } from \"@seelen-ui/lib\";\nimport { LogicalSize } from \"@seelen-ui/lib/tauri\";\n\nimport { App } from \"./app.tsx\";\n\nimport i18n, { loadTranslations } from \"./i18n/index.ts\";\n\nimport \"@shared/styles/colors.css\";\nimport \"./styles/variables.css\";\nimport \"@shared/styles/reset.css\";\nimport \"./styles/global.css\";\nimport \"@shared/styles/RichText.css\";\n\nconst { window } = Widget.self;\n\nawait Promise.all([\n  window.setDecorations(false),\n  window.setSizeConstraints({ minWidth: 600, minHeight: 400 }),\n  window.setSize(new LogicalSize(800, 500)),\n]);\nawait window.center();\n\nawait Widget.self.init();\nawait Widget.self.show();\nawait Widget.self.focus();\n\nWidget.self.onTrigger(() => {\n  window.unminimize();\n  window.setFocus();\n});\n\nawait loadTranslations();\n\nconst container = getRootContainer();\ncreateRoot(container).render(\n  <I18nextProvider i18n={i18n}>\n    <HashRouter>\n      <App />\n    </HashRouter>\n  </I18nextProvider>,\n);\n"
  },
  {
    "path": "src/ui/react/settings/modules/ByMonitor/infra/WallpaperSettingsModal.tsx",
    "content": "import { useComputed } from \"@preact/signals\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Modal, Select } from \"antd\";\nimport { type ReactNode, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { $virtual_desktops } from \"../../shared/signals\";\nimport { setMonitorWallpaperCollection, setWorkspaceWallpaperCollection } from \"../../Wall/application.ts\";\nimport { settings } from \"../../../state/mod.ts\";\nimport { SettingsGroup, SettingsOption } from \"../../../components/SettingsBox/index.tsx\";\n\ninterface Props {\n  monitorId: string;\n  title: ReactNode;\n}\n\nexport function WallpaperSettingsModal({ monitorId, title }: Props) {\n  const [open, setOpen] = useState(false);\n  const wallpaperCollections = settings.value.wallpaperCollections;\n  const monitorsV3 = settings.value.monitorsV3;\n  const { t } = useTranslation();\n\n  const monitorConfig = monitorsV3[monitorId];\n  const selectedCollection = monitorConfig?.wallpaperCollection ?? null;\n\n  // Get workspaces for this monitor from virtual desktops signal\n  const monitorWorkspaces = useComputed(() => {\n    return $virtual_desktops.value?.monitors[monitorId]?.workspaces || [];\n  });\n\n  return (\n    <>\n      <Modal\n        open={open}\n        onCancel={() => setOpen(false)}\n        title={title}\n        footer={null}\n        centered\n        width={600}\n      >\n        <SettingsGroup>\n          <SettingsOption>\n            <b>{t(\"wall.monitor_collection\")}</b>\n            <Select\n              style={{ width: 300 }}\n              value={selectedCollection ?? undefined}\n              onChange={(value) => setMonitorWallpaperCollection(monitorId, value || null)}\n              placeholder={t(\"inherit\")}\n              allowClear\n            >\n              {wallpaperCollections.map((collection) => (\n                <Select.Option key={collection.id} value={collection.id}>\n                  {collection.name}\n                </Select.Option>\n              ))}\n            </Select>\n          </SettingsOption>\n        </SettingsGroup>\n\n        {monitorWorkspaces.value.length > 0 && (\n          <SettingsGroup>\n            <div style={{ marginBottom: 12 }}>\n              <b>{t(\"wall.workspace_collections\")}</b>\n            </div>\n            {monitorWorkspaces.value.map((workspace, idx) => {\n              const workspaceConfig = monitorConfig?.byWorkspace?.[workspace.id];\n              const workspaceCollection = workspaceConfig?.wallpaperCollection ?? null;\n\n              return (\n                <SettingsOption key={workspace.id}>\n                  <span>{workspace.name || `Workspace ${idx + 1}`}</span>\n                  <Select\n                    style={{ width: 300 }}\n                    value={workspaceCollection ?? undefined}\n                    onChange={(value) => setWorkspaceWallpaperCollection(monitorId, workspace.id, value || null)}\n                    placeholder={t(\"inherit\")}\n                    allowClear\n                  >\n                    {wallpaperCollections.map((collection) => (\n                      <Select.Option key={collection.id} value={collection.id}>\n                        {collection.name}\n                      </Select.Option>\n                    ))}\n                  </Select>\n                </SettingsOption>\n              );\n            })}\n          </SettingsGroup>\n        )}\n      </Modal>\n      <Button type=\"default\" onClick={() => setOpen(true)}>\n        <Icon iconName=\"RiSettings4Fill\" />\n      </Button>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/ByMonitor/infra/WidgetSettingsModal.tsx",
    "content": "// This file is for testing, not final implementation yet.\n\nimport type { WidgetId } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Modal } from \"antd\";\nimport { type ReactNode, useState } from \"react\";\n\nimport { WidgetConfiguration } from \"../../resources/Widget/View.tsx\";\n\ninterface Props {\n  widgetId: WidgetId;\n  monitorId: string;\n  title: ReactNode;\n}\n\nexport function WidgetSettingsModal({ widgetId, monitorId, title }: Props) {\n  const [open, setOpen] = useState(false);\n\n  return (\n    <>\n      <Modal\n        open={open}\n        onCancel={() => setOpen(false)}\n        title={title}\n        footer={null}\n        centered\n      >\n        <WidgetConfiguration widgetId={widgetId} monitorId={monitorId} />\n      </Modal>\n      <Button type=\"default\" onClick={() => setOpen(true)}>\n        <Icon iconName=\"RiSettings4Fill\" />\n      </Button>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/ByMonitor/infra/index.module.css",
    "content": ".itemContainer {\n  display: grid;\n  grid-template-columns: minmax(200px, 1fr) 2fr;\n  grid-template-rows: 100%;\n  gap: var(--spacing-s);\n\n  .itemLeft {\n    display: grid;\n    grid-template-rows: min-content 1fr;\n    justify-items: center;\n    align-items: center;\n    gap: var(--spacing-2xs);\n\n    .label {\n      font-weight: 600;\n      font-size: 1.2rem;\n      line-height: 1.2em;\n      text-align: center;\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      width: 100%;\n    }\n  }\n\n  .itemRight {\n    overflow-y: auto;\n    min-height: 100%;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/ByMonitor/infra/index.tsx",
    "content": "import type { PhysicalMonitor, Widget } from \"@seelen-ui/lib/types\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { widgets } from \"../../../state/resources.ts\";\nimport { monitors, settings } from \"../../../state/mod.ts\";\n\nimport { Monitor } from \"../../../components/monitor/index.tsx\";\nimport { SettingsGroup, SettingsOption } from \"../../../components/SettingsBox/index.tsx\";\nimport { WidgetSettingsModal } from \"./WidgetSettingsModal.tsx\";\nimport { WallpaperSettingsModal } from \"./WallpaperSettingsModal.tsx\";\nimport cs from \"./index.module.css\";\n\ninterface MonitorConfigProps {\n  device: PhysicalMonitor;\n}\n\nexport function MonitorConfig({ device }: MonitorConfigProps) {\n  const { t } = useTranslation();\n\n  return (\n    <SettingsGroup>\n      <div className={cs.itemContainer}>\n        <div className={cs.itemLeft}>\n          <div className={cs.label}>{device.name || device.id}</div>\n          <Monitor\n            monitorId={device.id}\n            width={device.rect.right - device.rect.left}\n            height={device.rect.bottom - device.rect.top}\n          />\n        </div>\n        <div className={cs.itemRight}>\n          <SettingsGroup>\n            <SettingsOption>\n              <b>Resolution</b>\n              <div>\n                {device.rect.right - device.rect.left} x {device.rect.bottom - device.rect.top}\n              </div>\n            </SettingsOption>\n            <SettingsOption>\n              <b>{t(\"wall.wallpaper_collection\")}</b>\n              <WallpaperSettingsModal\n                monitorId={device.id}\n                title={\n                  <>\n                    {device.name || device.id}\n                    {\" / \"}\n                    {t(\"wall.wallpaper_settings\")}\n                  </>\n                }\n              />\n            </SettingsOption>\n          </SettingsGroup>\n\n          <SettingsGroup>\n            {widgets.value.filter(isConfigurableByMonitor).map((widget) => {\n              return (\n                <SettingsOption key={widget.id}>\n                  <ResourceText text={widget.metadata.displayName} />\n                  <WidgetSettingsModal\n                    widgetId={widget.id}\n                    monitorId={device.id}\n                    title={\n                      <>\n                        {device.name || device.id}\n                        {\" / \"}\n                        <ResourceText text={widget.metadata.displayName} />\n                      </>\n                    }\n                  />\n                </SettingsOption>\n              );\n            })}\n          </SettingsGroup>\n        </div>\n      </div>\n    </SettingsGroup>\n  );\n}\n\nexport function SettingsByMonitor() {\n  const devices = monitors.value;\n  const settingsByMonitor = settings.value.monitorsV3;\n\n  return (\n    <>\n      {devices.map((device) => {\n        let monitor = settingsByMonitor[device.id];\n        if (!monitor) {\n          console.warn(`Monitor settings not initialized ${device.id}`);\n          return null;\n        }\n        return <MonitorConfig key={device.id} device={device} />;\n      })}\n    </>\n  );\n}\n\nfunction isConfigurableByMonitor(widget: Widget) {\n  if (widget.instances === \"ReplicaByMonitor\") {\n    return true;\n  }\n\n  // Check if any setting item allows configuration by monitor\n  const stack = [...widget.settings];\n\n  while (stack.length > 0) {\n    const definition = stack.pop()!;\n\n    // If it's a group, add its items to the stack\n    if (\"group\" in definition) {\n      stack.push(...definition.group.items);\n    } else {\n      // It's a setting item, check if it allows configuration by monitor\n      if (definition.allowSetByMonitor) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Home/MiniStore.module.css",
    "content": ".title {\n  font-size: 1.4rem;\n  line-height: 1.4em;\n  font-weight: 600;\n  margin-bottom: var(--spacing-m);\n}\n\n.resources {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));\n  gap: var(--spacing-s);\n\n  .resourceSkeleton {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: var(--spacing-2xs);\n    padding: var(--spacing-xs);\n    background-color: var(--color-gray-50);\n    border-radius: var(--config-border-radius);\n\n    :global(.ant-skeleton):first-child {\n      width: 100%;\n      aspect-ratio: 1 / 1;\n\n      :global(.ant-skeleton-image) {\n        width: 100%;\n        height: 100%;\n      }\n    }\n  }\n\n  .resource {\n    background-color: var(--color-gray-50);\n    overflow-wrap: anywhere;\n    box-shadow: 2px 2px 10px 1px #0005;\n    border-radius: 8px;\n\n    > img {\n      background: var(--color-gray-200);\n      aspect-ratio: 1 / 1;\n      border-radius: 8px 8px 0 0;\n    }\n\n    .text {\n      flex: 1;\n      text-align: center;\n      max-height: 54px;\n      padding: var(--spacing-2xs);\n      font-weight: 600;\n      width: 100%;\n      overflow: auto;\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Home/MiniStore.tsx",
    "content": "import type { Resource } from \"@seelen-ui/lib/types\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText\";\nimport { Skeleton } from \"antd\";\nimport { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport cs from \"./MiniStore.module.css\";\nimport { compressUuid } from \"../resources/ResourceCard\";\n\nexport function ResourceSkeleton() {\n  return (\n    <div className={cs.resourceSkeleton}>\n      <Skeleton.Image active style={{ width: \"100%\", aspectRatio: \"1 / 1\" }} />\n      <Skeleton active paragraph={false} />\n    </div>\n  );\n}\n\ninterface Featured {\n  newArrivals: Resource[];\n  top: Resource[];\n  popular: Resource[];\n  staffLiked: Resource[];\n}\n\nexport function RemoteResources() {\n  const [resources, setResources] = useState<Resource[]>([]);\n\n  const { t } = useTranslation();\n\n  useEffect(() => {\n    fetch(\"https://product.seelen.io/resources/featured\")\n      .then((res) => res.json())\n      .then((data: Featured) =>\n        setResources(\n          data.newArrivals.filter((r) => {\n            return !r.metadata.portrait?.includes(\"cloudinary\");\n          }),\n        )\n      )\n      .catch(() => {});\n  }, []);\n\n  return (\n    <>\n      <h1 className={cs.title}>{t(\"home.new_resources\")}</h1>\n      <div className={cs.resources}>\n        {resources.length === 0 &&\n          Array.from({ length: 10 }).map((_, i) => (\n            <ResourceSkeleton\n              key={i}\n            />\n          ))}\n\n        {resources.map((resource) => {\n          if (!resource.metadata.portrait) return null;\n          return (\n            <a\n              key={resource.id}\n              href={`https://seelen.io/resources/${compressUuid(resource.id)}`}\n              target=\"_blank\"\n              className={cs.resource}\n            >\n              <img src={resource.metadata.portrait} />\n              <div className={cs.text}>\n                <ResourceText text={resource.metadata.displayName} />\n              </div>\n            </a>\n          );\n        })}\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Home/News.module.css",
    "content": ".notices {\n  width: 100%;\n  height: 50vh;\n  gap: var(--spacing-s);\n  display: grid;\n  grid-template-rows: 1fr min-content;\n\n  .notice {\n    width: 100%;\n    height: 100%;\n    position: relative;\n    border-radius: 10px;\n    overflow: hidden;\n\n    .image {\n      min-width: 100%;\n      height: 100%;\n      object-fit: cover;\n      background-color: var(--color-gray-300);\n    }\n\n    .content {\n      z-index: 1;\n      position: absolute;\n      left: 0;\n      bottom: 0;\n      width: 100%;\n      display: grid;\n      grid-template-columns: 1fr 1fr;\n      grid-template-rows: min-content min-content;\n      padding: var(--spacing-l);\n      background: linear-gradient(transparent, #000a 100%);\n      color: white;\n\n      .title {\n        font-size: 1.4rem;\n        font-weight: 600;\n      }\n\n      .message {\n        display: block;\n        text-overflow: ellipsis;\n        word-wrap: break-word;\n        overflow: hidden;\n        font-size: 0.9rem;\n        max-height: 3.6em;\n        line-height: 1.8em;\n      }\n\n      .linkButton {\n        grid-column: 2 / 3;\n        grid-row: 1 / 3;\n        display: flex;\n        align-items: flex-end;\n        justify-content: flex-end;\n      }\n    }\n  }\n\n  .pagination {\n    align-self: center;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: var(--spacing-s);\n\n    .paginationDot {\n      width: 10px;\n      height: 10px;\n      border-radius: 50%;\n      background-color: var(--color-gray-400);\n      transition: background-color 0.2s ease-in-out;\n\n      &.active {\n        background-color: var(--system-accent-color);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Home/News.tsx",
    "content": "import { useInterval } from \"libs/ui/react/utils/hooks\";\nimport { cx } from \"libs/ui/react/utils/styling\";\nimport { Button, Skeleton } from \"antd\";\nimport { useAnimate } from \"framer-motion\";\nimport { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport cs from \"./News.module.css\";\n\nconst PRODUCT_API_URL = \"https://product.seelen.io\";\n\ninterface BlogPromotion {\n  type: \"None\" | \"ShowEverywhere\" | \"ShowInApp\";\n  value?: string;\n}\n\ninterface Blog {\n  _id: string;\n  slug: string;\n  promotion: BlogPromotion;\n  body: {\n    title: string;\n    description: string;\n    portrait: string | null;\n  };\n}\n\ninterface New {\n  title: string;\n  message: string;\n  url: string;\n  image: string | null;\n}\n\nasync function getPromotedNews(): Promise<New[]> {\n  let response = await fetch(`${PRODUCT_API_URL}/blogs`);\n  if (!response.ok) {\n    return [];\n  }\n  let blogs: Blog[] = await response.json();\n\n  return blogs\n    .filter(\n      (b) =>\n        (b.promotion.type === \"ShowInApp\" && b.promotion.value === \"seelen-ui\") ||\n        b.promotion.type === \"ShowEverywhere\",\n    )\n    .map((b) => ({\n      title: b.body.title,\n      message: b.body.description,\n      url: `https://seelen.io/blog/${b.slug}`,\n      image: b.body.portrait,\n    }));\n}\n\nexport function NoticeSlider() {\n  const [news, setNews] = useState<New[]>([]);\n  const [currentIdx, setCurrentIdx] = useState<number>(0);\n\n  const [scope, animate] = useAnimate<HTMLDivElement>();\n  const { t } = useTranslation();\n\n  useEffect(() => {\n    getPromotedNews().then(setNews);\n  }, []);\n\n  useInterval(\n    () => {\n      animate(scope.current, { opacity: 0 }).then(() => {\n        setCurrentIdx((v) => v + 1);\n        animate(scope.current, { opacity: 1 });\n      });\n    },\n    10000,\n    [currentIdx],\n  );\n\n  let current = news[currentIdx % news.length];\n\n  return (\n    <div className={cs.notices}>\n      <div ref={scope} className={cs.notice}>\n        {current\n          ? (\n            <>\n              {current.image\n                ? <img className={cs.image} src={current.image} alt={current.title} />\n                : <div className={cs.image} />}\n              <div className={cs.content}>\n                <h3 className={cs.title}>{current.title}</h3>\n                <p className={cs.message}>{current.message}</p>\n                <div className={cs.linkButton}>\n                  <Button href={current.url} target=\"_blank\" type=\"primary\">\n                    {t(\"see_more\")}\n                  </Button>\n                </div>\n              </div>\n            </>\n          )\n          : (\n            <>\n              <div className={cs.image} />\n              <div className={cs.content}>\n                <Skeleton active className={cs.title} paragraph={false} />\n                <Skeleton active className={cs.message} title={false} />\n                <div className={cs.linkButton}>\n                  <Skeleton.Button active />\n                </div>\n              </div>\n            </>\n          )}\n      </div>\n      <div className={cs.pagination}>\n        {news.map((_item, index) => (\n          <div\n            key={index}\n            className={cx(cs.paginationDot, {\n              [cs.active!]: index === currentIdx % news.length,\n            })}\n            onClick={() => {\n              animate(scope.current, { opacity: 0 }).then(() => {\n                setCurrentIdx(index);\n                animate(scope.current, { opacity: 1 });\n              });\n            }}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Home/index.module.css",
    "content": "\n"
  },
  {
    "path": "src/ui/react/settings/modules/Home/index.tsx",
    "content": "import { RemoteResources } from \"./MiniStore.tsx\";\nimport { NoticeSlider } from \"./News.tsx\";\n\nexport function Home() {\n  return (\n    <>\n      <NoticeSlider />\n      <RemoteResources />\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/IconPackEditor/index.module.css",
    "content": ".iconGroup {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--spacing-s);\n\n  .iconContainer {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: var(--spacing-2xs);\n\n    .iconBox {\n      width: 128px;\n      height: 128px;\n      border-radius: 20px;\n      padding: var(--spacing-l);\n      background-color: #9c9c9c;\n      box-shadow: 1px 1px 1px #0005;\n\n      img {\n        object-fit: contain;\n        width: 100%;\n        height: 100%;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/IconPackEditor/index.tsx",
    "content": "import type { Icon, IconPackEntry } from \"@seelen-ui/lib/types\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\nimport { Button, Input, message } from \"antd\";\nimport { useMemo, useState } from \"react\";\n\nimport { iconPacks } from \"../../state/resources.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../components/SettingsBox/index.tsx\";\nimport cs from \"./index.module.css\";\nimport { Icon as ReactIcon } from \"libs/ui/react/components/Icon/index.tsx\";\n\nfunction resolveAsSrc(parent: string, icon: Icon): Icon {\n  return {\n    base: icon.base ? convertFileSrc(`${parent}\\\\${icon.base}`) : null,\n    light: icon.light ? convertFileSrc(`${parent}\\\\${icon.light}`) : null,\n    dark: icon.dark ? convertFileSrc(`${parent}\\\\${icon.dark}`) : null,\n    mask: icon.mask ? convertFileSrc(`${parent}\\\\${icon.mask}`) : null,\n    isAproximatelySquare: icon.isAproximatelySquare,\n  };\n}\n\nexport function IconPackEditorView() {\n  const [filterValue, setFilterValue] = useState(\"\");\n\n  const entries = useMemo(() => {\n    const system = iconPacks.value.find((i) => i.id === \"@system/icon-pack\");\n    return (\n      system?.entries\n        .filter((e) => containsSearched(e, filterValue))\n        .map((e) => {\n          const newEntry = { ...e };\n          if (newEntry.icon) {\n            newEntry.icon = resolveAsSrc(system.metadata.path, newEntry.icon);\n          }\n          return newEntry;\n        }) || []\n    );\n  }, [filterValue]);\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption>\n          <b>Search</b>\n          <Input\n            value={filterValue}\n            onChange={(e) => setFilterValue(e.currentTarget.value)}\n            placeholder=\"example: discord...\"\n          />\n        </SettingsOption>\n      </SettingsGroup>\n      {entries.map((entry, idx) => {\n        if (!entry.icon) {\n          return null;\n        }\n\n        return (\n          <SettingsGroup key={idx}>\n            <IconTitle entry={entry} />\n            <IconEditor icon={entry.icon} />\n          </SettingsGroup>\n        );\n      })}\n    </>\n  );\n}\n\nfunction IconTitle({ entry }: { entry: IconPackEntry }) {\n  const copyToClipboard = (text: string, label: string) => {\n    navigator.clipboard\n      .writeText(text)\n      .then(() => {\n        message.success(`${label} copied to clipboard`);\n      })\n      .catch(() => {\n        message.error(`Failed to copy ${label}`);\n      });\n  };\n\n  if (entry.type === \"unique\") {\n    return (\n      <div>\n        {entry.umid && (\n          <p>\n            <b>umid:</b>\n            {entry.umid}\n            <Button\n              type=\"text\"\n              size=\"small\"\n              onClick={() => copyToClipboard(entry.umid!, \"UMID\")}\n              style={{ marginLeft: 8 }}\n            >\n              <ReactIcon iconName=\"IoCopyOutline\" />\n            </Button>\n          </p>\n        )}\n        {entry.path && (\n          <p>\n            <b>path or filename:</b>\n            {entry.path}\n            <Button\n              type=\"text\"\n              onClick={() => copyToClipboard(entry.path!, \"Path\")}\n              style={{ marginLeft: 8 }}\n            >\n              <ReactIcon iconName=\"IoCopyOutline\" />\n            </Button>\n          </p>\n        )}\n      </div>\n    );\n  }\n\n  if (entry.type === \"shared\") {\n    return (\n      <div>\n        <b>Extension:</b>\n        {entry.extension}\n        <Button\n          type=\"text\"\n          size=\"small\"\n          onClick={() => copyToClipboard(entry.extension, \"Extension\")}\n          style={{ marginLeft: 8 }}\n        >\n          <ReactIcon iconName=\"IoCopyOutline\" />\n        </Button>\n      </div>\n    );\n  }\n\n  return (\n    <div>\n      <b>Key:</b>\n      {entry.key}\n      <Button\n        type=\"text\"\n        size=\"small\"\n        onClick={() => copyToClipboard(entry.key, \"Key\")}\n        style={{ marginLeft: 8 }}\n      >\n        <ReactIcon iconName=\"IoCopyOutline\" />\n      </Button>\n    </div>\n  );\n}\n\nfunction IconEditor({ icon }: { icon: Icon }) {\n  return (\n    <div className={cs.iconGroup}>\n      {!!icon.base && (\n        <div className={cs.iconContainer}>\n          <div className={cs.iconBox}>\n            <img src={icon.base} loading=\"lazy\" />\n          </div>\n          <span className={cs.iconLabel}>base</span>\n        </div>\n      )}\n      {!!icon.light && (\n        <div className={cs.iconContainer}>\n          <div className={cs.iconBox}>\n            <img src={icon.light} loading=\"lazy\" />\n          </div>\n          <span className={cs.iconLabel}>light</span>\n        </div>\n      )}\n      {!!icon.dark && (\n        <div className={cs.iconContainer}>\n          <div className={cs.iconBox}>\n            <img src={icon.dark} loading=\"lazy\" />\n          </div>\n          <span className={cs.iconLabel}>dark</span>\n        </div>\n      )}\n      {!!icon.mask && (\n        <div className={cs.iconContainer}>\n          <div className={cs.iconBox}>\n            <img src={icon.mask} loading=\"lazy\" />\n          </div>\n          <span className={cs.iconLabel}>mask</span>\n        </div>\n      )}\n    </div>\n  );\n}\n\nfunction containsSearched(entry: IconPackEntry, filterValue: string) {\n  const searchString = filterValue.toLowerCase();\n  return (\n    (entry.type === \"unique\" &&\n      (!!entry.path?.toLowerCase().includes(searchString) ||\n        !!entry.umid?.toLowerCase().includes(searchString))) ||\n    (entry.type === \"shared\" && entry.extension.toLowerCase().includes(searchString)) ||\n    (entry.type === \"custom\" && entry.key.toLowerCase().includes(searchString))\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Wall/WallpaperList.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { ResourceKind, type WallpaperId } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { VerticalSortableSelect } from \"src/ui/react/settings/components/SortableSelector/index.tsx\";\nimport { Wallpaper } from \"libs/ui/react/components/Wallpaper/index.tsx\";\nimport { Button, Modal } from \"antd\";\n\nimport { getWallpaperCollections, updateWallpaperCollection } from \"./application.ts\";\nimport { wallpapers } from \"../../state/resources.ts\";\n\nimport { ResourcePortrait } from \"../resources/ResourceCard.tsx\";\nimport cs from \"./index.module.css\";\n\ninterface Props {\n  collectionId: string;\n}\n\nexport function WallpaperList({ collectionId }: Props) {\n  const $toPreview = useSignal<WallpaperId | null>(null);\n\n  const wallpaperCollections = getWallpaperCollections();\n  const collection = wallpaperCollections.find((c) => c.id === collectionId);\n\n  function onChangeEnabled(wallpaperIds: WallpaperId[]) {\n    if (!collection) {\n      return;\n    }\n\n    updateWallpaperCollection({\n      ...collection,\n      wallpapers: wallpaperIds,\n    });\n  }\n\n  if (!collection) {\n    return null;\n  }\n\n  const previewing = $toPreview.value ? wallpapers.value.find((w) => w.id === $toPreview.value) : null;\n\n  return (\n    <div style={{ height: \"60vh\" }}>\n      <VerticalSortableSelect\n        options={wallpapers.value.map((w) => ({\n          value: w.id,\n          label: (\n            <div className={cs.entryLabel}>\n              <ResourcePortrait resource={w} kind={ResourceKind.Wallpaper} />\n              <ResourceText className={cs.entryName} text={w.metadata.displayName} />\n              <Button type=\"text\" size=\"small\" onClick={() => ($toPreview.value = w.id)}>\n                <Icon iconName=\"FaEye\" />\n              </Button>\n            </div>\n          ),\n        }))}\n        enabled={collection.wallpapers}\n        onChange={onChangeEnabled}\n      />\n      <Modal\n        open={!!previewing}\n        title={<ResourceText text={previewing?.metadata.displayName} />}\n        onCancel={() => ($toPreview.value = null)}\n        footer={null}\n        centered\n      >\n        <div className={cs.preview}>\n          {previewing && <Wallpaper definition={previewing} muted />}\n        </div>\n      </Modal>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Wall/application.ts",
    "content": "import { settings } from \"../../state/mod\";\nimport type {\n  SeelenWallSettings,\n  WallpaperCollection,\n  WallpaperId,\n  WallpaperInstanceSettings,\n} from \"@seelen-ui/lib/types\";\n\n/**\n * Patches the Wall configuration with partial updates.\n *\n * @example\n * patchWallConfig({ enabled: true, interval: 3600 });\n */\nexport function patchWallConfig(patch: Partial<SeelenWallSettings>) {\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      \"@seelen/wallpaper-manager\": {\n        ...settings.value.byWidget[\"@seelen/wallpaper-manager\"],\n        ...patch,\n      },\n    },\n  };\n}\n\n/**\n * Gets the current Wall configuration\n */\nexport function getWallConfig(): SeelenWallSettings {\n  return settings.value.byWidget[\"@seelen/wallpaper-manager\"];\n}\n\n/**\n * Gets all wallpaper collections\n */\nexport function getWallpaperCollections(): WallpaperCollection[] {\n  return settings.value.wallpaperCollections;\n}\n\n/**\n * Adds a new wallpaper collection\n */\nexport function addWallpaperCollection(collection: WallpaperCollection) {\n  settings.value = {\n    ...settings.value,\n    wallpaperCollections: [...settings.value.wallpaperCollections, collection],\n  };\n}\n\n/**\n * Updates an existing wallpaper collection\n */\nexport function updateWallpaperCollection(collection: WallpaperCollection) {\n  const index = settings.value.wallpaperCollections.findIndex((c) => c.id === collection.id);\n  if (index === -1) return;\n\n  const newCollections = [...settings.value.wallpaperCollections];\n  newCollections[index] = collection;\n\n  settings.value = {\n    ...settings.value,\n    wallpaperCollections: newCollections,\n  };\n}\n\n/**\n * Deletes a wallpaper collection and cleans up references\n */\nexport function deleteWallpaperCollection(collectionId: string) {\n  const newCollections = settings.value.wallpaperCollections.filter((c) => c.id !== collectionId);\n\n  // Reset default collection if it was deleted\n  const newDefaultCollection = settings.value.byWidget[\"@seelen/wallpaper-manager\"].defaultCollection === collectionId\n    ? null\n    : settings.value.byWidget[\"@seelen/wallpaper-manager\"].defaultCollection;\n\n  // Reset monitor collections if they were using this collection\n  const newMonitors = { ...settings.value.monitorsV3 };\n  Object.keys(newMonitors).forEach((monitorId) => {\n    const monitor = newMonitors[monitorId];\n    if (monitor && monitor.wallpaperCollection === collectionId) {\n      newMonitors[monitorId] = {\n        ...monitor,\n        wallpaperCollection: null,\n      };\n    }\n  });\n\n  settings.value = {\n    ...settings.value,\n    wallpaperCollections: newCollections,\n    monitorsV3: newMonitors,\n    byWidget: {\n      ...settings.value.byWidget,\n      \"@seelen/wallpaper-manager\": {\n        ...settings.value.byWidget[\"@seelen/wallpaper-manager\"],\n        defaultCollection: newDefaultCollection,\n      },\n    },\n  };\n}\n\n/**\n * Sets the default wallpaper collection\n */\nexport function setDefaultWallpaperCollection(collectionId: string | null) {\n  patchWallConfig({ defaultCollection: collectionId });\n}\n\n/**\n * Sets a wallpaper collection for a specific monitor\n */\nexport function setMonitorWallpaperCollection(monitorId: string, collectionId: string | null) {\n  const monitor = settings.value.monitorsV3[monitorId];\n  if (!monitor) return;\n\n  settings.value = {\n    ...settings.value,\n    monitorsV3: {\n      ...settings.value.monitorsV3,\n      [monitorId]: {\n        ...monitor,\n        wallpaperCollection: collectionId,\n      },\n    },\n  };\n}\n\n/**\n * Sets a wallpaper collection for a specific workspace on a monitor\n */\nexport function setWorkspaceWallpaperCollection(\n  monitorId: string,\n  workspaceId: string,\n  collectionId: string | null,\n) {\n  const monitor = settings.value.monitorsV3[monitorId];\n  if (!monitor) return;\n\n  settings.value = {\n    ...settings.value,\n    monitorsV3: {\n      ...settings.value.monitorsV3,\n      [monitorId]: {\n        ...monitor,\n        byWorkspace: {\n          ...(monitor.byWorkspace || {}),\n          [workspaceId]: {\n            ...(monitor.byWorkspace?.[workspaceId] || {}),\n            wallpaperCollection: collectionId,\n          },\n        },\n      },\n    },\n  };\n}\n\n/**\n * Patches settings for a specific wallpaper instance\n */\nexport function patchWallpaperSettings(id: WallpaperId, patch: Partial<WallpaperInstanceSettings>) {\n  settings.value = {\n    ...settings.value,\n    byWallpaper: {\n      ...settings.value.byWallpaper,\n      [id]: {\n        ...(settings.value.byWallpaper[id] || {}),\n        ...patch,\n      } as WallpaperInstanceSettings,\n    },\n  };\n}\n\n/**\n * Resets settings for a specific wallpaper instance\n */\nexport function resetWallpaperSettings(id: WallpaperId) {\n  const newByWallpaper = { ...settings.value.byWallpaper };\n  delete newByWallpaper[id];\n\n  settings.value = {\n    ...settings.value,\n    byWallpaper: newByWallpaper,\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Wall/index.module.css",
    "content": ".interval {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--spacing-xs);\n\n  > div {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-2xs);\n  }\n}\n\n.entryLabel {\n  display: grid;\n  grid-template-columns: 48px 1fr 30px;\n  align-items: center;\n  gap: var(--spacing-xs);\n\n  .entryName {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n}\n\n.preview {\n  position: relative;\n  width: 100%;\n  aspect-ratio: 16 / 9;\n  overflow: hidden;\n  border-radius: 10px;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/Wall/infra.tsx",
    "content": "import { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Badge, Button, Input, InputNumber, Modal, Select, Switch, Tooltip } from \"antd\";\nimport { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link } from \"react-router\";\n\nimport {\n  addWallpaperCollection,\n  deleteWallpaperCollection,\n  getWallConfig,\n  getWallpaperCollections,\n  patchWallConfig,\n  setDefaultWallpaperCollection,\n  updateWallpaperCollection,\n} from \"./application.ts\";\n\nimport { MultimonitorBehaviour } from \"@seelen-ui/lib/types\";\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../components/SettingsBox/index.tsx\";\nimport { WallpaperList } from \"./WallpaperList.tsx\";\nimport cs from \"./index.module.css\";\n\nexport function WallSettings() {\n  const wall = getWallConfig();\n  const wallpaperCollections = getWallpaperCollections();\n  const { enabled, interval } = wall;\n\n  const [time, setTime] = useState({\n    hours: Math.floor(interval / 3600),\n    minutes: Math.floor((interval / 60) % 60),\n  });\n\n  const [editingCollectionId, setEditingCollectionId] = useState<string | null>(null);\n  const [editingCollectionName, setEditingCollectionName] = useState(\"\");\n  const [isCreatingCollection, setIsCreatingCollection] = useState(false);\n  const [newCollectionName, setNewCollectionName] = useState(\"\");\n\n  const { t } = useTranslation();\n\n  const editingCollection = editingCollectionId ? wallpaperCollections.find((c) => c.id === editingCollectionId) : null;\n\n  useEffect(() => {\n    setTime({\n      hours: Math.floor(interval / 3600),\n      minutes: Math.floor((interval / 60) % 60),\n    });\n  }, [interval]);\n\n  useEffect(() => {\n    if (editingCollection) {\n      setEditingCollectionName(editingCollection.name);\n    }\n  }, [editingCollection]);\n\n  function onChangeEnabled(enabled: boolean) {\n    patchWallConfig({ enabled });\n  }\n\n  const updateTime = (key: \"hours\" | \"minutes\", value: number | null) => {\n    if (value === null) return;\n    const newTime = { ...time, [key]: Math.floor(value) };\n    setTime(newTime);\n    const newInterval = Math.max(newTime.hours * 3600 + newTime.minutes * 60, 60);\n    patchWallConfig({ interval: newInterval });\n  };\n\n  const handleCreateCollection = () => {\n    setIsCreatingCollection(true);\n    setNewCollectionName(\"\");\n  };\n\n  const handleConfirmCreateCollection = () => {\n    if (!newCollectionName.trim()) {\n      return;\n    }\n\n    const newCollection = {\n      id: crypto.randomUUID(),\n      name: newCollectionName.trim(),\n      wallpapers: [],\n    };\n    addWallpaperCollection(newCollection);\n    setIsCreatingCollection(false);\n    setNewCollectionName(\"\");\n  };\n\n  const handleCancelCreateCollection = () => {\n    setIsCreatingCollection(false);\n    setNewCollectionName(\"\");\n  };\n\n  const handleEditCollection = (id: string) => {\n    setEditingCollectionId(id);\n  };\n\n  const handleSaveCollectionName = () => {\n    if (!editingCollection || !editingCollectionName.trim()) return;\n\n    updateWallpaperCollection({\n      ...editingCollection,\n      name: editingCollectionName.trim(),\n    });\n  };\n\n  const handleCloseModal = () => {\n    handleSaveCollectionName();\n    setEditingCollectionId(null);\n    setEditingCollectionName(\"\");\n  };\n\n  const handleDeleteCollection = (id: string) => {\n    deleteWallpaperCollection(id);\n  };\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption\n          label={<b>{t(\"wall.enable\")}</b>}\n          action={<Switch value={enabled} onChange={onChangeEnabled} />}\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={<b>{t(\"wall.multimonitor_behaviour\")}</b>}\n          action={\n            <Select\n              style={{ width: 200 }}\n              value={wall.multimonitorBehaviour}\n              onChange={(value) => patchWallConfig({ multimonitorBehaviour: value })}\n              options={[\n                {\n                  label: t(\"wall.per_monitor\"),\n                  value: MultimonitorBehaviour.PerMonitor,\n                },\n                {\n                  label: t(\"wall.extend\"),\n                  value: MultimonitorBehaviour.Extend,\n                },\n              ]}\n            />\n          }\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={<b>{t(\"wall.random\")}</b>}\n          action={\n            <Switch\n              value={wall.randomize}\n              onChange={(randomize) => patchWallConfig({ randomize })}\n            />\n          }\n        />\n        <SettingsOption\n          label={<b>{t(\"wall.interval\")}</b>}\n          action={\n            <div className={cs.interval}>\n              {[\"hours\", \"minutes\"].map((unit) => (\n                <div key={unit}>\n                  <b>{t(`wall.${unit}`)}:</b>\n                  <InputNumber\n                    value={time[unit as keyof typeof time]}\n                    onChange={(value) => updateTime(unit as \"hours\" | \"minutes\", value)}\n                    min={0}\n                    style={{ width: 50 }}\n                  />\n                </div>\n              ))}\n            </div>\n          }\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"wall.backgrounds\")}\n          action={\n            <Link to=\"/resources/wallpaper\">\n              <Button type=\"primary\">\n                <Icon iconName=\"IoImages\" />\n              </Button>\n            </Link>\n          }\n        />\n\n        <SettingsSubGroup\n          label={\n            <SettingsOption\n              label={t(\"wall.collections\")}\n              action={\n                <Tooltip title={t(\"wall.create_collection\")}>\n                  <Button type=\"primary\" onClick={handleCreateCollection}>\n                    <Icon iconName=\"IoAdd\" />\n                  </Button>\n                </Tooltip>\n              }\n            />\n          }\n        >\n          {wallpaperCollections.length === 0\n            ? (\n              <div\n                style={{ padding: \"16px\", textAlign: \"center\", color: \"var(--config-text-muted)\" }}\n              >\n                {t(\"wall.no_collections\")}\n              </div>\n            )\n            : (\n              wallpaperCollections.map((collection) => {\n                const isDefault = wall.defaultCollection === collection.id;\n                return (\n                  <SettingsOption\n                    key={collection.id}\n                    label={\n                      <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n                        {isDefault && (\n                          <Tooltip title={t(\"wall.default_collection\")}>\n                            <Badge status=\"success\" />\n                          </Tooltip>\n                        )}\n                        <span>\n                          {collection.name}\n                          <span\n                            style={{\n                              marginLeft: 4,\n                              color: \"var(--config-text-muted)\",\n                              fontSize: \"0.9em\",\n                            }}\n                          >\n                            ({collection.wallpapers.length})\n                          </span>\n                        </span>\n                      </div>\n                    }\n                    action={\n                      <div style={{ display: \"flex\", gap: 8 }}>\n                        <Tooltip title={t(\"wall.edit_collection\")}>\n                          <Button size=\"small\" onClick={() => handleEditCollection(collection.id)}>\n                            <Icon iconName=\"MdEdit\" />\n                          </Button>\n                        </Tooltip>\n                        <Tooltip title={t(\"wall.delete_collection\")}>\n                          <Button\n                            size=\"small\"\n                            danger\n                            onClick={() => handleDeleteCollection(collection.id)}\n                          >\n                            <Icon iconName=\"IoTrash\" />\n                          </Button>\n                        </Tooltip>\n                      </div>\n                    }\n                  />\n                );\n              })\n            )}\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"wall.default_collection\")}\n          action={\n            <Select\n              style={{ width: 200 }}\n              value={wall.defaultCollection ?? undefined}\n              onChange={(value) => setDefaultWallpaperCollection(value || null)}\n              placeholder={t(\"wall.select_collection\")}\n              allowClear\n            >\n              {wallpaperCollections.map((collection) => (\n                <Select.Option key={collection.id} value={collection.id}>\n                  {collection.name}\n                </Select.Option>\n              ))}\n            </Select>\n          }\n        />\n      </SettingsGroup>\n\n      <Modal\n        title={t(\"wall.create_collection\")}\n        open={isCreatingCollection}\n        onOk={handleConfirmCreateCollection}\n        onCancel={handleCancelCreateCollection}\n        okText={t(\"wall.create\")}\n        cancelText={t(\"wall.cancel\")}\n        okButtonProps={{ disabled: !newCollectionName.trim() }}\n        centered\n      >\n        <Input\n          placeholder={t(\"wall.collection_name\")}\n          value={newCollectionName}\n          onChange={(e) => setNewCollectionName(e.currentTarget.value)}\n          onPressEnter={handleConfirmCreateCollection}\n          autoFocus\n          style={{ marginTop: 16 }}\n        />\n      </Modal>\n\n      <Modal\n        title={\n          <Input\n            style={{ width: \"80%\" }}\n            placeholder={t(\"wall.collection_name\")}\n            value={editingCollectionName}\n            onChange={(e) => setEditingCollectionName(e.currentTarget.value)}\n            onBlur={handleSaveCollectionName}\n            onPressEnter={handleSaveCollectionName}\n          />\n        }\n        open={!!editingCollectionId}\n        onCancel={handleCloseModal}\n        footer={\n          <Button type=\"primary\" onClick={handleCloseModal}>\n            {t(\"wall.close\")}\n          </Button>\n        }\n        width={800}\n        centered\n      >\n        {editingCollectionId && <WallpaperList collectionId={editingCollectionId} />}\n      </Modal>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/application.ts",
    "content": "import { settings } from \"../../state/mod\";\nimport type { PluginId, Rect, WindowManagerSettings, WmAnimations, WmDragBehavior } from \"@seelen-ui/lib/types\";\n\n/**\n * Patches the WindowManager configuration with partial updates.\n *\n * @example\n * patchWmConfig({ enabled: true });\n */\nexport function patchWmConfig(patch: Partial<WindowManagerSettings>) {\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      \"@seelen/window-manager\": {\n        ...settings.value.byWidget[\"@seelen/window-manager\"],\n        ...patch,\n      },\n    },\n  };\n}\n\n/**\n * Gets the current WindowManager configuration\n */\nexport function getWmConfig(): WindowManagerSettings {\n  return settings.value.byWidget[\"@seelen/window-manager\"];\n}\n\n/**\n * Sets the enabled state\n */\nexport function setWmEnabled(enabled: boolean) {\n  patchWmConfig({ enabled });\n}\n\n/**\n * Sets the default layout\n */\nexport function setWmDefaultLayout(defaultLayout: PluginId) {\n  patchWmConfig({ defaultLayout });\n}\n\n/**\n * Sets the workspace gap\n */\nexport function setWmWorkspaceGap(workspaceGap: number) {\n  patchWmConfig({ workspaceGap });\n}\n\n/**\n * Sets the workspace padding\n */\nexport function setWmWorkspacePadding(workspacePadding: number) {\n  patchWmConfig({ workspacePadding });\n}\n\n/**\n * Sets the workspace margin\n */\nexport function setWmWorkspaceMargin(workspaceMargin: Rect) {\n  patchWmConfig({ workspaceMargin });\n}\n\n/**\n * Sets the resize delta\n */\nexport function setWmResizeDelta(resizeDelta: number) {\n  patchWmConfig({ resizeDelta });\n}\n\n/**\n * Sets the drag behavior\n */\nexport function setWmDragBehavior(dragBehavior: WmDragBehavior) {\n  patchWmConfig({ dragBehavior });\n}\n\n/**\n * Sets the animations configuration\n */\nexport function setWmAnimations(animations: WmAnimations) {\n  patchWmConfig({ animations });\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/border/application.ts",
    "content": "import { settings } from \"../../../state/mod\";\nimport type { Border } from \"@seelen-ui/lib/types\";\n\n/**\n * Patches the WindowManager Border configuration with partial updates.\n *\n * @example\n * patchBorderConfig({ enabled: true, width: 2 });\n */\nexport function patchBorderConfig(patch: Partial<Border>) {\n  const currentWmSettings = settings.value.byWidget[\"@seelen/window-manager\"];\n\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      \"@seelen/window-manager\": {\n        ...currentWmSettings,\n        border: {\n          ...currentWmSettings.border,\n          ...patch,\n        },\n      },\n    },\n  };\n}\n\n/**\n * Gets the current Border configuration\n */\nexport function getBorderConfig(): Border {\n  return settings.value.byWidget[\"@seelen/window-manager\"].border;\n}\n\n/**\n * Sets the border enabled state\n */\nexport function setBorderEnabled(enabled: boolean) {\n  patchBorderConfig({ enabled });\n}\n\n/**\n * Sets the border offset\n */\nexport function setBorderOffset(offset: number) {\n  patchBorderConfig({ offset });\n}\n\n/**\n * Sets the border width\n */\nexport function setBorderWidth(width: number) {\n  patchBorderConfig({ width });\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/border/infra.tsx",
    "content": "import { InputNumber, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { getBorderConfig, setBorderEnabled, setBorderOffset, setBorderWidth } from \"./application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../components/SettingsBox/index.tsx\";\n\nexport const BorderSettings = () => {\n  const borderConfig = getBorderConfig();\n  const enabled = borderConfig.enabled;\n  const offset = borderConfig.offset;\n  const width = borderConfig.width;\n\n  const { t } = useTranslation();\n\n  const toggleEnabled = (value: boolean) => {\n    setBorderEnabled(value);\n  };\n\n  const updateOffset = (value: number | null) => {\n    setBorderOffset(value || 0);\n  };\n\n  const updateWidth = (value: number | null) => {\n    setBorderWidth(value || 0);\n  };\n\n  return (\n    <SettingsGroup>\n      <SettingsSubGroup\n        label={\n          <SettingsOption>\n            <span>{t(\"wm.border.enable\")}</span>\n            <Switch value={enabled} onChange={toggleEnabled} />\n          </SettingsOption>\n        }\n      >\n        <SettingsOption>\n          <span>{t(\"wm.border.offset\")}</span>\n          <InputNumber value={offset} onChange={updateOffset} />\n        </SettingsOption>\n        <SettingsOption>\n          <span>{t(\"wm.border.width\")}</span>\n          <InputNumber value={width} onChange={updateWidth} />\n        </SettingsOption>\n      </SettingsSubGroup>\n    </SettingsGroup>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/main/infra/Animations.tsx",
    "content": "import { InputNumber, Select, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../../components/SettingsBox/index.tsx\";\n\nimport { getWmConfig, setWmAnimations } from \"../../application.ts\";\n\nexport function WmAnimationsSettings() {\n  const wmConfig = getWmConfig();\n  const animations = wmConfig.animations;\n\n  const { t } = useTranslation();\n\n  return (\n    <SettingsGroup>\n      <SettingsSubGroup\n        label={\n          <SettingsOption\n            label={t(\"wm.animations.enable\")}\n            action={\n              <Switch\n                checked={animations.enabled}\n                onChange={(value) => {\n                  setWmAnimations({\n                    ...animations,\n                    enabled: value,\n                  });\n                }}\n              />\n            }\n          />\n        }\n      >\n        <SettingsOption\n          label={t(\"wm.animations.duration\")}\n          action={\n            <InputNumber\n              min={100}\n              max={1500}\n              value={Number(animations.durationMs)}\n              onChange={(value) => {\n                let parsed = value || 100;\n                setWmAnimations({\n                  ...animations,\n                  durationMs: parsed,\n                });\n              }}\n            />\n          }\n        />\n        <SettingsOption\n          label={t(\"wm.animations.ease_function\")}\n          action={\n            <Select\n              showSearch\n              options={EaseFunctions}\n              value={animations.easeFunction}\n              onSelect={(value) => {\n                setWmAnimations({\n                  ...animations,\n                  easeFunction: value,\n                });\n              }}\n              style={{ width: \"150px\" }}\n            />\n          }\n        />\n      </SettingsSubGroup>\n    </SettingsGroup>\n  );\n}\n\nconst EaseFunctions = [\n  \"Linear\",\n  \"EaseIn\",\n  \"EaseOut\",\n  \"EaseInOut\",\n  \"EaseInQuad\",\n  \"EaseOutQuad\",\n  \"EaseInOutQuad\",\n  \"EaseInCubic\",\n  \"EaseOutCubic\",\n  \"EaseInOutCubic\",\n  \"EaseInQuart\",\n  \"EaseOutQuart\",\n  \"EaseInOutQuart\",\n  \"EaseInQuint\",\n  \"EaseOutQuint\",\n  \"EaseInOutQuint\",\n  \"EaseInExpo\",\n  \"EaseOutExpo\",\n  \"EaseInOutExpo\",\n  \"EaseInCirc\",\n  \"EaseOutCirc\",\n  \"EaseInOutCirc\",\n  \"EaseInBack\",\n  \"EaseOutBack\",\n  \"EaseInOutBack\",\n  \"EaseInElastic\",\n  \"EaseOutElastic\",\n  \"EaseInOutElastic\",\n  \"EaseInBounce\",\n  \"EaseOutBounce\",\n  \"EaseInOutBounce\",\n].map((f) => ({ value: f }));\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/main/infra/GlobalPaddings.tsx",
    "content": "import type { Rect } from \"@seelen-ui/lib\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, InputNumber } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { getWmConfig, setWmWorkspaceGap, setWmWorkspaceMargin, setWmWorkspacePadding } from \"../../application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../../components/SettingsBox/index.tsx\";\n\nexport const GlobalPaddings = () => {\n  const wmConfig = getWmConfig();\n  const workspaceGap = wmConfig.workspaceGap;\n  const workspacePadding = wmConfig.workspacePadding;\n  const workAreaOffset = wmConfig.workspaceMargin;\n\n  const onChangeGlobalOffset = (side: keyof Rect, value: number | null) => {\n    setWmWorkspaceMargin({\n      ...workAreaOffset,\n      [side]: Math.round(value || 0),\n    });\n  };\n\n  const onChangeDefaultGap = (value: number | null) => {\n    setWmWorkspaceGap(Math.round(value || 0));\n  };\n\n  const onChangeDefaultPadding = (value: number | null) => {\n    setWmWorkspacePadding(Math.round(value || 0));\n  };\n\n  return (\n    <WindowManagerSpacingSettings\n      gap={workspaceGap}\n      padding={workspacePadding}\n      margins={workAreaOffset}\n      onChangeGap={onChangeDefaultGap}\n      onChangePadding={onChangeDefaultPadding}\n      onChangeMargins={onChangeGlobalOffset}\n    />\n  );\n};\n\ninterface WindowManagerSpacingSettings {\n  gap: number | null;\n  padding: number | null;\n  margins: Rect | null;\n  onChangeGap: (v: number | null) => void;\n  onChangePadding: (v: number | null) => void;\n  onChangeMargins: (side: keyof Rect, value: number | null) => void;\n  onClear?: () => void;\n}\n\nexport function WindowManagerSpacingSettings(\n  props: WindowManagerSpacingSettings,\n) {\n  const {\n    gap,\n    padding,\n    margins,\n    onChangeGap,\n    onChangePadding,\n    onChangeMargins,\n    onClear,\n  } = props;\n\n  const { t } = useTranslation();\n\n  return (\n    <SettingsGroup>\n      {onClear && (\n        <SettingsOption>\n          <span>{t(\"header.labels.seelen_wm\")}</span>\n          <Button onClick={onClear}>\n            <Icon iconName=\"IoTrash\" size={14} />\n          </Button>\n        </SettingsOption>\n      )}\n      <SettingsOption>\n        <b>{t(\"wm.space_between_containers\")}</b>\n        <InputNumber\n          value={gap}\n          onChange={onChangeGap}\n          min={0}\n          placeholder={t(\"inherit\")}\n        />\n      </SettingsOption>\n      <SettingsOption>\n        <b>{t(\"wm.workspace_padding\")}</b>\n        <InputNumber\n          value={padding}\n          onChange={onChangePadding}\n          min={0}\n          placeholder={t(\"inherit\")}\n        />\n      </SettingsOption>\n      <SettingsSubGroup label={t(\"wm.workspace_offset\")}>\n        <SettingsOption>\n          <span>{t(\"sides.left\")}</span>\n          <InputNumber\n            value={margins?.left}\n            onChange={onChangeMargins.bind(null, \"left\")}\n            min={0}\n            placeholder={t(\"inherit\")}\n          />\n        </SettingsOption>\n        <SettingsOption>\n          <span>{t(\"sides.top\")}</span>\n          <InputNumber\n            value={margins?.top}\n            onChange={onChangeMargins.bind(null, \"top\")}\n            min={0}\n            placeholder={t(\"inherit\")}\n          />\n        </SettingsOption>\n        <SettingsOption>\n          <span>{t(\"sides.right\")}</span>\n          <InputNumber\n            value={margins?.right}\n            onChange={onChangeMargins.bind(null, \"right\")}\n            min={0}\n            placeholder={t(\"inherit\")}\n          />\n        </SettingsOption>\n        <SettingsOption>\n          <span>{t(\"sides.bottom\")}</span>\n          <InputNumber\n            value={margins?.bottom}\n            onChange={onChangeMargins.bind(null, \"bottom\")}\n            min={0}\n            placeholder={t(\"inherit\")}\n          />\n        </SettingsOption>\n      </SettingsSubGroup>\n    </SettingsGroup>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/main/infra/Others.tsx",
    "content": "import { InputNumber, Select } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { getWmConfig, setWmDragBehavior, setWmResizeDelta } from \"../../application.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../../../components/SettingsBox/index.tsx\";\nimport { WmDragBehavior } from \"@seelen-ui/lib/types\";\n\nexport const OthersConfigs = () => {\n  const wmConfig = getWmConfig();\n  const resizeDelta = wmConfig.resizeDelta;\n  const dragBehavior = wmConfig.dragBehavior;\n\n  const { t } = useTranslation();\n\n  const onChangeResizeDelta = (value: number | null) => {\n    setWmResizeDelta(value || 0);\n  };\n\n  const onChangeDragBehavior = (value: WmDragBehavior) => {\n    setWmDragBehavior(value);\n  };\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption>\n          <b>{t(\"wm.drag_behavior\")}</b>\n          <Select\n            style={{ width: \"200px\" }}\n            value={dragBehavior}\n            options={[\n              {\n                label: t(\"wm.drag_behavior_options.sort\"),\n                value: WmDragBehavior.Sort,\n              },\n              {\n                label: t(\"wm.drag_behavior_options.swap\"),\n                value: WmDragBehavior.Swap,\n              },\n            ]}\n            onSelect={onChangeDragBehavior}\n          />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption>\n          <b>{t(\"wm.resize_delta\")}</b>\n          <InputNumber value={resizeDelta} onChange={onChangeResizeDelta} min={1} max={40} />\n        </SettingsOption>\n      </SettingsGroup>\n    </>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/main/infra/index.module.css",
    "content": "\n"
  },
  {
    "path": "src/ui/react/settings/modules/WindowManager/main/infra/index.tsx",
    "content": "import { SeelenWindowManagerWidgetId } from \"@seelen-ui/lib\";\nimport type { PluginId } from \"@seelen-ui/lib/types\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { ConfigProvider, Select, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BorderSettings } from \"../../border/infra.tsx\";\n\nimport { getWmConfig, setWmDefaultLayout, setWmEnabled } from \"../../application.ts\";\nimport { plugins } from \"../../../../state/resources.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../../../components/SettingsBox/index.tsx\";\nimport { WmAnimationsSettings } from \"./Animations.tsx\";\nimport { GlobalPaddings } from \"./GlobalPaddings.tsx\";\nimport { OthersConfigs } from \"./Others.tsx\";\n\nexport function WindowManagerSettings() {\n  const wmSettings = getWmConfig();\n  const defaultLayout = wmSettings.defaultLayout;\n\n  const { t } = useTranslation();\n\n  const onToggleEnable = (value: boolean) => {\n    setWmEnabled(value);\n  };\n\n  const onSelectLayout = (value: PluginId) => {\n    setWmDefaultLayout(value);\n  };\n\n  const layouts = plugins.value.filter((plugin) => plugin.target === SeelenWindowManagerWidgetId);\n  const usingLayout = layouts.find((plugin) => plugin.id === defaultLayout);\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption>\n          <div>\n            <b>{t(\"wm.enable\")}</b>\n          </div>\n          <Switch checked={wmSettings.enabled} onChange={onToggleEnable} />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <ConfigProvider componentDisabled={!wmSettings.enabled}>\n        <SettingsGroup>\n          <SettingsOption>\n            <div>\n              <b>{t(\"wm.layout\")}:</b>\n            </div>\n            <Select\n              style={{ width: \"200px\" }}\n              value={defaultLayout}\n              options={layouts.map((layout) => ({\n                key: layout.id,\n                label: <ResourceText text={layout.metadata.displayName} />,\n                value: layout.id,\n              }))}\n              onSelect={onSelectLayout}\n            />\n          </SettingsOption>\n          <div>\n            <p>\n              <b>{t(\"wm.description\")}:</b>\n              <ResourceText text={usingLayout?.metadata.description || \"-\"} />,\n            </p>\n          </div>\n        </SettingsGroup>\n\n        <GlobalPaddings />\n        <BorderSettings />\n        <WmAnimationsSettings />\n        <OthersConfigs />\n      </ConfigProvider>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/app/default.ts",
    "content": "import { type AppConfig, AppIdentifierType, MatchingStrategy } from \"@seelen-ui/lib/types\";\n\nexport const defaultAppConfig: AppConfig = {\n  name: \"New App\",\n  identifier: {\n    id: \"new-app.exe\",\n    matchingStrategy: MatchingStrategy.Equals,\n    kind: AppIdentifierType.Exe,\n    negation: false,\n    or: [],\n    and: [],\n  },\n  options: [],\n  isBundled: false,\n  category: null,\n  boundMonitor: null,\n  boundWorkspace: null,\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/app/filters.ts",
    "content": "export const getSorterByText = (key: string) => (paramA: anyObject, paramB: anyObject) => {\n  const a = String(paramA[key]).toLowerCase();\n  const b = String(paramB[key]).toLowerCase();\n  if (a < b) {\n    return -1;\n  }\n  if (a > b) {\n    return 1;\n  }\n  return 0;\n};\n\nexport const getSorterByBool = (key: string) => (paramA: anyObject, paramB: anyObject) => {\n  const a = Boolean(paramA[key]);\n  const b = Boolean(paramB[key]);\n  if (!a && b) {\n    return 1;\n  }\n  if (a && !b) {\n    return -1;\n  }\n  return 0;\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/app/reducer.ts",
    "content": "import type { AppConfig } from \"@seelen-ui/lib/types\";\nimport { appsConfig, settings } from \"../../../state/mod\";\n\ninterface AppPayload {\n  idx: number;\n}\n\nfunction setConfigByApps(newState: AppConfig[]) {\n  settings.value = {\n    ...settings.value,\n    byApp: newState.filter((app) => !app.isBundled),\n  };\n}\n\nexport const actions = {\n  delete: (payload: number) => {\n    const newState = [...appsConfig.value].filter((_, idx) => idx !== payload);\n    setConfigByApps(newState);\n  },\n\n  deleteMany: (payload: number[]) => {\n    const newState = [...appsConfig.value].filter((_, idx) => !payload.includes(idx));\n    setConfigByApps(newState);\n  },\n\n  push: (payload: AppConfig[]) => {\n    setConfigByApps([...appsConfig.value, ...payload]);\n  },\n\n  replace: (payload: AppPayload & { app: AppConfig }) => {\n    const { idx, app } = payload;\n    const newState = [...appsConfig.value];\n    newState[idx] = app;\n    setConfigByApps(newState);\n  },\n\n  swap: (payload: [number, number]) => {\n    const [idx1, idx2] = payload;\n    const App1 = appsConfig.value[idx1];\n    const App2 = appsConfig.value[idx2];\n\n    const newState = [...appsConfig.value];\n    if (App1 && App2) {\n      newState[idx1] = App2;\n      newState[idx2] = App1;\n    }\n\n    setConfigByApps(newState);\n  },\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/domain.ts",
    "content": "import type { AppConfig } from \"@seelen-ui/lib/types\";\n\nexport interface AppConfigurationExtended extends AppConfig {\n  key: number;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/infra/EditModal.tsx",
    "content": "import { AppExtraFlag, type AppIdentifier } from \"@seelen-ui/lib/types\";\nimport { ConfigProvider, Input, Modal, Select, Switch } from \"antd\";\nimport { cloneDeep } from \"lodash\";\nimport { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { defaultAppConfig } from \"../app/default.ts\";\n\nimport type { AppConfigurationExtended } from \"../domain.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../components/SettingsBox/index.tsx\";\nimport { Identifier } from \"./Identifier.tsx\";\nimport cs from \"./index.module.css\";\nimport { appsConfig, settings } from \"../../../state/mod.ts\";\n\ninterface Props {\n  idx?: number;\n  open: boolean;\n  onSave: (app: AppConfigurationExtended) => void;\n  onCancel: () => void;\n  isNew?: boolean;\n  readonlyApp?: AppConfigurationExtended;\n}\n\nconst getAppSelector = (idx: number | undefined, isNew: boolean) => {\n  return idx != null && !isNew ? appsConfig.value[idx]! : cloneDeep(defaultAppConfig);\n};\n\nexport const EditAppModal = ({ idx, onCancel, onSave, isNew, open, readonlyApp }: Props) => {\n  const { t } = useTranslation();\n\n  const monitors = Object.values(settings.value.monitorsV3);\n  const _app = getAppSelector(idx, !!isNew);\n  const initialState = readonlyApp || _app;\n  const isReadonly = !!readonlyApp;\n\n  const [app, setApp] = useState(initialState);\n\n  useEffect(() => {\n    if (isNew && !open) {\n      // reset state on close\n      setApp(initialState);\n    }\n  }, [open]);\n\n  const onInternalSave = () => {\n    onSave(app as AppConfigurationExtended);\n  };\n\n  const updateName = (e: React.ChangeEvent<HTMLInputElement>) => setApp({ ...app, name: e.currentTarget.value });\n  const updateCategory = (e: React.ChangeEvent<HTMLInputElement>) =>\n    setApp({ ...app, category: e.currentTarget.value || null });\n\n  const onChangeIdentifier = (identifier: AppIdentifier) => setApp({ ...app, identifier });\n\n  const onSelectMonitor = (value: number | null) => setApp({ ...app, boundMonitor: value });\n  const onSelectWorkspace = (value: number | null) => setApp({ ...app, boundWorkspace: value });\n\n  const onChangeOption = (option: AppExtraFlag, checked: boolean) => {\n    setApp({\n      ...app,\n      options: checked ? [...app.options, option] : app.options.filter((o) => o !== option),\n    });\n  };\n\n  const monitorsOptions = monitors.map((_, i) => ({\n    label: `Monitor ${i + 1}`,\n    value: i,\n  }));\n  const workspaceOptions = Array.from({ length: 10 }).map((_, i) => ({\n    label: `Workspace ${i + 1}`,\n    value: i,\n  }));\n\n  let title = t(\"apps_configurations.app.title_edit\");\n  let okText = t(\"apps_configurations.app.ok_edit\");\n\n  if (isNew) {\n    title = t(\"apps_configurations.app.title_create\");\n    okText = t(\"apps_configurations.app.ok_create\");\n  }\n\n  if (isReadonly) {\n    title = t(\"apps_configurations.app.title_readonly\");\n    okText = t(\"apps_configurations.app.ok_readonly\");\n  }\n\n  return (\n    <Modal\n      title={title.replace(\"{{name}}\", app.name)}\n      open={open}\n      onCancel={onCancel}\n      onOk={onInternalSave}\n      cancelText={t(\"cancel\")}\n      okText={okText}\n      cancelButtonProps={isReadonly ? { style: { display: \"none\" } } : undefined}\n      centered\n      className={cs.editModal}\n      width=\"90vw\"\n    >\n      <ConfigProvider componentDisabled={isReadonly}>\n        {!!readonlyApp && (\n          <SettingsGroup>\n            <b>{t(\"apps_configurations.bundled_title\")}</b>\n            <p>{t(\"apps_configurations.bundled_msg\")}</p>\n          </SettingsGroup>\n        )}\n\n        <SettingsGroup>\n          <SettingsOption>\n            <span>{t(\"apps_configurations.app.name\")}</span>\n            <Input value={app.name} onChange={updateName} required />\n          </SettingsOption>\n          <SettingsOption>\n            <span>{t(\"apps_configurations.app.category\")}</span>\n            <Input\n              value={app.category || \"\"}\n              placeholder={t(\"apps_configurations.app.category_placeholder\")}\n              onChange={updateCategory}\n            />\n          </SettingsOption>\n        </SettingsGroup>\n\n        <Identifier identifier={app.identifier} onChange={onChangeIdentifier} />\n\n        <SettingsGroup>\n          <SettingsSubGroup label={t(\"apps_configurations.app.bindings\")}>\n            <SettingsOption>\n              <span>{t(\"apps_configurations.app.monitor\")}</span>\n              <Select\n                value={app.boundMonitor}\n                placeholder={t(\"apps_configurations.app.monitor_placeholder\")}\n                allowClear\n                options={monitorsOptions}\n                onChange={onSelectMonitor}\n              />\n            </SettingsOption>\n            <SettingsOption>\n              <span>{t(\"apps_configurations.app.workspace\")}</span>\n              <Select\n                value={app.boundWorkspace}\n                placeholder={t(\"apps_configurations.app.workspace_placeholder\")}\n                allowClear\n                options={workspaceOptions}\n                onChange={onSelectWorkspace}\n              />\n            </SettingsOption>\n          </SettingsSubGroup>\n        </SettingsGroup>\n\n        <SettingsGroup>\n          {Object.values(AppExtraFlag).map((value) => {\n            if (value === AppExtraFlag.Unknown) return null;\n            return (\n              <SettingsOption\n                key={value}\n                label={t(`apps_configurations.app.options.${value}`)}\n                action={\n                  <Switch\n                    value={app.options.includes(value as any as AppExtraFlag)}\n                    onChange={onChangeOption.bind(this, value as any as AppExtraFlag)}\n                  />\n                }\n              />\n            );\n          })}\n        </SettingsGroup>\n      </ConfigProvider>\n    </Modal>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/infra/Identifier.module.css",
    "content": ".removeButton {\n  display: flex;\n  gap: var(--spacing-xs);\n  align-items: center;\n  justify-content: center;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/infra/Identifier.tsx",
    "content": "import { type AppIdentifier, AppIdentifierType, MatchingStrategy } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Input, Select, Switch } from \"antd\";\nimport { cloneDeep } from \"lodash\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { OptionsFromEnum } from \"../../shared/utils/app.ts\";\nimport { defaultAppConfig } from \"../app/default.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../../components/SettingsBox/index.tsx\";\nimport cs from \"./Identifier.module.css\";\n\ninterface Props {\n  identifier: AppIdentifier;\n  onChange: (id: AppIdentifier) => void;\n  onRemove?: () => void;\n}\n\nexport function Identifier({ identifier, onChange, onRemove }: Props) {\n  const { id, kind, matchingStrategy } = identifier;\n\n  const { t } = useTranslation();\n\n  const onChangeId = (e: React.ChangeEvent<HTMLInputElement>) => {\n    onChange({ ...identifier, id: e.currentTarget.value });\n  };\n\n  const onSelectKind = (value: AppIdentifierType) => {\n    onChange({ ...identifier, kind: value });\n  };\n\n  const onSelectMatchingStrategy = (value: MatchingStrategy) => {\n    onChange({ ...identifier, matchingStrategy: value });\n  };\n\n  const onChangeNegation = (value: boolean) => {\n    onChange({ ...identifier, negation: value });\n  };\n\n  const onChangeAndItem = (idx: number, value: AppIdentifier) => {\n    onChange({\n      ...identifier,\n      and: identifier.and.map((id, i) => (i === idx ? value : id)),\n    });\n  };\n\n  const onChangeOrItem = (idx: number, value: AppIdentifier) => {\n    onChange({\n      ...identifier,\n      or: identifier.or.map((id, i) => (i === idx ? value : id)),\n    });\n  };\n\n  const onRemoveAndItem = (idx: number) => {\n    onChange({\n      ...identifier,\n      and: identifier.and.filter((_, i) => i !== idx),\n    });\n  };\n\n  const onRemoveOrItem = (idx: number) => {\n    onChange({ ...identifier, or: identifier.or.filter((_, i) => i !== idx) });\n  };\n\n  const onAddAndItem = () => {\n    onChange({\n      ...identifier,\n      and: [cloneDeep(defaultAppConfig.identifier), ...identifier.and],\n    });\n  };\n\n  const onAddOrItem = () => {\n    onChange({\n      ...identifier,\n      or: [cloneDeep(defaultAppConfig.identifier), ...identifier.or],\n    });\n  };\n\n  return (\n    <SettingsGroup>\n      {onRemove && (\n        <SettingsOption>\n          <span>{t(\"apps_configurations.identifier.remove\")}</span>\n          <Button\n            type=\"text\"\n            danger\n            onClick={onRemove}\n            className={cs.removeButton}\n          >\n            <Icon iconName=\"IoTrash\" />\n          </Button>\n        </SettingsOption>\n      )}\n      <SettingsOption>\n        <span>{t(\"apps_configurations.identifier.id\")}</span>\n        <Input value={id} onChange={onChangeId} />\n      </SettingsOption>\n      <SettingsOption>\n        <span>{t(\"apps_configurations.identifier.kind\")}</span>\n        <Select\n          value={kind}\n          options={OptionsFromEnum(\n            t,\n            AppIdentifierType,\n            \"apps_configurations.identifier.type\",\n          )}\n          onSelect={onSelectKind}\n        />\n      </SettingsOption>\n      <SettingsOption>\n        <span>{t(\"apps_configurations.identifier.matching_strategy\")}</span>\n        <Select\n          value={matchingStrategy}\n          options={OptionsFromEnum(\n            t,\n            MatchingStrategy,\n            \"apps_configurations.identifier.matching_strategy_option\",\n          )}\n          onSelect={onSelectMatchingStrategy}\n        />\n      </SettingsOption>\n      <SettingsOption>\n        <span>{t(\"apps_configurations.identifier.negation\")}</span>\n        <Switch value={identifier.negation} onChange={onChangeNegation} />\n      </SettingsOption>\n\n      <hr />\n\n      <SettingsOption>\n        <b>{t(\"apps_configurations.identifier.and\")}</b>\n        <Button type=\"dashed\" onClick={onAddAndItem}>\n          {t(\"apps_configurations.identifier.add_block\")}\n        </Button>\n      </SettingsOption>\n      {identifier.and.map((id, idx) => (\n        <Identifier\n          key={idx}\n          identifier={id}\n          onChange={(value) => onChangeAndItem(idx, value)}\n          onRemove={() => onRemoveAndItem(idx)}\n        />\n      ))}\n\n      <SettingsOption>\n        <b>{t(\"apps_configurations.identifier.or\")}</b>\n        <Button type=\"dashed\" onClick={onAddOrItem}>\n          {t(\"apps_configurations.identifier.add_block\")}\n        </Button>\n      </SettingsOption>\n      {identifier.or.map((id, idx) => (\n        <Identifier\n          key={idx}\n          identifier={id}\n          onChange={(value) => onChangeOrItem(idx, value)}\n          onRemove={() => onRemoveOrItem(idx)}\n        />\n      ))}\n    </SettingsGroup>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/infra/index.module.css",
    "content": ".container {\n  height: 100%;\n  width: 100%;\n  display: grid;\n  grid-template-rows: 1fr 40px;\n  grid-template-columns: 100%;\n}\n\n.newBtn {\n  width: 100%;\n  background-color: var(--color-green-600);\n  padding: 0;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: inline-block;\n\n  &:hover {\n    background-color: var(--color-green-700) !important;\n  }\n}\n\n.actions {\n  button {\n    width: 100%;\n  }\n}\n\n.table {\n  :global(.ant-pagination-options) {\n    display: none;\n  }\n\n  :global(.ant-empty) {\n    margin: 119px;\n  }\n\n  :global(.ant-table-body) {\n    :global(.ant-table-cell) {\n      text-wrap: nowrap;\n      text-overflow: ellipsis;\n      overflow: hidden;\n    }\n  }\n\n  :global(.ant-pagination) {\n    margin-top: var(--spacing-s) !important;\n    margin-bottom: 0 !important;\n  }\n}\n\n.editModal {\n  :global(.ant-modal-body) {\n    padding-right: var(--spacing-s);\n  }\n\n  :global(.ant-select) {\n    min-width: 150px;\n  }\n}\n\n.footer {\n  position: absolute;\n  bottom: 15px;\n  display: flex;\n  align-items: center;\n  gap: var(--spacing-s);\n\n  button {\n    width: 80px;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/appsConfigurations/infra/infra.tsx",
    "content": "import { type AppConfig, AppExtraFlag } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Input, Modal, Switch, Table, Tooltip } from \"antd\";\nimport type { ColumnsType, ColumnType } from \"antd/es/table\";\nimport type { TFunction } from \"i18next\";\nimport { cloneDeep, debounce } from \"lodash\";\nimport { type ChangeEvent, useCallback, useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { cx } from \"../../shared/utils/app.ts\";\nimport { getSorterByBool, getSorterByText } from \"../app/filters.ts\";\n\nimport type { AppConfigurationExtended } from \"../domain.ts\";\n\nimport { EditAppModal } from \"./EditModal.tsx\";\nimport cs from \"./index.module.css\";\nimport { actions } from \"../app/reducer.ts\";\nimport { appsConfig } from \"../../../state/mod.ts\";\n\nconst ReadonlySwitch = (value: boolean, record: AppConfigurationExtended, _index: number) => {\n  return (\n    <Switch\n      value={value}\n      disabled\n      className={cx({\n        [cs.readonly!]: record.isBundled,\n      })}\n    />\n  );\n};\n\nconst getColumns = (t: TFunction): ColumnsType<AppConfigurationExtended> => {\n  return [\n    {\n      title: t(\"apps_configurations.app.name\"),\n      dataIndex: \"name\",\n      key: \"name\",\n      fixed: \"left\",\n      width: 120,\n      sorter: getSorterByText(\"name\"),\n      render: (name) => (\n        <Tooltip placement=\"topLeft\" title={name}>\n          {name}\n        </Tooltip>\n      ),\n    },\n    {\n      title: t(\"apps_configurations.app.category\"),\n      dataIndex: \"category\",\n      key: \"category\",\n      width: 120,\n      render(value, _record, _index) {\n        return value || \"-\";\n      },\n      sorter: getSorterByText(\"category\"),\n    },\n    ...Object.values(AppExtraFlag)\n      .filter((option) => option !== AppExtraFlag.Unknown)\n      .map(\n        (option) => ({\n          title: t(`apps_configurations.app.options.${option}`),\n          dataIndex: option,\n          key: option,\n          align: \"center\",\n          width: 140,\n          render: ReadonlySwitch,\n          sorter: getSorterByBool(option),\n        } as ColumnType<AppConfigurationExtended>),\n      ),\n    {\n      title: <ActionsTitle />,\n      key: \"operation\",\n      fixed: \"right\",\n      align: \"center\",\n      width: 60,\n      render: (_, record, index) => <Actions record={record} index={index} />,\n    },\n  ];\n};\n\nfunction ActionsTitle() {\n  const [isModalOpen, setIsModalOpen] = useState(false);\n\n  const { t } = useTranslation();\n\n  const showModal = () => setIsModalOpen(true);\n  const onCancel = () => setIsModalOpen(false);\n  const onSave = (app: AppConfig) => {\n    actions.push([app]);\n    setIsModalOpen(false);\n  };\n\n  return (\n    <div>\n      <EditAppModal open={isModalOpen} isNew onSave={onSave} onCancel={onCancel} />\n      <Button className={cs.newBtn} type=\"primary\" onClick={showModal}>\n        {t(\"apps_configurations.new\")}\n      </Button>\n    </div>\n  );\n}\n\nfunction Actions({ record }: { record: AppConfigurationExtended; index: number }) {\n  const [isModalOpen, setIsModalOpen] = useState(false);\n\n  const showModal = () => setIsModalOpen(true);\n  const onCancel = () => setIsModalOpen(false);\n  const onSave = (app: AppConfigurationExtended) => {\n    if (record.isBundled) {\n      let newApp = cloneDeep(app);\n      newApp.isBundled = false;\n      actions.push([newApp]);\n    } else {\n      actions.replace({ idx: record.key, app });\n    }\n    setIsModalOpen(false);\n  };\n\n  return (\n    <div className={cs.actions}>\n      {isModalOpen && (\n        <EditAppModal\n          open\n          idx={record.isBundled ? undefined : record.key}\n          onSave={onSave}\n          onCancel={onCancel}\n          readonlyApp={record.isBundled ? record : undefined}\n        />\n      )}\n      <Button type={record.isBundled ? \"default\" : \"primary\"} onClick={showModal}>\n        {record.isBundled ? <Icon iconName=\"FaEye\" /> : <Icon iconName=\"MdOutlineEdit\" />}\n      </Button>\n    </div>\n  );\n}\n\nexport function AppsConfiguration() {\n  const [delay, setDelay] = useState(300);\n  const [loading, setLoading] = useState(true);\n  const [selectedAppsKey, setSelectedAppsKey] = useState<number[]>([]);\n  const [searched, setSearched] = useState(\"\");\n  const [data, setData] = useState<AppConfigurationExtended[]>([]);\n\n  useEffect(() => {\n    const data: AppConfigurationExtended[] = [];\n\n    appsConfig.value.forEach((app, index) => data.unshift({ ...app, key: index }));\n\n    setTimeout(() => {\n      setData(\n        data.filter((app) => {\n          return (\n            app.name.toLowerCase().includes(searched) ||\n            app.identifier.id.toLowerCase().includes(searched) ||\n            app.identifier.and.some((id) => id.id.toLowerCase().includes(searched)) ||\n            app.identifier.or.some((id) => id.id.toLowerCase().includes(searched))\n          );\n        }),\n      );\n      setLoading(false);\n      setDelay(0);\n    }, delay);\n  }, [appsConfig.value, searched]);\n\n  const { t } = useTranslation();\n\n  const performSwap = useCallback(() => {\n    actions.swap(selectedAppsKey as [number, number]);\n  }, [selectedAppsKey]);\n\n  const confirmDelete = useCallback(() => {\n    const modal = Modal.confirm({\n      title: t(\"apps_configurations.confirm_delete_title\"),\n      content: t(\"apps_configurations.confirm_delete\"),\n      okText: t(\"delete\"),\n      onOk: () => {\n        actions.deleteMany(selectedAppsKey);\n        setSelectedAppsKey([]);\n        modal.destroy();\n      },\n      okButtonProps: { danger: true },\n      cancelText: t(\"cancel\"),\n      centered: true,\n    });\n  }, [selectedAppsKey]);\n\n  const onSearch = useCallback(\n    debounce((e: ChangeEvent<HTMLInputElement>) => {\n      setSearched(e.currentTarget.value.toLowerCase());\n    }, 200),\n    [],\n  );\n\n  const columns = getColumns(t);\n  columns[0]!.title = (\n    <Input\n      onChange={onSearch}\n      onClick={(e) => e.stopPropagation()}\n      placeholder={t(\"apps_configurations.search\")}\n    />\n  );\n\n  return (\n    <div className={cs.container}>\n      <Table\n        loading={loading}\n        dataSource={data}\n        columns={columns}\n        pagination={{ pageSize: 25 }}\n        scroll={{ y: \"calc(100vh - 180px)\", x: \"100vw\" }}\n        className={cs.table}\n        rowSelection={{\n          selectedRowKeys: selectedAppsKey,\n          onChange(selectedRowKeys, _selectedRows, _info) {\n            setSelectedAppsKey(selectedRowKeys as number[]);\n          },\n          getCheckboxProps(record) {\n            return {\n              disabled: record.isBundled,\n            };\n          },\n        }}\n      />\n      <div className={cs.footer}>\n        {\n          /* <Button onClick={importApps}>{t(\"apps_configurations.import\")}</Button>\n        <Button onClick={exportApps} disabled={!selectedAppsKey.length}>\n          {t(\"apps_configurations.export\")}\n        </Button> */\n        }\n        <Button type=\"primary\" danger disabled={!selectedAppsKey.length} onClick={confirmDelete}>\n          {t(\"apps_configurations.delete\")}\n        </Button>\n        <Button onClick={performSwap} type=\"primary\" disabled={selectedAppsKey.length !== 2}>\n          {t(\"apps_configurations.swap\")}\n        </Button>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/developer/application.ts",
    "content": "import { dialog } from \"@seelen-ui/lib/tauri\";\nimport { path } from \"@tauri-apps/api\";\nimport { settings } from \"../../state/mod\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n\nexport async function simulatePerm(widgetId: string, perm: string): Promise<void> {\n  await invoke(SeelenCommand.SimulatePerm, { widgetId, perm });\n}\n\n/**\n * Gets the devTools setting\n */\nexport function getDevTools(): boolean {\n  return settings.value.devTools;\n}\n\n/**\n * Sets the devTools setting\n */\nexport function setDevTools(devTools: boolean) {\n  settings.value = {\n    ...settings.value,\n    devTools,\n  };\n}\n\n/**\n * Loads a custom configuration file\n */\nexport async function LoadCustomConfigFile() {\n  const file = await dialog.open({\n    defaultPath: await path.homeDir(),\n    multiple: false,\n    title: \"Select settings file\",\n    filters: [{ name: \"settings\", extensions: [\"json\"] }],\n  });\n\n  if (!file) {\n    return;\n  }\n\n  settings.value = await invoke(SeelenCommand.StateGetSettings, { path: file });\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/developer/infra.tsx",
    "content": "import { SeelenCommand } from \"@seelen-ui/lib\";\nimport { path } from \"@tauri-apps/api\";\nimport { invoke } from \"@tauri-apps/api/core\";\nimport { Button, Input, Select, Switch } from \"antd\";\nimport { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { resolveDataPath } from \"../shared/config/infra.ts\";\n\nimport { getWegConfig, patchWegConfig } from \"../seelenweg/application.ts\";\nimport { getDevTools, LoadCustomConfigFile, setDevTools, simulatePerm } from \"./application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../components/SettingsBox/index.tsx\";\n\nexport function DeveloperTools() {\n  const devTools = getDevTools();\n  const showEndTask = getWegConfig().showEndTask;\n\n  const [simWidgetId, setSimWidgetId] = useState(\"\");\n  const [simPerm, setSimPerm] = useState<string>(\"run\");\n  const [simResult, setSimResult] = useState<string | null>(null);\n\n  const { t } = useTranslation();\n\n  function onToggleDevTools(value: boolean) {\n    setDevTools(value);\n  }\n\n  async function openSettingsFile() {\n    invoke(SeelenCommand.OpenFile, {\n      path: await resolveDataPath(\"settings.json\"),\n    });\n  }\n\n  async function openInstallFolder() {\n    invoke(SeelenCommand.OpenFile, { path: await path.resourceDir() });\n  }\n\n  async function openDataFolder() {\n    invoke(SeelenCommand.OpenFile, { path: await path.appDataDir() });\n  }\n\n  async function onSimulatePerm() {\n    setSimResult(null);\n    try {\n      await simulatePerm(simWidgetId, simPerm);\n      setSimResult(t(\"devtools.simulate_perm.result_allowed\"));\n    } catch {\n      setSimResult(t(\"devtools.simulate_perm.result_denied\"));\n    }\n  }\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption>\n          <b>{t(\"devtools.enable\")}</b>\n          <Switch value={devTools} onChange={onToggleDevTools} />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption>\n          <b>{t(\"weg.show_end_task\")}</b>\n          <Switch\n            checked={showEndTask}\n            onChange={(value) => patchWegConfig({ showEndTask: value })}\n          />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"devtools.app_folders\")}>\n          <SettingsOption>\n            <span>{t(\"devtools.install_folder\")}</span>\n            <Button onClick={openInstallFolder}>{t(\"open\")}</Button>\n          </SettingsOption>\n          <SettingsOption>\n            <span>{t(\"devtools.data_folder\")}</span>\n            <Button onClick={openDataFolder}>{t(\"open\")}</Button>\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption>\n          <span>{t(\"devtools.settings_file\")}</span>\n          <Button onClick={openSettingsFile}>{t(\"open\")}</Button>\n        </SettingsOption>\n        <SettingsOption>\n          <span>{t(\"devtools.custom_config_file\")}:</span>\n          <Button onClick={LoadCustomConfigFile}>{t(\"devtools.load\")}</Button>\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"devtools.simulate_perm.label\")}>\n          <SettingsOption>\n            <Input\n              value={simWidgetId}\n              onChange={(e) => {\n                setSimWidgetId(e.currentTarget.value);\n                setSimResult(null);\n              }}\n              placeholder={t(\"devtools.simulate_perm.widget_id_placeholder\")}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <Select\n              value={simPerm}\n              onChange={(v) => {\n                setSimPerm(v);\n                setSimResult(null);\n              }}\n              options={[{ value: \"run\" }, { value: \"open_file\" }]}\n            />\n            <Button onClick={onSimulatePerm} disabled={!simWidgetId}>\n              {t(\"devtools.simulate_perm.trigger\")}\n            </Button>\n          </SettingsOption>\n          {simResult && (\n            <SettingsOption>\n              <span>{simResult}</span>\n            </SettingsOption>\n          )}\n        </SettingsSubGroup>\n      </SettingsGroup>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/extras/application.ts",
    "content": "import { settings } from \"../../state/mod\";\nimport type { UpdaterSettings } from \"@seelen-ui/lib/types\";\n\n/**\n * Gets the Discord RPC setting\n */\nexport function getDrpc(): boolean {\n  return settings.value.drpc;\n}\n\n/**\n * Sets the Discord RPC setting\n */\nexport function setDrpc(drpc: boolean) {\n  settings.value = {\n    ...settings.value,\n    drpc,\n  };\n}\n\n/**\n * Gets the updater settings\n */\nexport function getUpdaterSettings(): UpdaterSettings {\n  return settings.value.updater;\n}\n\n/**\n * Patches the updater settings with partial updates\n */\nexport function patchUpdaterSettings(patch: Partial<UpdaterSettings>) {\n  settings.value = {\n    ...settings.value,\n    updater: {\n      ...settings.value.updater,\n      ...patch,\n    },\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/extras/infra.module.css",
    "content": ".info {\n  a {\n    color: var(--color-blue-500);\n    &:hover {\n      color: var(--color-blue-700);\n    }\n  }\n\n  .logo {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: var(--spacing-l);\n    padding: 4rem 0;\n\n    > img {\n      object-fit: contain;\n      width: 150px;\n      aspect-ratio: 1/1;\n    }\n\n    figcaption {\n      font-size: 1.4rem;\n      font-weight: 600;\n      color: var(--color-black);\n    }\n  }\n\n  .version {\n    font-size: 0.8rem;\n    font-weight: 700;\n    color: var(--color-black);\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  .info .logo > img {\n    filter: invert(1);\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/extras/infrastructure.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { process } from \"@seelen-ui/lib/tauri\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Select, Switch, Tooltip } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { EnvConfig } from \"../shared/config/infra.ts\";\nimport cs from \"./infra.module.css\";\n\nimport { getDrpc, getUpdaterSettings, patchUpdaterSettings, setDrpc } from \"./application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../components/SettingsBox/index.tsx\";\nimport { UpdateChannel } from \"@seelen-ui/lib/types\";\n\nconst [isDevMode, isMsixBuild, isFixed] = await Promise.all([\n  invoke(SeelenCommand.IsDevMode),\n  invoke(SeelenCommand.IsAppxPackage),\n  invoke(SeelenCommand.HasFixedRuntime),\n]);\n\nexport function Information() {\n  const drpc = getDrpc();\n  const updaterSettings = getUpdaterSettings();\n\n  const { t } = useTranslation();\n\n  function onToggleDrpc(value: boolean) {\n    setDrpc(value);\n  }\n\n  function onChangeUpdateChannel(channel: UpdateChannel) {\n    patchUpdaterSettings({ channel });\n  }\n\n  return (\n    <div className={cs.info}>\n      <figure className={cs.logo}>\n        <img src=\"./company_logo.svg\" alt=\"Seelen Corp.\" />\n        <figcaption>Seelen Corp.</figcaption>\n      </figure>\n\n      <SettingsGroup>\n        <SettingsSubGroup label=\"Seelen UI\">\n          <SettingsOption\n            label={t(\"extras.version\")}\n            description={isFixed ? t(\"extras.version_fixed\") : false}\n            action={\n              <span className={cs.version}>\n                v{EnvConfig.version} {isDevMode && \"(dev)\"} {isMsixBuild && \"(msix)\"} {isFixed && \"(fixed)\"}\n              </span>\n            }\n          />\n          <SettingsOption\n            label={t(\"update.channel\")}\n            action={\n              <Select\n                value={updaterSettings.channel}\n                disabled={isMsixBuild}\n                onChange={onChangeUpdateChannel}\n                options={Object.values(UpdateChannel).map((c) => ({\n                  value: c,\n                  label: c,\n                }))}\n              />\n            }\n          />\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"extras.links\")}>\n          <SettingsOption>\n            <span>Github:</span>\n            <a href=\"https://github.com/eythaann/seelen-ui\" target=\"_blank\">\n              github.com/eythaann/seelen-ui\n            </a>\n          </SettingsOption>\n          <SettingsOption>\n            <span>Discord:</span>\n            <a href=\"https://discord.gg/ABfASx5ZAJ\" target=\"_blank\">\n              discord.gg/ABfASx5ZAJ\n            </a>\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption>\n          <b>Discord RPC</b>\n          <Switch value={drpc} onChange={onToggleDrpc} />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption>\n          <b style={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}>\n            {t(\"extras.clear_icons\")}\n            <Tooltip title={t(\"extras.clear_icons_tooltip\")}>\n              <Icon iconName=\"LuCircleHelp\" />\n            </Tooltip>\n          </b>\n          <Button\n            type=\"dashed\"\n            danger\n            onClick={() => invoke(SeelenCommand.StateDeleteCachedIcons)}\n            style={{ width: \"50px\" }}\n          >\n            <Icon iconName=\"IoReload\" size={12} />\n          </Button>\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption>\n          <b>{t(\"extras.relaunch\")}</b>\n          <Button type=\"dashed\" onClick={() => process.relaunch()} style={{ width: \"50px\" }}>\n            <Icon iconName=\"IoReload\" size={12} />\n          </Button>\n        </SettingsOption>\n        <SettingsOption>\n          <b>{t(\"extras.exit\")}</b>\n          <Button type=\"dashed\" danger onClick={() => process.exit(0)} style={{ width: \"50px\" }}>\n            <Icon iconName=\"IoClose\" />\n          </Button>\n        </SettingsOption>\n      </SettingsGroup>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/fancyToolbar/application.ts",
    "content": "import { settings } from \"../../state/mod\";\nimport type { FancyToolbarSettings, FancyToolbarSide, HideMode } from \"@seelen-ui/lib/types\";\n\n/**\n * Patches the FancyToolbar configuration with partial updates.\n * This helper function simplifies updating the toolbar settings by handling\n * the nested structure automatically.\n *\n * @example\n * patchToolbarConfig({ enabled: true, height: 40 });\n */\nexport function patchToolbarConfig(patch: Partial<FancyToolbarSettings>) {\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      \"@seelen/fancy-toolbar\": {\n        ...settings.value.byWidget[\"@seelen/fancy-toolbar\"],\n        ...patch,\n      },\n    },\n  };\n}\n\n/**\n * Gets the current FancyToolbar configuration\n */\nexport function getToolbarConfig(): FancyToolbarSettings {\n  return settings.value.byWidget[\"@seelen/fancy-toolbar\"];\n}\n\n/**\n * Sets the enabled state\n */\nexport function setToolbarEnabled(enabled: boolean) {\n  patchToolbarConfig({ enabled });\n}\n\n/**\n * Sets the toolbar item size\n */\nexport function setToolbarItemSize(itemSize: number) {\n  patchToolbarConfig({ itemSize });\n}\n\n/**\n * Sets the toolbar margin\n */\nexport function setToolbarMargin(margin: number) {\n  patchToolbarConfig({ margin });\n}\n\n/**\n * Sets the toolbar padding\n */\nexport function setToolbarPadding(padding: number) {\n  patchToolbarConfig({ padding });\n}\n\n/**\n * Sets the toolbar position\n */\nexport function setToolbarPosition(position: FancyToolbarSide) {\n  patchToolbarConfig({ position });\n}\n\n/**\n * Sets the hide mode\n */\nexport function setToolbarHideMode(hideMode: HideMode) {\n  patchToolbarConfig({ hideMode });\n}\n\n/**\n * Sets the delay to show\n */\nexport function setToolbarDelayToShow(delayToShow: number) {\n  patchToolbarConfig({ delayToShow });\n}\n\n/**\n * Sets the delay to hide\n */\nexport function setToolbarDelayToHide(delayToHide: number) {\n  patchToolbarConfig({ delayToHide });\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/fancyToolbar/infra.tsx",
    "content": "import { FancyToolbarSide, HideMode } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, InputNumber, Select, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { OptionsFromEnum } from \"../shared/utils/app.ts\";\nimport {\n  getToolbarConfig,\n  setToolbarDelayToHide,\n  setToolbarDelayToShow,\n  setToolbarEnabled,\n  setToolbarHideMode,\n  setToolbarItemSize,\n  setToolbarMargin,\n  setToolbarPadding,\n  setToolbarPosition,\n} from \"./application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../components/SettingsBox/index.tsx\";\nimport Compact from \"antd/es/space/Compact\";\n\nexport function FancyToolbarSettings() {\n  const settings = getToolbarConfig();\n  const delayToShow = settings.delayToShow;\n  const delayToHide = settings.delayToHide;\n\n  const { t } = useTranslation();\n\n  const onToggleEnable = (value: boolean) => {\n    setToolbarEnabled(value);\n  };\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption>\n          <b>{t(\"toolbar.enable\")}</b>\n          <Switch checked={settings.enabled} onChange={onToggleEnable} />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"toolbar.label\")}>\n          <SettingsOption\n            label={t(\"toolbar.item_size\")}\n            action={\n              <InputNumber\n                value={settings.itemSize}\n                onChange={(value) => setToolbarItemSize(value || 0)}\n                min={0}\n              />\n            }\n          />\n\n          <SettingsOption\n            label={t(\"toolbar.padding\")}\n            action={\n              <InputNumber\n                value={settings.padding}\n                onChange={(value) => setToolbarPadding(value || 0)}\n                min={0}\n              />\n            }\n          />\n\n          <SettingsOption\n            label={t(\"toolbar.margin\")}\n            action={\n              <InputNumber\n                value={settings.margin}\n                onChange={(value) => setToolbarMargin(value || 0)}\n                min={0}\n              />\n            }\n          />\n\n          <SettingsOption\n            label={t(\"toolbar.dock_side\")}\n            action={\n              <Compact>\n                {Object.values(FancyToolbarSide).map((side) => (\n                  <Button\n                    key={side}\n                    type={side === settings.position ? \"primary\" : \"default\"}\n                    onClick={() => setToolbarPosition(side)}\n                  >\n                    <Icon iconName={`CgToolbar${side}`} size={18} />\n                  </Button>\n                ))}\n              </Compact>\n            }\n          />\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup\n          label={\n            <SettingsOption>\n              <b>{t(\"toolbar.auto_hide\")}</b>\n              <Select\n                style={{ width: \"120px\" }}\n                value={settings.hideMode}\n                options={OptionsFromEnum(t, HideMode, \"toolbar.hide_mode\")}\n                onChange={(value) => setToolbarHideMode(value)}\n              />\n            </SettingsOption>\n          }\n        >\n          <SettingsOption>\n            <span>{t(\"toolbar.delay_to_show\")} (ms)</span>\n            <InputNumber\n              value={delayToShow}\n              min={0}\n              disabled={settings.hideMode === HideMode.Never}\n              onChange={(value) => setToolbarDelayToShow(value || 0)}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <span>{t(\"toolbar.delay_to_hide\")} (ms)</span>\n            <InputNumber\n              value={delayToHide}\n              min={0}\n              disabled={settings.hideMode === HideMode.Never}\n              onChange={(value) => setToolbarDelayToHide(value || 0)}\n            />\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/general/application.ts",
    "content": "import { needRestart, settings } from \"../../state/mod\";\nimport type { PerformanceModeSettings, StartOfWeek } from \"@seelen-ui/lib/types\";\n\n/**\n * Gets the current language setting\n */\nexport function getLanguage(): string | null {\n  return settings.value.language;\n}\n\n/**\n * Sets the language\n */\nexport function setLanguage(language: string | null) {\n  settings.value = {\n    ...settings.value,\n    language,\n  };\n}\n\n/**\n * Gets the date format\n */\nexport function getDateFormat(): string {\n  return settings.value.dateFormat;\n}\n\n/**\n * Sets the date format\n */\nexport function setDateFormat(dateFormat: string) {\n  settings.value = {\n    ...settings.value,\n    dateFormat,\n  };\n}\n\n/**\n * Gets the start of week setting\n */\nexport function getStartOfWeek(): StartOfWeek {\n  return settings.value.startOfWeek;\n}\n\n/**\n * Sets the start of week\n */\nexport function setStartOfWeek(startOfWeek: StartOfWeek) {\n  settings.value = {\n    ...settings.value,\n    startOfWeek,\n  };\n}\n\n/**\n * Gets the performance mode settings\n */\nexport function getPerformanceMode(): PerformanceModeSettings {\n  return settings.value.performanceMode;\n}\n\n/**\n * Sets the performance mode settings\n */\nexport function setPerformanceMode(performanceMode: PerformanceModeSettings) {\n  settings.value = {\n    ...settings.value,\n    performanceMode,\n  };\n}\n\n/**\n * Gets the polling interval (seconds)\n */\nexport function getPollingInterval(): number {\n  return settings.value.pollingInterval;\n}\n\n/**\n * Sets the polling interval (seconds, minimum 1)\n */\nexport function setPollingInterval(pollingInterval: number) {\n  settings.value = {\n    ...settings.value,\n    pollingInterval: Math.max(1, Math.floor(pollingInterval)),\n  };\n}\n\n/**\n * Gets the hardware acceleration setting\n */\nexport function getHardwareAcceleration(): boolean {\n  return settings.value.hardwareAcceleration;\n}\n\n/**\n * Sets the hardware acceleration setting\n */\nexport function setHardwareAcceleration(hardwareAcceleration: boolean) {\n  needRestart.value = true;\n  settings.value = {\n    ...settings.value,\n    hardwareAcceleration,\n  };\n}\n\n/**\n * Patches the performance mode settings with partial updates\n */\nexport function patchPerformanceMode(patch: Partial<PerformanceModeSettings>) {\n  settings.value = {\n    ...settings.value,\n    performanceMode: {\n      ...settings.value.performanceMode,\n      ...patch,\n    },\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/general/infra/Colors.tsx",
    "content": "import { SeelenCommand } from \"@seelen-ui/lib\";\nimport { invoke } from \"@tauri-apps/api/core\";\nimport { ColorPicker } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { uiColors } from \"../../../state/system.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../../components/SettingsBox/index.tsx\";\nimport cs from \"./index.module.css\";\n\nexport function Colors() {\n  const colors = uiColors.value;\n\n  const { t } = useTranslation();\n\n  return (\n    <SettingsGroup>\n      <SettingsOption>\n        <b>{t(\"general.accent_color\")}</b>\n        <div\n          onClickCapture={(e) => {\n            e.stopPropagation();\n            invoke(SeelenCommand.OpenFile, { path: \"ms-settings:colors\" })\n              .catch(console.error);\n          }}\n        >\n          <ColorPicker value={colors.accent} disabledAlpha showText />\n        </div>\n      </SettingsOption>\n      <div className={cs.palette}>\n        <div style={{ backgroundColor: colors.accent_darkest }} />\n        <div style={{ backgroundColor: colors.accent_darker }} />\n        <div style={{ backgroundColor: colors.accent_dark }} />\n        <div style={{ backgroundColor: colors.accent }} />\n        <div style={{ backgroundColor: colors.accent_light }} />\n        <div style={{ backgroundColor: colors.accent_lighter }} />\n        <div style={{ backgroundColor: colors.accent_lightest }} />\n      </div>\n    </SettingsGroup>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/general/infra/Performance.tsx",
    "content": "import { PerformanceMode, type PerformanceModeSettings } from \"@seelen-ui/lib/types\";\nimport { Select } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../components/SettingsBox/index.tsx\";\n\nimport { getPerformanceMode, patchPerformanceMode } from \"../application.ts\";\n\nexport function PerformanceSettings() {\n  const perf = getPerformanceMode();\n\n  const { t } = useTranslation();\n\n  function patchPerfSettings(patch: Partial<PerformanceModeSettings>) {\n    patchPerformanceMode(patch);\n  }\n\n  const options: { label: string; value: PerformanceMode }[] = [\n    {\n      label: t(\"general.performance_mode.options.disabled\"),\n      value: PerformanceMode.Disabled,\n    },\n    {\n      label: t(\"general.performance_mode.options.minimal\"),\n      value: PerformanceMode.Minimal,\n    },\n    {\n      label: t(\"general.performance_mode.options.extreme\"),\n      value: PerformanceMode.Extreme,\n    },\n  ];\n\n  return (\n    <SettingsGroup>\n      <SettingsSubGroup label=\"Performance Mode\">\n        <SettingsOption\n          label={t(\"general.performance_mode.plugged\")}\n          action={\n            <Select\n              value={perf.default}\n              options={options}\n              onSelect={(value) => patchPerfSettings({ default: value })}\n            />\n          }\n        />\n        <SettingsOption\n          label={t(\"general.performance_mode.on_battery\")}\n          action={\n            <Select\n              value={perf.onBattery}\n              options={options}\n              onSelect={(onBattery) => patchPerfSettings({ onBattery })}\n            />\n          }\n        />\n        <SettingsOption\n          label={t(\"general.performance_mode.on_energy_saver\")}\n          action={\n            <Select\n              value={perf.onEnergySaver}\n              options={options}\n              onSelect={(onEnergySaver) => patchPerfSettings({ onEnergySaver })}\n            />\n          }\n        />\n      </SettingsSubGroup>\n    </SettingsGroup>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/general/infra/index.module.css",
    "content": ".title {\n  font-size: 1.2rem;\n  font-weight: bold;\n}\n\n.transfer {\n  :global(.ant-transfer-list) {\n    overflow: hidden;\n\n    :global(.ant-transfer-list-header) {\n      :global(.ant-transfer-list-header-selected) {\n        display: none;\n      }\n\n      :global(.ant-transfer-list-header-title) {\n        font-weight: 600;\n        text-align: center;\n      }\n    }\n\n    :global(.ant-transfer-list-body) {\n      background-color: var(--color-gray-50);\n      padding: var(--spacing-2xs) var(--spacing-xs);\n    }\n  }\n}\n\n.tagList {\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n  gap: var(--spacing-2xs);\n\n  .tag {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 0px var(--spacing-2xs);\n    height: 20px;\n    border-radius: 6px;\n    background-color: var(--color-gray-100);\n    color: var(--color-gray-900);\n    font-size: 0.5rem;\n    line-height: 10px;\n  }\n}\n\n.palette {\n  width: 100%;\n  height: 30px;\n  display: flex;\n  border-radius: 6px;\n  overflow: hidden;\n\n  > div {\n    flex: 1;\n    height: 100%;\n  }\n}\n\n.wallpaperButton {\n  flex: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin-left: var(--spacing-l);\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/general/infra/index.tsx",
    "content": "import { SupportedLanguages } from \"@seelen-ui/lib\";\nimport { Input, InputNumber, Select, Switch } from \"antd\";\nimport { useTransition } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { startup } from \"../../shared/tauri/infra.ts\";\nimport { autostart } from \"../../../state/system.ts\";\nimport {\n  getDateFormat,\n  getHardwareAcceleration,\n  getLanguage,\n  getPollingInterval,\n  getStartOfWeek,\n  setDateFormat,\n  setHardwareAcceleration,\n  setLanguage,\n  setPollingInterval,\n  setStartOfWeek,\n} from \"../application.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../../components/SettingsBox/index.tsx\";\nimport { Colors } from \"./Colors.tsx\";\nimport { PerformanceSettings } from \"./Performance.tsx\";\n\nexport function General() {\n  const [changingAutostart, startTransition] = useTransition();\n\n  const language = getLanguage();\n  const dateFormat = getDateFormat();\n  const startOfWeek = getStartOfWeek();\n  const hardwareAcceleration = getHardwareAcceleration();\n  const pollingInterval = getPollingInterval();\n\n  const { t } = useTranslation();\n\n  function onAutoStart(value: boolean) {\n    startTransition(async () => {\n      try {\n        if (value) {\n          await startup.enable();\n        } else {\n          await startup.disable();\n        }\n        autostart.value = await startup.isEnabled();\n      } catch (e) {\n        console.error(e);\n      }\n    });\n  }\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"general.startup\")}\n          action={<Switch onChange={onAutoStart} value={autostart.value} loading={changingAutostart} />}\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"general.language\")}\n          action={\n            <Select\n              showSearch\n              filterOption={(_searching, option) => {\n                if (!option) {\n                  return true;\n                }\n                const searching = _searching.toLocaleLowerCase();\n                let label = option.label.toLocaleLowerCase();\n                let enLabel = option.enLabel.toLocaleLowerCase();\n                return label.includes(searching) || enLabel.includes(searching);\n              }}\n              style={{ width: \"200px\" }}\n              value={language}\n              options={[...SupportedLanguages]}\n              onSelect={(value) => setLanguage(value)}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"general.date_format\")}\n          description={\n            <a\n              href=\"https://momentjs.com/docs/#/displaying/format/\"\n              target=\"_blank\"\n              style={{ opacity: 0.8 }}\n            >\n              {t(\"general.date_format_how_to\")}\n            </a>\n          }\n          action={\n            <Input\n              style={{ width: \"200px\", maxWidth: \"200px\" }}\n              placeholder=\"YYYY-MM-DD\"\n              value={dateFormat}\n              onChange={(e) => {\n                setDateFormat(e.currentTarget.value);\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"general.start_of_week\")}\n          action={\n            <Select\n              style={{ width: \"200px\" }}\n              value={startOfWeek}\n              options={[\n                { label: t(\"general.monday\"), value: \"Monday\" },\n                { label: t(\"general.sunday\"), value: \"Sunday\" },\n                { label: t(\"general.saturday\"), value: \"Saturday\" },\n              ]}\n              onSelect={(value) => setStartOfWeek(value)}\n            />\n          }\n        />\n      </SettingsGroup>\n\n      <Colors />\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"general.hardware_acceleration\")}\n          description={t(\"general.hardware_acceleration_description\")}\n          action={<Switch onChange={setHardwareAcceleration} checked={hardwareAcceleration} />}\n        />\n\n        <SettingsOption\n          label={t(\"general.polling_interval\")}\n          description={t(\"general.polling_interval_description\")}\n          action={\n            <InputNumber\n              style={{ width: \"100px\" }}\n              min={1}\n              step={1}\n              precision={0}\n              value={pollingInterval}\n              onChange={(value) => value !== null && setPollingInterval(value)}\n            />\n          }\n        />\n      </SettingsGroup>\n\n      <PerformanceSettings />\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/IconPacks.tsx",
    "content": "import { type IconPack, type IconPackId, ResourceKind } from \"@seelen-ui/lib/types\";\nimport { Switch } from \"antd\";\nimport { Reorder } from \"framer-motion\";\nimport { useTranslation } from \"react-i18next\";\nimport { useState } from \"preact/hooks\";\n\nimport cs from \"./infra.module.css\";\n\nimport { setActiveIconPacks, settings } from \"../../state/mod.ts\";\nimport { iconPacks as allIconPacks } from \"../../state/resources.ts\";\n\nimport { resolveDisplayName, ResourceCard, ResourceListHeader } from \"./ResourceCard.tsx\";\n\nexport function IconPacksView() {\n  const activeIds = settings.value.activeIconPacks;\n  const { t, i18n } = useTranslation();\n  const [search, setSearch] = useState(\"\");\n\n  function toggleIconPack(id: IconPackId) {\n    if (activeIds.includes(id)) {\n      setActiveIconPacks(activeIds.filter((x) => x !== id));\n    } else {\n      setActiveIconPacks([...activeIds, id]);\n    }\n  }\n\n  function onReorder(activeIconPacks: IconPackId[]) {\n    setActiveIconPacks(activeIconPacks);\n  }\n\n  const allFiltered = search\n    ? allIconPacks.value.filter((pack) =>\n      resolveDisplayName(pack.metadata.displayName, i18n.language)\n        .toLowerCase()\n        .includes(search.toLowerCase())\n    )\n    : allIconPacks.value;\n\n  const disabled: IconPack[] = [];\n  const enabled: IconPack[] = [];\n  for (const pack of allFiltered) {\n    if (activeIds.includes(pack.id)) {\n      enabled.push(pack);\n    } else {\n      disabled.push(pack);\n    }\n  }\n  enabled.sort((a, b) => activeIds.indexOf(a.id) - activeIds.indexOf(b.id));\n\n  return (\n    <div className={cs.list}>\n      <ResourceListHeader\n        discoverUrl=\"https://seelen.io/resources/s?category=IconPack\"\n        search={search}\n        onSearch={setSearch}\n      />\n\n      <b>{t(\"general.icon_pack.selected\")}</b>\n      <Reorder.Group values={activeIds} onReorder={onReorder} className={cs.reorderGroup}>\n        {enabled.map((iconPack) => (\n          <Reorder.Item key={iconPack.id} value={iconPack.id}>\n            <ResourceCard\n              resource={iconPack}\n              kind={ResourceKind.IconPack}\n              actions={iconPack.id === \"@system/icon-pack\"\n                ? undefined\n                : <Switch defaultChecked onChange={() => toggleIconPack(iconPack.id)} />}\n            />\n          </Reorder.Item>\n        ))}\n      </Reorder.Group>\n\n      <b>{t(\"general.icon_pack.available\")}</b>\n      {disabled.map((iconPack) => (\n        <ResourceCard\n          key={iconPack.id}\n          resource={iconPack}\n          kind={ResourceKind.IconPack}\n          actions={<Switch defaultChecked={false} onChange={() => toggleIconPack(iconPack.id)} />}\n        />\n      ))}\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Plugins.tsx",
    "content": "import { type Plugin, ResourceKind } from \"@seelen-ui/lib/types\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { useTranslation } from \"react-i18next\";\nimport { useState } from \"preact/hooks\";\nimport React from \"react\";\n\nimport cs from \"./infra.module.css\";\n\nimport { plugins, widgets } from \"../../state/resources.ts\";\n\nimport { resolveDisplayName, ResourceCard, ResourceListHeader } from \"./ResourceCard.tsx\";\n\nexport function PluginsView() {\n  const { i18n } = useTranslation();\n  const [search, setSearch] = useState(\"\");\n\n  function targetLabel(target: string) {\n    const widget = widgets.value.find((w) => w.id === target);\n    if (widget) {\n      return <ResourceText text={widget.metadata.displayName} />;\n    }\n    return <span>{target}</span>;\n  }\n\n  const filtered = search\n    ? plugins.value.filter((p) =>\n      resolveDisplayName(p.metadata.displayName, i18n.language).toLowerCase().includes(search.toLowerCase())\n    )\n    : plugins.value;\n\n  const groupedByTarget = filtered.reduce((acc, plugin) => {\n    acc[plugin.target] ??= {\n      label: targetLabel(plugin.target),\n      plugins: [],\n    };\n    acc[plugin.target]!.plugins.push(plugin);\n    return acc;\n  }, {} as Record<string, { label: React.ReactNode; plugins: Plugin[] }>);\n\n  Object.values(groupedByTarget).forEach((group) => {\n    group.plugins.sort((a, b) => a.id.localeCompare(b.id));\n  });\n\n  return (\n    <>\n      <ResourceListHeader\n        discoverUrl=\"https://seelen.io/resources/s?category=Plugin\"\n        search={search}\n        onSearch={setSearch}\n      />\n\n      <div className={cs.list}>\n        {Object.values(groupedByTarget).map((group, idx) => (\n          <React.Fragment key={idx}>\n            <b>{group.label}</b>\n            {group.plugins.map((plugin) => (\n              <ResourceCard\n                key={plugin.id}\n                resource={plugin}\n                kind={ResourceKind.Plugin}\n              />\n            ))}\n          </React.Fragment>\n        ))}\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/ResourceCard.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport type {\n  Resource,\n  ResourceId,\n  ResourceKind,\n  ResourceMetadata,\n  ResourceText as IResourceText,\n  Wallpaper,\n} from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { ResourceText, ResourceTextAsMarkdown } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\nimport { Button, Flex, Input, Popconfirm, Tooltip } from \"antd\";\nimport type { ComponentChildren } from \"preact\";\nimport { useEffect, useState } from \"preact/hooks\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { SettingsGroup, SettingsOption } from \"../../components/SettingsBox/index.tsx\";\nimport { EnvConfig } from \"../shared/config/infra.ts\";\nimport cs from \"./infra.module.css\";\nimport type { IconName } from \"libs/ui/icons.ts\";\nimport { $corruptedWallpapers } from \"../shared/signals.ts\";\n\ntype AnyResource = {\n  id: ResourceId;\n  metadata: ResourceMetadata;\n  icon?: string | null;\n};\n\ninterface ResourceCardProps {\n  kind: ResourceKind;\n  resource: AnyResource;\n  actions?: React.ReactNode;\n  body?: React.ReactNode;\n}\n\nexport function ResourceCard({ resource, kind, actions, body }: ResourceCardProps) {\n  const [hasUpdate, setHasUpdate] = useState(false);\n  const isCorrupted = kind === \"Wallpaper\" && $corruptedWallpapers.value.has(resource.id);\n\n  const { t } = useTranslation();\n\n  useEffect(() => {\n    async function checkUpdate() {\n      if (resource.id.startsWith(\"@\")) {\n        return;\n      }\n\n      const res = await fetch(`https://product.seelen.io/resource/${resource.id}`);\n      const remoteResource: Resource = await res.json();\n      const lastUpdateRelease = new Date(remoteResource.updatedAt);\n      const writtenAt = new Date(resource.metadata.writtenAt);\n      setHasUpdate(lastUpdateRelease > writtenAt);\n    }\n\n    checkUpdate();\n  }, []);\n\n  const [major = 0, minor = 0, patch = 0] = EnvConfig.version.split(\".\").map(Number);\n  const [majorTarget = 0, minorTarget = 0, patchTarget = 0] = resource.metadata.appTargetVersion || [];\n\n  const targetIsOlder = !!resource.metadata.appTargetVersion &&\n    (majorTarget < major ||\n      (majorTarget === major && minorTarget < minor) ||\n      (majorTarget === major && minorTarget === minor && patchTarget < patch));\n\n  const targetIsNewer = !!resource.metadata.appTargetVersion &&\n    (majorTarget > major ||\n      (majorTarget === major && minorTarget > minor) ||\n      (majorTarget === major && minorTarget === minor && patchTarget > patch));\n\n  return (\n    <div\n      className={cx(cs.card, {\n        [cs.warn!]: targetIsOlder,\n        [cs.danger!]: targetIsNewer || isCorrupted,\n      })}\n    >\n      <ResourcePortrait resource={resource} kind={kind}>\n        {targetIsOlder && (\n          <Tooltip title={t(\"resources.outdated\")}>\n            <Icon iconName=\"IoWarning\" className={cs.warning} />\n          </Tooltip>\n        )}\n        {targetIsNewer && (\n          <Tooltip title={t(\"resources.app_outdated\")}>\n            <Icon iconName=\"IoWarning\" className={cs.danger} />\n          </Tooltip>\n        )}\n        {isCorrupted && (\n          <Tooltip title={t(\"resources.corrupted_wallpaper\")}>\n            <Icon iconName=\"MdErrorOutline\" className={cs.corrupted} />\n          </Tooltip>\n        )}\n      </ResourcePortrait>\n\n      <div className={cs.header}>\n        <ResourceText className={cs.title} text={resource.metadata.displayName} />\n\n        <div className={cs.actionsTop}>\n          {!resource.id.startsWith(\"@\") && !hasUpdate && (\n            <Tooltip title={t(\"resources.see_on_website\")}>\n              <Button\n                type=\"link\"\n                href={`https://seelen.io/resources/${compressUuid(resource.id)}`}\n                target=\"_blank\"\n              >\n                <Icon iconName=\"TbWorldShare\" />\n              </Button>\n            </Tooltip>\n          )}\n\n          {hasUpdate && (\n            <Tooltip title={t(\"resources.has_update\")} placement=\"left\">\n              <Button\n                type=\"link\"\n                href={`https://seelen.io/resources/${compressUuid(resource.id)}?update`}\n                target=\"_blank\"\n              >\n                <Icon iconName=\"MdUpdate\" />\n              </Button>\n            </Tooltip>\n          )}\n\n          {actions}\n        </div>\n      </div>\n\n      <div className={cs.body}>\n        {body || <ResourceTextAsMarkdown text={resource.metadata.description} />}\n      </div>\n\n      <div className={cs.footer}>\n        {!resource.metadata.bundled && resource.metadata.path.includes(\"com.seelen.seelen-ui\") && (\n          <Tooltip title={t(\"resources.delete\")} placement=\"left\">\n            <Popconfirm\n              title={t(\"action.confirm\")}\n              description={t(\"action.confirm_body\")}\n              okText={t(\"yes\")}\n              cancelText={t(\"no\")}\n              onConfirm={() => {\n                invoke(SeelenCommand.RemoveResource, { kind, id: resource.id });\n              }}\n            >\n              <Button type=\"text\" danger>\n                <Icon iconName=\"BiTrash\" />\n              </Button>\n            </Popconfirm>\n          </Tooltip>\n        )}\n      </div>\n    </div>\n  );\n}\n\nconst icons: Record<ResourceKind, IconName> = {\n  Theme: \"IoColorPaletteOutline\",\n  IconPack: \"LiaIconsSolid\",\n  Plugin: \"BsPlugin\",\n  Widget: \"BiSolidWidget\",\n  Wallpaper: \"LuWallpaper\",\n  SoundPack: \"PiWaveformBold\",\n};\n\ninterface ResourcePortraitProps {\n  resource: AnyResource;\n  kind: ResourceKind;\n  children?: ComponentChildren;\n}\n\nexport function ResourceIcon({ kind }: { kind: ResourceKind }) {\n  return <Icon className={cs.kindIcon} iconName={icons[kind]!} />;\n}\n\nfunction ResourcePortraitInner({ resource, kind }: ResourcePortraitProps) {\n  if (resource.metadata.portrait) {\n    return <img src={resource.metadata.portrait} />;\n  }\n\n  if (kind === \"Wallpaper\") {\n    const wallpaper = resource as Wallpaper;\n    if (wallpaper.thumbnailFilename) {\n      return (\n        <img\n          src={convertFileSrc(`${resource.metadata.path}\\\\${wallpaper.thumbnailFilename}`)}\n          style={{ filter: \"blur(0.4px)\" }}\n          loading=\"lazy\"\n        />\n      );\n    }\n  }\n\n  if (resource.icon) {\n    return <Icon className={cs.kindIcon} iconName={resource.icon as IconName} />;\n  }\n\n  return <ResourceIcon kind={kind} />;\n}\n\nexport function ResourcePortrait({ resource, kind, children }: ResourcePortraitProps) {\n  return (\n    <figure className={cs.portrait}>\n      <ResourcePortraitInner resource={resource} kind={kind} />\n      {children}\n    </figure>\n  );\n}\n\nexport function ResourceDescription({ text }: { text?: IResourceText }) {\n  if (!text) return null;\n  return (\n    <div className={cs.description}>\n      <ResourceTextAsMarkdown text={text} />\n    </div>\n  );\n}\n\nexport function resolveDisplayName(name: IResourceText | undefined, language: string): string {\n  if (!name) return \"\";\n  if (typeof name === \"string\") return name;\n  return name[language] ?? name[\"en\"] ?? \"\";\n}\n\ninterface ResourceListHeaderProps {\n  discoverUrl: string;\n  search: string;\n  onSearch: (v: string) => void;\n  children?: ComponentChildren;\n}\n\nexport function ResourceListHeader({\n  discoverUrl,\n  search,\n  onSearch,\n  children,\n}: ResourceListHeaderProps) {\n  const { t } = useTranslation();\n  return (\n    <>\n      <SettingsGroup>\n        <Flex gap={10}>\n          <b>{t(\"search\")}:</b>\n          <Input\n            type=\"search\"\n            value={search}\n            onChange={(e) => onSearch((e.target as HTMLInputElement).value)}\n            style={{ width: \"100%\" }}\n          />\n        </Flex>\n\n        <SettingsOption>\n          <span>{t(\"resources.discover\")}:</span>\n          <Button href={discoverUrl} target=\"_blank\" type=\"link\">\n            {discoverUrl}\n          </Button>\n        </SettingsOption>\n        {children}\n      </SettingsGroup>\n    </>\n  );\n}\n\nexport function compressUuid(uuid: string): string {\n  let hex = uuid.replace(/-/g, \"\");\n  let data = String.fromCharCode.apply(\n    null,\n    hex.match(/\\w{2}/g)!.map(function (a) {\n      return parseInt(a, 16);\n    }),\n  );\n  return btoa(data).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/SoundPacks.tsx",
    "content": "export function SoundPacksView() {\n  return null;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/AllView.tsx",
    "content": "import { ResourceKind, type Theme, type ThemeId, type Widget } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Switch, Tooltip } from \"antd\";\nimport { Reorder } from \"framer-motion\";\nimport { useTranslation } from \"react-i18next\";\nimport { useState } from \"preact/hooks\";\nimport { NavLink } from \"react-router\";\n\nimport cs from \"../infra.module.css\";\n\nimport { setActiveThemes, settings } from \"../../../state/mod.ts\";\nimport { themes, widgets } from \"../../../state/resources.ts\";\n\nimport { resolveDisplayName, ResourceCard, ResourceListHeader } from \"../ResourceCard.tsx\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { cx } from \"../../shared/utils/app.ts\";\n\nexport function ThemesView() {\n  const activeIds = settings.value.activeThemes;\n  const { t, i18n } = useTranslation();\n  const [search, setSearch] = useState(\"\");\n\n  function toggleTheme(themeId: ThemeId) {\n    if (activeIds.includes(themeId)) {\n      setActiveThemes(activeIds.filter((x) => x !== themeId));\n    } else {\n      setActiveThemes([...activeIds, themeId]);\n    }\n  }\n\n  function onReorder(themes: ThemeId[]) {\n    setActiveThemes(themes);\n  }\n\n  const allFiltered = search\n    ? themes.value.filter((theme) =>\n      resolveDisplayName(theme.metadata.displayName, i18n.language).toLowerCase().includes(search.toLowerCase())\n    )\n    : themes.value;\n\n  const disabled: Theme[] = [];\n  const enabled: Theme[] = [];\n  for (const theme of allFiltered) {\n    if (activeIds.includes(theme.id)) {\n      enabled.push(theme);\n    } else {\n      disabled.push(theme);\n    }\n  }\n  enabled.sort((a, b) => activeIds.indexOf(a.id) - activeIds.indexOf(b.id));\n\n  return (\n    <>\n      <ResourceListHeader\n        discoverUrl=\"https://seelen.io/resources/s?category=Theme\"\n        search={search}\n        onSearch={setSearch}\n      />\n\n      <div className={cs.list}>\n        <b>{t(\"general.theme.selected\")}</b>\n        <Reorder.Group values={activeIds} onReorder={onReorder} className={cs.reorderGroup}>\n          {enabled.map((theme) => (\n            <Reorder.Item key={theme.id} value={theme.id}>\n              <ThemeItem\n                key={theme.id}\n                theme={theme}\n                onToggle={() => toggleTheme(theme.id)}\n                checked\n                widgets={widgets.value}\n              />\n            </Reorder.Item>\n          ))}\n        </Reorder.Group>\n\n        <b>{t(\"general.theme.available\")}</b>\n        {disabled.map((theme) => (\n          <ThemeItem\n            key={theme.id}\n            theme={theme}\n            onToggle={() => toggleTheme(theme.id)}\n            checked={false}\n            widgets={widgets.value}\n          />\n        ))}\n      </div>\n    </>\n  );\n}\n\ninterface ThemeItemProps {\n  theme: Theme;\n  onToggle: () => void;\n  checked: boolean;\n  widgets: Widget[];\n}\n\nfunction ThemeItem({ theme, checked, onToggle, widgets }: ThemeItemProps) {\n  let query = new URLSearchParams();\n  query.set(\"id\", theme.id);\n\n  const { t } = useTranslation();\n\n  let gpuImpact = false;\n  let affectedWidgets: Widget[] = [];\n\n  for (const [widgetId, style] of Object.entries(theme.styles)) {\n    const widget = widgets.find((x) => x.id === widgetId);\n    if (widget) {\n      affectedWidgets.push(widget);\n    }\n\n    if (\n      style &&\n      style.includes(\"@keyframes\") &&\n      style.includes(\"animation:\") &&\n      style.includes(\"infinite\")\n    ) {\n      gpuImpact = true;\n    }\n  }\n\n  return (\n    <ResourceCard\n      resource={theme}\n      kind={ResourceKind.Theme}\n      body={\n        <div className={cs.tags}>\n          {theme.id !== \"@default/theme\" && gpuImpact && (\n            <Tooltip title={t(\"resources.high_impact\")}>\n              <div className={cx(cs.tag, cs.warn)}>\n                <Icon iconName=\"HiCpuChip\" />\n              </div>\n            </Tooltip>\n          )}\n\n          {affectedWidgets.map((widget) => (\n            <div key={widget.id} className={cs.tag}>\n              <ResourceText text={widget.metadata.displayName} />\n            </div>\n          ))}\n        </div>\n      }\n      actions={\n        <>\n          {theme.settings.length > 0 && (\n            <NavLink to={`/theme?${query}`}>\n              <Button type=\"text\">\n                <Icon iconName=\"RiSettings4Fill\" />\n              </Button>\n            </NavLink>\n          )}\n          {theme.id !== \"@default/theme\" && <Switch value={checked} onChange={onToggle} />}\n        </>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/View.tsx",
    "content": "import { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { useSearchParams } from \"react-router\";\n\nimport { SettingsGroup, SettingsOption } from \"../../../components/SettingsBox/index.tsx\";\n\nimport { ThemeConfigDefinition } from \"./components/ThemeConfigDefinition.tsx\";\nimport { ResourceDescription } from \"../ResourceCard.tsx\";\nimport { resetThemeVariables } from \"./application.ts\";\nimport { themes } from \"../../../state/resources.ts\";\n\nexport function ThemeView() {\n  const { t } = useTranslation();\n\n  const [searchParams] = useSearchParams();\n  const id = searchParams.get(\"id\");\n\n  const theme = themes.value.find((t) => t.id === id);\n\n  const handleReset = () => {\n    resetThemeVariables(theme!.id);\n  };\n\n  if (!theme) {\n    return <div>wow 404 !?</div>;\n  }\n\n  return (\n    <>\n      <SettingsGroup>\n        <ResourceDescription text={theme.metadata.description} />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"reset_all_to_default\")}\n          action={\n            <Button onClick={handleReset}>\n              <Icon iconName=\"RiResetLeftLine\" />\n            </Button>\n          }\n        />\n      </SettingsGroup>\n      {theme.settings.map((def, idx) => <ThemeConfigDefinition key={idx} themeId={theme.id} def={def} />)}\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/application.ts",
    "content": "import { settings } from \"../../../state/mod\";\nimport type { ThemeId, ThemeSettings } from \"@seelen-ui/lib/types\";\n\n/**\n * Gets all theme variables for a specific theme\n */\nexport function getThemeVariables(themeId: ThemeId): ThemeSettings {\n  return settings.value.byTheme[themeId] || {};\n}\n\n/**\n * Gets a specific theme variable\n */\nexport function getThemeVariable(themeId: ThemeId, name: string): string | undefined {\n  return settings.value.byTheme[themeId]?.[name];\n}\n\n/**\n * Sets a theme variable\n */\nexport function setThemeVariable(themeId: ThemeId, name: string, value: string) {\n  const currentThemeVars = settings.value.byTheme[themeId] || {};\n\n  settings.value = {\n    ...settings.value,\n    byTheme: {\n      ...settings.value.byTheme,\n      [themeId]: {\n        ...currentThemeVars,\n        [name]: value,\n      },\n    },\n  };\n}\n\n/**\n * Deletes a theme variable (resets to default)\n */\nexport function deleteThemeVariable(themeId: ThemeId, name: string) {\n  const currentThemeVars = settings.value.byTheme[themeId] || {};\n  const { [name]: _, ...remainingVars } = currentThemeVars;\n\n  settings.value = {\n    ...settings.value,\n    byTheme: {\n      ...settings.value.byTheme,\n      [themeId]: remainingVars,\n    },\n  };\n}\n\n/**\n * Resets all theme variables for a specific theme\n */\nexport function resetThemeVariables(themeId: ThemeId) {\n  settings.value = {\n    ...settings.value,\n    byTheme: {\n      ...settings.value.byTheme,\n      [themeId]: {},\n    },\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/components/ThemeConfigDefinition.tsx",
    "content": "import type { ThemeConfigDefinition as ThemeConfigDef, ThemeId } from \"@seelen-ui/lib/types\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport type { ReactNode } from \"react\";\n\nimport { SettingsGroup, SettingsSubGroup } from \"../../../../components/SettingsBox/index.tsx\";\n\nimport { ThemeSetting } from \"./ThemeSetting.tsx\";\n\nexport interface ThemeConfigDefinitionProps {\n  def: ThemeConfigDef;\n  themeId: ThemeId;\n  nestLevel?: number;\n}\n\nexport function ThemeConfigDefinition({\n  def,\n  themeId,\n  nestLevel = 0,\n}: ThemeConfigDefinitionProps) {\n  const content = renderContent(def, themeId, nestLevel);\n\n  return nestLevel === 0 ? <SettingsGroup>{content}</SettingsGroup> : content;\n}\n\nfunction renderContent(def: ThemeConfigDef, themeId: ThemeId, nestLevel: number): ReactNode {\n  if (\"group\" in def) {\n    return (\n      <SettingsSubGroup label={<ResourceText text={def.group.header} />}>\n        {def.group.items.map((item, idx) => (\n          <ThemeConfigDefinition key={idx} themeId={themeId} def={item} nestLevel={nestLevel + 1} />\n        ))}\n      </SettingsSubGroup>\n    );\n  }\n\n  return <ThemeSetting themeId={themeId} definition={def} />;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/components/ThemeSetting.tsx",
    "content": "import type { ThemeId, ThemeVariableDefinition } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { Button, ColorPicker, Input, InputNumber, Select, Slider, Space, Tooltip } from \"antd\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\nimport { open } from \"@tauri-apps/plugin-dialog\";\nimport type { ReactNode } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { SettingsOption } from \"../../../../components/SettingsBox/index.tsx\";\nimport { CSS_UNITS } from \"../constants.ts\";\nimport { useThemeVariable } from \"../hooks/useThemeVariable.ts\";\n\nexport interface ThemeSettingProps {\n  themeId: ThemeId;\n  definition: ThemeVariableDefinition;\n}\n\nexport function ThemeSetting({ themeId, definition }: ThemeSettingProps) {\n  const { value: userStoredValue, onChange, onReset } = useThemeVariable(themeId, definition.name);\n  const { t } = useTranslation();\n\n  const input = renderInput(definition, userStoredValue, onChange, onReset);\n\n  return (\n    <SettingsOption\n      label={<ResourceText text={definition.label} />}\n      tip={definition.tip ? <ResourceText text={definition.tip} /> : undefined}\n      description={definition.description ? <ResourceText text={definition.description} /> : undefined}\n      action={\n        <Space.Compact>\n          {input}\n          <Tooltip title={t(\"reset_to_default\")}>\n            <Button onClick={onReset}>\n              <Icon iconName=\"BiReset\" />\n            </Button>\n          </Tooltip>\n        </Space.Compact>\n      }\n    />\n  );\n}\n\nfunction renderInput(\n  definition: ThemeVariableDefinition,\n  userStoredValue: string | undefined,\n  onChange: (value: string) => void,\n  onReset: () => void,\n): ReactNode {\n  if (definition.options) {\n    return (\n      <Select\n        options={definition.options.map((value) => ({ value: String(value) }))}\n        value={userStoredValue}\n        defaultValue={`${definition.initialValue}`}\n        onChange={onChange}\n      />\n    );\n  }\n\n  const { min, max, step } = definition;\n\n  switch (definition.syntax) {\n    case \"<color>\": {\n      const value = userStoredValue || definition.initialValue;\n      return (\n        <ColorPicker\n          showText\n          value={value}\n          onChangeComplete={(color) => onChange(color.toHexString())}\n        />\n      );\n    }\n\n    case \"<length-percentage>\": {\n      const numericValue = userStoredValue ? parseFloat(userStoredValue) : definition.initialValue;\n      const unit = userStoredValue?.replace(/[\\d.]+/, \"\") || definition.initialValueUnit;\n\n      return (\n        <Space.Compact>\n          <InputNumber\n            value={numericValue}\n            onChange={(newValue) => {\n              if (newValue == null) {\n                onReset();\n                return;\n              }\n              onChange(`${newValue}${unit}`);\n            }}\n            min={min || undefined}\n            max={max || undefined}\n            step={step || undefined}\n          />\n          <Select\n            options={CSS_UNITS.map((unit) => ({ value: unit }))}\n            style={{ width: 60, minWidth: 60 }}\n            value={unit}\n            onChange={(newUnit) => onChange(`${numericValue}${newUnit}`)}\n          />\n        </Space.Compact>\n      );\n    }\n\n    case \"<number>\": {\n      const value = userStoredValue ? parseFloat(userStoredValue) : definition.initialValue;\n\n      const handleChange = (newValue: number | null) => {\n        if (newValue == null) {\n          onReset();\n          return;\n        }\n        onChange(`${newValue}`);\n      };\n\n      if (step != null) {\n        return (\n          <Slider\n            style={{ flex: 1, minWidth: 120 }}\n            value={value}\n            onChange={handleChange}\n            min={min || undefined}\n            max={max || undefined}\n            step={step || undefined}\n          />\n        );\n      }\n\n      return (\n        <InputNumber\n          value={value}\n          onChange={handleChange}\n          min={min || undefined}\n          max={max || undefined}\n          step={step || undefined}\n        />\n      );\n    }\n\n    case \"<url>\": {\n      const handleSelectFile = async () => {\n        const selected = await open({\n          multiple: false,\n          directory: false,\n          filters: [\n            {\n              name: \"Image\",\n              extensions: [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"],\n            },\n          ],\n        });\n\n        if (selected) {\n          onChange(convertFileSrc(selected));\n        }\n      };\n\n      return (\n        <Space.Compact style={{ flex: 1 }}>\n          <Input readOnly value={userStoredValue} placeholder=\"Select a file...\" />\n          <Button onClick={handleSelectFile}>\n            <Icon iconName=\"FaFolderOpen\" />\n          </Button>\n        </Space.Compact>\n      );\n    }\n\n    case \"<string>\": {\n      const value = userStoredValue ?? definition.initialValue;\n      return (\n        <Input\n          value={value}\n          onChange={(e) => onChange(e.currentTarget.value)}\n          minLength={min || undefined}\n          maxLength={max || undefined}\n        />\n      );\n    }\n\n    default: {\n      // @ts-expect-error should never happen\n      definition.syntax;\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/constants.ts",
    "content": "export const CSS_UNITS = [\n  \"px\",\n  \"%\",\n  \"rem\",\n  \"em\",\n  \"vh\",\n  \"vw\",\n  \"vmin\",\n  \"vmax\",\n  \"ch\",\n  \"ex\",\n  \"dvh\",\n  \"dvw\",\n  \"lvh\",\n  \"lvw\",\n  \"svh\",\n  \"svw\",\n] as const;\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Theme/hooks/useThemeVariable.ts",
    "content": "import type { ThemeId } from \"@seelen-ui/lib/types\";\nimport { useCallback } from \"react\";\n\nimport { deleteThemeVariable, getThemeVariable, setThemeVariable } from \"../application.ts\";\n\nexport interface UseThemeVariableResult {\n  value: string | undefined;\n  onChange: (value: string) => void;\n  onReset: () => void;\n}\n\nexport function useThemeVariable(themeId: ThemeId, variableName: string): UseThemeVariableResult {\n  const value = getThemeVariable(themeId, variableName);\n\n  const onChange = useCallback(\n    (value: string) => {\n      setThemeVariable(themeId, variableName, value);\n    },\n    [themeId, variableName],\n  );\n\n  const onReset = useCallback(() => {\n    deleteThemeVariable(themeId, variableName);\n  }, [themeId, variableName]);\n\n  return { value, onChange, onReset };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Wallpapers/AllView.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { ResourceKind, type Wallpaper } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { useState } from \"preact/hooks\";\nimport { NavLink } from \"react-router\";\n\nimport cs from \"../infra.module.css\";\n\nimport { wallpapers } from \"../../../state/resources.ts\";\n\nimport { resolveDisplayName, ResourceCard, ResourceListHeader } from \"../ResourceCard.tsx\";\n\nexport function AllWallpapersView() {\n  const { t, i18n } = useTranslation();\n  const [search, setSearch] = useState(\"\");\n\n  const filtered = search\n    ? wallpapers.value.filter((w) =>\n      resolveDisplayName(w.metadata.displayName, i18n.language).toLowerCase().includes(search.toLowerCase())\n    )\n    : wallpapers.value;\n\n  return (\n    <>\n      <ResourceListHeader\n        discoverUrl=\"https://seelen.io/resources/s?category=Wallpaper\"\n        search={search}\n        onSearch={setSearch}\n      >\n        <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n          <b>{t(\"resources.import_wallpapers\")}</b>\n          <Button\n            type=\"default\"\n            onClick={() => {\n              invoke(SeelenCommand.StateRequestWallpaperAddition);\n            }}\n          >\n            <Icon iconName=\"MdLibraryAdd\" />\n          </Button>\n        </div>\n      </ResourceListHeader>\n\n      <div className={cs.list}>\n        {filtered.map((resource) => <WallpaperItem key={resource.id} resource={resource} />)}\n      </div>\n    </>\n  );\n}\n\nfunction WallpaperItem({ resource }: { resource: Wallpaper }) {\n  return (\n    <ResourceCard\n      resource={resource}\n      kind={ResourceKind.Wallpaper}\n      actions={\n        <NavLink to={`/wallpaper?${new URLSearchParams({ id: resource.id })}`}>\n          <Button type=\"text\">\n            <Icon iconName=\"RiSettings4Fill\" />\n          </Button>\n        </NavLink>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Wallpapers/View.module.css",
    "content": ".previewContainer {\n  position: sticky;\n  top: 0;\n  width: 100%;\n  max-width: 600px;\n  aspect-ratio: 16 / 9;\n  margin-bottom: var(--spacing-s);\n  z-index: 10;\n  pointer-events: none;\n}\n\n.preview {\n  position: relative;\n  width: 100%;\n  aspect-ratio: 16 / 9;\n  background-color: #000;\n  overflow: hidden;\n  border-radius: 10px;\n\n  animation-name: shrink-on-scroll;\n  animation-direction: alternate;\n  animation-fill-mode: forwards;\n  animation-timeline: scroll();\n  animation-range: 0px 200px;\n}\n\n@keyframes shrink-on-scroll {\n  0% {\n    width: 100%;\n  }\n  100% {\n    width: 30vw;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Wallpapers/View.tsx",
    "content": "import { SUPPORTED_VIDEO_WALLPAPER_EXTENSIONS, WallpaperConfiguration } from \"@seelen-ui/lib\";\nimport type { PlaybackSpeed, WallpaperId, WallpaperInstanceSettings } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { Wallpaper } from \"libs/ui/react/components/Wallpaper/index.tsx\";\nimport { Button, ColorPicker, Select, Slider, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { useSearchParams } from \"react-router\";\n\nimport { patchWallpaperSettings, resetWallpaperSettings } from \"./application.ts\";\nimport { wallpapers } from \"../../../state/resources.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../components/SettingsBox/index.tsx\";\nimport { ResourceDescription } from \"../ResourceCard.tsx\";\n\nimport styles from \"./View.module.css\";\nimport { settings } from \"../../../state/mod.ts\";\n\nconst playbackSpeeds: `${PlaybackSpeed}`[] = [\n  \"xDot25\",\n  \"xDot5\",\n  \"xDot75\",\n  \"x1\",\n  \"x1Dot25\",\n  \"x1Dot5\",\n  \"x1Dot75\",\n  \"x2\",\n];\nconst playbackSpeedOptions = playbackSpeeds.map((s) => ({\n  label: s.replace(\"Dot\", \".\").replace(\"x.\", \"x0.\"),\n  value: s,\n}));\n\nconst defaultWallpaperConfig = await WallpaperConfiguration.default();\n\nexport function SingleWallpaperView() {\n  const [searchParams] = useSearchParams();\n  const resourceId = searchParams.get(\"id\") as WallpaperId;\n\n  const editingWallpaper = wallpapers.value.find((wallpaper) => wallpaper.id === resourceId);\n\n  const storedSettings = settings.value.byWallpaper;\n  const config = {\n    ...defaultWallpaperConfig,\n    ...(storedSettings[resourceId] || {}),\n  };\n\n  const { t } = useTranslation();\n\n  if (!editingWallpaper) {\n    return <div>Ups 404</div>;\n  }\n\n  function patchWallpaperConfig(patch: Partial<WallpaperInstanceSettings>) {\n    patchWallpaperSettings(resourceId, patch);\n  }\n\n  function onReset() {\n    resetWallpaperSettings(resourceId);\n  }\n\n  return (\n    <>\n      <div className={styles.previewContainer}>\n        <div className={styles.preview}>\n          <Wallpaper definition={editingWallpaper} config={config} muted />\n        </div>\n      </div>\n\n      <SettingsGroup>\n        <b style={{ textAlign: \"center\", fontSize: \"1.1rem\" }}>\n          <ResourceText text={editingWallpaper.metadata.displayName} />\n        </b>\n        <ResourceDescription text={editingWallpaper.metadata.description} />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"reset_all_to_default\")}\n          action={\n            <Button onClick={onReset}>\n              <Icon iconName=\"RiResetLeftLine\" />\n            </Button>\n          }\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"wall.playback\")}\n          action={\n            <Select\n              value={config.playbackSpeed}\n              options={playbackSpeedOptions}\n              onSelect={(playbackSpeed) => {\n                patchWallpaperConfig({ playbackSpeed });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.flipHorizontal\")}\n          action={\n            <Switch\n              value={config.flipHorizontal}\n              onChange={(flipHorizontal) => {\n                patchWallpaperConfig({ flipHorizontal });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.flipVertical\")}\n          action={\n            <Switch\n              value={config.flipVertical}\n              onChange={(flipVertical) => {\n                patchWallpaperConfig({ flipVertical });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.blur\")}\n          action={\n            <Slider\n              value={config.blur}\n              min={0}\n              max={50}\n              onChange={(blur) => {\n                patchWallpaperConfig({ blur });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.objectFit\")}\n          action={\n            <Select\n              value={config.objectFit}\n              options={[\n                { label: t(\"wall.fit.cover\"), value: \"cover\" },\n                { label: t(\"wall.fit.contain\"), value: \"contain\" },\n                { label: t(\"wall.fit.fill\"), value: \"fill\" },\n              ]}\n              onSelect={(objectFit) => {\n                patchWallpaperConfig({ objectFit });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.objectPosition\")}\n          action={\n            <Select\n              value={config.objectPosition}\n              options={[\n                { label: t(\"wall.position.top\"), value: \"top\" },\n                { label: t(\"wall.position.center\"), value: \"center\" },\n                { label: t(\"wall.position.bottom\"), value: \"bottom\" },\n                { label: t(\"wall.position.left\"), value: \"left\" },\n                { label: t(\"wall.position.right\"), value: \"right\" },\n              ]}\n              onSelect={(objectPosition) => {\n                patchWallpaperConfig({ objectPosition });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.saturation\")}\n          action={\n            <Slider\n              value={config.saturation}\n              min={0}\n              step={0.01}\n              max={2}\n              onChange={(saturation) => {\n                patchWallpaperConfig({ saturation });\n              }}\n            />\n          }\n        />\n\n        <SettingsOption\n          label={t(\"wall.contrast\")}\n          action={\n            <Slider\n              value={config.contrast}\n              min={0}\n              step={0.01}\n              max={2}\n              onChange={(contrast) => {\n                patchWallpaperConfig({ contrast });\n              }}\n            />\n          }\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup\n          label={\n            <SettingsOption\n              label={t(\"wall.withOverlay\")}\n              action={\n                <Switch\n                  value={config.withOverlay}\n                  onChange={(withOverlay) => {\n                    patchWallpaperConfig({ withOverlay });\n                  }}\n                />\n              }\n            />\n          }\n        >\n          <SettingsOption\n            label={t(\"wall.overlayMixBlendMode\")}\n            action={\n              <Select\n                value={config.overlayMixBlendMode}\n                options={[\n                  { label: \"normal\", value: \"normal\" },\n                  { label: \"multiply\", value: \"multiply\" },\n                  { label: \"screen\", value: \"screen\" },\n                  { label: \"overlay\", value: \"overlay\" },\n                  { label: \"darken\", value: \"darken\" },\n                  { label: \"lighten\", value: \"lighten\" },\n                  { label: \"color-dodge\", value: \"color-dodge\" },\n                  { label: \"color-burn\", value: \"color-burn\" },\n                  { label: \"hard-light\", value: \"hard-light\" },\n                  { label: \"soft-light\", value: \"soft-light\" },\n                  { label: \"difference\", value: \"difference\" },\n                  { label: \"exclusion\", value: \"exclusion\" },\n                  { label: \"hue\", value: \"hue\" },\n                  { label: \"saturation\", value: \"saturation\" },\n                  { label: \"color\", value: \"color\" },\n                  { label: \"luminosity\", value: \"luminosity\" },\n                  { label: \"plus-darker\", value: \"plus-darker\" },\n                  { label: \"plus-lighter\", value: \"plus-lighter\" },\n                ]}\n                onSelect={(overlayMixBlendMode) => {\n                  patchWallpaperConfig({ overlayMixBlendMode });\n                }}\n              />\n            }\n          />\n\n          <SettingsOption\n            label={t(\"wall.overlayColor\")}\n            action={\n              <ColorPicker\n                showText\n                value={config.overlayColor}\n                onChangeComplete={(color) => {\n                  patchWallpaperConfig({ overlayColor: color.toHexString() });\n                }}\n              />\n            }\n          />\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      {SUPPORTED_VIDEO_WALLPAPER_EXTENSIONS.some((ext) => editingWallpaper.filename?.toLowerCase()?.endsWith(ext)) && (\n        <SettingsGroup>\n          <SettingsOption\n            label={t(\"wall.muted\")}\n            action={\n              <Switch\n                value={config.muted}\n                onChange={(muted) => {\n                  patchWallpaperConfig({ muted });\n                }}\n              />\n            }\n          />\n        </SettingsGroup>\n      )}\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Wallpapers/application.ts",
    "content": "import { settings } from \"../../../state/mod\";\nimport type { WallpaperId, WallpaperInstanceSettings } from \"@seelen-ui/lib/types\";\n\n/**\n * Gets the settings for a specific wallpaper\n */\nexport function getWallpaperSettings(id: WallpaperId): WallpaperInstanceSettings | undefined {\n  return settings.value.byWallpaper[id];\n}\n\n/**\n * Patches the settings for a specific wallpaper\n */\nexport function patchWallpaperSettings(id: WallpaperId, patch: Partial<WallpaperInstanceSettings>) {\n  settings.value = {\n    ...settings.value,\n    byWallpaper: {\n      ...settings.value.byWallpaper,\n      [id]: {\n        ...(settings.value.byWallpaper[id] || {}),\n        ...patch,\n      } as WallpaperInstanceSettings,\n    },\n  };\n}\n\n/**\n * Resets the settings for a specific wallpaper\n */\nexport function resetWallpaperSettings(id: WallpaperId) {\n  const { [id]: _, ...rest } = settings.value.byWallpaper;\n  settings.value = {\n    ...settings.value,\n    byWallpaper: rest,\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Widget/AllView.tsx",
    "content": "import { ResourceKind, type Widget } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { useState } from \"preact/hooks\";\nimport { NavLink } from \"react-router\";\n\nimport cs from \"../infra.module.css\";\n\nimport { widgets } from \"../../../state/resources.ts\";\nimport { getWidgetConfig, patchWidgetConfig } from \"./application.ts\";\n\nimport { resolveDisplayName, ResourceCard, ResourceListHeader } from \"../ResourceCard.tsx\";\n\nconst SYSTEM_WIDGET_IDS = [\"@seelen/settings\", \"@seelen/popup\", \"@seelen/context-menu\"];\n\nfunction WidgetItem({ widget }: { widget: Widget }) {\n  const rootConfig = getWidgetConfig(widget.id);\n  const enabled = rootConfig?.enabled ?? (widget.loader !== \"Legacy\" && !!widget.metadata.bundled);\n\n  const query = new URLSearchParams({ id: widget.id });\n\n  return (\n    <ResourceCard\n      resource={widget}\n      kind={ResourceKind.Widget}\n      actions={\n        <>\n          {widget.settings.length > 0 && (\n            <NavLink to={`/widget?${query}`}>\n              <Button type=\"text\">\n                <Icon iconName=\"RiSettings4Fill\" />\n              </Button>\n            </NavLink>\n          )}\n          {!SYSTEM_WIDGET_IDS.includes(widget.id) && (\n            <Switch\n              value={enabled}\n              onChange={(value) => patchWidgetConfig(widget.id, { enabled: value })}\n            />\n          )}\n        </>\n      }\n    />\n  );\n}\n\nexport function WidgetsView() {\n  const { i18n } = useTranslation();\n  const [search, setSearch] = useState(\"\");\n\n  const filtered = search\n    ? widgets.value.filter((w) =>\n      resolveDisplayName(w.metadata.displayName, i18n.language).toLowerCase().includes(search.toLowerCase())\n    )\n    : widgets.value;\n\n  return (\n    <>\n      <ResourceListHeader\n        discoverUrl=\"https://seelen.io/resources/s?category=Widget\"\n        search={search}\n        onSearch={setSearch}\n      />\n\n      <div className={cs.list}>\n        {filtered.map((widget) => <WidgetItem key={widget.id} widget={widget} />)}\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Widget/ConfigRenderer.tsx",
    "content": "import {\n  type WidgetConfigDefinition,\n  WidgetSelectSubtype,\n  type WidgetSettingItem,\n  type WidgetSettingsDeclarationList,\n} from \"@seelen-ui/lib/types\";\nimport { ResourceText } from \"libs/ui/react/components/ResourceText/index.tsx\";\nimport { Button, ColorPicker, Flex, Input, InputNumber, Select, Slider, Switch, Tooltip } from \"antd\";\nimport type { ReactNode } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../components/SettingsBox/index.tsx\";\nimport Compact from \"antd/es/space/Compact\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\n\ninterface Props {\n  // settings definitions\n  definitions: WidgetSettingsDeclarationList;\n  // config values set by the user\n  values: Record<string, any>;\n  // callback to update the config\n  onConfigChange: (key: string, value: any) => void;\n  // whether the widget is being configured by monitor\n  isByMonitor?: boolean;\n}\n\nexport function RenderBySettingsDeclaration({ definitions, values, onConfigChange }: Props) {\n  return (\n    <>\n      {definitions.map((definition, idx) => (\n        <WidgetConfigDefinition\n          key={idx}\n          definition={definition}\n          values={values}\n          onConfigChange={onConfigChange}\n          nestLevel={0}\n        />\n      ))}\n    </>\n  );\n}\n\n// ================================================\n\ninterface WidgetConfigDefinitionProps {\n  definition: WidgetConfigDefinition;\n  values: Record<string, any>;\n  onConfigChange: (key: string, value: any) => void;\n  nestLevel: number;\n}\n\nfunction WidgetConfigDefinition({\n  definition,\n  values,\n  onConfigChange,\n  nestLevel,\n}: WidgetConfigDefinitionProps) {\n  const content = renderContent(definition, values, onConfigChange, nestLevel);\n\n  return nestLevel === 0 ? <SettingsGroup>{content}</SettingsGroup> : content;\n}\n\nfunction renderContent(\n  definition: WidgetConfigDefinition,\n  values: Record<string, any>,\n  onConfigChange: (key: string, value: any) => void,\n  nestLevel: number,\n): ReactNode {\n  // Check if it's a group (has \"group\" property)\n  if (\"group\" in definition) {\n    return (\n      <SettingsSubGroup label={<ResourceText text={definition.group.label} />}>\n        {definition.group.items.map((item, idx) => (\n          <WidgetConfigDefinition\n            key={idx}\n            definition={item}\n            values={values}\n            onConfigChange={onConfigChange}\n            nestLevel={nestLevel + 1}\n          />\n        ))}\n      </SettingsSubGroup>\n    );\n  }\n\n  // It's a setting item\n  return <WidgetSettingItemRenderer def={definition} values={values} onConfigChange={onConfigChange} />;\n}\n\n// ================================================\n\ninterface WidgetSettingItemRendererProps {\n  def: WidgetSettingItem;\n  values: Record<string, any>;\n  onConfigChange: (key: string, value: any) => void;\n}\n\nfunction WidgetSettingItemRenderer({\n  def,\n  values,\n  onConfigChange,\n}: WidgetSettingItemRendererProps) {\n  // Check if all dependencies are met\n  const isDependencyMet = useMemo(() => {\n    if (!def.dependencies || def.dependencies.length === 0) {\n      return true;\n    }\n    return def.dependencies.every((depKey) => !!values[depKey]);\n  }, [def.dependencies, values]);\n\n  if (!isDependencyMet) {\n    return null;\n  }\n\n  const action = renderInput(def, values, onConfigChange);\n\n  return (\n    <SettingsOption\n      label={<ResourceText text={def.label} />}\n      tip={def.tip ? <ResourceText text={def.tip} /> : undefined}\n      description={def.description ? <ResourceText text={def.description} /> : undefined}\n      action={action}\n    />\n  );\n}\n\nfunction renderInput(\n  def: WidgetSettingItem,\n  values: Record<string, any>,\n  onConfigChange: (key: string, value: any) => void,\n): ReactNode {\n  const commonProps = {\n    value: values[def.key] ?? def.defaultValue,\n    onChange: (value: any) => onConfigChange(def.key, value),\n  };\n\n  switch (def.type) {\n    case \"Switch\": {\n      return <Switch {...commonProps} />;\n    }\n\n    case \"Select\": {\n      if (def.subtype === WidgetSelectSubtype.Inline) {\n        return (\n          <Compact>\n            {def.options.map(({ value, label, icon }) => (\n              <Tooltip key={value} title={<ResourceText text={label} />}>\n                <Button\n                  key={value}\n                  type={value === (values[def.key] ?? def.defaultValue) ? \"primary\" : \"default\"}\n                  onClick={() => onConfigChange(def.key, value)}\n                >\n                  {icon ? <Icon iconName={icon as any} /> : value}\n                </Button>\n              </Tooltip>\n            ))}\n          </Compact>\n        );\n      }\n\n      // Convert WidgetSelectOption[] to Ant Design Select options format\n      const options = def.options.map((opt) => ({\n        label: (\n          <Flex gap={8} align=\"center\">\n            {opt.icon && <Icon iconName={opt.icon as any} />}\n            <ResourceText text={opt.label} />\n          </Flex>\n        ),\n        value: opt.value,\n      }));\n\n      return <Select {...commonProps} options={options} />;\n    }\n\n    case \"InputText\": {\n      const textProps = {\n        ...commonProps,\n        minLength: def.minLength ?? undefined,\n        maxLength: def.maxLength ?? undefined,\n        onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>\n          onConfigChange(def.key, e.currentTarget.value),\n      };\n\n      if (def.multiline) {\n        return <Input.TextArea {...textProps} />;\n      }\n\n      return <Input {...textProps} />;\n    }\n\n    case \"InputNumber\": {\n      return (\n        <InputNumber\n          {...commonProps}\n          min={def.min ?? undefined}\n          max={def.max ?? undefined}\n          step={def.step ?? undefined}\n        />\n      );\n    }\n\n    case \"Range\": {\n      return (\n        <Slider\n          {...commonProps}\n          style={{ width: \"200px\" }}\n          min={def.min ?? undefined}\n          max={def.max ?? undefined}\n          step={def.step ?? undefined}\n        />\n      );\n    }\n\n    case \"Color\": {\n      return (\n        <ColorPicker\n          {...commonProps}\n          disabledAlpha={!def.allowAlpha}\n          onChange={undefined}\n          onChangeComplete={(v) => {\n            onConfigChange(def.key, v.toHexString());\n          }}\n        />\n      );\n    }\n\n    default: {\n      // @ts-expect-error should never happen\n      def.type;\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Widget/InstanceSelector.tsx",
    "content": "import type { WidgetId } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Select, Space } from \"antd\";\n\nimport { patchWidgetInstanceConfig, removeWidgetInstance } from \"./application.ts\";\n\ninterface InstanceSelectorProps {\n  widgetId: WidgetId;\n  selected: string | null;\n  onSelect: (value: string | null) => void;\n  options: {\n    value: string;\n    label: string;\n  }[];\n}\n\nexport function WidgetInstanceSelector(\n  { widgetId, options, selected, onSelect }: InstanceSelectorProps,\n) {\n  const onInstanceCreated = () => {\n    const instanceId = crypto.randomUUID();\n    patchWidgetInstanceConfig(widgetId, instanceId, {});\n    onSelect(instanceId);\n  };\n\n  const onInstanceDeleted = () => {\n    if (selected) {\n      const idx = options.findIndex((t) => t.value === selected);\n      const newIdx = idx === 0 ? idx + 1 : idx - 1;\n      onSelect(options[newIdx]?.value || null);\n      removeWidgetInstance(widgetId, selected);\n    }\n  };\n\n  return (\n    <Space.Compact>\n      <Select\n        style={{ width: 300 }}\n        value={selected}\n        onSelect={onSelect}\n        options={options}\n        allowClear\n        onClear={() => onSelect(null)}\n        placeholder=\"-\"\n      />\n      <Button onClick={onInstanceCreated}>\n        <Icon iconName=\"FaPlus\" />\n      </Button>\n      <Button onClick={onInstanceDeleted}>\n        <Icon iconName=\"IoTrash\" />\n      </Button>\n    </Space.Compact>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Widget/View.tsx",
    "content": "import type { WidgetId } from \"@seelen-ui/lib/types\";\nimport { Switch } from \"antd\";\nimport { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useSearchParams } from \"react-router\";\n\nimport {\n  getMonitorWidgetConfig,\n  getWidgetConfig,\n  patchWidgetConfig as patchWidget,\n  patchWidgetInstanceConfig as patchInstance,\n  patchWidgetMonitorConfig as patchMonitor,\n} from \"./application.ts\";\nimport { widgets } from \"../../../state/resources.ts\";\nimport { getDevTools } from \"../../developer/application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../../components/SettingsBox/index.tsx\";\nimport { ResourceDescription } from \"../ResourceCard.tsx\";\nimport { RenderBySettingsDeclaration } from \"./ConfigRenderer.tsx\";\nimport { WidgetInstanceSelector } from \"./InstanceSelector.tsx\";\nimport { SeelenWegSettings } from \"../../seelenweg/infra.tsx\";\nimport { WindowManagerSettings } from \"../../WindowManager/main/infra/index.tsx\";\nimport { FancyToolbarSettings } from \"../../fancyToolbar/infra.tsx\";\nimport { WallSettings } from \"../../Wall/infra.tsx\";\n\nexport function WidgetConfiguration({\n  widgetId,\n  monitorId,\n}: {\n  widgetId: WidgetId;\n  monitorId?: string;\n}) {\n  const [selectedInstance, setSelectedInstance] = useState<string | null>(null);\n\n  const widget = widgets.value.find((t) => t.id === widgetId);\n  const rootConfig = getWidgetConfig(widgetId) || {\n    enabled: widget?.loader !== \"Legacy\" && !!widget?.metadata.bundled,\n  };\n\n  const monitorConfig = monitorId ? getMonitorWidgetConfig(monitorId, widgetId) : undefined;\n  const areDevToolsEnabled = getDevTools();\n\n  const { t } = useTranslation();\n\n  if (!widget) {\n    return <div>404</div>;\n  }\n\n  const onConfigChange = (key: string, value: any) => {\n    if (monitorId) {\n      patchMonitor(monitorId, widgetId, { [key]: value });\n      return;\n    }\n\n    // intances `enabled` always inherit from widget root config\n    if (selectedInstance && key !== \"enabled\") {\n      patchInstance(widgetId, selectedInstance, { [key]: value });\n      return;\n    }\n\n    patchWidget(widgetId, { [key]: value });\n  };\n\n  const instances = Object.keys(rootConfig.$instances || {}).map((instanceId) => ({\n    label: `Instance ${instanceId.slice(0, 6)}`,\n    value: instanceId,\n  }));\n\n  const instanceConfig = selectedInstance ? rootConfig.$instances?.[selectedInstance] : undefined;\n  const config = {\n    ...rootConfig,\n    ...(instanceConfig || {}),\n    ...(monitorConfig || {}),\n  };\n\n  const showToggleEnabled = !monitorId || widget.instances === \"ReplicaByMonitor\";\n\n  return (\n    <>\n      <SettingsGroup>\n        <ResourceDescription text={widget.metadata.description} />\n      </SettingsGroup>\n\n      {showToggleEnabled && (\n        <SettingsGroup>\n          <SettingsOption>\n            <b>{monitorId ? t(\"widget.enable_for_monitor\") : t(\"widget.enable\")}</b>\n            <Switch\n              checked={config.enabled}\n              onChange={(value) => {\n                onConfigChange(\"enabled\", value);\n              }}\n            />\n          </SettingsOption>\n        </SettingsGroup>\n      )}\n\n      {widget.instances === \"Multiple\" && (\n        <SettingsGroup>\n          <SettingsOption>\n            <b>{t(\"widget.instances\")}</b>\n            <WidgetInstanceSelector\n              widgetId={widgetId}\n              options={instances}\n              selected={selectedInstance}\n              onSelect={setSelectedInstance}\n            />\n          </SettingsOption>\n        </SettingsGroup>\n      )}\n\n      <RenderBySettingsDeclaration\n        definitions={widget.settings}\n        values={config}\n        onConfigChange={onConfigChange}\n        isByMonitor={!!monitorId}\n      />\n\n      {areDevToolsEnabled && (\n        <SettingsGroup>\n          <SettingsSubGroup label={<b>Raw Config</b>}>\n            <pre>{JSON.stringify(rootConfig, null, 2)}</pre>\n          </SettingsSubGroup>\n          {!!monitorId && (\n            <SettingsSubGroup label={<b>Raw Monitor Patch</b>}>\n              <pre>{monitorConfig ? JSON.stringify(monitorConfig, null, 2) : \"Inherited\"}</pre>\n            </SettingsSubGroup>\n          )}\n        </SettingsGroup>\n      )}\n    </>\n  );\n}\n\nexport function WidgetView() {\n  const [searchParams] = useSearchParams();\n  const widgetId = searchParams.get(\"id\") as WidgetId;\n\n  if (widgetId === \"@seelen/weg\") {\n    return <SeelenWegSettings />;\n  }\n\n  if (widgetId === \"@seelen/window-manager\") {\n    return <WindowManagerSettings />;\n  }\n\n  if (widgetId === \"@seelen/fancy-toolbar\") {\n    return <FancyToolbarSettings />;\n  }\n\n  if (widgetId === \"@seelen/wallpaper-manager\") {\n    return <WallSettings />;\n  }\n\n  return <WidgetConfiguration widgetId={widgetId} />;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/Widget/application.ts",
    "content": "import { settings } from \"../../../state/mod\";\nimport type { WidgetId } from \"@seelen-ui/lib/types\";\n\n/**\n * Gets the configuration for a specific widget\n */\nexport function getWidgetConfig(widgetId: WidgetId) {\n  return settings.value.byWidget[widgetId];\n}\n\n/**\n * Patches the configuration for a specific widget\n */\nexport function patchWidgetConfig(widgetId: WidgetId, config: Record<string, unknown>) {\n  const currentWidget = settings.value.byWidget[widgetId] || { enabled: true };\n\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      [widgetId]: {\n        ...currentWidget,\n        ...config,\n      },\n    },\n  };\n}\n\n/**\n * Gets the configuration for a specific widget instance\n */\nexport function getWidgetInstanceConfig(widgetId: WidgetId, instanceId: string) {\n  return settings.value.byWidget[widgetId]?.$instances?.[instanceId];\n}\n\n/**\n * Patches the configuration for a specific widget instance\n */\nexport function patchWidgetInstanceConfig(\n  widgetId: WidgetId,\n  instanceId: string,\n  config: Record<string, any>,\n) {\n  const currentWidget = settings.value.byWidget[widgetId] || { enabled: true };\n  const instances = currentWidget.$instances || {};\n  const currentInstance = instances[instanceId] || {};\n\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      [widgetId]: {\n        ...currentWidget,\n        $instances: {\n          ...instances,\n          [instanceId]: {\n            ...currentInstance,\n            ...config,\n          },\n        },\n      },\n    },\n  };\n}\n\n/**\n * Removes a widget instance\n */\nexport function removeWidgetInstance(widgetId: WidgetId, instanceId: string) {\n  const currentWidget = settings.value.byWidget[widgetId];\n  if (!currentWidget) {\n    return;\n  }\n\n  const instances = currentWidget.$instances || {};\n  const { [instanceId]: _, ...remainingInstances } = instances;\n\n  const newWidget = { ...currentWidget };\n  if (Object.keys(remainingInstances).length === 0) {\n    delete newWidget.$instances;\n  } else {\n    newWidget.$instances = remainingInstances;\n  }\n\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      [widgetId]: newWidget,\n    },\n  };\n}\n\n/**\n * Gets the monitor-specific configuration for a widget\n */\nexport function getMonitorWidgetConfig(monitorId: string, widgetId: WidgetId) {\n  return settings.value.monitorsV3[monitorId]?.byWidget[widgetId];\n}\n\n/**\n * Patches the monitor-specific configuration for a widget\n */\nexport function patchWidgetMonitorConfig(\n  monitorId: string,\n  widgetId: WidgetId,\n  config: Record<string, any>,\n) {\n  const monitor = settings.value.monitorsV3[monitorId];\n  if (!monitor) {\n    return;\n  }\n\n  const currentWidgetConfig = monitor.byWidget[widgetId] || { enabled: true };\n\n  settings.value = {\n    ...settings.value,\n    monitorsV3: {\n      ...settings.value.monitorsV3,\n      [monitorId]: {\n        ...monitor,\n        byWidget: {\n          ...monitor.byWidget,\n          [widgetId]: {\n            ...currentWidgetConfig,\n            ...config,\n          },\n        },\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/infra.module.css",
    "content": ".kindSelector {\n  display: grid;\n  grid-template-columns: 1fr 1fr 1fr;\n  grid-auto-rows: 1fr;\n  gap: var(--spacing-s);\n  width: 100%;\n  height: 100%;\n\n  .kind {\n    width: 100%;\n    height: 100%;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    gap: var(--spacing-xs);\n    border: 2px solid var(--color-gray-300);\n    border-radius: 8px;\n\n    &:hover {\n      background-color: var(--color-blue-100);\n      border: 2px solid var(--color-blue-400);\n      color: var(--color-blue-800);\n    }\n\n    .kindIcon {\n      height: 25%;\n    }\n  }\n\n  .kindDisabled {\n    opacity: 0.4;\n    cursor: not-allowed;\n    pointer-events: none;\n  }\n}\n\n.container {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: var(--spacing-s);\n}\n\n.list {\n  display: flex;\n  flex-direction: column;\n  gap: var(--spacing-s);\n\n  .reorderGroup {\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-s);\n  }\n\n  .card {\n    width: 100%;\n    height: 120px;\n    padding: var(--spacing-s);\n    border-radius: 18px;\n\n    display: grid;\n    grid-template-columns: 100px 1fr;\n    grid-template-rows: min-content 1fr min-content;\n    gap: var(--spacing-2xs) var(--spacing-s);\n\n    background-color: var(--color-gray-50);\n\n    /* &.warn {\n      border: 1px solid var(--color-yellow-100);\n    } */\n\n    &.danger {\n      background-color: var(--color-red-100);\n      border: 1px solid var(--color-red-800);\n      color: var(--color-red-900);\n    }\n\n    .portrait {\n      grid-column: 1 / 2;\n      grid-row: 1 / 4;\n    }\n\n    .header {\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n      overflow: hidden;\n      gap: var(--spacing-xs);\n\n      .title {\n        font-weight: 600;\n        font-size: 1.2rem;\n        white-space: nowrap;\n        overflow: hidden;\n        text-overflow: ellipsis;\n      }\n\n      .actionsTop {\n        display: flex;\n        align-items: center;\n        gap: var(--spacing-xs);\n      }\n    }\n\n    .body {\n      overflow: auto;\n      padding-right: var(--spacing-xs);\n    }\n\n    .footer {\n      display: flex;\n      justify-content: flex-end;\n      align-items: center;\n      gap: var(--spacing-xs);\n    }\n  }\n}\n\n.portrait {\n  background-color: var(--color-gray-200);\n  border-radius: 10px;\n  aspect-ratio: 1/1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: relative;\n\n  > video,\n  > img {\n    width: 100%;\n    height: 100%;\n    border-radius: 8px;\n    object-fit: cover;\n  }\n\n  .kindIcon {\n    height: 50%;\n  }\n\n  .warning,\n  .danger,\n  .corrupted {\n    position: absolute;\n    bottom: 8px;\n    right: 8px;\n    height: 20px;\n  }\n\n  .warning {\n    color: var(--color-yellow-500);\n    opacity: 0.6;\n  }\n\n  .danger {\n    color: var(--color-red-900);\n  }\n}\n\n.description {\n  max-height: 200px;\n  overflow-y: auto;\n  overflow-x: hidden;\n  margin-right: calc(var(--spacing-s) * -1);\n  padding-right: var(--spacing-s);\n}\n\n.tags {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--spacing-2xs);\n\n  .tag {\n    display: flex;\n    align-items: center;\n    padding: var(--spacing-2xs);\n    border-radius: 6px;\n    background-color: var(--color-gray-100);\n    color: var(--color-gray-600);\n    font-size: 0.7rem;\n    line-height: 1em;\n\n    &.warn {\n      background-color: light-dark(#ffffb9, #4e4400);\n      color: var(--color-yellow-500);\n    }\n\n    &.danger {\n      background-color: var(--color-red-100);\n      color: var(--color-red-600);\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/resources/infra.tsx",
    "content": "import { ResourceKind } from \"@seelen-ui/lib/types\";\nimport { useTranslation } from \"react-i18next\";\nimport { NavLink, Route, Routes } from \"react-router\";\n\nimport cs from \"./infra.module.css\";\n\nimport { RoutePath } from \"../../components/navigation/routes.tsx\";\nimport { IconPacksView } from \"./IconPacks.tsx\";\nimport { PluginsView } from \"./Plugins.tsx\";\nimport { ResourceIcon } from \"./ResourceCard.tsx\";\nimport { SoundPacksView } from \"./SoundPacks.tsx\";\nimport { ThemesView } from \"./Theme/AllView.tsx\";\nimport { AllWallpapersView } from \"./Wallpapers/AllView.tsx\";\nimport { WidgetsView } from \"./Widget/AllView.tsx\";\n\nconst kinds = Object.values(ResourceKind);\nconst DISABLED_KINDS: ResourceKind[] = [ResourceKind.SoundPack];\n\nexport function ResourcesView() {\n  return (\n    <Routes>\n      <Route index Component={KindSelector} />\n      <Route path=\"theme\" Component={ThemesView} />\n      <Route path=\"plugin\" Component={PluginsView} />\n      <Route path=\"widget\" Component={WidgetsView} />\n      <Route path=\"wallpaper\" Component={AllWallpapersView} />\n      <Route path=\"iconpack\" Component={IconPacksView} />\n      <Route path=\"soundpack\" Component={SoundPacksView} />\n    </Routes>\n  );\n}\n\nfunction KindSelector() {\n  const { t } = useTranslation();\n\n  return (\n    <div className={cs.kindSelector}>\n      {kinds.map((kind) => {\n        const disabled = DISABLED_KINDS.includes(kind);\n        return disabled\n          ? (\n            <div key={kind} className={`${cs.kind} ${cs.kindDisabled}`}>\n              <ResourceIcon kind={kind} />\n              <b>{t(`header.labels.${kind.toLowerCase()}`)}</b>\n            </div>\n          )\n          : (\n            <NavLink\n              key={kind}\n              to={`${RoutePath.Resource}/${kind.toLowerCase()}`}\n              className={cs.kind}\n            >\n              <ResourceIcon kind={kind} />\n              <b>{t(`header.labels.${kind.toLowerCase()}`)}</b>\n            </NavLink>\n          );\n      })}\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/seelenweg/application.ts",
    "content": "import { settings } from \"../../state/mod\";\nimport type { SeelenWegSettings } from \"@seelen-ui/lib/types\";\n\n/**\n * Patches the SeelenWeg configuration with partial updates.\n * This helper function simplifies updating the weg settings by handling\n * the nested structure automatically.\n *\n * @example\n * patchWegConfig({ enabled: true, margin: 10 });\n */\nexport function patchWegConfig(patch: Partial<SeelenWegSettings>) {\n  settings.value = {\n    ...settings.value,\n    byWidget: {\n      ...settings.value.byWidget,\n      \"@seelen/weg\": {\n        ...settings.value.byWidget[\"@seelen/weg\"],\n        ...patch,\n      },\n    },\n  };\n}\n\n/**\n * Gets the current SeelenWeg configuration\n */\nexport function getWegConfig(): SeelenWegSettings {\n  return settings.value.byWidget[\"@seelen/weg\"];\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/seelenweg/infra.tsx",
    "content": "import { HideMode, SeelenWegMode, SeelenWegSide } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, InputNumber, Select, Switch } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { OptionsFromEnum } from \"../shared/utils/app.ts\";\nimport { getWegConfig, patchWegConfig } from \"./application.ts\";\n\nimport { SettingsGroup, SettingsOption, SettingsSubGroup } from \"../../components/SettingsBox/index.tsx\";\nimport Compact from \"antd/es/space/Compact\";\n\nexport const SeelenWegSettings = () => {\n  const settings = getWegConfig();\n\n  const { t } = useTranslation();\n\n  const onToggleEnable = (value: boolean) => {\n    patchWegConfig({ enabled: value });\n  };\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption>\n          <div>\n            <b>{t(\"weg.enable\")}</b>\n          </div>\n          <Switch checked={settings.enabled} onChange={onToggleEnable} />\n        </SettingsOption>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"weg.label\")}>\n          <SettingsOption>\n            <div>{t(\"weg.width\")}</div>\n            <Select\n              style={{ width: \"120px\" }}\n              value={settings.mode}\n              options={OptionsFromEnum(t, SeelenWegMode, \"weg.mode\")}\n              onChange={(value) => patchWegConfig({ mode: value })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.dock_side\")}</div>\n            <Compact>\n              {Object.values(SeelenWegSide).map((side) => (\n                <Button\n                  key={side}\n                  type={side === settings.position ? \"primary\" : \"default\"}\n                  onClick={() => patchWegConfig({ position: side })}\n                >\n                  <Icon iconName={`CgToolbar${side}`} size={18} />\n                </Button>\n              ))}\n            </Compact>\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.margin\")}</div>\n            <InputNumber\n              value={settings.margin}\n              onChange={(value) => patchWegConfig({ margin: value || 0 })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.padding\")}</div>\n            <InputNumber\n              value={settings.padding}\n              onChange={(value) => patchWegConfig({ padding: value || 0 })}\n            />\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup\n          label={\n            <SettingsOption>\n              <b>{t(\"weg.auto_hide\")}</b>\n              <Select\n                style={{ width: \"120px\" }}\n                value={settings.hideMode}\n                options={OptionsFromEnum(t, HideMode, \"weg.hide_mode\")}\n                onChange={(value) => patchWegConfig({ hideMode: value })}\n              />\n            </SettingsOption>\n          }\n        >\n          <SettingsOption>\n            <span>{t(\"weg.delay_to_show\")} (ms)</span>\n            <InputNumber\n              value={settings.delayToShow}\n              min={0}\n              disabled={settings.hideMode === HideMode.Never}\n              onChange={(value) => patchWegConfig({ delayToShow: value || 0 })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <span>{t(\"weg.delay_to_hide\")} (ms)</span>\n            <InputNumber\n              value={settings.delayToHide}\n              min={0}\n              disabled={settings.hideMode === HideMode.Never}\n              onChange={(value) => patchWegConfig({ delayToHide: value || 0 })}\n            />\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"weg.filtering\")}>\n          <SettingsOption>\n            <div>{t(\"weg.items.temporal_visibility.label\")}</div>\n            <Select\n              style={{ width: \"120px\" }}\n              value={settings.temporalItemsVisibility}\n              options={[\n                { value: \"All\", label: t(\"weg.items.temporal_visibility.all\") },\n                {\n                  value: \"OnMonitor\",\n                  label: t(\"weg.items.temporal_visibility.on_monitor\"),\n                },\n              ]}\n              onChange={(value) => patchWegConfig({ temporalItemsVisibility: value })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.items.pinned_visibility.label\")}</div>\n            <Select\n              style={{ width: \"120px\" }}\n              value={settings.pinnedItemsVisibility}\n              options={[\n                {\n                  value: \"Always\",\n                  label: t(\"weg.items.pinned_visibility.always\"),\n                },\n                {\n                  value: \"WhenPrimary\",\n                  label: t(\"weg.items.pinned_visibility.when_primary\"),\n                },\n              ]}\n              onChange={(value) => patchWegConfig({ pinnedItemsVisibility: value })}\n            />\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n\n      <SettingsGroup>\n        <SettingsSubGroup label={t(\"weg.items.label\")}>\n          <SettingsOption>\n            <div>{t(\"weg.items.size\")}</div>\n            <InputNumber\n              value={settings.size}\n              onChange={(value) => patchWegConfig({ size: value || 0 })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.items.gap\")}</div>\n            <InputNumber\n              value={settings.spaceBetweenItems}\n              onChange={(value) => patchWegConfig({ spaceBetweenItems: value || 0 })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.items.show_window_title\")}</div>\n            <Switch\n              checked={settings.showWindowTitle}\n              onChange={(value) => patchWegConfig({ showWindowTitle: value })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.items.show_instance_counter\")}</div>\n            <Switch\n              checked={settings.showInstanceCounter}\n              onChange={(value) => patchWegConfig({ showInstanceCounter: value })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.items.visible_separators\")}</div>\n            <Switch\n              checked={settings.visibleSeparators}\n              onChange={(value) => patchWegConfig({ visibleSeparators: value })}\n            />\n          </SettingsOption>\n          <SettingsOption>\n            <div>{t(\"weg.items.split_windows\")}</div>\n            <Switch\n              checked={settings.splitWindows}\n              onChange={(value) => patchWegConfig({ splitWindows: value })}\n            />\n          </SettingsOption>\n        </SettingsSubGroup>\n      </SettingsGroup>\n    </>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/settings/modules/shared/config/infra.ts",
    "content": "import { app, path } from \"@tauri-apps/api\";\n\nexport const EnvConfig = {\n  version: await app.getVersion(),\n};\n\nexport async function resolveDataPath(...sub: string[]) {\n  return await path.join(await path.appDataDir(), ...sub);\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/shared/signals.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { lazySignal } from \"libs/ui/react/utils/LazySignal\";\nimport { signal } from \"@preact/signals\";\n\nexport const $virtual_desktops = lazySignal(async () => {\n  return await invoke(SeelenCommand.StateGetVirtualDesktops);\n});\n\nsubscribe(SeelenEvent.VirtualDesktopsChanged, (event) => {\n  $virtual_desktops.value = event.payload;\n});\n\nawait $virtual_desktops.init();\n\n/**\n * Session-only set of wallpaper IDs that failed thumbnail extraction\n * These wallpapers will be marked as corrupted and won't be retried\n */\nexport const $corruptedWallpapers = signal<Set<string>>(new Set());\n"
  },
  {
    "path": "src/ui/react/settings/modules/shared/tauri/infra.ts",
    "content": "import { SeelenCommand } from \"@seelen-ui/lib\";\nimport { invoke } from \"@tauri-apps/api/core\";\n\nexport class startup {\n  static async enable(): Promise<void> {\n    await invoke(SeelenCommand.SetAutoStart, { enabled: true });\n  }\n\n  static async disable(): Promise<void> {\n    await invoke(SeelenCommand.SetAutoStart, { enabled: false });\n  }\n\n  static async isEnabled(): Promise<boolean> {\n    return await invoke<boolean>(\"get_auto_start_status\");\n  }\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/shared/utils/app.ts",
    "content": "import type { TFunction } from \"i18next\";\n\nimport type { HexColor } from \"./domain.ts\";\n\ntype Args = undefined | string | { [x: string]: boolean | null | undefined };\nexport const cx = (...args: Args[]): string => {\n  return args\n    .map((arg) => {\n      if (!arg) {\n        return;\n      }\n\n      if (typeof arg === \"string\") {\n        return arg;\n      }\n\n      return Object.keys(arg)\n        .map((key) => (arg[key] ? key : \"\"))\n        .join(\" \");\n    })\n    .join(\" \");\n};\n\nexport const capitalize = (text: string) => {\n  return text.slice(0, 1).toUpperCase() + text.slice(1);\n};\n\nexport const defaultOnNull = <T>(value: T | null | undefined, onNull: T): T => {\n  if (value == null) {\n    return onNull;\n  }\n  return value;\n};\n\nexport const validateHexColor = (str: string): HexColor | null => {\n  if (!str.startsWith(\"#\")) {\n    return null;\n  }\n  return str as HexColor;\n};\n\nexport const OptionsFromEnum = (\n  t: TFunction,\n  obj: anyObject,\n  translationPrefix: string,\n) => {\n  return Object.values(obj).map((value) => ({\n    label: t(translationPrefix + \".\" + toSnakeCase(value)),\n    value,\n  }));\n};\n\nfunction toSnakeCase(text: string) {\n  let snake = \"\";\n  for (let i = 0; i < text.length; i++) {\n    const char = text[i]!;\n    if (char === char.toLowerCase() && !char.match(/[0-9]/)) {\n      snake += char.toLowerCase();\n    } else if (i == 0) {\n      snake += `${char.toLowerCase()}`;\n    } else {\n      snake += `_${char.toLowerCase()}`;\n    }\n  }\n  return snake;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/shared/utils/domain.ts",
    "content": "export type HexColor = `#${string}`;\n"
  },
  {
    "path": "src/ui/react/settings/modules/shortcuts/application.ts",
    "content": "import type { SluHotkey, SluShortcutsSettings } from \"@seelen-ui/lib/types\";\nimport { signal } from \"@preact/signals\";\nimport { settings } from \"../../state/mod\";\nimport { Settings } from \"@seelen-ui/lib\";\nimport type { WidgetId } from \"@seelen-ui/lib/types\";\n\nexport const shortcutsError = signal<Set<string>>(new Set());\n\n// Load default settings for reset functionality\nconst defaultSettings = await Settings.default();\n\ninterface Groups {\n  windowManager: {\n    state: SluHotkey[];\n    sizing: SluHotkey[];\n    positioning: SluHotkey[];\n    tilingFocus: SluHotkey[];\n    tilingLayout: SluHotkey[];\n  };\n  wallpaperManager: SluHotkey[];\n  virtualDesktop: {\n    main: SluHotkey[];\n    switch: SluHotkey[];\n    move: SluHotkey[];\n    send: SluHotkey[];\n  };\n  weg: SluHotkey[];\n  misc: SluHotkey[];\n}\n\nexport function getHotkeysGroups(hotkeys: SluHotkey[]): Groups {\n  const groups: Groups = {\n    windowManager: {\n      state: [],\n      sizing: [],\n      positioning: [],\n      tilingFocus: [],\n      tilingLayout: [],\n    },\n    wallpaperManager: [],\n    virtualDesktop: {\n      main: [],\n      switch: [],\n      move: [],\n      send: [],\n    },\n    weg: [],\n    misc: [],\n  };\n\n  for (const hotkey of hotkeys) {\n    const {\n      action: { name },\n    } = hotkey;\n\n    switch (name) {\n      // window manager\n      case \"increase_width\":\n      case \"decrease_width\":\n      case \"increase_height\":\n      case \"decrease_height\":\n      case \"restore_sizes\":\n        groups.windowManager.sizing.push(hotkey);\n        break;\n      case \"focus_top\":\n      case \"focus_bottom\":\n      case \"focus_left\":\n      case \"focus_right\":\n        groups.windowManager.tilingFocus.push(hotkey);\n        break;\n      case \"move_window_left\":\n      case \"move_window_right\":\n      case \"move_window_up\":\n      case \"move_window_down\":\n        groups.windowManager.positioning.push(hotkey);\n        break;\n      case \"reserve_left\":\n      case \"reserve_right\":\n      case \"reserve_top\":\n      case \"reserve_bottom\":\n      case \"reserve_stack\":\n      case \"reserve_float\":\n        groups.windowManager.tilingLayout.push(hotkey);\n        break;\n      case \"pause_tiling\":\n      case \"toggle_float\":\n      case \"toggle_monocle\":\n      case \"cycle_stack_next\":\n      case \"cycle_stack_prev\":\n        groups.windowManager.state.push(hotkey);\n        break;\n      // weg\n      case \"start_weg_app\":\n        groups.weg.push(hotkey);\n        break;\n      // wallpaper manager\n      case \"cycle_wallpaper_next\":\n      case \"cycle_wallpaper_prev\":\n        groups.wallpaperManager.push(hotkey);\n        break;\n      // virtual desktop\n      case \"create_new_workspace\":\n      case \"destroy_current_workspace\":\n      case \"switch_to_next_workspace\":\n      case \"switch_to_previous_workspace\":\n        groups.virtualDesktop.main.push(hotkey);\n        break;\n      case \"switch_workspace\":\n        groups.virtualDesktop.switch.push(hotkey);\n        break;\n      case \"move_to_workspace\":\n        groups.virtualDesktop.move.push(hotkey);\n        break;\n      case \"send_to_workspace\":\n        groups.virtualDesktop.send.push(hotkey);\n        break;\n      // misc\n      case \"misc_open_settings\":\n      case \"misc_toggle_lock_tracing\":\n      case \"misc_toggle_win_event_tracing\":\n      case \"misc_force_restart\":\n      case \"misc_force_quit\":\n        groups.misc.push(hotkey);\n        break;\n      default:\n        break;\n    }\n  }\n\n  return groups;\n}\n\nexport function validateShortcuts(hotkeys: SluHotkey[]) {\n  const errors = new Set<string>();\n  const shortcutMap = new Map<string, string[]>();\n\n  for (const hotkey of hotkeys) {\n    if (hotkey.readonly || hotkey.system) {\n      continue;\n    }\n\n    const shortcutKey = hotkey.keys.join(\"+\").toLowerCase();\n    if (!shortcutMap.has(shortcutKey)) {\n      shortcutMap.set(shortcutKey, []);\n    }\n    shortcutMap.get(shortcutKey)!.push(hotkey.id);\n  }\n\n  for (const [, ids] of shortcutMap) {\n    if (ids.length > 1) {\n      ids.forEach((id) => errors.add(id));\n    }\n  }\n\n  shortcutsError.value = errors;\n}\n\n/**\n * Gets the current shortcuts configuration\n */\nexport function getShortcutsConfig(): SluShortcutsSettings {\n  return settings.value.shortcuts;\n}\n\n/**\n * Sets the enabled state for shortcuts\n */\nexport function setShortcutsEnabled(enabled: boolean) {\n  settings.value = {\n    ...settings.value,\n    shortcuts: {\n      ...settings.value.shortcuts,\n      enabled,\n    },\n  };\n}\n\n/**\n * Updates a specific shortcut by id\n */\nexport function updateShortcut(id: string, keys: string[]) {\n  const appCommands = settings.value.shortcuts.appCommands.map((c) => c.id === id ? { ...c, keys } : c);\n\n  settings.value = {\n    ...settings.value,\n    shortcuts: {\n      ...settings.value.shortcuts,\n      appCommands,\n    },\n  };\n}\n\n/**\n * Resets all shortcuts to default values\n */\nexport function resetShortcuts() {\n  settings.value = {\n    ...settings.value,\n    shortcuts: {\n      enabled: settings.value.shortcuts.enabled,\n      appCommands: structuredClone(defaultSettings.shortcuts.appCommands),\n    },\n  };\n}\n\n/**\n * Checks if a widget is enabled\n */\nexport function isWidgetEnabled(widgetId: WidgetId): boolean {\n  const widget = settings.value.byWidget[widgetId];\n  return !!widget?.enabled;\n}\n"
  },
  {
    "path": "src/ui/react/settings/modules/shortcuts/infrastructure.tsx",
    "content": "import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport type { SluHotkey } from \"@seelen-ui/lib/types\";\nimport { Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { Button, Input, Switch, Tooltip } from \"antd\";\nimport { useTranslation } from \"react-i18next\";\nimport { useEffect } from \"react\";\n\nimport {\n  getHotkeysGroups,\n  getShortcutsConfig,\n  isWidgetEnabled,\n  resetShortcuts,\n  setShortcutsEnabled,\n  shortcutsError,\n  updateShortcut,\n  validateShortcuts,\n} from \"./application.ts\";\n\nimport { SettingsGroup, SettingsOption } from \"../../components/SettingsBox/index.tsx\";\n\nexport function Shortcuts() {\n  const shortcutsConfig = getShortcutsConfig();\n  const { enabled, appCommands } = shortcutsConfig;\n\n  const { t } = useTranslation();\n\n  useEffect(() => {\n    validateShortcuts(appCommands);\n  }, [appCommands]);\n\n  function onToogleShortcuts(enabled: boolean) {\n    setShortcutsEnabled(enabled);\n  }\n\n  function onShortcutChanged(id: string, keys: string[]) {\n    updateShortcut(id, keys);\n  }\n\n  function onReset() {\n    resetShortcuts();\n  }\n\n  const groups = getHotkeysGroups(appCommands);\n\n  function mapHokey(hotkey: SluHotkey) {\n    return (\n      <Shortcut\n        key={hotkey.id}\n        hotkey={hotkey}\n        onChanged={(keys) => onShortcutChanged(hotkey.id, keys)}\n      />\n    );\n  }\n\n  return (\n    <>\n      <SettingsGroup>\n        <SettingsOption\n          label={t(\"shortcuts.enable\")}\n          tip={t(\"shortcuts.enable_tooltip\")}\n          action={<Switch value={enabled} onChange={onToogleShortcuts} />}\n        />\n\n        <SettingsOption\n          label={t(\"shortcuts.reset\")}\n          action={\n            <Button onClick={onReset}>\n              <Icon iconName=\"RiResetLeftLine\" />\n            </Button>\n          }\n        />\n      </SettingsGroup>\n\n      <SettingsGroup>{groups.virtualDesktop.main.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.virtualDesktop.switch.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.virtualDesktop.move.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.virtualDesktop.send.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.windowManager.state.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.windowManager.sizing.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.windowManager.positioning.map(mapHokey)}</SettingsGroup>\n\n      <SettingsGroup>{groups.windowManager.tilingFocus.map(mapHokey)}</SettingsGroup>\n\n      {/* TODO implement live layout modification */}\n      {/* <SettingsGroup>{groups.windowManager.tilingLayout.map(mapHokey)}</SettingsGroup> */}\n\n      <SettingsGroup>{groups.weg.map(mapHokey)}</SettingsGroup>\n\n      {/* TODO implement wallpaper change shortcut */}\n      {/* <SettingsGroup>{groups.wallpaperManager.map(mapHokey)}</SettingsGroup> */}\n\n      <SettingsGroup>{groups.misc.map(mapHokey)}</SettingsGroup>\n    </>\n  );\n}\n\ninterface ShortcutProps {\n  hotkey: SluHotkey;\n  onChanged: (keys: string[]) => void;\n}\n\nfunction Shortcut({\n  hotkey: { id, action, keys, readonly, system, attached_to },\n  onChanged,\n}: ShortcutProps) {\n  const { t } = useTranslation();\n\n  const isEnabled = !attached_to || isWidgetEnabled(attached_to);\n\n  const args: Record<string, number | string> = \"index\" in action ? { 0: action.index } : {};\n  const hasError = shortcutsError.value.has(id);\n\n  function onEdit() {\n    if (readonly || system || !isEnabled) {\n      return;\n    }\n\n    invoke(SeelenCommand.RequestToUserInputShortcut, {\n      callbackEvent: \"finished\",\n    });\n\n    Widget.getCurrent().webview.once<null | string[]>(\"finished\", (e) => {\n      // Cancel if user didn't input at least 2 keys\n      if (e.payload && e.payload.length >= 2) {\n        onChanged(e.payload);\n      }\n    });\n  }\n\n  let tooltipTitle: string | undefined;\n  if (readonly || system) {\n    tooltipTitle = t(\"shortcuts.readonly_tooltip\");\n  } else if (hasError) {\n    tooltipTitle = t(\"shortcuts.duplicate_error\");\n  }\n\n  return (\n    <SettingsOption\n      disabled={!isEnabled}\n      label={t(`shortcuts.labels.${action.name}`, args)}\n      action={\n        <Tooltip title={tooltipTitle} placement=\"left\">\n          <Input\n            value={keys.join(\" + \")}\n            onClick={onEdit}\n            status={hasError ? \"error\" : undefined}\n            readOnly\n            disabled={!isEnabled}\n          />\n        </Tooltip>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n\n    <div id=\"splashscreen\">\n      <img class=\"splashscreen-image\" src=\"./splashscreen.png\" />\n    </div>\n    <style>\n      #splashscreen {\n        position: fixed;\n        top: 0;\n        left: 0;\n\n        width: 100vw;\n        height: 100vh;\n        z-index: 1000;\n\n        will-change: contents;\n\n        background-color: var(--color-gray-50);\n        transition: opacity 300ms linear;\n        opacity: 1;\n\n        &.hidden {\n          display: none;\n        }\n\n        &.vanish {\n          opacity: 0;\n        }\n\n        .splashscreen-image {\n          width: 100%;\n          height: 100%;\n          object-fit: cover;\n        }\n      }\n    </style>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/react/settings/router.tsx",
    "content": "import { Route, Routes } from \"react-router\";\n\nimport { AppsConfiguration } from \"./modules/appsConfigurations/infra/infra.tsx\";\nimport { SettingsByMonitor } from \"./modules/ByMonitor/infra/index.tsx\";\nimport { DeveloperTools } from \"./modules/developer/infra.tsx\";\nimport { Information } from \"./modules/extras/infrastructure.tsx\";\nimport { General } from \"./modules/general/infra/index.tsx\";\nimport { ResourcesView } from \"./modules/resources/infra.tsx\";\nimport { Shortcuts } from \"./modules/shortcuts/infrastructure.tsx\";\n\nimport { Layout } from \"./components/layout/index.tsx\";\nimport { RoutePath } from \"./components/navigation/routes.tsx\";\nimport { Home } from \"./modules/Home/index.tsx\";\nimport { IconPackEditorView } from \"./modules/IconPackEditor/index.tsx\";\nimport { ThemeView } from \"./modules/resources/Theme/View.tsx\";\nimport { SingleWallpaperView } from \"./modules/resources/Wallpapers/View.tsx\";\nimport { WidgetView } from \"./modules/resources/Widget/View.tsx\";\n\nexport function Routing() {\n  return (\n    <Routes>\n      <Route Component={Layout}>\n        <Route index Component={Home} />\n        <Route path={RoutePath.General} Component={General} />\n        <Route path={RoutePath.Resource + \"/*\"} Component={ResourcesView} />\n        <Route path={RoutePath.SettingsByMonitor} Component={SettingsByMonitor} />\n        <Route path={RoutePath.Shortcuts} Component={Shortcuts} />\n        <Route path={RoutePath.SettingsByApplication} Component={AppsConfiguration} />\n        <Route path={RoutePath.Extras} Component={Information} />\n        <Route path={RoutePath.DevTools} Component={DeveloperTools} />\n        <Route path={RoutePath.IconPackEditor} Component={IconPackEditorView} />\n        <Route path=\"widget\" Component={WidgetView} />\n        <Route path=\"theme\" Component={ThemeView} />\n        <Route path=\"wallpaper\" Component={SingleWallpaperView} />\n      </Route>\n    </Routes>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/settings/state/mod.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport type { IconPackId, ThemeId } from \"@seelen-ui/lib/types\";\nimport { batch, computed, effect, signal } from \"@preact/signals\";\nimport { Modal } from \"antd\";\nimport { monitors } from \"./system\";\nimport { cloneDeep } from \"lodash\";\nimport i18n from \"../i18n\";\nimport { bundledAppConfigs, iconPacks, themes } from \"./resources\";\n\nexport const settings = signal(await invoke(SeelenCommand.StateGetSettings, { path: null }));\nconst initialSettings = signal(JSON.stringify(settings.value));\nsubscribe(SeelenEvent.StateSettingsChanged, ({ payload }) => {\n  settings.value = payload;\n  initialSettings.value = JSON.stringify(payload);\n});\n\nexport const hasChanges = computed(() => initialSettings.value !== JSON.stringify(settings.value));\nexport const needRestart = signal(false);\n\nexport const appsConfig = computed(() => [...bundledAppConfigs.value, ...settings.value.byApp]);\n\nexport async function saveSettings() {\n  try {\n    initialSettings.value = JSON.stringify(settings.value);\n    await invoke(SeelenCommand.StateWriteSettings, {\n      settings: settings.value,\n    });\n  } catch (error) {\n    Modal.error({\n      title: \"Error on Save\",\n      content: String(error),\n      centered: true,\n    });\n  }\n}\n\nexport * from \"./resources\";\nexport * from \"./system\";\n\nconst defaultMonitorConfig = await invoke(SeelenCommand.StateGetDefaultMonitorSettings);\neffect(() => {\n  const sanitized = settings.peek();\n  for (const monitor of monitors.value) {\n    if (!sanitized.monitorsV3[monitor.id]) {\n      sanitized.monitorsV3[monitor.id] = cloneDeep(defaultMonitorConfig);\n    }\n  }\n  batch(() => {\n    settings.value = sanitized;\n    initialSettings.value = JSON.stringify(sanitized);\n  });\n});\n\neffect(() => {\n  i18n.changeLanguage(settings.value.language || \"en\");\n});\n\n/// ===============================================================\n/// ==========================ACTIONS==============================\n/// ===============================================================\n\nexport function restoreToLastSaved() {\n  settings.value = JSON.parse(initialSettings.value);\n}\n\nexport function setActiveIconPacks(payload: IconPackId[]) {\n  let active = new Set(payload);\n\n  for (const id of payload) {\n    if (!iconPacks.value.some((x) => x.id === id)) {\n      active.delete(id);\n    }\n  }\n\n  settings.value = {\n    ...settings.value,\n    activeIconPacks: Array.from(active),\n  };\n}\n\nexport function setActiveThemes(payload: ThemeId[]) {\n  let active = new Set(payload);\n\n  for (const id of payload) {\n    if (!themes.value.some((x) => x.id === id)) {\n      active.delete(id);\n    }\n  }\n\n  settings.value = {\n    ...settings.value,\n    activeThemes: Array.from(active),\n  };\n}\n"
  },
  {
    "path": "src/ui/react/settings/state/resources.ts",
    "content": "import { lazySignal } from \"libs/ui/react/utils/LazySignal\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\n\nexport const widgets = lazySignal(() => invoke(SeelenCommand.StateGetWidgets));\nsubscribe(SeelenEvent.StateWidgetsChanged, widgets.setByPayload);\nawait widgets.init();\n\nexport const plugins = lazySignal(() => invoke(SeelenCommand.StateGetPlugins));\nsubscribe(SeelenEvent.StatePluginsChanged, plugins.setByPayload);\nawait plugins.init();\n\nexport const themes = lazySignal(() => invoke(SeelenCommand.StateGetThemes));\nsubscribe(SeelenEvent.StateThemesChanged, themes.setByPayload);\nawait themes.init();\n\nexport const iconPacks = lazySignal(() => invoke(SeelenCommand.StateGetIconPacks));\nsubscribe(SeelenEvent.StateIconPacksChanged, iconPacks.setByPayload);\nawait iconPacks.init();\n\nexport const wallpapers = lazySignal(() => invoke(SeelenCommand.StateGetWallpapers));\nsubscribe(SeelenEvent.StateWallpapersChanged, wallpapers.setByPayload);\nawait wallpapers.init();\n\n// readonly configs\nexport const bundledAppConfigs = lazySignal(() => invoke(SeelenCommand.StateGetSettingsByApp));\nsubscribe(SeelenEvent.StateSettingsByAppChanged, bundledAppConfigs.setByPayload);\nawait bundledAppConfigs.init();\n"
  },
  {
    "path": "src/ui/react/settings/state/system.ts",
    "content": "import { lazySignal } from \"libs/ui/react/utils/LazySignal\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { signal } from \"@preact/signals\";\n\nexport const uiColors = lazySignal(() => invoke(SeelenCommand.SystemGetColors));\nsubscribe(SeelenEvent.ColorsChanged, uiColors.setByPayload);\nawait uiColors.init();\n\nexport const monitors = lazySignal(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\nawait monitors.init();\n\nexport const autostart = signal(await invoke(SeelenCommand.GetAutoStartStatus));\nexport async function setAutoStart(enabled: boolean) {\n  await invoke(SeelenCommand.SetAutoStart, { enabled });\n  autostart.value = enabled;\n}\n"
  },
  {
    "path": "src/ui/react/settings/styles/global.css",
    "content": "#root {\n  width: 100%;\n  height: 100%;\n}\n\nbody {\n  height: 100vh;\n  width: 100vw;\n  overflow: hidden;\n}\n\nhr {\n  margin: var(--spacing-2xs) 0;\n}\n\nb {\n  font-weight: 600;\n}\n\n.ant-color-picker-trigger {\n  min-width: 90px !important;\n}\n\n.ant-modal-body {\n  max-height: 65vh !important;\n  overflow-y: auto !important;\n}\n\n.ant-modal-content {\n  padding: var(--spacing-m) var(--spacing-xl) !important;\n}\n"
  },
  {
    "path": "src/ui/react/settings/styles/variables.css",
    "content": ":root {\n  /* font variables */\n  --placeholder-font-color: var(--color-gray-400);\n  --error-font-color: var(--color-red-800);\n\n  /* config variables */\n  --config-border-radius: 8px;\n\n  /* z indexs */\n  --z-header: 1;\n  --z-docs-menu: 2;\n  --z-modal: 3;\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/app.tsx",
    "content": "import { $system_colors } from \"libs/ui/react/utils/signals.ts\";\nimport { useDarkMode } from \"libs/ui/react/utils/styling.ts\";\nimport { ConfigProvider, theme } from \"antd\";\nimport { Widget } from \"@seelen-ui/lib\";\nimport { ErrorBoundary } from \"../weg/components/Error/index.tsx\";\nimport { ErrorFallback } from \"./components/Error/index.tsx\";\nimport { FancyToolbar } from \"./modules/main/Toolbar.tsx\";\nimport { useSignalEffect } from \"@preact/signals\";\nimport { $lastFocusedOnMonitor } from \"./modules/shared/state/windows.ts\";\nimport { useEffect } from \"preact/hooks\";\n\nexport function App() {\n  const isDarkMode = useDarkMode();\n\n  useEffect(() => {\n    Widget.self.ready({ show: false });\n  }, []);\n\n  useSignalEffect(() => {\n    const fullscreened = !!$lastFocusedOnMonitor.value?.isFullscreened;\n    if (fullscreened) {\n      Widget.self.hide();\n    } else {\n      Widget.self.show();\n    }\n  });\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          colorPrimary: isDarkMode ? $system_colors.value.accent_light : $system_colors.value.accent_dark,\n        },\n        components: {\n          Calendar: {\n            fullBg: \"transparent\",\n            fullPanelBg: \"transparent\",\n            itemActiveBg: \"transparent\",\n          },\n        },\n        algorithm: isDarkMode ? theme.darkAlgorithm : theme.defaultAlgorithm,\n      }}\n    >\n      <ErrorBoundary fallback={<ErrorFallback />}>\n        <FancyToolbar />\n      </ErrorBoundary>\n    </ConfigProvider>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/components/Error/index.module.css",
    "content": ".error {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  width: 100%;\n  height: var(--config-height);\n  background-color: var(--color-blue-800);\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/components/Error/index.tsx",
    "content": "import cs from \"./index.module.css\";\n\ninterface Props {\n  msg?: string;\n}\n\nexport function ErrorFallback({ msg = \"Something went wrong\" }: Props) {\n  return <div className={cs.error}>{msg}</div>;\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/index.ts",
    "content": "import \"moment/min/locales\";\nimport type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport i18n from \"i18next\";\nimport yaml from \"js-yaml\";\nimport { initReactI18next } from \"react-i18next\";\n\ni18n.use(initReactI18next).init(\n  {\n    lng: \"en\",\n    fallbackLng: \"en\",\n    interpolation: {\n      escapeValue: false,\n    },\n    resources: {},\n  },\n  undefined,\n);\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  for (const [key, value] of Object.entries(translations)) {\n    i18n.addResourceBundle(key, \"translation\", yaml.load(value.default));\n  }\n}\n\nexport default i18n;\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/af.yml",
    "content": "battery:\n  remaining: '{{0}}% oor'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Beskikbare toestelle (nie gepaar nie)\n  cancel: Kanselleer\n  connected: Gekoppelde toestelle\n  forget: Vergeet\n  lowenergy: Lae energie\n  more: Meer Bluetooth -instellings\n  not_found: Geen Bluetooth -toestelle gevind nie\n  pair: Paar\n  paired: Gestoorde toestelle\n  placeholder:\n    passphrase: Passfrase, kode of PIN\n  scanning: Skandering vir toestelle\ncontext_menu:\n  add_custom_text: Voeg pasgemaakte teksitem by\n  modules: Modules\n  remove: Verwyder die module\n  reorder_disable: Sluitbalk\n  reorder_enable: Ontsluit werkbalk\n  restore: Herstel na standaard\n  settings: Instellings\n  task_manager: Taakbestuurder\nmedia:\n  default_multimedia_volume: Multimedia volume\n  device:\n    channel:\n      system: Stelselklanke\n    comunications: Kommunikasie\n    missing: Ontbrekende toestel\n    mixer: Volume menger\n    multimedia: Multimedia\n    settings: Meer toestelinstellings\n    volume: Toestelvolume\n  input_devices: Insettoestelle\n  output_devices: Uitsettoestelle\n  players: Mediaspelers\nnotifications:\n  clear: Duidelik alles\n  empty: Geen kennisgewings nie\n  settings: Meer kennisgewingsinstellings\n  title: Kennisgewings\nplaceholder:\n  bluetooth_devices: Bluetooth & toestelle\n  ethernet_connected: toegang tot die internet\n  ethernet_disconnected: Geen internettoegang nie\n  notifications: Kennisgewings\n  settings: Vinnige instellings\n  volume: Volume\nplugged: Geprop\nsettings:\n  app_settings: Appinstellings\n  brightness: Helderheid\n  power: Mag\n  restart: Begin oor\n  title: Instellings\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/am.yml",
    "content": "battery:\n  remaining: '{{0}}% ይቀራል'\n  smart_charge: ብልጥ ክፍያ\nbluetooth:\n  available: የሚገኙ መሣሪያዎች (የተጣመሩ)\n  cancel: ይቅር\n  connected: የተገናኙ መሣሪያዎች\n  forget: ረሳ\n  lowenergy: ዝቅተኛ ኃይል\n  more: ተጨማሪ የብሉቱዝ ቅንብሮች\n  not_found: ምንም የብሉቱዝ መሣሪያዎች አልተገኙም\n  pair: ጥንድ ጥንድ\n  paired: የተቀመጡ መሣሪያዎች\n  placeholder:\n    passphrase: የይለፍ ሐረግ, ኮድ ወይም ፒን\n  scanning: ለመሣሪያዎች መቃኘት\ncontext_menu:\n  add_custom_text: ብጁ ጽሑፍን ንጥል ያክሉ\n  modules: ሞጁሎች\n  remove: ሞጁሎችን ያስወግዱ\n  reorder_disable: የመሳሪያ አሞሌ\n  reorder_enable: የመሣሪያ አሞሌን ይክፈቱ\n  restore: ወደ ነባሪነት ይመልሱ\n  settings: ቅንብሮች\n  task_manager: ተግባር አስተዳዳሪ\nmedia:\n  default_multimedia_volume: መልቲሚዲያ መጠን\n  device:\n    channel:\n      system: የስርዓት ድምጾች\n    comunications: ግንኙነቶች\n    missing: የመሳሪያ መሣሪያ\n    mixer: ጥራዝ ድብልቅ\n    multimedia: መልቲሚዲያ\n    settings: ተጨማሪ የመሣሪያ ቅንብሮች\n    volume: የመሣሪያ መጠን\n  input_devices: የግቤት መሣሪያዎች\n  output_devices: የውጤት መሣሪያዎች\n  players: የሚዲያ ተጫዋቾች\nnotifications:\n  clear: ሁሉንም አጥራ\n  empty: ማስታወቂያዎች የሉም\n  settings: ተጨማሪ የማሳወቂያዎች ቅንብሮች\n  title: ማሳወቂያዎች\nplaceholder:\n  bluetooth_devices: ብሉቱዝ እና መሣሪያዎች\n  ethernet_connected: የበይነመረብ መዳረሻ\n  ethernet_disconnected: ምንም የበይነመረብ ግንኙነት የለም\n  notifications: ማሳወቂያዎች\n  settings: ፈጣን ቅንብሮች\n  volume: ድምጽ\nplugged: ተሰክቷል\nsettings:\n  app_settings: የመተግበሪያ ቅንጅቶች\n  brightness: ብሩህነት\n  power: ኃይል\n  restart: እንደገና ጀምር\n  title: ቅንብሮች\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ar.yml",
    "content": "battery:\n  remaining: '{{0}}% متبقي'\n  smart_charge: الشحن الذكي\nbluetooth:\n  available: الأجهزة المتوفرة (غير مقترنة)\n  cancel: إلغاء\n  connected: الأجهزة المتصلة\n  forget: انسى\n  lowenergy: طاقة منخفضة\n  more: المزيد من إعدادات البلوتوث\n  not_found: لم يتم العثور على أجهزة بلوتوث\n  pair: زوج\n  paired: الأجهزة المحفوظة\n  placeholder:\n    passphrase: عبارة المرور أو الرمز أو رقم التعريف الشخصي\n  scanning: المسح الضوئي للأجهزة\ncontext_menu:\n  add_custom_text: إضافة عنصر نص مخصص\n  modules: الوحدات النمطية\n  remove: إزالة الوحدة النمطية\n  reorder_disable: قفل شريط الأدوات\n  reorder_enable: فتح شريط الأدوات\n  restore: الاستعادة إلى الوضع الافتراضي\n  settings: إعدادات\n  task_manager: مدير المهام\nmedia:\n  default_multimedia_volume: حجم الوسائط المتعددة\n  device:\n    channel:\n      system: أصوات النظام\n    comunications: مجال الاتصالات\n    missing: الجهاز المفقود\n    mixer: خلاط الصوت\n    multimedia: الوسائط المتعددة\n    settings: المزيد من إعدادات الجهاز\n    volume: حجم الجهاز\n  input_devices: أجهزة الإدخال\n  output_devices: أجهزة الإخراج\n  players: مشغلات الوسائط\nnotifications:\n  clear: مسح الكل\n  empty: لا توجد إخطارات\n  settings: المزيد من إعدادات الإشعارات\n  title: الإشعارات\nplaceholder:\n  bluetooth_devices: بلوتوث\n  ethernet_connected: خدمة الإنترنت\n  ethernet_disconnected: لا يوجد اتصال بالإنترنت\n  notifications: الإشعارات\n  settings: الإعدادات السريعة\n  volume: الصوت\nplugged: موصول\nsettings:\n  app_settings: إعدادات التطبيقات\n  brightness: السطوع\n  power: الطاقة\n  restart: إعادة التشغيل\n  title: إعدادات\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/az.yml",
    "content": "battery:\n  remaining: '{{0}}% Qalan'\n  smart_charge: Ağıllı şarj\nbluetooth:\n  available: Mövcud qurğular (qoşululmur)\n  cancel: Ləğv etmək\n  connected: Bağlı qurğular\n  forget: Unutmaq\n  lowenergy: Aşağı enerji\n  more: Daha çox bluetooth parametrləri\n  not_found: Bluetooth cihazları tapılmadı\n  pair: Cütləşdirmək\n  paired: Qurtaran qurğular\n  placeholder:\n    passphrase: Passphrase, kod və ya pin\n  scanning: Cihazlar üçün tarama\ncontext_menu:\n  add_custom_text: Xüsusi mətn elementi əlavə edin\n  modules: Modullar\n  remove: Modulu çıxarın\n  reorder_disable: Lövhə\n  reorder_enable: Toolbar kilidini açın\n  restore: Defolt olaraq bərpa edin\n  settings: Parametrlər\n  task_manager: Tapşırıq meneceri\nmedia:\n  default_multimedia_volume: Multimedia həcmi\n  device:\n    channel:\n      system: Sistem səsləri\n    comunications: Rabitə\n    missing: İtkin cihaz\n    mixer: Həcm qarışdırıcı\n    multimedia: Multimedia\n    settings: Daha çox cihaz parametrləri\n    volume: Cihaz həcmi\n  input_devices: Giriş cihazları\n  output_devices: Çıxış cihazları\n  players: Media oyunçuları\nnotifications:\n  clear: Hamısını təmizləmək\n  empty: Bildiriş yoxdur\n  settings: Daha çox bildiriş parametrləri\n  title: Bildirişlər\nplaceholder:\n  bluetooth_devices: Bluetooth və Cihazlar\n  ethernet_connected: İnternetə çıxışı\n  ethernet_disconnected: İnternetə giriş yoxdur\n  notifications: Bildirişlər\n  settings: Tez Parametrlər\n  volume: Həcm\nplugged: Qoşulub\nsettings:\n  app_settings: Tətbiq parametrləri\n  brightness: Parlaqlıq\n  power: Güc\n  restart: Yenidən başlamaq\n  title: Parametrlər\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/bg.yml",
    "content": "battery:\n  remaining: '{{0}}% Остават'\n  smart_charge: Интелигентно зареждане\nbluetooth:\n  available: Налични устройства (не са сдвоени)\n  cancel: Отмяна на\n  connected: Свързани устройства\n  forget: Забравете\n  lowenergy: Ниска енергия\n  more: Още настройки на Bluetooth\n  not_found: Не са намерени Bluetooth устройства\n  pair: Двойка\n  paired: Запазени устройства\n  placeholder:\n    passphrase: Парола, код или ПИН код\n  scanning: Сканиране за устройства\ncontext_menu:\n  add_custom_text: Добавяне на потребителски текстов елемент\n  modules: Модули\n  remove: Премахване на модула\n  reorder_disable: Заключване на лентата с инструменти\n  reorder_enable: Отключване на лентата с инструменти\n  restore: Възстановяване на настройките по подразбиране\n  settings: Настройки\n  task_manager: Диспечер на задачите\nmedia:\n  default_multimedia_volume: Обем на мултимедията\n  device:\n    channel:\n      system: Системни звуци\n    comunications: Комуникации\n    missing: Липсващо устройство\n    mixer: Смесител за обем\n    multimedia: Мултимедия\n    settings: Още настройки на устройството\n    volume: Обем на устройството\n  input_devices: Входни устройства\n  output_devices: Изходни устройства\n  players: Медийни играчи\nnotifications:\n  clear: Изчистване на всички\n  empty: Няма известия\n  settings: Още настройки за известия\n  title: Известия\nplaceholder:\n  bluetooth_devices: Bluetooth & устройства\n  ethernet_connected: достъп до интернет\n  ethernet_disconnected: Няма достъп до интернет\n  notifications: Известия\n  settings: Бързи настройки\n  volume: Сила на звука\nplugged: Запушен\nsettings:\n  app_settings: Настройки на приложението\n  brightness: Яркост\n  power: Мощност\n  restart: Рестартирам\n  title: Настройки\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/bn.yml",
    "content": "battery:\n  remaining: '{{0}}% বাকি'\n  smart_charge: স্মার্ট চার্জ\nbluetooth:\n  available: উপলব্ধ ডিভাইস (জোড় নয়)\n  cancel: বাতিল\n  connected: সংযুক্ত ডিভাইস\n  forget: ভুলে যাও\n  lowenergy: কম শক্তি\n  more: আরও ব্লুটুথ সেটিংস\n  not_found: কোনও ব্লুটুথ ডিভাইস পাওয়া যায় নি\n  pair: জুটি\n  paired: সংরক্ষণ করা ডিভাইস\n  placeholder:\n    passphrase: পাসফ্রেজ, কোড বা পিন\n  scanning: ডিভাইসগুলির জন্য স্ক্যানিং\ncontext_menu:\n  add_custom_text: কাস্টম পাঠ্য আইটেম যুক্ত করুন\n  modules: মডিউল\n  remove: মডিউল সরান\n  reorder_disable: লক টুলবার\n  reorder_enable: সরঞ্জামদণ্ড আনলক করুন\n  restore: ডিফল্ট পুনরুদ্ধার\n  settings: সেটিংস\n  task_manager: টাস্ক ম্যানেজার\nmedia:\n  default_multimedia_volume: মাল্টিমিডিয়া ভলিউম\n  device:\n    channel:\n      system: সিস্টেম শব্দ\n    comunications: যোগাযোগ\n    missing: অনুপস্থিত ডিভাইস\n    mixer: ভলিউম মিক্সার\n    multimedia: মাল্টিমিডিয়া\n    settings: আরও ডিভাইস সেটিংস\n    volume: ডিভাইস ভলিউম\n  input_devices: ইনপুট ডিভাইস\n  output_devices: আউটপুট ডিভাইস\n  players: মিডিয়া প্লেয়ার\nnotifications:\n  clear: সব পরিষ্কার\n  empty: কোন বিজ্ঞপ্তি\n  settings: আরও বিজ্ঞপ্তি সেটিংস\n  title: বিজ্ঞপ্তি\nplaceholder:\n  bluetooth_devices: ব্লুটুথ এবং ডিভাইস\n  ethernet_connected: ইন্টারনেট সুবিধা\n  ethernet_disconnected: কোনও ইন্টারনেট অ্যাক্সেস নেই\n  notifications: বিজ্ঞপ্তি\n  settings: দ্রুত সেটিংস\n  volume: ভলিউম\nplugged: প্লাগ করা হয়েছে\nsettings:\n  app_settings: অ্যাপ সেটিংস\n  brightness: উজ্জ্বলতা\n  power: শক্তি\n  restart: আবার শুরু\n  title: সেটিংস\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/bs.yml",
    "content": "battery:\n  remaining: '{{0}}% preostalo'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Dostupni uređaji (nisu upareni)\n  cancel: Otkazati\n  connected: Povezani uređaji\n  forget: Zaboraviti\n  lowenergy: Niska energija\n  more: Više Bluetooth postavki\n  not_found: Nisu pronađeni Bluetooth uređaji\n  pair: Par\n  paired: Spremljeni uređaji\n  placeholder:\n    passphrase: Passphrase, kod ili pin\n  scanning: Skeniranje za uređaje\ncontext_menu:\n  add_custom_text: Dodajte prilagođeni tekst\n  modules: Moduli\n  remove: Ukloni modul\n  reorder_disable: Zaključana alatna traka\n  reorder_enable: Otključajte alatnu traku\n  restore: Vratite do zadane vrijednosti\n  settings: Postavke\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: MULTIMEDIA VOLUME\n  device:\n    channel:\n      system: Sistem zvuči\n    comunications: Komunikacije\n    missing: Uređaj koji nedostaje\n    mixer: Mikser za jačinu zvuka\n    multimedia: Multimedija\n    settings: Više postavki uređaja\n    volume: Volumen uređaja\n  input_devices: Uređaji za unos\n  output_devices: Izlazni uređaji\n  players: Medijski igrači\nnotifications:\n  clear: Očistiti sve\n  empty: Nema obaveštenja\n  settings: Više obavijesti Postavke\n  title: Obavijesti\nplaceholder:\n  bluetooth_devices: Bluetooth i uređaji\n  ethernet_connected: pristup Internetu\n  ethernet_disconnected: Nema pristupa internetu\n  notifications: Obavijesti\n  settings: Brze postavke\n  volume: Zapremina\nplugged: Plugged\nsettings:\n  app_settings: Postavke aplikacija\n  brightness: Svjetlost\n  power: Snaga\n  restart: Ponovo pokrenuti\n  title: Postavke\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ca.yml",
    "content": "battery:\n  remaining: '{{0}}% restant'\n  smart_charge: Càrrega intel·ligent\nbluetooth:\n  available: Dispositius disponibles (no emparellats)\n  cancel: Cancel·lar\n  connected: Dispositius connectats\n  forget: Oblidar\n  lowenergy: Baixa energia\n  more: Més configuració Bluetooth\n  not_found: No es troben dispositius Bluetooth\n  pair: Parella\n  paired: Dispositius desats\n  placeholder:\n    passphrase: Passphrase, codi o pin\n  scanning: Escaneig per a dispositius\ncontext_menu:\n  add_custom_text: Afegiu un element de text personalitzat\n  modules: Mòduls\n  remove: Elimina el mòdul\n  reorder_disable: Barra d’eines de bloqueig\n  reorder_enable: Desbloqueja la barra d’eines\n  restore: Restaureu a Default\n  settings: Configuració\n  task_manager: Gestor de tasques\nmedia:\n  default_multimedia_volume: Volum multimèdia\n  device:\n    channel:\n      system: Sona del sistema\n    comunications: Comunicacions\n    missing: Falta el dispositiu\n    mixer: Mesclador de volum\n    multimedia: Multimèdia\n    settings: Més configuració del dispositiu\n    volume: Volum del dispositiu\n  input_devices: Dispositius d'entrada\n  output_devices: Dispositius de sortida\n  players: Reproductors de mitjans de comunicació\nnotifications:\n  clear: Esborrar tot\n  empty: Sense notificacions\n  settings: Més configuració de notificacions\n  title: Notificacions\nplaceholder:\n  bluetooth_devices: Bluetooth i dispositius\n  ethernet_connected: accés a Internet\n  ethernet_disconnected: Sense accés a Internet\n  notifications: Notificacions\n  settings: Configuració ràpida\n  volume: Volum\nplugged: Endollat\nsettings:\n  app_settings: Configuració de l'aplicació\n  brightness: Brillantor\n  power: Força\n  restart: Reinicia\n  title: Configuració\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/cs.yml",
    "content": "battery:\n  remaining: Zbývá {{0}} %.\n  smart_charge: Chytré nabíjení\nbluetooth:\n  available: Dostupná zařízení (nespárovaná)\n  cancel: Zrušit\n  connected: Připojená zařízení\n  forget: Zapomenout\n  lowenergy: Nízká spotřeba energie\n  more: Další nastavení Bluetooth\n  not_found: Nebyla nalezena žádná zařízení Bluetooth\n  pair: Párovat\n  paired: Uložená zařízení\n  placeholder:\n    passphrase: Heslo, kód nebo PIN\n  scanning: Skenování zařízení\ncontext_menu:\n  add_custom_text: Přidání vlastní textové položky\n  modules: Moduly\n  remove: Odebrat modul\n  reorder_disable: Zamknout panel nástrojů\n  reorder_enable: Odemknout panel nástrojů\n  restore: Obnovení výchozího nastavení\n  settings: Nastavení\n  task_manager: Správce úloh\nmedia:\n  default_multimedia_volume: Multimediální hlasitost\n  device:\n    channel:\n      system: Zvuky systému\n    comunications: Komunikace\n    missing: Chybějící zařízení\n    mixer: Směšovač hlasitosti\n    multimedia: Multimédia\n    settings: Další nastavení zařízení\n    volume: Hlasitost zařízení\n  input_devices: Vstupní zařízení\n  output_devices: Výstupní zařízení\n  players: Přehrávače médií\nnotifications:\n  clear: Vymazat vše\n  empty: Žádné oznámení\n  settings: Další nastavení oznámení\n  title: Oznámení\nplaceholder:\n  bluetooth_devices: Bluetooth & zařízení\n  ethernet_connected: Přístup na internet\n  ethernet_disconnected: Žádný přístup na internet\n  notifications: Oznámení\n  settings: Rychlé nastavení\n  volume: Hlasitost\nplugged: Zapojené\nsettings:\n  app_settings: Nastavení aplikace\n  brightness: Jas\n  power: Napájení\n  restart: Restart\n  title: Nastavení\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/cy.yml",
    "content": "battery:\n  remaining: '{{0}}% Ar ôl'\n  smart_charge: Tâl Smart\nbluetooth:\n  available: Dyfeisiau sydd ar gael (heb baru)\n  cancel: Chansliff\n  connected: Dyfeisiau Cysylltiedig\n  forget: Hanghofion\n  lowenergy: Egni isel\n  more: Mwy o osodiadau bluetooth\n  not_found: Ni ddarganfuwyd unrhyw ddyfeisiau Bluetooth\n  pair: Phâr\n  paired: Dyfeisiau a arbedwyd\n  placeholder:\n    passphrase: Passphrase, cod neu pin\n  scanning: Sganio am ddyfeisiau\ncontext_menu:\n  add_custom_text: Ychwanegu Eitem Testun Custom\n  modules: Modiwlau\n  remove: Tynnwch y modiwl\n  reorder_disable: Bar offer cloi\n  reorder_enable: Datgloi Bar Offer\n  restore: Adfer i Ddiffyg\n  settings: Gosodiadau\n  task_manager: Rheolwr Tasg\nmedia:\n  default_multimedia_volume: Cyfrol amlgyfrwng\n  device:\n    channel:\n      system: SYLFAEN SYSTEM\n    comunications: Gyfathrebiadau\n    missing: Dyfais ar goll\n    mixer: Cymysgydd Cyfrol\n    multimedia: Amlgyfrwng\n    settings: Mwy o osodiadau dyfeisiau\n    volume: Cyfaint dyfeisiau\n  input_devices: Dyfeisiau Mewnbwn\n  output_devices: Dyfeisiau Allbwn\n  players: Chwaraewyr cyfryngau\nnotifications:\n  clear: Clirio popeth\n  empty: Dim Hysbysiadau\n  settings: Gosodiadau Mwy o Hysbysiadau\n  title: Hysbysiadau\nplaceholder:\n  bluetooth_devices: Bluetooth a Dyfeisiau\n  ethernet_connected: Mynediad i'r Rhyngrwyd\n  ethernet_disconnected: Dim Mynediad i'r Rhyngrwyd\n  notifications: Hysbysiadau\n  settings: Gosodiadau Cyflym\n  volume: Nghyfrol\nplugged: Wedi'i blygio\nsettings:\n  app_settings: Gosodiadau App\n  brightness: Disgleirdeb\n  power: Bwerau\n  restart: Ail-ddechrau\n  title: Gosodiadau\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/da.yml",
    "content": "battery:\n  remaining: '{{0}} % tilbage'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Tilgængelige enheder (ikke parret)\n  cancel: Annuller\n  connected: Forbundne enheder\n  forget: Glemme\n  lowenergy: Lav energi\n  more: Flere Bluetooth-indstillinger\n  not_found: Ingen Bluetooth-enheder fundet\n  pair: Par\n  paired: Gemte enheder\n  placeholder:\n    passphrase: Adgangssætning, kode eller PIN-kode\n  scanning: Scanning efter enheder\ncontext_menu:\n  add_custom_text: Tilføj brugerdefineret tekstelement\n  modules: Moduler\n  remove: Fjern modulet\n  reorder_disable: Lås værktøjslinjen\n  reorder_enable: Lås værktøjslinjen op\n  restore: Gendan til standard\n  settings: Indstillinger\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: Multimedievolumen\n  device:\n    channel:\n      system: Systemets lyde\n    comunications: Kommunikation\n    missing: Manglende enhed\n    mixer: Volumenmixer\n    multimedia: Multimedia\n    settings: Flere enhedsindstillinger\n    volume: Enhedens volumen\n  input_devices: Input-enheder\n  output_devices: Output-enheder\n  players: Medieafspillere\nnotifications:\n  clear: Ryd alle\n  empty: Ingen meddelelser\n  settings: Flere indstillinger for notifikationer\n  title: Meddelelser\nplaceholder:\n  bluetooth_devices: Bluetooth & enheder\n  ethernet_connected: Internetadgang\n  ethernet_disconnected: Ingen internetadgang\n  notifications: Underretninger\n  settings: Hurtige indstillinger\n  volume: Bind\nplugged: Tilsluttet\nsettings:\n  app_settings: Appindstillinger\n  brightness: Lysstyrke\n  power: Magt\n  restart: Genstart\n  title: Indstillinger\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/de.yml",
    "content": "battery:\n  remaining: \"{{0}}\\_% verbleibend\"\n  smart_charge: Intelligentes Laden\nbluetooth:\n  available: Verfügbare Geräte (nicht gepaart)\n  cancel: Abbrechen\n  connected: Vernetzte Geräte\n  forget: Vergessen Sie\n  lowenergy: Niedrige Energie\n  more: Weitere Bluetooth-Einstellungen\n  not_found: Keine Bluetooth-Geräte gefunden\n  pair: Paar\n  paired: Gespeicherte Geräte\n  placeholder:\n    passphrase: Passphrase, Code oder PIN\n  scanning: Scannen nach Geräten\ncontext_menu:\n  add_custom_text: Benutzerdefiniertes Textelement hinzufügen\n  modules: Module\n  remove: Modul entfernen\n  reorder_disable: Symbolleiste sperren\n  reorder_enable: Symbolleiste entsperren\n  restore: Wiederherstellen auf Standard\n  settings: Einstellungen\n  task_manager: Task-Manager\nmedia:\n  default_multimedia_volume: Multimedia-Volumen\n  device:\n    channel:\n      system: Systemtöne\n    comunications: Kommunikation\n    missing: Fehlendes Gerät\n    mixer: Lautstärkemischer\n    multimedia: Multimedia\n    settings: Weitere Geräteeinstellungen\n    volume: Gerät Volumen\n  input_devices: Eingabegeräte\n  output_devices: Ausgabegeräte\n  players: Medienplayer\nnotifications:\n  clear: Alle löschen\n  empty: Keine Benachrichtigungen\n  settings: Weitere Einstellungen für Benachrichtigungen\n  title: Benachrichtigungen\nplaceholder:\n  bluetooth_devices: Bluetooth & Geräte\n  ethernet_connected: Internetzugang\n  ethernet_disconnected: Kein Internetzugang\n  notifications: Benachrichtigungen\n  settings: Schnelleinstellungen\n  volume: Lautstärke\nplugged: Eingesteckt\nsettings:\n  app_settings: App-Einstellungen\n  brightness: Helligkeit\n  power: Leistung\n  restart: Neustarten\n  title: Einstellungen\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/el.yml",
    "content": "battery:\n  remaining: '{{0}}% που απομένει'\n  smart_charge: Έξυπνη φόρτιση\nbluetooth:\n  available: Διαθέσιμες συσκευές (χωρίς ζεύξη)\n  cancel: Ακύρωση\n  connected: Συνδεδεμένες συσκευές\n  forget: Ξεχάστε το\n  lowenergy: Χαμηλή ενέργεια\n  more: Περισσότερες ρυθμίσεις Bluetooth\n  not_found: Δεν βρέθηκαν συσκευές Bluetooth\n  pair: Ζευγάρι\n  paired: Αποθηκευμένες συσκευές\n  placeholder:\n    passphrase: Συνθηματική φράση, κωδικός ή PIN\n  scanning: Σάρωση για συσκευές\ncontext_menu:\n  add_custom_text: Προσθήκη προσαρμοσμένου στοιχείου κειμένου\n  modules: Ενότητες\n  remove: Αφαιρέστε την ενότητα\n  reorder_disable: Κλείδωμα γραμμής εργαλείων\n  reorder_enable: Ξεκλείδωμα γραμμής εργαλείων\n  restore: Επαναφορά στην προεπιλογή\n  settings: Ρυθμίσεις\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: Όγκος πολυμέσων\n  device:\n    channel:\n      system: Ήχοι συστήματος\n    comunications: Διαβιβάσεις\n    missing: Λείπει η συσκευή\n    mixer: Μίξερ έντασης ήχου\n    multimedia: ΠΟΛΥΜΕΣΑ\n    settings: Περισσότερες ρυθμίσεις συσκευής\n    volume: Όγκος συσκευής\n  input_devices: Συσκευές εισόδου\n  output_devices: Συσκευές εξόδου\n  players: Media players\nnotifications:\n  clear: Εκκαθάριση όλων\n  empty: Δεν υπάρχουν ειδοποιήσεις\n  settings: Περισσότερες ρυθμίσεις ειδοποιήσεων\n  title: Ειδοποιήσεις\nplaceholder:\n  bluetooth_devices: Bluetooth & συσκευές\n  ethernet_connected: πρόσβαση στο διαδίκτυο\n  ethernet_disconnected: Χωρίς πρόσβαση στο Διαδίκτυο\n  notifications: Ειδοποιήσεις\n  settings: Γρήγορες ρυθμίσεις\n  volume: Ενταση ΗΧΟΥ\nplugged: Συνδεδεμένο\nsettings:\n  app_settings: Ρυθμίσεις εφαρμογής\n  brightness: Φωτεινότητα\n  power: Εξουσία\n  restart: Επανεκκίνηση\n  title: Ρυθμίσεις\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/en.yml",
    "content": "battery:\n  remaining: '{{0}}% Remaining'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Available Devices (Not Paired)\n  cancel: Cancel\n  connected: Connected Devices\n  forget: Forget\n  lowenergy: Low Energy\n  more: More Bluetooth Settings\n  not_found: No Bluetooth devices found\n  pair: Pair\n  paired: Saved Devices\n  placeholder:\n    passphrase: Passphrase, Code or PIN\n  scanning: Scanning for devices\ncontext_menu:\n  add_custom_text: Add custom text item\n  modules: Modules\n  remove: Remove Module\n  reorder_disable: Lock toolbar\n  reorder_enable: Unlock toolbar\n  restore: Restore to Default\n  settings: Settings\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: Multimedia Volume\n  device:\n    channel:\n      system: System sounds\n    comunications: Communications\n    missing: Missing Device\n    mixer: Volume Mixer\n    multimedia: Multimedia\n    settings: More Device Settings\n    volume: Device Volume\n  input_devices: Input devices\n  output_devices: Output devices\n  players: Media Players\nnotifications:\n  clear: Clear all\n  empty: No notifications\n  settings: More Notifications Settings\n  title: Notifications\nplaceholder:\n  bluetooth_devices: Bluetooth & Devices\n  ethernet_connected: Internet access\n  ethernet_disconnected: No internet access\n  notifications: Notifications\n  settings: Quick Settings\n  volume: Volume\nplugged: Plugged\nsettings:\n  app_settings: App Settings\n  brightness: Brightness\n  power: Power\n  restart: Restart\n  title: Settings\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/es.yml",
    "content": "battery:\n  remaining: '{{0}}% restante'\n  smart_charge: Carga inteligente\nbluetooth:\n  available: Dispositivos disponibles (no emparejados)\n  cancel: Cancelar\n  connected: Dispositivos conectados\n  forget: Olvídese de\n  lowenergy: Baja energía\n  more: Más ajustes de Bluetooth\n  not_found: No se han encontrado dispositivos Bluetooth\n  pair: Par\n  paired: Dispositivos guardados\n  placeholder:\n    passphrase: Frase de acceso, código o PIN\n  scanning: Búsqueda de dispositivos\ncontext_menu:\n  add_custom_text: Añadir texto personalizado\n  modules: Módulos\n  remove: Eliminar el módulo\n  reorder_disable: Bloquear barra de herramientas\n  reorder_enable: Desbloquear la barra de herramientas\n  restore: Restablecer valores por defecto\n  settings: Ajustes\n  task_manager: Administrador de tareas\nmedia:\n  default_multimedia_volume: Volumen multimedia\n  device:\n    channel:\n      system: Sonidos del sistema\n    comunications: Comunicaciones\n    missing: Dispositivo ausente\n    mixer: Mezclador de volumen\n    multimedia: Multimedia\n    settings: Más ajustes del dispositivo\n    volume: Volumen del dispositivo\n  input_devices: Dispositivos de entrada\n  output_devices: Dispositivos de salida\n  players: Reproductores de medios\nnotifications:\n  clear: Borrar todo\n  empty: Sin notificaciones\n  settings: Más ajustes de notificaciones\n  title: Notificaciones\nplaceholder:\n  bluetooth_devices: Bluetooth y dispositivos\n  ethernet_connected: Acceso a internet\n  ethernet_disconnected: Sin acceso a internet\n  notifications: Notificaciones\n  settings: Configuraciones rápidas\n  volume: Volumen\nplugged: Atascado\nsettings:\n  app_settings: Configuraciones de la aplicación\n  brightness: Luminosidad\n  power: Fuerza\n  restart: Reiniciar\n  title: Configuraciones\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/et.yml",
    "content": "battery:\n  remaining: '{{0}}% järelejäänud'\n  smart_charge: Nutikas laadimine\nbluetooth:\n  available: Saadaval olevad seadmed (ei ole ühendatud)\n  cancel: Tühista\n  connected: Ühendatud seadmed\n  forget: Unustage\n  lowenergy: Madal energia\n  more: Rohkem Bluetooth-seadistusi\n  not_found: Bluetooth-seadmeid ei leitud\n  pair: Paar\n  paired: Salvestatud seadmed\n  placeholder:\n    passphrase: Salasõna, kood või PIN-kood\n  scanning: Seadmete skaneerimine\ncontext_menu:\n  add_custom_text: Lisa kohandatud tekstielement\n  modules: Moodulid\n  remove: Eemaldage moodul\n  reorder_disable: Tööriistariba lukustamine\n  reorder_enable: Tööriistariba avamine\n  restore: Taastada vaikimisi\n  settings: Seaded\n  task_manager: Tegumihaldur\nmedia:\n  default_multimedia_volume: Multimeedia maht\n  device:\n    channel:\n      system: Süsteemi helid\n    comunications: Suhtlus\n    missing: Puuduv seade\n    mixer: Helitugevuse segisti\n    multimedia: Multimeedia\n    settings: Rohkem seadme seaded\n    volume: Seadme maht\n  input_devices: Sisendseadmed\n  output_devices: Väljundseadmed\n  players: Meediamängijad\nnotifications:\n  clear: Tühjenda kõik\n  empty: Teated puuduvad\n  settings: Rohkem teavituste seadistusi\n  title: Teated\nplaceholder:\n  bluetooth_devices: Bluetooth ja seadmed\n  ethernet_connected: internetiühendus\n  ethernet_disconnected: Interneti -ühendus pole\n  notifications: Teatised\n  settings: Kiired seaded\n  volume: Maht\nplugged: Pistikuga ühendatud\nsettings:\n  app_settings: Rakenduse sätted\n  brightness: Heledus\n  power: Võimsus\n  restart: Taaskäivita\n  title: Sätted\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/eu.yml",
    "content": "battery:\n  remaining: '{{0}}% geratzen da'\n  smart_charge: Karga adimenduna\nbluetooth:\n  available: Gailu erabilgarriak (ez da parekatuta)\n  cancel: Indargabetu\n  connected: Konektatutako gailuak\n  forget: Ahaztu\n  lowenergy: Energia baxua\n  more: Bluetooth ezarpen gehiago\n  not_found: Ez da Bluetooth gailurik aurkitu\n  pair: Pare\n  paired: Gorde diren gailuak\n  placeholder:\n    passphrase: Pasahitza, kodea edo PINa\n  scanning: Gailuetarako eskaneatzea\ncontext_menu:\n  add_custom_text: Gehitu testu-elementu pertsonalizatua\n  modules: Modulu\n  remove: Kendu modulua\n  reorder_disable: Blokeatu tresna barra\n  reorder_enable: Desblokeatu tresna barra\n  restore: Lehenetsitako lehenetsia\n  settings: Ezarpenak\n  task_manager: Zereginen kudeatzailea\nmedia:\n  default_multimedia_volume: Multimedia bolumena\n  device:\n    channel:\n      system: Sistema soinuak\n    comunications: Komunikazioak\n    missing: Gailua falta da\n    mixer: Bolumen-nahastea\n    multimedia: Multimedia\n    settings: Gailuaren ezarpen gehiago\n    volume: Gailuaren bolumena\n  input_devices: Sarrerako gailuak\n  output_devices: Irteerako gailuak\n  players: Komunikabideetako jokalariak\nnotifications:\n  clear: Garbitu guztiak\n  empty: Ez da jakinarazpenik\n  settings: Jakinarazpen gehiago ezarpenak\n  title: Jakinarazpenak\nplaceholder:\n  bluetooth_devices: Bluetooth eta gailuak\n  ethernet_connected: Interneterako sarbidea\n  ethernet_disconnected: Ez da Interneteko sarbiderik\n  notifications: Jakinarazpenak\n  settings: Ezarpen azkarrak\n  volume: Ozentasun\nplugged: Entxufatuta\nsettings:\n  app_settings: Aplikazioaren ezarpenak\n  brightness: Distira\n  power: Botere\n  restart: Berriro hasi\n  title: Ezarpen\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/fa.yml",
    "content": "battery:\n  remaining: '{{0}}% باقی مانده است'\n  smart_charge: شارژ هوشمند\nbluetooth:\n  available: دستگاه های موجود (جفت نشده)\n  cancel: لغو کردن\n  connected: دستگاه های متصل\n  forget: فراموش کردن\n  lowenergy: کم انرژی\n  more: تنظیمات بلوتوث بیشتر\n  not_found: هیچ دستگاه بلوتوث یافت نشده است\n  pair: جفت\n  paired: دستگاه های ذخیره شده\n  placeholder:\n    passphrase: عارضه ، کد یا پین\n  scanning: اسکن برای دستگاه ها\ncontext_menu:\n  add_custom_text: مورد متن سفارشی را اضافه کنید\n  modules: ماژول\n  remove: ماژول را حذف کنید\n  reorder_disable: نوار ابزار قفل\n  reorder_enable: نوار ابزار را باز کنید\n  restore: بازگرداندن به طور پیش فرض\n  settings: تنظیمات\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: حجم چند رسانه ای\n  device:\n    channel:\n      system: صداهای سیستم\n    comunications: ارتباطات\n    missing: دستگاه گمشده\n    mixer: میکسر حجم\n    multimedia: چند رسانه ای\n    settings: تنظیمات بیشتر دستگاه\n    volume: حجم دستگاه\n  input_devices: دستگاه های ورودی\n  output_devices: دستگاه های خروجی\n  players: بازیکنان رسانه ای\nnotifications:\n  clear: همه را پاک کنید\n  empty: بدون اعلان\n  settings: تنظیمات اعلان بیشتر\n  title: اعلان ها\nplaceholder:\n  bluetooth_devices: بلوتوث و دستگاه\n  ethernet_connected: دسترسی به اینترنت\n  ethernet_disconnected: بدون دسترسی به اینترنت\n  notifications: اطلاعیه\n  settings: تنظیمات سریع\n  volume: جلد\nplugged: وصل شده است\nsettings:\n  app_settings: تنظیمات برنامه\n  brightness: روشنایی\n  power: قدرت\n  restart: مجدداً\n  title: تنظیمات\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/fi.yml",
    "content": "battery:\n  remaining: '{{0}} % jäljellä'\n  smart_charge: Älykäs lataus\nbluetooth:\n  available: Käytettävissä olevat laitteet (ei paritettu)\n  cancel: Peruuta\n  connected: Liitetyt laitteet\n  forget: Unohda\n  lowenergy: Matalaenergia\n  more: Lisää Bluetooth-asetuksia\n  not_found: Bluetooth-laitteita ei löytynyt\n  pair: Pari\n  paired: Tallennetut laitteet\n  placeholder:\n    passphrase: Salasana, koodi tai PIN-koodi\n  scanning: Laitteiden skannaus\ncontext_menu:\n  add_custom_text: Lisää mukautettu tekstikohta\n  modules: Moduulit\n  remove: Poista moduuli\n  reorder_disable: Lukitse työkalurivi\n  reorder_enable: Avaa työkalurivi\n  restore: Palauta oletusasetukset\n  settings: Asetukset\n  task_manager: Tehtävienhallinta\nmedia:\n  default_multimedia_volume: Multimedian äänenvoimakkuus\n  device:\n    channel:\n      system: Järjestelmän äänet\n    comunications: Viitaus\n    missing: Puuttuva laite\n    mixer: Äänenvoimakkuuden sekoitin\n    multimedia: Multimedia\n    settings: Lisää laiteasetuksia\n    volume: Laitteen tilavuus\n  input_devices: Syöttölaitteet\n  output_devices: Lähtölaitteet\n  players: Mediapelaajat\nnotifications:\n  clear: Tyhjennä kaikki\n  empty: Ei ilmoituksia\n  settings: Lisää ilmoitusten asetuksia\n  title: Ilmoitukset\nplaceholder:\n  bluetooth_devices: Bluetooth ja laitteet\n  ethernet_connected: Internet-yhteys\n  ethernet_disconnected: Ei Internet-yhteyttä\n  notifications: Ilmoitukset\n  settings: Pika -asetukset\n  volume: Tilavuus\nplugged: Kytketty\nsettings:\n  app_settings: Sovellusasetukset\n  brightness: Kirkkaus\n  power: Voima\n  restart: Uudelleenkäynnistää\n  title: asetukset\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/fr.yml",
    "content": "battery:\n  remaining: \"{{0}}\\_% restants\"\n  smart_charge: Chargement intelligent\nbluetooth:\n  available: Appareils disponibles (non appairés)\n  cancel: Annuler\n  connected: Appareils connectés\n  forget: Oublier\n  lowenergy: Faible énergie\n  more: Autres paramètres Bluetooth\n  not_found: Aucun appareil Bluetooth n'a été trouvé\n  pair: Appairer\n  paired: Appareils sauvegardés\n  placeholder:\n    passphrase: Passphrase, code ou PIN\n  scanning: Recherche d'appareils\ncontext_menu:\n  add_custom_text: Ajouter un texte personnalisé\n  modules: Modules\n  remove: Retirer le module\n  reorder_disable: Verrouiller la barre d'outils\n  reorder_enable: Déverrouiller la barre d'outils\n  restore: Rétablir la valeur par défaut\n  settings: Paramètres\n  task_manager: Gestionnaire des tâches\nmedia:\n  default_multimedia_volume: Volume multimédia\n  device:\n    channel:\n      system: Sons du système\n    comunications: Communications\n    missing: Dispositif manquant\n    mixer: Mélangeur de volume\n    multimedia: Multimédia\n    settings: Autres paramètres de l'appareil\n    volume: Volume de l'appareil\n  input_devices: Dispositifs d'entrée\n  output_devices: Dispositifs de sortie\n  players: Lecteurs multimédias\nnotifications:\n  clear: Tout effacer\n  empty: Aucune notification\n  settings: Autres paramètres de notification\n  title: Notifications\nplaceholder:\n  bluetooth_devices: Bluetooth\n  ethernet_connected: Accès Internet\n  ethernet_disconnected: Pas d'accès Internet\n  notifications: Notifications\n  settings: Réglages rapides\n  volume: Volume\nplugged: Branché\nsettings:\n  app_settings: Paramètres de l'application\n  brightness: Luminosité\n  power: Marche/Arrêt\n  restart: Redémarrer\n  title: Paramètres\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/gu.yml",
    "content": "battery:\n  remaining: '{{0}}% બાકી'\n  smart_charge: સ્માર્ટ ચાર્જ\nbluetooth:\n  available: ઉપલબ્ધ ઉપકરણો (જોડી નથી)\n  cancel: રદ કરવું\n  connected: સંલગ્ન ઉપકરણો\n  forget: ભૂલવું\n  lowenergy: ઓછી energyર્જા\n  more: વધુ બ્લૂટૂથ સેટિંગ્સ\n  not_found: કોઈ બ્લૂટૂથ ઉપકરણો મળ્યાં નથી\n  pair: જોડી\n  paired: બચાવેલા ઉપકરણો\n  placeholder:\n    passphrase: પાસફ્રેઝ, કોડ અથવા પિન\n  scanning: ઉપકરણો માટે સ્કેન કરવું\ncontext_menu:\n  add_custom_text: કસ્ટમ ટેક્સ્ટ આઇટમ ઉમેરો\n  modules: અંતલો\n  remove: મોડ્યુલ દૂર કરો\n  reorder_disable: તલવાર\n  reorder_enable: ટૂલબારને અનલ lock ક કરો\n  restore: ડિફ default લ્ટ પર પુન restore સ્થાપિત કરો\n  settings: સેટિંગ્સ\n  task_manager: ટાસ્ક મેનેજર\nmedia:\n  default_multimedia_volume: બહુમાળ વોલ્યુમ\n  device:\n    channel:\n      system: સિસ્ટમ અવાજો\n    comunications: સંચાર\n    missing: ખૂટેલું ઉપકરણ\n    mixer: વોલ્યુમ મિક્સર\n    multimedia: બહુમાળી\n    settings: વધુ ઉપકરણ સેટિંગ્સ\n    volume: ઉપકરણનું પ્રમાણ\n  input_devices: ઇનપુટ ઉપકરણો\n  output_devices: ઉત્પાદન\n  players: માધ્યમો ખેલાડીઓ\nnotifications:\n  clear: બધુ સાફ કરવું\n  empty: કોઈ સૂચનાઓ\n  settings: વધુ સૂચનાઓ સેટિંગ્સ\n  title: સૂચના\nplaceholder:\n  bluetooth_devices: બ્લૂટૂથ અને ઉપકરણો\n  ethernet_connected: ઇન્ટરનેટનો ઉપયોગ\n  ethernet_disconnected: કોઈ ઇન્ટરનેટ એક્સેસ નથી\n  notifications: સૂચના\n  settings: ઝડપી સેટિંગ્સ\n  volume: જથ્થો\nplugged: પ્લગ કરેલ\nsettings:\n  app_settings: એપ્લિકેશન સેટિંગ્સ\n  brightness: ઉદ્ધતાઈ\n  power: શક્તિ\n  restart: પુનઃપ્રારંભ\n  title: પતાવટ\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/he.yml",
    "content": "battery:\n  remaining: '{{0}}% נותרו'\n  smart_charge: טעינה חכמה\nbluetooth:\n  available: מכשירים זמינים\n  cancel: ביטול\n  connected: מכשירים מחוברים\n  forget: הסר התקן\n  lowenergy: סוללה נמוכה\n  more: הגדרות Bluetooth נוספות\n  not_found: לא נמצאו מכשירי Bluetooth\n  pair: הוסף התקן\n  paired: מכשירים שנשמרו\n  placeholder:\n    passphrase: משפט סיסמה, קוד או סיכה\n  scanning: סריקה לאיתור מכשירים\ncontext_menu:\n  add_custom_text: הוסף פריט טקסט מותאם אישית\n  modules: מודולים\n  remove: הסר את המודול\n  reorder_disable: סרגל הכלים לנעול\n  reorder_enable: בטל את סרגל הכלים\n  restore: שחזר לברירת מחדל\n  settings: הגדרות\n  task_manager: מנהל משימות\nmedia:\n  default_multimedia_volume: עוצמת קול מולטימדיה\n  device:\n    channel:\n      system: צלילי מערכת\n    comunications: תקשורת\n    missing: מכשיר חסר\n    mixer: מערבל עוצמת קול\n    multimedia: מולטימדיה\n    settings: הגדרות מכשירים נוספות\n    volume: נפח המכשיר\n  input_devices: מכשירי קלט\n  output_devices: מכשירי פלט\n  players: נגני מדיה\nnotifications:\n  clear: נקה הכל\n  empty: אין התראות\n  settings: הגדרות התראות נוספות\n  title: התראות\nplaceholder:\n  bluetooth_devices: Bluetooth ומכשירים\n  ethernet_connected: גישה לאינטרנט\n  ethernet_disconnected: אין גישה לאינטרנט\n  notifications: התראות\n  settings: הגדרות מהירות\n  volume: עוצמת קול\nplugged: פָּקוּק\nsettings:\n  app_settings: הגדרות אפליקציה\n  brightness: בהירות\n  power: סוללה\n  restart: הפעל מחדש\n  title: הגדרות\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/hi.yml",
    "content": "battery:\n  remaining: '{{0}}% शेष'\n  smart_charge: स्मार्ट चार्ज\nbluetooth:\n  available: उपलब्ध उपकरण (युग्मित नहीं)\n  cancel: रद्द करना\n  connected: जुड़ी हुई डिवाइसेज\n  forget: भूल जाओ\n  lowenergy: कम ऊर्जा\n  more: अधिक ब्लूटूथ सेटिंग्स\n  not_found: कोई ब्लूटूथ डिवाइस नहीं मिला\n  pair: जोड़ा\n  paired: बचाए गए उपकरण\n  placeholder:\n    passphrase: पासफ्रेज़, कोड या पिन\n  scanning: उपकरणों के लिए स्कैनिंग\ncontext_menu:\n  add_custom_text: कस्टम पाठ आइटम जोड़ें\n  modules: मॉड्यूल\n  remove: मॉड्यूल निकालें\n  reorder_disable: लॉक टूलबार\n  reorder_enable: अनलॉक टूलबार\n  restore: डिफ़ॉल्ट रूप से पुनर्स्थापित करें\n  settings: सेटिंग्स\n  task_manager: कार्य प्रबंधक\nmedia:\n  default_multimedia_volume: बहुरूपी मात्रा\n  device:\n    channel:\n      system: सिस्टम लगता है\n    comunications: संचार\n    missing: गरीब युक्ति\n    mixer: वॉल्यूम मिक्सर\n    multimedia: बहुतायत\n    settings: अधिक डिवाइस सेटिंग्स\n    volume: युक्ति मात्रा\n  input_devices: आगत यंत्र\n  output_devices: आउटपुट डिवाइस\n  players: मीडिया -खिलाड़ी\nnotifications:\n  clear: सभी साफ करें\n  empty: कोई सूचना नहीं\n  settings: अधिक सूचनाएँ सेटिंग्स\n  title: अधिसूचना\nplaceholder:\n  bluetooth_devices: ब्लूटूथ और डिवाइस\n  ethernet_connected: इंटरनेट का उपयोग\n  ethernet_disconnected: इंटरनेट की सुविधा नहीं है\n  notifications: अधिसूचना\n  settings: त्वरित सेटिंग\n  volume: आयतन\nplugged: प्लग\nsettings:\n  app_settings: एप्लिकेशन सेटिंग\n  brightness: चमक\n  power: शक्ति\n  restart: पुनः आरंभ करें\n  title: समायोजन\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/hr.yml",
    "content": "battery:\n  remaining: '{{0}}% Preostalo'\n  smart_charge: Pametno punjenje\nbluetooth:\n  available: Dostupni uređaji (nisu upareni)\n  cancel: Otkazati\n  connected: Povezani uređaji\n  forget: Zaboraviti\n  lowenergy: Niska energija\n  more: Više Bluetooth postavki\n  not_found: Nisu pronađeni Bluetooth uređaji\n  pair: Par\n  paired: Spremljeni uređaji\n  placeholder:\n    passphrase: Prolaznu frazu, kod ili pin\n  scanning: Skeniranje za uređaje\ncontext_menu:\n  add_custom_text: Dodajte prilagođenu tekstualnu stavku\n  modules: Moduli\n  remove: Uklonite modul\n  reorder_disable: Alatna traka za zaključavanje\n  reorder_enable: Otključavanje alatne trake\n  restore: Vratite u zadani\n  settings: postavke\n  task_manager: Upravitelj zadataka\nmedia:\n  default_multimedia_volume: Multimedijski volumen\n  device:\n    channel:\n      system: Zvukovi sustava\n    comunications: Komunikacija\n    missing: Nedostaje uređaj\n    mixer: Mikser\n    multimedia: Multimedija\n    settings: Više postavki uređaja\n    volume: Glasnoća uređaja\n  input_devices: Ulazni uređaji\n  output_devices: Izlazni uređaji\n  players: Medijski igrači\nnotifications:\n  clear: Očisti sve\n  empty: Nema obavijesti\n  settings: Više postavki obavijesti\n  title: Obavijesti\nplaceholder:\n  bluetooth_devices: Bluetooth i uređaji\n  ethernet_connected: pristup internetu\n  ethernet_disconnected: Nema pristupa internetu\n  notifications: Obavijesti\n  settings: Brze postavke\n  volume: Volumen\nplugged: Uključeno\nsettings:\n  app_settings: Postavke aplikacija\n  brightness: Svjetlost\n  power: Vlast\n  restart: Ponovno pokrenuti\n  title: Postavke\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/hu.yml",
    "content": "battery:\n  remaining: '{{0}}% Maradt'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Elérhető eszközök (nem párosított)\n  cancel: Mégse\n  connected: Csatlakoztatott eszközök\n  forget: Eszköz elfelejtése\n  lowenergy: Alacsony energiafogyasztás\n  more: További Bluetooth-beállítások\n  not_found: Nincs elérhető Bluetooth-eszköz\n  pair: Párosítás\n  paired: Párosított eszközök\n  placeholder:\n    passphrase: Jelszó, kód vagy PIN\n  scanning: Eszközök keresése\ncontext_menu:\n  add_custom_text: Egyéni szövegelem hozzáadása\n  modules: Modulok\n  remove: Modul eltávolítása\n  reorder_disable: Eszköztár zárolása\n  reorder_enable: Eszköztár feloldása\n  restore: Alapértelmezett állapot visszaállítása\n  settings: Beállítások\n  task_manager: Feladatkezelő\nmedia:\n  default_multimedia_volume: Alapértelmezett médiakötet\n  device:\n    channel:\n      system: Rendszerhangok\n    comunications: Kommunikáció\n    missing: Hiányzó eszköz\n    mixer: Hangerő-keverő\n    multimedia: Multimédia\n    settings: További eszközbeállítások\n    volume: Eszköz hangerő\n  input_devices: Bemeneti eszközök\n  output_devices: Kimeneti eszközök\n  players: Médialejátszók\nnotifications:\n  clear: Összes törlése\n  empty: Nincsenek értesítések\n  settings: További értesítési beállítások\n  title: Értesítések\nplaceholder:\n  bluetooth_devices: Bluetooth és eszközök\n  ethernet_connected: Internet-hozzáférés\n  ethernet_disconnected: Nincs internet-hozzáférés\n  notifications: Értesítések\n  settings: Gyorsbeállítások\n  volume: Hangerő\nplugged: Dugva\nsettings:\n  app_settings: Alkalmazásbeállítások\n  brightness: Fényerő\n  power: Kikapcsolás\n  restart: Újraindítás\n  title: Beállítások\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/hy.yml",
    "content": "battery:\n  remaining: '{{0}}% Մնացել է'\n  smart_charge: Խելացի լիցքավորում\nbluetooth:\n  available: Առկա սարքեր (զույգը չեն)\n  cancel: Չեղարկել\n  connected: Միացված սարքեր\n  forget: Մոռանալ\n  lowenergy: Ցածր էներգիա\n  more: Ավելի շատ Bluetooth պարամետրեր\n  not_found: Bluetooth սարքեր չեն գտնվել\n  pair: Զույգ\n  paired: Պահպանված սարքեր\n  placeholder:\n    passphrase: Անհատական, կոդ կամ քորոց\n  scanning: Սարքավորումների սկանավորում\ncontext_menu:\n  add_custom_text: Ավելացնել սովորական տեքստային կետ\n  modules: Մոդուլներ\n  remove: Հեռացրեք մոդուլը\n  reorder_disable: Կողպեք գործիքագոտին\n  reorder_enable: Բացել գործիքագոտին\n  restore: Վերականգնել լռելյայն\n  settings: Կարգավորումներ\n  task_manager: Առաջադրանքների կառավարիչ\nmedia:\n  default_multimedia_volume: Մուլտիմեդիա ծավալը\n  device:\n    channel:\n      system: Համակարգային հնչյուններ\n    comunications: Կապի\n    missing: Բացակայում է սարքը\n    mixer: Ծավալային խառնիչ\n    multimedia: Մուլտիմեդիա\n    settings: Ավելի շատ սարքի պարամետրեր\n    volume: Սարքի ծավալը\n  input_devices: Մուտքային սարքեր\n  output_devices: Արդյունքային սարքեր\n  players: Մեդիա նվագարկիչներ\nnotifications:\n  clear: Մաքրել բոլորը\n  empty: Ոչ մի ծանուցում\n  settings: Ավելի շատ ծանուցումների պարամետրեր\n  title: Ծանուցումներ\nplaceholder:\n  bluetooth_devices: Bluetooth & սարքեր\n  ethernet_connected: Ինտերնետ հասանելիության\n  ethernet_disconnected: Ոչ մի ինտերնետ մուտք չկա\n  notifications: Ծանուցումներ\n  settings: Արագ պարամետրեր\n  volume: Ծավալ\nplugged: Խցանված\nsettings:\n  app_settings: Ծրագրի կարգավորումներ\n  brightness: Պայծառություն\n  power: Հզորություն\n  restart: Վերսկսել\n  title: Կարգավորումներ\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/id.yml",
    "content": "battery:\n  remaining: '{{0}}% Tersisa'\n  smart_charge: Pengisian Cerdas\nbluetooth:\n  available: Perangkat yang Tersedia (Tidak Dipasangkan)\n  cancel: Batal\n  connected: Perangkat yang Terhubung\n  forget: Lupakan.\n  lowenergy: Energi Rendah\n  more: Pengaturan Bluetooth lainnya\n  not_found: Tidak ditemukan perangkat Bluetooth\n  pair: Pasangan\n  paired: Perangkat yang Disimpan\n  placeholder:\n    passphrase: Frasa Sandi, Kode atau PIN\n  scanning: Memindai perangkat\ncontext_menu:\n  add_custom_text: Menambahkan item teks khusus\n  modules: Modul\n  remove: Hapus modul\n  reorder_disable: Mengunci bilah alat\n  reorder_enable: Buka kunci bilah alat\n  restore: Kembalikan ke Default\n  settings: Pengaturan\n  task_manager: Manajer Tugas\nmedia:\n  default_multimedia_volume: Volume Multimedia\n  device:\n    channel:\n      system: Suara sistem\n    comunications: Komunikasi\n    missing: Perangkat Hilang\n    mixer: Pengaduk Volume\n    multimedia: Multimedia\n    settings: Pengaturan Perangkat Lainnya\n    volume: Volume Perangkat\n  input_devices: Perangkat input\n  output_devices: Perangkat keluaran\n  players: Pemain Media\nnotifications:\n  clear: Hapus semua\n  empty: Tidak ada pemberitahuan\n  settings: Pengaturan Pemberitahuan Lainnya\n  title: Pemberitahuan\nplaceholder:\n  bluetooth_devices: Bluetooth & perangkat\n  ethernet_connected: Akses internet\n  ethernet_disconnected: Tidak ada akses internet\n  notifications: Pemberitahuan\n  settings: Pengaturan Cepat\n  volume: Volume\nplugged: Terpasang\nsettings:\n  app_settings: Pengaturan aplikasi\n  brightness: Kecerahan\n  power: Kekuatan\n  restart: Mengulang kembali\n  title: Pengaturan\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/is.yml",
    "content": "battery:\n  remaining: '{{0}}% eftir'\n  smart_charge: Snjallhleðsla\nbluetooth:\n  available: Laus tæki (ekki parað)\n  cancel: Hætta við\n  connected: Tengd tæki\n  forget: Gleymdu\n  lowenergy: Lítil orka\n  more: Fleiri Bluetooth stillingar\n  not_found: Engin Bluetooth tæki fundust\n  pair: Par\n  paired: Vistað tæki\n  placeholder:\n    passphrase: Passphrase, kóða eða pinna\n  scanning: Skönnun fyrir tæki\ncontext_menu:\n  add_custom_text: Bættu við sérsniðnum textahluta\n  modules: Einingar\n  remove: Fjarlægðu eininguna\n  reorder_disable: Læsa tækjastiku\n  reorder_enable: Opnaðu tækjastiku\n  restore: Endurheimta í sjálfgefið\n  settings: Stillingar\n  task_manager: Verkefnastjóri\nmedia:\n  default_multimedia_volume: Margmiðlunarrúmmál\n  device:\n    channel:\n      system: Kerfishljóð\n    comunications: Samskipti\n    missing: Vantar tæki\n    mixer: Bindi blöndunartæki\n    multimedia: Margmiðlun\n    settings: Fleiri tækjastillingar\n    volume: Tæki rúmmál\n  input_devices: Inntakstæki\n  output_devices: Framleiðsla tæki\n  players: Fjölmiðla leikmenn\nnotifications:\n  clear: Skýrt allt\n  empty: Engar tilkynningar\n  settings: Fleiri tilkynningarstillingar\n  title: Tilkynningar\nplaceholder:\n  bluetooth_devices: Bluetooth og tæki\n  ethernet_connected: internet aðgangur\n  ethernet_disconnected: Enginn internetaðgangur\n  notifications: Tilkynningar\n  settings: Fljótar stillingar\n  volume: Bindi\nplugged: Tengdur\nsettings:\n  app_settings: Forritastillingar\n  brightness: Birtustig\n  power: Máttur\n  restart: Endurræsa\n  title: Stillingar\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/it.yml",
    "content": "battery:\n  remaining: '{{0}}% rimanente'\n  smart_charge: Carica intelligente\nbluetooth:\n  available: Dispositivi disponibili (non accoppiati)\n  cancel: Annullamento\n  connected: Dispositivi connessi\n  forget: Dimenticare\n  lowenergy: Bassa energia\n  more: Altre impostazioni Bluetooth\n  not_found: Nessun dispositivo Bluetooth trovato\n  pair: Coppia\n  paired: Dispositivi salvati\n  placeholder:\n    passphrase: Passphrase, codice o PIN\n  scanning: Scansione dei dispositivi\ncontext_menu:\n  add_custom_text: Aggiungere un elemento di testo personalizzato\n  modules: Moduli\n  remove: Rimuovi Modulo\n  reorder_disable: Blocco della barra degli strumenti\n  reorder_enable: Sbloccare la barra degli strumenti\n  restore: Ripristino delle impostazioni predefinite\n  settings: Impostazioni\n  task_manager: Gestione Attività\nmedia:\n  default_multimedia_volume: Volume multimediale\n  device:\n    channel:\n      system: Suoni del sistema\n    comunications: Comunicazioni\n    missing: Dispositivo mancante\n    mixer: Miscelatore di volume\n    multimedia: Multimediale\n    settings: Altre impostazioni del dispositivo\n    volume: Volume del dispositivo\n  input_devices: Dispositivi di ingresso\n  output_devices: Dispositivi di uscita\n  players: Lettori Multimediali\nnotifications:\n  clear: Cancella tutto\n  empty: Nessuna notifica\n  settings: Altre impostazioni di notifica\n  title: Notifiche\nplaceholder:\n  bluetooth_devices: Bluetooth & Dispositivi\n  ethernet_connected: Accesso a Internet\n  ethernet_disconnected: Nessun accesso a Internet\n  notifications: Notifiche\n  settings: Impostazioni Rapide\n  volume: Volume\nplugged: Collegato\nsettings:\n  app_settings: Impostazioni App\n  brightness: Luminosità\n  power: Alimentazione\n  restart: Riavvia\n  title: Impostazioni\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ja.yml",
    "content": "battery:\n  remaining: 残り {{0}}%\n  smart_charge: スマートチャージ\nbluetooth:\n  available: 利用可能なデバイス\n  cancel: キャンセル\n  connected: 接続済みデバイス\n  forget: 削除\n  lowenergy: LE\n  more: その他のBluetooth設定\n  not_found: Bluetoothデバイスが見つかりません\n  pair: ペアリング\n  paired: 保存済みデバイス\n  placeholder:\n    passphrase: パスフレーズ、コードまたはPIN\n  scanning: デバイスのスキャン\ncontext_menu:\n  add_custom_text: カスタムテキストの追加\n  modules: モジュール\n  remove: モジュールを削除\n  reorder_disable: ツールバーをロック\n  reorder_enable: ツールバーのロック解除\n  restore: デフォルトに戻す\n  settings: SeelenUIの設定\n  task_manager: タスクマネージャー\nmedia:\n  default_multimedia_volume: デフォルトの音量\n  device:\n    channel:\n      system: システム音量\n    comunications: コミュニケーション\n    missing: デバイスが見つかりません\n    mixer: ミキサー\n    multimedia: マルチメディアの音量\n    settings: その他のデバイスの設定\n    volume: デバイスの音量\n  input_devices: 入力デバイス\n  output_devices: 出力デバイス\n  players: メディアプレーヤー\nnotifications:\n  clear: すべてクリア\n  empty: 通知なし\n  settings: その他の通知設定\n  title: 通知\nplaceholder:\n  bluetooth_devices: Bluetoothとデバイス\n  ethernet_connected: 接続済み\n  ethernet_disconnected: 未接続\n  notifications: 通知\n  settings: SeelenUIの設定\n  volume: 音量\nplugged: 接続済み\nsettings:\n  app_settings: SeelenUIの設定\n  brightness: 明るさ\n  power: 電源\n  restart: 再起動\n  title: 設定\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ka.yml",
    "content": "battery:\n  remaining: '{{0}}% დარჩენილია'\n  smart_charge: ჭკვიანი დამუხტვა\nbluetooth:\n  available: ხელმისაწვდომი მოწყობილობები (არ არის დაწყვილებული)\n  cancel: გაუქმება\n  connected: დაკავშირებული მოწყობილობები\n  forget: დავიწყება\n  lowenergy: დაბალი ენერგია\n  more: მეტი Bluetooth პარამეტრები\n  not_found: Bluetooth მოწყობილობები ვერ მოიძებნა\n  pair: წყვილი\n  paired: შენახული მოწყობილობები\n  placeholder:\n    passphrase: პასფაზის, კოდის ან პინის\n  scanning: მოწყობილობების სკანირება\ncontext_menu:\n  add_custom_text: დაამატეთ პერსონალური ტექსტის ელემენტი\n  modules: მოდულები\n  remove: ამოიღეთ მოდული\n  reorder_disable: ჩაკეტეთ პანელი\n  reorder_enable: განბლოკეთ პანელი\n  restore: აღადგინეთ ნაგულისხმევი\n  settings: პარამეტრები\n  task_manager: სამუშაო მენეჯერი\nmedia:\n  default_multimedia_volume: მულტიმედიური მოცულობა\n  device:\n    channel:\n      system: სისტემის ჟღერადობა\n    comunications: კომუნიკაცია\n    missing: დაკარგული მოწყობილობა\n    mixer: მოცულობის მიქსერი\n    multimedia: მულტიმედია\n    settings: მეტი მოწყობილობის პარამეტრები\n    volume: მოწყობილობის მოცულობა\n  input_devices: შეყვანის მოწყობილობები\n  output_devices: გამომავალი მოწყობილობები\n  players: მედიის მოთამაშეები\nnotifications:\n  clear: გასუფთავება\n  empty: შეტყობინებები არ არის\n  settings: მეტი შეტყობინებების პარამეტრები\n  title: შეტყობინებები\nplaceholder:\n  bluetooth_devices: Bluetooth & მოწყობილობები\n  ethernet_connected: ინტერნეტი\n  ethernet_disconnected: ინტერნეტი არ არის\n  notifications: შეტყობინებები\n  settings: სწრაფი პარამეტრები\n  volume: ტომი\nplugged: შეერთებულა\nsettings:\n  app_settings: პროგრამის პარამეტრები\n  brightness: სიკაშკაშე\n  power: ძალა\n  restart: Რესტარტი\n  title: პარამეტრები\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/km.yml",
    "content": "battery:\n  remaining: នៅសល់ {{0}}%\n  smart_charge: ការសាកថ្មឆ្លាតវៃ\nbluetooth:\n  available: ឧបករណ៍ដែលអាចប្រើបាន (មិនមានគូ)\n  cancel: លប់ចោល\n  connected: ឧបករណ៍ដែលបានភ្ជាប់\n  forget: ផ្លេច\n  lowenergy: ថាមពលទាប\n  more: ការកំណត់ប៊្លូធូសច្រើនទៀត\n  not_found: រកមិនឃើញឧបករណ៍ប៊្លូធូសទេ\n  pair: មយយកុ\n  paired: ឧបករណ៍ដែលបានរក្សាទុក\n  placeholder:\n    passphrase: ឃ្លាសម្ងាត់កូដឬលេខសម្ងាត់\n  scanning: ស្កេនរកឧបករណ៍\ncontext_menu:\n  add_custom_text: បន្ថែមធាតុអត្ថបទផ្ទាល់ខ្លួន\n  modules: ម៉ូឌុល\n  remove: យកម៉ូឌុលចេញ\n  reorder_disable: ចាក់សោរបារឧបករណ៍\n  reorder_enable: ដោះសោរបារឧបករណ៍ដោះសោរបារឧបករណ៍\n  restore: ស្តារទៅលំនាំដើមវិញ\n  settings: ការកំណត់\n  task_manager: កម្មវិធីគ្រប់គ្រងភារកិច្ច\nmedia:\n  default_multimedia_volume: បរិមាណពហុព័ត៌មាន\n  device:\n    channel:\n      system: សំឡេងប្រព័ន្ធ\n    comunications: ការអាេយអាេយរយការត្យេដ្ឋ\n    missing: ឧបករណ៍ដែលបាត់\n    mixer: ឧបករណ៍លាយកម្រិតសំឡេង\n    multimedia: ពហុមេឌា\n    settings: ការកំណត់ឧបករណ៍ច្រើនទៀត\n    volume: បរិមាណឧបករណ៍\n  input_devices: ឧបករណ៍បញ្ចូល\n  output_devices: ឧបករណ៍លទ្ធផល\n  players: អ្នកលេងប្រព័ន្ធផ្សព្វផ្សាយ\nnotifications:\n  clear: ជម្រះទាំងអស់\n  empty: គ្មានការជូនដំណឹង\n  settings: ការកំណត់ការជូនដំណឹងបន្ថែម\n  title: ការជូនដំណឹង\nplaceholder:\n  bluetooth_devices: ប៊្លូធូសនិងឧបករណ៍\n  ethernet_connected: ការចូលប្រើអ៊ីនធឺណិត\n  ethernet_disconnected: គ្មានការចូលប្រើអ៊ីនធឺណិតទេ\n  notifications: ការជូនដំណឹង\n  settings: ការកំណត់រហ័ស\n  volume: ចមនយន\nplugged: ដោត\nsettings:\n  app_settings: ការកំណត់កម្មវិធី\n  brightness: ពន្លឺ\n  power: អមនាច\n  restart: រ្យេដិតនោ\n  title: ការកំណត់\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ko.yml",
    "content": "battery:\n  remaining: '{{0}}% 남음'\n  smart_charge: 스마트 충전\nbluetooth:\n  available: 사용 가능한 장치(페어링되지 않음)\n  cancel: 취소\n  connected: 연결된 장치\n  forget: 잊어버림\n  lowenergy: 저에너지\n  more: 추가 블루투스 설정\n  not_found: 블루투스 장치를 찾을 수 없습니다.\n  pair: 페어\n  paired: 저장된 장치\n  placeholder:\n    passphrase: 비밀번호, 코드 또는 PIN\n  scanning: 디바이스 검색\ncontext_menu:\n  add_custom_text: 사용자 지정 텍스트 항목 추가\n  modules: 모듈\n  remove: 모듈 제거\n  reorder_disable: 잠금 도구 모음\n  reorder_enable: 도구 모음 잠금 해제\n  restore: 기본값으로 복원\n  settings: 설정\n  task_manager: 작업 관리자\nmedia:\n  default_multimedia_volume: 멀티미디어 볼륨\n  device:\n    channel:\n      system: 시스템 사운드\n    comunications: 통신 장치\n    missing: 누락된 장치\n    mixer: 볼륨 믹서\n    multimedia: 멀티미디어 장치\n    settings: 더 많은 장치 설정\n    volume: 장치 볼륨\n  input_devices: 입력 장치\n  output_devices: 출력 장치\n  players: 미디어 플레이어\nnotifications:\n  clear: 모두 지우기\n  empty: 알림 없음\n  settings: 추가 알림 설정\n  title: 알림\nplaceholder:\n  bluetooth_devices: 블루투스 및 장치\n  ethernet_connected: 인터넷 연결됨\n  ethernet_disconnected: 인터넷 연결 안됨\n  notifications: 알림\n  settings: 빠른 설정\n  volume: 볼륨\nplugged: 연결됨\nsettings:\n  app_settings: 앱 설정\n  brightness: 밝기\n  power: 전원\n  restart: 다시 시작\n  title: 설정\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ku.yml",
    "content": "battery:\n  remaining: '{{0}}% mayî'\n  smart_charge: Charge Smart\nbluetooth:\n  available: Amûrên berdest (ne paldan)\n  cancel: Bişûndekirin\n  connected: Amûrên girêdayî\n  forget: Jibîrkirin\n  lowenergy: Enerjiya kêm\n  more: Mîhengên bêtir Bluetooth\n  not_found: Amûrên Bluetooth-ê nehat dîtin\n  pair: Cot\n  paired: Amûrên hilanîn\n  placeholder:\n    passphrase: Passphrase, kod an pin\n  scanning: Ji bo amûrên şehîn\ncontext_menu:\n  add_custom_text: Tiştê nivîsê ya xwerû zêde bikin\n  modules: Modules\n  remove: Modulê rakirin\n  reorder_disable: Toolbar tool\n  reorder_enable: Vebijêrin Toolbar\n  restore: Restore to Default\n  settings: Settings\n  task_manager: Gerînendetiya Peywiran\nmedia:\n  default_multimedia_volume: Volume Multimedia\n  device:\n    channel:\n      system: Dengên pergalê\n    comunications: Têkilî\n    missing: Cîhaza wenda\n    mixer: Mixer mixer\n    multimedia: Multimedia\n    settings: Mîhengên bêtir amûrê\n    volume: Qumarê amûrê\n  input_devices: Amûrên input\n  output_devices: Amûrên deriyê\n  players: Lîstikên Medyayê\nnotifications:\n  clear: Hemî paqij bikin\n  empty: No notifications\n  settings: Mîhengên bêtir agahdariyan\n  title: Notifications\nplaceholder:\n  bluetooth_devices: Bluetooth & Amûrên\n  ethernet_connected: Gihîştina înternetê\n  ethernet_disconnected: Ne gihîştina înternetê\n  notifications: Notifications\n  settings: Mîhengên Bilez\n  volume: Bend\nplugged: Plugged\nsettings:\n  app_settings: Mîhengên App\n  brightness: Biriqîn\n  power: Erk\n  restart: Destpêkirin\n  title: Mîhengan\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/lb.yml",
    "content": "battery:\n  remaining: '{{0}}% Rescht'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Verfügbaren Apparater (net gepaart)\n  cancel: Ofbriechen\n  connected: Verbonne Geräter\n  forget: Vergiess\n  lowenergy: Wéineg Energie\n  more: Méi Bluetooth Astellunge\n  not_found: Keng Bluetooth Geräter fonnt\n  pair: Pabeier\n  paired: Gerett Geräter\n  placeholder:\n    passphrase: Passphrase, Code oder PIN\n  scanning: Scannen fir Apparater\ncontext_menu:\n  add_custom_text: Füügt personaliséiert Textartikel\n  modules: Moduls\n  remove: Ewechzehuelen Modul\n  reorder_disable: Lock Toolbar\n  reorder_enable: Spär Toolbar\n  restore: Restauréieren op Standard\n  settings: Astellungen\n  task_manager: Aufgab Manager\nmedia:\n  default_multimedia_volume: Multimedia Volumen\n  device:\n    channel:\n      system: System kléngt\n    comunications: Kommunikatiounen\n    missing: Vermësste Gerät\n    mixer: Volumenmëschung\n    multimedia: Multimedia\n    settings: Méi Apparat Astellunge\n    volume: Apparat Volumen\n  input_devices: Input Apparater\n  output_devices: Ausgangsapparater\n  players: Media Spiller\nnotifications:\n  clear: Emlotzen alleng\n  empty: Keng Notifikatiounen\n  settings: Méi Notifikatiounen Astellunge\n  title: Notifikatiounen\nplaceholder:\n  bluetooth_devices: Bluetooth & Apparater\n  ethernet_connected: Internet Zougang\n  ethernet_disconnected: Keen Internetzougang\n  notifications: Notifikatiounen\n  settings: Quick Astellungen\n  volume: Vuesso\nplugged: Verstoppt\nsettings:\n  app_settings: App Astellunge\n  brightness: Hellegkeet\n  power: Kraaftbeldscht\n  restart: Neistart\n  title: Astellunge\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/lo.yml",
    "content": "battery:\n  remaining: ຍັງເຫຼືອ {{0}}%.\n  smart_charge: ການສາກໄຟອັດສະລິຍະ\nbluetooth:\n  available: ອຸປະກອນທີ່ມີຢູ່ (ບໍ່ໄດ້ຈັບຄູ່ກັນ)\n  cancel: ຍົກເລີກ\n  connected: ອຸປະກອນເຊື່ອມຕໍ່\n  forget: ລືມ\n  lowenergy: ພະລັງງານຕ່ໍາ\n  more: ການຕັ້ງຄ່າ Bluetooth ເພີ່ມເຕີມ\n  not_found: ບໍ່ພົບອຸປະກອນ Bluetooth\n  pair: ຄູ່\n  paired: ອຸປະກອນທີ່ບັນທຶກໄວ້\n  placeholder:\n    passphrase: passphrase, ລະຫັດຫຼື PIN\n  scanning: ການສະແກນສໍາລັບອຸປະກອນຕ່າງໆ\ncontext_menu:\n  add_custom_text: ເພີ່ມລາຍການຕົວຫນັງສືທີ່ກໍານົດເອງ\n  modules: ໂມດູນ\n  remove: ດຶງອອກຈາກໂມດູນ\n  reorder_disable: ແຖບເຄື່ອງມືລັອກ\n  reorder_enable: ແຖບເຄື່ອງມືປົດລັອກ\n  restore: ຟື້ນຟູໃຫ້ເປັນຄ່າເລີ່ມຕົ້ນ\n  settings: ການຕັ້ງຄ່າ\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: ປະລິມານມັນຕິມີເດຍ\n  device:\n    channel:\n      system: ລະບົບ SYSTYS\n    comunications: ການສື່ສານ\n    missing: ອຸປະກອນທີ່ຂາດຫາຍໄປ\n    mixer: ປະລິມານ mixer ປະລິມານ\n    multimedia: ໂມສອນເຄີຍີຫໍ້\n    settings: ການຕັ້ງຄ່າອຸປະກອນເພີ່ມເຕີມ\n    volume: ປະລິມານອຸປະກອນ\n  input_devices: ອຸປະກອນປ້ອນຂໍ້ມູນ\n  output_devices: ອຸປະກອນຜົນໄດ້ຮັບ\n  players: ເຄື່ອງຫຼີ້ນສື່\nnotifications:\n  clear: ລ້າງທັງຫມົດ\n  empty: ບໍ່ມີການແຈ້ງເຕືອນ\n  settings: ການຕັ້ງຄ່າການແຈ້ງເຕືອນເພີ່ມເຕີມ\n  title: ແຈ້ງການ\nplaceholder:\n  bluetooth_devices: Bluetooth & ອຸປະກອນ\n  ethernet_connected: ການ​ເຂົ້າ​ເຖິງ​ອິນ​ເຕີ​ເນັດ\n  ethernet_disconnected: ບໍ່ມີການເຂົ້າເຖິງອິນເຕີເນັດ\n  notifications: ແຈ້ງການ\n  settings: ການຕັ້ງຄ່າດ່ວນ\n  volume: ປະລິມານ\nplugged: ສຽບ\nsettings:\n  app_settings: ການຕັ້ງຄ່າແອັບ app\n  brightness: ຄວາມສະຫວ່າງ\n  power: ພະເດດ\n  restart: ເລີ່ມ​ຕົ້ນ​ໃຫມ່\n  title: ການກໍານົດ\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/lt.yml",
    "content": "battery:\n  remaining: Liko {{0}} %\n  smart_charge: Išmanusis įkrovimas\nbluetooth:\n  available: Turimi įrenginiai (nesuporuoti)\n  cancel: Atšaukti\n  connected: Prijungti įrenginiai\n  forget: Pamirškite\n  lowenergy: Mažai energijos\n  more: Daugiau \"Bluetooth\" nustatymų\n  not_found: '\"Bluetooth\" įrenginių nerasta'\n  pair: Pora\n  paired: Išsaugoti įrenginiai\n  placeholder:\n    passphrase: Slaptažodžių frazė, kodas arba PIN kodas\n  scanning: Įrenginių nuskaitymas\ncontext_menu:\n  add_custom_text: Pridėti pasirinktinį teksto elementą\n  modules: Moduliai\n  remove: Pašalinkite modulį\n  reorder_disable: Užrakinti įrankių juostą\n  reorder_enable: Atrakinti įrankių juostą\n  restore: Atkurti numatytąjį nustatymą\n  settings: Nustatymai\n  task_manager: Užduočių tvarkyklė\nmedia:\n  default_multimedia_volume: Daugialypės terpės apimtis\n  device:\n    channel:\n      system: Sistemos garsai\n    comunications: Ryšiai\n    missing: Trūkstamas įrenginys\n    mixer: Garsumo maišytuvas\n    multimedia: Multimedija\n    settings: Daugiau įrenginio nustatymų\n    volume: Įrenginio tūris\n  input_devices: Įvesties įrenginiai\n  output_devices: Išvesties įrenginiai\n  players: Žiniasklaidos žaidėjai\nnotifications:\n  clear: Išvalyti viską\n  empty: Jokių pranešimų\n  settings: Daugiau pranešimų nustatymų\n  title: Pranešimai\nplaceholder:\n  bluetooth_devices: „Bluetooth“ ir įrenginiai\n  ethernet_connected: Interneto ryšys\n  ethernet_disconnected: Jokios interneto prieigos\n  notifications: Pranešimai\n  settings: Greiti nustatymai\n  volume: Apimtis\nplugged: Prijungtas\nsettings:\n  app_settings: Programos nustatymai\n  brightness: Ryškumas\n  power: Galia\n  restart: Perkrauti\n  title: Nustatymai\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/lv.yml",
    "content": "battery:\n  remaining: '{{0}}% atlikuši'\n  smart_charge: Viedā uzlāde\nbluetooth:\n  available: Pieejamās ierīces (nav savienotas pārī)\n  cancel: Atcelt\n  connected: Saistītās ierīces\n  forget: Aizmirstiet\n  lowenergy: Zems enerģijas patēriņš\n  more: Vairāk Bluetooth iestatījumu\n  not_found: Bluetooth ierīces nav atrastas\n  pair: Pāris\n  paired: Saglabātās ierīces\n  placeholder:\n    passphrase: Frāze, kods vai PIN kods\n  scanning: Ierīču skenēšana\ncontext_menu:\n  add_custom_text: Pievienot pielāgotu teksta elementu\n  modules: Moduļi\n  remove: Noņemt moduli\n  reorder_disable: Instrumentu joslas bloķēšana\n  reorder_enable: Rīkjoslas atbloķēšana\n  restore: Atjaunot noklusējuma iestatījumus\n  settings: Iestatījumi\n  task_manager: Uzdevumu pārvaldnieks\nmedia:\n  default_multimedia_volume: Multivides apjoms\n  device:\n    channel:\n      system: Sistēmas skaņas\n    comunications: Sakari\n    missing: Trūkst ierīces\n    mixer: Tilpuma mikseris\n    multimedia: Multimedija\n    settings: Vairāk ierīces iestatījumu\n    volume: Ierīces tilpums\n  input_devices: Ievadierīces\n  output_devices: Izejas ierīces\n  players: Plašsaziņas līdzekļu spēlētāji\nnotifications:\n  clear: Notīrīt visus\n  empty: Nav paziņojumu\n  settings: Vairāk paziņojumu iestatījumu\n  title: Paziņojumi\nplaceholder:\n  bluetooth_devices: Bluetooth & ierīces\n  ethernet_connected: Interneta pieslēgums\n  ethernet_disconnected: Nav piekļuves internetam\n  notifications: Paziņojumi\n  settings: Ātrie iestatījumi\n  volume: Tilpums\nplugged: Pieslēgts\nsettings:\n  app_settings: Lietotņu iestatījumi\n  brightness: Spilgtums\n  power: Spēks\n  restart: Restartēt\n  title: Iestatījumi\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/mk.yml",
    "content": "battery:\n  remaining: Преостануваат {{0}}%\n  smart_charge: Паметно полнење\nbluetooth:\n  available: Достапни уреди (не спарени)\n  cancel: Откажи\n  connected: Поврзани уреди\n  forget: Заборавете\n  lowenergy: Ниска енергија\n  more: Повеќе поставки за Bluetooth\n  not_found: Не се пронајдени уреди со Bluetooth\n  pair: Пар\n  paired: Зачувани уреди\n  placeholder:\n    passphrase: Фраза, код или игла\n  scanning: Скенирање за уреди\ncontext_menu:\n  add_custom_text: Додадете сопствена ставка за текст\n  modules: Модули\n  remove: Отстранете го модулот\n  reorder_disable: Заклучете ја лентата со алатки\n  reorder_enable: Отклучете ја лентата со алатки\n  restore: Враќање на стандардно\n  settings: Поставки\n  task_manager: Управувач со задачи\nmedia:\n  default_multimedia_volume: Мултимедијален волумен\n  device:\n    channel:\n      system: Звучи на системот\n    comunications: Комуникации\n    missing: Уред што недостасува\n    mixer: Миксер за волумен\n    multimedia: Мултимедија\n    settings: Повеќе поставки на уредот\n    volume: Волумен на уредот\n  input_devices: Внесување уреди\n  output_devices: Излезни уреди\n  players: Медиа плеери\nnotifications:\n  clear: Исчистете ги сите\n  empty: Нема известувања\n  settings: Поставки за повеќе известувања\n  title: Известувања\nplaceholder:\n  bluetooth_devices: Bluetooth & уреди\n  ethernet_connected: Интернет пристап\n  ethernet_disconnected: Нема пристап до Интернет\n  notifications: Известувања\n  settings: Брзи поставки\n  volume: Волумен\nplugged: Приклучен\nsettings:\n  app_settings: Поставки за апликации\n  brightness: Осветленост\n  power: Моќ\n  restart: Рестарт\n  title: Поставки\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/mn.yml",
    "content": "battery:\n  remaining: '{{0}}% үлдсэн'\n  smart_charge: Ухаалаг цэнэглэгч\nbluetooth:\n  available: Боломжтой төхөөрөмжүүд (хосолсонгүй)\n  cancel: Цуаах\n  connected: Холбогдсон төхөөрөмжүүд\n  forget: Мартах\n  lowenergy: Бага эрчим хүч\n  more: Илүү их Bluetooth Settings\n  not_found: Блютүүт төхөөрөмж байхгүй байна\n  pair: Хос\n  paired: Хадгалсан төхөөрөмж\n  placeholder:\n    passphrase: Нэвтрэх, код, код эсвэл PIN\n  scanning: Төхөөрөмжийг скан хийх\ncontext_menu:\n  add_custom_text: Захиалгат текстийг нэмж оруулна уу\n  modules: Барилгын\n  remove: Модулийг зайлуулах\n  reorder_disable: Цонхны түгжээ\n  reorder_enable: Хэрэгслийн самбарыг нээнэ үү\n  restore: Анхдагч болгож сэргээнэ үү\n  settings: Тохиргоо\n  task_manager: Ажлын менежер\nmedia:\n  default_multimedia_volume: Мультимедиа муж\n  device:\n    channel:\n      system: Синик систем\n    comunications: Харилцаа\n    missing: Төхөөрөмж байхгүй байна\n    mixer: Буугийн холимог\n    multimedia: Multimiedia\n    settings: Илүү олон төхөөрөмжийн тохиргоо\n    volume: Оффис\n  input_devices: Дурсгал дурсгалын төхөөрөмж\n  output_devices: Гаралтын төхөөрөмжүүд\n  players: Хэвлэл хэвлэл тоглогч\nnotifications:\n  clear: Бүгдийг цэвэрлэ\n  empty: Мэдэгдэл байхгүй байна\n  settings: Илүү олон мэдэгдлийн тохиргоо\n  title: Мэдэгдэл\nplaceholder:\n  bluetooth_devices: Блютүүт ба төхөөрөмж\n  ethernet_connected: Интернэт интернет нэвтрэх\n  ethernet_disconnected: Интернэтэд нэвтрэх боломжгүй байна\n  notifications: Мэдэгдэл\n  settings: Түлэлцэн тохиргоо\n  volume: Хэмжээ\nplugged: Залгаастай\nsettings:\n  app_settings: Дуулахын тохиргоо\n  brightness: Тод\n  power: Хүч чадал\n  restart: Дахин ачааллах\n  title: Тохиргоо\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ms.yml",
    "content": "battery:\n  remaining: '{{0}}% Baki'\n  smart_charge: Caj Pintar\nbluetooth:\n  available: Peranti yang ada (tidak dipasangkan)\n  cancel: Batalkan\n  connected: Peranti yang disambungkan\n  forget: Lupa\n  lowenergy: Tenaga rendah\n  more: Lebih banyak tetapan Bluetooth\n  not_found: Tiada peranti Bluetooth dijumpai\n  pair: Pasangan\n  paired: Peranti yang disimpan\n  placeholder:\n    passphrase: Frasa, kod atau pin\n  scanning: Mengimbas peranti\ncontext_menu:\n  add_custom_text: Tambahkan item teks tersuai\n  modules: Modul\n  remove: Keluarkan modul\n  reorder_disable: Kunci Toolbar\n  reorder_enable: Buka kunci bar alat\n  restore: Pulihkan ke lalai\n  settings: tetapan\n  task_manager: Pengurus Tugas\nmedia:\n  default_multimedia_volume: Jumlah multimedia\n  device:\n    channel:\n      system: Sistem bunyi\n    comunications: Komunikasi\n    missing: Peranti yang hilang\n    mixer: Pengadun kelantangan\n    multimedia: Multimedia\n    settings: Lebih banyak tetapan peranti\n    volume: Jumlah peranti\n  input_devices: Peranti input\n  output_devices: Peranti output\n  players: Pemain media\nnotifications:\n  clear: Kosongkan semua\n  empty: Tiada pemberitahuan\n  settings: Lebih banyak tetapan pemberitahuan\n  title: Pemberitahuan\nplaceholder:\n  bluetooth_devices: Bluetooth & peranti\n  ethernet_connected: akses internet\n  ethernet_disconnected: Tiada akses Internet\n  notifications: Pemberitahuan\n  settings: Tetapan cepat\n  volume: Kelantangan\nplugged: Terpasang\nsettings:\n  app_settings: Tetapan aplikasi\n  brightness: Kecerahan\n  power: Kuasa\n  restart: Mula semula\n  title: Tetapan\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/mt.yml",
    "content": "battery:\n  remaining: '{{0}}% Fadal'\n  smart_charge: Ħlas Intelliġenti\nbluetooth:\n  available: Apparat disponibbli (mhux imqabbad)\n  cancel: Ikkanċella\n  connected: Apparat konness\n  forget: Tinsa\n  lowenergy: Enerġija baxxa\n  more: Aktar settings Bluetooth\n  not_found: L-ebda apparat Bluetooth ma nstab\n  pair: Par\n  paired: Apparat salvat\n  placeholder:\n    passphrase: Passphrase, kodiċi jew pin\n  scanning: Skennjar għal apparati\ncontext_menu:\n  add_custom_text: Żid oġġett tat-test tad-dwana\n  modules: Moduli\n  remove: Neħħi l-modulu\n  reorder_disable: Lock Toolbar\n  reorder_enable: Nisfruttaw toolbar\n  restore: Irrestawra lil Default\n  settings: Settings\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: Volum multimedjali\n  device:\n    channel:\n      system: Ħsejjes tas-sistema\n    comunications: Komunikazzjonijiet\n    missing: Apparat nieqes\n    mixer: Volum Mixer\n    multimedia: Multimedja\n    settings: Aktar settings tal-apparat\n    volume: Volum tal-Apparat\n  input_devices: Apparat ta 'input\n  output_devices: Apparat tal-ħruġ\n  players: Plejers tal-midja\nnotifications:\n  clear: Ċar kollox\n  empty: L-ebda notifika\n  settings: Aktar settings ta 'notifiki\n  title: Notifiki\nplaceholder:\n  bluetooth_devices: Bluetooth & Apparat\n  ethernet_connected: Aċċess għall-Internet\n  ethernet_disconnected: L-ebda aċċess għall-internet\n  notifications: Notifiki\n  settings: Settings ta 'malajr\n  volume: Volum\nplugged: Ipplaggjat\nsettings:\n  app_settings: Settings tal-App\n  brightness: Luminożità\n  power: Qawwa\n  restart: Erġa ibda\n  title: Settings\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ne.yml",
    "content": "battery:\n  remaining: '{{0}}% बाँकी'\n  smart_charge: स्मार्ट चार्ज\nbluetooth:\n  available: उपलब्ध उपकरणहरू (जोडी छैन)\n  cancel: रद्द गर्नु\n  connected: जडित उपकरणहरू\n  forget: भुल्नु\n  lowenergy: कम ऊर्जा\n  more: अधिक ब्लुटुथ सेटिंग्स\n  not_found: कुनै ब्लुटुथ उपकरणहरू फेला परेन\n  pair: जोडी\n  paired: बचत गरिएको उपकरणहरू\n  placeholder:\n    passphrase: पासफ्रेज, कोड वा पिन\n  scanning: उपकरणहरूको लागि स्क्यान गर्दै\ncontext_menu:\n  add_custom_text: कस्टम पाठ वस्तु थप्नुहोस्\n  modules: मोड्युलहरू\n  remove: मोड्युल हटाउनुहोस्\n  reorder_disable: लक उपकरणपट्टी\n  reorder_enable: अनलक उपकरणपट्टी\n  restore: पूर्वनिर्धारित गर्न पुनर्स्थापना\n  settings: सेटिङहरू\n  task_manager: कार्य प्रबन्धक\nmedia:\n  default_multimedia_volume: मल्टिमेडिया भोल्युम\n  device:\n    channel:\n      system: प्रणाली ध्वनिहरू\n    comunications: संचार\n    missing: हराइरहेको उपकरण\n    mixer: भोल्युम मिक्सर\n    multimedia: मल्टिमेडिया\n    settings: अधिक उपकरण सेटिंग्स\n    volume: उपकरण भोल्युम\n  input_devices: इनपुट उपकरणहरू\n  output_devices: आउटपुट उपकरणहरू\n  players: मिडिया खेलाडीहरु\nnotifications:\n  clear: सबै खाली गर्नुहोस्\n  empty: NOSESTIONES\n  settings: अधिक सूचनाहरू सेटिंग्स\n  title: सूचना\nplaceholder:\n  bluetooth_devices: ब्लुटुथ र उपकरणहरू\n  ethernet_connected: इन्टरनेट पहुँच\n  ethernet_disconnected: कुनै इन्टर्नेट पहुँच छैन\n  notifications: सूचना\n  settings: द्रुत सेटिंग्स\n  volume: आवाज\nplugged: प्लग गरिएको\nsettings:\n  app_settings: अनुप्रयोग सेटिंग्स\n  brightness: काठको जग\n  power: बल\n  restart: फेरि शुरु गर्नु\n  title: बतंग\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/nl.yml",
    "content": "battery:\n  remaining: '{{0}}% resterend'\n  smart_charge: Slim opladen\nbluetooth:\n  available: Beschikbare apparaten (niet gekoppeld)\n  cancel: Annuleren\n  connected: Aangesloten apparaten\n  forget: Vergeet\n  lowenergy: Lage energie\n  more: Meer Bluetooth-instellingen\n  not_found: Geen Bluetooth-apparaten gevonden\n  pair: Paar\n  paired: Opgeslagen apparaten\n  placeholder:\n    passphrase: Wachtzin, code of pincode\n  scanning: Scannen naar apparaten\ncontext_menu:\n  add_custom_text: Aangepast tekstitem toevoegen\n  modules: Modules\n  remove: Verwijder de module\n  reorder_disable: Werkbalk vergrendelen\n  reorder_enable: Werkbalk ontgrendelen\n  restore: Terugzetten naar standaard\n  settings: Instellingen\n  task_manager: Taakbeheer\nmedia:\n  default_multimedia_volume: Multimedia-volume\n  device:\n    channel:\n      system: Systeemgeluiden\n    comunications: Communicatie\n    missing: Ontbrekend apparaat\n    mixer: Volumemixer\n    multimedia: Multimedia\n    settings: Meer apparaatinstellingen\n    volume: Apparaat Volume\n  input_devices: Invoerapparaten\n  output_devices: Uitvoerapparaten\n  players: Mediaspelers\nnotifications:\n  clear: Alles wissen\n  empty: Geen meldingen\n  settings: Meer instellingen voor meldingen\n  title: Meldingen\nplaceholder:\n  bluetooth_devices: Bluetooth & apparaten\n  ethernet_connected: internet toegang\n  ethernet_disconnected: Geen toegang tot het internet\n  notifications: Meldingen\n  settings: Snelle instellingen\n  volume: Volume\nplugged: Aangesloten\nsettings:\n  app_settings: App instellingen\n  brightness: Helderheid\n  power: Energie\n  restart: Herstarten\n  title: Instellingen\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/no.yml",
    "content": "battery:\n  remaining: '{{0}} % gjenstår'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Tilgjengelige enheter (ikke sammenkoblet)\n  cancel: Kansellere\n  connected: Tilkoblede enheter\n  forget: Glemme\n  lowenergy: Lav energi\n  more: Flere Bluetooth -innstillinger\n  not_found: Ingen Bluetooth -enheter funnet\n  pair: Par\n  paired: Lagrede enheter\n  placeholder:\n    passphrase: Passfrase, kode eller pinne\n  scanning: Skanning etter enheter\ncontext_menu:\n  add_custom_text: Legg til tilpasset tekstelement\n  modules: Moduler\n  remove: Fjern modulen\n  reorder_disable: Lås verktøylinje\n  reorder_enable: Lås opp verktøylinje\n  restore: Gjenopprett til standard\n  settings: Innstillinger\n  task_manager: Oppgavebehandling\nmedia:\n  default_multimedia_volume: Multimedia -volum\n  device:\n    channel:\n      system: Systemet høres ut\n    comunications: Kommunikasjon\n    missing: Manglende enhet\n    mixer: Volummikser\n    multimedia: Multimedia\n    settings: Flere enhetsinnstillinger\n    volume: Enhetsvolum\n  input_devices: Inngangsenheter\n  output_devices: Utgangsenheter\n  players: Mediespillere\nnotifications:\n  clear: Fjern alle\n  empty: Ingen varsler\n  settings: Flere varsler\n  title: Varsler\nplaceholder:\n  bluetooth_devices: Bluetooth & Devices\n  ethernet_connected: Internettilgang\n  ethernet_disconnected: Ingen internettilgang\n  notifications: Varsler\n  settings: Raske innstillinger\n  volume: Volum\nplugged: Plugget\nsettings:\n  app_settings: Appinnstillinger\n  brightness: Lysstyrke\n  power: Makt\n  restart: Omstart\n  title: Innstillinger\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/pa.yml",
    "content": "battery:\n  remaining: '{{0}}% ਬਾਕੀ'\n  smart_charge: ਸਮਾਰਟ ਚਾਰਜ\nbluetooth:\n  available: ਉਪਲਬਧ ਜੰਤਰ (ਪੇਅਰਡ ਨਹੀਂ)\n  cancel: ਰੱਦ ਕਰੋ\n  connected: ਜੁੜੇ ਜੰਤਰ\n  forget: ਭੁੱਲ ਜਾਓ\n  lowenergy: ਘੱਟ energy ਰਜਾ\n  more: ਹੋਰ ਬਲਿ Bluetooth ਟੁੱਥ ਸੈਟਿੰਗਜ਼\n  not_found: ਕੋਈ ਬਲੂਟੂਥ ਉਪਕਰਣ ਨਹੀਂ ਮਿਲਿਆ\n  pair: ਜੋੜੀ\n  paired: ਸੁਰੱਖਿਅਤ ਜੰਤਰ\n  placeholder:\n    passphrase: ਪ੍ਹੈਰਾ, ਕੋਡ ਜਾਂ ਪਿੰਨ\n  scanning: ਡਿਵਾਈਸਾਂ ਲਈ ਸਕੈਨ ਕਰਨਾ\ncontext_menu:\n  add_custom_text: ਕਸਟਮ ਟੈਕਸਟ ਆਈਟਮ ਸ਼ਾਮਲ ਕਰੋ\n  modules: ਮੋਡੀ ules ਲ\n  remove: ਮੋਡੀ module ਲ ਹਟਾਓ\n  reorder_disable: ਲਾਕ ਟੂਲਬਾਰ\n  reorder_enable: ਅਨਲੌਕ ਟੂਲਬਾਰ\n  restore: ਡਿਫੌਲਟ ਤੇ ਰੀਸਟੋਰ ਕਰੋ\n  settings: ਸੈਟਿੰਗਾਂ\n  task_manager: ਟਾਸਕ ਮੈਨੇਜਰ\nmedia:\n  default_multimedia_volume: ਮਲਟੀਮੀਡੀਆ ਵਾਲੀਅਮ\n  device:\n    channel:\n      system: ਸਿਸਟਮ ਆਵਾਜ਼\n    comunications: ਸੰਚਾਰ\n    missing: ਗੁੰਮ ਗਈ ਡਿਵਾਈਸ\n    mixer: ਵਾਲੀਅਮ ਮਿਕਸਰ\n    multimedia: ਮਲਟੀਮੀਡੀਆ\n    settings: ਹੋਰ ਜੰਤਰ ਸੈਟਿੰਗ\n    volume: ਜੰਤਰ ਵਾਲੀਅਮ\n  input_devices: ਇਨਪੁਟ ਜੰਤਰ\n  output_devices: ਆਉਟਪੁੱਟ ਜੰਤਰ\n  players: ਮੀਡੀਆ ਪਲੇਅਰ\nnotifications:\n  clear: ਸਭ ਨੂੰ ਸਾਫ ਕਰੋ\n  empty: ਕੋਈ ਨੋਟੀਫਿਕੇਸ਼ਨ ਨਹੀਂ\n  settings: ਹੋਰ ਸੂਚਨਾਵਾਂ ਸੈਟਿੰਗਾਂ\n  title: ਸੂਚਨਾਵਾਂ\nplaceholder:\n  bluetooth_devices: ਬਲਿ Bluetooth ਟੁੱਥ ਅਤੇ ਉਪਕਰਣ\n  ethernet_connected: ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ\n  ethernet_disconnected: ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ\n  notifications: ਸੂਚਨਾਵਾਂ\n  settings: ਤੇਜ਼ ਸੈਟਿੰਗ\n  volume: ਵਾਲੀਅਮ\nplugged: ਪਲੱਗ ਕੀਤਾ\nsettings:\n  app_settings: ਐਪ ਸੈਟਿੰਗਾਂ\n  brightness: ਚਮਕ\n  power: ਸ਼ਕਤੀ\n  restart: ਰੀਸਟਾਰਟ\n  title: ਸੈਟਿੰਗਜ਼\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/pl.yml",
    "content": "battery:\n  remaining: Pozostało {{0}}%\n  smart_charge: Inteligentne ładowanie\nbluetooth:\n  available: Dostępne urządzenia (niesparowane)\n  cancel: Anuluj\n  connected: Połączone urządzenia\n  forget: Zapomnij\n  lowenergy: Niski poziom baterii\n  more: Więcej ustawień Bluetooth\n  not_found: Nie znaleziono urządzeń Bluetooth\n  pair: Sparuj\n  paired: Zapisane urządzenia\n  placeholder:\n    passphrase: Hasło lub kod PIN\n  scanning: Skanowanie w poszukiwaniu urządzeń\ncontext_menu:\n  add_custom_text: Dodaj niestandardowy element tekstowy\n  modules: Moduły\n  remove: Usuń moduł\n  reorder_disable: Zablokuj pasek narzędzi\n  reorder_enable: Odblokuj pasek narzędzi\n  restore: Przywróć domyślne\n  settings: Ustawienia\n  task_manager: Menedżer zadań\nmedia:\n  default_multimedia_volume: Poziomy głośności\n  device:\n    channel:\n      system: Dźwięki systemowe\n    comunications: Komunikacja\n    missing: Brak urządzenia\n    mixer: Mikser głośności\n    multimedia: Multimedia\n    settings: Więcej ustawień urządzenia\n    volume: Głośność urządzenia\n  input_devices: Urządzenia wejściowe\n  output_devices: Urządzenia wyjściowe\n  players: Odtwarzacze multimedialne\nnotifications:\n  clear: Wyczyść wszystko\n  empty: Brak powiadomień\n  settings: Więcej ustawień powiadomień\n  title: Powiadomienia\nplaceholder:\n  bluetooth_devices: Bluetooth i urządzenia\n  ethernet_connected: dostęp do Internetu\n  ethernet_disconnected: Brak dostępu do internetu\n  notifications: Powiadomienia\n  settings: Szybkie ustawienia\n  volume: Głośność\nplugged: Zatkany\nsettings:\n  app_settings: Ustawienia aplikacji\n  brightness: Jasność\n  power: Zasilanie\n  restart: Uruchom ponownie\n  title: Ustawienia\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ps.yml",
    "content": "battery:\n  remaining: '{{0}}٪ پاتې'\n  smart_charge: سمارټ چارج\nbluetooth:\n  available: موجود وسایل (جوړ شوی ندی)\n  cancel: لغوه کول\n  connected: تړلي وسایل\n  forget: هیر یی کړه\n  lowenergy: ټیټ انرژي\n  more: نور بلوتوث بیا تنظیمات\n  not_found: هیڅ بلوتوث توکي وموندل شول\n  pair: جوړه\n  paired: خوندي شوي وسیلې\n  placeholder:\n    passphrase: پاسفراس، کوډ یا پن\n  scanning: د وسیلو لپاره سکین کول\ncontext_menu:\n  add_custom_text: د دودیز متن توکي اضافه کړئ\n  modules: موډلونه\n  remove: موډول لرې کړئ\n  reorder_disable: لاک توکپټه\n  reorder_enable: تواکاره توکپټه\n  restore: ډیفالټ ته راګرځي\n  settings: ترتیبات\n  task_manager: د دندې مدیر\nmedia:\n  default_multimedia_volume: ملټي میډیا حجم\n  device:\n    channel:\n      system: سیسټم غږونه\n    comunications: مخابرات\n    missing: ورک شوی وسیله\n    mixer: حجم مکسر\n    multimedia: ملټي میډیا\n    settings: د آلې امستنې\n    volume: د وسیلې حجم\n  input_devices: ننوت وسایل\n  output_devices: وتستۍ وسایل\n  players: د میډیا لوبغاړي\nnotifications:\n  clear: ټول پاک کړه\n  empty: خبرتیاوې نشته\n  settings: نور د خبرتیاوې تنظیمات\n  title: خبرتیاوې\nplaceholder:\n  bluetooth_devices: بلوتوث او وسایل\n  ethernet_connected: انټرنیټ لاسرسی\n  ethernet_disconnected: هیڅ انټرنیټ لاسرسی\n  notifications: خبرتیاوې\n  settings: ګړندي امستنې\n  volume: حجم\nplugged: پلګ شوی\nsettings:\n  app_settings: د ایپ امستنې\n  brightness: روښانه کول\n  power: ځواک\n  restart: بیا روښانه کول\n  title: امستنې\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/pt-BR.yml",
    "content": "battery:\n  remaining: '{{0}}% restante'\n  smart_charge: Carga inteligente\nbluetooth:\n  available: Dispositivos disponíveis (não emparelhados)\n  cancel: Cancelar\n  connected: Dispositivos conectados\n  forget: Esquecer\n  lowenergy: Baixa energia\n  more: Mais configurações de Bluetooth\n  not_found: Nenhum dispositivo Bluetooth encontrado\n  pair: Emparelhar\n  paired: Dispositivos emparelhados\n  placeholder:\n    passphrase: Senha, código ou PIN\n  scanning: Procurando dispositivos\ncontext_menu:\n  add_custom_text: Adicionar item de texto personalizado\n  modules: Módulos\n  remove: Remover módulo\n  reorder_disable: Bloquear barra de ferramentas\n  reorder_enable: Desbloquear barra de ferramentas\n  restore: Restaurar padrão\n  settings: Configurações\n  task_manager: Gerenciador de Tarefas\nmedia:\n  default_multimedia_volume: Volume multimídia\n  device:\n    channel:\n      system: Sons do sistema\n    comunications: Comunicações\n    missing: Dispositivo ausente\n    mixer: Mixer de volume\n    multimedia: Multimídia\n    settings: Mais configurações do dispositivo\n    volume: Volume do dispositivo\n  input_devices: Dispositivos de entrada\n  output_devices: Dispositivos de saída\n  players: Reprodutores de mídia\nnotifications:\n  clear: Limpar tudo\n  empty: Sem notificações\n  settings: Mais configurações de notificações\n  title: Notificações\nplaceholder:\n  bluetooth_devices: Bluetooth e Dispositivos\n  ethernet_connected: Acesso à Internet\n  ethernet_disconnected: Sem acesso à Internet\n  notifications: Notificações\n  settings: Configurações rápidas\n  volume: Volume\nplugged: Conectado\nsettings:\n  app_settings: Configurações do aplicativo\n  brightness: Brilho\n  power: Energia\n  restart: Reiniciar\n  title: Configurações\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/pt-PT.yml",
    "content": "battery:\n  remaining: '{{0}}% restante'\n  smart_charge: Carga inteligente\nbluetooth:\n  available: Dispositivos disponíveis (não emparelhados)\n  cancel: Cancelar\n  connected: Dispositivos conectados\n  forget: Esquecer-se\n  lowenergy: Baixa energia\n  more: Mais configurações do Bluetooth\n  not_found: Nenhum dispositivo Bluetooth encontrado\n  pair: Par\n  paired: Dispositivos salvos\n  placeholder:\n    passphrase: Senha, código ou pino\n  scanning: Digitalização para dispositivos\ncontext_menu:\n  add_custom_text: Adicionar item de texto personalizado\n  modules: Módulos\n  remove: Remova o módulo\n  reorder_disable: Barra de ferramentas de bloqueio\n  reorder_enable: Desbloqueie a barra de ferramentas\n  restore: Restaurar para padrão\n  settings: Configurações\n  task_manager: Gerente de tarefas\nmedia:\n  default_multimedia_volume: Volume multimídia\n  device:\n    channel:\n      system: Sons do sistema\n    comunications: Comunicações\n    missing: Dispositivo ausente\n    mixer: Misturador de volume\n    multimedia: Multimídia\n    settings: Mais configurações de dispositivo\n    volume: Volume do dispositivo\n  input_devices: Dispositivos de entrada\n  output_devices: Dispositivos de saída\n  players: Players de mídia\nnotifications:\n  clear: Limpar tudo\n  empty: Sem notificações\n  settings: Mais configurações de notificações\n  title: Notificações\nplaceholder:\n  bluetooth_devices: Bluetooth e dispositivos\n  ethernet_connected: acesso à Internet\n  ethernet_disconnected: Sem acesso à Internet\n  notifications: Notificações\n  settings: Configurações rápidas\n  volume: Volume\nplugged: Conectado\nsettings:\n  app_settings: Configurações do aplicativo\n  brightness: Brilho\n  power: Poder\n  restart: Reiniciar\n  title: Configurações\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ro.yml",
    "content": "battery:\n  remaining: '{{0}}% rămase'\n  smart_charge: Încărcare inteligentă\nbluetooth:\n  available: Dispozitive disponibile (neperecheate)\n  cancel: Anulează\n  connected: Dispozitive conectate\n  forget: Uită\n  lowenergy: Energie redusă\n  more: Mai multe setări Bluetooth\n  not_found: Nu s-au găsit dispozitive Bluetooth\n  pair: Pereche\n  paired: Dispozitive salvate\n  placeholder:\n    passphrase: Frază de acces, cod sau PIN\n  scanning: Scanarea pentru dispozitive\ncontext_menu:\n  add_custom_text: Adăugați un element de text personalizat\n  modules: Module\n  remove: Scoateți modulul\n  reorder_disable: Blocați bara de instrumente\n  reorder_enable: Deblocați bara de instrumente\n  restore: Restaurarea la setările implicite\n  settings: Setări\n  task_manager: Manager de activități\nmedia:\n  default_multimedia_volume: Volum multimedia\n  device:\n    channel:\n      system: Sunete de sistem\n    comunications: Comunicări\n    missing: Dispozitiv lipsă\n    mixer: Mixer de volum\n    multimedia: Multimedia\n    settings: Mai multe setări ale dispozitivului\n    volume: Volumul dispozitivului\n  input_devices: Dispozitive de intrare\n  output_devices: Dispozitive de ieșire\n  players: Playere media\nnotifications:\n  clear: Ștergeți toate\n  empty: Nu există notificări\n  settings: Mai multe setări pentru notificări\n  title: Notificări\nplaceholder:\n  bluetooth_devices: Bluetooth & Devices\n  ethernet_connected: acces la internet\n  ethernet_disconnected: Fara acces la internet\n  notifications: Notificări\n  settings: Setări rapide\n  volume: Volum\nplugged: Conectat\nsettings:\n  app_settings: Setări aplicație\n  brightness: Luminozitate\n  power: Putere\n  restart: Repornire\n  title: Setări\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ru.yml",
    "content": "battery:\n  remaining: Осталось {{0}}%\n  smart_charge: Умная зарядка\nbluetooth:\n  available: Доступные устройства (не сопряженные)\n  cancel: Отмена\n  connected: Подключенные устройства\n  forget: Удалить\n  lowenergy: Низкое энергопотребление\n  more: Другие настройки Bluetooth\n  not_found: Устройства Bluetooth не найдены\n  pair: Сопрячь\n  paired: Сопряжённые устройства\n  placeholder:\n    passphrase: Пароль, код или PIN-код\n  scanning: Сканирование для поиска устройств\ncontext_menu:\n  add_custom_text: Добавить пользовательский текстовый элемент\n  modules: Модули\n  remove: Удалить модуль\n  reorder_disable: Заблокировать панели\n  reorder_enable: Разблокировать панель\n  restore: Восстановить по умолчанию\n  settings: Настройки\n  task_manager: Диспетчер задач\nmedia:\n  default_multimedia_volume: Громкость устройств мультимедиа\n  device:\n    channel:\n      system: Системные звуки\n    comunications: Связь\n    missing: Отсутствующее устройство\n    mixer: Микшер громкости\n    multimedia: Мультимедиа\n    settings: Другие настройки устройства\n    volume: Громкость устройства\n  input_devices: Устройства ввода\n  output_devices: Устройства вывода\n  players: Медиаплееры\nnotifications:\n  clear: Очистить все\n  empty: Нет уведомлений\n  settings: Другие настройки уведомлений\n  title: Уведомления\nplaceholder:\n  bluetooth_devices: Bluetooth и устройства\n  ethernet_connected: Сеть\n  ethernet_disconnected: Нет сети\n  notifications: Уведомления\n  settings: Быстрые настройки\n  volume: Громкость\nplugged: Заткнуто\nsettings:\n  app_settings: Настройки приложения\n  brightness: Яркость\n  power: Питание\n  restart: Перезагрузка\n  title: Настройки\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/si.yml",
    "content": "battery:\n  remaining: '{{0}}% ඉතිරියි'\n  smart_charge: ස්මාර්ට් ආරෝපණය\nbluetooth:\n  available: ලබා ගත හැකි උපාංග (යුගලනය නොවේ)\n  cancel: අවලංගු කරන්න\n  connected: සම්බන්ධිත උපාංග\n  forget: අමතක කරන්න\n  lowenergy: අඩු ශක්තිය\n  more: වඩා බ්ලූටූත් සැකසුම්\n  not_found: බ්ලූටූත් උපාංග කිසිවක් හමු නොවීය\n  pair: යුගලය\n  paired: සුරකින ලද උපාංග\n  placeholder:\n    passphrase: මුරපදය, කේතය හෝ පින්\n  scanning: උපාංග සඳහා පරිලෝකනය කිරීම\ncontext_menu:\n  add_custom_text: අභිරුචි පෙළ අයිතමය එක් කරන්න\n  modules: මොඩියුල\n  remove: මොඩියුලය ඉවත් කරන්න\n  reorder_disable: අගුළු මෙවලම් තීරුව\n  reorder_enable: මෙවලම් තීරුව අගුළු ඇරීම\n  restore: පෙරනිමියට ප්රතිස්ථාපනය කරන්න\n  settings: සැකසීම්\n  task_manager: කාර්ය කළමනාකරු\nmedia:\n  default_multimedia_volume: බහුමාධ්ය පරිමාව\n  device:\n    channel:\n      system: පද්ධති ශබ්ද\n    comunications: සන්නිවේදනය\n    missing: නැතිවූ උපාංගය\n    mixer: වෙළුම් මික්සර්\n    multimedia: බහුමාධ්ය\n    settings: තවත් උපාංග සැකසුම්\n    volume: උපාංග පරිමාව\n  input_devices: ආදාන උපාංග\n  output_devices: නිමැවුම් උපාංග\n  players: මාධ්ය වාදකයන්\nnotifications:\n  clear: සියල්ල ඉවත් කරන්න\n  empty: දැනුම්දීම් නොමැත\n  settings: දැනුම්දීම් සැකසුම්\n  title: දැනුම්දීම්\nplaceholder:\n  bluetooth_devices: බ්ලූටූත් සහ උපාංග\n  ethernet_connected: අන්තර්ජාල පිවිසුම\n  ethernet_disconnected: අන්තර්ජාල ප්රවේශයක් නොමැත\n  notifications: දැනුම්දීම්\n  settings: ඉක්මන් සැකසුම්\n  volume: පරිමාව\nplugged: ප්ලග් කර ඇත\nsettings:\n  app_settings: යෙදුම් සැකසුම්\n  brightness: දීප්තිය\n  power: බලය\n  restart: යළි අරඹන්න\n  title: සැකසුම්\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/sk.yml",
    "content": "battery:\n  remaining: Zostáva {{0}} %.\n  smart_charge: Inteligentné nabíjanie\nbluetooth:\n  available: Dostupné zariadenia (nespárované)\n  cancel: Zrušiť\n  connected: Pripojené zariadenia\n  forget: Zabudnite na\n  lowenergy: Nízka energia\n  more: Ďalšie nastavenia Bluetooth\n  not_found: Nenašli sa žiadne zariadenia Bluetooth\n  pair: Pár\n  paired: Uložené zariadenia\n  placeholder:\n    passphrase: Fráza, kód alebo PIN\n  scanning: Skenovanie zariadení\ncontext_menu:\n  add_custom_text: Pridanie vlastnej textovej položky\n  modules: Moduly\n  remove: Odstrániť modul\n  reorder_disable: Uzamknutie panela nástrojov\n  reorder_enable: Odomknutie panela nástrojov\n  restore: Obnovenie predvoleného nastavenia\n  settings: Nastavenia\n  task_manager: Správca úloh\nmedia:\n  default_multimedia_volume: Multimediálny objem\n  device:\n    channel:\n      system: Zvuky systému\n    comunications: Komunikácia\n    missing: Chýbajúce zariadenie\n    mixer: Zmiešavač hlasitosti\n    multimedia: Multimédiá\n    settings: Ďalšie nastavenia zariadenia\n    volume: Objem zariadenia\n  input_devices: Vstupné zariadenia\n  output_devices: Výstupné zariadenia\n  players: Médiá\nnotifications:\n  clear: Vymazať všetko\n  empty: Žiadne oznámenia\n  settings: Ďalšie nastavenia oznámení\n  title: Oznámenia\nplaceholder:\n  bluetooth_devices: Bluetooth a zariadenia\n  ethernet_connected: Prístup na internet\n  ethernet_disconnected: Žiadny prístup na internet\n  notifications: Oznámenia\n  settings: Rýchle nastavenia\n  volume: Zväzok\nplugged: Zapojené\nsettings:\n  app_settings: Nastavenia aplikácií\n  brightness: Jas\n  power: Moc\n  restart: Reštart\n  title: nastavenie\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/so.yml",
    "content": "battery:\n  remaining: '{{0}}% hadhay'\n  smart_charge: Lacag Wacan\nbluetooth:\n  available: Aaladda la heli karo (ma aha mid lammaane)\n  cancel: Burin\n  connected: Aaladda ku xidhan\n  forget: Hilmaamid\n  lowenergy: Tamar hooseeya\n  more: Meelo badan oo Bluetooth ah\n  not_found: Ma jiro aaladaha Bluetooth ee la helay\n  pair: Labo\n  paired: Aaladda badbaadisay\n  placeholder:\n    passphrase: Passphrase, koodh ama PIN\n  scanning: Iskaanka aaladaha\ncontext_menu:\n  add_custom_text: Ku dar sheyga qoraalka ee caadada ah\n  modules: Moduleles\n  remove: Ka saar cutubka\n  reorder_disable: Quful aalad\n  reorder_enable: Fur furaha aaladda\n  restore: Dib u soo celinta\n  settings: Dejinta\n  task_manager: Maareeyaha Hawsha\nmedia:\n  default_multimedia_volume: Mugga mugga badan\n  device:\n    channel:\n      system: Nidaamka Nidaamka\n    comunications: Isgaar isgaar\n    missing: Qalabka la waayey\n    mixer: Isku-darka mugga\n    multimedia: Urur badan\n    settings: Meelo badan oo qalab ah\n    volume: Mugga aaladda\n  input_devices: Aaladaha wax soo galida\n  output_devices: Qalabka wax soo saarka\n  players: Ciyaartoyda warbaahinta\nnotifications:\n  clear: Caddee dhammaan\n  empty: Wax wargelin ah\n  settings: Meelo badan oo ogeysiisyo ah\n  title: Ogeysiisyada\nplaceholder:\n  bluetooth_devices: Bluetooth & aaladda\n  ethernet_connected: Helitaanka Internetka\n  ethernet_disconnected: Marin internet ah\n  notifications: Ogeysiisyada\n  settings: Meel deg deg ah\n  volume: Mug\nplugged: Ku xidhan\nsettings:\n  app_settings: App Goobaha\n  brightness: Dhalaalaya\n  power: Awood\n  restart: Dib u bilow\n  title: Dejinta\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/sr.yml",
    "content": "battery:\n  remaining: '{{0}}% преосталих'\n  smart_charge: Смарт Цхарге\nbluetooth:\n  available: Доступни уређаји (нису упарени)\n  cancel: Отказати\n  connected: Повезани уређаји\n  forget: Заборавити\n  lowenergy: Ниска енергија\n  more: Више Блуетоотх поставки\n  not_found: Није пронађен Блуетоотх уређаји\n  pair: Паузати\n  paired: Сачувани уређаји\n  placeholder:\n    passphrase: Пасспхраза, код или ПИН\n  scanning: Скенирање за уређаје\ncontext_menu:\n  add_custom_text: Додајте прилагођени текст текста\n  modules: Модули\n  remove: Уклони модул\n  reorder_disable: Алатна трака за закључавање\n  reorder_enable: Откључајте траку са алаткама\n  restore: Врати на подразумевано\n  settings: Подешавања\n  task_manager: Таск Манагер\nmedia:\n  default_multimedia_volume: Мултимедијална количина\n  device:\n    channel:\n      system: Систем звукови\n    comunications: Комуникација\n    missing: Недостаје уређај\n    mixer: Миксер за јачину звука\n    multimedia: Мултимедијални\n    settings: Више подешавања уређаја\n    volume: Запремина уређаја\n  input_devices: Улазни уређаји\n  output_devices: Излазни уређаји\n  players: Медиа Играчи\nnotifications:\n  clear: Очистити све\n  empty: Нема обавештења\n  settings: Више поставки обавештења\n  title: Обавештења\nplaceholder:\n  bluetooth_devices: Блуетоотх и уређаји\n  ethernet_connected: Приступ интернету\n  ethernet_disconnected: Нема приступа интернету\n  notifications: Обавештења\n  settings: Брза подешавања\n  volume: Запремина\nplugged: Плуггед\nsettings:\n  app_settings: Подешавања апликација\n  brightness: Светлина\n  power: Моћ\n  restart: Поново покренути\n  title: Подешавања\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/sv.yml",
    "content": "battery:\n  remaining: '{{0}}% återstår'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Tillgängliga enheter (ej ihopkopplade)\n  cancel: Avbryt\n  connected: Uppkopplade enheter\n  forget: Glömma\n  lowenergy: Låg energi\n  more: Fler Bluetooth-inställningar\n  not_found: Inga Bluetooth-enheter hittades\n  pair: Par\n  paired: Sparade enheter\n  placeholder:\n    passphrase: Lösenfras, kod eller PIN-kod\n  scanning: Skanning efter enheter\ncontext_menu:\n  add_custom_text: Lägg till anpassat textobjekt\n  modules: Moduler\n  remove: Ta bort modulen\n  reorder_disable: Lås verktygsfältet\n  reorder_enable: Lås upp verktygsfältet\n  restore: Återställ till standard\n  settings: Inställningar\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: Multimedia Volym\n  device:\n    channel:\n      system: Systemljud\n    comunications: Kommunikation\n    missing: Saknad enhet\n    mixer: Volymmixer\n    multimedia: Multimedia\n    settings: Fler enhetsinställningar\n    volume: Enhetens volym\n  input_devices: Inmatningsenheter\n  output_devices: Utmatningsenheter\n  players: Mediaspelare\nnotifications:\n  clear: Rensa alla\n  empty: Inga meddelanden\n  settings: Fler inställningar för aviseringar\n  title: Meddelanden\nplaceholder:\n  bluetooth_devices: Bluetooth & enheter\n  ethernet_connected: internetåtkomst\n  ethernet_disconnected: Ingen internetuppkoppling\n  notifications: Meddelanden\n  settings: Snabbinställningar\n  volume: Volym\nplugged: Pluggad\nsettings:\n  app_settings: App inställningar\n  brightness: Ljusstyrka\n  power: Driva\n  restart: Omstart\n  title: inställningar\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/sw.yml",
    "content": "battery:\n  remaining: '{{0}}% Imesalia'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Vifaa vinavyopatikana (sio paired)\n  cancel: Ghairi\n  connected: Vifaa vilivyounganishwa\n  forget: Kusahau\n  lowenergy: Nishati ya chini\n  more: Mipangilio zaidi ya Bluetooth\n  not_found: Hakuna vifaa vya Bluetooth vilivyopatikana\n  pair: Jozi\n  paired: Vifaa vilivyookolewa\n  placeholder:\n    passphrase: Passphrase, msimbo au pini\n  scanning: Skanning kwa vifaa\ncontext_menu:\n  add_custom_text: Ongeza kipengee cha maandishi ya kawaida\n  modules: Moduli\n  remove: Ondoa moduli\n  reorder_disable: Kufunga zana\n  reorder_enable: Fungua zana ya zana\n  restore: Rejesha kwa chaguo -msingi\n  settings: Mipangilio\n  task_manager: Meneja wa Kazi\nmedia:\n  default_multimedia_volume: Kiasi cha multimedia\n  device:\n    channel:\n      system: Mfumo unasikika\n    comunications: Mawasiliano\n    missing: Kifaa kinachokosekana\n    mixer: Mchanganyiko wa kiasi\n    multimedia: Multimedia\n    settings: Mipangilio zaidi ya kifaa\n    volume: Kiasi cha kifaa\n  input_devices: Vifaa vya pembejeo\n  output_devices: Vifaa vya pato\n  players: Wachezaji wa media\nnotifications:\n  clear: Futa zote\n  empty: Hakuna arifa\n  settings: Mipangilio ya Arifa zaidi\n  title: Arifa\nplaceholder:\n  bluetooth_devices: Bluetooth & vifaa\n  ethernet_connected: Ufikiaji wa mtandao\n  ethernet_disconnected: Hakuna ufikiaji wa mtandao\n  notifications: Arifa\n  settings: Mipangilio ya haraka\n  volume: Kiasi\nplugged: Imechomekwa\nsettings:\n  app_settings: Mipangilio ya programu\n  brightness: Mwangaza\n  power: Nguvu\n  restart: Anzisha tena\n  title: Mipangilio\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ta.yml",
    "content": "battery:\n  remaining: '{{0}}% மீதமுள்ளது'\n  smart_charge: ஸ்மார்ட் சார்ஜ்\nbluetooth:\n  available: கிடைக்கக்கூடிய சாதனங்கள் (ஜோடி அல்ல)\n  cancel: ரத்துசெய்\n  connected: இணைக்கப்பட்ட சாதனங்கள்\n  forget: மறந்துவிடு\n  lowenergy: குறைந்த ஆற்றல்\n  more: மேலும் புளூடூத் அமைப்புகள்\n  not_found: புளூடூத் சாதனங்கள் எதுவும் கிடைக்கவில்லை\n  pair: ஜோடி\n  paired: சேமித்த சாதனங்கள்\n  placeholder:\n    passphrase: கடவுச்சொல், குறியீடு அல்லது முள்\n  scanning: சாதனங்களுக்கான ஸ்கேன்\ncontext_menu:\n  add_custom_text: தனிப்பயன் உரை உருப்படியைச் சேர்க்கவும்\n  modules: தொகுதிகள்\n  remove: தொகுதியை அகற்று\n  reorder_disable: பூட்டு கருவிப்பட்டி\n  reorder_enable: கருவிப்பட்டியைத் திறக்கவும்\n  restore: இயல்புநிலைக்கு மீட்டமைக்கவும்\n  settings: அமைப்புகள்\n  task_manager: பணி மேலாளர்\nmedia:\n  default_multimedia_volume: மல்டிமீடியா தொகுதி\n  device:\n    channel:\n      system: கணினி ஒலிகள்\n    comunications: தகவல்தொடர்புகள்\n    missing: சாதனம் இல்லை\n    mixer: தொகுதி மிக்சர்\n    multimedia: மல்டிமீடியா\n    settings: மேலும் சாதன அமைப்புகள்\n    volume: சாதன தொகுதி\n  input_devices: உள்ளீட்டு சாதனங்கள்\n  output_devices: வெளியீட்டு சாதனங்கள்\n  players: மீடியா பிளேயர்கள்\nnotifications:\n  clear: அனைத்தையும் அழிக்கவும்\n  empty: அறிவிப்புகள் இல்லை\n  settings: மேலும் அறிவிப்புகள் அமைப்புகள்\n  title: அறிவிப்புகள்\nplaceholder:\n  bluetooth_devices: புளூடூத் & சாதனங்கள்\n  ethernet_connected: இணைய அணுகல்\n  ethernet_disconnected: இணைய அணுகல் இல்லை\n  notifications: அறிவிப்புகள்\n  settings: விரைவான அமைப்புகள்\n  volume: தொகுதி\nplugged: செருகப்பட்டது\nsettings:\n  app_settings: பயன்பாட்டு அமைப்புகள்\n  brightness: பிரகாசம்\n  power: சக்தி\n  restart: மறுதொடக்கம்\n  title: அமைப்புகள்\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/te.yml",
    "content": "battery:\n  remaining: '{{0}}% మిగిలి ఉంది'\n  smart_charge: స్మార్ట్ ఛార్జ్\nbluetooth:\n  available: అందుబాటులో ఉన్న పరికరాలు (జత చేయబడలేదు)\n  cancel: రద్దు చేయండి\n  connected: కనెక్ట్ చేయబడిన పరికరాలు\n  forget: మర్చిపో\n  lowenergy: తక్కువ శక్తి\n  more: మరిన్ని బ్లూటూత్ సెట్టింగులు\n  not_found: బ్లూటూత్ పరికరాలు కనుగొనబడలేదు\n  pair: జత\n  paired: సేవ్ చేసిన పరికరాలు\n  placeholder:\n    passphrase: పాస్‌ఫ్రేజ్, కోడ్ లేదా పిన్\n  scanning: పరికరాల కోసం స్కానింగ్\ncontext_menu:\n  add_custom_text: అనుకూల వచన అంశాన్ని జోడించండి\n  modules: గుణకాలు\n  remove: మాడ్యూల్ తొలగించండి\n  reorder_disable: లాక్ టూల్‌బార్\n  reorder_enable: అన్‌లాక్ టూల్‌బార్\n  restore: డిఫాల్ట్‌కు పునరుద్ధరించండి\n  settings: సెట్టింగ్‌లు\n  task_manager: టాస్క్ మేనేజర్\nmedia:\n  default_multimedia_volume: మల్టీమీడియా వాల్యూమ్\n  device:\n    channel:\n      system: సిస్టమ్ శబ్దాలు\n    comunications: కమ్యూనికేషన్స్\n    missing: తప్పిపోయిన పరికరం\n    mixer: వాల్యూమ్ మిక్సర్\n    multimedia: మల్టీమీడియా\n    settings: మరిన్ని పరికర సెట్టింగులు\n    volume: పరికర వాల్యూమ్\n  input_devices: ఇన్పుట్ పరికరాలు\n  output_devices: అవుట్పుట్ పరికరాలు\n  players: మీడియా ప్లేయర్స్\nnotifications:\n  clear: అన్నీ క్లియర్ చేయండి\n  empty: నోటిఫికేషన్‌లు లేవు\n  settings: మరిన్ని నోటిఫికేషన్ సెట్టింగులు\n  title: నోటిఫికేషన్‌లు\nplaceholder:\n  bluetooth_devices: బ్లూటూత్ & పరికరాలు\n  ethernet_connected: ఇంటర్నెట్ సదుపాయం\n  ethernet_disconnected: ఇంటర్నెట్ సదుపాయం లేదు\n  notifications: నోటిఫికేషన్‌లు\n  settings: శీఘ్ర సెట్టింగులు\n  volume: వాల్యూమ్\nplugged: ప్లగ్ చేయబడింది\nsettings:\n  app_settings: అనువర్తన సెట్టింగులు\n  brightness: ప్రకాశం\n  power: శక్తి\n  restart: పున art ప్రారంభం\n  title: సెట్టింగులు\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/tg.yml",
    "content": "battery:\n  remaining: '{{0}}% боқимонда'\n  smart_charge: Заряди интеллектуалӣ\nbluetooth:\n  available: Дастгоҳҳои дастрас (ҷуфтнашаванда нестанд)\n  cancel: Манъ кардан\n  connected: Дастгоҳҳои пайвастшуда\n  forget: Фаромӯш кардан\n  lowenergy: Энергияи паст\n  more: Танзимоти бештар Bluetooth\n  not_found: Не таҳлили Bluetooth ёфт нашуд\n  pair: Ьуфт\n  paired: Дастгоҳҳои захирашуда\n  placeholder:\n    passphrase: Гузарвожа, рамз ё PIN\n  scanning: Ҷустуҷӯи дастгоҳҳо\ncontext_menu:\n  add_custom_text: Ададҳои матнии фармоишӣ илова кунед\n  modules: Модулҳо\n  remove: Модулро хориҷ кунед\n  reorder_disable: Лок\n  reorder_enable: Ворид кардани панели асбобҳо\n  restore: Барқарор кардан\n  settings: Танзимотҳо\n  task_manager: Менеҷери вазифа\nmedia:\n  default_multimedia_volume: Ҳаҷми мултимедиявӣ\n  device:\n    channel:\n      system: Садо медиҳад\n    comunications: Иртибот\n    missing: Дастгоҳи гумшуда\n    mixer: Миксер\n    multimedia: Мултимедиявӣ\n    settings: Танзимоти телефон\n    volume: Ҳаҷми дастгоҳ\n  input_devices: Дастгоҳҳои вуруд\n  output_devices: Дастгоҳҳои баромад\n  players: Бозингарони ВАО\nnotifications:\n  clear: Тамоман тоза\n  empty: Огоҳӣ\n  settings: Танзимоти огоҳиномаи огоҳӣ\n  title: Огоҳиҳо\nplaceholder:\n  bluetooth_devices: Bluetooth & Дастгоҳҳо\n  ethernet_connected: Дастрасии Интернет\n  ethernet_disconnected: Дастрасии Интернет надорад\n  notifications: Огоҳиҳо\n  settings: Танзимоти фаврӣ\n  volume: Баланд\nplugged: Васл карда шудааст\nsettings:\n  app_settings: Танзимоти барнома\n  brightness: Равшанӣ\n  power: Нерӯ\n  restart: Оғози дубора\n  title: Танзимот\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/th.yml",
    "content": "battery:\n  remaining: เหลืออีก {{0}}%\n  smart_charge: ชาร์จอัจฉริยะ\nbluetooth:\n  available: อุปกรณ์ที่มีอยู่ (ไม่จับคู่)\n  cancel: ยกเลิก\n  connected: อุปกรณ์ที่เชื่อมต่อ\n  forget: ลืม\n  lowenergy: พลังงานต่ำ\n  more: การตั้งค่าบลูทู ธ เพิ่มเติม\n  not_found: ไม่พบอุปกรณ์บลูทู ธ\n  pair: คู่\n  paired: อุปกรณ์ที่บันทึกไว้\n  placeholder:\n    passphrase: รหัสผ่านรหัสหรือพิน\n  scanning: การสแกนสำหรับอุปกรณ์\ncontext_menu:\n  add_custom_text: เพิ่มรายการข้อความที่กำหนดเอง\n  modules: โมดูล\n  remove: ลบโมดูล\n  reorder_disable: ล็อคแถบเครื่องมือ\n  reorder_enable: ปลดล็อคแถบเครื่องมือ\n  restore: คืนค่าเป็นค่าเริ่มต้น\n  settings: การตั้งค่า\n  task_manager: ตัวจัดการงาน\nmedia:\n  default_multimedia_volume: ปริมาณมัลติมีเดีย\n  device:\n    channel:\n      system: เสียงของระบบ\n    comunications: การสื่อสาร\n    missing: อุปกรณ์ที่หายไป\n    mixer: เครื่องผสมระดับเสียง\n    multimedia: มัลติมีเดีย\n    settings: การตั้งค่าอุปกรณ์เพิ่มเติม\n    volume: ปริมาณอุปกรณ์\n  input_devices: อุปกรณ์อินพุต\n  output_devices: อุปกรณ์เอาต์พุต\n  players: เครื่องเล่นสื่อ\nnotifications:\n  clear: ล้างทั้งหมด\n  empty: ไม่มีการแจ้งเตือน\n  settings: การตั้งค่าการแจ้งเตือนเพิ่มเติม\n  title: การแจ้งเตือน\nplaceholder:\n  bluetooth_devices: บลูทู ธ และอุปกรณ์\n  ethernet_connected: การเข้าถึงอินเทอร์เน็ต\n  ethernet_disconnected: ไม่มีการเข้าถึงอินเทอร์เน็ต\n  notifications: การแจ้งเตือน\n  settings: การตั้งค่าอย่างรวดเร็ว\n  volume: ปริมาณ\nplugged: เสียบปลั๊กแล้ว\nsettings:\n  app_settings: การตั้งค่าแอป\n  brightness: ความสว่าง\n  power: พลัง\n  restart: เริ่มต้นใหม่\n  title: การตั้งค่า\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/tl.yml",
    "content": "battery:\n  remaining: '{{0}}% ang natitira'\n  smart_charge: Smart Charge\nbluetooth:\n  available: Mga magagamit na aparato (hindi ipinares)\n  cancel: Kanselahin\n  connected: Mga konektadong aparato\n  forget: Kalimutan\n  lowenergy: Mababang enerhiya\n  more: Marami pang mga setting ng Bluetooth\n  not_found: Walang nahanap na mga aparato ng Bluetooth\n  pair: Pares\n  paired: Nai -save na mga aparato\n  placeholder:\n    passphrase: Passphrase, code o pin\n  scanning: Pag -scan para sa mga aparato\ncontext_menu:\n  add_custom_text: Magdagdag ng pasadyang item ng teksto\n  modules: Mga module\n  remove: Alisin ang module\n  reorder_disable: I -lock ang toolbar\n  reorder_enable: I -unlock ang toolbar\n  restore: Ibalik sa default\n  settings: Mga setting\n  task_manager: Task Manager\nmedia:\n  default_multimedia_volume: Dami ng multimedia\n  device:\n    channel:\n      system: Tunog ng system\n    comunications: Komunikasyon\n    missing: Nawawalang aparato\n    mixer: Dami ng panghalo\n    multimedia: Multimedia\n    settings: Higit pang mga setting ng aparato\n    volume: Dami ng aparato\n  input_devices: Mga aparato sa pag -input\n  output_devices: Mga aparato ng output\n  players: Mga manlalaro ng media\nnotifications:\n  clear: Malinaw ang lahat\n  empty: Walang mga abiso\n  settings: Marami pang Mga Setting ng Mga Abiso\n  title: Mga abiso\nplaceholder:\n  bluetooth_devices: Bluetooth at mga aparato\n  ethernet_connected: Internet access\n  ethernet_disconnected: Walang internet access\n  notifications: Mga abiso\n  settings: Mabilis na mga setting\n  volume: Dami\nplugged: Nakasaksak\nsettings:\n  app_settings: Mga Setting ng App\n  brightness: Ningning\n  power: Kapangyarihan\n  restart: I -restart\n  title: Mga setting\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/tr.yml",
    "content": "battery:\n  remaining: '%{{0}} Kalan'\n  smart_charge: Akıllı Şarj\nbluetooth:\n  available: Mevcut Cihazlar (Eşleştirilmemiş)\n  cancel: İptal\n  connected: Bağlantılı Cihazlar\n  forget: Unutmak\n  lowenergy: Düşük Enerji\n  more: Daha Fazla Bluetooth Ayarı\n  not_found: Bluetooth cihazı bulunamadı\n  pair: Çift\n  paired: Kaydedilen Cihazlar\n  placeholder:\n    passphrase: Parola, Kod veya PIN\n  scanning: Cihazlar için tarama\ncontext_menu:\n  add_custom_text: Özel metin öğesi ekleme\n  modules: Modüller\n  remove: Modülü Kaldır\n  reorder_disable: Araç çubuğunu kilitle\n  reorder_enable: Araç çubuğunun kilidini aç\n  restore: Varsayılana Geri Yükle\n  settings: Ayarlar\n  task_manager: Görev Yöneticisi\nmedia:\n  default_multimedia_volume: Multimedya Hacmi\n  device:\n    channel:\n      system: Sistem sesleri\n    comunications: İletişim\n    missing: Kayıp Cihaz\n    mixer: Ses Mikseri\n    multimedia: Multimedya\n    settings: Daha Fazla Cihaz Ayarı\n    volume: Cihaz Hacmi\n  input_devices: Giriş cihazları\n  output_devices: Çıkış cihazları\n  players: Medya Oynatıcıları\nnotifications:\n  clear: Tümünü temizle\n  empty: Bildirim yok\n  settings: Daha Fazla Bildirim Ayarı\n  title: Bildirimler\nplaceholder:\n  bluetooth_devices: Bluetooth ve Cihazlar\n  ethernet_connected: İnternet erişimi\n  ethernet_disconnected: İnternet erişimi yok\n  notifications: Bildirimler\n  settings: Hızlı Ayarlar\n  volume: Ses\nplugged: Takılı\nsettings:\n  app_settings: Uygulama Ayarları\n  brightness: Parlaklık\n  power: Güç\n  restart: Yeniden başlat\n  title: Ayarlar\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/uk.yml",
    "content": "battery:\n  remaining: Залишилося {{0}}%.\n  smart_charge: Smart Charge\nbluetooth:\n  available: Доступні пристрої (не в парі)\n  cancel: Скасувати\n  connected: Підключені пристрої\n  forget: Забудь.\n  lowenergy: Низька енергія\n  more: Більше налаштувань Bluetooth\n  not_found: Пристроїв Bluetooth не знайдено\n  pair: Пара\n  paired: Збережені пристрої\n  placeholder:\n    passphrase: Пароль, код або PIN-код\n  scanning: Сканування на наявність пристроїв\ncontext_menu:\n  add_custom_text: Додати власний текстовий елемент\n  modules: Модулі\n  remove: Видалити модуль\n  reorder_disable: Заблокувати панель інструментів\n  reorder_enable: Розблокувати панель інструментів\n  restore: Відновити за замовчуванням\n  settings: Налаштування\n  task_manager: Диспетчер задач\nmedia:\n  default_multimedia_volume: Мультимедійна гучність\n  device:\n    channel:\n      system: Звуки системи\n    comunications: Комунікація\n    missing: Відсутній пристрій\n    mixer: Змішувач гучності\n    multimedia: Мультимедіа\n    settings: Більше налаштувань пристрою\n    volume: Гучність пристрою\n  input_devices: Пристрої введення\n  output_devices: Пристрої виводу\n  players: Медіаплеєри\nnotifications:\n  clear: Зачистити все.\n  empty: Немає сповіщень\n  settings: Більше налаштувань сповіщень\n  title: Сповіщення\nplaceholder:\n  bluetooth_devices: Bluetooth та пристрої\n  ethernet_connected: Доступ в інтернет\n  ethernet_disconnected: Немає доступу до Інтернету\n  notifications: Сповіщення\n  settings: Швидкі налаштування\n  volume: Гучність\nplugged: Підключено\nsettings:\n  app_settings: Налаштування додатку\n  brightness: Яскравість\n  power: Живлення\n  restart: Перезапустити\n  title: Налаштування\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/ur.yml",
    "content": "battery:\n  remaining: '{{0}} ٪ باقی'\n  smart_charge: سمارٹ چارج\nbluetooth:\n  available: دستیاب آلات (جوڑا نہیں)\n  cancel: منسوخ کریں\n  connected: منسلک آلات\n  forget: بھول جاؤ\n  lowenergy: کم توانائی\n  more: مزید بلوٹوتھ کی ترتیبات\n  not_found: کوئی بلوٹوتھ ڈیوائس نہیں ملا\n  pair: جوڑی\n  paired: محفوظ شدہ آلات\n  placeholder:\n    passphrase: پاسفریز ، کوڈ یا پن\n  scanning: آلات کے لئے اسکیننگ\ncontext_menu:\n  add_custom_text: کسٹم ٹیکسٹ آئٹم شامل کریں\n  modules: ماڈیولز\n  remove: ماڈیول کو ہٹا دیں\n  reorder_disable: لاک ٹول بار\n  reorder_enable: ٹول بار کو انلاک کریں\n  restore: ڈیفالٹ میں بحال کریں\n  settings: ترتیبات\n  task_manager: ٹاسک مینیجر\nmedia:\n  default_multimedia_volume: ملٹی میڈیا حجم\n  device:\n    channel:\n      system: سسٹم کی آوازیں\n    comunications: کمیونیکیشن\n    missing: لاپتہ آلہ\n    mixer: حجم مکسر\n    multimedia: ملٹی میڈیا\n    settings: مزید آلہ کی ترتیبات\n    volume: ڈیوائس کا حجم\n  input_devices: ان پٹ ڈیوائسز\n  output_devices: آؤٹ پٹ ڈیوائسز\n  players: میڈیا پلیئرز\nnotifications:\n  clear: سب کو صاف کریں\n  empty: کوئی اطلاعات نہیں\n  settings: مزید اطلاعات کی ترتیبات\n  title: اطلاعات\nplaceholder:\n  bluetooth_devices: بلوٹوتھ اور ڈیوائسز\n  ethernet_connected: ایتھر نیٹ کنیکٹڈ\n  ethernet_disconnected: ایتھر نیٹ ڈسکنیکٹ\n  notifications: اطلاعات\n  settings: سیٹٹنگ\n  volume: والیم\nplugged: پلگ\nsettings:\n  app_settings: ایپ کی ترتیبات\n  brightness: چمک\n  power: پاور\n  restart: ریسٹارٹ\n  title: عنوان\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/uz.yml",
    "content": "battery:\n  remaining: '{{0}}% qoldi'\n  smart_charge: Smart zaryadlash\nbluetooth:\n  available: Mavjud qurilmalar (biriktirilmagan)\n  cancel: Bekor qilmoq\n  connected: Ulangan qurilmalar\n  forget: Unutmoq\n  lowenergy: Kam energiya\n  more: Bluetooth sozlamalari\n  not_found: Bluetooth moslamalari topilmadi\n  pair: Juftlik\n  paired: Saqlangan qurilmalar\n  placeholder:\n    passphrase: Passprase, kod yoki PIN\n  scanning: Qurilmalar uchun skanerlash\ncontext_menu:\n  add_custom_text: Maxsus matn elementini qo'shing\n  modules: Modullar\n  remove: Modulni olib tashlash\n  reorder_disable: Asboblar paneli\n  reorder_enable: Asboblar panelini oching\n  restore: Odatiy holga qaytaring\n  settings: Sozlamalar\n  task_manager: Vazifa menejeri\nmedia:\n  default_multimedia_volume: Multimedia hajmi\n  device:\n    channel:\n      system: Tizim tovushlari\n    comunications: Aloqa\n    missing: Yo'qolgan qurilma\n    mixer: Mikroavtobus\n    multimedia: Multimedia\n    settings: Qo'shimcha qurilma sozlamalari\n    volume: Qurilma hajmi\n  input_devices: Kirish moslamalari\n  output_devices: Chiqish moslamalari\n  players: Media pleerlari\nnotifications:\n  clear: Hamma narsani tozalang\n  empty: Hech qanday bildirishnomalar yo'q\n  settings: Ko'proq xabarnomalar sozlamalari\n  title: Bildirishnoma\nplaceholder:\n  bluetooth_devices: Bluetooth va qurilmalar\n  ethernet_connected: Internetga ulanish\n  ethernet_disconnected: Internetga kirish yo'q\n  notifications: Bildirishnoma\n  settings: Tez sozlamalar\n  volume: Hajmi\nplugged: Ulangan\nsettings:\n  app_settings: Ilova sozlamalari\n  brightness: Yorqinlik\n  power: Kuch\n  restart: Qayta ishga tushirish\n  title: Sozlash\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/vi.yml",
    "content": "battery:\n  remaining: Còn lại {{0}}%\n  smart_charge: Sạc thông minh\nbluetooth:\n  available: Các thiết bị có sẵn (không được ghép nối)\n  cancel: Hủy bỏ\n  connected: Các thiết bị được kết nối\n  forget: Quên\n  lowenergy: Năng lượng thấp\n  more: Thêm cài đặt Bluetooth\n  not_found: Không tìm thấy thiết bị Bluetooth\n  pair: Đôi\n  paired: Thiết bị lưu\n  placeholder:\n    passphrase: Cụm mật khẩu, mã hoặc pin\n  scanning: Quét cho các thiết bị\ncontext_menu:\n  add_custom_text: Thêm mục văn bản tùy chỉnh\n  modules: Mô -đun\n  remove: Xóa mô -đun\n  reorder_disable: Thanh công cụ khóa\n  reorder_enable: Mở khóa thanh công cụ\n  restore: Khôi phục về mặc định\n  settings: Cài đặt\n  task_manager: Trình quản lý tác vụ\nmedia:\n  default_multimedia_volume: Khối lượng đa phương tiện\n  device:\n    channel:\n      system: Âm thanh hệ thống\n    comunications: Truyền thông\n    missing: Thiếu thiết bị\n    mixer: Máy trộn khối lượng\n    multimedia: Đa phương tiện\n    settings: Nhiều cài đặt thiết bị hơn\n    volume: Khối lượng thiết bị\n  input_devices: Thiết bị đầu vào\n  output_devices: Thiết bị đầu ra\n  players: Người chơi truyền thông\nnotifications:\n  clear: Rõ ràng tất cả\n  empty: Không có thông báo\n  settings: Nhiều cài đặt thông báo\n  title: Thông báo\nplaceholder:\n  bluetooth_devices: Bluetooth & thiết bị\n  ethernet_connected: truy cập Internet\n  ethernet_disconnected: Không có kết nối mạng\n  notifications: Thông báo\n  settings: Cài đặt nhanh\n  volume: Âm lượng\nplugged: Đã cắm\nsettings:\n  app_settings: Cài đặt ứng dụng\n  brightness: Độ sáng\n  power: Quyền lực\n  restart: Khởi động lại\n  title: Cài đặt\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/yo.yml",
    "content": "battery:\n  remaining: '{{0}}% to ku'\n  smart_charge: Smart idiyele\nbluetooth:\n  available: Awọn ẹrọ ti o wa (ko so pọ)\n  cancel: Fagilee\n  connected: Awọn ẹrọ ti o sopọ mọ\n  forget: Gbagbe\n  lowenergy: Agbara kekere\n  more: Awọn eto Bluetooth diẹ sii\n  not_found: Ko si awọn ẹrọ Bluetooth naa\n  pair: Meji\n  paired: Awọn ẹrọ ti o fipamọ\n  placeholder:\n    passphrase: Ọrọ kukuru, koodu tabi PIN\n  scanning: Sisọnu fun awọn ẹrọ\ncontext_menu:\n  add_custom_text: Ṣafikun Nkan Nkan Nkan\n  modules: Awọn modulu\n  remove: Yọ module\n  reorder_disable: Ọpa irinṣẹ Titiipa\n  reorder_enable: Ṣii bọtini irinṣẹ\n  restore: Mu pada si aiyipada\n  settings: Eto\n  task_manager: Oluṣakoso iṣẹ-ṣiṣe\nmedia:\n  default_multimedia_volume: Iwọn didun Multimedia\n  device:\n    channel:\n      system: Awọn ohun eto\n    comunications: Awọn ibaraẹnisọrọ\n    missing: Ẹrọ sonu\n    mixer: Ipọpọ iwọn didun\n    multimedia: Multimedia\n    settings: Eto ẹrọ diẹ sii\n    volume: Iwọn didun ẹrọ\n  input_devices: Ẹrọ titẹ sii\n  output_devices: Awọn ẹrọ iṣelọpọ\n  players: Awọn oṣere Media\nnotifications:\n  clear: Pa gbogbo run\n  empty: Ko si awọn iwifunni\n  settings: Awọn ifiranṣẹ iwifunni diẹ sii\n  title: Awọn iwifunni\nplaceholder:\n  bluetooth_devices: Bluetooth & awọn ẹrọ\n  ethernet_connected: Wiwọle Intanẹẹti\n  ethernet_disconnected: Ko si wiwọle intanẹẹti ko si\n  notifications: Awọn iwifunni\n  settings: Eto Awọn ọna\n  volume: Iwọn didun\nplugged: Ti so pọ\nsettings:\n  app_settings: Eto App\n  brightness: Didan\n  power: Agbara\n  restart: Tun bẹrẹ\n  title: Ètò\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/zh-CN.yml",
    "content": "battery:\n  remaining: 剩余 {{0}}%\n  smart_charge: 智能充电\nbluetooth:\n  available: 可用设备（未配对）\n  cancel: 取消\n  connected: 已连接设备\n  forget: 忘记设备\n  lowenergy: 低功耗模式\n  more: 更多蓝牙设置\n  not_found: 未找到蓝牙设备\n  pair: 配对\n  paired: 已配对设备\n  placeholder:\n    passphrase: 密码、代码或 PIN\n  scanning: 正在搜索设备\ncontext_menu:\n  add_custom_text: 添加自定义文本项\n  modules: 模块\n  remove: 移除模块\n  reorder_disable: 锁定花式工具栏\n  reorder_enable: 解锁花式工具栏\n  restore: 恢复默认值\n  settings: 设置\n  task_manager: 任务管理器\nmedia:\n  default_multimedia_volume: 多媒体音量\n  device:\n    channel:\n      system: 系统声音\n    comunications: 通信\n    missing: 设备缺失\n    mixer: 音量混合器\n    multimedia: 多媒体\n    settings: 更多设备设置\n    volume: 设备音量\n  input_devices: 输入设备\n  output_devices: 输出设备\n  players: 媒体播放器\nnotifications:\n  clear: 全部清除\n  empty: 无通知\n  settings: 更多通知设置\n  title: 通知\nplaceholder:\n  bluetooth_devices: 蓝牙与设备\n  ethernet_connected: 已连接互联网\n  ethernet_disconnected: 无互联网连接\n  notifications: 通知\n  settings: 快速设置\n  volume: 音量\nplugged: 堵塞\nsettings:\n  app_settings: 应用设置\n  brightness: 亮度\n  power: 电源\n  restart: 重启\n  title: 设置\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/zh-TW.yml",
    "content": "battery:\n  remaining: 剩餘 {{0}}%\n  smart_charge: 智能充電\nbluetooth:\n  available: 可用設備（未配對）\n  cancel: 取消\n  connected: 連接的設備\n  forget: 忘記\n  lowenergy: 低能量\n  more: 更多藍牙設置\n  not_found: 找不到藍牙設備\n  pair: 一對\n  paired: 保存的設備\n  placeholder:\n    passphrase: 密碼，代碼或PIN\n  scanning: 掃描設備\ncontext_menu:\n  add_custom_text: 添加自定義文本項目\n  modules: 模塊\n  remove: 刪除模塊\n  reorder_disable: 鎖工具欄\n  reorder_enable: 解鎖工具欄\n  restore: 還原為默認\n  settings: 設定\n  task_manager: 工作管理員\nmedia:\n  default_multimedia_volume: 多媒體體積\n  device:\n    channel:\n      system: 系統聲音\n    comunications: 通訊\n    missing: 缺少設備\n    mixer: 音量混合器\n    multimedia: 多媒體\n    settings: 更多設備設置\n    volume: 設備音量\n  input_devices: 輸入設備\n  output_devices: 輸出設備\n  players: 媒體播放器\nnotifications:\n  clear: 清除全部\n  empty: 沒有通知\n  settings: 更多的通知設置\n  title: 通知\nplaceholder:\n  bluetooth_devices: 藍牙和設備\n  ethernet_connected: 互聯網訪問\n  ethernet_disconnected: 沒有互聯網訪問\n  notifications: 通知\n  settings: 快速設置\n  volume: 體積\nplugged: 堵塞\nsettings:\n  app_settings: 應用設置\n  brightness: 亮度\n  power: 力量\n  restart: 重新啟動\n  title: 設定\n"
  },
  {
    "path": "src/ui/react/toolbar/i18n/translations/zu.yml",
    "content": "battery:\n  remaining: '{{0}}% Okusele'\n  smart_charge: Ukushaja okuhlakaniphile\nbluetooth:\n  available: Amadivayisi atholakalayo (angabhangqwa)\n  cancel: Hlikihla\n  connected: Amadivayisi axhunyiwe\n  forget: Khohlwa\n  lowenergy: Amandla aphansi\n  more: Izilungiselelo eziningi ze-Bluetooth\n  not_found: Awekho amadivaysi e-Bluetooth atholakele\n  pair: Okubili\n  paired: Amadivayisi alondoloziwe\n  placeholder:\n    passphrase: I-Passphase, ikhodi noma iPIN\n  scanning: Ukuskena kwamadivayisi\ncontext_menu:\n  add_custom_text: Engeza into yombhalo wangokwezifiso\n  modules: Amashumane\n  remove: Susa module\n  reorder_disable: Khiya i-Toolbar\n  reorder_enable: Vula ithuluzi lamathuluzi\n  restore: Buyisela ezenzakalelayo\n  settings: Izilungiselelo\n  task_manager: Umphathi Womsebenzi\nmedia:\n  default_multimedia_volume: Ivolumu yemultimedia\n  device:\n    channel:\n      system: Umsindo wesistimu\n    comunications: Ukuxoxisana\n    missing: Idivayisi elahlekile\n    mixer: I-Movolur Mixer\n    multimedia: Multimedia\n    settings: Izilungiselelo Eziningi Zedivayisi\n    volume: Ivolumu yedivayisi\n  input_devices: Amadivayisi wokufaka\n  output_devices: Amadivayisi okukhipha\n  players: Abadlali beMedia\nnotifications:\n  clear: Sula konke\n  empty: Azikho izaziso\n  settings: Izilungiselelo zezaziso eziningi\n  title: Izaziso\nplaceholder:\n  bluetooth_devices: I-Bluetooth & Amadivayisi\n  ethernet_connected: Ukufinyelela i-Inthanethi\n  ethernet_disconnected: Akukho ukufinyelela kwe-inthanethi\n  notifications: Izaziso\n  settings: Izilungiselelo ezisheshayo\n  volume: Ukuvimbanisa\nplugged: Kuxhunyiwe\nsettings:\n  app_settings: Izilungiselelo zohlelo lokusebenza\n  brightness: Ukugqama\n  power: Ubuqhwaga\n  restart: Qalanisa\n  title: Amasethingi\n"
  },
  {
    "path": "src/ui/react/toolbar/index.tsx",
    "content": "import { getRootContainer } from \"libs/ui/react/utils/index.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { I18nextProvider } from \"react-i18next\";\n\nimport { App } from \"./app.tsx\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport i18n, { loadTranslations } from \"./i18n/index.ts\";\n\nimport \"@shared/styles/colors.css\";\nimport \"./styles/variables.css\";\nimport \"@shared/styles/reset.css\";\nimport \"./styles/global.css\";\n\nawait loadTranslations();\n\nawait Widget.getCurrent().init();\n\nconst container = getRootContainer();\ncreateRoot(container).render(\n  <I18nextProvider i18n={i18n}>\n    <App />\n  </I18nextProvider>,\n);\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/app/actionEvaluator.ts",
    "content": "import Sandbox from \"@nyariv/sandboxjs\";\nimport { SeelenCommand, SeelenEvent } from \"@seelen-ui/lib\";\nimport { invoke } from \"@tauri-apps/api/core\";\nimport { emit, emitTo } from \"@tauri-apps/api/event\";\n\nconst ActionsScope = {\n  SeelenCommand,\n  SeelenEvent,\n  invoke(command: SeelenCommand, args?: any) {\n    invoke(command, args);\n  },\n  emit(event: SeelenEvent, payload?: unknown) {\n    emit(event, payload);\n  },\n  emitTo(target: string, event: SeelenEvent, payload?: unknown) {\n    emitTo(target, event, payload);\n  },\n  // ==================== abstractions ====================\n  open(path: string) {\n    invoke(SeelenCommand.OpenFile, { path });\n  },\n  run(\n    program: string,\n    args: string[] | null = null,\n    workingDir: string | null = null,\n    elevated: boolean = false,\n  ) {\n    invoke(SeelenCommand.Run, { program, args, workingDir, elevated });\n  },\n};\n\nexport async function EvaluateAction(code: string, scope: Record<string, any>) {\n  const sandbox = new Sandbox();\n  const executor = sandbox.compileAsync(code);\n  await executor({ ...scope, ...ActionsScope }).run();\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/app/hooks/scope.ts",
    "content": "import { useComputed } from \"@preact/signals\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { ToolbarJsScope } from \"@seelen-ui/lib/types\";\nimport type { WidgetId } from \"@seelen-ui/lib/types\";\nimport { useSyncClockInterval, useThrottle } from \"libs/ui/react/utils/hooks\";\nimport moment from \"moment\";\nimport { useEffect, useState } from \"preact/hooks\";\nimport { useTranslation } from \"react-i18next\";\nimport { $allByWidget, $settings } from \"../../../shared/state/mod\";\nimport { $virtual_desktop } from \"../../../shared/state/system\";\nimport { $focused } from \"../../../shared/state/windows\";\nimport {\n  useLazyBatteries,\n  useLazyBluetoothDevices,\n  useLazyCores,\n  useLazyDisks,\n  useLazyLanguages,\n  useLazyMediaDevices,\n  useLazyMediaSessions,\n  useLazyMemory,\n  useLazyNetworkAdapters,\n  useLazyNetworkLocalIp,\n  useLazyNetworkStatistics,\n  useLazyNotifications,\n  useLazyOnline,\n  useLazyPowerMode,\n  useLazyPowerStatus,\n  useLazyUser,\n} from \"../../../shared/state/lazy\";\n\nexport function useItemScope(scopes: Readonly<ToolbarJsScope[]>) {\n  let fetching = false;\n  const scope = {} as Record<any, any>;\n\n  if (scopes.includes(ToolbarJsScope.Date)) {\n    const dateScope = useDateScope();\n    if (dateScope.fetching) fetching = true;\n    if (dateScope.data) Object.assign(scope, dateScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Notifications)) {\n    const notificationsScope = useNotificationsScope();\n    if (notificationsScope.fetching) fetching = true;\n    if (notificationsScope.data) Object.assign(scope, notificationsScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Media)) {\n    const mediaScope = useMediaScope();\n    if (mediaScope.fetching) fetching = true;\n    if (mediaScope.data) Object.assign(scope, mediaScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Network)) {\n    const networkScope = useNetworkScope();\n    if (networkScope.fetching) fetching = true;\n    if (networkScope.data) Object.assign(scope, networkScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Keyboard)) {\n    const keyboardScope = useKeyboardScope();\n    if (keyboardScope.fetching) fetching = true;\n    if (keyboardScope.data) Object.assign(scope, keyboardScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.User)) {\n    const userScope = useUserScope();\n    if (userScope.fetching) fetching = true;\n    if (userScope.data) Object.assign(scope, userScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Bluetooth)) {\n    const bluetoothScope = useBluetoothScope();\n    if (bluetoothScope.fetching) fetching = true;\n    if (bluetoothScope.data) Object.assign(scope, bluetoothScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Power)) {\n    const powerScope = usePowerScope();\n    if (powerScope.fetching) fetching = true;\n    if (powerScope.data) Object.assign(scope, powerScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.FocusedApp)) {\n    const focusedAppScope = useFocusedAppScope();\n    if (focusedAppScope.fetching) fetching = true;\n    if (focusedAppScope.data) Object.assign(scope, focusedAppScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Workspaces)) {\n    const workspacesScope = useWorkspacesScope();\n    if (workspacesScope.fetching) fetching = true;\n    if (workspacesScope.data) Object.assign(scope, workspacesScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Disk)) {\n    const diskScope = useDiskScope();\n    if (diskScope.fetching) fetching = true;\n    if (diskScope.data) Object.assign(scope, diskScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.NetworkStatistics)) {\n    const networkStatisticsScope = useNetworkStatisticsScope();\n    if (networkStatisticsScope.fetching) fetching = true;\n    if (networkStatisticsScope.data) Object.assign(scope, networkStatisticsScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Memory)) {\n    const memoryScope = useMemoryScope();\n    if (memoryScope.fetching) fetching = true;\n    if (memoryScope.data) Object.assign(scope, memoryScope.data);\n  }\n\n  if (scopes.includes(ToolbarJsScope.Cpu)) {\n    const cpuScope = useCpuScope();\n    if (cpuScope.fetching) fetching = true;\n    if (cpuScope.data) Object.assign(scope, cpuScope.data);\n  }\n\n  return { fetching, data: scope };\n}\n\nfunction useDateScope() {\n  const momentJsLangMap: { [key: string]: string } = {\n    no: \"nb\",\n    zh: \"zh-cn\",\n  };\n\n  const $date_format = useComputed(() => $settings.value.dateFormat);\n\n  const {\n    i18n: { language: lang },\n  } = useTranslation();\n  let language = momentJsLangMap[lang] || lang;\n\n  const [date, setDate] = useState(moment().locale(language).format($date_format.value));\n\n  // inmediately update the date, like interval is reseted on deps change\n  useEffect(() => {\n    setDate(moment().locale(language).format($date_format.value));\n  }, [$date_format.value, language]);\n\n  useSyncClockInterval(\n    () => {\n      setDate(moment().locale(language).format($date_format.value));\n    },\n    $date_format.value.includes(\"ss\") ? \"seconds\" : \"minutes\",\n    [$date_format.value, language],\n  );\n\n  return {\n    fetching: false,\n    data: { date },\n  };\n}\n\nfunction useNotificationsScope() {\n  const { fetching, data: notifications } = useLazyNotifications();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  return {\n    fetching: false,\n    data: { count: notifications?.length || 0 },\n  };\n}\n\nfunction useMediaScope() {\n  const { fetching: fetchingDevices, data: mediaDevices } = useLazyMediaDevices();\n  const { fetching: fetchingSessions, data: mediaSessions } = useLazyMediaSessions();\n\n  if (fetchingDevices || fetchingSessions) {\n    return { fetching: true, data: null };\n  }\n\n  const [mediaInputs, mediaOutputs] = mediaDevices!;\n\n  const defaultOutputDevice = mediaOutputs.find((d: any) => d.isDefaultMultimedia);\n  const defaultInputDevice = mediaInputs.find((d: any) => d.isDefaultMultimedia);\n  const defaultMediaSession = mediaSessions!.find((d: any) => d.default);\n\n  const { id, volume = 0, muted: isMuted = true } = defaultOutputDevice || {};\n\n  const { volume: inputVolume = 0, muted: inputIsMuted = true } = defaultInputDevice || {};\n\n  const mediaSession = defaultMediaSession || null;\n\n  function onWheel(e: WheelEvent) {\n    const isUp = e.deltaY < 0;\n    const level = Math.max(0, Math.min(1, volume + (isUp ? 0.02 : -0.02)));\n    if (id) {\n      invoke(SeelenCommand.SetVolumeLevel, {\n        deviceId: id,\n        level,\n        sessionId: null,\n      });\n    }\n  }\n\n  return {\n    fetching: false,\n    data: {\n      volume,\n      isMuted,\n      inputVolume,\n      inputIsMuted,\n      mediaSession,\n      onWheel,\n    },\n  };\n}\n\nfunction useNetworkScope() {\n  const { fetching: fetchingOnline, data: online } = useLazyOnline();\n  const { fetching: fetchingInterfaces, data: interfaces } = useLazyNetworkAdapters();\n  const { fetching: fetchingIp, data: defaultIp } = useLazyNetworkLocalIp();\n\n  if (fetchingOnline || fetchingInterfaces || fetchingIp) {\n    return { fetching: true, data: null };\n  }\n\n  const usingInterface = interfaces?.find((i: any) => i.ipv4 === defaultIp) || null;\n\n  return {\n    fetching: false,\n    data: {\n      online,\n      interfaces,\n      usingInterface,\n    },\n  };\n}\n\nfunction useKeyboardScope() {\n  const { fetching, data: languages } = useLazyLanguages();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  const activeLang = languages?.find((l: any) => l.keyboardLayouts.some((k: any) => k.active)) || languages?.[0];\n  const activeKeyboard = activeLang?.keyboardLayouts.find((k: any) => k.active) || activeLang?.keyboardLayouts[0];\n\n  let activeLangPrefix = activeLang?.nativeName\n    ?.split(\"\")\n    .slice(0, 3)\n    .filter((c: any) => ![\"(\", \")\", \" \"].includes(c))\n    .join(\"\")\n    .toLocaleUpperCase() || \"\";\n\n  let words = activeKeyboard?.displayName?.split(/[\\s\\-\\(\\)]/) || [];\n  let activeKeyboardPrefix = words.length > 1\n    ? words\n      .map((word: any) => word[0])\n      .join(\"\")\n      .toLocaleUpperCase()\n    : words[0]?.slice(0, 3).toLocaleUpperCase() || \"\";\n\n  return {\n    fetching: false,\n    data: {\n      activeLang,\n      activeKeyboard,\n      activeLangPrefix,\n      activeKeyboardPrefix,\n      languages,\n    },\n  };\n}\n\nfunction useUserScope() {\n  const { fetching, data: user } = useLazyUser();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  const userMenuConfig = $allByWidget.value?.[\"@seelen/user-menu\" as WidgetId];\n  const source = (userMenuConfig as Record<string, unknown> | undefined)?.displayNameSource;\n  const displayName = source === \"xboxGamertag\" && user?.xboxGamertag ? user.xboxGamertag : user?.name;\n\n  return {\n    fetching: false,\n    data: {\n      user: { ...user, displayName },\n    },\n  };\n}\n\nfunction useBluetoothScope() {\n  const { fetching, data: bluetoothDevices } = useLazyBluetoothDevices();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  const connectedDevices = bluetoothDevices?.filter((item: any) => item.connected) || [];\n\n  return {\n    fetching: false,\n    data: {\n      devices: bluetoothDevices,\n      connectedDevices,\n    },\n  };\n}\n\nfunction usePowerScope() {\n  const { fetching: fetchingPower, data: power } = useLazyPowerStatus();\n  const { fetching: fetchingMode, data: powerMode } = useLazyPowerMode();\n  const { fetching: fetchingBatteries, data: batteries } = useLazyBatteries();\n\n  if (fetchingPower || fetchingMode || fetchingBatteries) {\n    return { fetching: true, data: null };\n  }\n\n  return {\n    fetching: false,\n    data: {\n      power,\n      powerMode,\n      batteries,\n    },\n  };\n}\n\nfunction useFocusedAppScope() {\n  return {\n    fetching: false,\n    data: {\n      focusedApp: $focused.value,\n    },\n  };\n}\n\nfunction useWorkspacesScope() {\n  const workspaces = $virtual_desktop.value?.workspaces || [];\n  const activeWorkspace = $virtual_desktop.value?.active_workspace;\n\n  const onWheel = useThrottle(\n    (isUp: boolean) => {\n      const index = workspaces.findIndex((w: any) => w.id === activeWorkspace);\n      const newIndex = isUp ? index - 1 : index + 1;\n      if (newIndex >= 0 && newIndex < workspaces.length) {\n        let workspace = workspaces[newIndex]!;\n        invoke(SeelenCommand.SwitchWorkspace, { workspaceId: workspace.id });\n      }\n    },\n    500,\n    { trailing: false },\n  );\n\n  return {\n    fetching: false,\n    data: {\n      workspaces,\n      activeWorkspace,\n      onWheel,\n    },\n  };\n}\n\nfunction useDiskScope() {\n  const { fetching, data: disks } = useLazyDisks();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  return {\n    fetching: false,\n    data: { disks },\n  };\n}\n\nfunction useNetworkStatisticsScope() {\n  const { fetching, data: networkStatistics } = useLazyNetworkStatistics();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  return {\n    fetching: false,\n    data: { networkStatistics },\n  };\n}\n\nfunction useMemoryScope() {\n  const { fetching, data: memory } = useLazyMemory();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  return {\n    fetching: false,\n    data: { memory },\n  };\n}\n\nfunction useCpuScope() {\n  const { fetching, data: cores } = useLazyCores();\n\n  if (fetching) {\n    return { fetching: true, data: null };\n  }\n\n  return {\n    fetching: false,\n    data: { cores },\n  };\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/app/hooks/useItemScope.ts",
    "content": "import type { WidgetId } from \"@seelen-ui/lib/types\";\nimport { useMemo, useRef } from \"preact/compat\";\nimport { useTranslation } from \"react-i18next\";\nimport { triggerWidget } from \"../services/widgetTrigger.ts\";\nimport type { ItemScopeOptions } from \"../../domain/types.ts\";\n\n/**\n * Hook to manage the scope object for toolbar item evaluation.\n * Combines environment variables, translations, extra vars, and fetched data.\n * Optimized to consolidate scope creation and reduce re-computation.\n * @param options - Configuration options for the scope\n * @returns The scope object with all combined data\n */\nexport function useFullItemScope(options: ItemScopeOptions) {\n  const { itemId, extraVars = {}, fetchedData = {} } = options;\n  const { t } = useTranslation();\n\n  // Store the trigger function in a ref to keep it stable\n  const triggerRef = useRef<(widgetId: WidgetId) => void>();\n\n  // Update trigger function only when itemId changes\n  if (!triggerRef.current || triggerRef.current.toString() !== itemId) {\n    triggerRef.current = (widgetId: WidgetId) => triggerWidget(widgetId, itemId);\n  }\n\n  // Memoize the scope object, only recomputing when t or itemId changes\n  // extraVars and fetchedData are handled by deep comparison\n  const scope = useMemo(() => ({\n    t,\n    trigger: triggerRef.current!,\n  }), [t, itemId]);\n\n  // Use deep comparison for extraVars and fetchedData\n  // Merge them into the scope in a stable way\n  return useMemo(() => ({\n    ...extraVars,\n    ...fetchedData,\n    ...scope,\n  }), [extraVars, fetchedData, scope]);\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/app/hooks/useRemoteData.ts",
    "content": "import type { RemoteDataDeclaration } from \"@seelen-ui/lib/types\";\nimport { useDeepCompareEffect } from \"libs/ui/react/utils/hooks\";\nimport { useRef, useState } from \"preact/compat\";\n\n/**\n * Hook to fetch and manage remote data with optional periodic updates.\n * @param remoteData - Object containing remote data declarations\n * @returns Object with fetched data for each key\n */\nexport function useRemoteData(\n  remoteData: Record<string, RemoteDataDeclaration | undefined>,\n) {\n  const [state, setState] = useState<Record<string, any>>(() => {\n    return Object.keys(remoteData).reduce((acc, key) => ({ ...acc, [key]: undefined }), {});\n  });\n\n  const intervalsRef = useRef<Record<string, ReturnType<typeof setInterval>>>({});\n  const mountedRef = useRef(true);\n\n  const fetchData = async (key: string, rd: RemoteDataDeclaration): Promise<void> => {\n    if (!mountedRef.current) return;\n\n    try {\n      const response = await fetch(rd.url, rd.requestInit as RequestInit);\n      const data = response.headers.get(\"Content-Type\")?.includes(\"application/json\")\n        ? await response.json()\n        : await response.text();\n\n      if (mountedRef.current) {\n        setState((prev) => ({\n          ...prev,\n          [key]: data,\n        }));\n      }\n    } catch (error) {\n      console.error(`Error fetching ${key}:`, error);\n    }\n  };\n\n  useDeepCompareEffect(() => {\n    mountedRef.current = true;\n    Object.values(intervalsRef.current).forEach(clearInterval);\n    intervalsRef.current = {};\n\n    const initialState = Object.keys(remoteData).reduce(\n      (acc, key) => ({ ...acc, [key]: undefined }),\n      {},\n    );\n\n    setState((prev) => ({ ...initialState, ...prev }));\n\n    Object.entries(remoteData).forEach(([key, rd]) => {\n      if (!rd) return;\n      fetchData(key, rd);\n      if (rd.updateIntervalSeconds) {\n        intervalsRef.current[key] = globalThis.setInterval(\n          () => fetchData(key, rd),\n          rd.updateIntervalSeconds * 1000,\n        );\n      }\n    });\n\n    return () => {\n      mountedRef.current = false;\n      Object.values(intervalsRef.current).forEach(clearInterval);\n    };\n  }, [remoteData]);\n\n  return state;\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/app/services/index.ts",
    "content": "/**\n * Application layer - Services\n * Business logic services for toolbar item functionality\n */\n\nexport { triggerWidget } from \"./widgetTrigger.ts\";\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/app/services/widgetTrigger.ts",
    "content": "import { Alignment, FancyToolbarSide, type WidgetId } from \"@seelen-ui/lib/types\";\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport { toPhysicalPixels } from \"libs/ui/react/utils/index.ts\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { $settings } from \"../../../shared/state/mod.ts\";\n\n/**\n * Triggers a widget at a calculated position relative to the toolbar item.\n * @param widgetId - The ID of the widget to trigger\n * @param itemId - The ID of the toolbar item element\n */\nexport async function triggerWidget(widgetId: WidgetId, itemId: string): Promise<void> {\n  if (typeof widgetId !== \"string\") {\n    return;\n  }\n\n  const { x: windowX, y: windowY } = await getCurrentWindow().outerPosition();\n\n  // Get position of the element on the screen\n  const element = document.getElementById(itemId);\n  if (!element) {\n    console.error(`Element with id ${itemId} not found`);\n    return;\n  }\n\n  const domRect = element.getBoundingClientRect();\n  const x = windowX + toPhysicalPixels(domRect.left + domRect.width / 2);\n\n  const rootRect = document.getElementById(\"root\")!.getBoundingClientRect();\n  const isTopPosition = $settings.value.position === FancyToolbarSide.Top;\n\n  const y = isTopPosition ? windowY + toPhysicalPixels(rootRect.bottom) : windowY + toPhysicalPixels(rootRect.top);\n\n  await invoke(SeelenCommand.TriggerWidget, {\n    payload: {\n      id: widgetId,\n      desiredPosition: { x, y },\n      alignX: Alignment.Center,\n      alignY: isTopPosition ? Alignment.Start : Alignment.End,\n    },\n  });\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/domain/index.ts",
    "content": "/**\n * Domain layer\n * Pure business logic types and interfaces\n */\n\nexport type { ItemScopeOptions } from \"./types.ts\";\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/domain/types.ts",
    "content": "/**\n * Domain types for the toolbar item module.\n * These types represent pure business logic without infrastructure dependencies.\n */\n\n/**\n * Configuration options for the item scope.\n * Defines the data structure needed to create a scope for item evaluation.\n */\nexport interface ItemScopeOptions {\n  itemId: string;\n  extraVars?: Record<string, any>;\n  fetchedData?: Record<string, any>;\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/infra/ContextMenu.tsx",
    "content": "import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport { Alignment, type ContextMenu, FancyToolbarSide } from \"@seelen-ui/lib/types\";\n\nimport { $actions } from \"../../shared/state/items.ts\";\nimport { $settings } from \"../../shared/state/mod.ts\";\nimport { useTranslation } from \"react-i18next\";\nimport { useCallback, useEffect, useMemo } from \"preact/hooks\";\n\nconst identifier = crypto.randomUUID();\n\nexport function useItemContextMenu(itemId: string) {\n  const { t } = useTranslation();\n\n  // Memoize callbackEvent since it only depends on itemId\n  const callbackEvent = useMemo(\n    () => `context-menu::${itemId.replace(\"@\", \"\")}`,\n    [itemId],\n  );\n\n  useEffect(() => {\n    const unlistener = Widget.self.webview.listen(callbackEvent, ({ payload }) => {\n      const { key } = payload as any;\n      if (key === \"remove\") {\n        $actions.removeItem(itemId);\n      }\n    });\n\n    return () => {\n      unlistener.then((cb) => cb());\n    };\n  }, [itemId, callbackEvent]);\n\n  // Memoize the menu object to prevent reconstruction on every render\n  const menu: ContextMenu = useMemo(() => ({\n    identifier,\n    items: [\n      {\n        type: \"Item\",\n        key: \"remove\",\n        label: t(\"context_menu.remove\"),\n        icon: \"CgExtensionRemove\",\n        callbackEvent,\n      },\n    ],\n  }), [t, callbackEvent]);\n\n  // Memoize the callback to prevent recreation\n  const onContextMenu = useCallback(() => {\n    const alignY = $settings.value.position === FancyToolbarSide.Bottom ? Alignment.End : Alignment.Start;\n    invoke(SeelenCommand.TriggerContextMenu, {\n      menu: { ...menu, alignX: Alignment.Center, alignY },\n      forwardTo: null,\n    });\n  }, [menu, $settings.value.position]);\n\n  return {\n    onContextMenu,\n  };\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/infra/EvaluatedComponents.tsx",
    "content": "import Sandbox from \"@nyariv/sandboxjs\";\nimport { FileIcon, Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { convertFileSrc } from \"@tauri-apps/api/core\";\nimport { memo, useCallback, useMemo } from \"preact/compat\";\nimport { z } from \"zod\";\n\nimport { EvaluateAction } from \"../app/actionEvaluator.ts\";\n\ninterface SanboxedComponentProps {\n  code: string;\n  scope: Record<string, any>;\n}\n\nenum ObjectComponentKind {\n  Icon = \"Icon\",\n  AppIcon = \"AppIcon\",\n  Image = \"Image\",\n  Button = \"Button\",\n  Group = \"Group\",\n}\n\nconst ComponentCreatorScope = {\n  icon: (arg1?: unknown, arg2?: unknown) => {\n    return EvaluatedReactIconPropsSchema.parse({\n      name: arg1,\n      size: arg2,\n    });\n  },\n  Icon: (arg: unknown) => EvaluatedReactIconPropsSchema.parse(arg),\n  AppIcon: (arg: unknown) => EvaluatedAppIconPropsSchema.parse(arg),\n  Image: (arg: unknown) => EvaluatedImagePropsSchema.parse(arg),\n  Button: (arg: unknown) => EvaluatedButtonPropsSchema.parse(arg),\n  Group: (arg: unknown) => EvaluatedGroupPropsSchema.parse(arg),\n};\n\n// Global cache for compiled code to avoid recompiling the same template\n// across multiple items or component re-mounts\nconst compiledCodeCache = new Map<string, ReturnType<typeof compileCodeInternal>>();\n\nfunction compileCodeInternal(code: string) {\n  try {\n    const sandbox = new Sandbox();\n    return {\n      sandbox,\n      executor: sandbox.compile(code),\n    };\n  } catch (e) {\n    console.error(\"Error compiling code: \", e);\n    return null;\n  }\n}\n\nfunction compileCode(code: string) {\n  if (compiledCodeCache.has(code)) {\n    return compiledCodeCache.get(code)!;\n  }\n\n  const result = compileCodeInternal(code);\n  compiledCodeCache.set(code, result);\n  return result;\n}\n\nexport function useSandboxedCode({ code, scope }: SanboxedComponentProps): unknown {\n  const compiled = useMemo(() => compileCode(code), [code]);\n\n  const computed = useMemo(() => {\n    if (!compiled) {\n      return null;\n    }\n\n    try {\n      return compiled?.executor({ ...scope, ...ComponentCreatorScope }).run();\n    } catch (error) {\n      const { env: _, ...rest } = scope;\n\n      console.error(\"Error while executing JS sandboxed code:\", {\n        error,\n        code,\n        scope: rest,\n      });\n      return null;\n    }\n  }, [compiled, scope]);\n\n  return computed;\n}\n\nexport function StringFromEvaluated({ content }: { content: unknown }): string {\n  switch (typeof content) {\n    case \"string\":\n      return content;\n    case \"number\":\n    case \"boolean\":\n    case \"bigint\":\n      return String(content);\n    case \"object\":\n      if (content === null) {\n        return \"\";\n      }\n\n      if (Array.isArray(content)) {\n        return content\n          .map((item: unknown) => {\n            return StringFromEvaluated({ content: item });\n          })\n          .join(\"\");\n      }\n\n      return \"\";\n    default:\n      return \"\";\n  }\n}\n\n// Memoized version to prevent re-renders when content hasn't changed\nexport const ElementsFromEvaluated = memo(function ElementsFromEvaluated({\n  content,\n}: {\n  content: unknown;\n}) {\n  // Use useMemo for array mapping to generate stable keys\n  const renderContent = useMemo(() => {\n    switch (typeof content) {\n      case \"string\":\n        return <span>{content}</span>;\n      case \"number\":\n      case \"boolean\":\n      case \"bigint\":\n        return <span>{String(content)}</span>;\n      case \"object\":\n        if (content === null) {\n          return null;\n        }\n\n        if (Array.isArray(content)) {\n          return content.map((item: unknown, index: number) => {\n            // Create a stable key based on content if possible\n            const key = typeof item === \"object\" && item !== null && \"@component\" in item\n              ? `${(item as any)[\"@component\"]}-${index}`\n              : index;\n            return <ElementsFromEvaluated key={key} content={item} />;\n          });\n        }\n\n        if (\"@component\" in content) {\n          switch (content[\"@component\"]) {\n            case ObjectComponentKind.Icon:\n              return <EvaluatedReactIcon {...EvaluatedReactIconPropsSchema.parse(content)} />;\n            case ObjectComponentKind.AppIcon:\n              return <EvaluatedAppIcon {...EvaluatedAppIconPropsSchema.parse(content)} />;\n            case ObjectComponentKind.Image:\n              return <EvaluatedImage {...EvaluatedImagePropsSchema.parse(content)} />;\n            case ObjectComponentKind.Button:\n              return <EvaluatedButton {...EvaluatedButtonPropsSchema.parse(content)} />;\n            case ObjectComponentKind.Group:\n              return <EvaluatedGroup {...EvaluatedGroupPropsSchema.parse(content)} />;\n            default:\n              return null;\n          }\n        }\n\n        return null;\n      default:\n        return null;\n    }\n  }, [content]);\n\n  return renderContent;\n});\n\ntype EvaluatedButtonProps = z.infer<typeof EvaluatedButtonPropsSchema>;\nconst EvaluatedButtonPropsSchema = z.object({\n  \"@component\": z.literal(ObjectComponentKind.Button).default(ObjectComponentKind.Button),\n  style: z.record(z.any()).default({}),\n  content: z.unknown().nullish(),\n  onClick: z.string().nullish(),\n});\nconst EvaluatedButton = memo(function EvaluatedButton({\n  style,\n  content,\n  onClick,\n}: EvaluatedButtonProps) {\n  const handleClick = useCallback(() => {\n    if (onClick) {\n      EvaluateAction(onClick, {});\n    }\n  }, [onClick]);\n\n  return (\n    <button data-skin=\"transparent\" style={style} onClick={handleClick}>\n      <ElementsFromEvaluated content={content} />\n    </button>\n  );\n});\n\ntype EvaluatedReactIconProps = z.infer<typeof EvaluatedReactIconPropsSchema>;\nconst EvaluatedReactIconPropsSchema = z.object({\n  \"@component\": z.literal(ObjectComponentKind.Icon).default(ObjectComponentKind.Icon),\n  name: z.string(),\n});\nconst EvaluatedReactIcon = memo(function EvaluatedReactIcon({ name }: EvaluatedReactIconProps) {\n  return <Icon iconName={name as any} />;\n});\n\ntype EvaluatedImageProps = z.infer<typeof EvaluatedImagePropsSchema>;\nconst EvaluatedImagePropsSchema = z.object({\n  \"@component\": z.literal(ObjectComponentKind.Image).default(ObjectComponentKind.Image),\n  url: z.string().nullish(),\n  path: z.string().nullish(),\n});\nconst EvaluatedImage = memo(function EvaluatedImage({ url, path }: EvaluatedImageProps) {\n  const imageSrc = useMemo(() => (path ? convertFileSrc(path) : url || \"\"), [path, url]);\n  return <img src={imageSrc} />;\n});\n\ntype EvaluatedAppIconProps = z.infer<typeof EvaluatedAppIconPropsSchema>;\nconst EvaluatedAppIconPropsSchema = z.object({\n  \"@component\": z.literal(ObjectComponentKind.AppIcon).default(ObjectComponentKind.AppIcon),\n  path: z.string().nullish(),\n  umid: z.string().nullish(),\n});\nconst EvaluatedAppIcon = memo(function EvaluatedAppIcon({ path, umid }: EvaluatedAppIconProps) {\n  return <FileIcon path={path} umid={umid} />;\n});\n\ntype EvaluatedGroupProps = z.infer<typeof EvaluatedGroupPropsSchema>;\nconst EvaluatedGroupPropsSchema = z.object({\n  \"@component\": z.literal(ObjectComponentKind.Group).default(ObjectComponentKind.Group),\n  style: z.record(z.any()).default({}),\n  content: z.unknown().nullish(),\n});\nconst EvaluatedGroup = memo(function EvaluatedGroup({ content, style }: EvaluatedGroupProps) {\n  return (\n    <div style={style}>\n      <ElementsFromEvaluated content={content} />\n    </div>\n  );\n});\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/item/infra/infra.tsx",
    "content": "import { useSortable } from \"@dnd-kit/react/sortable\";\nimport { computed } from \"@preact/signals\";\nimport type { ToolbarItem } from \"@seelen-ui/lib/types\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useCallback, useMemo } from \"preact/compat\";\nimport type { Ref } from \"preact\";\n\nimport { EvaluateAction } from \"../app/actionEvaluator.ts\";\nimport { $toolbar_state } from \"../../shared/state/items.ts\";\nimport { useItemContextMenu } from \"./ContextMenu.tsx\";\nimport { ElementsFromEvaluated, StringFromEvaluated, useSandboxedCode } from \"./EvaluatedComponents.tsx\";\nimport { useRemoteData } from \"../app/hooks/useRemoteData.ts\";\nimport { useFullItemScope } from \"../app/hooks/useItemScope.ts\";\nimport { useItemScope } from \"../app/hooks/scope.ts\";\nimport { RestrictToHorizontalAxis } from \"@dnd-kit/abstract/modifiers\";\n\nexport interface ItemProps {\n  module: ToolbarItem;\n  index: number;\n  group: string;\n}\n\ninterface InnerItemProps {\n  module: ToolbarItem;\n  extraVars: Record<string, unknown>;\n  nodeRef?: Ref<HTMLDivElement>;\n  isDragging?: boolean;\n}\n\ninterface SortableInnerItemProps extends ItemProps {\n  extraVars: Record<string, unknown>;\n}\n\n// Module-level computed signal (read only in the signal-reactive layer below)\nconst isReorderDisabled = computed(() => $toolbar_state.value.isReorderDisabled);\n\nfunction InnerItem({ module, extraVars, nodeRef, isDragging = false }: InnerItemProps) {\n  const { id, onClick, style, remoteData = {} } = module;\n\n  const fetchedData = useRemoteData(remoteData);\n  const { onContextMenu } = useItemContextMenu(id);\n\n  const scope = useFullItemScope({\n    itemId: id,\n    extraVars,\n    fetchedData,\n  });\n\n  const content = useSandboxedCode({ code: module.template, scope });\n  const tooltip = module.tooltip ? useSandboxedCode({ code: module.tooltip, scope }) : null;\n  const badge = module.badge ? useSandboxedCode({ code: module.badge, scope }) : null;\n\n  const handleClick = useCallback(() => {\n    if (onClick) {\n      EvaluateAction(onClick, scope);\n    }\n  }, [onClick, scope]);\n\n  const handleContextMenu = useCallback(\n    (e: MouseEvent) => {\n      e.stopPropagation();\n      onContextMenu();\n    },\n    [onContextMenu],\n  );\n\n  const itemStyle = {\n    ...style,\n    opacity: isDragging ? 0.3 : 1,\n  };\n\n  const itemTitle = useMemo(\n    () => (tooltip ? StringFromEvaluated({ content: tooltip }) : undefined),\n    [tooltip],\n  );\n\n  if (!content) {\n    return null;\n  }\n\n  return (\n    <div\n      id={id}\n      ref={nodeRef}\n      data-dragging={isDragging}\n      title={itemTitle}\n      style={itemStyle}\n      className={cx(\"ft-bar-item\", {\n        \"ft-bar-item-clickable\": onClick,\n      })}\n      onClick={handleClick}\n      onContextMenu={handleContextMenu}\n    >\n      <div className=\"ft-bar-item-content\">\n        <ElementsFromEvaluated content={content} />\n        {!!badge && (\n          <div className=\"ft-bar-item-badge\">\n            <ElementsFromEvaluated content={badge} />\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\nfunction SortableInnerItem({ module, index, group, extraVars }: SortableInnerItemProps) {\n  const { ref, isDragging } = useSortable({\n    id: module.id,\n    index,\n    type: \"item\",\n    accept: \"item\",\n    group,\n    disabled: isReorderDisabled.value,\n    modifiers: [RestrictToHorizontalAxis],\n  });\n\n  return (\n    <InnerItem\n      module={module}\n      extraVars={extraVars}\n      nodeRef={ref as Ref<HTMLDivElement>}\n      isDragging={isDragging}\n    />\n  );\n}\n\nexport function SortableItem({ module, index, group }: ItemProps) {\n  const { fetching, data: extraVars } = useItemScope(module.scopes);\n  if (fetching) {\n    return null;\n  }\n  return <SortableInnerItem module={module} index={index} group={group} extraVars={extraVars} />;\n}\n\nexport function Item({ module }: ItemProps) {\n  const { fetching, data: extraVars } = useItemScope(module.scopes);\n  if (fetching) {\n    return null;\n  }\n  return <InnerItem module={module} extraVars={extraVars} />;\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/main/ContextMenu.tsx",
    "content": "import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport type { ContextMenu, ContextMenuItem, PluginId, WidgetId } from \"@seelen-ui/lib/types\";\nimport { useMemo } from \"preact/compat\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { $actions, $plugins, $toolbar_state } from \"../shared/state/items.ts\";\nimport { getResourceText } from \"@shared\";\nimport { restoreStateToDefault } from \"../shared/state/default.ts\";\n\nconst identifier = crypto.randomUUID();\nconst modulesIdentifier = crypto.randomUUID();\n\nconst onContextMenuClick = \"onContextMenuClick\";\nconst onTogglePlugin = \"onTogglePlugin\";\n\nWidget.self.webview.listen(onContextMenuClick, ({ payload }) => {\n  const { key, checked: _checked } = payload as any;\n\n  if (key === \"reoder\") {\n    $toolbar_state.value = {\n      ...$toolbar_state.value,\n      isReorderDisabled: !$toolbar_state.value.isReorderDisabled,\n    };\n  }\n\n  if (key === \"task_manager\") {\n    invoke(SeelenCommand.OpenFile, { path: \"Taskmgr.exe\" });\n  }\n\n  if (key === \"settings\") {\n    invoke(SeelenCommand.TriggerWidget, {\n      payload: { id: \"@seelen/settings\" as WidgetId },\n    });\n  }\n\n  if (key === \"restore\") {\n    restoreStateToDefault();\n  }\n});\n\nWidget.self.webview.listen(onTogglePlugin, ({ payload }) => {\n  const { key: pluginId, checked } = payload as any;\n  if (checked) {\n    $actions.addItem(pluginId);\n  } else {\n    $actions.removeItem(pluginId);\n  }\n});\n\nexport function useMainContextMenu(): ContextMenu {\n  const {\n    t,\n    i18n: { language },\n  } = useTranslation();\n\n  // Memoize context menu to prevent reconstruction on every render\n  return useMemo(() => {\n    const allItems = [\n      ...$toolbar_state.value.left,\n      ...$toolbar_state.value.center,\n      ...$toolbar_state.value.right,\n    ];\n\n    function isAlreadyAdded(id: PluginId): boolean {\n      return allItems.some((item) => item === id);\n    }\n\n    return {\n      identifier,\n      items: [\n        {\n          type: \"Submenu\",\n          icon: \"CgExtensionAdd\",\n          label: t(\"context_menu.modules\"),\n          identifier: modulesIdentifier,\n          items: [\n            // restore\n            {\n              type: \"Item\",\n              key: \"restore\",\n              icon: \"TbRestore\",\n              label: t(\"context_menu.restore\"),\n              callbackEvent: onContextMenuClick,\n            },\n            {\n              type: \"Separator\",\n            },\n            ...$plugins.value\n              .map<Extract<ContextMenuItem, { type: \"Item\" }>>((plugin) => ({\n                type: \"Item\",\n                key: plugin.id,\n                label: getResourceText(plugin.metadata.displayName, language),\n                icon: plugin.icon,\n                callbackEvent: onTogglePlugin,\n                checked: isAlreadyAdded(plugin.id),\n              }))\n              .toSorted((p1, p2) => p1.label.localeCompare(p2.label)),\n          ],\n        },\n        {\n          type: \"Separator\",\n        },\n        {\n          type: \"Item\",\n          key: \"reoder\",\n          icon: $toolbar_state.value.isReorderDisabled ? \"VscUnlock\" : \"VscLock\",\n          label: t(\n            $toolbar_state.value.isReorderDisabled ? \"context_menu.reorder_enable\" : \"context_menu.reorder_disable\",\n          ),\n          callbackEvent: onContextMenuClick,\n        },\n        {\n          type: \"Item\",\n          key: \"task_manager\",\n          icon: \"PiChartLineFill\",\n          label: t(\"context_menu.task_manager\"),\n          callbackEvent: onContextMenuClick,\n          checked: null,\n          disabled: false,\n        },\n        {\n          type: \"Item\",\n          key: \"settings\",\n          icon: \"RiSettings4Fill\",\n          label: t(\"context_menu.settings\"),\n          callbackEvent: onContextMenuClick,\n        },\n      ],\n    };\n  }, [$toolbar_state.value, $plugins.value, t, language]);\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/main/CornerAction.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { memo, useCallback } from \"preact/compat\";\n\nexport const ShowDesktopButton = memo(function ShowDesktopButton() {\n  const handleClick = useCallback(() => {\n    invoke(SeelenCommand.ShowDesktop);\n  }, []);\n\n  return (\n    <button\n      className=\"ft-corner-button\"\n      onClick={handleClick}\n      title=\"Show Desktop\"\n      aria-label=\"Show Desktop\"\n    />\n  );\n});\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/main/ItemsContainer.tsx",
    "content": "import { useDroppable } from \"@dnd-kit/react\";\nimport type { ToolbarItem, ToolbarItem2 } from \"@seelen-ui/lib/types\";\nimport { memo } from \"preact/compat\";\nimport { computed } from \"@preact/signals\";\n\nimport { $plugins } from \"../shared/state/items.ts\";\nimport { SortableItem } from \"../item/infra/infra.tsx\";\nimport { isEqual } from \"lodash\";\nimport { CollisionPriority } from \"@dnd-kit/abstract\";\n\nconst plugins = computed(() => {\n  const dict: Record<string, ToolbarItem> = {};\n  for (const plugin of $plugins.value) {\n    dict[plugin.id] = plugin.plugin as ToolbarItem;\n  }\n  return dict;\n});\n\ninterface Props {\n  id: string;\n  items: ToolbarItem2[];\n}\n\nfunction GroupComponent({ id, items }: Props) {\n  const droppable = useDroppable({\n    id,\n    type: \"container\",\n    accept: \"item\",\n    collisionPriority: CollisionPriority.Low,\n  });\n\n  return (\n    <div ref={droppable.ref} className={`ft-bar-container ft-bar-${id}`} data-drop-target={droppable.isDropTarget}>\n      {items.map((entry, index) => {\n        let module: ToolbarItem | undefined;\n\n        if (typeof entry === \"string\") {\n          const cached = plugins.value[entry];\n          if (!cached) {\n            return null;\n          }\n\n          module = { ...cached, id: entry };\n        } else {\n          module = entry;\n        }\n\n        return <SortableItem key={module.id} module={module} index={index} group={id} />;\n      })}\n    </div>\n  );\n}\n\nexport const Group = memo(GroupComponent, (prevProps, nextProps) => {\n  return isEqual(prevProps, nextProps);\n});\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/main/Toolbar.tsx",
    "content": "import { DragDropProvider, DragOverlay } from \"@dnd-kit/react\";\nimport { move } from \"@dnd-kit/helpers\";\nimport { useComputed } from \"@preact/signals\";\nimport { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useCallback } from \"preact/compat\";\n\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport { $isDragging, $plugins, $toolbar_state } from \"../shared/state/items.ts\";\nimport { $settings } from \"../shared/state/mod.ts\";\nimport { Alignment, FancyToolbarSide } from \"@seelen-ui/lib/types\";\nimport { Group } from \"./ItemsContainer.tsx\";\nimport { Item } from \"../item/infra/infra.tsx\";\nimport { $hidden_by_autohide, $lastFocusedOnMonitor, $thereIsMaximizedOnBg } from \"../shared/state/windows.ts\";\nimport { ShowDesktopButton } from \"./CornerAction.tsx\";\nimport { useMainContextMenu } from \"./ContextMenu.tsx\";\nimport { matchIds } from \"../shared/utils.ts\";\n\nexport function FancyToolbar() {\n  const $containers = useComputed(() => ({\n    left: $toolbar_state.value.left,\n    center: $toolbar_state.value.center,\n    right: $toolbar_state.value.right,\n  }));\n\n  const contextMenuDef = useMainContextMenu();\n\n  const onContextMenu = useCallback(() => {\n    const alignY = $settings.value.position === FancyToolbarSide.Bottom ? Alignment.End : Alignment.Start;\n    invoke(SeelenCommand.TriggerContextMenu, {\n      menu: { ...contextMenuDef, alignX: Alignment.Center, alignY },\n      forwardTo: null,\n    });\n  }, [contextMenuDef, $settings.value.position]);\n\n  return (\n    <div\n      className={cx(\"ft-bar\", $settings.value.position.toLowerCase(), {\n        \"ft-bar-hidden\": $hidden_by_autohide.value,\n      })}\n      data-has-margin={!!$settings.value.margin}\n      data-there-is-maximized-on-background={$thereIsMaximizedOnBg.value}\n      data-focused-is-maximized={!!$lastFocusedOnMonitor.value?.isMaximized}\n      data-focused-is-overlay={!!$lastFocusedOnMonitor.value?.isSeelenOverlay}\n      onContextMenu={onContextMenu}\n    >\n      <ShowDesktopButton />\n      <BackgroundByLayersV2 prefix=\"ft-bar\" />\n\n      <DragDropProvider\n        onDragStart={() => {\n          $isDragging.value = true;\n        }}\n        onDragEnd={() => {\n          $isDragging.value = false;\n        }}\n        onDragOver={(event) => {\n          let temp = {\n            left: $containers.value.left.map((item) => (typeof item === \"string\" ? item : item.id)),\n            center: $containers.value.center.map((item) => typeof item === \"string\" ? item : item.id),\n            right: $containers.value.right.map((item) => typeof item === \"string\" ? item : item.id),\n          };\n\n          let moved = move(temp, event);\n          const allItems = [\n            ...$toolbar_state.value.left,\n            ...$toolbar_state.value.center,\n            ...$toolbar_state.value.right,\n          ];\n\n          $toolbar_state.value = {\n            ...$toolbar_state.value,\n            left: moved.left.map((id) => allItems.find((i) => matchIds(i, id))!),\n            center: moved.center.map((id) => allItems.find((i) => matchIds(i, id))!),\n            right: moved.right.map((id) => allItems.find((i) => matchIds(i, id))!),\n          };\n        }}\n      >\n        {Object.entries($containers.value).map(([id, items]) => <Group key={id} id={id} items={items} />)}\n\n        <DragOverlay>\n          {(source) => {\n            const allItems = [\n              ...$toolbar_state.value.left,\n              ...$toolbar_state.value.center,\n              ...$toolbar_state.value.right,\n            ];\n\n            const entry = allItems.find((i) => matchIds(i, source.id as string));\n            if (!entry) return null;\n\n            if (typeof entry === \"string\") {\n              const plugin = $plugins.value.find((p) => p.id === entry);\n              if (!plugin) return null;\n              const module = { ...(plugin.plugin as any), id: entry };\n              return <Item module={module} index={0} group=\"\" />;\n            }\n\n            return <Item module={entry} index={0} group=\"\" />;\n          }}\n        </DragOverlay>\n      </DragDropProvider>\n\n      <ShowDesktopButton />\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/state/default.ts",
    "content": "import { type PluginId, type ToolbarItem, ToolbarJsScope } from \"@seelen-ui/lib/types\";\nimport { $toolbar_state } from \"./items\";\n\nconst baseItem: ToolbarItem = {\n  id: \"-\",\n  scopes: [],\n  template: 'return \"\"',\n  tooltip: null,\n  badge: null,\n  remoteData: {},\n  style: {},\n  onClick: null,\n};\n\nexport function restoreStateToDefault() {\n  // based on src\\background\\state\\application\\toolbar_items.rs\n  $toolbar_state.value = {\n    isReorderDisabled: false,\n    left: [\n      \"@seelen/tb-user-menu\" as PluginId,\n      {\n        ...baseItem,\n        id: crypto.randomUUID() as string,\n        template: 'return \"|\"',\n      },\n      \"@default/focused-app\" as PluginId,\n      {\n        ...baseItem,\n        id: crypto.randomUUID() as string,\n        scopes: [ToolbarJsScope.FocusedApp],\n        template: 'return focusedApp.title ? \"-\" : \"\"',\n      },\n      \"@default/focused-app-title\" as PluginId,\n    ],\n    center: [\"@seelen/tb-calendar-popup\" as PluginId],\n    right: [\n      \"@seelen/tb-system-tray\" as PluginId,\n      \"@seelen/tb-keyboard-selector\" as PluginId,\n      \"@seelen/tb-bluetooth-popup\" as PluginId,\n      \"@seelen/tb-network-popup\" as PluginId,\n      \"@seelen/tb-media-popup\" as PluginId,\n      \"@default/power\" as PluginId,\n      \"@seelen/tb-notifications\" as PluginId,\n      \"@seelen/tb-quick-settings\" as PluginId,\n    ],\n  };\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/state/items.ts",
    "content": "import { effect, signal } from \"@preact/signals\";\nimport { invoke, PluginList, SeelenCommand } from \"@seelen-ui/lib\";\nimport type { PluginId, ToolbarItem2, ToolbarState } from \"@seelen-ui/lib/types\";\n\nimport { matchIds } from \"../utils.ts\";\nimport { debounce } from \"lodash\";\nimport { emit, listen } from \"@tauri-apps/api/event\";\nimport { restoreStateToDefault } from \"./default.ts\";\n\n/** True while a drag operation is in progress. Prevents emit/save feedback loop during onDragOver. */\nexport const $isDragging = signal(false);\nexport const $toolbar_state = signal(await invoke(SeelenCommand.StateGetToolbarItems));\n\nexport const $plugins = signal((await PluginList.getAsync()).forCurrentWidget());\nawait PluginList.onChange((list) => {\n  $plugins.value = list.forCurrentWidget();\n});\n\nlisten(\"hidden::sync-toolbar-items\", ({ payload }) => {\n  // avoid infinite loop\n  if (!$isDragging.value && JSON.stringify(payload) !== JSON.stringify($toolbar_state.value)) {\n    $toolbar_state.value = payload as ToolbarState;\n  }\n});\n\nconst emitSyncEvent = debounce((items: ToolbarState) => {\n  emit(\"hidden::sync-toolbar-items\", items);\n}, 300);\n\nconst saveTbState = debounce(async (items: ToolbarState) => {\n  console.trace(\"Saving toolbar state\");\n  await invoke(SeelenCommand.StateWriteToolbarItems, { items });\n}, 1000);\n\n// TODO: do the same sync method as was done for the dock widget\neffect(() => {\n  // Skip emit/save while dragging; onDragEnd will flip $isDragging back to false,\n  // which re-triggers this effect once with the final committed order.\n  if ($isDragging.value) return;\n\n  if (\n    $toolbar_state.value.left.length === 0 &&\n    $toolbar_state.value.center.length === 0 &&\n    $toolbar_state.value.right.length === 0\n  ) {\n    restoreStateToDefault();\n    return;\n  }\n\n  emitSyncEvent($toolbar_state.value);\n  saveTbState($toolbar_state.value);\n});\n\nexport const $actions = {\n  addTextItem(text: string) {\n    const cleaned = text.trim().replace(/\"/g, '\\\\\"');\n    const newRight = [...$toolbar_state.value.right];\n    newRight.push({\n      id: globalThis.crypto.randomUUID(),\n      type: \"text\",\n      template: `return \"${cleaned}\"`,\n    } as any);\n    $toolbar_state.value = { ...$toolbar_state.value, right: newRight };\n  },\n  addItem(id: PluginId) {\n    const { left, center, right } = $toolbar_state.value;\n    const alreadyExists = left.includes(id) || right.includes(id) || center.includes(id);\n    if (!alreadyExists) {\n      $toolbar_state.value = {\n        ...$toolbar_state.value,\n        right: [...right, id],\n      };\n    }\n  },\n  removeItem(id: string) {\n    let filter = (d: ToolbarItem2) => !matchIds(d, id);\n    const { left, center, right, ...rest } = $toolbar_state.value;\n    $toolbar_state.value = {\n      ...rest,\n      left: left.filter(filter),\n      center: center.filter(filter),\n      right: right.filter(filter),\n    };\n  },\n};\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/state/lazy.ts",
    "content": "import { type AllSeelenCommandReturns, invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { useEffect, useState } from \"preact/hooks\";\n\nexport interface Scope<T> {\n  fetching: boolean;\n  data: T | null;\n}\n\ninterface LazyStateConfig<C extends SeelenCommand> {\n  command: C;\n  event: SeelenEvent;\n}\n\n/**\n * Hook to create a lazy state that fetches data on first use\n * and subscribes to events only when needed\n */\nexport function useLazyState<C extends SeelenCommand>(\n  config: LazyStateConfig<C>,\n): Scope<AllSeelenCommandReturns[C]> {\n  const { command, event } = config;\n\n  const [scope, setScope] = useState<Scope<AllSeelenCommandReturns[C]>>({\n    fetching: true,\n    data: null,\n  });\n\n  useEffect(() => {\n    let unsubscribe: (() => void) | undefined;\n\n    // Subscribe to event if provided\n    if (event) {\n      const subscribePromise = subscribe(event, ({ payload }) => {\n        setScope({ fetching: false, data: payload as any });\n      });\n\n      subscribePromise.then((unsub) => {\n        unsubscribe = unsub;\n      });\n    }\n\n    // Fetch initial data\n    const fetchData = async () => {\n      try {\n        const data = await invoke(command as any);\n        setScope((prev) => {\n          // Only set if not already set by event\n          if (prev.fetching) {\n            return { fetching: false, data };\n          }\n          return prev;\n        });\n      } catch (error) {\n        console.error(`Failed to fetch data for command ${command}:`, error);\n        setScope({ fetching: false, data: null });\n      }\n    };\n\n    fetchData();\n\n    return () => {\n      if (unsubscribe) {\n        unsubscribe();\n      }\n    };\n  }, [command, event]);\n\n  return scope;\n}\n\n// Specific hooks for each scope requirement\n\nexport function useLazyUser() {\n  return useLazyState({\n    command: SeelenCommand.GetUser,\n    event: SeelenEvent.UserChanged,\n  });\n}\n\nexport function useLazyLanguages() {\n  return useLazyState({\n    command: SeelenCommand.SystemGetLanguages,\n    event: SeelenEvent.SystemLanguagesChanged,\n  });\n}\n\nexport function useLazyPowerStatus() {\n  return useLazyState({\n    command: SeelenCommand.GetPowerStatus,\n    event: SeelenEvent.PowerStatus,\n  });\n}\n\nexport function useLazyPowerMode() {\n  return useLazyState({\n    command: SeelenCommand.GetPowerMode,\n    event: SeelenEvent.PowerMode,\n  });\n}\n\nexport function useLazyBatteries() {\n  return useLazyState({\n    command: SeelenCommand.GetBatteries,\n    event: SeelenEvent.BatteriesStatus,\n  });\n}\n\nexport function useLazyMediaSessions() {\n  return useLazyState({\n    command: SeelenCommand.GetMediaSessions,\n    event: SeelenEvent.MediaSessions,\n  });\n}\n\nexport function useLazyMediaDevices() {\n  return useLazyState({\n    command: SeelenCommand.GetMediaDevices,\n    event: SeelenEvent.MediaDevices,\n  });\n}\n\nexport function useLazyNetworkAdapters() {\n  return useLazyState({\n    command: SeelenCommand.GetNetworkAdapters,\n    event: SeelenEvent.NetworkAdapters,\n  });\n}\n\nexport function useLazyNetworkLocalIp() {\n  return useLazyState({\n    command: SeelenCommand.GetNetworkDefaultLocalIp,\n    event: SeelenEvent.NetworkDefaultLocalIp,\n  });\n}\n\nexport function useLazyOnline() {\n  return useLazyState({\n    command: SeelenCommand.GetNetworkInternetConnection,\n    event: SeelenEvent.NetworkInternetConnection,\n  });\n}\n\nexport function useLazyBluetoothDevices() {\n  return useLazyState({\n    command: SeelenCommand.GetBluetoothDevices,\n    event: SeelenEvent.BluetoothDevicesChanged,\n  });\n}\n\nexport function useLazyNotifications() {\n  return useLazyState({\n    command: SeelenCommand.GetNotifications,\n    event: SeelenEvent.Notifications,\n  });\n}\n\nexport function useLazyDisks() {\n  return useLazyState({\n    command: SeelenCommand.GetSystemDisks,\n    event: SeelenEvent.SystemDisksChanged,\n  });\n}\n\nexport function useLazyNetworkStatistics() {\n  return useLazyState({\n    command: SeelenCommand.GetSystemNetwork,\n    event: SeelenEvent.SystemNetworkChanged,\n  });\n}\n\nexport function useLazyMemory() {\n  return useLazyState({\n    command: SeelenCommand.GetSystemMemory,\n    event: SeelenEvent.SystemMemoryChanged,\n  });\n}\n\nexport function useLazyCores() {\n  return useLazyState({\n    command: SeelenCommand.GetSystemCores,\n    event: SeelenEvent.SystemCoresChanged,\n  });\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/state/mod.ts",
    "content": "import { computed, effect, signal } from \"@preact/signals\";\nimport { Settings } from \"@seelen-ui/lib\";\nimport { FancyToolbarSide } from \"@seelen-ui/lib/types\";\nimport { toPhysicalPixels } from \"libs/ui/react/utils\";\nimport { $current_monitor } from \"./system\";\nimport i18n from \"../../../i18n\";\n\nconst initialSettings = await Settings.getAsync();\nexport const $settings = signal({\n  ...initialSettings.byWidget[\"@seelen/fancy-toolbar\"],\n  language: initialSettings.language || \"en\",\n  dateFormat: initialSettings.dateFormat,\n  startOfWeek: initialSettings.startOfWeek,\n});\n\nexport const $allByWidget = signal(initialSettings.byWidget);\nSettings.onChange((settings) => {\n  $settings.value = {\n    ...settings.byWidget[\"@seelen/fancy-toolbar\"],\n    language: settings.language || \"en\",\n    dateFormat: settings.dateFormat,\n    startOfWeek: settings.startOfWeek,\n  };\n  $allByWidget.value = settings.byWidget;\n});\n\neffect(() => {\n  const { itemSize, margin, padding } = $settings.value;\n  i18n.changeLanguage($settings.value.language || undefined);\n\n  const styles = document.documentElement.style;\n  styles.setProperty(\"--config-item-size\", `${itemSize}px`);\n  styles.setProperty(\"--config-margin\", `${margin}px`);\n  styles.setProperty(\"--config-padding\", `${padding}px`);\n  styles.setProperty(\"--config-height\", `${itemSize + padding * 2 + margin * 2}px`);\n});\n\nexport const $widget_rect = computed(() => {\n  const { itemSize, margin, padding } = $settings.value;\n  const height = toPhysicalPixels(itemSize + padding * 2 + margin * 2);\n  const rect = { ...$current_monitor.value.rect };\n\n  if ($settings.value.position === FancyToolbarSide.Top) {\n    rect.bottom = $current_monitor.value.rect.top + height;\n  } else if ($settings.value.position === FancyToolbarSide.Bottom) {\n    rect.top = $current_monitor.value.rect.bottom - height;\n  }\n\n  return rect;\n});\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/state/system.ts",
    "content": "import { computed } from \"@preact/signals\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { FancyToolbarSide } from \"@seelen-ui/lib/types\";\nimport { lazySignal } from \"libs/ui/react/utils/LazySignal\";\n\nconst currentMonitorId = Widget.getCurrent().decoded.monitorId!;\n\nexport const $virtual_desktop = lazySignal(async () => {\n  const initialDesktops = await invoke(SeelenCommand.StateGetVirtualDesktops);\n  return initialDesktops.monitors[currentMonitorId];\n});\nsubscribe(SeelenEvent.VirtualDesktopsChanged, (e) => {\n  $virtual_desktop.value = e.payload.monitors[currentMonitorId];\n});\nawait $virtual_desktop.init();\n\nexport const $monitors = lazySignal(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, $monitors.setByPayload);\nawait $monitors.init();\n\nexport const $current_monitor = computed(\n  () => $monitors.value.find((m) => m.id === currentMonitorId)!,\n);\n\n// PERFORMANCE NOTE: Mouse position tracking is always active for auto-hide detection.\n// This is a high-frequency event but the computed $mouse_at_edge is lightweight (simple comparisons).\n// Further optimization could conditionally subscribe only when hideMode !== Never,\n// but requires more complex lifecycle management.\nconst $mouse_pos = lazySignal(() => invoke(SeelenCommand.GetMousePosition));\nsubscribe(SeelenEvent.GlobalMouseMove, $mouse_pos.setByPayload);\nawait $mouse_pos.init();\n\nexport const $mouse_at_edge = computed<FancyToolbarSide | null>(() => {\n  const box = $current_monitor.value.rect;\n  const x = $mouse_pos.value[0];\n  const y = $mouse_pos.value[1];\n\n  if (x < box.left || x > box.right || y < box.top || y > box.bottom) {\n    return null;\n  }\n\n  if (y === box.top) {\n    return FancyToolbarSide.Top;\n  }\n\n  if (y === box.bottom - 1) {\n    return FancyToolbarSide.Bottom;\n  }\n  return null;\n});\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/state/windows.ts",
    "content": "import { computed, effect, signal } from \"@preact/signals\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { type FocusedApp, HideMode } from \"@seelen-ui/lib/types\";\nimport { debounce } from \"lodash\";\nimport { $settings, $widget_rect } from \"./mod\";\nimport { $mouse_at_edge } from \"./system\";\nimport { $is_this_webview_focused } from \"libs/ui/react/utils/signals\";\nimport { lazySignal } from \"libs/ui/react/utils/LazySignal\";\n\nconst widget = Widget.getCurrent();\n\nexport const $interactables = lazySignal(() => invoke(SeelenCommand.GetUserAppWindows));\nsubscribe(SeelenEvent.UserAppWindowsChanged, $interactables.setByPayload);\nawait $interactables.init();\n\nexport const $thereIsMaximizedOnBg = computed(() => {\n  return $interactables.value.some(\n    (w) => !w.isIconic && w.isZoomed && w.monitor === widget.decoded.monitorId,\n  );\n});\n\nexport const $focused = lazySignal(() => invoke(SeelenCommand.GetFocusedApp));\nexport const $lastFocusedOnMonitor = lazySignal<FocusedApp | null>(async () => {\n  const focused = await invoke(SeelenCommand.GetFocusedApp);\n  return focused.monitor === widget.decoded.monitorId ? focused : null;\n});\nsubscribe(SeelenEvent.GlobalFocusChanged, (e) => {\n  $focused.value = e.payload;\n  if (e.payload.monitor === widget.decoded.monitorId) {\n    $lastFocusedOnMonitor.value = e.payload;\n  }\n});\nawait $focused.init();\nawait $lastFocusedOnMonitor.init();\n\nexport const $is_tb_overlapped = computed(() => {\n  const by = $lastFocusedOnMonitor.value;\n  const interactables = $interactables.value;\n\n  if (!by || !by.rect) {\n    return false;\n  }\n\n  if (!interactables.some((w) => w.hwnd === by.hwnd)) {\n    return false;\n  }\n\n  const a = $widget_rect.value;\n  const b = by.rect;\n\n  // The edge pixel overlapping do not matters. This resolves the shared pixel in between the monitors,\n  // hereby a fullscreened app shared pixel collision does not hide other monitor windows.\n  if (a.right <= b.left || a.left >= b.right || a.bottom <= b.top || a.top >= b.bottom) {\n    return false;\n  }\n\n  return true;\n});\n\nexport const $hidden_by_autohide = signal(false);\nconst setToolbarAsHidden = computed(() => {\n  return debounce(() => ($hidden_by_autohide.value = true), $settings.value.delayToHide);\n});\nconst setToolbarAsNotHidden = computed(() => {\n  return debounce(() => ($hidden_by_autohide.value = false), $settings.value.delayToShow);\n});\n\neffect(() => {\n  Widget.self.window.setIgnoreCursorEvents($hidden_by_autohide.value);\n});\n\neffect(() => {\n  let hidden = false;\n  let flush = false;\n\n  let isMouseOverEdge = $mouse_at_edge.value === $settings.value.position;\n\n  switch ($settings.value.hideMode) {\n    case HideMode.Never:\n      hidden = false;\n      flush = true;\n      break;\n    case HideMode.Always:\n      hidden = !$is_this_webview_focused.value && !isMouseOverEdge;\n      break;\n    case HideMode.OnOverlap:\n      hidden = $is_tb_overlapped.value &&\n        !$is_this_webview_focused.value &&\n        !isMouseOverEdge;\n      break;\n  }\n\n  if (hidden) {\n    setToolbarAsNotHidden.peek().cancel();\n    setToolbarAsHidden.peek()();\n    return;\n  }\n\n  setToolbarAsHidden.peek().cancel();\n  setToolbarAsNotHidden.peek()();\n  if (flush) {\n    setToolbarAsNotHidden.peek().flush();\n  }\n});\n"
  },
  {
    "path": "src/ui/react/toolbar/modules/shared/utils.ts",
    "content": "import type { ToolbarItem2 } from \"@seelen-ui/lib/types\";\n\nexport function matchIds(tb: ToolbarItem2, id: string): boolean {\n  if (typeof tb === \"string\") {\n    return tb === id;\n  }\n  return tb.id === id;\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/react/toolbar/styles/global.css",
    "content": "/**\n * The styles in this file are only structural for toolbar,\n * all the design should be added to the default theme css.\n */\n\n:root {\n  font-size: var(--config-item-size);\n}\n\nbody {\n  overflow: hidden;\n  background: transparent;\n  width: 100vw;\n  height: 100vh;\n  line-height: var(--config-item-size);\n\n  button {\n    line-height: var(--config-item-size);\n  }\n}\n\n#root {\n  --item-padding: calc(var(--config-padding) / 2);\n\n  position: absolute;\n  width: 100vw;\n  transition: transform 0.2s ease-in-out;\n\n  &:has(.ft-bar.top) {\n    top: 0;\n  }\n\n  &:has(.ft-bar.bottom) {\n    bottom: 0;\n  }\n\n  &:has(.ft-bar-hidden.top:not(:hover)) {\n    transform: translate3d(0, calc(var(--config-height) * -1), 0);\n  }\n\n  &:has(.ft-bar-hidden.bottom:not(:hover)) {\n    transform: translate3d(0, calc(var(--config-height)), 0);\n  }\n}\n\n.ft-bar {\n  position: relative;\n  height: calc(var(--config-item-size) + var(--config-padding) * 2);\n  padding: var(--item-padding) calc(var(--config-padding) * 2);\n  margin: var(--config-margin);\n  overflow: hidden;\n\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n\n  figure,\n  img {\n    width: var(--config-item-size);\n    height: var(--config-item-size);\n    object-fit: contain;\n  }\n\n  .ft-bar-left,\n  .ft-bar-center,\n  .ft-bar-right {\n    display: flex;\n    align-items: center;\n    height: min-content;\n    gap: 4px;\n  }\n\n  .ft-bar-left {\n    max-width: 50%;\n    width: 50%;\n    overflow: hidden;\n    justify-content: flex-start;\n  }\n\n  .ft-bar-center {\n    justify-content: center;\n    padding: 0 6px;\n  }\n\n  .ft-bar-right {\n    max-width: 50%;\n    width: 50%;\n    overflow: hidden;\n    justify-content: flex-end;\n  }\n}\n\n.ft-bar-item {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 0;\n  height: min-content;\n  padding: var(--item-padding);\n\n  button {\n    height: 100%;\n    padding: 0 var(--item-padding);\n    border: 0;\n  }\n\n  .ft-bar-item-content {\n    display: flex;\n    align-items: center;\n    width: 100%;\n\n    > span {\n      text-wrap: nowrap;\n      text-overflow: ellipsis;\n      overflow: hidden;\n      white-space: pre;\n    }\n  }\n}\n\n.ft-corner-button {\n  position: absolute;\n  z-index: 1;\n  width: 10px;\n  height: 100%;\n\n  &:first-child {\n    left: 0;\n  }\n\n  &:last-child {\n    right: 0;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/toolbar/styles/variables.css",
    "content": ":root {\n  --config-item-size: 24px;\n  --config-margin: 0px;\n  --config-padding: 4px;\n  --config-height: calc(var(--config-item-size) + var(--config-padding) * 2 + var(--config-margin) * 2);\n\n  --config-time-before-hide: 800ms;\n  --config-time-before-show: 100ms;\n}\n"
  },
  {
    "path": "src/ui/react/weg/app.tsx",
    "content": "import { $system_colors } from \"libs/ui/react/utils/signals.ts\";\nimport { useDarkMode } from \"libs/ui/react/utils/styling.ts\";\nimport { ConfigProvider, theme } from \"antd\";\n\nimport { ErrorBoundary } from \"./components/Error/index.tsx\";\nimport { SeelenWeg } from \"./modules/bar/index.tsx\";\nimport { useSignalEffect } from \"@preact/signals\";\nimport { $lastFocusedOnMonitor } from \"./modules/shared/state/windows.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nexport function App() {\n  const isDarkMode = useDarkMode();\n\n  useSignalEffect(() => {\n    const fullscreened = !!$lastFocusedOnMonitor.value?.isFullscreened;\n    if (fullscreened) {\n      Widget.self.hide();\n    } else {\n      Widget.self.show();\n    }\n  });\n\n  return (\n    <ConfigProvider\n      componentSize=\"small\"\n      theme={{\n        token: {\n          colorPrimary: isDarkMode ? $system_colors.value.accent_light : $system_colors.value.accent_dark,\n        },\n        algorithm: isDarkMode ? theme.darkAlgorithm : theme.defaultAlgorithm,\n      }}\n    >\n      <ErrorBoundary fallback={<div>Something went wrong</div>}>\n        <SeelenWeg />\n      </ErrorBoundary>\n    </ConfigProvider>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/weg/components/Error/index.tsx",
    "content": "import React from \"react\";\n\ninterface Props {\n  fallback: React.ReactNode;\n  children: React.ReactNode;\n}\n\ninterface State {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<Props, State> {\n  constructor(props: Props) {\n    super(props);\n    this.state = { hasError: false };\n  }\n\n  static getDerivedStateFromError() {\n    return { hasError: true };\n  }\n\n  componentDidCatch(error: any, info: any) {\n    console.error(error, info);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return this.props.fallback;\n    }\n\n    return this.props.children;\n  }\n}\n"
  },
  {
    "path": "src/ui/react/weg/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport i18n from \"i18next\";\nimport yaml from \"js-yaml\";\nimport { initReactI18next } from \"react-i18next\";\n\ni18n.use(initReactI18next).init(\n  {\n    lng: \"en\",\n    fallbackLng: \"en\",\n    interpolation: {\n      escapeValue: false,\n    },\n    resources: {},\n  },\n  undefined,\n);\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  for (const [key, value] of Object.entries(translations)) {\n    i18n.addResourceBundle(key, \"translation\", yaml.load(value.default));\n  }\n}\n\nexport default i18n;\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/af.yml",
    "content": "app_menu:\n  close: Naby\n  close_multiple: Sluit alles\n  copy_handles: Kopiehandvatsels\n  kill: Doodproses\n  kill_multiple: Maak alle prosesse dood\n  open_file_location: Oop lêlokasie\n  pin: Speld\n  pin_to_center: Speld na die sentrum\n  pin_to_left: Speld na links\n  pin_to_right: Speld na regs\n  run_as: Hardloop as administrateur\n  unpin: Unpin\ncontext_menu:\n  remove_module: Verwyder module\n  reorder_disable: Sluit taakbalk\n  reorder_enable: Ontsluit taakbalk\nmedia:\n  not_playing: Niks speel nie\nshow_desktop:\n  label: Wys lessenaar\nstart:\n  label: Begin Kieslys\ntaskbar_menu:\n  add_file: Pin Custom File\n  desktop: Voeg lessenaarmodule by\n  media: Voeg mediasmodule by\n  settings: Instellings\n  start: Voeg beginmodule by\n  task_manager: Taakbestuurder\n  trash_bin: Voeg asblikmodule by\ntrash_bin:\n  empty_bin: Leë asblik\nweg:\n  empty: Geen items om te wys nie\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/am.yml",
    "content": "app_menu:\n  close: ገጠመ\n  close_multiple: ሁሉንም ዝጋ\n  copy_handles: መያዣዎች\n  kill: የመግደል ሂደት\n  kill_multiple: ሁሉንም ሂደቶች ይገድሉ\n  open_file_location: የፋይል ቦታ ይክፈቱ\n  pin: ፒን\n  pin_to_center: ፒን እስከ ማእከል\n  pin_to_left: ፒን ወደ ግራ\n  pin_to_right: በቀኝ በኩል ፒን\n  run_as: እንደ አስተዳዳሪ ይሮጡ\n  unpin: እየቀነሰ ይሄዳል\ncontext_menu:\n  remove_module: ሞጁሉን አስወግድ\n  reorder_disable: የተግባር አሞሌ\n  reorder_enable: የተካሄደውን ሥራ ይክፈቱ\nmedia:\n  not_playing: ምንም የሚጫወት የለም\nshow_desktop:\n  label: ዴስክቶፕን አሳይ\nstart:\n  label: የጀምር ምናሌ\ntaskbar_menu:\n  add_file: የፒን ብጁ ፋይል\n  desktop: የዴስክቶፕ ሞጁል አክል\n  media: የሚዲያ ሞዱል ያክሉ\n  settings: ቅንብሮች\n  start: የመነሻ ሞጁል ያክሉ\n  task_manager: ተግባር አስተዳዳሪ\n  trash_bin: ሪሳይክል ቢን ሞዱልን አክል\ntrash_bin:\n  empty_bin: ባዶ ሪሳይክል ቢን\nweg:\n  empty: ምንም የሚታዩ ንጥሎች የሉም\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ar.yml",
    "content": "app_menu:\n  close: اغلق\n  close_multiple: اغلق الكل\n  copy_handles: انسخ الرمز\n  kill: قتل العملية\n  kill_multiple: اقتل جميع العمليات\n  open_file_location: افتح مكان الملف\n  pin: ثبت الملف\n  pin_to_center: ثبت الملف في الوسط\n  pin_to_left: ثبت الملف على اليسار\n  pin_to_right: ثبت الملف على اليمين\n  run_as: تشغيل كمسؤول\n  unpin: إزالة التثبيت\ncontext_menu:\n  remove_module: إزالة الوحدة النمطية\n  reorder_disable: قفل شريط المهام\n  reorder_enable: فتح شريط المهام\nmedia:\n  not_playing: لا توجد ملفات\nshow_desktop:\n  label: إظهار سطح المكتب\nstart:\n  label: قائمة ابدأ\ntaskbar_menu:\n  add_file: ثبت الملف\n  desktop: إضافة وحدة سطح المكتب\n  media: أضف وحدة الوسائط\n  settings: الأعدادات\n  start: أضف وحدة البداية\n  task_manager: مدير المهام\n  trash_bin: إضافة وحدة سلة المحذوفات\ntrash_bin:\n  empty_bin: سلة المحذوفات فارغة\nweg:\n  empty: لا توجد عناصر لعرضها\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/az.yml",
    "content": "app_menu:\n  close: Yaxın\n  close_multiple: Hamısını bağlayın\n  copy_handles: Copular\n  kill: Öldürmə prosesi\n  kill_multiple: Bütün prosesləri öldürün\n  open_file_location: Fayl yeri açın\n  pin: Pin\n  pin_to_center: Mərkəzə pin\n  pin_to_left: Pin sola\n  pin_to_right: Sağa pin\n  run_as: İdarəçi kimi qaçmaq\n  unpin: Qeyri-pəncələmək\ncontext_menu:\n  remove_module: Modulu çıxarın\n  reorder_disable: Tapşırıq çubuğunu kilidləyin\n  reorder_enable: Tapşırıq çubuğunu açın\nmedia:\n  not_playing: Heç nə oynamır\nshow_desktop:\n  label: Masaüstünü göstər\nstart:\n  label: Başlat Menyu\ntaskbar_menu:\n  add_file: Xüsusi faylı pin edin\n  desktop: Masaüstü Modulu əlavə edin\n  media: Media modulu əlavə edin\n  settings: Parametrlər\n  start: Başlama modulu əlavə edin\n  task_manager: Tapşırıq meneceri\n  trash_bin: Zibil qutusu modulu əlavə edin\ntrash_bin:\n  empty_bin: Zibil qutusunu boşaldın\nweg:\n  empty: Göstəriləcək element yoxdur\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/bg.yml",
    "content": "app_menu:\n  close: Близо\n  close_multiple: Затвори всички\n  copy_handles: Копиращи дръжки\n  kill: Процес на унищожаване\n  kill_multiple: Убийте всички процеси\n  open_file_location: Отворете местоположението на файла\n  pin: Щифт\n  pin_to_center: Щифт към центъра\n  pin_to_left: Пин вляво\n  pin_to_right: Пин вдясно\n  run_as: Изпълни като администратор\n  unpin: Unpin\ncontext_menu:\n  remove_module: Премахване на модула\n  reorder_disable: Заключване на лентата на задачите\n  reorder_enable: Отключване на лентата на задачите\nmedia:\n  not_playing: Нищо не играе\nshow_desktop:\n  label: Показване на работния плот\nstart:\n  label: Старт меню\ntaskbar_menu:\n  add_file: ПИН Персонализиран файл\n  desktop: Добавяне на десктоп модул\n  media: Добавете медиен модул\n  settings: Настройки\n  start: Добавете старт модул\n  task_manager: Диспечер на задачите\n  trash_bin: Добавяне на модул за кошче\ntrash_bin:\n  empty_bin: Изпразване на кошчето\nweg:\n  empty: Няма елементи за показване\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/bn.yml",
    "content": "app_menu:\n  close: বন্ধ\n  close_multiple: সব বন্ধ করা\n  copy_handles: হ্যান্ডলগুলি অনুলিপি\n  kill: কিল প্রসেস\n  kill_multiple: সমস্ত প্রক্রিয়া হত্যা\n  open_file_location: নথির অবস্থান বের করা\n  pin: পিন\n  pin_to_center: কেন্দ্রে পিন\n  pin_to_left: বাম দিকে পিন\n  pin_to_right: ডান পিন\n  run_as: প্রশাসক হিসাবে চালান\n  unpin: আনপিন\ncontext_menu:\n  remove_module: মডিউল সরান\n  reorder_disable: লক টাস্কবার\n  reorder_enable: টাস্কবার আনলক করুন\nmedia:\n  not_playing: কিছুই খেলছে না\nshow_desktop:\n  label: ডেস্কটপ দেখান\nstart:\n  label: স্টার্ট মেনু\ntaskbar_menu:\n  add_file: কাস্টম ফাইল পিন করুন\n  desktop: ডেস্কটপ মডিউল যোগ করুন\n  media: মিডিয়া মডিউল যুক্ত করুন\n  settings: সেটিংস\n  start: স্টার্ট মডিউল যুক্ত করুন\n  task_manager: টাস্ক ম্যানেজার\n  trash_bin: রিসাইকেল বিন মডিউল যোগ করুন\ntrash_bin:\n  empty_bin: খালি রিসাইকেল বিন\nweg:\n  empty: দেখানোর জন্য কোনো আইটেম নেই\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/bs.yml",
    "content": "app_menu:\n  close: Zatvoriti\n  close_multiple: Zatvoriti sve\n  copy_handles: Ručke za kopiranje\n  kill: Kill Process\n  kill_multiple: Ubijte sve procese\n  open_file_location: Otvorite lokaciju datoteke\n  pin: Pin\n  pin_to_center: Pin za centar\n  pin_to_left: Pin na lijevo\n  pin_to_right: Pin na desno\n  run_as: Trčite kao administrator\n  unpin: Otrcati\ncontext_menu:\n  remove_module: Uklonite modul\n  reorder_disable: Zaključavanje zadataka\n  reorder_enable: Otključajte traku zadataka\nmedia:\n  not_playing: Ništa se ne igra\nshow_desktop:\n  label: Prikaži radnu površinu\nstart:\n  label: Start Menu\ntaskbar_menu:\n  add_file: PIN prilagođena datoteka\n  desktop: Dodaj radni modul\n  media: Dodajte medijski modul\n  settings: Postavke\n  start: Dodajte početni modul\n  task_manager: Task Manager\n  trash_bin: Dodajte modul korpe za otpatke\ntrash_bin:\n  empty_bin: Ispraznite koš za smeće\nweg:\n  empty: Nema stavki za prikaz\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ca.yml",
    "content": "app_menu:\n  close: Tanca\n  close_multiple: Tanca tot\n  copy_handles: Manelles de còpia\n  kill: Procés de matança\n  kill_multiple: Mata tots els processos\n  open_file_location: Obriu la ubicació del fitxer\n  pin: Agulla\n  pin_to_center: Pin al centre\n  pin_to_left: Pin a l’esquerra\n  pin_to_right: Pin a la dreta\n  run_as: Executa com administrador\n  unpin: Desfeta\ncontext_menu:\n  remove_module: Elimina el mòdul\n  reorder_disable: Barra de tasques de bloqueig\n  reorder_enable: Desbloqueja la barra de tasques\nmedia:\n  not_playing: Res està jugant\nshow_desktop:\n  label: Mostra l'escriptori\nstart:\n  label: Menú d'inici\ntaskbar_menu:\n  add_file: Pin el fitxer personalitzat\n  desktop: Afegeix un mòdul d'escriptori\n  media: Afegiu el mòdul de suports\n  settings: Configuració\n  start: Afegiu el mòdul d'inici\n  task_manager: Gestor de tasques\n  trash_bin: Afegiu el mòdul de la paperera de reciclatge\ntrash_bin:\n  empty_bin: Buida la paperera de reciclatge\nweg:\n  empty: No hi ha elements per mostrar\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/cs.yml",
    "content": "app_menu:\n  close: Zavřít\n  close_multiple: Zavřít všechny\n  copy_handles: Kopírovat úchyty\n  kill: Ukončit proces\n  kill_multiple: Ukončit všechny procesy\n  open_file_location: Otevřít umístění souboru\n  pin: Připnout\n  pin_to_center: Připnout na střed\n  pin_to_left: Připnout doleva\n  pin_to_right: Připnout doprava\n  run_as: Spustit jako administrátor\n  unpin: Odepnout\ncontext_menu:\n  remove_module: Odebrat modul\n  reorder_disable: Uzamčení hlavního panelu\n  reorder_enable: Odemknutí hlavního panelu\nmedia:\n  not_playing: Nic nehraje\nshow_desktop:\n  label: Zobrazit plochu\nstart:\n  label: Nabídka Start\ntaskbar_menu:\n  add_file: Připnout vlastní soubor\n  desktop: Přidat modul plochy\n  media: Přidat mediální modul\n  settings: Nastavení\n  start: Přidat startovací modul\n  task_manager: Správce úloh\n  trash_bin: Přidat modul koše\ntrash_bin:\n  empty_bin: Vyprázdněte koš\nweg:\n  empty: Žádné položky k zobrazení\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/cy.yml",
    "content": "app_menu:\n  close: Chaewch\n  close_multiple: Cau popeth\n  copy_handles: Dolenni copi\n  kill: Proses Lladd\n  kill_multiple: Lladd Pob Proses\n  open_file_location: Lleoliad Ffeil Agored\n  pin: Piniff\n  pin_to_center: Pin i'r canol\n  pin_to_left: Pin i'r chwith\n  pin_to_right: Pin i'r dde\n  run_as: Rhedeg fel Gweinyddwr\n  unpin: Dadleuon\ncontext_menu:\n  remove_module: Dileu Modiwl\n  reorder_disable: Bar tasgau clo\n  reorder_enable: Datgloi Bar Tasg\nmedia:\n  not_playing: Nid oes dim yn chwarae\nshow_desktop:\n  label: Dangos Penbwrdd\nstart:\n  label: Dewislen Cychwyn\ntaskbar_menu:\n  add_file: Pin ffeil arfer\n  desktop: Ychwanegu Modiwl Penbwrdd\n  media: Ychwanegu Modiwl Cyfryngau\n  settings: Gosodiadau\n  start: Ychwanegu Modiwl Start\n  task_manager: Rheolwr Tasg\n  trash_bin: Ychwanegu Modiwl Bin Ailgylchu\ntrash_bin:\n  empty_bin: Bin Ailgylchu Gwag\nweg:\n  empty: Dim eitemau i'w dangos\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/da.yml",
    "content": "app_menu:\n  close: Tæt\n  close_multiple: Luk alt\n  copy_handles: Kopier håndtag\n  kill: Aflivningsproces\n  kill_multiple: Dræb alle processer\n  open_file_location: Åbn filplacering\n  pin: Pin\n  pin_to_center: Stift til centrum\n  pin_to_left: Pin til venstre\n  pin_to_right: Pin til højre\n  run_as: Kør som administrator\n  unpin: Unpin\ncontext_menu:\n  remove_module: Fjern modul\n  reorder_disable: Lås proceslinjen\n  reorder_enable: Lås proceslinjen op\nmedia:\n  not_playing: Intet spiller\nshow_desktop:\n  label: Vis skrivebord\nstart:\n  label: Startmenu\ntaskbar_menu:\n  add_file: Pin brugerdefineret fil\n  desktop: Tilføj skrivebordsmodul\n  media: Tilføj mediemodul\n  settings: Indstillinger\n  start: Tilføj startmodul\n  task_manager: Task Manager\n  trash_bin: Tilføj papirkurvsmodul\ntrash_bin:\n  empty_bin: Tøm papirkurven\nweg:\n  empty: Ingen elementer at vise\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/de.yml",
    "content": "app_menu:\n  close: Schließen\n  close_multiple: Alle schließen\n  copy_handles: Handles kopieren\n  kill: Prozess beenden\n  kill_multiple: Beenden Sie alle Prozesse\n  open_file_location: Dateispeicherort öffnen\n  pin: Anheften\n  pin_to_center: Zentral anheften\n  pin_to_left: Links anheften\n  pin_to_right: Rechts anheften\n  run_as: Als Administrator ausführen\n  unpin: Lösen\ncontext_menu:\n  remove_module: Modul entfernen\n  reorder_disable: Taskleiste sperren\n  reorder_enable: Taskleiste entsperren\nmedia:\n  not_playing: Nichts spielt\nshow_desktop:\n  label: Desktop anzeigen\nstart:\n  label: Startmenü\ntaskbar_menu:\n  add_file: PIN benutzerdefinierte Datei\n  desktop: Desktop-Modul hinzufügen\n  media: Medienmodul hinzufügen\n  settings: Einstellungen\n  start: Startmodul hinzufügen\n  task_manager: Task-Manager\n  trash_bin: Fügen Sie das Papierkorbmodul hinzu\ntrash_bin:\n  empty_bin: Leeren Sie den Papierkorb\nweg:\n  empty: Keine Artikel zum Anzeigen vorhanden\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/el.yml",
    "content": "app_menu:\n  close: Κλείσε\n  close_multiple: Κλείσ'τα όλα\n  copy_handles: Λαβές αντιγράφων\n  kill: Διαδικασία θανάτωσης\n  kill_multiple: Σκοτώστε όλες τις διαδικασίες\n  open_file_location: Ανοίξτε την τοποθεσία αρχείου\n  pin: Καρφίτσα\n  pin_to_center: Καρφίτσα στο κέντρο\n  pin_to_left: Καρφίτσα στα αριστερά\n  pin_to_right: Pin προς τα δεξιά\n  run_as: Εκτελέστε ως διαχειριστής\n  unpin: Ξεκαρφιτσώνω\ncontext_menu:\n  remove_module: Αφαιρέστε τη μονάδα\n  reorder_disable: Κλείδωμα γραμμής εργασιών\n  reorder_enable: Ξεκλείδωμα της γραμμής εργασιών\nmedia:\n  not_playing: Τίποτα δεν παίζει\nshow_desktop:\n  label: Εμφάνιση επιφάνειας εργασίας\nstart:\n  label: Μενού Έναρξη\ntaskbar_menu:\n  add_file: PIN προσαρμοσμένο αρχείο\n  desktop: Προσθήκη μονάδας επιφάνειας εργασίας\n  media: Προσθήκη ενότητας μέσων\n  settings: Ρυθμίσεις\n  start: Προσθήκη μονάδας εκκίνησης\n  task_manager: Task Manager\n  trash_bin: Προσθήκη μονάδας κάδου ανακύκλωσης\ntrash_bin:\n  empty_bin: Άδειος Κάδος Ανακύκλωσης\nweg:\n  empty: Δεν υπάρχουν στοιχεία για εμφάνιση\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/en.yml",
    "content": "app_menu:\n  close: Close\n  close_multiple: Close All\n  copy_handles: Copy Handles\n  kill: Kill Process\n  kill_multiple: Kill All Processes\n  open_file_location: Open File Location\n  pin: Pin\n  pin_to_center: Pin to Center\n  pin_to_left: Pin to Left\n  pin_to_right: Pin to Right\n  run_as: Run as Administrator\n  unpin: Unpin\ncontext_menu:\n  remove_module: Remove Module\n  reorder_disable: Lock taskbar\n  reorder_enable: Unlock taskbar\nmedia:\n  not_playing: Nothing is playing\nshow_desktop:\n  label: Show Desktop\nstart:\n  label: Start Menu\ntaskbar_menu:\n  add_file: Pin Custom File\n  desktop: Add Desktop Module\n  media: Add Media Module\n  settings: Settings\n  start: Add Start Module\n  task_manager: Task Manager\n  trash_bin: Add Recycle Bin Module\ntrash_bin:\n  empty_bin: Empty Recycle Bin\nweg:\n  empty: No items to show\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/es.yml",
    "content": "app_menu:\n  close: Cerrar\n  close_multiple: Cerrar todo\n  copy_handles: Copiar identificadores\n  kill: Finalizar proceso\n  kill_multiple: Finalizar todos los procesos\n  open_file_location: Abrir ubicación del archivo\n  pin: Anclar\n  pin_to_center: Anclar al centro\n  pin_to_left: Anclar a la izquierda\n  pin_to_right: Anclar a la derecha\n  run_as: Ejecutar como administrador\n  unpin: Desanclar\ncontext_menu:\n  remove_module: Quitar módulo\n  reorder_disable: Bloquear la barra de tareas\n  reorder_enable: Desbloquear la barra de tareas\nmedia:\n  not_playing: Nada en reproducción\nshow_desktop:\n  label: Mostrar escritorio\nstart:\n  label: Menú Inicio\ntaskbar_menu:\n  add_file: Pin archivo personalizado\n  desktop: Agregar módulo de escritorio\n  media: Añadir módulo de medios\n  settings: Ajustes\n  start: Añadir módulo de inicio\n  task_manager: Administrador de tareas\n  trash_bin: Agregar módulo de papelera de reciclaje\ntrash_bin:\n  empty_bin: Papelera de reciclaje vacía\nweg:\n  empty: No hay elementos para mostrar\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/et.yml",
    "content": "app_menu:\n  close: Sulge\n  close_multiple: Sulgege kõik\n  copy_handles: Koopiakäepidemed\n  kill: Tapmisprotsess\n  kill_multiple: Tapke kõik protsessid\n  open_file_location: Ava faili asukoht\n  pin: Nööpnõel\n  pin_to_center: Tihvt keskele\n  pin_to_left: Tihvt vasakule\n  pin_to_right: Tihvt paremal\n  run_as: Joosta administraatorina\n  unpin: Lahti keelama\ncontext_menu:\n  remove_module: Eemalda moodul\n  reorder_disable: Ülesanderiba lukustamine\n  reorder_enable: Avage tegumiriba avamine\nmedia:\n  not_playing: Miski ei mängi\nshow_desktop:\n  label: Kuva töölaud\nstart:\n  label: Menüü Start\ntaskbar_menu:\n  add_file: PIN -i kohandatud fail\n  desktop: Lisage töölauamoodul\n  media: Lisage meediumimoodul\n  settings: Seaded\n  start: Lisage käivitusmoodul\n  task_manager: Tegumihaldur\n  trash_bin: Lisage prügikasti moodul\ntrash_bin:\n  empty_bin: Tühjenda prügikast\nweg:\n  empty: Kuvatavaid üksusi pole\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/eu.yml",
    "content": "app_menu:\n  close: Hurbileko\n  close_multiple: Itxi guztiak\n  copy_handles: Kopiatu heldulekuak\n  kill: Hiltzeko prozesua\n  kill_multiple: Kill Prozesu guztiak\n  open_file_location: Ireki fitxategiaren kokapena\n  pin: Iskilinba\n  pin_to_center: Pin zentrora\n  pin_to_left: Ezkerrera\n  pin_to_right: PIN eskuin\n  run_as: Exekutatu administratzaile gisa\n  unpin: Desjendu\ncontext_menu:\n  remove_module: Kendu modulua\n  reorder_disable: Blokeatu ataza barra\n  reorder_enable: Desblokeatu ataza barra\nmedia:\n  not_playing: Ez da ezer jolasten\nshow_desktop:\n  label: Erakutsi mahaigaina\nstart:\n  label: Hasi menua\ntaskbar_menu:\n  add_file: PIN fitxategi pertsonalizatua\n  desktop: Gehitu mahaigaineko modulua\n  media: Gehitu multimedia modulua\n  settings: Ezarpenak\n  start: Gehitu abiapuntu modulua\n  task_manager: Zereginen kudeatzailea\n  trash_bin: Gehitu Zaborrontzia Modulua\ntrash_bin:\n  empty_bin: Hustu zakarrontzira\nweg:\n  empty: Ez dago erakusteko elementurik\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/fa.yml",
    "content": "app_menu:\n  close: بستن\n  close_multiple: همه را ببندید\n  copy_handles: دسته کپی کردن\n  kill: فرآیند کشتن\n  kill_multiple: کشتن تمام فرآیندها\n  open_file_location: مکان فایل را باز کنید\n  pin: پین کردن\n  pin_to_center: پین به مرکز\n  pin_to_left: پین به سمت چپ\n  pin_to_right: پین به راست\n  run_as: به عنوان مدیر اجرا کنید\n  unpin: باز کردن\ncontext_menu:\n  remove_module: حذف ماژول\n  reorder_disable: نوار وظیفه قفل\n  reorder_enable: نوار کار را باز کنید\nmedia:\n  not_playing: هیچ چیز بازی نمی کند\nshow_desktop:\n  label: نمایش دسکتاپ\nstart:\n  label: منوی شروع\ntaskbar_menu:\n  add_file: پرونده سفارشی\n  desktop: اضافه کردن ماژول دسکتاپ\n  media: ماژول رسانه را اضافه کنید\n  settings: تنظیمات\n  start: ماژول شروع را اضافه کنید\n  task_manager: Task Manager\n  trash_bin: اضافه کردن ماژول سطل بازیافت\ntrash_bin:\n  empty_bin: سطل بازیافت خالی\nweg:\n  empty: هیچ موردی برای نمایش وجود ندارد\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/fi.yml",
    "content": "app_menu:\n  close: kiinni\n  close_multiple: Sulje kaikki\n  copy_handles: Kopiokahvat\n  kill: Tappoprosessi\n  kill_multiple: Tapa kaikki prosessit\n  open_file_location: Avaa tiedoston sijainti\n  pin: Nasta\n  pin_to_center: Pintää keskustaan\n  pin_to_left: Vasemmalle\n  pin_to_right: Pistä oikealle\n  run_as: Suorita järjestelmänvalvojana\n  unpin: Purkaa\ncontext_menu:\n  remove_module: Poista moduuli\n  reorder_disable: Lukitse tehtäväpalkki\n  reorder_enable: Tehtäväpalkin lukituksen avaaminen\nmedia:\n  not_playing: Mikään ei pelaa\nshow_desktop:\n  label: Näytä työpöytä\nstart:\n  label: Käynnistä-valikko\ntaskbar_menu:\n  add_file: Pin mukautettu tiedosto\n  desktop: Lisää työpöytämoduuli\n  media: Lisää mediamoduuli\n  settings: Asetukset\n  start: Lisää aloitusmoduuli\n  task_manager: Tehtävienhallinta\n  trash_bin: Lisää roskakorimoduuli\ntrash_bin:\n  empty_bin: Tyhjennä roskakori\nweg:\n  empty: Ei näytettäviä kohteita\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/fr.yml",
    "content": "app_menu:\n  close: Fermer\n  close_multiple: Tout fermer\n  copy_handles: Copier l'ID du processus\n  kill: Terminer le processus\n  kill_multiple: Terminer tous les processus\n  open_file_location: Ouvrir l'emplacement du fichier\n  pin: Épingler\n  pin_to_center: Épingler au centre\n  pin_to_left: Épingler à gauche\n  pin_to_right: Épingler à droite\n  run_as: Exécuter en tant qu'administrateur\n  unpin: Détacher\ncontext_menu:\n  remove_module: Supprimer le module\n  reorder_disable: Verrouiller la barre des tâches\n  reorder_enable: Déverrouiller la barre des tâches\nmedia:\n  not_playing: Vide\nshow_desktop:\n  label: Afficher le bureau\nstart:\n  label: Menu Démarrer\ntaskbar_menu:\n  add_file: Épingler un fichier\n  desktop: Ajouter un module de bureau\n  media: Afficher le module multimédia\n  settings: Paramètres\n  start: Afficher le module \"Menu démarrer\"\n  task_manager: Gestionnaire des tâches\n  trash_bin: Ajouter un module de corbeille\ntrash_bin:\n  empty_bin: Corbeille vide\nweg:\n  empty: Aucun élément à afficher\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/gu.yml",
    "content": "app_menu:\n  close: બંધ\n  close_multiple: બંધ કરવું\n  copy_handles: નકલ\n  kill: કીલ પ્રોસેસ\n  kill_multiple: બધી પ્રક્રિયાઓને મારી નાખો\n  open_file_location: ફાઇલ સ્થાન ખોલો\n  pin: પિન\n  pin_to_center: કેન્દ્રથી પિનથી કેન્દ્રમાં\n  pin_to_left: ડાબી બાજુ\n  pin_to_right: જમણી બાજુ\n  run_as: વહીવટકર્તા તરીકે ચલાવો\n  unpin: ખોળવું\ncontext_menu:\n  remove_module: મોડ્યુલ દૂર કરો\n  reorder_disable: ટાસ્કબાર\n  reorder_enable: ટાસ્કબારને અનલ lock ક કરો\nmedia:\n  not_playing: કંઈ રમી રહ્યું નથી\nshow_desktop:\n  label: ડેસ્કટોપ બતાવો\nstart:\n  label: સ્ટાર્ટ મેનૂ\ntaskbar_menu:\n  add_file: પિન કસ્ટમ ફાઇલ\n  desktop: ડેસ્કટોપ મોડ્યુલ ઉમેરો\n  media: મીડિયા મોડ્યુલ ઉમેરો\n  settings: સેટિંગ્સ\n  start: પ્રારંભ મોડ્યુલ ઉમેરો\n  task_manager: ટાસ્ક મેનેજર\n  trash_bin: રિસાયકલ બિન મોડ્યુલ ઉમેરો\ntrash_bin:\n  empty_bin: ખાલી રિસાયકલ બિન\nweg:\n  empty: બતાવવા માટે કોઈ આઇટમ નથી\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/he.yml",
    "content": "app_menu:\n  close: סגור\n  close_multiple: סגור הכל\n  copy_handles: העתק ידיות\n  kill: הרוג תהליך\n  kill_multiple: הרוג את כל התהליכים\n  open_file_location: פתח את מיקום הקובץ\n  pin: הצמד\n  pin_to_center: הצמד למרכז\n  pin_to_left: הצמד לשמאל\n  pin_to_right: הצמד לימין\n  run_as: הפעל כמנהל\n  unpin: בטל הצמדה\ncontext_menu:\n  remove_module: הסר מודול\n  reorder_disable: נעל את שורת המשימות\n  reorder_enable: ביטול נעילת שורת המשימות\nmedia:\n  not_playing: שום דבר לא משחק\nshow_desktop:\n  label: הצג את שולחן העבודה\nstart:\n  label: תפריט התחל\ntaskbar_menu:\n  add_file: הצמד קובץ מותאם אישית\n  desktop: הוסף מודול שולחן עבודה\n  media: הוסף מודול מדיה\n  settings: הגדרות\n  start: הוסף מודול התחל\n  task_manager: מנהל משימות\n  trash_bin: הוסף מודול סל המיחזור\ntrash_bin:\n  empty_bin: ריק סל המיחזור\nweg:\n  empty: אין פריטים להצגה\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/hi.yml",
    "content": "app_menu:\n  close: बंद करना\n  close_multiple: सब बंद करें\n  copy_handles: कॉपी हैंडल\n  kill: मारने की प्रक्रिया\n  kill_multiple: सभी प्रक्रियाओं को ख़त्म करें\n  open_file_location: फ़ाइल के स्थान को खोलें\n  pin: नत्थी करना\n  pin_to_center: केंद्र को पिन करना\n  pin_to_left: बाईं ओर पिन\n  pin_to_right: दाईं ओर पिन करना\n  run_as: व्यवस्थापक के रूप में चलाएं\n  unpin: अनपिन\ncontext_menu:\n  remove_module: मॉड्यूल निकालें\n  reorder_disable: लॉक टास्कबार\n  reorder_enable: टास्कबार अनलॉक करें\nmedia:\n  not_playing: कुछ भी नहीं खेल रहा है\nshow_desktop:\n  label: डेक्सटोप दिखाओ\nstart:\n  label: शुरुआत की सूची\ntaskbar_menu:\n  add_file: पिन कस्टम फ़ाइल\n  desktop: डेस्कटॉप मॉड्यूल जोड़ें\n  media: मीडिया मॉड्यूल जोड़ें\n  settings: सेटिंग्स\n  start: स्टार्ट मॉड्यूल जोड़ें\n  task_manager: कार्य प्रबंधक\n  trash_bin: रीसायकल बिन मॉड्यूल जोड़ें\ntrash_bin:\n  empty_bin: खाली रीसायकल बिन\nweg:\n  empty: दिखाने के लिए कोई आइटम नहीं\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/hr.yml",
    "content": "app_menu:\n  close: Zatvoriti\n  close_multiple: Zatvori sve\n  copy_handles: Ručke za kopiranje\n  kill: Kill Process\n  kill_multiple: Ubij sve procese\n  open_file_location: Otvorena lokacija datoteke\n  pin: Pričvrstiti\n  pin_to_center: Pin do centra\n  pin_to_left: PIN ulijevo\n  pin_to_right: PIN na desno\n  run_as: Pokreni kao administrator\n  unpin: Raskoš\ncontext_menu:\n  remove_module: Ukloni modul\n  reorder_disable: Zaključajte zadaću\n  reorder_enable: Otključajte zadaću\nmedia:\n  not_playing: Ništa se ne igra\nshow_desktop:\n  label: Prikaži radnu površinu\nstart:\n  label: Izbornik Start\ntaskbar_menu:\n  add_file: PIN prilagođena datoteka\n  desktop: Dodaj Desktop Module\n  media: Dodajte medijski modul\n  settings: postavke\n  start: Dodajte modul za start\n  task_manager: Upravitelj zadataka\n  trash_bin: Dodajte modul koša za smeće\ntrash_bin:\n  empty_bin: Ispraznite koš za smeće\nweg:\n  empty: Nema stavki za prikaz\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/hu.yml",
    "content": "app_menu:\n  close: Bezárás\n  close_multiple: Zárja be az összeset\n  copy_handles: Másolás fogantyúk\n  kill: Kill Process\n  kill_multiple: Öld meg az összes folyamatot\n  open_file_location: Nyissa meg a fájl helyét\n  pin: Csap\n  pin_to_center: Pin a középpontba\n  pin_to_left: Pin balra\n  pin_to_right: Pin jobbra\n  run_as: Futtatás rendszergazdaként\n  unpin: Kibont\ncontext_menu:\n  remove_module: Modul eltávolítása\n  reorder_disable: Feladatsáv zárolása\n  reorder_enable: Feloldja a tálcát\nmedia:\n  not_playing: Semmi sem játszik\nshow_desktop:\n  label: Asztal megjelenítése\nstart:\n  label: Start menü\ntaskbar_menu:\n  add_file: Pin egyedi fájl\n  desktop: Asztali modul hozzáadása\n  media: Adjon hozzá média modult\n  settings: Beállítások elemre\n  start: Adja hozzá a Start modult\n  task_manager: Feladatkezelő\n  trash_bin: Adja hozzá a Lomtár modult\ntrash_bin:\n  empty_bin: Ürítse ki a Lomtárat\nweg:\n  empty: Nincs megjeleníthető elem\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/hy.yml",
    "content": "app_menu:\n  close: փակել\n  close_multiple: Փակել բոլորը\n  copy_handles: Պատճենել բռնակներ\n  kill: Սպանության գործընթաց\n  kill_multiple: Սպանել բոլոր գործընթացները\n  open_file_location: Բաց ֆայլի գտնվելու վայրը\n  pin: Թոշակ\n  pin_to_center: Քորոց դեպի կենտրոն\n  pin_to_left: PIN- ը ձախից\n  pin_to_right: Քորոց աջից\n  run_as: Գործարկել որպես ադմինիստրատոր\n  unpin: Անապաղում\ncontext_menu:\n  remove_module: Հեռացնել մոդուլը\n  reorder_disable: Կողպեք առաջադրանքներիբար\n  reorder_enable: Բացել աշխատանքային գիծը\nmedia:\n  not_playing: Ոչինչ չի խաղում\nshow_desktop:\n  label: Ցուցադրել աշխատասեղան\nstart:\n  label: Սկսել ցանկը\ntaskbar_menu:\n  add_file: PIN պատվերով ֆայլ\n  desktop: Ավելացնել աշխատասեղանի մոդուլ\n  media: Ավելացնել մեդիա մոդուլ\n  settings: Կարգավորումներ\n  start: Ավելացնել Սկսել մոդուլը\n  task_manager: Առաջադրանքների կառավարիչ\n  trash_bin: Ավելացնել թափոնարկղ մոդուլ\ntrash_bin:\n  empty_bin: Դատարկ թափոնարկղ\nweg:\n  empty: Ցուցադրելու տարրեր չկան\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/id.yml",
    "content": "app_menu:\n  close: Menutup\n  close_multiple: Tutup semua\n  copy_handles: Pegangan Salin\n  kill: Bunuh Proses\n  kill_multiple: Matikan Semua Proses\n  open_file_location: Buka Lokasi File\n  pin: Pin\n  pin_to_center: Pin ke tengah\n  pin_to_left: Pin ke kiri\n  pin_to_right: Pin ke kanan\n  run_as: Jalankan sebagai administrator\n  unpin: Membuka peniti\ncontext_menu:\n  remove_module: Hapus Modul\n  reorder_disable: Mengunci bilah tugas\n  reorder_enable: Buka kunci bilah tugas\nmedia:\n  not_playing: Tidak ada yang bermain\nshow_desktop:\n  label: Tampilkan Desktop\nstart:\n  label: Menu Mulai\ntaskbar_menu:\n  add_file: Pin file khusus\n  desktop: Tambahkan Modul Desktop\n  media: Tambahkan modul media\n  settings: Pengaturan\n  start: Tambahkan modul Mulai\n  task_manager: Manajer Tugas\n  trash_bin: Tambahkan Modul Recycle Bin\ntrash_bin:\n  empty_bin: Kosongkan Tempat Sampah\nweg:\n  empty: Tidak ada item untuk ditampilkan\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/is.yml",
    "content": "app_menu:\n  close: Lokaðu\n  close_multiple: Lokaðu öllu\n  copy_handles: Afrit handföng\n  kill: Kill Process\n  kill_multiple: Drepa alla ferla\n  open_file_location: Opnaðu staðsetningu skráar\n  pin: PIN\n  pin_to_center: Pinna í miðju\n  pin_to_left: Pinna til vinstri\n  pin_to_right: Pinna til hægri\n  run_as: Hlaupa sem stjórnandi\n  unpin: Taka úr\ncontext_menu:\n  remove_module: Fjarlægðu einingu\n  reorder_disable: Læstu verkstiku\n  reorder_enable: Opnaðu verkefnastiku\nmedia:\n  not_playing: Ekkert er að spila\nshow_desktop:\n  label: Sýna skjáborð\nstart:\n  label: Start Valmynd\ntaskbar_menu:\n  add_file: Pinna sérsniðna skrá\n  desktop: Bæta við skrifborðseiningu\n  media: Bættu við Media Module\n  settings: Stillingar\n  start: Bættu við Start Module\n  task_manager: Verkefnastjóri\n  trash_bin: Bæta við ruslatunnueiningu\ntrash_bin:\n  empty_bin: Tóm ruslatunnu\nweg:\n  empty: Engin atriði til að sýna\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/it.yml",
    "content": "app_menu:\n  close: Chiudi\n  close_multiple: Chiudi Tutto\n  copy_handles: Copia Handle\n  kill: Uccidi il processo\n  kill_multiple: Uccidi tutti i processi\n  open_file_location: Apri Percorso del File\n  pin: Fissa\n  pin_to_center: Fissa al Centro\n  pin_to_left: Fissa a Sinistra\n  pin_to_right: Fissa a Destra\n  run_as: Esegui come Amministratore\n  unpin: Rimuovi\ncontext_menu:\n  remove_module: Rimuovi modulo\n  reorder_disable: Blocco della barra delle applicazioni\n  reorder_enable: Sbloccare la barra delle applicazioni\nmedia:\n  not_playing: Nulla in riproduzione\nshow_desktop:\n  label: Mostra scrivania\nstart:\n  label: Menù di avvio\ntaskbar_menu:\n  add_file: Aggiungi File Personalizzato\n  desktop: Aggiungi modulo desktop\n  media: Aggiungi Modulo Media\n  settings: Impostazioni\n  start: Aggiungi Modulo Start\n  task_manager: Gestione Attività\n  trash_bin: Aggiungi il modulo Cestino\ntrash_bin:\n  empty_bin: Cestino vuoto\nweg:\n  empty: Nessun elemento da mostrare\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ja.yml",
    "content": "app_menu:\n  close: 閉じる\n  close_multiple: すべて閉じる\n  copy_handles: ハンドルをコピー\n  kill: プロセスを強制終了\n  kill_multiple: すべてのプロセスを強制終了\n  open_file_location: ファイルの場所を開く\n  pin: ピン留め\n  pin_to_center: 中央にピン留め\n  pin_to_left: 左にピン留め\n  pin_to_right: 右にピン留め\n  run_as: 管理者として実行\n  unpin: ピン留めを解除\ncontext_menu:\n  remove_module: モジュールの削除\n  reorder_disable: タスクバーをロック\n  reorder_enable: タスクバーのロック解除\nmedia:\n  not_playing: 何も再生されていません\nshow_desktop:\n  label: デスクトップを表示\nstart:\n  label: スタートメニュー\ntaskbar_menu:\n  add_file: ファイルを追加\n  desktop: デスクトップモジュールの追加\n  media: メディアモジュールを追加\n  settings: 設定\n  start: スタートメニューの追加\n  task_manager: タスクマネージャー\n  trash_bin: ごみ箱モジュールの追加\ntrash_bin:\n  empty_bin: ごみ箱を空にする\nweg:\n  empty: 表示する項目がありません\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ka.yml",
    "content": "app_menu:\n  close: ახლო\n  close_multiple: Დახურე ყველა\n  copy_handles: ასლის სახელურები\n  kill: მოკვლის პროცესი\n  kill_multiple: მოკალი ყველა პროცესი\n  open_file_location: გახსენით ფაილის ადგილმდებარეობა\n  pin: ქინძისთავი\n  pin_to_center: Pin to Center\n  pin_to_left: პინი მარცხნივ\n  pin_to_right: Pin მარჯვნივ\n  run_as: Ადმინისტრატორის სახელით გაშვება\n  unpin: გაუქმება\ncontext_menu:\n  remove_module: მოდულის ამოღება\n  reorder_disable: ჩაკეტეთ დავალების პანელი\n  reorder_enable: განბლოკეთ დავალების ზოლი\nmedia:\n  not_playing: არაფერი თამაშობს\nshow_desktop:\n  label: დესკტოპის ჩვენება\nstart:\n  label: დაწყება მენიუ\ntaskbar_menu:\n  add_file: Pin Custom File\n  desktop: დესკტოპის მოდულის დამატება\n  media: დაამატეთ მედია მოდული\n  settings: პარამეტრები\n  start: დაამატეთ დაწყების მოდული\n  task_manager: სამუშაო მენეჯერი\n  trash_bin: დაამატეთ Recycle Bin მოდული\ntrash_bin:\n  empty_bin: ცარიელი გადასამუშავებელი ურნა\nweg:\n  empty: საჩვენებელი ელემენტი არ არის\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/km.yml",
    "content": "app_menu:\n  close: បិត\n  close_multiple: បិទទាំងអស់\n  copy_handles: ចម្លងចំណុចទាញ\n  kill: ដំណើរការសម្លាប់\n  kill_multiple: សម្លាប់ដំណើរការទាំងអស់។\n  open_file_location: បើកទីតាំងឯកសារ\n  pin: ម្ជុល\n  pin_to_center: ម្ជុលទៅកណ្តាល\n  pin_to_left: ម្ជុលទៅឆ្វេង\n  pin_to_right: ម្ជុលទៅស្តាំ\n  run_as: ដំណើរការ​ជា​រ​អ្នកគ្រប់គ្រង\n  unpin: អក្គោអិន\ncontext_menu:\n  remove_module: យកម៉ូឌុលចេញ\n  reorder_disable: ចាក់សោរបារភារកិច្ច\n  reorder_enable: ដោះសោរបារភារកិច្ច\nmedia:\n  not_playing: គ្មានអ្វីកំពុងលេងទេ\nshow_desktop:\n  label: បង្ហាញផ្ទៃតុ\nstart:\n  label: ម៉ឺនុយចាប់ផ្តើម\ntaskbar_menu:\n  add_file: លេខកូដសម្ងាត់លេខសម្ងាត់\n  desktop: បន្ថែមម៉ូឌុលផ្ទៃតុ\n  media: បន្ថែមម៉ូឌុលមេឌៀបន្ថែម\n  settings: ការកំណត់\n  start: បន្ថែមម៉ូឌុលចាប់ផ្តើម\n  task_manager: កម្មវិធីគ្រប់គ្រងភារកិច្ច\n  trash_bin: បន្ថែមម៉ូឌុល Recycle Bin\ntrash_bin:\n  empty_bin: ធុងសំរាមទទេ\nweg:\n  empty: មិនមានធាតុដែលត្រូវបង្ហាញទេ។\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ko.yml",
    "content": "app_menu:\n  close: 닫기\n  close_multiple: 모두 닫기\n  copy_handles: 핸들 복사\n  kill: 프로세스 종료\n  kill_multiple: 모든 프로세스 종료\n  open_file_location: 파일 위치 열기\n  pin: 고정\n  pin_to_center: 중앙에 고정\n  pin_to_left: 왼쪽에 고정\n  pin_to_right: 오른쪽에 고정\n  run_as: 관리자 권한으로 실행\n  unpin: 고정 해제\ncontext_menu:\n  remove_module: 모듈 제거\n  reorder_disable: 작업 표시줄 잠금\n  reorder_enable: 작업 표시줄 잠금 해제\nmedia:\n  not_playing: 재생 중인 항목 없음\nshow_desktop:\n  label: 데스크탑 표시\nstart:\n  label: 시작 메뉴\ntaskbar_menu:\n  add_file: 사용자 정의 파일 추가\n  desktop: 데스크탑 모듈 추가\n  media: 미디어 모듈 추가\n  settings: 설정\n  start: 시작 버튼 추가\n  task_manager: 작업 관리자\n  trash_bin: 휴지통 모듈 추가\ntrash_bin:\n  empty_bin: 휴지통 비우기\nweg:\n  empty: 표시할 항목이 없습니다.\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ku.yml",
    "content": "app_menu:\n  close: Nêzîkî\n  close_multiple: Her tiştî bigire\n  copy_handles: Destên kopî bikin\n  kill: Pêvajoya Kuştinê\n  kill_multiple: Hemî Pêvajoyan Bikujin\n  open_file_location: Cihê pelê vekirî\n  pin: Derzî\n  pin_to_center: Pin to navenda\n  pin_to_left: Pin çepê\n  pin_to_right: Pin berbi rast\n  run_as: Wek Rêvebir bisekinin\n  unpin: Unpin\ncontext_menu:\n  remove_module: Modulê jêbirin\n  reorder_disable: Taskbar Lock\n  reorder_enable: Taskbar Unlock\nmedia:\n  not_playing: Tiştek nayê lîstin\nshow_desktop:\n  label: Sermaseyê nîşan bide\nstart:\n  label: Pêşeka Destpêkê\ntaskbar_menu:\n  add_file: Pelê Custom Pel\n  desktop: Modula Sermaseyê zêde bikin\n  media: Modela medyayê lê zêde bike\n  settings: Settings\n  start: Modulê dest pê bikin\n  task_manager: Gerînendetiya Peywiran\n  trash_bin: Modula Recycle Bin zêde bikin\ntrash_bin:\n  empty_bin: Vala Recycle Bin\nweg:\n  empty: Tiştek tune ku nîşan bide\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/lb.yml",
    "content": "app_menu:\n  close: Zoumaachen\n  close_multiple: Alles dobäi\n  copy_handles: Kopie Handelen\n  kill: Kill Prozess\n  kill_multiple: Kill All Prozesser\n  open_file_location: Open Dateiplaz\n  pin: Pin\n  pin_to_center: PIN op Zentrum\n  pin_to_left: PIN fir lénks\n  pin_to_right: PIN op riets\n  run_as: Lafen als Administrator\n  unpin: Net unsträichen\ncontext_menu:\n  remove_module: Ewechzehuelen Modul\n  reorder_disable: Lock Taskbar\n  reorder_enable: Spär Taskbar\nmedia:\n  not_playing: Näischt spillt\nshow_desktop:\n  label: Show Desktop\nstart:\n  label: Start Menu\ntaskbar_menu:\n  add_file: PIN personaliséiert Datei\n  desktop: Dobäizemaachen Desktop Modul\n  media: Füügt Media Modul bäi\n  settings: Astellungen\n  start: Füügt Start Modul\n  task_manager: Aufgab Manager\n  trash_bin: Recycle Bin Modul dobäisetzen\ntrash_bin:\n  empty_bin: Eidel Recycle Bin\nweg:\n  empty: Keng Elementer ze weisen\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/lo.yml",
    "content": "app_menu:\n  close: ປິດ\n  close_multiple: ປິດທັງຫມົດ\n  copy_handles: ຄັດລອກ\n  kill: ຂະບວນການຂ້າ\n  kill_multiple: ຂ້າຂະບວນການທັງຫມົດ\n  open_file_location: ເປີດສະຖານທີ່ເອກະສານ\n  pin: ເຂັມ PIN\n  pin_to_center: PIN ເຖິງສູນກາງ\n  pin_to_left: ເຂັມຊ້າຍໄປ\n  pin_to_right: PIN ກັບຂວາ\n  run_as: ດໍາເນີນການເປັນຜູ້ບໍລິຫານ\n  unpin: unppin\ncontext_menu:\n  remove_module: ເອົາໂມດູນອອກ\n  reorder_disable: ລັອກ task task\n  reorder_enable: ປົດລ taskbar taskbar\nmedia:\n  not_playing: ບໍ່ມີຫຍັງຫຼີ້ນ\nshow_desktop:\n  label: ສະແດງ Desktop\nstart:\n  label: ເມນູເລີ່ມຕົ້ນ\ntaskbar_menu:\n  add_file: PIN File Custom\n  desktop: ເພີ່ມໂມດູນ Desktop\n  media: ຕື່ມການໂມດູນສື່\n  settings: ການຕັ້ງຄ່າ\n  start: ເພີ່ມໂມດູນ Start\n  task_manager: Task Manager\n  trash_bin: ເພີ່ມໂມດູນ Recycle Bin\ntrash_bin:\n  empty_bin: ຖັງ Recycle ຫວ່າງເປົ່າ\nweg:\n  empty: ບໍ່ມີລາຍການໃຫ້ສະແດງ\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/lt.yml",
    "content": "app_menu:\n  close: Uždaryti\n  close_multiple: Uždaryk viską\n  copy_handles: Kopijavimo rankenos\n  kill: Nužudymo procesas\n  kill_multiple: Nužudyti visus procesus\n  open_file_location: Atidarykite failo vietą\n  pin: Kaištis\n  pin_to_center: Kaištis iki centro\n  pin_to_left: Kaištis į kairę\n  pin_to_right: Kaištis į dešinę\n  run_as: Vykdykite kaip administratorius\n  unpin: Unpin\ncontext_menu:\n  remove_module: Pašalinti modulį\n  reorder_disable: Užrakinti užduočių juostą\n  reorder_enable: Užduočių juostos atrakinimas\nmedia:\n  not_playing: Niekas nežaidžia\nshow_desktop:\n  label: Rodyti darbalaukį\nstart:\n  label: Pradėti meniu\ntaskbar_menu:\n  add_file: PIN pasirinktinis failas\n  desktop: Pridėti darbalaukio modulį\n  media: Pridėkite laikmenos modulį\n  settings: Nustatymai\n  start: Pridėkite pradžios modulį\n  task_manager: Užduočių tvarkyklė\n  trash_bin: Pridėkite šiukšliadėžės modulį\ntrash_bin:\n  empty_bin: Ištuštinkite šiukšliadėžę\nweg:\n  empty: Nėra elementų, kuriuos būtų galima rodyti\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/lv.yml",
    "content": "app_menu:\n  close: Tuvs\n  close_multiple: Aizvērt visu\n  copy_handles: Kopēt rokturus\n  kill: Nogalināšanas process\n  kill_multiple: Nogalināt visus procesus\n  open_file_location: Atveriet faila atrašanās vietu\n  pin: Piespraust\n  pin_to_center: Piespraudiet centrā\n  pin_to_left: Piespraudiet pa kreisi\n  pin_to_right: Piespraudiet labajā pusē\n  run_as: Izpildīt kā administratoram\n  unpin: Neslēpt\ncontext_menu:\n  remove_module: Noņemiet moduli\n  reorder_disable: Uzdevumjoslas bloķēšana\n  reorder_enable: Uzdevumjoslas atbloķēšana\nmedia:\n  not_playing: Nekas nespēlē\nshow_desktop:\n  label: Rādīt darbvirsmu\nstart:\n  label: Sākt izvēlne\ntaskbar_menu:\n  add_file: Piesprauž pielāgotu failu\n  desktop: Pievienojiet darbvirsmas moduli\n  media: Pievienojiet multivides moduli\n  settings: Iestatījumi\n  start: Pievienojiet starta moduli\n  task_manager: Uzdevumu pārvaldnieks\n  trash_bin: Pievienojiet atkritnes moduli\ntrash_bin:\n  empty_bin: Iztukšojiet atkritni\nweg:\n  empty: Nav vienumu, ko parādīt\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/mk.yml",
    "content": "app_menu:\n  close: Затвори\n  close_multiple: Затворете ги сите\n  copy_handles: Копирање рачки\n  kill: Процес на убивање\n  kill_multiple: Убиј ги сите процеси\n  open_file_location: Отворете ја локацијата на датотеката\n  pin: Игла\n  pin_to_center: Игла до центар\n  pin_to_left: Игла на лево\n  pin_to_right: Игла надесно\n  run_as: Работи како администратор\n  unpin: Unpin\ncontext_menu:\n  remove_module: Отстранете го модулот\n  reorder_disable: Заклучете ја лентата со задачи\n  reorder_enable: Отклучете ја лентата со задачи\nmedia:\n  not_playing: Ништо не игра\nshow_desktop:\n  label: Прикажи работна површина\nstart:\n  label: Старт мени\ntaskbar_menu:\n  add_file: Пинк за прилагодена датотека\n  desktop: Додадете модул за работна површина\n  media: Додадете медиумски модул\n  settings: Поставки\n  start: Додадете модул за почеток\n  task_manager: Управувач со задачи\n  trash_bin: Додадете модул за корпа за отпадоци\ntrash_bin:\n  empty_bin: Празна корпа за отпадоци\nweg:\n  empty: Нема ставки за прикажување\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/mn.yml",
    "content": "app_menu:\n  close: Ойр дөхөм\n  close_multiple: Бүгдийг хаах\n  copy_handles: Хуулбарлах\n  kill: Алах үйл явц\n  kill_multiple: Бүх процессыг устгах\n  open_file_location: Нээлттэй файлын байршлыг нээнэ үү\n  pin: Сулбээр зүү\n  pin_to_center: Төв рүү зүү\n  pin_to_left: Зүүн тийш зүү\n  pin_to_right: Баруун тийш зүү\n  run_as: Администратороор ажиллуулах\n  unpin: Нууц\ncontext_menu:\n  remove_module: Модуль устгах\n  reorder_disable: Түгжих ажлын самбар\n  reorder_enable: Taskbar түгжээг тайл\nmedia:\n  not_playing: Юу ч тоглоогүй байна\nshow_desktop:\n  label: Ширээний компьютерийг харуулах\nstart:\n  label: Эхлэх цэс\ntaskbar_menu:\n  add_file: PIN захиалгын файл\n  desktop: Ширээний модуль нэмнэ үү\n  media: Медиа модулийг нэмнэ\n  settings: Тохиргоо\n  start: Эхлэх модулийг нэмнэ үү\n  task_manager: Ажлын менежер\n  trash_bin: Хогийн савны модулийг нэмнэ үү\ntrash_bin:\n  empty_bin: Хогийн савыг хоосон\nweg:\n  empty: Үзүүлэх зүйл алга\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ms.yml",
    "content": "app_menu:\n  close: Tutup\n  close_multiple: Tutup semua\n  copy_handles: Salin pemegang\n  kill: Proses Bunuh\n  kill_multiple: Bunuh Semua Proses\n  open_file_location: Buka lokasi fail\n  pin: Pin\n  pin_to_center: Pin ke pusat\n  pin_to_left: Pin ke kiri\n  pin_to_right: Pin ke kanan\n  run_as: Jalankan sebagai pentadbir\n  unpin: Unpin\ncontext_menu:\n  remove_module: Keluarkan Modul\n  reorder_disable: Kunci bar tugas\n  reorder_enable: Buka kunci bar tugas\nmedia:\n  not_playing: Tidak ada yang bermain\nshow_desktop:\n  label: Tunjukkan Desktop\nstart:\n  label: Menu Mula\ntaskbar_menu:\n  add_file: Fail tersuai pin\n  desktop: Tambah Modul Desktop\n  media: Tambah Modul Media\n  settings: tetapan\n  start: Tambah Modul Mula\n  task_manager: Pengurus Tugas\n  trash_bin: Tambah Modul Tong Kitar Semula\ntrash_bin:\n  empty_bin: Tong Kitar Semula Kosong\nweg:\n  empty: Tiada item untuk ditunjukkan\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/mt.yml",
    "content": "app_menu:\n  close: Viċin\n  close_multiple: Agħlaq ilkoll\n  copy_handles: Kopja pumi\n  kill: Qtil Proċess\n  kill_multiple: Oqtol il-Proċessi Kollha\n  open_file_location: Post tal-fajl miftuħ\n  pin: Pin\n  pin_to_center: Pin għal ċentru\n  pin_to_left: Pin lejn ix-xellug\n  pin_to_right: Pin għal dritt\n  run_as: Mexxi bħala amministratur\n  unpin: Unpin\ncontext_menu:\n  remove_module: Neħħi Modulu\n  reorder_disable: Lock Taskbar\n  reorder_enable: Nisfruttaw taskbar\nmedia:\n  not_playing: Xejn ma jilgħab\nshow_desktop:\n  label: Uri Desktop\nstart:\n  label: Start Menu\ntaskbar_menu:\n  add_file: Fajl tad-dwana pin\n  desktop: Żid il-Modulu tad-Desktop\n  media: Żid il-modulu tal-midja\n  settings: Settings\n  start: Żid il-modulu tal-bidu\n  task_manager: Task Manager\n  trash_bin: Żid Recycle Bin Modulu\ntrash_bin:\n  empty_bin: Vojta Recycle Bin\nweg:\n  empty: Ebda oġġetti li juru\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ne.yml",
    "content": "app_menu:\n  close: घनिष्ट\n  close_multiple: चौमा\n  copy_handles: मुख्य ह्यान्डल\n  kill: प्रक्रिया मार्नुहोस्\n  kill_multiple: सबै प्रक्रियाहरू मार्नुहोस्\n  open_file_location: फाइल स्थान खोल्नुहोस्\n  pin: आलपिन\n  pin_to_center: केन्द्रमा पिन\n  pin_to_left: बाँया बायाँ\n  pin_to_right: दायाँ राख्न पिन\n  run_as: प्रशासक रूपमा चलाउन\n  unpin: चुस्नु\ncontext_menu:\n  remove_module: मोड्युल हटाउनुहोस्\n  reorder_disable: लक टकबार लक\n  reorder_enable: टास्कबार अनलक गर्नुहोस्\nmedia:\n  not_playing: केहि खेल्दैन\nshow_desktop:\n  label: डेस्कटप देखाउनुहोस्\nstart:\n  label: सुरु मेनु\ntaskbar_menu:\n  add_file: पिन अनुकूलन फाइल\n  desktop: डेस्कटप मोड्युल थप्नुहोस्\n  media: मिडिया मोड्युल थप्नुहोस्\n  settings: सेटिङहरू\n  start: स्टार्ट मोड्युल थप्नुहोस्\n  task_manager: कार्य प्रबन्धक\n  trash_bin: रिसायकल बिन मोड्युल थप्नुहोस्\ntrash_bin:\n  empty_bin: रिसायकल बिन खाली गर्नुहोस्\nweg:\n  empty: देखाउनका लागि कुनै वस्तुहरू छैनन्\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/nl.yml",
    "content": "app_menu:\n  close: Sluiten\n  close_multiple: Sluit alles\n  copy_handles: Kopieer handgrepen\n  kill: Dood proces\n  kill_multiple: Dood alle processen\n  open_file_location: Open de bestandslocatie\n  pin: Pin\n  pin_to_center: Pin in het midden\n  pin_to_left: Pin naar links\n  pin_to_right: Pin naar rechts\n  run_as: Als administrator uitvoeren\n  unpin: Losmaken\ncontext_menu:\n  remove_module: Module verwijderen\n  reorder_disable: Taakbalk vergrendelen\n  reorder_enable: Taakbalk ontgrendelen\nmedia:\n  not_playing: Niets speelt\nshow_desktop:\n  label: Bureaublad weergeven\nstart:\n  label: Startmenu\ntaskbar_menu:\n  add_file: Pin aangepaste bestand\n  desktop: Voeg bureaubladmodule toe\n  media: Voeg mediamodule toe\n  settings: Instellingen\n  start: Startmodule toevoegen\n  task_manager: Taakbeheer\n  trash_bin: Voeg Prullenbakmodule toe\ntrash_bin:\n  empty_bin: Prullenbak leegmaken\nweg:\n  empty: Er zijn geen items om te tonen\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/no.yml",
    "content": "app_menu:\n  close: Lukk\n  close_multiple: Lukk alle\n  copy_handles: Kopier håndtak\n  kill: Drep prosessen\n  kill_multiple: Drep alle prosesser\n  open_file_location: Åpne fil plassering\n  pin: Pin\n  pin_to_center: Pin til sentrum\n  pin_to_left: Pin til venstre\n  pin_to_right: Pin til høyre\n  run_as: Kjør som administrator\n  unpin: Upinn\ncontext_menu:\n  remove_module: Fjern modulen\n  reorder_disable: Lås oppgavelinje\n  reorder_enable: Lås opp oppgavelinje\nmedia:\n  not_playing: Ingenting spiller\nshow_desktop:\n  label: Vis skrivebord\nstart:\n  label: Start-menyen\ntaskbar_menu:\n  add_file: Pin tilpasset fil\n  desktop: Legg til skrivebordsmodul\n  media: Legg til mediemodul\n  settings: Innstillinger\n  start: Legg til startmodul\n  task_manager: Oppgavebehandling\n  trash_bin: Legg til papirkurvmodul\ntrash_bin:\n  empty_bin: Tøm papirkurven\nweg:\n  empty: Ingen elementer å vise\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/pa.yml",
    "content": "app_menu:\n  close: ਨੇੜੇ\n  close_multiple: ਸਾਰੇ ਬੰਦ ਕਰੋ\n  copy_handles: ਕਾੱਪੀ ਹੈਂਡਲ\n  kill: ਪ੍ਰਕਿਰਿਆ ਨੂੰ ਮਾਰੋ\n  kill_multiple: ਸਾਰੀਆਂ ਪ੍ਰਕਿਰਿਆਵਾਂ ਨੂੰ ਮਾਰੋ\n  open_file_location: ਓਪਨ ਫਾਈਲ ਟਿਕਾਣਾ\n  pin: ਪਿੰਨ\n  pin_to_center: ਕੇਂਦਰ ਨੂੰ ਪਿੰਨ\n  pin_to_left: ਪਿੰਨ ਛੱਡ ਕੇ\n  pin_to_right: ਪਿੰਨ ਕਰਨ ਲਈ\n  run_as: ਪ੍ਰਬੰਧਕ ਦੇ ਤੌਰ ਤੇ ਚਲਾਓ\n  unpin: Unpin\ncontext_menu:\n  remove_module: ਮੋਡੀਊਲ ਹਟਾਓ\n  reorder_disable: ਟਾਸਕਬਾਰ ਨੂੰ ਲਾਕ ਕਰੋ\n  reorder_enable: ਟਾਸਕਬਾਰ ਨੂੰ ਅਨਲੌਕ ਕਰੋ\nmedia:\n  not_playing: ਕੁਝ ਨਹੀਂ ਖੇਡ ਰਿਹਾ\nshow_desktop:\n  label: ਡੈਸਕਟਾਪ ਦਿਖਾਓ\nstart:\n  label: ਸਟਾਰਟ ਮੀਨੂ\ntaskbar_menu:\n  add_file: ਪਿੰਨ ਕਸਟਮ ਫਾਈਲ\n  desktop: ਡੈਸਕਟਾਪ ਮੋਡੀਊਲ ਸ਼ਾਮਲ ਕਰੋ\n  media: ਮੀਡੀਆ ਮੋਡੀ .ਲ ਸ਼ਾਮਲ ਕਰੋ\n  settings: ਸੈਟਿੰਗਾਂ\n  start: ਸਟਾਰਟ ਮੋਡੀ ule ਲ ਸ਼ਾਮਲ ਕਰੋ\n  task_manager: ਟਾਸਕ ਮੈਨੇਜਰ\n  trash_bin: ਰੀਸਾਈਕਲ ਬਿਨ ਮੋਡੀਊਲ ਸ਼ਾਮਲ ਕਰੋ\ntrash_bin:\n  empty_bin: ਖਾਲੀ ਰੀਸਾਈਕਲ ਬਿਨ\nweg:\n  empty: ਦਿਖਾਉਣ ਲਈ ਕੋਈ ਆਈਟਮਾਂ ਨਹੀਂ ਹਨ\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/pl.yml",
    "content": "app_menu:\n  close: Zamknij\n  close_multiple: Zamknij wszystko\n  copy_handles: Kopiuj\n  kill: Wymuś zamknięcie\n  kill_multiple: Wymuś zamknięcie wszystkich procesów\n  open_file_location: Otwórz lokalizację pliku\n  pin: Przypnij\n  pin_to_center: Przypnij na środku\n  pin_to_left: Przypnij po lewej\n  pin_to_right: Przypnij po prawej\n  run_as: Uruchom jako administrator\n  unpin: Odepnij\ncontext_menu:\n  remove_module: Usuń moduł\n  reorder_disable: Zablokuj pasek zadań\n  reorder_enable: Odblokuj pasek zadań\nmedia:\n  not_playing: Nic nie gra\nshow_desktop:\n  label: Pokaż pulpit\nstart:\n  label: Menu startowe\ntaskbar_menu:\n  add_file: Dodaj skrót\n  desktop: Dodaj moduł pulpitu\n  media: Dodaj moduł multimediów\n  settings: Ustawienia\n  start: Dodaj przycisk Start\n  task_manager: Menedżer zadań\n  trash_bin: Dodaj moduł Kosza\ntrash_bin:\n  empty_bin: Opróżnij Kosz\nweg:\n  empty: Brak elementów do pokazania\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ps.yml",
    "content": "app_menu:\n  close: بندول\n  close_multiple: ټول وتړئ\n  copy_handles: لاسوندونه کاپي کوي\n  kill: د وژلو پروسه\n  kill_multiple: ټولې پروسې وژني\n  open_file_location: د فایل ځای خلاص کړئ\n  pin: پن\n  pin_to_center: مرکز ته پن\n  pin_to_left: د کی to اړخ ته پن\n  pin_to_right: ښیې خوا ته\n  run_as: د مدیر په توګه چلول\n  unpin: غیر رنګ\ncontext_menu:\n  remove_module: موډل لرې کړئ\n  reorder_disable: ټاسک بار\n  reorder_enable: د ټاسلف بار\nmedia:\n  not_playing: هیڅ شی نه لوبیږی\nshow_desktop:\n  label: ډیسټاپ وښایاست\nstart:\n  label: د پیل مینو\ntaskbar_menu:\n  add_file: دود دودیز فایل\n  desktop: د ډیسټاپ ماډل اضافه کړئ\n  media: د میډیا موډول اضافه کړئ\n  settings: ترتیبات\n  start: د پیل ماډل اضافه کړئ\n  task_manager: د دندې مدیر\n  trash_bin: د ریسایکل بن ماډل اضافه کړئ\ntrash_bin:\n  empty_bin: د ریسایکل بن خالي کړئ\nweg:\n  empty: د ښودلو لپاره هیڅ توکي نشته\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/pt-BR.yml",
    "content": "app_menu:\n  close: Fechar\n  close_multiple: Fechar tudo\n  copy_handles: Copiar identificadores\n  kill: Encerrar processo\n  kill_multiple: Encerrar todos os processos\n  open_file_location: Abrir local do arquivo\n  pin: Fixar\n  pin_to_center: Fixar no centro\n  pin_to_left: Fixar à esquerda\n  pin_to_right: Fixar à direita\n  run_as: Executar como administrador\n  unpin: Desafixar\ncontext_menu:\n  remove_module: Remover Módulo\n  reorder_disable: Bloquear barra de tarefas\n  reorder_enable: Desbloquear barra de tarefas\nmedia:\n  not_playing: Nada está tocando\nshow_desktop:\n  label: Mostrar área de trabalho\nstart:\n  label: Menu Iniciar\ntaskbar_menu:\n  add_file: Fixar arquivo personalizado\n  desktop: Adicionar módulo de área de trabalho\n  media: Adicionar módulo de mídia\n  settings: Configurações\n  start: Adicionar módulo Iniciar\n  task_manager: Gerenciador de Tarefas\n  trash_bin: Adicionar módulo de lixeira\ntrash_bin:\n  empty_bin: Esvaziar Lixeira\nweg:\n  empty: Nenhum item para mostrar\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/pt-PT.yml",
    "content": "app_menu:\n  close: Perto\n  close_multiple: Feche tudo\n  copy_handles: Alças de cópia\n  kill: Processo de matar\n  kill_multiple: Mate todos os processos\n  open_file_location: Localização do arquivo aberto\n  pin: Pino\n  pin_to_center: Pino para centralizar\n  pin_to_left: Pino para a esquerda\n  pin_to_right: Pino para a direita\n  run_as: Executar como administrador\n  unpin: Não\ncontext_menu:\n  remove_module: Remover Módulo\n  reorder_disable: Bloquear barra de tarefas\n  reorder_enable: Desbloqueie a barra de tarefas\nmedia:\n  not_playing: Nada está tocando\nshow_desktop:\n  label: Mostrar área de trabalho\nstart:\n  label: Menu Iniciar\ntaskbar_menu:\n  add_file: PIN File personalizado\n  desktop: Adicionar módulo de área de trabalho\n  media: Adicione módulo de mídia\n  settings: Configurações\n  start: Adicione o módulo inicial\n  task_manager: Gerente de tarefas\n  trash_bin: Adicionar módulo de reciclagem\ntrash_bin:\n  empty_bin: Esvaziar Lixeira\nweg:\n  empty: Sem itens para mostrar\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ro.yml",
    "content": "app_menu:\n  close: Închide\n  close_multiple: Inchide tot\n  copy_handles: Copiați mânerele\n  kill: Procesul de ucidere\n  kill_multiple: Omoara toate procesele\n  open_file_location: Deschide locația fișierului\n  pin: Ac\n  pin_to_center: Pin în centru\n  pin_to_left: Pin la stânga\n  pin_to_right: Pin la dreapta\n  run_as: Rulat ca administrator\n  unpin: Unpin\ncontext_menu:\n  remove_module: Eliminați modulul\n  reorder_disable: Blocarea barei de activități\n  reorder_enable: Deblocați bara de activități\nmedia:\n  not_playing: Nimic nu se joacă\nshow_desktop:\n  label: Arată Desktop\nstart:\n  label: Meniul Start\ntaskbar_menu:\n  add_file: Fișier personalizat\n  desktop: Adăugați modul desktop\n  media: Adăugați modul media\n  settings: Setări\n  start: Adăugați modulul de pornire\n  task_manager: Manager de activități\n  trash_bin: Adăugați modulul Coș de reciclare\ntrash_bin:\n  empty_bin: Goliți coșul de reciclare\nweg:\n  empty: Nu există elemente de afișat\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ru.yml",
    "content": "app_menu:\n  close: Закрыть\n  close_multiple: Закрыть все\n  copy_handles: Копировать дескрипторы\n  kill: Завершить процесс\n  kill_multiple: Завершить все процессы\n  open_file_location: Открыть местоположение файла\n  pin: Закрепить\n  pin_to_center: По центру\n  pin_to_left: Слева\n  pin_to_right: Справа\n  run_as: Запустить от администратора\n  unpin: Открепить\ncontext_menu:\n  remove_module: Удалить модуль\n  reorder_disable: Заблокировать панель\n  reorder_enable: Разблокировать панель\nmedia:\n  not_playing: Ничего не играет\nshow_desktop:\n  label: Показать рабочий стол\nstart:\n  label: Меню «Пуск»\ntaskbar_menu:\n  add_file: Добавить файл\n  desktop: Добавить модуль рабочего стола\n  media: Добавить модуль медиа\n  settings: Настройки\n  start: Добавить модуль «Пуск»\n  task_manager: Диспетчер задач\n  trash_bin: Добавить модуль корзины\ntrash_bin:\n  empty_bin: Пустая корзина\nweg:\n  empty: Нет элементов для отображения\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/si.yml",
    "content": "app_menu:\n  close: වසන්න\n  close_multiple: සියල්ල වසා දමන්න\n  copy_handles: හසුරුමය\n  kill: කිල් ක්‍රියාවලිය\n  kill_multiple: සියලුම ක්‍රියාවලීන් මරා දමන්න\n  open_file_location: ගොනුව විවෘත ස්ථානය\n  pin: පින්\n  pin_to_center: මධ්යස්ථානයට පින් කරන්න\n  pin_to_left: වමට\n  pin_to_right: පින් කරන්න දකුණට\n  run_as: පරිපාලක ලෙස ක්රියාත්මක වන්න\n  unpin: UNTIN\ncontext_menu:\n  remove_module: මොඩියුලය ඉවත් කරන්න\n  reorder_disable: අගුළු කාර්ය තීරුව\n  reorder_enable: කාර්ය තීරු අගුළු ඇරීම\nmedia:\n  not_playing: කිසිවක් සෙල්ලම් කරන්නේ නැත\nshow_desktop:\n  label: ඩෙස්ක්ටොප් පෙන්වන්න\nstart:\n  label: ආරම්භක මෙනුව\ntaskbar_menu:\n  add_file: පින් අභිරුචි ගොනුව\n  desktop: ඩෙස්ක්ටොප් මොඩියුලය එක් කරන්න\n  media: මාධ්ය මොඩියුලය එක් කරන්න\n  settings: සැකසීම්\n  start: ආරම්භක මොඩියුලය එක් කරන්න\n  task_manager: කාර්ය කළමනාකරු\n  trash_bin: Recycle Bin Module එක එකතු කරන්න\ntrash_bin:\n  empty_bin: හිස් ප්‍රතිචක්‍රීකරණ බඳුන\nweg:\n  empty: පෙන්වීමට අයිතම නැත\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/sk.yml",
    "content": "app_menu:\n  close: Zavrieť\n  close_multiple: Zavrieť\n  copy_handles: Kopírovanie rukovätí\n  kill: Proces zabíjania\n  kill_multiple: Zabi všetky procesy\n  open_file_location: Otvorte umiestnenie súboru\n  pin: Pin\n  pin_to_center: Špendlík\n  pin_to_left: Špendlík doľava\n  pin_to_right: Špendlík\n  run_as: Spustiť ako správca\n  unpin: Zaniknúť\ncontext_menu:\n  remove_module: Odstráňte modul\n  reorder_disable: Uzamknutie panela úloh\n  reorder_enable: Odomknutie panela úloh\nmedia:\n  not_playing: Nič nehrá\nshow_desktop:\n  label: Zobraziť pracovnú plochu\nstart:\n  label: Ponuka Štart\ntaskbar_menu:\n  add_file: Vlastný súbor\n  desktop: Pridať modul pracovnej plochy\n  media: Pridať mediálny modul\n  settings: Nastavenia\n  start: Pridať štartový modul\n  task_manager: Správca úloh\n  trash_bin: Pridať modul koša\ntrash_bin:\n  empty_bin: Vyprázdniť kôš\nweg:\n  empty: Žiadne položky na zobrazenie\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/so.yml",
    "content": "app_menu:\n  close: Xirid\n  close_multiple: Xir dhammaan\n  copy_handles: Nuqullada gacanta\n  kill: Habka dilka\n  kill_multiple: Dila Dhammaan Nidaamyada\n  open_file_location: Goobta faylka furan\n  pin: Musbaar yar\n  pin_to_center: PIN ilaa Xarunta\n  pin_to_left: PIN ilaa bidix\n  pin_to_right: PIN ilaa midig\n  run_as: U ordo sidii maamule\n  unpin: Aan qarin\ncontext_menu:\n  remove_module: Ka saar Module\n  reorder_disable: Qufulka Qufulka\n  reorder_enable: Unlock Taskbarbar\nmedia:\n  not_playing: Ma jiraan wax ciyaaraya\nshow_desktop:\n  label: Muuji Desktop\nstart:\n  label: Bilow Menu\ntaskbar_menu:\n  add_file: Faylka caadada PIN\n  desktop: Kudar Module Desktop\n  media: Ku dar module warbaahinta\n  settings: Dejinta\n  start: Ku dar cutubka bilowga\n  task_manager: Maareeyaha Hawsha\n  trash_bin: Kudar Dib-u-warshadaynta Bin Module\ntrash_bin:\n  empty_bin: Dib u warshadaynta madhan\nweg:\n  empty: Ma jiraan waxyaabo la tuso\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/sr.yml",
    "content": "app_menu:\n  close: Близу\n  close_multiple: Затворите све\n  copy_handles: Копирајте ручке\n  kill: Килл Процесс\n  kill_multiple: Убијте све процесе\n  open_file_location: Отворите локацију датотеке\n  pin: Пин\n  pin_to_center: ПИН до центра\n  pin_to_left: Игните лево\n  pin_to_right: Пин удесно\n  run_as: Покрени као администратор\n  unpin: Развелити\ncontext_menu:\n  remove_module: Уклоните модул\n  reorder_disable: Закључајте траку задатака\n  reorder_enable: Откључајте траку задатака\nmedia:\n  not_playing: Ништа се не игра\nshow_desktop:\n  label: Прикажи радну површину\nstart:\n  label: Старт Мену\ntaskbar_menu:\n  add_file: ПИН Прилагођена датотека\n  desktop: Додајте модул радне површине\n  media: Додајте медијски модул\n  settings: Подешавања\n  start: Додајте почетни модул\n  task_manager: Таск Манагер\n  trash_bin: Додајте модул корпе за отпатке\ntrash_bin:\n  empty_bin: Празна корпа за отпатке\nweg:\n  empty: Нема ставки за приказ\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/sv.yml",
    "content": "app_menu:\n  close: Stänga\n  close_multiple: Stäng alla\n  copy_handles: Kopieringshandtag\n  kill: Kill Process\n  kill_multiple: Döda alla processer\n  open_file_location: Öppna filplats\n  pin: Stift\n  pin_to_center: Stift till mitten\n  pin_to_left: Stämma till vänster\n  pin_to_right: Nöja sig till höger\n  run_as: Kör som administratör\n  unpin: Lutande\ncontext_menu:\n  remove_module: Ta bort modulen\n  reorder_disable: Lås aktivitetsfältet\n  reorder_enable: Lås upp aktivitetsfältet\nmedia:\n  not_playing: Ingenting spelar\nshow_desktop:\n  label: Visa skrivbordet\nstart:\n  label: Startmenyn\ntaskbar_menu:\n  add_file: Pin Custom File\n  desktop: Lägg till skrivbordsmodul\n  media: Lägg till mediemodul\n  settings: Inställningar\n  start: Lägg till startmodul\n  task_manager: Task Manager\n  trash_bin: Lägg till papperskorgsmodul\ntrash_bin:\n  empty_bin: Töm papperskorgen\nweg:\n  empty: Inga objekt att visa\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/sw.yml",
    "content": "app_menu:\n  close: Karibu\n  close_multiple: Funga yote\n  copy_handles: Nakili Hushughulikia\n  kill: Mchakato wa kuua\n  kill_multiple: Kuua Taratibu Zote\n  open_file_location: Fungua eneo la faili\n  pin: Pini\n  pin_to_center: Pini katikati\n  pin_to_left: Pini kushoto\n  pin_to_right: Pini kulia\n  run_as: Kukimbia kama msimamizi\n  unpin: Unpin\ncontext_menu:\n  remove_module: Ondoa Moduli\n  reorder_disable: Lock Taskbar\n  reorder_enable: Fungua Taskbar\nmedia:\n  not_playing: Hakuna kinachocheza\nshow_desktop:\n  label: Onyesha Eneo-kazi\nstart:\n  label: Anza Menyu\ntaskbar_menu:\n  add_file: Piga faili ya kawaida\n  desktop: Ongeza Moduli ya Eneo-kazi\n  media: Ongeza moduli ya media\n  settings: Mipangilio\n  start: Ongeza moduli ya kuanza\n  task_manager: Meneja wa Kazi\n  trash_bin: Ongeza moduli ya Recycle Bin\ntrash_bin:\n  empty_bin: Bin Tupu ya Kusaga\nweg:\n  empty: Hakuna vipengee vya kuonyesha\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ta.yml",
    "content": "app_menu:\n  close: நெருக்கமான\n  close_multiple: அனைத்தையும் மூடு\n  copy_handles: நகல் கைப்பிடிகள்\n  kill: கொலை செயல்முறை\n  kill_multiple: அனைத்து செயல்முறைகளையும் கொல்லுங்கள்\n  open_file_location: கோப்பு இருப்பிடத்தைத் திறக்கவும்\n  pin: முள்\n  pin_to_center: மையத்திற்கு முள்\n  pin_to_left: இடதுபுறம் முள்\n  pin_to_right: வலதுபுறம் முள்\n  run_as: நிர்வாகியாக செயல்படுங்கள்\n  unpin: Unpin\ncontext_menu:\n  remove_module: தொகுதியை அகற்று\n  reorder_disable: பணிப்பட்டியைப் பூட்டவும்\n  reorder_enable: பணிப்பட்டியைத் திறக்கவும்\nmedia:\n  not_playing: எதுவும் விளையாடுவதில்லை\nshow_desktop:\n  label: டெஸ்க்டாப்பைக் காட்டு\nstart:\n  label: தொடக்க மெனு\ntaskbar_menu:\n  add_file: தனிப்பயன் கோப்பை முள்\n  desktop: டெஸ்க்டாப் தொகுதியைச் சேர்க்கவும்\n  media: மீடியா தொகுதி சேர்க்கவும்\n  settings: அமைப்புகள்\n  start: தொடக்க தொகுதி சேர்க்கவும்\n  task_manager: பணி மேலாளர்\n  trash_bin: மறுசுழற்சி தொட்டி தொகுதியைச் சேர்க்கவும்\ntrash_bin:\n  empty_bin: காலி மறுசுழற்சி தொட்டி\nweg:\n  empty: காட்ட உருப்படிகள் இல்லை\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/te.yml",
    "content": "app_menu:\n  close: దగ్గరగా\n  close_multiple: అన్నీ మూసివేయండి\n  copy_handles: కాపీ హ్యాండిల్స్\n  kill: కిల్ ప్రాసెస్\n  kill_multiple: అన్ని ప్రక్రియలను చంపండి\n  open_file_location: ఫైల్ స్థానం తెరవండి\n  pin: పిన్\n  pin_to_center: పిన్ టు సెంటర్\n  pin_to_left: పిన్ టు లెఫ్ట్\n  pin_to_right: కుడి నుండి పిన్\n  run_as: నిర్వాహకుడిగా అమలు చేయండి\n  unpin: అన్పిన్\ncontext_menu:\n  remove_module: మాడ్యూల్‌ని తీసివేయండి\n  reorder_disable: టాస్క్‌బార్ లాక్\n  reorder_enable: టాస్క్‌బార్‌ను అన్‌లాక్ చేయండి\nmedia:\n  not_playing: ఏమీ ఆడటం లేదు\nshow_desktop:\n  label: డెస్క్‌టాప్‌ని చూపించు\nstart:\n  label: ప్రారంభ మెను\ntaskbar_menu:\n  add_file: కస్టమ్ ఫైల్‌ను పిన్ చేయండి\n  desktop: డెస్క్‌టాప్ మాడ్యూల్‌ని జోడించండి\n  media: మీడియా మాడ్యూల్ జోడించండి\n  settings: సెట్టింగ్‌లు\n  start: ప్రారంభ మాడ్యూల్ జోడించండి\n  task_manager: టాస్క్ మేనేజర్\n  trash_bin: రీసైకిల్ బిన్ మాడ్యూల్ జోడించండి\ntrash_bin:\n  empty_bin: ఖాళీ రీసైకిల్ బిన్\nweg:\n  empty: చూపడానికి అంశాలు లేవు\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/tg.yml",
    "content": "app_menu:\n  close: Наздик\n  close_multiple: Ҳамаашро пӯшед\n  copy_handles: Дасткор\n  kill: Раванди куштан\n  kill_multiple: Ҳама равандҳоро кушед\n  open_file_location: Кушодани макони кушод\n  pin: PIN\n  pin_to_center: PIN ба марказ\n  pin_to_left: PIN ба чап\n  pin_to_right: PIN ба рост\n  run_as: Ҳамчун мудир кор кунед\n  unpin: Ватанӣ\ncontext_menu:\n  remove_module: Модулро хориҷ кунед\n  reorder_disable: Қулфи супоришҳо\n  reorder_enable: Қулфи панел\nmedia:\n  not_playing: Ҳеҷ чиз бозӣ намекунад\nshow_desktop:\n  label: Мизи кориро нишон диҳед\nstart:\n  label: Менюи оғоз\ntaskbar_menu:\n  add_file: Файли фармоишии PIN\n  desktop: Иловаи модули мизи корӣ\n  media: Иловаи модули ВАО\n  settings: Танзимотҳо\n  start: Иловаи модули оғоз\n  task_manager: Менеҷери вазифа\n  trash_bin: Модули қуттии партовро илова кунед\ntrash_bin:\n  empty_bin: Қуттии партови холӣ\nweg:\n  empty: Ягон ашё барои нишон додан\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/th.yml",
    "content": "app_menu:\n  close: ปิด\n  close_multiple: ปิดทั้งหมด\n  copy_handles: สำเนามือจับ\n  kill: ฆ่ากระบวนการ\n  kill_multiple: ฆ่ากระบวนการทั้งหมด\n  open_file_location: เปิดตำแหน่งไฟล์\n  pin: เข็มหมุด\n  pin_to_center: พินไปยังศูนย์\n  pin_to_left: พินไปทางซ้าย\n  pin_to_right: พินไปทางขวา\n  run_as: เรียกใช้เป็นผู้ดูแลระบบ\n  unpin: คลาย\ncontext_menu:\n  remove_module: ถอดโมดูลออก\n  reorder_disable: ล็อคแถบงาน\n  reorder_enable: ปลดล็อกแถบงาน\nmedia:\n  not_playing: ไม่มีอะไรเล่น\nshow_desktop:\n  label: แสดงเดสก์ท็อป\nstart:\n  label: เมนูเริ่ม\ntaskbar_menu:\n  add_file: ไฟล์ที่กำหนดเองพิน\n  desktop: เพิ่มโมดูลเดสก์ท็อป\n  media: เพิ่มโมดูลสื่อ\n  settings: การตั้งค่า\n  start: เพิ่มโมดูลเริ่มต้น\n  task_manager: ตัวจัดการงาน\n  trash_bin: เพิ่มโมดูลถังรีไซเคิล\ntrash_bin:\n  empty_bin: ถังรีไซเคิลว่างเปล่า\nweg:\n  empty: ไม่มีรายการที่จะแสดง\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/tl.yml",
    "content": "app_menu:\n  close: Malapit\n  close_multiple: Isara ang lahat\n  copy_handles: Kopyahin ang mga hawakan\n  kill: Proseso ng Patayin\n  kill_multiple: Patayin ang Lahat ng Proseso\n  open_file_location: Buksan ang lokasyon ng file\n  pin: Pin\n  pin_to_center: Pin sa gitna\n  pin_to_left: Pin sa kaliwa\n  pin_to_right: Pin sa kanan\n  run_as: Tumakbo bilang Administrator\n  unpin: Unpin\ncontext_menu:\n  remove_module: Alisin ang Module\n  reorder_disable: I -lock ang taskbar\n  reorder_enable: I -unlock ang taskbar\nmedia:\n  not_playing: Walang naglalaro\nshow_desktop:\n  label: Ipakita ang Desktop\nstart:\n  label: Start Menu\ntaskbar_menu:\n  add_file: Pin pasadyang file\n  desktop: Magdagdag ng Desktop Module\n  media: Magdagdag ng module ng media\n  settings: Mga setting\n  start: Magdagdag ng Start Module\n  task_manager: Task Manager\n  trash_bin: Magdagdag ng Recycle Bin Module\ntrash_bin:\n  empty_bin: Walang laman ang Recycle Bin\nweg:\n  empty: Walang maipapakitang item\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/tr.yml",
    "content": "app_menu:\n  close: Kapat\n  close_multiple: Tümünü Kapat\n  copy_handles: Tanıtıcıları Kopyala\n  kill: Görevi Sonlandır\n  kill_multiple: Tüm Görevleri Sonlandır\n  open_file_location: Dosya Konumunu Aç\n  pin: Sabitle\n  pin_to_center: Ortaya Sabitle\n  pin_to_left: Sola Sabitle\n  pin_to_right: Sağa Sabitle\n  run_as: Yönetici Olarak Çalıştır\n  unpin: Sabitlenmeyi Kaldır\ncontext_menu:\n  remove_module: Modülü Kaldır\n  reorder_disable: Görev çubuğunu kilitle\n  reorder_enable: Görev çubuğunun kilidini aç\nmedia:\n  not_playing: Medya oynatılmıyor\nshow_desktop:\n  label: Masaüstünü Göster\nstart:\n  label: Başlat Menüsü\ntaskbar_menu:\n  add_file: Özel Dosya Sabitle\n  desktop: Masaüstü Modülü Ekle\n  media: Medya Modülü Ekle\n  settings: Ayarlar\n  start: Başlat Modülü Ekle\n  task_manager: Görev Yöneticisi\n  trash_bin: Geri Dönüşüm Kutusu Modülü Ekle\ntrash_bin:\n  empty_bin: Geri Dönüşüm Kutusunu Boşalt\nweg:\n  empty: Gösterilecek öğe yok\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/uk.yml",
    "content": "app_menu:\n  close: Закрити\n  close_multiple: Закрити все\n  copy_handles: Копіювати ідентифікатор\n  kill: Зупинити процес\n  kill_multiple: Зупинити всі процеси\n  open_file_location: Розташування відкритого файлу\n  pin: Закріпити\n  pin_to_center: Закріпити по центру\n  pin_to_left: Закріпити зліва\n  pin_to_right: Закріпити зправа\n  run_as: Запустити від імені адміністратора\n  unpin: Відкріпити\ncontext_menu:\n  remove_module: Видалити модуль\n  reorder_disable: Блокування панелі завдань\n  reorder_enable: Розблокувати панель завдань\nmedia:\n  not_playing: Аудіо не відтворюється\nshow_desktop:\n  label: Показати робочий стіл\nstart:\n  label: Меню Пуск\ntaskbar_menu:\n  add_file: Закріпити власний файл\n  desktop: Додайте модуль робочого столу\n  media: Додати медіа модуль\n  settings: Налаштування\n  start: Додати кнопку Пуск\n  task_manager: Диспетчер задач\n  trash_bin: Додайте модуль кошика\ntrash_bin:\n  empty_bin: Очистити корзину\nweg:\n  empty: Немає елементів для показу\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/ur.yml",
    "content": "app_menu:\n  close: بند کریں\n  close_multiple: سب بند کرو\n  copy_handles: کاپی ہینڈلز\n  kill: عمل کو مار ڈالو\n  kill_multiple: تمام عمل کو مار ڈالو\n  open_file_location: فائل کا مقام کھولیں\n  pin: پن\n  pin_to_center: سینٹر سے پن\n  pin_to_left: بائیں سے پن\n  pin_to_right: دائیں سے پن\n  run_as: انتظامیہ کے طورپر چلانا\n  unpin: انپن\ncontext_menu:\n  remove_module: ماڈیول کو ہٹا دیں\n  reorder_disable: لاک ٹاسک بار\n  reorder_enable: ٹاسک بار کو غیر مقفل کریں\nmedia:\n  not_playing: کچھ نہیں کھیل رہا ہے\nshow_desktop:\n  label: ڈیسک ٹاپ دکھائیں\nstart:\n  label: مینو شروع کریں\ntaskbar_menu:\n  add_file: کسٹم فائل کو پن کریں\n  desktop: ڈیسک ٹاپ ماڈیول شامل کریں\n  media: میڈیا ماڈیول شامل کریں\n  settings: ترتیبات\n  start: اسٹارٹ ماڈیول شامل کریں\n  task_manager: ٹاسک مینیجر\n  trash_bin: ری سائیکل بن ماڈیول شامل کریں۔\ntrash_bin:\n  empty_bin: خالی ری سائیکل بن\nweg:\n  empty: دکھانے کے لیے کوئی آئٹمز نہیں۔\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/uz.yml",
    "content": "app_menu:\n  close: Yaqin\n  close_multiple: Hammasini yaqin\n  copy_handles: Nusxalash vositasi\n  kill: O'ldirish jarayoni\n  kill_multiple: Barcha jarayonlarni o'ldiring\n  open_file_location: Fayl joylashuvi\n  pin: Pin\n  pin_to_center: Markazga PIN\n  pin_to_left: Chapga o'ting\n  pin_to_right: O'ngga pin\n  run_as: Administrator sifatida yugurish\n  unpin: Chetlashmoq\ncontext_menu:\n  remove_module: Modulni olib tashlash\n  reorder_disable: Qulf vazifasi paneli\n  reorder_enable: Vazifalar panelini oching\nmedia:\n  not_playing: Hech narsa o'ynamaydi\nshow_desktop:\n  label: Ish stolini ko'rsatish\nstart:\n  label: Boshlash menyusi\ntaskbar_menu:\n  add_file: PIN-kod\n  desktop: Ish stoli modulini qo'shing\n  media: Media modul qo'shing\n  settings: Sozlamalar\n  start: Boshlash moduli qo'shing\n  task_manager: Vazifa menejeri\n  trash_bin: Chiqindi qutisi modulini qo'shing\ntrash_bin:\n  empty_bin: Chiqindi qutisini bo'sh\nweg:\n  empty: Ko‘rsatiladigan elementlar yo‘q\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/vi.yml",
    "content": "app_menu:\n  close: Đóng\n  close_multiple: Đóng tất cả\n  copy_handles: Sao chép tay cầm\n  kill: Quá trình tiêu diệt\n  kill_multiple: Giết tất cả các tiến trình\n  open_file_location: Mở vị trí file\n  pin: Ghim\n  pin_to_center: Ghim đến trung tâm\n  pin_to_left: Ghim sang trái\n  pin_to_right: Ghim sang phải\n  run_as: Chạy như quản trị viên\n  unpin: Giải nén\ncontext_menu:\n  remove_module: Xóa mô-đun\n  reorder_disable: Khóa thanh tác vụ\n  reorder_enable: Mở khóa Thanh nhiệm vụ\nmedia:\n  not_playing: Không có gì đang chơi\nshow_desktop:\n  label: Hiển thị màn hình\nstart:\n  label: Trình đơn Bắt đầu\ntaskbar_menu:\n  add_file: Pin tùy chỉnh tệp\n  desktop: Thêm mô-đun máy tính để bàn\n  media: Thêm mô -đun phương tiện\n  settings: Cài đặt\n  start: Thêm mô -đun bắt đầu\n  task_manager: Trình quản lý tác vụ\n  trash_bin: Thêm mô-đun thùng rác\ntrash_bin:\n  empty_bin: Thùng rác trống\nweg:\n  empty: Không có mục nào để hiển thị\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/yo.yml",
    "content": "app_menu:\n  close: Sunmọ\n  close_multiple: Sunmọ gbogbo\n  copy_handles: Daakọ mu\n  kill: Ilana pipa\n  kill_multiple: Pa Gbogbo awọn ilana\n  open_file_location: Ṣii ipo faili ṣii\n  pin: Pini\n  pin_to_center: PIN si ile-iṣẹ\n  pin_to_left: PIN si osi\n  pin_to_right: Pin si ọtun\n  run_as: Run bi alakoso\n  unpin: Ainidi\ncontext_menu:\n  remove_module: Yọ Module kuro\n  reorder_disable: Nutbar tiipa\n  reorder_enable: Ṣii ṣiṣẹ Iṣẹ-ṣiṣe Ṣii silẹ\nmedia:\n  not_playing: Ko si ohun ti ndun\nshow_desktop:\n  label: Ṣe afihan Ojú-iṣẹ\nstart:\n  label: Bẹrẹ Akojọ aṣyn\ntaskbar_menu:\n  add_file: Faili aṣa\n  desktop: Fi Ojú-iṣẹ Module\n  media: Ṣafikun module Media\n  settings: Eto\n  start: Ṣafikun module Bẹrẹ\n  task_manager: Oluṣakoso iṣẹ-ṣiṣe\n  trash_bin: Fi Atunlo Bin Module\ntrash_bin:\n  empty_bin: Ofo atunlo Bin\nweg:\n  empty: Ko si awọn nkan lati ṣafihan\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/zh-CN.yml",
    "content": "app_menu:\n  close: 关闭\n  close_multiple: 关闭全部\n  copy_handles: 复制句柄\n  kill: 结束进程\n  kill_multiple: 结束所有进程\n  open_file_location: 打开文件位置\n  pin: 固定\n  pin_to_center: 固定到中心\n  pin_to_left: 固定到左侧\n  pin_to_right: 固定到右侧\n  run_as: 以管理员身份运行\n  unpin: 取消固定\ncontext_menu:\n  remove_module: 移除模块\n  reorder_disable: 锁定任务栏\n  reorder_enable: 解锁任务栏\nmedia:\n  not_playing: 当前无播放内容\nshow_desktop:\n  label: 显示桌面\nstart:\n  label: 开始菜单\ntaskbar_menu:\n  add_file: 固定自定义文件\n  desktop: 添加桌面模块\n  media: 添加媒体模块\n  settings: 设置\n  start: 添加开始菜单模块\n  task_manager: 任务管理器\n  trash_bin: 添加回收站模块\ntrash_bin:\n  empty_bin: 清空回收站\nweg:\n  empty: 暂无内容显示\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/zh-TW.yml",
    "content": "app_menu:\n  close: 關閉\n  close_multiple: 關閉全部\n  copy_handles: 複製把手\n  kill: 殺死過程\n  kill_multiple: 殺死所有過程\n  open_file_location: 打開文件位置\n  pin: 別針\n  pin_to_center: 引腳到中心\n  pin_to_left: 引腳向左\n  pin_to_right: 銷釘向右\n  run_as: 作為管理員運行\n  unpin: Uncin\ncontext_menu:\n  remove_module: 移除模塊\n  reorder_disable: 鎖任務欄\n  reorder_enable: 解鎖任務欄\nmedia:\n  not_playing: 什麼都沒有玩\nshow_desktop:\n  label: 顯示桌面\nstart:\n  label: 開始菜單\ntaskbar_menu:\n  add_file: 引導自定義文件\n  desktop: 添加桌面模塊\n  media: 添加媒體模塊\n  settings: 設定\n  start: 添加開始模塊\n  task_manager: 工作管理員\n  trash_bin: 新增回收站模組\ntrash_bin:\n  empty_bin: 清空回收站\nweg:\n  empty: 沒有顯示的物品\n"
  },
  {
    "path": "src/ui/react/weg/i18n/translations/zu.yml",
    "content": "app_menu:\n  close: Ngokuminyanisa\n  close_multiple: Vala konke\n  copy_handles: Kopisha izibambo\n  kill: Kill Process\n  kill_multiple: Bulala Zonke Izinqubo\n  open_file_location: Vula Indawo yefayela\n  pin: Isipeletu\n  pin_to_center: Pin esikhungweni\n  pin_to_left: Pin kwesokunxele\n  pin_to_right: Pin kwesokudla\n  run_as: Gijima njengomlawuli\n  unpin: Unpin\ncontext_menu:\n  remove_module: Susa Imojuli\n  reorder_disable: Khiya i-taskbar\n  reorder_enable: Vula i-Taskbar\nmedia:\n  not_playing: Akukho okudlalayo\nshow_desktop:\n  label: Bonisa Ideskithophu\nstart:\n  label: Qala Imenyu\ntaskbar_menu:\n  add_file: I-PIN ifayela ngokwezifiso\n  desktop: Engeza Imojula yedeskithophu\n  media: Faka imodyuli yabezindaba\n  settings: Izilungiselelo\n  start: Faka imodyuli yokuqala\n  task_manager: Umphathi Womsebenzi\n  trash_bin: Engeza i-Recycle Bin Module\ntrash_bin:\n  empty_bin: I-Recycle Bin engenalutho\nweg:\n  empty: Azikho izinto ezizoboniswa\n"
  },
  {
    "path": "src/ui/react/weg/index.tsx",
    "content": "import { SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport { getRootContainer } from \"libs/ui/react/utils/index.ts\";\nimport { declareDocumentAsLayeredHitbox } from \"libs/ui/react/utils/layered.ts\";\nimport { invoke } from \"@tauri-apps/api/core\";\nimport { getCurrentWebviewWindow } from \"@tauri-apps/api/webviewWindow\";\nimport { createRoot } from \"react-dom/client\";\nimport { I18nextProvider } from \"react-i18next\";\n\nimport { App } from \"./app.tsx\";\n\nimport i18n, { loadTranslations } from \"./i18n/index.ts\";\n\nimport \"@shared/styles/colors.css\";\nimport \"./styles/variables.css\";\nimport \"@shared/styles/reset.css\";\nimport \"./styles/global.css\";\n\nawait declareDocumentAsLayeredHitbox();\nawait loadTranslations();\nawait Widget.getCurrent().init();\n\nconst container = getRootContainer();\ncreateRoot(container).render(\n  <I18nextProvider i18n={i18n}>\n    <App />\n  </I18nextProvider>,\n);\n\ngetCurrentWebviewWindow().onDragDropEvent(async (e) => {\n  if (e.payload.type === \"drop\") {\n    for (const path of e.payload.paths) {\n      await invoke(SeelenCommand.WegPinItem, { path });\n    }\n  }\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/bar/DockMenu.tsx",
    "content": "import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport { dialog } from \"@seelen-ui/lib/tauri\";\nimport type { ContextMenu } from \"@seelen-ui/lib/types\";\nimport type { TFunction } from \"i18next\";\n\nimport type { WidgetId } from \"@seelen-ui/lib/types\";\n\nimport { $dock_state, $dock_state_actions } from \"../shared/state/items.ts\";\n\nconst identifier = crypto.randomUUID();\nconst onBarMenuClick = \"weg::bar_menu_click\";\n\nlet _t: (key: string) => string = (key) => key;\n\ntype BarMenuKey =\n  | \"add-start-module\"\n  | \"add-toggle-desktop-module\"\n  | \"add-media-module\"\n  | \"add-trash-bin-module\"\n  | \"add-item\"\n  | \"reorder\"\n  | \"task_manager\"\n  | \"settings\";\n\nasync function handleBarMenuClick(key: BarMenuKey) {\n  switch (key) {\n    case \"add-start-module\":\n      $dock_state_actions.addStartModule();\n      break;\n\n    case \"add-toggle-desktop-module\":\n      $dock_state_actions.addDesktopModule();\n      break;\n\n    case \"add-media-module\":\n      $dock_state_actions.addMediaModule();\n      break;\n\n    case \"add-trash-bin-module\":\n      $dock_state_actions.addTrashBinModule();\n      break;\n\n    case \"reorder\":\n      $dock_state.value = {\n        ...$dock_state.value,\n        isReorderDisabled: !$dock_state.value.isReorderDisabled,\n      };\n      break;\n\n    case \"task_manager\":\n      invoke(SeelenCommand.OpenFile, { path: \"Taskmgr.exe\" });\n      break;\n\n    case \"settings\":\n      invoke(SeelenCommand.TriggerWidget, {\n        payload: { id: \"@seelen/settings\" as WidgetId },\n      });\n      break;\n\n    case \"add-item\": {\n      const files = await dialog.open({\n        title: _t(\"taskbar_menu.add_file\"),\n        multiple: true,\n        filters: [\n          { name: \"lnk\", extensions: [\"lnk\"] },\n          { name: \"*\", extensions: [\"*\"] },\n        ],\n      });\n      for (const path of files || []) {\n        await invoke(SeelenCommand.WegPinItem, { path });\n      }\n      break;\n    }\n  }\n}\n\nWidget.self.webview.listen(onBarMenuClick, ({ payload }) => {\n  handleBarMenuClick((payload as { key: BarMenuKey }).key);\n});\n\nexport function getSeelenWegMenu(t: TFunction): ContextMenu {\n  const { isReorderDisabled } = $dock_state.value;\n\n  return {\n    identifier,\n    items: [\n      // --- Add modules ---\n      {\n        type: \"Item\",\n        key: \"add-start-module\",\n        icon: \"BsWindows\",\n        label: t(\"taskbar_menu.start\"),\n        callbackEvent: onBarMenuClick,\n      },\n      {\n        type: \"Item\",\n        key: \"add-toggle-desktop-module\",\n        icon: \"IoDesktop\",\n        label: t(\"taskbar_menu.desktop\"),\n        callbackEvent: onBarMenuClick,\n      },\n      {\n        type: \"Item\",\n        key: \"add-media-module\",\n        icon: \"PiMusicNotesPlusFill\",\n        label: t(\"taskbar_menu.media\"),\n        callbackEvent: onBarMenuClick,\n      },\n      {\n        type: \"Item\",\n        key: \"add-trash-bin-module\",\n        icon: \"FaTrashAlt\",\n        label: t(\"taskbar_menu.trash_bin\"),\n        callbackEvent: onBarMenuClick,\n      },\n      { type: \"Separator\" },\n      // --- File pinning ---\n      {\n        type: \"Item\",\n        key: \"add-item\",\n        icon: \"RiFileAddLine\",\n        label: t(\"taskbar_menu.add_file\"),\n        callbackEvent: onBarMenuClick,\n      },\n      { type: \"Separator\" },\n      // --- Dock controls ---\n      {\n        type: \"Item\",\n        key: \"reorder\",\n        icon: isReorderDisabled ? \"CgLockUnlock\" : \"CgLock\",\n        label: t(isReorderDisabled ? \"context_menu.reorder_enable\" : \"context_menu.reorder_disable\"),\n        callbackEvent: onBarMenuClick,\n      },\n      {\n        type: \"Item\",\n        key: \"task_manager\",\n        icon: \"PiChartLineFill\",\n        label: t(\"taskbar_menu.task_manager\"),\n        callbackEvent: onBarMenuClick,\n      },\n      {\n        type: \"Item\",\n        key: \"settings\",\n        icon: \"RiSettings4Fill\",\n        label: t(\"taskbar_menu.settings\"),\n        callbackEvent: onBarMenuClick,\n      },\n    ],\n  };\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/bar/DraggableItem.tsx",
    "content": "import { useSortable } from \"@dnd-kit/react/sortable\";\nimport { RestrictToHorizontalAxis, RestrictToVerticalAxis } from \"@dnd-kit/abstract/modifiers\";\n\nimport type { PropsWithChildren } from \"preact/compat\";\n\nimport type { SwItem } from \"../shared/types.ts\";\nimport { isHorizontalDock } from \"../shared/state/settings.ts\";\n\ninterface Props extends PropsWithChildren {\n  item: SwItem;\n  index: number;\n  ghost?: boolean;\n}\n\nexport function DraggableItem({ children, item, index, ghost }: Props) {\n  const sortable = useSortable({\n    id: item.id,\n    index,\n    modifiers: [isHorizontalDock.value ? RestrictToHorizontalAxis : RestrictToVerticalAxis],\n  });\n\n  return (\n    <div\n      ref={sortable.ref}\n      style={{ opacity: sortable.isDragging || ghost ? 0.3 : 1 }}\n      data-dragging={sortable.isDragging}\n      className=\"weg-item-drag-container\"\n      // this was added here to avoid need to pass it to all the items types,\n      // this avoid the double context menu of dock menu and dock items.\n      onContextMenu={item.type === \"Separator\" ? undefined : (e) => e.stopPropagation()}\n    >\n      {children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/bar/ItemReordableList.tsx",
    "content": "import { DragDropProvider, DragOverlay } from \"@dnd-kit/react\";\nimport { move } from \"@dnd-kit/helpers\";\nimport { WegItemType, WegPinnedItemsVisibility, WegTemporalItemsVisibility } from \"@seelen-ui/lib/types\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { MediaSession } from \"../item/infra/MediaSession.tsx\";\nimport { Separator } from \"../item/infra/Separator.tsx\";\nimport { StartMenu } from \"../item/infra/StartMenu.tsx\";\nimport { UserApplication } from \"../item/infra/UserApplication.tsx\";\n\nimport type { SwItem } from \"../shared/types.ts\";\n\nimport { $dock_state } from \"../shared/state/items.ts\";\nimport { DraggableItem } from \"./DraggableItem.tsx\";\nimport { ShowDesktopModule } from \"../item/infra/ShowDesktop.tsx\";\nimport { $settings } from \"../shared/state/settings.ts\";\nimport { $current_monitor } from \"../shared/state/system.ts\";\nimport { computed } from \"@preact/signals\";\nimport { $interactables, getWindowsForItem } from \"../shared/state/windows.ts\";\nimport { TrashBin } from \"../item/infra/RecycleBin.tsx\";\n\nconst visibleItems = computed(() => {\n  const { pinnedItemsVisibility, temporalItemsVisibility } = $settings.value;\n  const monitor = $current_monitor.value;\n\n  const showPinned = pinnedItemsVisibility === WegPinnedItemsVisibility.Always || monitor.isPrimary;\n  const filterByMonitor = temporalItemsVisibility === WegTemporalItemsVisibility.OnMonitor;\n\n  const windows = filterByMonitor\n    ? $interactables.value.filter((w) => {\n      return w.monitor === monitor.id;\n    })\n    : $interactables.value;\n\n  return $dock_state.value.items.filter((item) => {\n    if (item.type !== \"AppOrFile\") {\n      return showPinned;\n    }\n\n    if (item.pinned && showPinned) {\n      return true;\n    }\n\n    return getWindowsForItem(item, windows).length > 0;\n  });\n});\n\nexport function DockItems() {\n  const { t } = useTranslation();\n\n  const isEmpty = visibleItems.value.filter((c) => c.type !== WegItemType.Separator).length === 0;\n\n  return (\n    <DragDropProvider\n      onDragOver={(event) => {\n        const newItems = move($dock_state.value.items, event);\n        $dock_state.value = { ...$dock_state.value, items: newItems };\n      }}\n    >\n      <div className=\"weg-items\">\n        {isEmpty ? <span className=\"weg-empty-state-label\">{t(\"weg.empty\")}</span> : (\n          visibleItems.value.map((item, index) => {\n            return (\n              <DraggableItem item={item} key={item.id} index={index}>\n                {ItemByType(item, false)}\n              </DraggableItem>\n            );\n          })\n        )}\n      </div>\n\n      <DragOverlay>\n        {(source) => {\n          const item = visibleItems.value.find((c) => c.id === source.id);\n          return item ? ItemByType(item, true) : null;\n        }}\n      </DragOverlay>\n    </DragDropProvider>\n  );\n}\n\nfunction ItemByType(item: SwItem, isOverlay: boolean) {\n  if (item.type === WegItemType.AppOrFile) {\n    return <UserApplication key={item.id} item={item} isOverlay={isOverlay} />;\n  }\n\n  if (item.type === WegItemType.StartMenu) {\n    return <StartMenu key={item.id} item={item} />;\n  }\n\n  if (item.type === WegItemType.ShowDesktop) {\n    return <ShowDesktopModule key={item.id} item={item} />;\n  }\n\n  if (item.type === WegItemType.Media) {\n    return <MediaSession key={item.id} item={item} />;\n  }\n\n  if (item.type === WegItemType.Separator) {\n    return <Separator key={item.id} item={item} />;\n  }\n\n  if (item.type === WegItemType.TrashBin) {\n    return <TrashBin key={item.id} item={item} />;\n  }\n\n  return null;\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/bar/index.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { SeelenWegMode, SeelenWegSide } from \"@seelen-ui/lib/types\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { useCallback } from \"preact/compat\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport { $dock_should_be_hidden, $settings } from \"../shared/state/mod.ts\";\nimport { getDockContextMenuAlignment } from \"../shared/state/settings.ts\";\nimport { DockItems } from \"./ItemReordableList.tsx\";\nimport { getSeelenWegMenu } from \"./DockMenu.tsx\";\n\nexport function SeelenWeg() {\n  const { t } = useTranslation();\n\n  const settings = $settings.value;\n  const isHorizontal = settings.position === SeelenWegSide.Top ||\n    settings.position === SeelenWegSide.Bottom;\n\n  const onContextMenu = useCallback(() => {\n    const { alignX, alignY } = getDockContextMenuAlignment(settings.position);\n    invoke(SeelenCommand.TriggerContextMenu, {\n      menu: { ...getSeelenWegMenu(t), alignX, alignY },\n      forwardTo: null,\n    });\n  }, [t, settings.position]);\n\n  return (\n    <div\n      className={cx(\"taskbar\", settings.position.toLowerCase(), {\n        horizontal: isHorizontal,\n        vertical: !isHorizontal,\n        // 'temporal-only': isTemporalOnlyWegBar, todo handle this type of state via new config\n        \"full-width\": settings.mode === SeelenWegMode.FullWidth,\n        hidden: $dock_should_be_hidden.value,\n      })}\n      onContextMenu={onContextMenu}\n    >\n      <BackgroundByLayersV2 prefix=\"taskbar\" />\n      <div className=\"weg-items-container\">\n        <DockItems />\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/application.ts",
    "content": "/** Returns a number between 0 and 255 */\nexport function calcLuminance(imageUrl: string): Promise<number> {\n  return new Promise((resolve, reject) => {\n    const img = new Image();\n    img.crossOrigin = \"Anonymous\";\n    img.src = imageUrl;\n\n    img.onload = () => {\n      const canvas = document.createElement(\"canvas\");\n      const ctx = canvas.getContext(\"2d\");\n\n      if (!ctx) {\n        reject(new Error(\"Unable to get canvas context\"));\n        return;\n      }\n\n      canvas.width = img.width;\n      canvas.height = img.height;\n      ctx.drawImage(img, 0, 0, img.width, img.height);\n\n      const imageData = ctx.getImageData(0, 0, img.width, img.height);\n      const data = imageData.data;\n      let totalLuminance = 0;\n\n      for (let i = 0; i < data.length; i += 4) {\n        const r = data[i] || 0;\n        const g = data[i + 1] || 0;\n        const b = data[i + 2] || 0;\n\n        // Calculate luminance\n        const luminance = 0.299 * r + 0.587 * g + 0.114 * b;\n        totalLuminance += luminance;\n      }\n\n      const avgLuminance = totalLuminance / (data.length / 4);\n      resolve(avgLuminance);\n    };\n\n    img.onerror = (err) => {\n      reject(err);\n    };\n  });\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/GeneralMenu.tsx",
    "content": "import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport type { ContextMenu, ContextMenuItem } from \"@seelen-ui/lib/types\";\nimport type { TFunction } from \"i18next\";\n\nimport { WegItemType } from \"@seelen-ui/lib/types\";\n\nimport type { SwItem } from \"../../shared/types.ts\";\n\nimport { $dock_state_actions } from \"../../shared/state/items.ts\";\n\nconst identifier = crypto.randomUUID();\nconst onItemMenuClick = \"weg::item_menu_click\";\n\nlet pendingItem: SwItem | null = null;\n\nWidget.self.webview.listen(onItemMenuClick, ({ payload }) => {\n  const { key } = payload as { key: string };\n  const item = pendingItem;\n  if (!item) return;\n\n  if (key === \"remove\" || key === \"unpin\") {\n    $dock_state_actions.remove(item.id);\n  } else if (key === \"open_location\") {\n    if (\"path\" in item) {\n      invoke(SeelenCommand.SelectFileOnExplorer, { path: item.path });\n    }\n  } else if (key === \"empty_bin\") {\n    invoke(SeelenCommand.TrashBinEmpty);\n  }\n});\n\nexport function getMenuForItem(t: TFunction, item: SwItem): ContextMenu {\n  pendingItem = item;\n\n  if (\n    item.type === WegItemType.ShowDesktop ||\n    item.type === WegItemType.Media ||\n    item.type === WegItemType.StartMenu ||\n    item.type === WegItemType.TrashBin\n  ) {\n    const items: ContextMenuItem[] = [\n      {\n        type: \"Item\",\n        key: \"remove\",\n        icon: \"CgExtensionRemove\",\n        label: t(\"context_menu.remove_module\"),\n        callbackEvent: onItemMenuClick,\n      },\n    ];\n\n    if (item.type === WegItemType.TrashBin) {\n      items.unshift(\n        {\n          type: \"Item\",\n          key: \"empty_bin\",\n          icon: \"FaRegTrashAlt\",\n          label: t(\"trash_bin.empty_bin\"),\n          callbackEvent: onItemMenuClick,\n        },\n        { type: \"Separator\" },\n      );\n    }\n\n    return {\n      identifier,\n      items,\n    };\n  }\n\n  // File or Folder pinned items\n  if (item.type === WegItemType.AppOrFile) {\n    return {\n      identifier,\n      items: [\n        {\n          type: \"Item\",\n          key: \"unpin\",\n          icon: \"RiUnpinLine\",\n          label: t(\"app_menu.unpin\"),\n          callbackEvent: onItemMenuClick,\n        },\n        { type: \"Separator\" },\n        {\n          type: \"Item\",\n          key: \"open_location\",\n          icon: \"MdOutlineMyLocation\",\n          label: t(\"app_menu.open_file_location\"),\n          callbackEvent: onItemMenuClick,\n        },\n      ],\n    };\n  }\n\n  return { identifier, items: [] };\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/MediaSession.css",
    "content": ".media-session {\n  display: grid;\n  position: relative;\n  width: 100%;\n  height: 100%;\n\n  .horizontal & {\n    grid-template-columns: var(--config-item-size) 1fr;\n  }\n\n  .vertical & {\n    grid-template-rows: var(--config-item-size) 1fr;\n  }\n\n  .media-session-blurred-thumbnail-container {\n    position: absolute;\n    overflow: hidden;\n    width: 100%;\n    height: 100%;\n\n    .media-session-blurred-thumbnail {\n      width: 100%;\n      height: 100%;\n      object-fit: fill;\n      filter: blur(10px) brightness(125%) contrast(125%);\n    }\n  }\n\n  .media-session-thumbnail-container {\n    position: relative;\n    width: 100%;\n    height: 100%;\n\n    .media-session-thumbnail {\n      width: 100%;\n      height: 100%;\n      object-fit: contain;\n      background: #0004;\n    }\n\n    .media-session-app-icon {\n      position: absolute;\n      width: 25%;\n      aspect-ratio: 1/1;\n      right: 5%;\n      bottom: 5%;\n      object-fit: contain;\n    }\n  }\n\n  .media-session-info {\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n    overflow: hidden;\n    padding: 4px;\n\n    .media-session-title {\n      letter-spacing: 0.3px;\n      line-height: 1.3em;\n      font-size: 0.7rem;\n      font-weight: 600;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n      overflow: hidden;\n      max-width: 100%;\n      margin-bottom: 2px;\n      margin-right: -2px;\n\n      &.media-session-title-default {\n        text-align: center;\n        white-space: normal;\n      }\n\n      .vertical & {\n        display: none;\n      }\n    }\n\n    .media-session-actions {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      gap: 2px;\n\n      .vertical & {\n        flex-direction: column;\n        gap: 12px;\n      }\n\n      .ant-btn {\n        color: inherit !important;\n        height: 16px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/MediaSession.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { useComputed } from \"@preact/signals\";\nimport { SeelenWegSide } from \"@seelen-ui/lib/types\";\nimport { FileIcon, Icon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport { convertFileSrc, invoke as tauriInvoke } from \"@tauri-apps/api/core\";\nimport { resolve, resourceDir } from \"@tauri-apps/api/path\";\nimport { Button } from \"antd\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { calcLuminance } from \"../application.ts\";\n\nimport type { MediaWegItem } from \"../../shared/types.ts\";\n\nimport { $players, $settings } from \"../../shared/state/mod.ts\";\nimport { getDockContextMenuAlignment } from \"../../shared/state/settings.ts\";\nimport { getMenuForItem } from \"./GeneralMenu.tsx\";\n\nimport \"./MediaSession.css\";\n\nconst MAX_LUMINANCE = 210;\nconst MIN_LUMINANCE = 40;\nconst BRIGHTNESS_MULTIPLIER = 1.5; // used in css\n\nconst DEFAULT_THUMBNAIL = await resolve(\n  await resourceDir(),\n  \"static\",\n  \"icons\",\n  \"music_thumbnail.jpg\",\n);\n\nexport function MediaSession({ item }: { item: MediaWegItem }) {\n  const [luminance, setLuminance] = useState(150);\n\n  const $dock_position = useComputed(() => $settings.value.position);\n  const session = $players.value.find((s) => s.default);\n\n  let thumbnailSrc = convertFileSrc(\n    session?.thumbnail ? session.thumbnail : DEFAULT_THUMBNAIL,\n  );\n\n  const { t } = useTranslation();\n\n  useEffect(() => {\n    calcLuminance(thumbnailSrc).then(setLuminance).catch(console.error);\n  }, [thumbnailSrc]);\n\n  const onContextMenu = useCallback(\n    (e: MouseEvent) => {\n      e.stopPropagation();\n      const { alignX, alignY } = getDockContextMenuAlignment($settings.value.position);\n      invoke(SeelenCommand.TriggerContextMenu, {\n        menu: { ...getMenuForItem(t, item), alignX, alignY },\n        forwardTo: null,\n      });\n    },\n    [item, t],\n  );\n\n  const filteredLuminance = Math.max(\n    Math.min(luminance * BRIGHTNESS_MULTIPLIER, MAX_LUMINANCE),\n    MIN_LUMINANCE,\n  );\n  const color = filteredLuminance < 125 ? \"#efefef\" : \"#222222\";\n\n  const onClickBtn = (cmd: string) => {\n    if (session) {\n      tauriInvoke(cmd, { id: session.umid }).catch(console.error);\n    }\n  };\n\n  const isHorizontal = $dock_position.value === SeelenWegSide.Bottom ||\n    $dock_position.value === SeelenWegSide.Top;\n\n  return (\n    <div\n      className={cx(\"weg-item\", \"media-session-container\", {\n        \"media-session-container-horizontal\": isHorizontal,\n        \"media-session-container-vertical\": !isHorizontal,\n      })}\n      onContextMenu={onContextMenu}\n      // style={{ zIndex: -1 }} // I don't known why the fuck this item is overlapping but this fixes it\n    >\n      <div\n        className=\"media-session\"\n        style={{\n          backgroundColor: `rgb(${filteredLuminance}, ${filteredLuminance}, ${filteredLuminance})`,\n        }}\n      >\n        <div className=\"media-session-blurred-thumbnail-container\">\n          <img\n            className=\"media-session-blurred-thumbnail\"\n            src={thumbnailSrc}\n            loading=\"lazy\"\n          />\n        </div>\n        <div className=\"media-session-thumbnail-container\">\n          <img\n            className=\"media-session-thumbnail\"\n            src={thumbnailSrc}\n            loading=\"lazy\"\n          />\n          <FileIcon\n            className=\"media-session-app-icon\"\n            umid={session?.umid}\n            noFallback\n          />\n        </div>\n        <div className=\"media-session-info\">\n          <span\n            className={cx(\"media-session-title\", {\n              \"media-session-title-default\": !session,\n            })}\n            style={{ color }}\n          >\n            {session ? session.title : t(\"media.not_playing\")}\n          </span>\n          {session && (\n            <div className=\"media-session-actions\">\n              <Button\n                type=\"text\"\n                size=\"small\"\n                onClick={onClickBtn.bind(null, \"media_prev\")}\n              >\n                <Icon\n                  iconName=\"IoPlaySkipBack\"\n                  color={color}\n                  size={12}\n                />\n              </Button>\n              <Button\n                type=\"text\"\n                size=\"small\"\n                onClick={onClickBtn.bind(null, \"media_toggle_play_pause\")}\n              >\n                <Icon\n                  iconName={session?.playing ? \"IoPause\" : \"IoPlay\"}\n                  color={color}\n                  size={12}\n                />\n              </Button>\n              <Button\n                type=\"text\"\n                size=\"small\"\n                onClick={onClickBtn.bind(null, \"media_next\")}\n              >\n                <Icon\n                  iconName=\"IoPlaySkipForward\"\n                  color={color}\n                  size={12}\n                />\n              </Button>\n            </div>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/RecycleBin.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { SpecificIcon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { memo, useCallback } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport type { TrashBinItem } from \"../../shared/types.ts\";\n\nimport { $settings, getDockContextMenuAlignment } from \"../../shared/state/settings.ts\";\nimport { getMenuForItem } from \"./GeneralMenu.tsx\";\nimport { $trash_bin_info } from \"../../shared/state/system.ts\";\n\ninterface Props {\n  item: TrashBinItem;\n}\n\nexport const TrashBin = memo(({ item }: Props) => {\n  const { t } = useTranslation();\n\n  const onContextMenu = useCallback(\n    (e: MouseEvent) => {\n      e.stopPropagation();\n      const { alignX, alignY } = getDockContextMenuAlignment($settings.value.position);\n      invoke(SeelenCommand.TriggerContextMenu, {\n        menu: { ...getMenuForItem(t, item), alignX, alignY },\n        forwardTo: null,\n      });\n    },\n    [item, t],\n  );\n\n  return (\n    <div\n      className=\"weg-item weg-item-show-desktop\"\n      onClick={() => {\n        invoke(SeelenCommand.OpenFile, { path: \"shell:RecycleBinFolder\" });\n      }}\n      onContextMenu={onContextMenu}\n    >\n      <BackgroundByLayersV2 />\n      <SpecificIcon\n        className=\"weg-item-icon\"\n        name={$trash_bin_info.value.itemCount > 0 ? \"bin::full\" : \"bin::empty\"}\n      />\n    </div>\n  );\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/Separator.tsx",
    "content": "import { cx } from \"libs/ui/react/utils/styling.ts\";\n\nimport type { SeparatorWegItem } from \"../../shared/types.ts\";\n\nimport { HARDCODED_SEPARATOR_LEFT, HARDCODED_SEPARATOR_RIGHT } from \"../../shared/state/items.ts\";\nimport { $settings } from \"../../shared/state/mod.ts\";\n\nexport function Separator({ item }: { item: SeparatorWegItem }) {\n  return (\n    <div\n      className={cx(\"weg-separator\", {\n        \"weg-separator-1\": item.id === HARDCODED_SEPARATOR_LEFT.id,\n        \"weg-separator-2\": item.id === HARDCODED_SEPARATOR_RIGHT.id,\n        visible: $settings.value.visibleSeparators,\n      })}\n    />\n  );\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/ShowDesktop.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { SpecificIcon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { memo, useCallback } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport type { ShowDesktopWegItem } from \"../../shared/types.ts\";\n\nimport { $settings, getDockContextMenuAlignment } from \"../../shared/state/settings.ts\";\nimport { getMenuForItem } from \"./GeneralMenu.tsx\";\n\ninterface Props {\n  item: ShowDesktopWegItem;\n}\n\nexport const ShowDesktopModule = memo(({ item }: Props) => {\n  const { t } = useTranslation();\n\n  const onContextMenu = useCallback(\n    (e: MouseEvent) => {\n      e.stopPropagation();\n      const { alignX, alignY } = getDockContextMenuAlignment($settings.value.position);\n      invoke(SeelenCommand.TriggerContextMenu, {\n        menu: { ...getMenuForItem(t, item), alignX, alignY },\n        forwardTo: null,\n      });\n    },\n    [item, t],\n  );\n\n  return (\n    <div\n      className=\"weg-item weg-item-show-desktop\"\n      onClick={() => {\n        invoke(SeelenCommand.ShowDesktop);\n      }}\n      onContextMenu={onContextMenu}\n    >\n      <BackgroundByLayersV2 />\n      <SpecificIcon className=\"weg-item-icon\" name=\"@seelen/weg::show-desktop\" />\n    </div>\n  );\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/StartMenu.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { SpecificIcon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { memo, useCallback } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport type { StartMenuWegItem } from \"../../shared/types.ts\";\n\nimport { $settings, getDockContextMenuAlignment } from \"../../shared/state/settings.ts\";\nimport { getMenuForItem } from \"./GeneralMenu.tsx\";\nimport { $delayedFocused } from \"../../shared/state/windows.ts\";\n\ninterface Props {\n  item: StartMenuWegItem;\n}\n\nconst startMenuExes = [\"SearchHost.exe\", \"StartMenuExperienceHost.exe\"];\n\nexport const StartMenu = memo(({ item }: Props) => {\n  const { t } = useTranslation();\n\n  const isStartMenuOpen = startMenuExes.some((program) => ($delayedFocused.value?.exe || \"\").endsWith(program));\n\n  const onContextMenu = useCallback(\n    (e: MouseEvent) => {\n      e.stopPropagation();\n      const { alignX, alignY } = getDockContextMenuAlignment($settings.value.position);\n      invoke(SeelenCommand.TriggerContextMenu, {\n        menu: { ...getMenuForItem(t, item), alignX, alignY },\n        forwardTo: null,\n      });\n    },\n    [item, t],\n  );\n\n  return (\n    <div\n      className=\"weg-item weg-item-start\"\n      onClick={() => {\n        if (!isStartMenuOpen) {\n          invoke(SeelenCommand.ShowStartMenu);\n        }\n      }}\n      onContextMenu={onContextMenu}\n    >\n      <BackgroundByLayersV2 />\n      <SpecificIcon\n        className=\"weg-item-icon weg-item-start-icon\"\n        name=\"@seelen/weg::start-menu\"\n      />\n    </div>\n  );\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/UserApplication.tsx",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport { SeelenWegSide, type UserAppWindow } from \"@seelen-ui/lib/types\";\nimport { FileIcon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { useWindowFocusChange } from \"libs/ui/react/utils/hooks.ts\";\nimport { cx } from \"libs/ui/react/utils/styling.ts\";\nimport moment from \"moment\";\nimport { useCallback, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BackgroundByLayersV2 } from \"libs/ui/react/components/BackgroundByLayers/infra.tsx\";\n\nimport type { AppOrFileWegItem } from \"../../shared/types.ts\";\n\nimport {\n  $delayedFocused,\n  $focused,\n  $interactables,\n  $notifications,\n  $open_popups,\n  $settings,\n} from \"../../shared/state/mod.ts\";\nimport { getDockContextMenuAlignment } from \"../../shared/state/settings.ts\";\nimport { getWindowsForItem } from \"../../shared/state/windows.ts\";\nimport { getUserApplicationContextMenu, launchItem } from \"./UserApplicationContextMenu.tsx\";\nimport { UserApplicationPreview } from \"./UserApplicationPreview.tsx\";\nimport { Flex, Popover } from \"antd\";\n\ninterface Props {\n  item: AppOrFileWegItem;\n  isOverlay?: boolean;\n}\n\ninterface InnerProps extends Props {\n  windows: UserAppWindow[];\n}\n\nfunction UserApplicationItem({ item, isOverlay, windows }: InnerProps) {\n  const [openPreview, setOpenPreview] = useState(false);\n  const [blockUntil, setBlockUntil] = useState(moment(new Date()));\n\n  const { t } = useTranslation();\n  const calculatePlacement = (position: any) => {\n    switch (position) {\n      case SeelenWegSide.Bottom: {\n        return \"top\";\n      }\n      case SeelenWegSide.Top: {\n        return \"bottom\";\n      }\n      case SeelenWegSide.Left: {\n        return \"right\";\n      }\n      case SeelenWegSide.Right: {\n        return \"left\";\n      }\n      default: {\n        throw new Error(\"Not Implemented!\");\n      }\n    }\n  };\n\n  useWindowFocusChange((focused) => {\n    if (!focused) {\n      setBlockUntil(moment(new Date()).add(1, \"second\"));\n      setOpenPreview(false);\n    }\n  });\n\n  const onContextMenu = useCallback(\n    (e: MouseEvent) => {\n      e.stopPropagation();\n      setOpenPreview(false);\n      const { alignX, alignY } = getDockContextMenuAlignment($settings.value.position);\n      invoke(SeelenCommand.TriggerContextMenu, {\n        menu: { ...getUserApplicationContextMenu(t, item, windows), alignX, alignY },\n        forwardTo: null,\n      });\n    },\n    [item, windows, t],\n  );\n\n  const notificationsCount = $notifications.value.filter((n) => n.appUmid === item.umid).length;\n  const itemLabel = $settings.value.showWindowTitle && windows.length ? windows[0]!.title : null;\n\n  const itemNode = (\n    <div\n      className=\"weg-item\"\n      onClick={() => {\n        const window = windows[0];\n        if (!window) {\n          launchItem(item, false);\n        } else {\n          invoke(SeelenCommand.WegToggleWindowState, {\n            hwnd: window.hwnd,\n            wasFocused: $delayedFocused.value?.hwnd === window.hwnd,\n          });\n        }\n      }}\n      onAuxClick={(e) => {\n        const window = windows[0];\n        if (e.button === 1 && window) {\n          invoke(SeelenCommand.WegCloseApp, { hwnd: window.hwnd });\n        }\n      }}\n      onContextMenu={onContextMenu}\n    >\n      <BackgroundByLayersV2 prefix=\"item\" />\n      <FileIcon\n        className=\"weg-item-icon\"\n        path={item.relaunch?.icon || item.path}\n        umid={item.umid}\n      />\n      {itemLabel && <div className=\"weg-item-title\">{itemLabel}</div>}\n      {notificationsCount > 0 && <div className=\"weg-item-notification-badge\">{notificationsCount}</div>}\n      {$settings.value.showInstanceCounter && windows.length > 1 && (\n        <div className=\"weg-item-instance-counter-badge\">{windows.length}</div>\n      )}\n      {!$settings.value.showWindowTitle && (\n        <div\n          className={cx(\"weg-item-open-sign\", {\n            \"weg-item-open-sign-active\": windows.length > 0,\n            \"weg-item-open-sign-focused\": windows.some((w) => w.hwnd === $focused.value.hwnd),\n          })}\n        />\n      )}\n    </div>\n  );\n\n  if (isOverlay) {\n    return itemNode;\n  }\n\n  return (\n    <Popover\n      open={openPreview}\n      placement={calculatePlacement($settings.value.position)}\n      onOpenChange={(open) => {\n        setOpenPreview(open && moment(new Date()) > blockUntil);\n        $open_popups.value[item.id] = open;\n      }}\n      trigger=\"hover\"\n      arrow={false}\n      content={\n        <BackgroundByLayersV2\n          className={cx(\"weg-item-preview-container\", $settings.value.position.toLowerCase())}\n          onMouseMoveCapture={(e) => e.stopPropagation()}\n          onContextMenu={(e) => {\n            e.stopPropagation();\n            e.preventDefault();\n          }}\n          prefix=\"preview\"\n        >\n          <div className=\"weg-item-preview-scrollbar\">\n            {windows.map((window) => (\n              <UserApplicationPreview key={window.hwnd} title={window.title} hwnd={window.hwnd} />\n            ))}\n            {windows.length === 0 && <div className=\"weg-item-display-name\">{item.displayName}</div>}\n          </div>\n        </BackgroundByLayersV2>\n      }\n    >\n      {itemNode}\n    </Popover>\n  );\n}\n\nexport function UserApplication({ item, isOverlay }: Props) {\n  const windows = getWindowsForItem(item, $interactables.value);\n\n  const { splitWindows, spaceBetweenItems } = $settings.value;\n  if (splitWindows && windows.length > 1) {\n    return (\n      <Flex align=\"center\" gap={spaceBetweenItems}>\n        {windows.map((window) => (\n          <UserApplicationItem\n            key={window.hwnd}\n            item={item}\n            isOverlay={isOverlay}\n            windows={[window]}\n          />\n        ))}\n      </Flex>\n    );\n  }\n\n  return <UserApplicationItem item={item} isOverlay={isOverlay} windows={windows} />;\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/UserApplicationContextMenu.tsx",
    "content": "import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\nimport type { ContextMenu, ContextMenuItem, UserAppWindow } from \"@seelen-ui/lib/types\";\nimport type { TFunction } from \"i18next\";\n\nimport type { AppOrFileWegItem } from \"../../shared/types.ts\";\n\nimport { $dock_state_actions } from \"../../shared/state/items.ts\";\nimport { $settings } from \"../../shared/state/settings.ts\";\n\nconst identifier = crypto.randomUUID();\nconst onAppMenuClick = \"weg::app_menu_click\";\n\nlet pendingAppItem: AppOrFileWegItem | null = null;\nlet pendingAppWindows: UserAppWindow[] = [];\n\nWidget.self.webview.listen(onAppMenuClick, ({ payload }) => {\n  const { key } = payload as { key: string };\n  const item = pendingAppItem;\n  const windows = pendingAppWindows;\n  if (!item) return;\n\n  if (key === \"unpin\") {\n    if (windows.length) {\n      $dock_state_actions.unpinApp(item.id);\n    } else {\n      $dock_state_actions.remove(item.id);\n    }\n  } else if (key === \"pin\") {\n    $dock_state_actions.pinApp(item.id);\n  } else if (key === \"run\") {\n    launchItem(item, false);\n  } else if (key === \"open_location\") {\n    invoke(SeelenCommand.SelectFileOnExplorer, { path: item.path });\n  } else if (key === \"run_as\") {\n    launchItem(item, true);\n  } else if (key === \"copy_hwnd\") {\n    navigator.clipboard.writeText(JSON.stringify(windows.map((w) => w.hwnd.toString(16))));\n  } else if (key === \"close\") {\n    windows.forEach((w) => {\n      invoke(SeelenCommand.WegCloseApp, { hwnd: w.hwnd });\n    });\n  } else if (key === \"kill\") {\n    windows.forEach((w) => {\n      invoke(SeelenCommand.WegKillApp, { hwnd: w.hwnd });\n    });\n  }\n});\n\nexport function getUserApplicationContextMenu(\n  t: TFunction,\n  item: AppOrFileWegItem,\n  windows: UserAppWindow[],\n): ContextMenu {\n  pendingAppItem = item;\n  pendingAppWindows = windows;\n\n  const items: ContextMenuItem[] = [];\n\n  if (!item.preventPinning) {\n    if (item.pinned) {\n      items.push({\n        type: \"Item\",\n        key: \"unpin\",\n        icon: \"RiUnpinLine\",\n        label: t(\"app_menu.unpin\"),\n        callbackEvent: onAppMenuClick,\n      });\n    } else {\n      items.push({\n        type: \"Item\",\n        key: \"pin\",\n        icon: \"RiPushpinLine\",\n        label: t(\"app_menu.pin\"),\n        callbackEvent: onAppMenuClick,\n      });\n    }\n    items.push({ type: \"Separator\" });\n  }\n\n  items.push(\n    {\n      type: \"Item\",\n      key: \"run\",\n      icon: \"IoOpenOutline\",\n      label: item.displayName,\n      callbackEvent: onAppMenuClick,\n    },\n    {\n      type: \"Item\",\n      key: \"open_location\",\n      icon: \"MdOutlineMyLocation\",\n      label: t(\"app_menu.open_file_location\"),\n      callbackEvent: onAppMenuClick,\n    },\n    {\n      type: \"Item\",\n      key: \"run_as\",\n      icon: \"MdOutlineAdminPanelSettings\",\n      label: t(\"app_menu.run_as\"),\n      callbackEvent: onAppMenuClick,\n    },\n  );\n\n  if (windows.length) {\n    if ($settings.value.devTools) {\n      items.push({\n        type: \"Item\",\n        key: \"copy_hwnd\",\n        icon: \"AiOutlineCopy\",\n        label: t(\"app_menu.copy_handles\"),\n        callbackEvent: onAppMenuClick,\n      });\n    }\n\n    items.push({\n      type: \"Item\",\n      key: \"close\",\n      icon: \"BiWindowClose\",\n      label: windows.length > 1 ? t(\"app_menu.close_multiple\") : t(\"app_menu.close\"),\n      callbackEvent: onAppMenuClick,\n    });\n\n    if ($settings.value.showEndTask) {\n      items.push({\n        type: \"Item\",\n        key: \"kill\",\n        icon: \"MdOutlineDangerous\",\n        label: windows.length > 1 ? t(\"app_menu.kill_multiple\") : t(\"app_menu.kill\"),\n        callbackEvent: onAppMenuClick,\n      });\n    }\n  }\n\n  return { identifier, items };\n}\n\nexport function launchItem(item: AppOrFileWegItem, elevated: boolean) {\n  if (item.relaunch) {\n    return invoke(SeelenCommand.Run, {\n      program: item.relaunch.command,\n      args: item.relaunch.args,\n      workingDir: item.relaunch.workingDir,\n      elevated,\n    });\n  }\n\n  return invoke(SeelenCommand.Run, {\n    program: item.umid ? `shell:AppsFolder\\\\${item.umid}` : item.path,\n    args: null,\n    workingDir: null,\n    elevated,\n  });\n}\n"
  },
  {
    "path": "src/ui/react/weg/modules/item/infra/UserApplicationPreview.tsx",
    "content": "import { SeelenCommand } from \"@seelen-ui/lib\";\nimport { Icon, MissingIcon } from \"libs/ui/react/components/Icon/index.tsx\";\nimport { convertFileSrc, invoke } from \"@tauri-apps/api/core\";\n\nimport { $delayedFocused, $previews } from \"../../shared/state/mod.ts\";\nimport type { TargetedMouseEvent } from \"preact\";\n\ninterface PreviewProps {\n  title: string;\n  hwnd: number;\n}\n\nexport const UserApplicationPreview = ({ title, hwnd }: PreviewProps) => {\n  const preview = $previews.value[hwnd];\n\n  function onClick() {\n    invoke(SeelenCommand.WegToggleWindowState, {\n      hwnd,\n      wasFocused: $delayedFocused.value?.hwnd === hwnd,\n    });\n  }\n\n  function onAuxClick(e: TargetedMouseEvent<HTMLDivElement>) {\n    if (e.button === 1) {\n      invoke(SeelenCommand.WegCloseApp, { hwnd });\n    }\n  }\n\n  function onClose(e: TargetedMouseEvent<HTMLDivElement>) {\n    e.stopPropagation();\n    invoke(SeelenCommand.WegCloseApp, { hwnd });\n  }\n\n  return (\n    <div className=\"weg-item-preview\" onClick={onClick} onAuxClick={onAuxClick}>\n      <div className=\"weg-item-preview-topbar\">\n        <div className=\"weg-item-preview-title\">{title}</div>\n        <div className=\"weg-item-preview-close\" onClick={onClose}>\n          <Icon iconName=\"IoClose\" />\n        </div>\n      </div>\n      <div className=\"weg-item-preview-image-container\">\n        {preview\n          ? (\n            <img\n              className=\"weg-item-preview-image\"\n              src={convertFileSrc(preview.path) + \"?v=\" + preview.hash}\n            />\n          )\n          : <MissingIcon className=\"weg-item-no-preview\" />}\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/state/hidden.ts",
    "content": "import { computed, effect, signal } from \"@preact/signals\";\nimport { HideMode } from \"@seelen-ui/lib/types\";\nimport { $is_this_webview_focused } from \"libs/ui/react/utils/signals.ts\";\nimport { debounce } from \"lodash\";\n\nimport { $mouse_at_edge } from \"./system.ts\";\nimport { $settings } from \"./settings.ts\";\nimport { $is_dock_overlapped } from \"./windows.ts\";\n\nexport const $open_popups = signal<Record<string, boolean>>({});\nexport const $there_are_open_popups = computed(() => Object.values($open_popups.value).some((v) => v));\n\nexport const $dock_should_be_hidden = signal(false);\nconst setDockAsHidden = computed(() => {\n  return debounce(() => ($dock_should_be_hidden.value = true), $settings.value.delayToHide);\n});\nconst setDockAsNotHidden = computed(() => {\n  return debounce(() => ($dock_should_be_hidden.value = false), $settings.value.delayToShow);\n});\n\neffect(() => {\n  let hidden = false;\n  let flush = false;\n\n  let isMouseOverEdge = $mouse_at_edge.value === $settings.value.position;\n\n  switch ($settings.value.hideMode) {\n    case HideMode.Never:\n      hidden = false;\n      flush = true;\n      break;\n    case HideMode.Always:\n      hidden = !$is_this_webview_focused.value && !$there_are_open_popups.value && !isMouseOverEdge;\n      break;\n    case HideMode.OnOverlap:\n      hidden = $is_dock_overlapped.value &&\n        !$is_this_webview_focused.value &&\n        !$there_are_open_popups.value &&\n        !isMouseOverEdge;\n      break;\n  }\n\n  if (hidden) {\n    setDockAsNotHidden.peek().cancel();\n    setDockAsHidden.peek()();\n    return;\n  }\n\n  setDockAsHidden.peek().cancel();\n  setDockAsNotHidden.peek()();\n  if (flush) {\n    setDockAsNotHidden.peek().flush();\n  }\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/state/items.ts",
    "content": "import { effect, signal } from \"@preact/signals\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { type WegItem, type WegItems, WegItemType } from \"@seelen-ui/lib/types\";\nimport { debounce } from \"lodash\";\n\nimport type { SeparatorWegItem } from \"../types.ts\";\nimport { emit, listen } from \"@tauri-apps/api/event\";\n\ninterface OptimisticDockState {\n  isReorderDisabled: boolean;\n  items: WegItem[];\n}\n\ninterface SyncPayload {\n  source: string;\n  state: OptimisticDockState;\n}\n\nconst CLIENT_ID = crypto.randomUUID();\n\nexport const HARDCODED_SEPARATOR_LEFT: SeparatorWegItem = {\n  id: \"hardcoded-separator-1\",\n  type: WegItemType.Separator,\n};\n\nexport const HARDCODED_SEPARATOR_RIGHT: SeparatorWegItem = {\n  id: \"hardcoded-separator-2\",\n  type: WegItemType.Separator,\n};\n\nexport const $dock_state = signal(getStateFromStored(await invoke(SeelenCommand.StateGetWegItems)));\n\nsubscribe(SeelenEvent.WegAddItem, (e) => {\n  const item: WegItem = {\n    ...e.payload,\n    id: crypto.randomUUID(), // ensure uniqueness\n    type: WegItemType.AppOrFile,\n  };\n\n  const items = [...$dock_state.value.items];\n  const separatorIdx = items.findIndex((i) => i.id === HARDCODED_SEPARATOR_RIGHT.id);\n  items.splice(separatorIdx, 0, item);\n  $dock_state.value = { ...$dock_state.value, items };\n});\n\nlet isRemoteUpdate = false;\nlisten<SyncPayload>(\"hidden::sync-dock-items\", ({ payload }) => {\n  if (payload.source === CLIENT_ID) return;\n\n  if (JSON.stringify(payload.state) !== JSON.stringify($dock_state.value)) {\n    isRemoteUpdate = true;\n    $dock_state.value = payload.state;\n  }\n});\n\nconst emitSyncEvent = debounce((items: OptimisticDockState) => {\n  emit<SyncPayload>(\"hidden::sync-dock-items\", {\n    source: CLIENT_ID,\n    state: items,\n  });\n}, 300);\n\nconst saveDockState = debounce(async (state: OptimisticDockState) => {\n  console.trace(\"Saving dock state\");\n\n  const index1 = state.items.findIndex((i) => i.id === HARDCODED_SEPARATOR_LEFT.id);\n  const index2 = state.items.findIndex((i) => i.id === HARDCODED_SEPARATOR_RIGHT.id);\n\n  await invoke(SeelenCommand.StateWriteWegItems, {\n    items: {\n      isReorderDisabled: state.isReorderDisabled,\n      left: state.items.slice(0, index1),\n      center: state.items.slice(index1 + 1, index2),\n      right: state.items.slice(index2 + 1),\n    },\n  });\n}, 1000);\n\nlet mounted = false;\neffect(() => {\n  const state = $dock_state.value;\n\n  // avoid writing on start of the widget\n  if (!mounted) {\n    mounted = true;\n    return;\n  }\n\n  if (isRemoteUpdate) {\n    isRemoteUpdate = false;\n    return;\n  }\n\n  emitSyncEvent(state);\n  saveDockState(state);\n});\n\nfunction getStateFromStored(state: WegItems): OptimisticDockState {\n  return {\n    isReorderDisabled: state.isReorderDisabled,\n    items: [\n      ...state.left,\n      HARDCODED_SEPARATOR_LEFT,\n      ...state.center,\n      HARDCODED_SEPARATOR_RIGHT,\n      ...state.right,\n    ],\n  };\n}\n\nexport const $dock_state_actions = {\n  remove(idToRemove: string) {\n    $dock_state.value = {\n      ...$dock_state.value,\n      items: $dock_state.value.items.filter((item) => item.id !== idToRemove),\n    };\n  },\n  pinApp(id: string) {\n    $dock_state.value = {\n      ...$dock_state.value,\n      items: $dock_state.value.items.map((item) => {\n        if (item.id === id) {\n          return { ...item, pinned: true };\n        }\n        return item;\n      }),\n    };\n  },\n  unpinApp(id: string) {\n    $dock_state.value = {\n      ...$dock_state.value,\n      items: $dock_state.value.items.map((item) => {\n        if (item.id === id) {\n          return { ...item, pinned: false };\n        }\n        return item;\n      }),\n    };\n  },\n  addMediaModule() {\n    if (!$dock_state.value.items.some((current) => current.type === WegItemType.Media)) {\n      const newItems = [...$dock_state.value.items];\n      newItems.push({\n        id: crypto.randomUUID(),\n        type: WegItemType.Media,\n      });\n      $dock_state.value = { ...$dock_state.value, items: newItems };\n    }\n  },\n  addStartModule() {\n    if (!$dock_state.value.items.some((current) => current.type === WegItemType.StartMenu)) {\n      const newItems = [...$dock_state.value.items];\n      newItems.unshift({\n        id: crypto.randomUUID(),\n        type: WegItemType.StartMenu,\n      });\n      $dock_state.value = { ...$dock_state.value, items: newItems };\n    }\n  },\n  addDesktopModule() {\n    if (!$dock_state.value.items.some((current) => current.type === WegItemType.ShowDesktop)) {\n      const newItems = [...$dock_state.value.items];\n      newItems.unshift({\n        id: crypto.randomUUID(),\n        type: WegItemType.ShowDesktop,\n      });\n      $dock_state.value = { ...$dock_state.value, items: newItems };\n    }\n  },\n  addTrashBinModule() {\n    if (!$dock_state.value.items.some((current) => current.type === WegItemType.TrashBin)) {\n      const newItems = [...$dock_state.value.items];\n      newItems.push({\n        id: crypto.randomUUID(),\n        type: WegItemType.TrashBin,\n      });\n      $dock_state.value = { ...$dock_state.value, items: newItems };\n    }\n  },\n};\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/state/mod.ts",
    "content": "import { $interactables, getWindowsForItem } from \"./windows.ts\";\nimport { $dock_state, HARDCODED_SEPARATOR_RIGHT } from \"./items.ts\";\nimport { effect } from \"@preact/signals\";\nimport { WegItemType } from \"@seelen-ui/lib/types\";\nimport type { AppOrFileWegItem } from \"../types.ts\";\n\neffect(() => {\n  const interactables = $interactables.value;\n  const state = $dock_state.value;\n\n  const appOrFileItems = state.items.filter(\n    (item): item is AppOrFileWegItem => item.type === WegItemType.AppOrFile,\n  );\n\n  // Remove non-pinned items that have no windows\n  const itemsToRemove = new Set(\n    appOrFileItems\n      .filter((item) => !item.pinned && getWindowsForItem(item, interactables).length === 0)\n      .map((item) => item.id),\n  );\n\n  // Find windows not covered by any remaining item\n  const remainingItems = appOrFileItems.filter((item) => !itemsToRemove.has(item.id));\n  const uncoveredWindows = interactables.filter(\n    (w) => !remainingItems.some((item) => getWindowsForItem(item, [w]).length > 0),\n  );\n\n  // Group by umid or process path to avoid duplicate items for the same app\n  const seen = new Set<string>();\n  const newItems: AppOrFileWegItem[] = [];\n\n  for (const w of uncoveredWindows) {\n    const key = w.umid ?? w.process.path?.toString();\n    if (!key) continue;\n    if (seen.has(key)) continue;\n    seen.add(key);\n\n    newItems.push({\n      id: crypto.randomUUID(),\n      type: WegItemType.AppOrFile,\n      displayName: w.appName,\n      umid: w.umid ?? null,\n      path: w.process.path?.toString() ?? \"\",\n      pinned: false,\n      preventPinning: w.preventPinning,\n      relaunch: w.relaunch ?? null,\n    });\n  }\n\n  if (itemsToRemove.size === 0 && newItems.length === 0) return;\n\n  const filteredItems = state.items.filter((item) => !itemsToRemove.has(item.id));\n  const separatorRightIdx = filteredItems.findIndex((i) => i.id === HARDCODED_SEPARATOR_RIGHT.id);\n  $dock_state.value = {\n    ...state,\n    items: [\n      ...filteredItems.slice(0, separatorRightIdx),\n      ...newItems,\n      ...filteredItems.slice(separatorRightIdx),\n    ],\n  };\n});\n\nexport * from \"./settings.ts\";\nexport * from \"./windows.ts\";\nexport * from \"./system.ts\";\nexport * from \"./items.ts\";\nexport * from \"./hidden.ts\";\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/state/settings.ts",
    "content": "import { lazySignal } from \"libs/ui/react/utils/LazySignal\";\nimport { Settings } from \"@seelen-ui/lib\";\nimport { Alignment } from \"@seelen-ui/lib/types\";\nimport { computed, effect } from \"@preact/signals\";\nimport i18n from \"../../../i18n\";\nimport { $current_monitor } from \"./system\";\nimport { toPhysicalPixels } from \"libs/ui/react/utils\";\nimport { SeelenWegSide } from \"node_modules/@seelen-ui/lib/esm/gen/types/SeelenWegSide\";\n\nexport const $settings = lazySignal(async () => {\n  const settings = await Settings.getAsync();\n  return {\n    ...settings.byWidget[\"@seelen/weg\"],\n    language: settings.language,\n    devTools: settings.devTools,\n  };\n});\nSettings.onChange((settings) => {\n  $settings.value = {\n    ...settings.byWidget[\"@seelen/weg\"],\n    language: settings.language,\n    devTools: settings.devTools,\n  };\n});\nawait $settings.init();\n\nexport const isHorizontalDock = computed(\n  () =>\n    $settings.value.position === SeelenWegSide.Top ||\n    $settings.value.position === SeelenWegSide.Bottom,\n);\n\neffect(() => {\n  const settings = $settings.value;\n  i18n.changeLanguage(settings.language || undefined);\n\n  // @deprecated on future a utility function to parse widget settings as variables will be used.\n  const styles = document.documentElement.style;\n\n  styles.setProperty(\"--config-margin\", `${settings.margin}px`);\n  styles.setProperty(\"--config-padding\", `${settings.padding}px`);\n\n  styles.setProperty(\"--config-item-size\", `${settings.size}px`);\n  styles.setProperty(\"--config-item-zoom-size\", `${settings.zoomSize}px`);\n  styles.setProperty(\"--config-space-between-items\", `${settings.spaceBetweenItems}px`);\n});\n\nexport function getDockContextMenuAlignment(position: SeelenWegSide): { alignX: Alignment; alignY: Alignment } {\n  switch (position) {\n    case SeelenWegSide.Bottom:\n      return { alignX: Alignment.Center, alignY: Alignment.End };\n    case SeelenWegSide.Top:\n      return { alignX: Alignment.Center, alignY: Alignment.Start };\n    case SeelenWegSide.Left:\n      return { alignX: Alignment.Start, alignY: Alignment.Center };\n    case SeelenWegSide.Right:\n      return { alignX: Alignment.End, alignY: Alignment.Center };\n  }\n}\n\nexport const $widget_rect = computed(() => {\n  const rect = { ...$current_monitor.value.rect };\n  const size = toPhysicalPixels(\n    $settings.value.size + $settings.value.padding * 2 + $settings.value.margin * 2,\n  );\n\n  switch ($settings.value.position) {\n    case SeelenWegSide.Left:\n      rect.right = rect.left + size;\n      break;\n    case SeelenWegSide.Right:\n      rect.left = rect.right - size;\n      break;\n    case SeelenWegSide.Top:\n      rect.bottom = rect.top + size;\n      break;\n    case SeelenWegSide.Bottom:\n      rect.top = rect.bottom - size;\n      break;\n  }\n\n  return rect;\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/state/system.ts",
    "content": "import { computed } from \"@preact/signals\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { SeelenWegSide } from \"@seelen-ui/lib/types\";\nimport { lazySignal } from \"libs/ui/react/utils/LazySignal\";\n\nconst currentMonitorId = Widget.getCurrent().decoded.monitorId!;\n\nexport const $monitors = lazySignal(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, $monitors.setByPayload);\n\nexport const $players = lazySignal(() => invoke(SeelenCommand.GetMediaSessions));\nsubscribe(SeelenEvent.MediaSessions, $players.setByPayload);\n\nexport const $notifications = lazySignal(() => invoke(SeelenCommand.GetNotifications));\nsubscribe(SeelenEvent.Notifications, $notifications.setByPayload);\n\nexport const $mouse_pos = lazySignal(async () => {\n  const [x, y] = await invoke(SeelenCommand.GetMousePosition);\n  return { x, y };\n});\nsubscribe(SeelenEvent.GlobalMouseMove, ({ payload: [x, y] }) => {\n  $mouse_pos.value = { x, y };\n});\n\nexport const $trash_bin_info = lazySignal(() => invoke(SeelenCommand.GetTrashBinInfo));\nsubscribe(SeelenEvent.TrashBinChanged, $trash_bin_info.setByPayload);\n\nawait Promise.all([\n  $monitors.init(),\n  $players.init(),\n  $notifications.init(),\n  $mouse_pos.init(),\n  $trash_bin_info.init(),\n]);\n\nexport const $current_monitor = computed(\n  () => $monitors.value.find((m) => m.id === currentMonitorId)!,\n);\n\nexport const $mouse_at_edge = computed<SeelenWegSide | null>(() => {\n  const box = $current_monitor.value.rect;\n  const x = $mouse_pos.value.x;\n  const y = $mouse_pos.value.y;\n\n  if (x < box.left || x > box.right || y < box.top || y > box.bottom) {\n    return null;\n  }\n\n  if (y === box.top) {\n    return SeelenWegSide.Top;\n  }\n\n  if (x === box.left) {\n    return SeelenWegSide.Left;\n  }\n\n  if (y === box.bottom - 1) {\n    return SeelenWegSide.Bottom;\n  }\n\n  if (x === box.right - 1) {\n    return SeelenWegSide.Right;\n  }\n  return null;\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/state/windows.ts",
    "content": "import { lazySignal } from \"libs/ui/react/utils/LazySignal\";\nimport { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport type { FocusedApp, UserAppWindow } from \"@seelen-ui/lib/types\";\nimport { $widget_rect } from \"./settings\";\nimport { computed, signal } from \"@preact/signals\";\nimport { debounce } from \"lodash\";\nimport type { AppOrFileWegItem } from \"../types\";\n\n// on change of this function update src\\background\\widgets\\weg\\cli.rs too.\nexport function getWindowsForItem(\n  item: AppOrFileWegItem,\n  interactables: UserAppWindow[],\n): UserAppWindow[] {\n  if (item.umid) {\n    return interactables.filter((w) => w.umid && w.umid === item.umid);\n  }\n\n  const itemCommand = item.relaunch?.command.toLowerCase();\n  const itemPath = item.path.toLowerCase();\n\n  return interactables.filter((w) => {\n    const winPath = w.process.path?.toLowerCase() ?? \"\";\n    return winPath !== \"\" && (itemCommand === winPath || itemPath === winPath);\n  });\n}\n\nconst widget = Widget.getCurrent();\nconst selfWinId = await invoke(SeelenCommand.GetSelfWindowId);\n\nexport const $interactables = lazySignal(() => invoke(SeelenCommand.GetUserAppWindows));\nsubscribe(SeelenEvent.UserAppWindowsChanged, $interactables.setByPayload);\n\nexport const $previews = lazySignal(() => invoke(SeelenCommand.GetUserAppWindowsPreviews));\nsubscribe(SeelenEvent.UserAppWindowsPreviewsChanged, $previews.setByPayload);\n\n/** Used to check which window was last focused on interactions with the current window */\nexport const $delayedFocused = signal<FocusedApp | null>(null);\nexport const $focused = lazySignal(() => invoke(SeelenCommand.GetFocusedApp));\nexport const $lastFocusedOnMonitor = lazySignal<FocusedApp | null>(async () => {\n  const focused = await invoke(SeelenCommand.GetFocusedApp);\n  return focused.monitor === widget.decoded.monitorId ? focused : null;\n});\n\nconst setDelayedFocused = debounce((v: FocusedApp) => {\n  $delayedFocused.value = v;\n}, 200);\n\nsubscribe(SeelenEvent.GlobalFocusChanged, (e) => {\n  $focused.value = e.payload;\n\n  if (e.payload.monitor === widget.decoded.monitorId) {\n    $lastFocusedOnMonitor.value = e.payload;\n  }\n\n  setDelayedFocused(e.payload);\n  if (e.payload.hwnd !== selfWinId) {\n    setDelayedFocused.flush();\n  }\n});\n\nawait Promise.all([\n  $interactables.init(),\n  $previews.init(),\n  $focused.init(),\n  $lastFocusedOnMonitor.init(),\n]);\n\nexport const $is_dock_overlapped = computed(() => {\n  const by = $lastFocusedOnMonitor.value;\n  const interactables = $interactables.value;\n\n  if (!by || !by.rect) {\n    return false;\n  }\n\n  if (!interactables.some((w) => w.hwnd === by.hwnd)) {\n    return false;\n  }\n\n  const a = $widget_rect.value;\n  const b = by.rect;\n\n  // The edge pixel overlapping do not matters. This resolves the shared pixel in between the monitors,\n  // hereby a fullscreened app shared pixel collision does not hide other monitor windows.\n  if (a.right <= b.left || a.left >= b.right || a.bottom <= b.top || a.top >= b.bottom) {\n    return false;\n  }\n\n  return true;\n});\n"
  },
  {
    "path": "src/ui/react/weg/modules/shared/types.ts",
    "content": "import type { WegItem } from \"@seelen-ui/lib/types\";\n\nexport type AppOrFileWegItem = Extract<WegItem, { type: \"AppOrFile\" }>;\nexport type SeparatorWegItem = Extract<WegItem, { type: \"Separator\" }>;\nexport type MediaWegItem = Extract<WegItem, { type: \"Media\" }>;\nexport type StartMenuWegItem = Extract<WegItem, { type: \"StartMenu\" }>;\nexport type ShowDesktopWegItem = Extract<WegItem, { type: \"ShowDesktop\" }>;\nexport type TrashBinItem = Extract<WegItem, { type: \"TrashBin\" }>;\n\n/** @alias */\nexport type SwItem = WegItem;\n"
  },
  {
    "path": "src/ui/react/weg/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/react/weg/styles/global.css",
    "content": "/**\n * The styles in this file are only structural for Seelenweg,\n * all the design should be added to the default theme css.\n */\nbody {\n  overflow: hidden;\n  background: transparent;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  width: 100vw;\n  height: 100vh;\n\n  &:has(.taskbar.left) {\n    justify-content: flex-start;\n  }\n\n  &:has(.taskbar.right) {\n    justify-content: flex-end;\n  }\n\n  &:has(.taskbar.top) {\n    align-items: flex-start;\n  }\n\n  &:has(.taskbar.bottom) {\n    align-items: flex-end;\n  }\n}\n\n/** Root Styles */\n#root {\n  --dock-size: calc(var(--config-item-size) + (var(--config-padding) + var(--config-margin)) * 2);\n\n  position: absolute;\n  height: min-content;\n  width: min-content;\n  max-width: 100vw;\n  max-height: 100vh;\n  transition: transform 0.2s ease-in-out;\n\n  &:has(.taskbar.left) {\n    left: 0;\n  }\n\n  &:has(.taskbar.right) {\n    right: 0;\n  }\n\n  &:has(.taskbar.top) {\n    top: 0;\n  }\n\n  &:has(.taskbar.bottom) {\n    bottom: 0;\n  }\n\n  &:not(:hover) {\n    &:has(.taskbar.hidden.left) {\n      transform: translate(calc(var(--dock-size) * -1), 0);\n    }\n\n    &:has(.taskbar.hidden.right) {\n      transform: translate(var(--dock-size), 0);\n    }\n\n    &:has(.taskbar.hidden.top) {\n      transform: translate(0, calc(var(--dock-size) * -1));\n    }\n\n    &:has(.taskbar.hidden.bottom) {\n      transform: translate(0, var(--dock-size));\n    }\n  }\n}\n\n/** Taskbar Styles */\n.taskbar {\n  --max-taskbar-width: calc(100vw - var(--config-margin) * 2);\n  --max-taskbar-height: calc(100vh - var(--config-margin) * 2);\n\n  position: relative;\n  margin: var(--config-margin);\n  max-width: var(--max-taskbar-width);\n  max-height: var(--max-taskbar-height);\n\n  .weg-items-container {\n    overflow: auto;\n    width: 100%;\n    height: 100%;\n    padding: var(--config-padding);\n\n    &::-webkit-scrollbar {\n      display: none;\n    }\n\n    .weg-items {\n      display: flex;\n      gap: var(--config-space-between-items);\n    }\n  }\n\n  &.full-width {\n    .weg-item-drag-container:has(.weg-separator) {\n      flex: 1;\n    }\n  }\n\n  &.horizontal {\n    width: min-content;\n    height: calc(var(--config-item-size) + var(--config-padding) * 2);\n\n    &.full-width {\n      width: var(--max-taskbar-width);\n    }\n\n    .weg-items {\n      flex-direction: row;\n      min-width: 100%;\n      width: max-content;\n    }\n  }\n\n  &.vertical {\n    width: calc(var(--config-item-size) + var(--config-padding) * 2);\n    height: min-content;\n\n    &.full-width {\n      height: var(--max-taskbar-height);\n    }\n\n    .weg-items {\n      flex-direction: column;\n      min-height: 100%;\n      height: max-content;\n    }\n  }\n\n  &:has(.weg-item-drag-container.dragging) {\n    pointer-events: none;\n  }\n}\n\n.weg-separator {\n  opacity: 0;\n\n  .vertical & {\n    width: var(--config-item-size);\n  }\n\n  .horizontal & {\n    height: var(--config-item-size);\n  }\n\n  .weg-item-drag-container:not(:first-child):not(:last-child) > & {\n    &.visible {\n      opacity: 1;\n    }\n  }\n\n  .weg-item-drag-container:first-child > & {\n    .vertical & {\n      margin-top: calc(var(--config-space-between-items) * -1);\n    }\n\n    .horizontal & {\n      margin-left: calc(var(--config-space-between-items) * -1);\n    }\n  }\n\n  .weg-item-drag-container:last-child > & {\n    .vertical & {\n      margin-bottom: calc(var(--config-space-between-items) * -1);\n    }\n\n    .horizontal & {\n      margin-right: calc(var(--config-space-between-items) * -1);\n    }\n  }\n}\n\n.weg-item-preview-container {\n  position: relative;\n  max-width: 100vw;\n  gap: var(--config-space-between-items);\n\n  .weg-item-preview-scrollbar {\n    display: flex;\n    overflow-x: auto;\n\n    &::-webkit-scrollbar {\n      display: none;\n    }\n  }\n\n  .weg-item-preview {\n    z-index: 1;\n\n    &:hover {\n      filter: brightness(0.95);\n      backdrop-filter: brightness(0.95);\n    }\n\n    .weg-item-preview-topbar {\n      display: flex;\n      width: 240px;\n      justify-content: space-between;\n      gap: 10px;\n\n      .weg-item-preview-title {\n        overflow: hidden;\n        text-wrap: nowrap;\n        text-overflow: ellipsis;\n        flex: 1;\n      }\n\n      .weg-item-preview-close {\n        border-radius: 6px;\n        width: 20px;\n        height: 20px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        line-height: 5px;\n\n        &:hover {\n          background-color: #0000005c;\n        }\n      }\n    }\n  }\n}\n\n.ant-popover-container {\n  padding: 0 !important;\n}\n"
  },
  {
    "path": "src/ui/react/weg/styles/variables.css",
    "content": ":root {\n  --config-padding: 0px;\n  --config-margin: 0px;\n\n  --config-item-size: 0px;\n  --config-item-zoom-size: 0px;\n  --config-space-between-items: 0px;\n\n  --config-time-before-hide: 800ms;\n  --config-time-before-show: 100ms;\n}\n"
  },
  {
    "path": "src/ui/reduxRootState.ts",
    "content": "import type { UIColors } from \"@seelen-ui/lib/types\";\n\nexport interface IRootState<T> {\n  settings: T;\n  colors: UIColors;\n}\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/App.svelte",
    "content": "<script lang=\"ts\">\n  import { t } from \"./i18n\";\n  import { Widget, invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import type { WidgetId } from \"@seelen-ui/lib/types\";\n  import StartMenuBody from \"./components/StartMenuBody.svelte\";\n  import { globalState } from \"./state/mod.svelte\";\n  import { StartDisplayMode, StartView } from \"./constants\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { navigateInDirection } from \"./keyboard-navigation\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import { ConfigState } from \"./state/config.svelte\";\n  import { Effect } from \"@tauri-apps/api/window\";\n\n  let inputElement: HTMLInputElement | undefined = $state();\n\n  // Detect and manage query prefix\n  const queryPrefix = $derived.by(() => {\n    const query = globalState.searchQuery.trim();\n    const match = query.match(/^(apps|files|web):/i);\n    return match ? match[1]?.toLowerCase() || \"\" : \"\";\n  });\n\n  function handlePrefixChange(newPrefix: string) {\n    const query = globalState.searchQuery.trim();\n    const match = query.match(/^(apps|files|web):/i);\n    const search = match ? query.slice(match[0].length).trim() : query;\n\n    if (newPrefix) {\n      globalState.searchQuery = `${newPrefix}:${search ? \" \" + search : \"\"}`;\n    } else {\n      globalState.searchQuery = search;\n    }\n\n    inputElement?.focus();\n  }\n\n  function handleDocKeyDown(event: KeyboardEvent) {\n    switch (event.key) {\n      case \"Escape\":\n        event.preventDefault();\n        globalState.showing = false;\n        break;\n      case \"ArrowUp\":\n        event.preventDefault();\n        navigateInDirection(\"up\");\n        break;\n      case \"ArrowDown\":\n        event.preventDefault();\n        navigateInDirection(\"down\");\n        break;\n      case \"ArrowLeft\":\n        event.preventDefault();\n        navigateInDirection(\"left\");\n        break;\n      case \"ArrowRight\":\n        event.preventDefault();\n        navigateInDirection(\"right\");\n        break;\n    }\n  }\n\n  function handleInputKeyDown(event: KeyboardEvent) {\n    const input = event.currentTarget as HTMLInputElement;\n\n    switch (event.key) {\n      case \"Enter\":\n        event.preventDefault();\n\n        // Check for web: prefix\n        const query = globalState.searchQuery.trim();\n        const webPrefixMatch = query.match(/^web:/i);\n\n        if (webPrefixMatch) {\n          const searchQuery = query.slice(4).trim();\n          globalState.showing = false;\n          const encodedQuery = encodeURIComponent(searchQuery);\n          invoke(SeelenCommand.OpenFile, {\n            path: `https://www.google.com/search?q=${encodedQuery}`,\n          });\n          break;\n        }\n\n        // Click on preselected item or first item if none selected\n        let element: HTMLElement | null = null;\n\n        if (globalState.preselectedItem) {\n          element = document.querySelector(`[data-item-id=\"${globalState.preselectedItem}\"]`);\n        } else {\n          element = document.querySelector(\".app\");\n        }\n\n        if (element) {\n          element.click();\n        }\n        break;\n      case \"ArrowDown\":\n        event.stopPropagation();\n        navigateInDirection(\"down\");\n        break;\n      case \"ArrowRight\":\n        event.stopPropagation();\n        // Only navigate if cursor is at the end of the input\n        if (input.selectionStart === input.value.length) {\n          navigateInDirection(\"right\");\n        }\n        break;\n    }\n  }\n\n  // reset state when menu is shown\n  $effect(() => {\n    if (globalState.showing) {\n      globalState.searchQuery = \"\";\n      globalState.preselectedItem = null;\n      inputElement?.focus();\n    }\n  });\n\n  // Reset preselected item when search query changes\n  $effect(() => {\n    if (globalState.searchQuery) {\n      globalState.view = StartView.All;\n    }\n    globalState.preselectedItem = null;\n  });\n\n  $effect(() => {\n    Widget.self.ready({ show: false });\n  });\n\n  $effect(() => {\n    const effects = ConfigState.config.acrylic ? [Effect.Acrylic] : [];\n    Widget.self.window.setEffects({ effects });\n  });\n\n  function openUserMenu() {\n    invoke(SeelenCommand.TriggerWidget, {\n      payload: { id: \"@seelen/user-menu\" as WidgetId },\n    });\n  }\n\n  function openAppSettings() {\n    invoke(SeelenCommand.TriggerWidget, {\n      payload: { id: \"@seelen/settings\" as WidgetId },\n    });\n  }\n\n  function openPowerMenu() {\n    invoke(SeelenCommand.TriggerWidget, {\n      payload: { id: \"@seelen/power-menu\" as WidgetId },\n    });\n  }\n</script>\n\n<svelte:window onkeydown={handleDocKeyDown} />\n<div\n  class=\"apps-menu\"\n  class:fullscreen={globalState.displayMode === StartDisplayMode.Fullscreen}\n  data-acrylic={ConfigState.config.acrylic}\n>\n  <div class=\"apps-menu-header\">\n    <input\n      bind:this={inputElement}\n      bind:value={globalState.searchQuery}\n      type=\"search\"\n      data-skin=\"transparent\"\n      placeholder=\"Applications\"\n      onkeydown={handleInputKeyDown}\n    />\n\n    {#if globalState.searchQuery}\n      <select\n        data-skin=\"default\"\n        value={queryPrefix}\n        onchange={(e) => handlePrefixChange(e.currentTarget.value)}\n      >\n        <option value=\"\">{$t(\"query.all\")}</option>\n        <option value=\"apps\">{$t(\"query.apps\")}</option>\n        <option value=\"files\">{$t(\"query.files\")}</option>\n        <option value=\"web\">{$t(\"query.web\")}</option>\n      </select>\n    {/if}\n\n    <button\n      data-skin=\"default\"\n      onclick={() => {\n        globalState.view =\n          globalState.view === StartView.Favorites ? StartView.All : StartView.Favorites;\n        globalState.searchQuery = \"\";\n      }}\n    >\n      {#if globalState.view === StartView.Favorites}\n        {$t(\"all\")}\n      {:else}\n        {$t(\"back\")}\n      {/if}\n    </button>\n  </div>\n\n  <StartMenuBody />\n\n  <div class=\"apps-menu-footer\">\n    <div class=\"apps-menu-footer-left\">\n      <button data-skin=\"transparent\" class=\"user-profile\" onclick={openUserMenu}>\n        {#if globalState.user.profilePicturePath}\n          <img\n            class=\"user-profile-picture\"\n            src={convertFileSrc(globalState.user.profilePicturePath)}\n            alt={globalState.user.name}\n          />\n        {:else}\n          <Icon class=\"user-profile-picture\" iconName=\"PiFolderUser\" />\n        {/if}\n\n        <span>{globalState.user.name}</span>\n      </button>\n    </div>\n\n    <div class=\"apps-menu-footer-right\">\n      <button data-skin=\"transparent\" onclick={openAppSettings} title=\"App Settings\">\n        <Icon iconName=\"RiSettings4Fill\" />\n      </button>\n\n      <button data-skin=\"transparent\" onclick={openPowerMenu} title=\"Power Menu\">\n        <Icon iconName=\"IoPower\" />\n      </button>\n\n      <button\n        data-skin=\"transparent\"\n        onclick={() => {\n          globalState.displayMode =\n            globalState.displayMode === StartDisplayMode.Normal\n              ? StartDisplayMode.Fullscreen\n              : StartDisplayMode.Normal;\n        }}\n      >\n        <Icon\n          iconName={globalState.displayMode === StartDisplayMode.Fullscreen\n            ? \"IoContract\"\n            : \"IoExpand\"}\n        />\n      </button>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/components/AllAppsView.svelte",
    "content": "<script lang=\"ts\">\n  import { type StartMenuItem } from \"@seelen-ui/lib/types\";\n  import { globalState } from \"../state/mod.svelte\";\n  import { foldersAsStartMenuItems } from \"../state/knownFolders.svelte\";\n  import { t } from \"../i18n\";\n  import AppItem from \"./AppItem.svelte\";\n  import * as fuzzySearch from \"@m31coding/fuzzy-search\";\n  import { DragDropProvider } from \"@dnd-kit/svelte\";\n\n  interface Props {\n    onContextMenu: (event: MouseEvent, item: StartMenuItem) => void;\n  }\n\n  let { onContextMenu }: Props = $props();\n\n  const getItemKey = (item: StartMenuItem) => `${item.path}_${item.umid}`;\n\n  // Memoized filter function - avoid recreating on each render\n  const shouldIncludeItem = (item: StartMenuItem, prefix: string | null): boolean => {\n    if (prefix === \"web\") return false;\n\n    const isApp =\n      !!item.umid ||\n      item.path?.toLowerCase().endsWith(\".exe\") ||\n      item.path?.toLowerCase().endsWith(\".lnk\");\n\n    if (prefix === \"apps\") return isApp;\n    if (prefix === \"documents\") return !isApp;\n\n    return true;\n  };\n\n  // Cache filtered/sorted base items - only recalculates when allItems changes\n  const items = $derived.by(() => {\n    const allItems = globalState.allItems;\n    const filtered: StartMenuItem[] = [];\n    const seen = new Set<string>();\n\n    for (const item of allItems) {\n      if (!item.path) {\n        if (item.umid) {\n          const key = getItemKey(item);\n          if (!seen.has(key)) {\n            seen.add(key);\n            filtered.push(item);\n          }\n        }\n        continue;\n      }\n\n      const path = item.path.toLowerCase();\n      const lastSlash = Math.max(path.lastIndexOf(\"\\\\\"), path.lastIndexOf(\"/\"));\n      const filename = lastSlash >= 0 ? path.slice(lastSlash + 1) : path;\n\n      if (!filename.includes(\"uninstall\") && filename !== \"desktop.ini\") {\n        const key = getItemKey(item);\n        if (!seen.has(key)) {\n          seen.add(key);\n          filtered.push(item);\n        }\n      }\n    }\n\n    return filtered.sort((a, b) => a.display_name.localeCompare(b.display_name));\n  });\n\n  // Parse query only when searchQuery changes\n  const query = $derived.by(() => {\n    const rawQuery = globalState.searchQuery.trim();\n    const prefixMatch = rawQuery.match(/^(apps|files|web):/i);\n\n    if (prefixMatch) {\n      const prefix = prefixMatch[1]!.toLowerCase();\n      const search = rawQuery.slice(prefix.length + 1).trim();\n      return { prefix, search, isSearching: true };\n    }\n\n    return { prefix: null, search: rawQuery, isSearching: rawQuery.length > 0 };\n  });\n\n  // Only create searcher when items or search state changes - not on every query change\n  let cachedSearcher: fuzzySearch.DynamicSearcher<StartMenuItem, string> | null = null;\n  let lastSearchableItemsKey = \"\";\n\n  const filteredItems = $derived.by(() => {\n    if (!query.isSearching) {\n      return items;\n    }\n\n    if (query.prefix === \"web\") {\n      return [];\n    }\n\n    // Build searchable items list, deduplicating by key to avoid crashes on duplicate entries\n    const searchableItems: StartMenuItem[] = [];\n    const seenKeys = new Set<string>();\n    const { prefix } = query;\n\n    for (const item of items) {\n      if (shouldIncludeItem(item, prefix)) {\n        const key = getItemKey(item);\n        if (!seenKeys.has(key)) {\n          seenKeys.add(key);\n          searchableItems.push(item);\n        }\n      }\n    }\n\n    // Add known folders when searching\n    for (const item of foldersAsStartMenuItems.value) {\n      if (shouldIncludeItem(item, prefix)) {\n        const key = getItemKey(item);\n        if (!seenKeys.has(key)) {\n          seenKeys.add(key);\n          searchableItems.push(item);\n        }\n      }\n    }\n\n    // Create a stable key to check if we need to rebuild the searcher\n    const itemsKey = `${items.length}_${foldersAsStartMenuItems.value.length}_${prefix}`;\n\n    if (cachedSearcher === null || lastSearchableItemsKey !== itemsKey) {\n      const config = fuzzySearch.Config.createDefaultConfig();\n      // Allow all non-surrogate characters so non-Latin scripts (CJK, Arabic, etc.) are searchable,\n      // but exclude surrogate code points (0xD800–0xDFFF) which the library cannot safely handle.\n      config.normalizerConfig.allowCharacter = (c) => {\n        const code = c.charCodeAt(0);\n        return code < 0xd800 || code > 0xdfff;\n      };\n      cachedSearcher = fuzzySearch.SearcherFactory.createSearcher<StartMenuItem, string>(config);\n\n      cachedSearcher.indexEntities(searchableItems, getItemKey, (item) => [item.display_name]);\n\n      lastSearchableItemsKey = itemsKey;\n    }\n\n    if (!query.search) {\n      return searchableItems;\n    }\n\n    try {\n      const result = cachedSearcher.getMatches(new fuzzySearch.Query(query.search, 21));\n      return result.matches.map((match) => match.entity);\n    } catch {\n      // Fall back to simple case-insensitive substring match if fuzzy search fails\n      const lower = query.search.toLowerCase();\n      return searchableItems.filter((item) => item.display_name.toLowerCase().includes(lower));\n    }\n  });\n</script>\n\n<DragDropProvider>\n  <div class=\"all-apps-view\">\n    <div class=\"all-apps-view-list\">\n      {#each filteredItems as item, idx (getItemKey(item))}\n        <AppItem {item} {idx} {onContextMenu} draggable={false} lazy />\n      {/each}\n    </div>\n\n    {#if filteredItems.length === 0 && (query.search.length > 0 || query.prefix === \"web\")}\n      <div class=\"all-apps-view-empty\">\n        {query.prefix === \"web\" ? $t(\"web_search\") : $t(\"no_matching_items\")}\n      </div>\n    {/if}\n  </div>\n</DragDropProvider>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/components/AppItem.svelte",
    "content": "<script lang=\"ts\">\n  import type { StartMenuItem } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { FileIcon } from \"libs/ui/svelte/components/Icon\";\n  import { globalState } from \"../state/mod.svelte\";\n  import { createSortable } from \"@dnd-kit/svelte/sortable\";\n\n  interface Props {\n    item: StartMenuItem;\n    idx: number;\n    onContextMenu: (event: MouseEvent, item: StartMenuItem) => void;\n    draggable?: boolean;\n    isActiveDropzone?: boolean;\n    isInsideFolder?: boolean;\n    lazy?: boolean;\n  }\n\n  let {\n    item,\n    idx,\n    onContextMenu,\n    draggable: isDraggable = true,\n    isActiveDropzone = false,\n    isInsideFolder = false,\n    lazy = false,\n  }: Props = $props();\n\n  const itemId = $derived(item.umid || item.path.toLowerCase());\n  const isPreselected = $derived(\n    globalState.preselectedItem === itemId || (idx === 0 && !globalState.preselectedItem),\n  );\n\n  const sortable = createSortable({\n    get id() {\n      return itemId;\n    },\n    get disabled() {\n      return !isDraggable;\n    },\n    get index() {\n      return idx;\n    },\n    get type() {\n      return isInsideFolder ? \"grouped-app\" : \"app\";\n    },\n    get accept() {\n      return isInsideFolder ? \"grouped-app\" : [\"folder\", \"app\"];\n    },\n  });\n\n  function handleClick(event: MouseEvent) {\n    globalState.showing = false; // inmediate close\n    let program = item.umid ? `shell:AppsFolder\\\\${item.umid}` : item.path;\n    invoke(SeelenCommand.OpenFile, { path: program });\n  }\n\n  function handleContextMenu(event: MouseEvent) {\n    onContextMenu(event, item);\n  }\n\n  // class:is-dragging={sortableData.isDragging.current}\n</script>\n\n<button\n  {@attach sortable.attach}\n  data-item-id={itemId}\n  class=\"app\"\n  class:preselected={isPreselected && globalState.searchQuery}\n  class:is-dragging={sortable.isDragging}\n  class:is-dropping={sortable.isDropping}\n  class:is-drop-target={isActiveDropzone}\n  onclick={handleClick}\n  oncontextmenu={handleContextMenu}\n  onfocus={() => {\n    globalState.preselectedItem = itemId;\n  }}\n>\n  <FileIcon class=\"app-icon\" path={item.path} umid={item.umid} {lazy} />\n  <div class=\"app-name\" title={item.display_name}>\n    {item.display_name}\n  </div>\n</button>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/components/FolderItem.svelte",
    "content": "<script lang=\"ts\">\n  import type { FavFolderItem } from \"../state/mod.svelte\";\n  import { FileIcon } from \"libs/ui/svelte/components/Icon\";\n  import { globalState } from \"../state/mod.svelte\";\n  import { t } from \"../i18n\";\n  import { createSortable } from \"@dnd-kit/svelte/sortable\";\n  import FolderModal from \"./FolderModal.svelte\";\n  import type { StartMenuItem } from \"@seelen-ui/lib/types\";\n\n  interface Props {\n    folder: FavFolderItem;\n    idx: number;\n    onContextMenu: (event: MouseEvent, folder: FavFolderItem | StartMenuItem) => void;\n    isActiveDropzone?: boolean;\n  }\n\n  let { folder, idx, onContextMenu, isActiveDropzone = false }: Props = $props();\n\n  let isModalOpen = $state(false);\n  $effect(() => {\n    if (!globalState.showing) {\n      isModalOpen = false;\n    }\n  });\n\n  const isPreselected = $derived(\n    globalState.preselectedItem === folder.itemId || (idx === 0 && !globalState.preselectedItem),\n  );\n\n  const folderName = $derived(folder.name || $t(\"folder\"));\n  const expandedItems = $derived(\n    folder.itemIds.map((id) => ({\n      id,\n      item: globalState.getMenuItem(id)!,\n    })),\n  );\n\n  const sortable = createSortable({\n    get id() {\n      return folder.itemId;\n    },\n    get index() {\n      return idx;\n    },\n    type: \"folder\",\n  });\n\n  function openModal() {\n    isModalOpen = true;\n  }\n\n  function closeModal() {\n    isModalOpen = false;\n  }\n</script>\n\n<button\n  {@attach sortable.attach}\n  data-item-id={folder.itemId}\n  class=\"folder\"\n  class:preselected={isPreselected && globalState.searchQuery}\n  class:is-dragging={sortable.isDragging}\n  class:is-dropping={sortable.isDropping}\n  class:is-drop-target={isActiveDropzone}\n  onclick={openModal}\n  oncontextmenu={(event) => {\n    onContextMenu(event, folder);\n  }}\n  onfocus={() => {\n    globalState.preselectedItem = folder.itemId;\n  }}\n>\n  <div class=\"folder-grid\">\n    {#each expandedItems.slice(0, 4) as app}\n      <div class=\"folder-preview\">\n        <FileIcon class=\"folder-preview-icon\" path={app.item.path} umid={app.item.umid} />\n      </div>\n    {/each}\n  </div>\n  <div class=\"folder-name\" title={folderName}>\n    {folderName}\n  </div>\n</button>\n\n{#if isModalOpen}\n  <FolderModal {folder} onClose={closeModal} {onContextMenu} />\n{/if}\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/components/FolderModal.svelte",
    "content": "<script lang=\"ts\">\n  import type { StartMenuItem } from \"@seelen-ui/lib/types\";\n  import type { FavFolderItem } from \"../state/mod.svelte\";\n  import { globalState } from \"../state/mod.svelte\";\n  import { t } from \"../i18n\";\n  import { DragDropProvider } from \"@dnd-kit/svelte\";\n  import AppItem from \"./AppItem.svelte\";\n  import { arrayMove } from \"../utils\";\n\n  interface Props {\n    folder: FavFolderItem;\n    onClose: () => void;\n    onContextMenu: (event: MouseEvent, item: StartMenuItem) => void;\n  }\n\n  let { folder, onClose, onContextMenu }: Props = $props();\n\n  const folderName = $derived(folder.name || $t(\"folder\"));\n  const expandedItems = $derived(\n    folder.itemIds.map((id) => ({\n      id,\n      item: globalState.getMenuItem(id)!,\n    }))\n  );\n\n  let dialog = $state<HTMLDialogElement>();\n</script>\n\n<dialog\n  bind:this={dialog}\n  open\n  class=\"folder-modal\"\n  closedby=\"any\"\n  onkeydown={(e) => {\n    e.stopPropagation(); // avoid window keydown events for navigation\n  }}\n  onclose={onClose}\n>\n  <DragDropProvider\n    onDragOver={(event) => {\n      const { source, target } = event.operation;\n      if (!source || !target || source.id === target.id) {\n        return;\n      }\n\n      const oldIndex = expandedItems.findIndex((item) => item.id === source.id);\n      const newIndex = expandedItems.findIndex((item) => item.id === target.id);\n\n      if (oldIndex !== -1 && newIndex !== -1) {\n        let newItems = arrayMove(expandedItems, oldIndex, newIndex);\n        globalState.updateFolder(folder.itemId, {\n          itemIds: newItems.map((item) => item.id),\n        });\n      }\n    }}\n    onDragEnd={(event) => {\n      const { source } = event.operation;\n      if (!dialog || !source || !source.element) {\n        return;\n      }\n\n      let rect = source.element.getBoundingClientRect();\n      let dialogRect = dialog.getBoundingClientRect();\n\n      const hasIntersection =\n        rect.right > dialogRect.left &&\n        rect.left < dialogRect.right &&\n        rect.bottom > dialogRect.top &&\n        rect.top < dialogRect.bottom;\n\n      if (hasIntersection) {\n        return;\n      }\n\n      const draggedItemId = source.id as string;\n      const newItemIds = folder.itemIds.filter((id) => id !== draggedItemId);\n\n      // Update folder with new items or handle folder removal\n      if (newItemIds.length >= 2) {\n        globalState.updateFolder(folder.itemId, { itemIds: newItemIds });\n      }\n      // Folder has only 1 item left, convert to app and remove folder\n      else if (newItemIds.length === 1) {\n        const remainingItemId = newItemIds[0]!;\n        globalState.pinnedItems = globalState.pinnedItems\n          .filter((item) => !(item.type === \"folder\" && item.itemId === folder.itemId))\n          .concat({ type: \"app\", itemId: remainingItemId });\n      }\n      // Folder is empty, just remove it\n      else {\n        globalState.pinnedItems = globalState.pinnedItems.filter(\n          (item) => !(item.type === \"folder\" && item.itemId === folder.itemId)\n        );\n      }\n\n      // Add dragged item as standalone app\n      globalState.pinnedItems = [\n        ...globalState.pinnedItems,\n        { type: \"app\", itemId: draggedItemId },\n      ];\n      onClose();\n    }}\n  >\n    <div class=\"folder-modal\">\n      <div class=\"folder-modal-content\">\n        <input\n          type=\"text\"\n          data-skin=\"transparent\"\n          class=\"folder-modal-name\"\n          value={folderName}\n          placeholder={$t(\"folder\")}\n          oninput={(e) => {\n            globalState.updateFolder(folder.itemId, { name: e.currentTarget.value });\n          }}\n        />\n\n        <div class=\"folder-modal-items\">\n          {#each expandedItems as { id, item }, idx (id)}\n            <AppItem {item} {idx} isInsideFolder={true} {onContextMenu} />\n          {/each}\n        </div>\n      </div>\n    </div>\n  </DragDropProvider>\n</dialog>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/components/PinnedView.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"../state/mod.svelte\";\n  import { t } from \"../i18n\";\n  import AppItem from \"./AppItem.svelte\";\n  import FolderItem from \"./FolderItem.svelte\";\n  import { arrayMove } from \"../utils\";\n  import { DragDropProvider } from \"@dnd-kit/svelte\";\n  import { debounce } from \"lodash\";\n\n  import type { StartMenuItem } from \"@seelen-ui/lib/types\";\n  import type { FavFolderItem } from \"../state/mod.svelte\";\n\n  type UniqueIdentifier = string | number;\n\n  interface Props {\n    onContextMenu: (event: MouseEvent, item: StartMenuItem | FavFolderItem) => void;\n  }\n\n  let { onContextMenu }: Props = $props();\n\n  // Position-based sorting threshold (20% of item width)\n  const POSITION_THRESHOLD = 0.2;\n  let activeDropzoneId: UniqueIdentifier | null = $state(null);\n\n  // Debounced folder creation activation\n  const FOLDER_CREATION_DELAY_MS = 200;\n  const activateDropzone = debounce((targetId: UniqueIdentifier) => {\n    activeDropzoneId = targetId;\n  }, FOLDER_CREATION_DELAY_MS);\n\n  function cancelDropzone() {\n    activateDropzone.cancel();\n    activeDropzoneId = null;\n  }\n</script>\n\n<div class=\"pinned-view\">\n  <div class=\"pinned-view-list\">\n    <DragDropProvider\n      onDragMove={(event) => {\n        const { source, target } = event.operation;\n\n        // No collision - cancel dropzone\n        if (!source || !target || source.id === target.id) {\n          cancelDropzone();\n          return;\n        }\n\n        const sourceIndex = globalState.pinnedItems.findIndex((item) => item.itemId === source.id);\n        const targetIndex = globalState.pinnedItems.findIndex((item) => item.itemId === target.id);\n\n        if (sourceIndex === -1 || targetIndex === -1) {\n          return;\n        }\n\n        const sourceElement = source.element;\n        const targetElement = target.element;\n        if (!sourceElement || !targetElement) {\n          return;\n        }\n\n        const targetRect = targetElement.getBoundingClientRect();\n        const sourceRect = sourceElement.getBoundingClientRect();\n\n        // Calculate relative position (0 = left edge, 1 = right edge)\n        const sourceCenterX = sourceRect.left + sourceRect.width / 2;\n        const targetLeft = targetRect.left;\n        const targetWidth = targetRect.width;\n        const relativePosition = (sourceCenterX - targetLeft) / targetWidth;\n\n        let shouldSort = false;\n        if (sourceIndex > targetIndex) {\n          shouldSort = relativePosition < POSITION_THRESHOLD;\n        } else if (sourceIndex < targetIndex) {\n          shouldSort = relativePosition > 1 - POSITION_THRESHOLD;\n        }\n\n        if (shouldSort && sourceIndex !== targetIndex) {\n          cancelDropzone();\n          globalState.pinnedItems = arrayMove(globalState.pinnedItems, sourceIndex, targetIndex);\n          return;\n        }\n\n        if (source.type === \"folder\" && target.type === \"app\") {\n          return;\n        }\n        activateDropzone(target.id);\n      }}\n      onDragOver={(event) => {\n        event.preventDefault();\n      }}\n      onDragEnd={(event) => {\n        const { source, target } = event.operation;\n        activateDropzone.cancel();\n\n        // Create folder if dropzone was active\n        if (activeDropzoneId && source && target) {\n          const sourceId = source.id.toString();\n          const targetId = activeDropzoneId.toString();\n\n          // Find source and target items\n          const sourceItem = globalState.pinnedItems.find((item) => item.itemId === sourceId);\n          const targetIndex = globalState.pinnedItems.findIndex((item) => item.itemId === targetId);\n          const targetItem = globalState.pinnedItems[targetIndex];\n\n          if (!targetItem || !sourceItem) {\n            cancelDropzone();\n            return;\n          }\n\n          // Case 1: Source is folder + Target is folder - merge folders\n          if (sourceItem.type === \"folder\" && targetItem.type === \"folder\") {\n            globalState.mergeFolders(sourceItem.itemId, targetItem.itemId);\n          }\n          // Case 2: Source is app + Target is folder - add app to existing folder\n          else if (sourceItem.type === \"app\" && targetItem.type === \"folder\") {\n            globalState.addItemToFolder(targetItem.itemId, sourceId);\n          }\n          // Case 3: Source is app + Target is app - create new folder with both items\n          else if (sourceItem.type === \"app\" && targetItem.type === \"app\") {\n            // Don't create folder if dragging onto self\n            if (sourceId !== targetId) {\n              globalState.createFolder(sourceId, targetId, targetIndex);\n            }\n          }\n        }\n\n        cancelDropzone();\n      }}\n    >\n      {#each globalState.pinnedItems as pinnedItem, idx (pinnedItem.itemId)}\n        {#if pinnedItem.type === \"app\"}\n          {@const item = globalState.getMenuItem(pinnedItem.itemId)}\n          {#if item}\n            <AppItem\n              {item}\n              {idx}\n              {onContextMenu}\n              isActiveDropzone={activeDropzoneId === pinnedItem.itemId}\n            />\n          {/if}\n        {:else if pinnedItem.type === \"folder\"}\n          {@const folder = pinnedItem}\n          <FolderItem\n            {folder}\n            {idx}\n            {onContextMenu}\n            isActiveDropzone={activeDropzoneId === pinnedItem.itemId}\n          />\n        {/if}\n      {/each}\n    </DragDropProvider>\n  </div>\n\n  {#if globalState.pinnedItems.length === 0}\n    <div class=\"pinned-view-empty\">\n      <p>{$t(\"welcome_message\")}</p>\n      <p>{$t(\"welcome_message2\")}</p>\n    </div>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/components/StartMenuBody.svelte",
    "content": "<script lang=\"ts\">\n  import type { StartMenuItem } from \"@seelen-ui/lib/types\";\n  import type { FavFolderItem } from \"../state/mod.svelte\";\n  import { globalState } from \"../state/mod.svelte\";\n  import { StartView } from \"../constants\";\n  import PinnedView from \"./PinnedView.svelte\";\n  import AllAppsView from \"./AllAppsView.svelte\";\n  import { t } from \"../i18n\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { invoke, SeelenCommand, SeelenEvent } from \"@seelen-ui/lib\";\n  import { emit } from \"@tauri-apps/api/event\";\n\n  let contextMenu = $state<HTMLDivElement>();\n  let contextMenuX = $state(0);\n  let contextMenuY = $state(0);\n  let activeContextMenuItem = $state<StartMenuItem | FavFolderItem | null>(null);\n\n  const activeContextMenuItemPinned = $derived(\n    activeContextMenuItem && \"path\" in activeContextMenuItem\n      ? globalState.isPinned(activeContextMenuItem)\n      : false,\n  );\n\n  const activeContextMenuIsFolder = $derived(\n    activeContextMenuItem ? \"itemIds\" in activeContextMenuItem : false,\n  );\n\n  function handleContextMenu(event: MouseEvent, item: StartMenuItem | FavFolderItem) {\n    contextMenuX = event.clientX;\n    contextMenuY = event.clientY;\n    activeContextMenuItem = item;\n  }\n\n  function handleTogglePin() {\n    if (activeContextMenuItem && \"path\" in activeContextMenuItem) {\n      globalState.togglePin(activeContextMenuItem);\n    }\n    activeContextMenuItem = null;\n  }\n\n  function handlePinToDock() {\n    if (activeContextMenuItem && \"path\" in activeContextMenuItem) {\n      const item = activeContextMenuItem;\n      emit(SeelenEvent.WegAddItem, {\n        id: crypto.randomUUID(),\n        displayName: item.display_name,\n        umid: item.umid,\n        path: item.path,\n        pinned: true,\n        preventPinning: false,\n        relaunch: null,\n      });\n    }\n    activeContextMenuItem = null;\n  }\n\n  function handleDisbandFolder() {\n    if (activeContextMenuItem && \"itemIds\" in activeContextMenuItem) {\n      globalState.disbandFolder(activeContextMenuItem.itemId);\n    }\n    activeContextMenuItem = null;\n  }\n\n  $effect(() => {\n    if (activeContextMenuItem === null) return;\n\n    const handleOutside = (event: MouseEvent) => {\n      if (!contextMenu?.contains(event.target as HTMLElement)) {\n        activeContextMenuItem = null;\n      }\n    };\n\n    let timeoutId = setTimeout(() => {\n      document.addEventListener(\"click\", handleOutside);\n      document.addEventListener(\"contextmenu\", handleOutside);\n    }, 0);\n\n    return () => {\n      clearTimeout(timeoutId);\n      document.removeEventListener(\"click\", handleOutside);\n      document.removeEventListener(\"contextmenu\", handleOutside);\n    };\n  });\n</script>\n\n<div class=\"apps-menu-body\">\n  {#if globalState.view === StartView.Favorites}\n    <PinnedView onContextMenu={handleContextMenu} />\n  {:else if globalState.view === StartView.All}\n    <AllAppsView onContextMenu={handleContextMenu} />\n  {/if}\n</div>\n\n{#if activeContextMenuItem}\n  <div\n    bind:this={contextMenu}\n    class=\"context-menu\"\n    style=\"left: {contextMenuX}px; top: {contextMenuY}px;\"\n    onclick={(e) => e.stopPropagation()}\n    oncontextmenu={(e) => e.stopPropagation()}\n    role=\"menu\"\n    tabindex=\"0\"\n    onkeydown={(e) => {\n      if (e.key === \"Enter\" || e.key === \" \") {\n        e.currentTarget.click();\n      }\n    }}\n  >\n    {#if activeContextMenuIsFolder}\n      <button class=\"context-menu-item\" onclick={handleDisbandFolder}>\n        <Icon iconName=\"GiExpand\" />\n        <span>{$t(\"disband\")}</span>\n      </button>\n    {:else if activeContextMenuItem && \"path\" in activeContextMenuItem}\n      <button class=\"context-menu-item\" onclick={handleTogglePin}>\n        <Icon iconName={activeContextMenuItemPinned ? \"TbPinnedOff\" : \"TbPin\"} />\n        <span>{activeContextMenuItemPinned ? $t(\"unpin\") : $t(\"pin\")}</span>\n      </button>\n\n      {#if activeContextMenuItem.path}\n        <button\n          class=\"context-menu-item\"\n          onclick={() => {\n            if (activeContextMenuItem && \"path\" in activeContextMenuItem) {\n              globalState.showing = false;\n              invoke(SeelenCommand.SelectFileOnExplorer, { path: activeContextMenuItem.path });\n              activeContextMenuItem = null;\n            }\n          }}\n        >\n          <Icon iconName=\"MdOutlineMyLocation\" />\n          <span>{$t(\"open_file_location\")}</span>\n        </button>\n      {/if}\n\n      <button class=\"context-menu-item\" onclick={handlePinToDock}>\n        <Icon iconName=\"RiPushpinLine\" />\n        <span>{$t(\"pin_to_dock\")}</span>\n      </button>\n\n      {#if activeContextMenuItem.umid || activeContextMenuItem.path.toLowerCase().endsWith(\".lnk\")}\n        <button\n          class=\"context-menu-item\"\n          onclick={() => {\n            if (activeContextMenuItem && \"path\" in activeContextMenuItem) {\n              globalState.showing = false;\n              let program = activeContextMenuItem.umid\n                ? `shell:AppsFolder\\\\${activeContextMenuItem.umid}`\n                : activeContextMenuItem.path;\n              invoke(SeelenCommand.Run, {\n                program,\n                args: null,\n                workingDir: null,\n                elevated: true,\n              });\n              activeContextMenuItem = null;\n            }\n          }}\n        >\n          <Icon iconName=\"MdOutlineAdminPanelSettings\" />\n          <span>{$t(\"run_as_admin\")}</span>\n        </button>\n      {/if}\n    {/if}\n  </div>\n{/if}\n\n<style>\n  :global(.context-menu) {\n    position: fixed;\n    z-index: 1000;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/constants.ts",
    "content": "export enum StartDisplayMode {\n  Normal = \"normal\",\n  Fullscreen = \"fullscreen\",\n}\n\nexport enum StartView {\n  All = \"all\",\n  Favorites = \"favorites\",\n}\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/af.yml",
    "content": "all: Alle toepassings\nback: Terug\ndisband: Ontbind gids\nfolder: Vouer\nno_matching_items: Geen items gevind wat by jou soektog pas nie\nopen_file_location: Maak lêerligging oop\npin: Speld vas\npin_to_dock: Speld aan Dock vas\nquery:\n  all: Almal\n  apps: Toepassings\n  files: Lêers\n  web: Web\nrun_as_admin: Begin as administrateur\nunpin: Ontspeld\nweb_search: Druk Enter om op die web te soek\nwelcome_message: Welkom by die Seelen UI-programkieslys.\nwelcome_message2: Op hierdie oortjie kan jy jou gunsteling programme vaspen.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/am.yml",
    "content": "all: ሁሉም መተግበሪያዎች\nback: ተመለስ\ndisband: አቃፊን አሰናብት\nfolder: አቃፊ\nno_matching_items: ከእርስዎ ፍለጋ ጋር የሚዛመዱ ምንም ንጥሎች አልተገኙም።\nopen_file_location: የፋይል ቦታን ክፈት\npin: ፒን\npin_to_dock: ወደ መትከያ ይሰኩት\nquery:\n  all: ሁሉም\n  apps: መተግበሪያዎች\n  files: ፋይሎች\n  web: ድር\nrun_as_admin: እንደ አስተዳዳሪ ያሂዱ\nunpin: ንቀል\nweb_search: ድሩን ለመፈለግ አስገባን ይጫኑ\nwelcome_message: እንኳን ወደ Seelen UI መተግበሪያ ምናሌ በደህና መጡ።\nwelcome_message2: በዚህ ትር ላይ የሚወዷቸውን መተግበሪያዎች መሰካት ይችላሉ።\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ar.yml",
    "content": "all: جميع التطبيقات\nback: خلف\ndisband: حل المجلد\nfolder: مجلد\nno_matching_items: لم يتم العثور على عناصر مطابقة لبحثك\nopen_file_location: فتح موقع الملف\npin: دبوس\npin_to_dock: تثبيت على قفص الاتهام\nquery:\n  all: الجميع\n  apps: تطبيقات\n  files: ملفات\n  web: ويب\nrun_as_admin: تشغيل كمسؤول\nunpin: إزالة التثبيت\nweb_search: اضغط على Enter للبحث في الويب\nwelcome_message: مرحبًا بك في قائمة تطبيق Seelen UI.\nwelcome_message2: في علامة التبويب هذه، يمكنك تثبيت تطبيقاتك المفضلة.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/az.yml",
    "content": "all: Bütün Proqramlar\nback: Geri\ndisband: Qovluğu ləğv edin\nfolder: Qovluq\nno_matching_items: Axtarışınıza uyğun heç bir element tapılmadı\nopen_file_location: Fayl yerini açın\npin: Pin\npin_to_dock: Dock-a sancın\nquery:\n  all: Hamısı\n  apps: Proqramlar\n  files: Fayllar\n  web: Veb\nrun_as_admin: Administrator olaraq işə salın\nunpin: Sancağı çıxarın\nweb_search: Vebdə axtarış etmək üçün Enter düyməsini basın\nwelcome_message: Seelen UI tətbiq menyusuna xoş gəlmisiniz.\nwelcome_message2: Bu tabda siz sevimli proqramlarınızı sanclaya bilərsiniz.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/bg.yml",
    "content": "all: Всички приложения\nback: Назад\ndisband: Разпуснете папката\nfolder: Папка\nno_matching_items: Няма намерени артикули, отговарящи на вашето търсене\nopen_file_location: Отворете местоположението на файла\npin: ПИН\npin_to_dock: Фиксиране към Dock\nquery:\n  all: Всички\n  apps: Приложения\n  files: файлове\n  web: Мрежа\nrun_as_admin: Стартирайте като администратор\nunpin: Откачване\nweb_search: Натиснете Enter, за да търсите в мрежата\nwelcome_message: Добре дошли в менюто на приложението Seelen UI.\nwelcome_message2: В този раздел можете да закачите любимите си приложения.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/bn.yml",
    "content": "all: সব অ্যাপ\nback: ব্যাক\ndisband: ফোল্ডারটি বিচ্ছিন্ন করুন\nfolder: ফোল্ডার\nno_matching_items: আপনার অনুসন্ধানের সাথে মেলে কোনো আইটেম পাওয়া যায়নি\nopen_file_location: ফাইলের অবস্থান খুলুন\npin: পিন\npin_to_dock: ডকে পিন করুন\nquery:\n  all: সব\n  apps: অ্যাপস\n  files: ফাইল\n  web: ওয়েব\nrun_as_admin: প্রশাসক হিসাবে চালান\nunpin: আনপিন করুন\nweb_search: ওয়েব অনুসন্ধান করতে এন্টার টিপুন\nwelcome_message: Seelen UI অ্যাপ মেনুতে স্বাগতম।\nwelcome_message2: এই ট্যাবে, আপনি আপনার প্রিয় অ্যাপগুলি পিন করতে পারেন৷\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/bs.yml",
    "content": "all: Sve aplikacije\nback: Nazad\ndisband: Disband Folder\nfolder: Folder\nno_matching_items: Nema pronađenih artikala koji odgovaraju vašem pretraživanju\nopen_file_location: Otvorite lokaciju datoteke\npin: Pin\npin_to_dock: Zakačite na Dock\nquery:\n  all: Sve\n  apps: aplikacije\n  files: Fajlovi\n  web: Web\nrun_as_admin: Pokreni kao administrator\nunpin: Otkvači\nweb_search: Pritisnite Enter da pretražite web\nwelcome_message: Dobrodošli u meni aplikacije Seelen UI.\nwelcome_message2: Na ovoj kartici možete zakačiti svoje omiljene aplikacije.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ca.yml",
    "content": "all: Totes les aplicacions\nback: Enrere\ndisband: Dissoldre la carpeta\nfolder: Carpeta\nno_matching_items: No s'ha trobat cap element que coincideixi amb la vostra cerca\nopen_file_location: Obriu la ubicació del fitxer\npin: Pin\npin_to_dock: Fixa al Dock\nquery:\n  all: Tots\n  apps: Aplicacions\n  files: Fitxers\n  web: Web\nrun_as_admin: Executar com a administrador\nunpin: Desenganxa\nweb_search: Premeu Intro per cercar al web\nwelcome_message: Benvingut al menú de l'aplicació Seelen UI.\nwelcome_message2: En aquesta pestanya, podeu fixar les vostres aplicacions preferides.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/cs.yml",
    "content": "all: Všechny aplikace\nback: Zadní\ndisband: Rozpustit složku\nfolder: Složka\nno_matching_items: Nebyly nalezeny žádné položky odpovídající vašemu hledání\nopen_file_location: Otevřete Umístění souboru\npin: Kolík\npin_to_dock: Připnout do doku\nquery:\n  all: Vše\n  apps: Aplikace\n  files: Soubory\n  web: Web\nrun_as_admin: Spustit jako správce\nunpin: Odepnout\nweb_search: Stisknutím klávesy Enter vyhledáte na webu\nwelcome_message: Vítejte v nabídce aplikace Seelen UI.\nwelcome_message2: Na této kartě můžete připnout své oblíbené aplikace.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/cy.yml",
    "content": "all: Pob Ap\nback: Yn ol\ndisband: Ffolder Diddymu\nfolder: Ffolder\nno_matching_items: Ni chanfuwyd unrhyw eitemau sy'n cyfateb i'ch chwiliad\nopen_file_location: Agor Lleoliad Ffeil\npin: Pin\npin_to_dock: Pin i'r Doc\nquery:\n  all: Pawb\n  apps: Apiau\n  files: Ffeiliau\n  web: Gwe\nrun_as_admin: Rhedeg fel Gweinyddwr\nunpin: Dad-binio\nweb_search: Pwyswch Enter i chwilio'r we\nwelcome_message: Croeso i ddewislen app Seelen UI.\nwelcome_message2: Ar y tab hwn, gallwch binio'ch hoff apiau.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/da.yml",
    "content": "all: Alle apps\nback: Tilbage\ndisband: Opløs mappe\nfolder: Mappe\nno_matching_items: Der blev ikke fundet nogen varer, der matcher din søgning\nopen_file_location: Åbn Filplacering\npin: Stift\npin_to_dock: Fastgør til dock\nquery:\n  all: Alle\n  apps: Apps\n  files: Filer\n  web: Web\nrun_as_admin: Kør som administrator\nunpin: Frigør\nweb_search: Tryk på Enter for at søge på nettet\nwelcome_message: Velkommen til Seelen UI-appmenuen.\nwelcome_message2: På denne fane kan du fastgøre dine yndlingsapps.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/de.yml",
    "content": "all: Alle Apps\nback: Zurück\ndisband: Ordner auflösen\nfolder: Ordner\nno_matching_items: Es wurden keine Artikel gefunden, die Ihrer Suche entsprechen\nopen_file_location: Öffnen Sie den Dateispeicherort\npin: Stift\npin_to_dock: An Dock anheften\nquery:\n  all: Alle\n  apps: Apps\n  files: Dateien\n  web: Web\nrun_as_admin: Als Administrator ausführen\nunpin: Lösen\nweb_search: Drücken Sie die Eingabetaste, um das Internet zu durchsuchen\nwelcome_message: Willkommen im Menü der Seelen UI-App.\nwelcome_message2: Auf dieser Registerkarte können Sie Ihre Lieblings-Apps anpinnen.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/el.yml",
    "content": "all: Όλες οι εφαρμογές\nback: Πίσω\ndisband: Διάλυση φακέλου\nfolder: Φάκελος\nno_matching_items: Δεν βρέθηκαν στοιχεία που να ταιριάζουν με την αναζήτησή σας\nopen_file_location: Ανοίξτε την τοποθεσία αρχείου\npin: Καρφίτσα\npin_to_dock: Καρφίτσωμα στο Dock\nquery:\n  all: Ολοι\n  apps: Εφαρμογές\n  files: Αρχεία\n  web: Ιστός\nrun_as_admin: Εκτέλεση ως Διαχειριστής\nunpin: Ξεκαρφιτσώνω\nweb_search: Πατήστε Enter για αναζήτηση στον Ιστό\nwelcome_message: Καλώς ήρθατε στο μενού της εφαρμογής Seelen UI.\nwelcome_message2: Σε αυτήν την καρτέλα, μπορείτε να καρφιτσώσετε τις αγαπημένες σας εφαρμογές.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/en.yml",
    "content": "all: All Apps\nback: Back\ndisband: Disband Folder\nfolder: Folder\nno_matching_items: No items found matching your search\nopen_file_location: Open File Location\npin: Pin\npin_to_dock: Pin to Dock\nquery:\n  all: All\n  apps: Apps\n  files: Files\n  web: Web\nrun_as_admin: Run as Administrator\nunpin: Unpin\nweb_search: Press Enter to search the web\nwelcome_message: Welcome to the Seelen UI app menu.\nwelcome_message2: On this tab, you can pin your favorite apps.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/es.yml",
    "content": "all: Todas las aplicaciones\nback: Atrás\ndisband: Disolver carpeta\nfolder: Carpeta\nno_matching_items: No se encontraron artículos que coincidan con su búsqueda\nopen_file_location: Abrir ubicación del archivo\npin: Alfiler\npin_to_dock: Anclar al muelle\nquery:\n  all: Toda\n  apps: Aplicaciones\n  files: Archivos\n  web: Web\nrun_as_admin: Ejecutar como administradora\nunpin: Desprender\nweb_search: Presione Enter para buscar en la web\nwelcome_message: Bienvenido al menú de la aplicación Seelen UI.\nwelcome_message2: En esta pestaña, puedes fijar tus aplicaciones favoritas.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/et.yml",
    "content": "all: Kõik rakendused\nback: Tagasi\ndisband: Laiali kaust\nfolder: Kaust\nno_matching_items: Teie otsingule vastavaid üksusi ei leitud\nopen_file_location: Avage faili asukoht\npin: Pin\npin_to_dock: Kinnita dokki\nquery:\n  all: Kõik\n  apps: Rakendused\n  files: Failid\n  web: Veeb\nrun_as_admin: Käivita administraatorina\nunpin: Vabastage\nweb_search: Veebis otsimiseks vajutage sisestusklahvi\nwelcome_message: Tere tulemast Seeleni kasutajaliidese rakenduse menüüsse.\nwelcome_message2: Sellel vahekaardil saate kinnitada oma lemmikrakendused.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/eu.yml",
    "content": "all: Aplikazio guztiak\nback: Itzuli\ndisband: Desegin karpeta\nfolder: Karpeta\nno_matching_items: Ez da aurkitu zure bilaketarekin bat datorren elementurik\nopen_file_location: Ireki fitxategiaren kokapena\npin: Pin\npin_to_dock: Ainguratu Dock-era\nquery:\n  all: Denak\n  apps: Aplikazioak\n  files: Fitxategiak\n  web: Weba\nrun_as_admin: Exekutatu Administratzaile gisa\nunpin: Kendu aingura\nweb_search: Sakatu Sartu sarean bilatzeko\nwelcome_message: Ongi etorri Seelen UI aplikazioaren menura.\nwelcome_message2: Fitxa honetan, gogoko dituzun aplikazioak aingura ditzakezu.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/fa.yml",
    "content": "all: همه برنامه ها\nback: برگشت\ndisband: پوشه را از بین ببرید\nfolder: پوشه\nno_matching_items: هیچ موردی مطابق با جستجوی شما یافت نشد\nopen_file_location: مکان فایل را باز کنید\npin: سنجاق\npin_to_dock: پین به داک\nquery:\n  all: همه\n  apps: برنامه ها\n  files: فایل ها\n  web: وب\nrun_as_admin: به عنوان Administrator اجرا شود\nunpin: پین را بردارید\nweb_search: برای جستجو در وب، Enter را فشار دهید\nwelcome_message: به منوی اپلیکیشن Seelen UI خوش آمدید.\nwelcome_message2: در این تب می توانید اپلیکیشن های مورد علاقه خود را پین کنید.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/fi.yml",
    "content": "all: Kaikki sovellukset\nback: Takaisin\ndisband: Pura kansio\nfolder: Kansio\nno_matching_items: Hakuasi vastaavia kohteita ei löytynyt\nopen_file_location: Avaa Tiedoston sijainti\npin: Pin\npin_to_dock: Kiinnitä Dockiin\nquery:\n  all: Kaikki\n  apps: Sovellukset\n  files: Tiedostot\n  web: Web\nrun_as_admin: Suorita järjestelmänvalvojana\nunpin: Irrota kiinnitys\nweb_search: Hae verkosta painamalla Enter\nwelcome_message: Tervetuloa Seelen UI -sovellusvalikkoon.\nwelcome_message2: Tällä välilehdellä voit kiinnittää suosikkisovelluksesi.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/fr.yml",
    "content": "all: Toutes les applications\nback: Dos\ndisband: Supprimer le dossier\nfolder: Dossier\nno_matching_items: Aucun article trouvé correspondant à votre recherche\nopen_file_location: Emplacement du fichier ouvert\npin: Épingle\npin_to_dock: Épingler au Dock\nquery:\n  all: Toute\n  apps: Applications\n  files: Fichiers\n  web: Web\nrun_as_admin: Exécuter en tant qu'administrateur\nunpin: Détacher\nweb_search: Appuyez sur Entrée pour effectuer une recherche sur le Web\nwelcome_message: Bienvenue dans le menu de l'application Seelen UI.\nwelcome_message2: Sur cet onglet, vous pouvez épingler vos applications préférées.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/gu.yml",
    "content": "all: બધી એપ્સ\nback: પાછળ\ndisband: ફોલ્ડર વિખેરી નાખો\nfolder: ફોલ્ડર\nno_matching_items: તમારી શોધ સાથે મેળ ખાતી કોઈ આઇટમ મળી નથી\nopen_file_location: ફાઇલ સ્થાન ખોલો\npin: પિન\npin_to_dock: ડોક પર પિન કરો\nquery:\n  all: બધા\n  apps: એપ્લિકેશન્સ\n  files: ફાઇલો\n  web: વેબ\nrun_as_admin: એડમિનિસ્ટ્રેટર તરીકે ચલાવો\nunpin: અનપિન કરો\nweb_search: વેબ પર શોધવા માટે Enter દબાવો\nwelcome_message: Seelen UI એપ મેનુમાં આપનું સ્વાગત છે.\nwelcome_message2: આ ટેબ પર, તમે તમારી મનપસંદ એપ્લિકેશનોને પિન કરી શકો છો.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/he.yml",
    "content": "all: כל האפליקציות\nback: בְּחֲזָרָה\ndisband: תיקיית פירוק\nfolder: תיקייה\nno_matching_items: לא נמצאו פריטים התואמים לחיפוש שלך\nopen_file_location: פתח את מיקום הקובץ\npin: פִּין\npin_to_dock: הצמד למעגן\nquery:\n  all: כֹּל\n  apps: אפליקציות\n  files: קבצים\n  web: אינטרנט\nrun_as_admin: הפעל כמנהל\nunpin: לְהוֹצִיא סִיכָּה\nweb_search: הקש Enter כדי לחפש באינטרנט\nwelcome_message: ברוכים הבאים לתפריט אפליקציית ממשק המשתמש של Seelen.\nwelcome_message2: בכרטיסייה זו, תוכל להצמיד את האפליקציות המועדפות עליך.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/hi.yml",
    "content": "all: सभी ऐप्स\nback: पीछे\ndisband: फ़ोल्डर विघटित करें\nfolder: फ़ोल्डर\nno_matching_items: आपकी खोज से मेल खाता कोई आइटम नहीं मिला\nopen_file_location: फ़ाइल के स्थान को खोलें\npin: नत्थी करना\npin_to_dock: डॉक पर पिन करें\nquery:\n  all: सभी\n  apps: ऐप्स\n  files: फ़ाइलें\n  web: वेब\nrun_as_admin: व्यवस्थापक के रूप में चलाएं\nunpin: अनपिन\nweb_search: वेब पर खोजने के लिए Enter दबाएँ\nwelcome_message: सीलेन यूआई ऐप मेनू में आपका स्वागत है।\nwelcome_message2: इस टैब पर आप अपने पसंदीदा ऐप्स को पिन कर सकते हैं।\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/hr.yml",
    "content": "all: Sve aplikacije\nback: Nazad\ndisband: Raspusti mapu\nfolder: Mapa\nno_matching_items: Nema pronađenih stavki koje odgovaraju vašem pretraživanju\nopen_file_location: Otvorite lokaciju datoteke\npin: Pin\npin_to_dock: Prikvači na Dock\nquery:\n  all: Sve\n  apps: aplikacije\n  files: Datoteke\n  web: Web\nrun_as_admin: Pokreni kao administrator\nunpin: Otkvači\nweb_search: Pritisnite Enter za pretraživanje weba\nwelcome_message: Dobro došli u izbornik aplikacije Seelen UI.\nwelcome_message2: Na ovoj kartici možete prikvačiti svoje omiljene aplikacije.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/hu.yml",
    "content": "all: Minden alkalmazás\nback: Vissza\ndisband: Mappa feloldása\nfolder: Mappa\nno_matching_items: Nem található a keresésnek megfelelő elem\nopen_file_location: Nyissa meg a Fájl helye lehetőséget\npin: Pin\npin_to_dock: Rögzítés a dokkolóhoz\nquery:\n  all: Minden\n  apps: Alkalmazások elemre\n  files: Fájlok\n  web: Web\nrun_as_admin: Futtassa rendszergazdaként\nunpin: Kibont\nweb_search: Az interneten való kereséshez nyomja meg az Enter billentyűt\nwelcome_message: Üdvözöljük a Seelen UI alkalmazás menüjében.\nwelcome_message2: Ezen a lapon rögzítheti kedvenc alkalmazásait.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/hy.yml",
    "content": "all: Բոլոր հավելվածները\nback: Ետ\ndisband: Քանդել թղթապանակը\nfolder: Պանակ\nno_matching_items: Ձեր որոնմանը համապատասխանող տարրեր չեն գտնվել\nopen_file_location: Բացեք Ֆայլի գտնվելու վայրը\npin: Փին\npin_to_dock: Ամրացրեք նավահանգիստին\nquery:\n  all: Բոլորը\n  apps: Հավելվածներ\n  files: Ֆայլեր\n  web: Վեբ\nrun_as_admin: Գործարկել որպես ադմինիստրատոր\nunpin: Ապամրացնել\nweb_search: Սեղմեք Enter՝ համացանցում որոնելու համար\nwelcome_message: 'Բարի գալուստ Seelen UI հավելվածի ընտրացանկ:'\nwelcome_message2: 'Այս ներդիրում կարող եք ամրացնել ձեր սիրած հավելվածները:'\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/id.yml",
    "content": "all: Semua Aplikasi\nback: Kembali\ndisband: Bubarkan Folder\nfolder: Folder\nno_matching_items: Tidak ada item yang cocok dengan pencarian Anda\nopen_file_location: Buka Lokasi File\npin: Pin\npin_to_dock: Sematkan ke Dock\nquery:\n  all: Semua\n  apps: Aplikasi\n  files: File\n  web: jaring\nrun_as_admin: Jalankan sebagai Administrator\nunpin: Membuka peniti\nweb_search: Tekan Enter untuk mencari di web\nwelcome_message: Selamat datang di menu aplikasi Seelen UI.\nwelcome_message2: Di tab ini, Anda dapat menyematkan aplikasi favorit Anda.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/is.yml",
    "content": "all: Öll forrit\nback: Til baka\ndisband: Slepptu möppu\nfolder: Mappa\nno_matching_items: Engin atriði fundust sem passa við leitina þína\nopen_file_location: Opnaðu skráarstaðsetningu\npin: Pinna\npin_to_dock: Festu við Dock\nquery:\n  all: Allt\n  apps: Forrit\n  files: Skrár\n  web: Vefur\nrun_as_admin: Keyra sem stjórnandi\nunpin: Losaðu\nweb_search: Ýttu á Enter til að leita á vefnum\nwelcome_message: Velkomin í Seelen UI app valmyndina.\nwelcome_message2: Á þessum flipa geturðu fest uppáhaldsforritin þín.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/it.yml",
    "content": "all: Tutte le app\nback: Indietro\ndisband: Sciogli cartella\nfolder: Cartella\nno_matching_items: Nessun articolo trovato corrispondente alla tua ricerca\nopen_file_location: Apri Posizione file\npin: Spillo\npin_to_dock: Aggiungi al Dock\nquery:\n  all: Tutto\n  apps: App\n  files: File\n  web: Rete\nrun_as_admin: Esegui come amministratore\nunpin: Sblocca\nweb_search: Premi Invio per effettuare ricerche sul Web\nwelcome_message: Benvenuto nel menu dell'app Seelen UI.\nwelcome_message2: In questa scheda puoi aggiungere le tue app preferite.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ja.yml",
    "content": "all: すべてのアプリ\nback: 戻る\ndisband: フォルダの登録解除\nfolder: フォルダー\nno_matching_items: 検索に一致するアイテムはありません\nopen_file_location: ファイルの場所を開く\npin: ピン留め\npin_to_dock: ドックにピン留めする\nquery:\n  all: 全て\n  apps: アプリ\n  files: ファイル\n  web: ウェブ\nrun_as_admin: 管理者として実行\nunpin: ピン留め解除\nweb_search: Enter キーを押して Web を検索\nwelcome_message: Seelen UI アプリ メニューへようこそ。\nwelcome_message2: このタブでは、お気に入りのアプリをピン留めできます。\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ka.yml",
    "content": "all: ყველა აპლიკაცია\nback: უკან\ndisband: საქაღალდის დაშლა\nfolder: საქაღალდე\nno_matching_items: თქვენს ძიების შესატყვისი ელემენტი ვერ მოიძებნა\nopen_file_location: გახსენით ფაილის ადგილმდებარეობა\npin: პინი\npin_to_dock: ჩამაგრება დოკში\nquery:\n  all: ყველა\n  apps: აპები\n  files: ფაილები\n  web: ვებ\nrun_as_admin: გაუშვით როგორც ადმინისტრატორი\nunpin: ჩამაგრების მოხსნა\nweb_search: დააჭირეთ Enter ინტერნეტში მოსაძებნად\nwelcome_message: კეთილი იყოს თქვენი მობრძანება Seelen UI აპლიკაციის მენიუში.\nwelcome_message2: ამ ჩანართზე შეგიძლიათ დაამაგროთ თქვენი საყვარელი აპები.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/km.yml",
    "content": "all: កម្មវិធីទាំងអស់។\nback: ត្រឡប់មកវិញ\ndisband: បំបែកថតឯកសារ\nfolder: ថត\nno_matching_items: រកមិនឃើញធាតុដែលត្រូវនឹងការស្វែងរករបស់អ្នកទេ។\nopen_file_location: បើកទីតាំងឯកសារ\npin: ម្ជុល\npin_to_dock: ខ្ទាស់ទៅចត\nquery:\n  all: ទាំងអស់។\n  apps: កម្មវិធី\n  files: ឯកសារ\n  web: បណ្តាញ\nrun_as_admin: ដំណើរការជាអ្នកគ្រប់គ្រង\nunpin: ដកខ្ទាស់\nweb_search: ចុច Enter ដើម្បីស្វែងរកគេហទំព័រ\nwelcome_message: សូមស្វាគមន៍មកកាន់ម៉ឺនុយកម្មវិធី Seelen UI ។\nwelcome_message2: នៅលើផ្ទាំងនេះ អ្នកអាចខ្ទាស់កម្មវិធីដែលអ្នកចូលចិត្ត។\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ko.yml",
    "content": "all: 모든 앱\nback: 뒤쪽에\ndisband: 폴더 해체\nfolder: 폴더\nno_matching_items: 검색어와 일치하는 항목을 찾을 수 없습니다.\nopen_file_location: 파일 위치 열기\npin: 핀\npin_to_dock: 도킹에 고정\nquery:\n  all: 모두\n  apps: 앱\n  files: 파일\n  web: 편물\nrun_as_admin: 관리자로 실행\nunpin: 고정 해제\nweb_search: 웹을 검색하려면 Enter를 누르세요.\nwelcome_message: Seelen UI 앱 메뉴에 오신 것을 환영합니다.\nwelcome_message2: 이 탭에서는 즐겨찾는 앱을 고정할 수 있습니다.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ku.yml",
    "content": "all: Hemû Apps\nback: Paş\ndisband: Peldanka hilweşînin\nfolder: Peldank\nno_matching_items: Tiştek li gorî lêgerîna we nehat dîtin\nopen_file_location: Cihê Pelê vekin\npin: Derzî\npin_to_dock: Pin to Dock\nquery:\n  all: Gişt\n  apps: Apps\n  files: Pelên\n  web: Tevn\nrun_as_admin: Wekî Rêveber bimeşîne\nunpin: Rakin\nweb_search: Enter bikirtînin da ku li tevneyê bigerin\nwelcome_message: Hûn bi xêr hatin menuya sepana Seelen UI.\nwelcome_message2: Li ser vê tabê, hûn dikarin sepanên xweyên bijare pîne bikin.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/lb.yml",
    "content": "all: All Apps\nback: Zréck\ndisband: Opléisen Dossier\nfolder: Dossier\nno_matching_items: Keng Artikele fonnt déi mat Ärer Sich passen\nopen_file_location: Open Datei Location\npin: Pin\npin_to_dock: Pin an Dock\nquery:\n  all: All\n  apps: Apps\n  files: Fichieren\n  web: Web\nrun_as_admin: Run als Administrator\nunpin: Unpin\nweb_search: Dréckt Enter fir um Internet ze sichen\nwelcome_message: Wëllkomm op der Seelen UI App Menü.\nwelcome_message2: Op dëser Tab kënnt Dir Är Liiblingsapps pin.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/lo.yml",
    "content": "all: ແອັບທັງໝົດ\nback: ກັບຄືນໄປບ່ອນ\ndisband: Disband Folder\nfolder: ໂຟລເດີ\nno_matching_items: ບໍ່ພົບລາຍການທີ່ກົງກັບການຊອກຫາຂອງທ່ານ\nopen_file_location: ເປີດສະຖານທີ່ໄຟລ໌\npin: ປັກໝຸດ\npin_to_dock: ປັກໝຸດໃສ່ Dock\nquery:\n  all: ທັງໝົດ\n  apps: ແອັບ\n  files: ໄຟລ໌\n  web: ເວັບ\nrun_as_admin: ດໍາເນີນການເປັນ Administrator\nunpin: ຖອນປັກໝຸດ\nweb_search: ກົດ Enter ເພື່ອຊອກຫາເວັບ\nwelcome_message: ຍິນດີຕ້ອນຮັບສູ່ເມນູແອັບ Seelen UI.\nwelcome_message2: ໃນແຖບນີ້, ທ່ານສາມາດປັກໝຸດແອັບທີ່ທ່ານມັກໄດ້.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/lt.yml",
    "content": "all: Visos programos\nback: Atgal\ndisband: Išskleisti aplanką\nfolder: Aplankas\nno_matching_items: Nerasta prekių, atitinkančių jūsų paiešką\nopen_file_location: Atidarykite failo vietą\npin: Smeigtukas\npin_to_dock: Prisegti prie doko\nquery:\n  all: Visi\n  apps: Programėlės\n  files: Failai\n  web: Žiniatinklis\nrun_as_admin: Vykdyti kaip administratorius\nunpin: Atsegti\nweb_search: Norėdami ieškoti žiniatinklyje, paspauskite Enter\nwelcome_message: Sveiki atvykę į Seelen UI programos meniu.\nwelcome_message2: Šiame skirtuke galite prisegti mėgstamiausias programas.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/lv.yml",
    "content": "all: Visas lietotnes\nback: Atpakaļ\ndisband: Izformēt mapi\nfolder: Mape\nno_matching_items: Nav atrasts neviens vienums, kas atbilstu jūsu meklēšanai\nopen_file_location: Atveriet Faila atrašanās vietu\npin: Piespraust\npin_to_dock: Piespraust pie doka\nquery:\n  all: Visi\n  apps: Lietotnes\n  files: Faili\n  web: Web\nrun_as_admin: Palaist kā administrators\nunpin: Atspraust\nweb_search: Nospiediet Enter, lai meklētu tīmeklī\nwelcome_message: Laipni lūdzam Seelen UI lietotnes izvēlnē.\nwelcome_message2: Šajā cilnē varat piespraust savas iecienītākās lietotnes.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/mk.yml",
    "content": "all: Сите апликации\nback: Назад\ndisband: Растворете ја папката\nfolder: Папка\nno_matching_items: Не се најдени ставки што одговараат на вашето пребарување\nopen_file_location: Отворете ја локацијата на датотеката\npin: Пин\npin_to_dock: Закачете на приклучокот\nquery:\n  all: Сите\n  apps: Апликации\n  files: Датотеки\n  web: Веб\nrun_as_admin: Стартувај како администратор\nunpin: Откачи\nweb_search: Притиснете Enter за да пребарувате на интернет\nwelcome_message: Добре дојдовте во менито на апликацијата Seelen UI.\nwelcome_message2: На оваа картичка, можете да ги закачите вашите омилени апликации.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/mn.yml",
    "content": "all: Бүх програмууд\nback: Буцах\ndisband: Фолдерыг хаах\nfolder: Хавтас\nno_matching_items: Таны хайлтад тохирох зүйл олдсонгүй\nopen_file_location: Файлын байршлыг нээх\npin: Pin\npin_to_dock: Dock руу бэхлэх\nquery:\n  all: Бүгд\n  apps: Програмууд\n  files: Файлууд\n  web: Вэб\nrun_as_admin: Администратороор ажиллуулна уу\nunpin: Буулгах\nweb_search: Вэбээс хайхын тулд Enter дарна уу\nwelcome_message: Seelen UI програмын цэсэнд тавтай морилно уу.\nwelcome_message2: Энэ таб дээр та дуртай програмуудаа хавчуулж болно.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ms.yml",
    "content": "all: Semua Apl\nback: belakang\ndisband: Bubarkan Folder\nfolder: Folder\nno_matching_items: Tiada item ditemui sepadan dengan carian anda\nopen_file_location: Buka Lokasi Fail\npin: Pin\npin_to_dock: Sematkan pada Dok\nquery:\n  all: Semua\n  apps: Apl\n  files: Fail\n  web: Web\nrun_as_admin: Jalankan sebagai Pentadbir\nunpin: Nyahsemat\nweb_search: Tekan Enter untuk mencari di web\nwelcome_message: Selamat datang ke menu apl UI Seelen.\nwelcome_message2: Pada tab ini, anda boleh menyematkan apl kegemaran anda.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/mt.yml",
    "content": "all: L-Apps kollha\nback: Lura\ndisband: Xolja Folder\nfolder: Folder\nno_matching_items: Ma nstab l-ebda oġġett li jaqbel mat-tfittxija tiegħek\nopen_file_location: Open File Location\npin: Pin\npin_to_dock: Pin għall-Dock\nquery:\n  all: Kollha\n  apps: Apps\n  files: Fajls\n  web: Web\nrun_as_admin: Mexxi bħala Amministratur\nunpin: Unpin\nweb_search: Agħfas Enter biex tfittex il-web\nwelcome_message: Merħba fil-menu tal-app Seelen UI.\nwelcome_message2: Fuq din it-tab, tista' tippinja l-apps favoriti tiegħek.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ne.yml",
    "content": "all: सबै एपहरू\nback: पछाडि\ndisband: फोल्डर भत्काउनुहोस्\nfolder: फोल्डर\nno_matching_items: तपाईको खोजसँग मेल खाने कुनै वस्तुहरू फेला परेनन्\nopen_file_location: फाइल स्थान खोल्नुहोस्\npin: पिन\npin_to_dock: डकमा पिन गर्नुहोस्\nquery:\n  all: सबै\n  apps: एपहरू\n  files: फाइलहरू\n  web: वेब\nrun_as_admin: प्रशासकको रूपमा चलाउनुहोस्\nunpin: अनपिन गर्नुहोस्\nweb_search: वेब खोज्न इन्टर थिच्नुहोस्\nwelcome_message: Seelen UI एप मेनुमा स्वागत छ।\nwelcome_message2: यस ट्याबमा, तपाईंले आफ्नो मनपर्ने एपहरू पिन गर्न सक्नुहुन्छ।\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/nl.yml",
    "content": "all: Alle apps\nback: Rug\ndisband: Map ontbinden\nfolder: Map\nno_matching_items: Er zijn geen items gevonden die overeenkomen met uw zoekopdracht\nopen_file_location: Open Bestandslocatie\npin: Pin\npin_to_dock: Vastmaken aan dock\nquery:\n  all: Alle\n  apps: Apps\n  files: Bestanden\n  web: Web\nrun_as_admin: Uitvoeren als beheerder\nunpin: Losmaken\nweb_search: Druk op Enter om op internet te zoeken\nwelcome_message: Welkom bij het Seelen UI-appmenu.\nwelcome_message2: Op dit tabblad kunt u uw favoriete apps vastzetten.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/no.yml",
    "content": "all: Alle apper\nback: Tilbake\ndisband: Oppløs mappe\nfolder: Mappe\nno_matching_items: Ingen elementer funnet som samsvarer med søket ditt\nopen_file_location: Åpne filplassering\npin: Pin\npin_to_dock: Fest til dock\nquery:\n  all: Alle\n  apps: Apper\n  files: Filer\n  web: Web\nrun_as_admin: Kjør som administrator\nunpin: Løsne\nweb_search: Trykk på Enter for å søke på nettet\nwelcome_message: Velkommen til Seelen UI-appmenyen.\nwelcome_message2: På denne fanen kan du feste favorittappene dine.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/pa.yml",
    "content": "all: ਸਾਰੀਆਂ ਐਪਾਂ\nback: ਪਿੱਛੇ\ndisband: ਫੋਲਡਰ ਨੂੰ ਡਿਸਬੈਂਡ ਕਰੋ\nfolder: ਫੋਲਡਰ\nno_matching_items: ਤੁਹਾਡੀ ਖੋਜ ਨਾਲ ਮੇਲ ਖਾਂਦੀ ਕੋਈ ਆਈਟਮ ਨਹੀਂ ਮਿਲੀ\nopen_file_location: ਫਾਈਲ ਟਿਕਾਣਾ ਖੋਲ੍ਹੋ\npin: ਪਿੰਨ\npin_to_dock: ਡੌਕ 'ਤੇ ਪਿੰਨ ਕਰੋ\nquery:\n  all: ਸਾਰੇ\n  apps: ਐਪਸ\n  files: ਫਾਈਲਾਂ\n  web: ਵੈੱਬ\nrun_as_admin: ਪ੍ਰਸ਼ਾਸਕ ਵਜੋਂ ਚਲਾਓ\nunpin: ਅਨਪਿੰਨ ਕਰੋ\nweb_search: ਵੈੱਬ ਖੋਜਣ ਲਈ ਐਂਟਰ ਦਬਾਓ\nwelcome_message: Seelen UI ਐਪ ਮੀਨੂ ਵਿੱਚ ਸੁਆਗਤ ਹੈ।\nwelcome_message2: ਇਸ ਟੈਬ 'ਤੇ, ਤੁਸੀਂ ਆਪਣੀਆਂ ਮਨਪਸੰਦ ਐਪਾਂ ਨੂੰ ਪਿੰਨ ਕਰ ਸਕਦੇ ਹੋ।\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/pl.yml",
    "content": "all: Wszystkie aplikacje\nback: Z powrotem\ndisband: Rozwiąż folder\nfolder: Folder\nno_matching_items: Nie znaleziono żadnych elementów pasujących do Twojego wyszukiwania\nopen_file_location: Otwórz lokalizację pliku\npin: Szpilka\npin_to_dock: Przypnij do Docka\nquery:\n  all: Wszystko\n  apps: Aplikacje\n  files: Akta\n  web: Sieć\nrun_as_admin: Uruchom jako administrator\nunpin: Odpiąć\nweb_search: Naciśnij Enter, aby przeszukać Internet\nwelcome_message: Witamy w menu aplikacji Seelen UI.\nwelcome_message2: Na tej karcie możesz przypiąć swoje ulubione aplikacje.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ps.yml",
    "content": "all: ټول اطلاقات\nback: شاته\ndisband: فولډر منحل کړئ\nfolder: پوښۍ\nno_matching_items: ستاسو د لټون سره سمون خوري هیڅ توکي ونه موندل شول\nopen_file_location: د فایل ځای خلاص کړئ\npin: پن\npin_to_dock: ډاک ته پین ​​کړئ\nquery:\n  all: ټول\n  apps: ایپس\n  files: فایلونه\n  web: ویب\nrun_as_admin: د مدیر په توګه چلول\nunpin: خلاص کړئ\nweb_search: د ویب لټون کولو لپاره Enter فشار ورکړئ\nwelcome_message: د Seelen UI ایپ مینو ته ښه راغلاست.\nwelcome_message2: په دې ټب کې، تاسو کولی شئ خپل غوره ایپسونه پین ​​کړئ.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/pt-BR.yml",
    "content": "all: Todos os aplicativos\nback: Voltar\ndisband: Dissolver pasta\nfolder: Pasta\nno_matching_items: Nenhum item encontrado que corresponda à sua pesquisa\nopen_file_location: Abrir local do arquivo\npin: Alfinete\npin_to_dock: Fixar no Dock\nquery:\n  all: Todos\n  apps: Aplicativas\n  files: Arquivos\n  web: Rede\nrun_as_admin: Executar como administrador\nunpin: Liberar\nweb_search: Pressione Enter para pesquisar na web\nwelcome_message: Bem-vindo ao menu do aplicativo Seelen UI.\nwelcome_message2: Nesta guia você pode fixar seus aplicativos favoritos.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/pt-PT.yml",
    "content": "all: Todos os aplicativos\nback: Costas\ndisband: Dissolver pasta\nfolder: Pasta\nno_matching_items: Nenhum item encontrado que corresponda à sua pesquisa\nopen_file_location: Abrir local do arquivo\npin: Pino\npin_to_dock: Fixar no Dock\nquery:\n  all: Tudo\n  apps: Aplicativas\n  files: Arquivos\n  web: Rede\nrun_as_admin: Executar como administrador\nunpin: Liberar\nweb_search: Pressione Enter para pesquisar na web\nwelcome_message: Bem-vindo ao menu do aplicativo Seelen UI.\nwelcome_message2: Nesta guia você pode fixar seus aplicativos favoritos.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ro.yml",
    "content": "all: Toate aplicațiile\nback: Spate\ndisband: Desfaceți folderul\nfolder: Dosar\nno_matching_items: Nu s-au găsit articole care să corespundă căutării dvs\nopen_file_location: Deschideți locația fișierului\npin: Pin\npin_to_dock: Fixați pe andocare\nquery:\n  all: Toate\n  apps: Aplicații\n  files: Fișiere\n  web: Web\nrun_as_admin: Rulați ca administrator\nunpin: Anulați fixarea\nweb_search: Apăsați Enter pentru a căuta pe web\nwelcome_message: Bun venit la meniul aplicației Seelen UI.\nwelcome_message2: În această filă, puteți fixa aplicațiile preferate.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ru.yml",
    "content": "all: Все приложения\nback: Назад\ndisband: Расформировать папку\nfolder: Папка\nno_matching_items: Не найдено товаров, соответствующих вашему запросу\nopen_file_location: Открыть местоположение файла\npin: Приколоть\npin_to_dock: Закрепить в Dock\nquery:\n  all: Все\n  apps: Приложения\n  files: Файлы\n  web: Интернет\nrun_as_admin: Запуск от имени администратора\nunpin: Открепить\nweb_search: Нажмите Enter, чтобы выполнить поиск в Интернете.\nwelcome_message: Добро пожаловать в меню приложения Seelen UI.\nwelcome_message2: На этой вкладке вы можете закрепить свои любимые приложения.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/si.yml",
    "content": "all: සියලුම යෙදුම්\nback: ආපසු\ndisband: ෆෝල්ඩරය විසුරුවා හරින්න\nfolder: ෆෝල්ඩරය\nno_matching_items: ඔබගේ සෙවුමට ගැලපෙන අයිතම කිසිවක් හමු නොවීය\nopen_file_location: ගොනු ස්ථානය විවෘත කරන්න\npin: පින් කරන්න\npin_to_dock: ඩොක් එකට පින් කරන්න\nquery:\n  all: සියල්ල\n  apps: යෙදුම්\n  files: ගොනු\n  web: වෙබ්\nrun_as_admin: පරිපාලක ලෙස ධාවනය කරන්න\nunpin: ඇමුණුම ඉවත් කරන්න\nweb_search: වෙබය සෙවීමට Enter ඔබන්න\nwelcome_message: Seelen UI යෙදුම් මෙනුව වෙත සාදරයෙන් පිළිගනිමු.\nwelcome_message2: මෙම ටැබය මත, ඔබට ඔබේ ප්‍රියතම යෙදුම් ඇමිණිය හැක.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/sk.yml",
    "content": "all: Všetky aplikácie\nback: Späť\ndisband: Rozpustiť priečinok\nfolder: Priečinok\nno_matching_items: Nenašli sa žiadne položky zodpovedajúce vášmu vyhľadávaniu\nopen_file_location: Otvorte umiestnenie súboru\npin: Pin\npin_to_dock: Pripnúť do doku\nquery:\n  all: Všetky\n  apps: Aplikácie\n  files: Súbory\n  web: Web\nrun_as_admin: Spustite ako správca\nunpin: Odopnúť\nweb_search: Stlačením klávesu Enter spustíte vyhľadávanie na webe\nwelcome_message: Vitajte v ponuke aplikácie Seelen UI.\nwelcome_message2: Na tejto karte môžete pripnúť svoje obľúbené aplikácie.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/so.yml",
    "content": "all: All Apps\nback: Dib u noqo\ndisband: Kala dir galka\nfolder: Galka\nno_matching_items: Lama helin wax ku habboon raadintaada\nopen_file_location: Fur Goobta Faylka\npin: Pin\npin_to_dock: Ku dheji Goobta\nquery:\n  all: Dhammaan\n  apps: Apps\n  files: Faylasha\n  web: Shabakadda\nrun_as_admin: U orda sidii Maamule\nunpin: Furi\nweb_search: Taabo Gelida si aad u baadho shabakada\nwelcome_message: Ku soo dhawoow liiska abka Seelen UI.\nwelcome_message2: Taabkan, waxaad ku dhejin kartaa abka aad jeceshahay.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/sr.yml",
    "content": "all: Све апликације\nback: Назад\ndisband: Дисбанд Фолдер\nfolder: Фолдер\nno_matching_items: Нису пронађене ставке које одговарају вашој претрази\nopen_file_location: Отворите локацију датотеке\npin: Пин\npin_to_dock: Закачите на Доцк\nquery:\n  all: Све\n  apps: Аппс\n  files: Фајлови\n  web: Веб\nrun_as_admin: Покрени као администратор\nunpin: Отквачи\nweb_search: Притисните Ентер да претражите веб\nwelcome_message: Добродошли у мени апликације Сеелен УИ.\nwelcome_message2: На овој картици можете да закачите своје омиљене апликације.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/sv.yml",
    "content": "all: Alla appar\nback: Tillbaka\ndisband: Upplös mapp\nfolder: Mapp\nno_matching_items: Inga objekt hittades som matchar din sökning\nopen_file_location: Öppna filplats\npin: Stift\npin_to_dock: Fäst till Dock\nquery:\n  all: Alla\n  apps: Appar\n  files: Filer\n  web: Web\nrun_as_admin: Kör som administratör\nunpin: Lossa\nweb_search: Tryck på Retur för att söka på webben\nwelcome_message: Välkommen till Seelen UI-appmenyn.\nwelcome_message2: På den här fliken kan du fästa dina favoritappar.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/sw.yml",
    "content": "all: Programu Zote\nback: Nyuma\ndisband: Ondoa Folda\nfolder: Folda\nno_matching_items: Hakuna vipengee vilivyopatikana vinavyolingana na utafutaji wako\nopen_file_location: Fungua Mahali pa Faili\npin: Bandika\npin_to_dock: Bandika kwenye Gati\nquery:\n  all: Wote\n  apps: Programu\n  files: Faili\n  web: Mtandao\nrun_as_admin: Endesha kama Msimamizi\nunpin: Bandua\nweb_search: Bonyeza Enter kutafuta wavuti\nwelcome_message: Karibu kwenye menyu ya programu ya Seelen UI.\nwelcome_message2: Kwenye kichupo hiki, unaweza kubandika programu unazopenda.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ta.yml",
    "content": "all: அனைத்து பயன்பாடுகள்\nback: மீண்டும்\ndisband: கோப்புறையை கலைக்கவும்\nfolder: கோப்புறை\nno_matching_items: உங்கள் தேடலுக்குப் பொருந்தும் உருப்படிகள் எதுவும் இல்லை\nopen_file_location: கோப்பு இருப்பிடத்தைத் திறக்கவும்\npin: பின்\npin_to_dock: கப்பல்துறைக்கு பின்\nquery:\n  all: அனைத்து\n  apps: பயன்பாடுகள்\n  files: கோப்புகள்\n  web: வலை\nrun_as_admin: நிர்வாகியாக இயக்கவும்\nunpin: அன்பின்\nweb_search: இணையத்தில் தேட Enter ஐ அழுத்தவும்\nwelcome_message: Seelen UI ஆப்ஸ் மெனுவிற்கு வரவேற்கிறோம்.\nwelcome_message2: இந்தத் தாவலில், உங்களுக்குப் பிடித்த ஆப்ஸைப் பின் செய்யலாம்.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/te.yml",
    "content": "all: అన్ని యాప్‌లు\nback: వెనుకకు\ndisband: ఫోల్డర్‌ను రద్దు చేయండి\nfolder: ఫోల్డర్\nno_matching_items: మీ శోధనకు సరిపోలే అంశాలు ఏవీ కనుగొనబడలేదు\nopen_file_location: ఫైల్ స్థానాన్ని తెరవండి\npin: పిన్\npin_to_dock: డాక్‌కు పిన్ చేయండి\nquery:\n  all: అన్నీ\n  apps: యాప్‌లు\n  files: ఫైళ్లు\n  web: వెబ్\nrun_as_admin: అడ్మినిస్ట్రేటర్‌గా అమలు చేయండి\nunpin: అన్‌పిన్ చేయండి\nweb_search: వెబ్‌లో శోధించడానికి ఎంటర్ నొక్కండి\nwelcome_message: సీలెన్ UI యాప్ మెనుకి స్వాగతం.\nwelcome_message2: ఈ ట్యాబ్‌లో, మీరు మీకు ఇష్టమైన యాప్‌లను పిన్ చేయవచ్చు.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/tg.yml",
    "content": "all: Ҳама Барномаҳо\nback: Бозгашт\ndisband: Папкаро пароканда кунед\nfolder: Ҷузвдон\nno_matching_items: Ягон адад ба ҷустуҷӯи шумо ёфт нашуд\nopen_file_location: Ҷойгиршавии файлро кушоед\npin: Пин\npin_to_dock: Ба Док пайваст кунед\nquery:\n  all: Ҳама\n  apps: Барномаҳо\n  files: Файлҳо\n  web: Веб\nrun_as_admin: Ҳамчун администратор иҷро кунед\nunpin: Бекор кардан\nweb_search: Барои ҷустуҷӯи веб Enter-ро пахш кунед\nwelcome_message: Хуш омадед ба менюи барномаи Seelen UI.\nwelcome_message2: Дар ин ҷадвал шумо метавонед барномаҳои дӯстдоштаи худро пиндор кунед.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/th.yml",
    "content": "all: แอพทั้งหมด\nback: กลับ\ndisband: ยุบโฟลเดอร์\nfolder: โฟลเดอร์\nno_matching_items: ไม่พบรายการที่ตรงกับการค้นหาของคุณ\nopen_file_location: เปิดตำแหน่งไฟล์\npin: เข็มหมุด\npin_to_dock: ปักหมุดไว้ที่ Dock\nquery:\n  all: ทั้งหมด\n  apps: แอพ\n  files: ไฟล์\n  web: เว็บ\nrun_as_admin: เรียกใช้ในฐานะผู้ดูแลระบบ\nunpin: เลิกปักหมุด\nweb_search: กด Enter เพื่อค้นหาเว็บ\nwelcome_message: ยินดีต้อนรับสู่เมนูแอป Seelen UI\nwelcome_message2: บนแท็บนี้ คุณสามารถปักหมุดแอปโปรดของคุณได้\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/tl.yml",
    "content": "all: Lahat ng Apps\nback: Bumalik\ndisband: I-disband ang Folder\nfolder: Folder\nno_matching_items: Walang nakitang mga item na tumutugma sa iyong paghahanap\nopen_file_location: Buksan ang Lokasyon ng File\npin: Pin\npin_to_dock: I-pin sa Dock\nquery:\n  all: Lahat\n  apps: Mga app\n  files: Mga file\n  web: Web\nrun_as_admin: Patakbuhin bilang Administrator\nunpin: I-unpin\nweb_search: Pindutin ang Enter upang maghanap sa web\nwelcome_message: Maligayang pagdating sa menu ng Seelen UI app.\nwelcome_message2: Sa tab na ito, maaari mong i-pin ang iyong mga paboritong app.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/tr.yml",
    "content": "all: Tüm Uygulamalar\nback: Geri\ndisband: Klasörü Dağıt\nfolder: Klasör\nno_matching_items: Aramanızla eşleşen öğe bulunamadı\nopen_file_location: Dosya Konumunu Aç\npin: Sabitle\npin_to_dock: Dock'a sabitle\nquery:\n  all: Tüm\n  apps: Uygulamalar\n  files: Dosyalar\n  web: ağ\nrun_as_admin: Yönetici olarak çalıştır\nunpin: Sabitlemeyi kaldır\nweb_search: Web'de arama yapmak için Enter'a basın\nwelcome_message: Seelen UI uygulama menüsüne hoş geldiniz.\nwelcome_message2: Bu sekmede favori uygulamalarınızı sabitleyebilirsiniz.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/uk.yml",
    "content": "all: Усі додатки\nback: Назад\ndisband: Розформувати папку\nfolder: Папка\nno_matching_items: Не знайдено елементів, що відповідають вашому пошуку\nopen_file_location: Відкрийте розташування файлу\npin: Pin\npin_to_dock: Закріпити на Dock\nquery:\n  all: все\n  apps: програми\n  files: Файли\n  web: Інтернет\nrun_as_admin: Запуск від імені адміністратора\nunpin: Відкріпити\nweb_search: Натисніть Enter для пошуку в Інтернеті\nwelcome_message: Ласкаво просимо в меню програми Seelen UI.\nwelcome_message2: На цій вкладці ви можете закріпити улюблені програми.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/ur.yml",
    "content": "all: تمام ایپس\nback: واپس\ndisband: فولڈر کو ختم کریں\nfolder: فولڈر\nno_matching_items: آپ کی تلاش سے ملنے والی کوئی چیز نہیں ملی\nopen_file_location: فائل کا مقام کھولیں\npin: پن\npin_to_dock: ڈاک پر پن کریں۔\nquery:\n  all: سب\n  apps: ایپس\n  files: فائلیں\n  web: ویب\nrun_as_admin: ایڈمنسٹریٹر کی حیثیت سے چلائیں\nunpin: انپن\nweb_search: ویب کو تلاش کرنے کے لئے ENTER دبائیں\nwelcome_message: سیلین UI ایپ مینو میں خوش آمدید۔\nwelcome_message2: اس ٹیب پر ، آپ اپنی پسندیدہ ایپس کو پن کرسکتے ہیں۔\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/uz.yml",
    "content": "all: Barcha ilovalar\nback: Orqaga\ndisband: Jildni tarqatish\nfolder: Papka\nno_matching_items: Qidiruvingizga mos narsa topilmadi\nopen_file_location: Fayl joylashuvini oching\npin: Pin\npin_to_dock: Dockga mahkamlang\nquery:\n  all: Hammasi\n  apps: Ilovalar\n  files: Fayllar\n  web: Veb\nrun_as_admin: Administrator sifatida ishga tushirish\nunpin: Yechish\nweb_search: Internetda qidirish uchun Enter tugmasini bosing\nwelcome_message: Seelen UI ilovasi menyusiga xush kelibsiz.\nwelcome_message2: Ushbu yorliqda siz sevimli ilovalaringizni mahkamlashingiz mumkin.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/vi.yml",
    "content": "all: Tất cả ứng dụng\nback: Mặt sau\ndisband: Giải tán thư mục\nfolder: Thư mục\nno_matching_items: Không tìm thấy mục nào phù hợp với tìm kiếm của bạn\nopen_file_location: Mở vị trí tệp\npin: Ghim\npin_to_dock: Ghim vào Dock\nquery:\n  all: Tất cả\n  apps: Ứng dụng\n  files: Tập tin\n  web: Web\nrun_as_admin: Chạy với tư cách quản trị viên\nunpin: Bỏ ghim\nweb_search: Nhấn Enter để tìm kiếm trên web\nwelcome_message: Chào mừng bạn đến với menu ứng dụng Seelen UI.\nwelcome_message2: Trên tab này, bạn có thể ghim các ứng dụng yêu thích của mình.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/yo.yml",
    "content": "all: Gbogbo Apps\nback: Pada\ndisband: Tu Folda\nfolder: Foda\nno_matching_items: Ko si ohun kan ti o baamu wiwa rẹ\nopen_file_location: Ṣii Ibi Faili\npin: Pin\npin_to_dock: Pin si Dock\nquery:\n  all: Gbogbo\n  apps: Awọn ohun elo\n  files: Awọn faili\n  web: Ayelujara\nrun_as_admin: Ṣiṣe bi Alakoso\nunpin: Yọ kuro\nweb_search: Tẹ Tẹ lati wa wẹẹbu\nwelcome_message: Kaabo si Seelen UI app akojọ.\nwelcome_message2: Lori taabu yii, o le pin awọn ohun elo ayanfẹ rẹ.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/zh-CN.yml",
    "content": "all: 所有应用程序\nback: 后退\ndisband: 解散文件夹\nfolder: 文件夹\nno_matching_items: 未找到与您的搜索相符的商品\nopen_file_location: 打开文件位置\npin: 别针\npin_to_dock: 固定到底座\nquery:\n  all: 全部\n  apps: 应用程序\n  files: 文件\n  web: 网络\nrun_as_admin: 以管理员身份运行\nunpin: 取消固定\nweb_search: 按 Enter 键搜索网络\nwelcome_message: 欢迎使用 Seelen UI 应用程序菜单。\nwelcome_message2: 在此选项卡上，您可以固定您最喜欢的应用程序。\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/zh-TW.yml",
    "content": "all: 所有應用程序\nback: 後退\ndisband: 解散文件夾\nfolder: 資料夾\nno_matching_items: 未找到與您的搜索相符的商品\nopen_file_location: 打開文件位置\npin: 別針\npin_to_dock: 固定到底座\nquery:\n  all: 全部\n  apps: 應用程式\n  files: 文件\n  web: 網絡\nrun_as_admin: 以管理員身份運行\nunpin: 取消固定\nweb_search: 按 Enter 鍵搜索網絡\nwelcome_message: 歡迎使用 Seelen UI 應用程序菜單。\nwelcome_message2: 在此選項卡上，您可以固定您最喜歡的應用程序。\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/i18n/translations/zu.yml",
    "content": "all: Zonke Izinhlelo zokusebenza\nback: Emuva\ndisband: Khipha Ifolda\nfolder: Ifolda\nno_matching_items: Azikho izinto ezitholakele ezifana nosesho lwakho\nopen_file_location: Vula Indawo Yefayela\npin: Phina\npin_to_dock: Phina kuDokhi\nquery:\n  all: Konke\n  apps: Izinhlelo zokusebenza\n  files: Amafayela\n  web: Iwebhu\nrun_as_admin: Sebenzisa njengomlawuli\nunpin: Susa ukuphina\nweb_search: Cindezela u-Enter ukuze useshe iwebhu\nwelcome_message: Siyakwamukela kumenyu yohlelo lokusebenza lwe-Seelen UI.\nwelcome_message2: Kule thebhu, ungaphina izinhlelo zakho zokusebenza eziyintandokazi.\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/index.ts",
    "content": "import { getRootContainer } from \"libs/ui/react/utils/index.ts\";\nimport { mount } from \"svelte\";\nimport App from \"./App.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\nimport { onTriggered } from \"./state/positioning.svelte.ts\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\nimport { debounce } from \"lodash\";\nimport { globalState } from \"./state/mod.svelte.ts\";\n\nawait loadTranslations();\n\nconst widget = Widget.getCurrent();\nconst { window } = widget;\n\nawait widget.init();\n\nawait Promise.all([\n  window.setDecorations(false),\n  window.setMinimizable(false),\n  window.setMaximizable(false),\n  window.setClosable(false),\n  window.setSkipTaskbar(true),\n  window.setAlwaysOnTop(true),\n  window.setResizable(false),\n]);\n\nconst hideDelayed = debounce(() => {\n  globalState.showing = false;\n}, 100);\n\nwidget.window.onFocusChanged((e) => {\n  if (e.payload) {\n    hideDelayed.cancel();\n  } else {\n    hideDelayed();\n  }\n});\n\nwidget.onTrigger(async (args) => {\n  const visible = await widget.window.isVisible();\n  if (visible) {\n    globalState.showing = false;\n  } else {\n    onTriggered(args.monitorId);\n  }\n});\n\nmount(App, {\n  target: getRootContainer(),\n});\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/keyboard-navigation.ts",
    "content": "import { globalState } from \"./state/mod.svelte\";\n\nexport function navigateInDirection(direction: \"up\" | \"down\" | \"left\" | \"right\"): void {\n  const allItems = Array.from(document.querySelectorAll(\".app\")) as HTMLElement[];\n  if (allItems.length === 0) return;\n\n  let currentElement: HTMLElement | null = null;\n\n  if (globalState.preselectedItem) {\n    currentElement = allItems.find((item) => item.dataset.itemId === globalState.preselectedItem) || null;\n  } else {\n    currentElement = allItems[0] || null;\n  }\n\n  if (!currentElement) return;\n\n  const currentRect = currentElement.getBoundingClientRect();\n  const candidates = allItems\n    // filter items that are not in the same row/column\n    .filter((item) => {\n      if (item === currentElement) return false;\n      const rect = item.getBoundingClientRect();\n      switch (direction) {\n        case \"right\":\n          return rect.top === currentRect.top && rect.left > currentRect.left;\n        case \"left\":\n          return rect.top === currentRect.top && rect.left < currentRect.left;\n        case \"down\":\n          return rect.left === currentRect.left && rect.top > currentRect.top;\n        case \"up\":\n          return rect.left === currentRect.left && rect.top < currentRect.top;\n      }\n    });\n\n  let idxToTake = [\"right\", \"down\"].includes(direction) ? 0 : -1;\n  let toTake = candidates.at(idxToTake);\n  if (toTake) {\n    globalState.preselectedItem = toTake.dataset.itemId || null;\n  }\n}\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/state/config.svelte.ts",
    "content": "import { Settings, Widget } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\nimport z from \"zod\";\n\nlet settings = lazyRune(() => Settings.getAsync());\nSettings.onChange((s) => (settings.value = s));\nawait settings.init();\n\nconst WidgetConfigSchema = z.object({\n  acrylic: z.boolean(),\n});\n\nconst widgetConfig = $derived.by(\n  () =>\n    WidgetConfigSchema.safeParse(settings.value.getCurrentWidgetConfig()).data ??\n      (Widget.self.getDefaultConfig() as unknown as z.infer<typeof WidgetConfigSchema>),\n);\n\nexport const ConfigState = {\n  get config() {\n    return widgetConfig;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/state/knownFolders.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { FolderType, type StartMenuItem } from \"@seelen-ui/lib/types\";\n\nconst [desktopInit, downloadsInit, documentsInit, musicInit, picturesInit, videosInit] = await Promise.all([\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Desktop }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Downloads }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Documents }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Music }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Pictures }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Videos }),\n]);\n\nlet desktop = $state(desktopInit);\nlet downloads = $state(downloadsInit);\nlet documents = $state(documentsInit);\nlet music = $state(musicInit);\nlet pictures = $state(picturesInit);\nlet videos = $state(videosInit);\n\nsubscribe(SeelenEvent.UserFolderChanged, ({ payload: { ofFolder, content } }) => {\n  switch (ofFolder) {\n    case FolderType.Desktop:\n      desktop = content;\n      break;\n    case FolderType.Downloads:\n      downloads = content;\n      break;\n    case FolderType.Documents:\n      documents = content;\n      break;\n    case FolderType.Music:\n      music = content;\n      break;\n    case FolderType.Pictures:\n      pictures = content;\n      break;\n    case FolderType.Videos:\n      videos = content;\n      break;\n  }\n});\n\nfunction pathAsItem(path: string): StartMenuItem {\n  return {\n    path,\n    umid: null,\n    display_name: path.split(/[\\\\/]/g).pop() || \"\",\n    target: null,\n    toast_activator: null,\n  };\n}\n\nfunction predicate(path: string): boolean {\n  let lowercased = path.toLowerCase();\n  return !lowercased.endsWith(\".ini\") && !lowercased.endsWith(\".tmp\");\n}\n\nconst _foldersAsStartMenuItems = $derived.by(() => {\n  return [\n    desktop.filter(predicate).map(pathAsItem),\n    downloads.filter(predicate).map(pathAsItem),\n    documents.filter(predicate).map(pathAsItem),\n    music.filter(predicate).map(pathAsItem),\n    pictures.filter(predicate).map(pathAsItem),\n    videos.filter(predicate).map(pathAsItem),\n  ].flat();\n});\n\nexport const foldersAsStartMenuItems = {\n  get value() {\n    return _foldersAsStartMenuItems;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/state/mod.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport type { StartMenuItem, StartMenuLayoutItem } from \"@seelen-ui/lib/types\";\nimport { lazyRune, persistentRune } from \"libs/ui/svelte/utils\";\nimport { StartDisplayMode, StartView } from \"../constants\";\n\nconst user = lazyRune(() => invoke(SeelenCommand.GetUser));\nsubscribe(SeelenEvent.UserChanged, user.setByPayload);\n\nconst monitors = lazyRune(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\n\nconst startMenuItems = lazyRune(() => invoke(SeelenCommand.GetStartMenuItems));\nsubscribe(SeelenEvent.StartMenuItemsChanged, startMenuItems.setByPayload);\n\nawait Promise.all([user.init(), monitors.init(), startMenuItems.init()]);\n\n// Folder and pinned items types\nexport interface FavFolderItem {\n  type: \"folder\";\n  itemId: string;\n  name: string;\n  itemIds: string[];\n}\n\nexport interface FavAppItem {\n  type: \"app\";\n  itemId: string;\n}\n\nexport type FavPinnedItem = FavAppItem | FavFolderItem;\n\nconst initialState: FavPinnedItem[] = [];\n\ntry {\n  const layout = await invoke(SeelenCommand.GetNativeStartMenu);\n  for (const _item of layout.pinnedList) {\n    const item = _item as StartMenuLayoutItem as any;\n    if (item.desktopAppLink) {\n      let path = item.desktopAppLink.toLowerCase();\n      const found = startMenuItems.value.find((i) => i.path.toLowerCase() === path);\n\n      if (found) {\n        initialState.push({\n          type: \"app\",\n          itemId: getItemId(found),\n        });\n        continue;\n      }\n    }\n\n    initialState.push({\n      type: \"app\",\n      itemId: item.packagedAppId || item.destopAppId || item.desktopAppLink,\n    });\n  }\n} catch (error) {\n  console.error(\"Failed to get native pinned items:\", error);\n}\n\nconst pinnedItems = await persistentRune<FavPinnedItem[]>(\"favorites\", initialState);\n\n// Get unique identifier for an item\nfunction getItemId(item: StartMenuItem): string {\n  return item.umid || item.path.toLowerCase();\n}\n\n// Check if an item is pinned\nfunction isPinned(item: StartMenuItem): boolean {\n  const itemId = getItemId(item);\n  return pinnedItems.value.some((pinned) => {\n    if (pinned.type === \"app\") {\n      return pinned.itemId === itemId;\n    } else {\n      return pinned.itemIds.includes(itemId);\n    }\n  });\n}\n\n// Toggle pin status of an item\nfunction togglePin(item: StartMenuItem): void {\n  const itemId = getItemId(item);\n  const currentPinned = pinnedItems.value;\n\n  if (isPinned(item)) {\n    // Unpin: remove from apps or folders\n    pinnedItems.value = currentPinned\n      .map((pinned) => {\n        if (pinned.type === \"app\") {\n          return pinned.itemId === itemId ? null : pinned;\n        } else {\n          const newItemIds = pinned.itemIds.filter((id) => id !== itemId);\n          // Remove folder if it has less than 2 items\n          if (newItemIds.length < 2) {\n            return null;\n          }\n          return { ...pinned, itemIds: newItemIds };\n        }\n      })\n      .filter((item): item is FavPinnedItem => item !== null);\n  } else {\n    // Pin: add as app item\n    pinnedItems.value = [...currentPinned, { type: \"app\", itemId: itemId }];\n  }\n}\n\n// Update entire pinned items array\nfunction updatePinnedItems(items: FavPinnedItem[]): void {\n  pinnedItems.value = items;\n}\n\n// Create a new folder from two app items at a specific position\nfunction createFolder(itemId1: string, itemId2: string, targetIdx?: number): FavFolderItem {\n  const newFolder: FavFolderItem = {\n    type: \"folder\",\n    itemId: crypto.randomUUID(),\n    name: \"\",\n    itemIds: [itemId1, itemId2],\n  };\n\n  // Remove both items if they exist as standalone apps\n  const filtered = pinnedItems.value.filter(\n    (item) => !(item.type === \"app\" && (item.itemId === itemId1 || item.itemId === itemId2)),\n  );\n\n  // Insert folder at target position or at end\n  if (targetIdx !== undefined && targetIdx >= 0 && targetIdx <= filtered.length) {\n    const before = filtered.slice(0, targetIdx);\n    const after = filtered.slice(targetIdx);\n    pinnedItems.value = [...before, newFolder, ...after];\n  } else {\n    pinnedItems.value = [...filtered, newFolder];\n  }\n\n  return newFolder;\n}\n\n// Add an item to an existing folder\nfunction addItemToFolder(folderId: string, itemId: string): void {\n  pinnedItems.value = pinnedItems.value.map((pinned) => {\n    if (pinned.type === \"folder\" && pinned.itemId === folderId) {\n      // Check if item already exists in folder\n      if (!pinned.itemIds.includes(itemId)) {\n        return { ...pinned, itemIds: [...pinned.itemIds, itemId] };\n      }\n    }\n    return pinned;\n  });\n\n  // Remove the item if it exists as standalone app\n  pinnedItems.value = pinnedItems.value.filter(\n    (item) => !(item.type === \"app\" && item.itemId === itemId),\n  );\n}\n\n// Update folder properties\nfunction updateFolder(\n  folderId: string,\n  updates: Partial<Omit<FavFolderItem, \"type\" | \"id\">>,\n): void {\n  pinnedItems.value = pinnedItems.value.map((pinned) => {\n    if (pinned.type === \"folder\" && pinned.itemId === folderId) {\n      return { ...pinned, ...updates };\n    }\n    return pinned;\n  });\n}\n\n// Merge source folder into target folder\nfunction mergeFolders(sourceFolderId: string, targetFolderId: string): void {\n  const sourceFolder = pinnedItems.value.find(\n    (item) => item.type === \"folder\" && item.itemId === sourceFolderId,\n  ) as FavFolderItem | undefined;\n\n  if (!sourceFolder) {\n    return;\n  }\n\n  // Add all items from source folder to target folder\n  pinnedItems.value = pinnedItems.value.map((pinned) => {\n    if (pinned.type === \"folder\" && pinned.itemId === targetFolderId) {\n      // Merge items, avoiding duplicates\n      const mergedItemIds = [...new Set([...pinned.itemIds, ...sourceFolder.itemIds])];\n      return { ...pinned, itemIds: mergedItemIds };\n    }\n    return pinned;\n  });\n\n  // Remove the source folder\n  pinnedItems.value = pinnedItems.value.filter(\n    (item) => !(item.type === \"folder\" && item.itemId === sourceFolderId),\n  );\n}\n\n// Disband a folder, converting all items to individual apps\nfunction disbandFolder(folderId: string): void {\n  const folder = pinnedItems.value.find(\n    (item) => item.type === \"folder\" && item.itemId === folderId,\n  ) as FavFolderItem | undefined;\n\n  if (!folder) {\n    return;\n  }\n\n  const folderIndex = pinnedItems.value.findIndex((item) => item.itemId === folderId);\n  const withoutFolder = pinnedItems.value.filter((item) => item.itemId !== folderId);\n  const newApps: FavAppItem[] = folder.itemIds.map((itemId) => ({\n    type: \"app\",\n    itemId,\n  }));\n\n  pinnedItems.value = [\n    ...withoutFolder.slice(0, folderIndex),\n    ...newApps,\n    ...withoutFolder.slice(folderIndex),\n  ];\n}\n\n// Verify pinned items still exist\nfunction verifyPinnedItems() {\n  const items = startMenuItems.value;\n  const validIds = new Set(items.map(getItemId));\n\n  const validPinned = pinnedItems.value\n    .map((pinned) => {\n      if (pinned.type === \"app\") {\n        return validIds.has(pinned.itemId) ? pinned : null;\n      } else {\n        const validItemIds = pinned.itemIds.filter((id) => validIds.has(id));\n        // Remove folder if it has less than 2 items\n        if (validItemIds.length < 2) {\n          return null;\n        }\n        return { ...pinned, itemIds: validItemIds };\n      }\n    })\n    .filter((item): item is FavPinnedItem => item !== null);\n\n  if (validPinned.length !== pinnedItems.value.length) {\n    pinnedItems.value = validPinned;\n  }\n}\n\n$effect.root(() => {\n  $effect(() => {\n    startMenuItems.value;\n    verifyPinnedItems();\n  });\n});\n\nconst displayMode = await persistentRune(\"display_mode\", StartDisplayMode.Normal);\nclass State {\n  isPinned = isPinned;\n  togglePin = togglePin;\n  getItemId = getItemId;\n  updatePinnedItems = updatePinnedItems;\n  createFolder = createFolder;\n  addItemToFolder = addItemToFolder;\n  updateFolder = updateFolder;\n  mergeFolders = mergeFolders;\n  disbandFolder = disbandFolder;\n\n  desiredMonitorId = $state<string | null>(null);\n  showing = $state(false);\n\n  view = $state(StartView.Favorites);\n  searchQuery = $state(\"\");\n  preselectedItem = $state<string | null>(null);\n\n  get user() {\n    return user.value;\n  }\n\n  get monitors() {\n    return monitors.value;\n  }\n\n  get pinnedItems() {\n    return pinnedItems.value;\n  }\n\n  set pinnedItems(value: FavPinnedItem[]) {\n    pinnedItems.value = value;\n  }\n\n  get allItems() {\n    return startMenuItems.value;\n  }\n\n  get displayMode() {\n    return displayMode.value;\n  }\n\n  set displayMode(value: StartDisplayMode) {\n    displayMode.value = value;\n  }\n\n  // Get StartMenuItem by ID\n  getMenuItem(id: string): StartMenuItem | undefined {\n    return this.allItems.find((item) => getItemId(item) === id);\n  }\n}\n\nexport const globalState = new State();\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/state/positioning.svelte.ts",
    "content": "import { Widget } from \"@seelen-ui/lib\";\nimport type { PhysicalMonitor } from \"@seelen-ui/lib/types\";\nimport { globalState } from \"./mod.svelte\";\nimport { StartDisplayMode, StartView } from \"../constants\";\n\nlet monitorToShow = $derived.by(() => {\n  let targetMonitor: PhysicalMonitor | undefined;\n\n  if (globalState.desiredMonitorId) {\n    targetMonitor = globalState.monitors.find((m) => m.id === globalState.desiredMonitorId);\n  }\n\n  // Fallback to primary monitor if not found or not specified\n  if (!targetMonitor) {\n    targetMonitor = globalState.monitors.find((m) => m.isPrimary) || globalState.monitors[0];\n  }\n\n  return targetMonitor;\n});\n\nasync function placeCenteredToMonitor(targetMonitor: PhysicalMonitor): Promise<void> {\n  const widget = Widget.getCurrent();\n  const monitorWidth = targetMonitor.rect.right - targetMonitor.rect.left;\n  const monitorHeight = targetMonitor.rect.bottom - targetMonitor.rect.top;\n\n  // globalState.displayMode === StartDisplayMode.Fullscreen\n  let x = targetMonitor.rect.left;\n  let y = targetMonitor.rect.top;\n  let width = monitorWidth;\n  let height = monitorHeight;\n\n  if (globalState.displayMode === StartDisplayMode.Normal) {\n    width = Math.round(Math.min(monitorWidth * 0.6, 1200 * targetMonitor.scaleFactor));\n    height = Math.round(Math.min(monitorHeight * 0.6, 1200 * targetMonitor.scaleFactor));\n\n    const monitorCenterX = targetMonitor.rect.left + monitorWidth / 2;\n    const monitorCenterY = targetMonitor.rect.top + monitorHeight / 2;\n\n    x = Math.round(monitorCenterX - width / 2);\n    y = Math.round(monitorCenterY - height / 2);\n  }\n\n  await widget.window.setShadow(globalState.displayMode === StartDisplayMode.Normal);\n  await widget.setPosition({\n    left: x,\n    top: y,\n    right: x + width,\n    bottom: y + height,\n  });\n\n  if (globalState.showing) {\n    await widget.show();\n    await widget.focus();\n  }\n}\n\n$effect.root(() => {\n  $effect(() => {\n    globalState.displayMode;\n    if (monitorToShow && globalState.showing) {\n      placeCenteredToMonitor(monitorToShow);\n    }\n  });\n\n  $effect(() => {\n    if (!globalState.showing) {\n      Widget.self.hide();\n    }\n  });\n});\n\nexport function onTriggered(monitorId?: string | null) {\n  globalState.view = StartView.Favorites;\n  globalState.desiredMonitorId = monitorId || null;\n  globalState.showing = true;\n}\n"
  },
  {
    "path": "src/ui/svelte/apps-menu/utils.ts",
    "content": "export function arrayMove<T>(array: T[], from: number, to: number): T[] {\n  const newArray = array.slice();\n  newArray.splice(to < 0 ? newArray.length + to : to, 0, newArray.splice(from, 1)[0]!);\n  return newArray;\n}\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/app.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"./state.svelte\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { RadioDeviceKind } from \"@seelen-ui/lib/types\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"./i18n\";\n  import BluetoothDevice from \"./components/BluetoothDevice.svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n\n  // Filter devices to avoid duplicates (same name, prefer non-LE version)\n  const uniqueDevices = $derived.by(() => {\n    const devices = globalState.devices;\n    const filtered: typeof devices = [];\n    const seen = new Set<string>();\n\n    for (const device of devices) {\n      const existing = devices.find(\n        (d) => d.name === device.name && d.id !== device.id && !d.isLowEnergy\n      );\n      if (existing) {\n        if (!device.isLowEnergy) {\n          // This is the classic version, keep it\n          if (!seen.has(device.name)) {\n            filtered.push(device);\n            seen.add(device.name);\n          }\n        }\n        // Skip LE version if classic exists\n      } else {\n        // No duplicate, add it\n        if (!seen.has(device.name)) {\n          filtered.push(device);\n          seen.add(device.name);\n        }\n      }\n    }\n\n    return filtered;\n  });\n\n  const connectedDevices = $derived(uniqueDevices.filter((d) => d.paired && d.connected));\n  const pairedDevices = $derived(uniqueDevices.filter((d) => d.paired && !d.connected));\n  const availableDevices = $derived(uniqueDevices.filter((d) => !d.paired));\n\n  const bluetoothRadio = $derived(globalState.bluetoothRadio);\n\n  function openBluetoothSettings() {\n    invoke(SeelenCommand.OpenFile, { path: \"ms-settings:bluetooth\" });\n  }\n\n  async function toggleBluetoothRadio() {\n    if (bluetoothRadio) {\n      await invoke(SeelenCommand.SetRadioState, {\n        kind: RadioDeviceKind.Bluetooth,\n        enabled: !bluetoothRadio.is_enabled,\n      });\n    }\n  }\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<div class=\"slu-standard-popover bluetooth-popup\">\n  {#if !bluetoothRadio}\n    <div class=\"bluetooth-no-adapter\">\n      {$t(\"no_adapter\")}\n    </div>\n  {:else}\n    <div class=\"bluetooth-radio-control\">\n      <div class=\"bluetooth-radio-label\">\n        <Icon iconName=\"IoBluetooth\" />\n        <span>Bluetooth</span>\n      </div>\n      <label class=\"bluetooth-radio-switch\">\n        <input\n          type=\"checkbox\"\n          data-skin=\"switch\"\n          checked={bluetoothRadio.is_enabled}\n          onchange={toggleBluetoothRadio}\n        />\n      </label>\n    </div>\n  {/if}\n\n  {#if bluetoothRadio?.is_enabled}\n    {#if connectedDevices.length > 0}\n      <div class=\"bt-list\">\n        <div class=\"bt-list-title\">{$t(\"connected\")}</div>\n        <div class=\"bt-list-devices\">\n          {#each connectedDevices as device (device.id)}\n            <BluetoothDevice {device} />\n          {/each}\n        </div>\n      </div>\n    {/if}\n\n    {#if pairedDevices.length > 0}\n      <div class=\"bt-list\">\n        <div class=\"bt-list-title\">{$t(\"paired\")}</div>\n        <div class=\"bt-list-devices\">\n          {#each pairedDevices as device (device.id)}\n            <BluetoothDevice {device} />\n          {/each}\n        </div>\n      </div>\n    {/if}\n\n    <div class=\"bt-list\">\n      <div class=\"bt-list-title\">\n        <span>{$t(\"available\")}</span>\n        {#if globalState.isScanning}\n          <div class=\"bluetooth-scanning\">\n            <Icon iconName=\"TbRefresh\" />\n          </div>\n        {/if}\n      </div>\n      <div class=\"bt-list-devices\">\n        {#if availableDevices.length > 0}\n          {#each availableDevices as device (device.id)}\n            <BluetoothDevice {device} />\n          {/each}\n        {:else}\n          <div class=\"bluetooth-empty\">{$t(\"not_found\")}</div>\n        {/if}\n      </div>\n    </div>\n\n    {#if bluetoothRadio?.is_enabled}\n      <div class=\"bluetooth-footer\">\n        <button data-skin=\"transparent\" onclick={openBluetoothSettings}>\n          {$t(\"more\")}\n        </button>\n      </div>\n    {/if}\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/components/BluetoothDevice.svelte",
    "content": "<script lang=\"ts\">\n  import type { BluetoothDevice, DevicePairingNeededAction } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"../i18n\";\n  import { getIconForBTDevice, getMinorAsString } from \"../icons\";\n  import { globalState } from \"../state.svelte\";\n  import { useTransition } from \"libs/ui/svelte/utils/hooks.svelte\";\n\n  interface Props {\n    device: BluetoothDevice;\n  }\n  let { device }: Props = $props();\n\n  const transition = useTransition();\n\n  let selected = $derived(globalState.selectedDeviceId === device.id);\n  let loading = $derived(transition.loading);\n  let error = $derived(transition.error);\n\n  let pairingAction = $state<DevicePairingNeededAction | null>(null);\n\n  let pinInput = $state(\"\");\n  let usernameInput = $state(\"\");\n  let passwordInput = $state(\"\");\n\n  // Watch for selection changes to reset state\n  $effect(() => {\n    if (!selected) {\n      handleCancelPair();\n      transition.clearError();\n    }\n  });\n\n  async function handleDisconnect() {\n    transition.start(async () => {\n      await invoke(SeelenCommand.DisconnectBluetoothDevice, { id: device.id });\n    });\n  }\n\n  async function handleForget() {\n    transition.start(async () => {\n      await invoke(SeelenCommand.ForgetBluetoothDevice, { id: device.id });\n    });\n  }\n\n  async function handleTriggerPair() {\n    if (pairingAction) {\n      transition.start(async () => {\n        await invoke(SeelenCommand.ConfirmBluetoothDevicePairing, {\n          id: device.id,\n          answer: {\n            accept: true,\n            pin: pinInput || null,\n            username: usernameInput || null,\n            password: passwordInput || null,\n            address: null,\n          },\n        });\n        pairingAction = null;\n      });\n    } else {\n      transition.start(async () => {\n        pairingAction = await invoke(SeelenCommand.RequestPairBluetoothDevice, {\n          id: device.id,\n        });\n      });\n    }\n\n    pinInput = \"\";\n    usernameInput = \"\";\n    passwordInput = \"\";\n  }\n\n  async function handleCancelPair() {\n    if (pairingAction) {\n      await invoke(SeelenCommand.ConfirmBluetoothDevicePairing, {\n        id: device.id,\n        answer: {\n          accept: false,\n          pin: null,\n          username: null,\n          password: null,\n          address: null,\n        },\n      }).catch((e) => {\n        console.error(\"Cancel pairing error:\", e);\n      });\n    }\n\n    pairingAction = null;\n    pinInput = \"\";\n    usernameInput = \"\";\n    passwordInput = \"\";\n  }\n\n  function getPairingMessage(): string {\n    if (!pairingAction) return \"\";\n    switch (pairingAction.needs) {\n      case \"ProvidePin\":\n        return $t(\"provide_pin\");\n      case \"ConfirmPinMatch\":\n        return $t(\"confirm_pin\");\n      case \"DisplayPin\":\n        return $t(\"display_pin\");\n      case \"ConfirmOnly\":\n        return $t(\"confirm_only\");\n      case \"ProvidePasswordCredential\":\n        return $t(\"provide_password\");\n      case \"ProvideAddress\":\n        return $t(\"provide_address\");\n      case \"None\":\n        return \"\";\n    }\n  }\n\n  function showPairingFields(): boolean {\n    if (!selected || !pairingAction || loading) {\n      return false;\n    }\n    const action = pairingAction;\n    return (\n      action.needs === \"ProvidePin\" ||\n      action.needs === \"ConfirmPinMatch\" ||\n      action.needs === \"DisplayPin\" ||\n      action.needs === \"ProvidePasswordCredential\"\n    );\n  }\n\n  function getDeviceTooltip(): string {\n    if (device.appearance) {\n      return `${device.appearance.category} - ${device.appearance.subcategory}`;\n    }\n    return `${device.majorClass} - ${getMinorAsString(device.minorClass)}`;\n  }\n</script>\n\n<div\n  class=\"bt-device\"\n  class:bt-device-selected={selected}\n  onclick={() => {\n    if (!selected) {\n      globalState.selectedDeviceId = device.id;\n    }\n  }}\n  role=\"button\"\n  tabindex=\"0\"\n  onkeydown={(e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      globalState.selectedDeviceId = device.id;\n    }\n  }}\n>\n  <div class=\"bt-device-info\">\n    <Icon iconName={device.connected ? \"TbBluetoothConnected\" : \"TbBluetooth\"} />\n    <span class=\"bt-device-name\">{device.name}</span>\n    <Icon iconName={getIconForBTDevice(device) as any} title={getDeviceTooltip()} />\n    {#if device.isLowEnergy}\n      <Icon iconName=\"MdOutlineEnergySavingsLeaf\" title={$t(\"lowenergy\")} />\n    {/if}\n  </div>\n\n  {#if selected}\n    {#if loading && !device.paired}\n      <div class=\"bt-device-loading\">{$t(\"pairing\")}</div>\n    {/if}\n\n    {#if showPairingFields()}\n      <div class=\"bt-device-pairing\">\n        <div class=\"bt-device-pairing-message\">\n          {getPairingMessage()}\n        </div>\n\n        {#if pairingAction?.needs === \"ProvidePin\"}\n          <input\n            type=\"text\"\n            data-skin=\"default\"\n            class:error\n            bind:value={pinInput}\n            placeholder=\"PIN\"\n          />\n        {/if}\n\n        {#if pairingAction?.needs === \"ConfirmPinMatch\" || pairingAction?.needs === \"DisplayPin\"}\n          <div class=\"bt-device-pin-display\">\n            {pairingAction.pin || \"\"}\n          </div>\n        {/if}\n\n        {#if pairingAction?.needs === \"ProvidePasswordCredential\"}\n          <input\n            type=\"text\"\n            data-skin=\"default\"\n            class:error\n            bind:value={usernameInput}\n            placeholder={$t(\"username\")}\n          />\n          <input\n            type=\"password\"\n            data-skin=\"default\"\n            class:error\n            bind:value={passwordInput}\n            placeholder={$t(\"password\")}\n          />\n        {/if}\n      </div>\n    {/if}\n\n    {#if !loading}\n      <div class=\"bt-device-actions\">\n        {#if device.paired}\n          {#if device.canDisconnect}\n            <button data-skin=\"default\" onclick={handleDisconnect} disabled={loading}>\n              {$t(\"disconnect\")}\n            </button>\n          {/if}\n          <button data-skin=\"default\" onclick={handleForget} disabled={loading}>\n            {$t(\"unpair\")}\n          </button>\n        {:else}\n          {#if pairingAction}\n            <button data-skin=\"default\" onclick={handleCancelPair} disabled={loading}>\n              {$t(\"cancel\")}\n            </button>\n          {/if}\n\n          <button data-skin=\"solid\" onclick={handleTriggerPair} disabled={loading}>\n            {pairingAction ? $t(\"confirm\") : $t(\"pair\")}\n          </button>\n        {/if}\n      </div>\n    {/if}\n\n    {#if error}\n      <div class=\"bt-device-error\">\n        {$t(\"pairing_failed\")}\n      </div>\n    {/if}\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/af.yml",
    "content": "available: Beskikbare toestelle\ncancel: Kanselleer\nconfirm: Bevestig\nconfirm_only: Bevestig saambinding\nconfirm_pin: Bevestig dat die PIN ooreenstem\nconnected: Gekoppel\ndisconnect: Ontkoppel\ndisplay_pin: Voer hierdie PIN op die toestel in\nlowenergy: Lae energie\nmore: Meer Bluetooth-instellings\nno_adapter: Geen Bluetooth-adapter gevind nie\nnot_found: Geen toestelle gevind nie\npair: Paar\npaired: Gekoppel\npairing: Bind tans saam …\npairing_failed: Kon nie saambind nie\npassword: Wagwoord\nprovide_address: Voer adres in\nprovide_password: Voer gebruikersnaam en wagwoord in\nprovide_pin: Voer PIN in\nscanning: Skandeer tans vir toestelle …\nunpair: Ontkoppel\nunpair_failed: Ontbinding het misluk\nusername: Gebruikersnaam\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/am.yml",
    "content": "available: የሚገኙ መሣሪያዎች\ncancel: ይቅር\nconfirm: አረጋግጥ\nconfirm_only: ማሰራጫ ያረጋግጡ\nconfirm_pin: ፒን ይዛመዳል ያረጋግጡ\nconnected: ተገናኝቷል\ndisconnect: ያላቅቁ\ndisplay_pin: በመሣሪያው ላይ ይህንን ፒን ያስገቡ\nlowenergy: ዝቅተኛ ኃይል\nmore: ተጨማሪ የብሉቱዝ ቅንብሮች\nno_adapter: ምንም ብሉቱዝ አስማሚ አልተገኘም\nnot_found: ምንም መሣሪያዎች አልተገኙም\npair: ጥንድ ጥንድ\npaired: የተጣመሩ\npairing: ማጣመር ...\npairing_failed: ማሰራጨት አልተሳካም\npassword: የይለፍ ቃል\nprovide_address: አድራሻ ያስገቡ\nprovide_password: የተጠቃሚ ስም እና የይለፍ ቃል ያስገቡ\nprovide_pin: ፒን ያስገቡ\nscanning: ለመሳሪያዎች መቃኘት ...\nunpair: ያልተነከረ\nunpair_failed: ያልተሳካ አልተሳካም\nusername: የተጠቃሚ ስም\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ar.yml",
    "content": "available: الأجهزة المتوفرة\ncancel: يلغي\nconfirm: يتأكد\nconfirm_only: تأكيد الاقتران\nconfirm_pin: تأكد من تطابق رقم التعريف الشخصي\nconnected: متصل\ndisconnect: قطع الاتصال\ndisplay_pin: أدخل رقم التعريف الشخصي هذا على الجهاز\nlowenergy: طاقة منخفضة\nmore: المزيد من إعدادات البلوتوث\nno_adapter: لم يتم العثور على محول Bluetooth\nnot_found: لم يتم العثور على أي أجهزة\npair: زوج\npaired: يقترن\npairing: الاقتران...\npairing_failed: فشل الاقتران\npassword: كلمة المرور\nprovide_address: أدخل العنوان\nprovide_password: أدخل اسم المستخدم وكلمة المرور\nprovide_pin: أدخل رقم التعريف الشخصي\nscanning: جارٍ البحث عن الأجهزة...\nunpair: إلغاء الاقتران\nunpair_failed: فشل إلغاء الاقتران\nusername: اسم المستخدم\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/az.yml",
    "content": "available: Mövcud qurğular\ncancel: Ləğv etmək\nconfirm: Təsdiq etmək\nconfirm_only: Cütləşmə təsdiqləyin\nconfirm_pin: PİN uyğun olduğunu təsdiqləyin\nconnected: Bağlı\ndisconnect: Ayırd etmək\ndisplay_pin: Cihazdakı bu pini daxil edin\nlowenergy: Aşağı enerji\nmore: Daha çox bluetooth parametrləri\nno_adapter: Bluetooth adapteri tapılmadı\nnot_found: Heç bir cihaz tapılmadı\npair: Cütləşdirmək\npaired: Qoşalaşmış\npairing: Cütləşmə ...\npairing_failed: Cütləşmə uğursuz oldu\npassword: Parol\nprovide_address: Addamaq\nprovide_password: İstifadəçi adı və şifrənizi daxil edin\nprovide_pin: Pin daxil etmək\nscanning: Cihazlar üçün tarama ...\nunpair: Çəpər\nunpair_failed: Uğursuzluq uğursuz oldu\nusername: İstifadəçi adı\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/bg.yml",
    "content": "available: Налични устройства\ncancel: Отказ\nconfirm: Потвърдете\nconfirm_only: Потвърдете сдвояването\nconfirm_pin: Потвърдете, че ПИН кодът съвпада\nconnected: Свързан\ndisconnect: Прекъснете връзката\ndisplay_pin: Въведете този PIN на устройството\nlowenergy: Ниска енергия\nmore: Още Bluetooth настройки\nno_adapter: Няма намерен Bluetooth адаптер\nnot_found: Няма намерени устройства\npair: чифт\npaired: Сдвоени\npairing: Сдвояване...\npairing_failed: Неуспешно сдвояване\npassword: Парола\nprovide_address: Въведете адрес\nprovide_password: Въведете потребителско име и парола\nprovide_pin: Въведете PIN\nscanning: Сканира се за устройства...\nunpair: Раздвояване\nunpair_failed: Премахването на двойката не бе успешно\nusername: Потребителско име\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/bn.yml",
    "content": "available: উপলব্ধ ডিভাইস\ncancel: বাতিল করুন\nconfirm: নিশ্চিত করুন\nconfirm_only: পেয়ারিং নিশ্চিত করুন\nconfirm_pin: নিশ্চিত করুন যে পিন মিলছে\nconnected: সংযুক্ত\ndisconnect: সংযোগ বিচ্ছিন্ন করুন\ndisplay_pin: ডিভাইসে এই পিনটি লিখুন\nlowenergy: কম শক্তি\nmore: আরও ব্লুটুথ সেটিংস\nno_adapter: কোনো ব্লুটুথ অ্যাডাপ্টার পাওয়া যায়নি\nnot_found: কোনো ডিভাইস পাওয়া যায়নি\npair: জোড়া\npaired: জোড়া হয়েছে\npairing: পেয়ার করা হচ্ছে...\npairing_failed: পেয়ার করা ব্যর্থ হয়েছে৷\npassword: পাসওয়ার্ড\nprovide_address: ঠিকানা লিখুন\nprovide_password: ব্যবহারকারীর নাম এবং পাসওয়ার্ড লিখুন\nprovide_pin: পিন লিখুন\nscanning: ডিভাইসের জন্য স্ক্যান করা হচ্ছে...\nunpair: আনপেয়ার করুন\nunpair_failed: যুক্ত করা ব্যর্থ হয়েছে৷\nusername: ব্যবহারকারীর নাম\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/bs.yml",
    "content": "available: Dostupni uređaji\ncancel: Otkaži\nconfirm: Potvrdi\nconfirm_only: Potvrdite uparivanje\nconfirm_pin: Potvrdite da se PIN podudara\nconnected: Povezano\ndisconnect: Prekini vezu\ndisplay_pin: Unesite ovaj PIN na uređaju\nlowenergy: Niska energija\nmore: Više Bluetooth postavki\nno_adapter: Nije pronađen Bluetooth adapter\nnot_found: Nije pronađen nijedan uređaj\npair: Par\npaired: Upareno\npairing: Uparivanje...\npairing_failed: Uparivanje nije uspjelo\npassword: Lozinka\nprovide_address: Unesite adresu\nprovide_password: Unesite korisničko ime i lozinku\nprovide_pin: Unesite PIN\nscanning: Skeniranje uređaja...\nunpair: Unpair\nunpair_failed: Prekid uparivanja nije uspio\nusername: Korisničko ime\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ca.yml",
    "content": "available: Dispositius disponibles\ncancel: Cancel·la\nconfirm: Confirmeu\nconfirm_only: Confirmeu l'aparellament\nconfirm_pin: Confirmeu que el PIN coincideixi\nconnected: Connectat\ndisconnect: Desconnecta\ndisplay_pin: Introduïu aquest PIN al dispositiu\nlowenergy: Baixa energia\nmore: Més configuració de Bluetooth\nno_adapter: No s'ha trobat cap adaptador Bluetooth\nnot_found: No s'ha trobat cap dispositiu\npair: Parella\npaired: Parellat\npairing: Maridatge...\npairing_failed: S'ha produït un error en l'aparellament\npassword: Contrasenya\nprovide_address: Introduïu l'adreça\nprovide_password: Introduïu el nom d'usuari i la contrasenya\nprovide_pin: Introduïu el PIN\nscanning: S'estan buscant dispositius...\nunpair: Desvincular\nunpair_failed: S'ha produït un error en desvincular\nusername: Nom d'usuari\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/cs.yml",
    "content": "available: Dostupná zařízení\ncancel: Zrušit\nconfirm: Potvrdit\nconfirm_only: Potvrďte spárování\nconfirm_pin: Potvrďte, že se PIN shoduje\nconnected: Připojeno\ndisconnect: Odpojit\ndisplay_pin: Zadejte tento PIN na zařízení\nlowenergy: Nízká energie\nmore: Další nastavení Bluetooth\nno_adapter: Nebyl nalezen žádný adaptér Bluetooth\nnot_found: Nebyla nalezena žádná zařízení\npair: Pár\npaired: Spárováno\npairing: Párování...\npairing_failed: Párování se nezdařilo\npassword: Heslo\nprovide_address: Zadejte adresu\nprovide_password: Zadejte uživatelské jméno a heslo\nprovide_pin: Zadejte PIN\nscanning: Vyhledávání zařízení...\nunpair: Zrušit spárování\nunpair_failed: Zrušení párování se nezdařilo\nusername: Uživatelské jméno\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/cy.yml",
    "content": "available: Dyfeisiau sydd ar Gael\ncancel: Canslo\nconfirm: Cadarnhau\nconfirm_only: Cadarnhau paru\nconfirm_pin: Cadarnhewch fod y PIN yn cyfateb\nconnected: Wedi'i gysylltu\ndisconnect: Datgysylltu\ndisplay_pin: Rhowch y PIN hwn ar y ddyfais\nlowenergy: Ynni Isel\nmore: Mwy o Gosodiadau Bluetooth\nno_adapter: Ni chanfuwyd addasydd Bluetooth\nnot_found: Heb ganfod dyfeisiau\npair: Pâr\npaired: Wedi paru\npairing: Yn paru...\npairing_failed: Methodd y paru\npassword: Cyfrinair\nprovide_address: Rhowch gyfeiriad\nprovide_password: Rhowch enw defnyddiwr a chyfrinair\nprovide_pin: Rhowch PIN\nscanning: Wrthi'n sganio am ddyfeisiau...\nunpair: Di-bâr\nunpair_failed: Methodd y dad-baru\nusername: Enw defnyddiwr\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/da.yml",
    "content": "available: Tilgængelige enheder\ncancel: Ophæve\nconfirm: Bekræfte\nconfirm_only: Bekræft parring\nconfirm_pin: Bekræft, at PIN-koden stemmer overens\nconnected: Forbundet\ndisconnect: Afbryde\ndisplay_pin: Indtast denne PIN-kode på enheden\nlowenergy: Lav energi\nmore: Flere Bluetooth-indstillinger\nno_adapter: Ingen Bluetooth-adapter fundet\nnot_found: Ingen enheder fundet\npair: Par\npaired: Parret\npairing: Parrer...\npairing_failed: Parring mislykkedes\npassword: Adgangskode\nprovide_address: Indtast adresse\nprovide_password: Indtast brugernavn og adgangskode\nprovide_pin: Indtast PIN-kode\nscanning: Scanner efter enheder...\nunpair: Fjern parring\nunpair_failed: Ophævelse af parring mislykkedes\nusername: Brugernavn\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/de.yml",
    "content": "available: Verfügbare Geräte\ncancel: Stornieren\nconfirm: Bestätigen\nconfirm_only: Bestätigen Sie die Kopplung\nconfirm_pin: Bestätigen Sie, dass die PIN übereinstimmt\nconnected: Verbunden\ndisconnect: Trennen\ndisplay_pin: Geben Sie diese PIN am Gerät ein\nlowenergy: Niedrige Energie\nmore: Weitere Bluetooth-Einstellungen\nno_adapter: Kein Bluetooth-Adapter gefunden\nnot_found: Keine Geräte gefunden\npair: Paar\npaired: Gepaart\npairing: Paarung...\npairing_failed: Die Kopplung ist fehlgeschlagen\npassword: Passwort\nprovide_address: Adresse eingeben\nprovide_password: Geben Sie Benutzernamen und Passwort ein\nprovide_pin: PIN eingeben\nscanning: Nach Geräten suchen...\nunpair: Entkoppeln\nunpair_failed: Das Entkoppeln ist fehlgeschlagen\nusername: Benutzername\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/el.yml",
    "content": "available: Διαθέσιμες Συσκευές\ncancel: Ματαίωση\nconfirm: Επιβεβαιώνω\nconfirm_only: Επιβεβαιώστε τη σύζευξη\nconfirm_pin: Επιβεβαιώστε ότι το PIN ταιριάζει\nconnected: Συνδεδεμένος\ndisconnect: Αποσυνδέω\ndisplay_pin: Εισαγάγετε αυτό το PIN στη συσκευή\nlowenergy: Χαμηλής Ενέργειας\nmore: Περισσότερες ρυθμίσεις Bluetooth\nno_adapter: Δεν βρέθηκε προσαρμογέας Bluetooth\nnot_found: Δεν βρέθηκαν συσκευές\npair: Ζεύγος\npaired: Ζευγάρισε\npairing: Σύζευξη...\npairing_failed: Η σύζευξη απέτυχε\npassword: Σύνθημα\nprovide_address: Εισαγάγετε τη διεύθυνση\nprovide_password: Εισαγάγετε όνομα χρήστη και κωδικό πρόσβασης\nprovide_pin: Εισαγάγετε το PIN\nscanning: Σάρωση για συσκευές...\nunpair: Κατάργηση ζεύξης\nunpair_failed: Η κατάργηση σύζευξης απέτυχε\nusername: Όνομα χρήστη\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/en.yml",
    "content": "available: Available Devices\ncancel: Cancel\nconfirm: Confirm\nconfirm_only: Confirm pairing\nconfirm_pin: Confirm that the PIN matches\nconnected: Connected\ndisconnect: Disconnect\ndisplay_pin: Enter this PIN on the device\nlowenergy: Low Energy\nmore: More Bluetooth Settings\nno_adapter: No Bluetooth adapter found\nnot_found: No devices found\npair: Pair\npaired: Paired\npairing: Pairing...\npairing_failed: Pairing failed\npassword: Password\nprovide_address: Enter address\nprovide_password: Enter username and password\nprovide_pin: Enter PIN\nscanning: Scanning for devices...\nunpair: Unpair\nunpair_failed: Unpairing failed\nusername: Username\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/es.yml",
    "content": "available: Dispositivos disponibles\ncancel: Cancelar\nconfirm: Confirmar\nconfirm_only: Confirmar emparejamiento\nconfirm_pin: Confirma que el PIN coincide\nconnected: Conectado\ndisconnect: Desconectar\ndisplay_pin: Ingrese este PIN en el dispositivo\nlowenergy: Energía baja\nmore: Más configuraciones de Bluetooth\nno_adapter: No se encontró ningún adaptador Bluetooth\nnot_found: No se encontraron dispositivos\npair: Par\npaired: emparejado\npairing: Emparejamiento...\npairing_failed: El emparejamiento falló\npassword: Contraseña\nprovide_address: Introduzca la dirección\nprovide_password: Ingrese nombre de usuario y contraseña\nprovide_pin: Introducir PIN\nscanning: Buscando dispositivos...\nunpair: Desemparejar\nunpair_failed: Error al desemparejar\nusername: Nombre de usuario\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/et.yml",
    "content": "available: Saadaolevad seadmed\ncancel: Tühista\nconfirm: Kinnita\nconfirm_only: Kinnitage sidumine\nconfirm_pin: Veenduge, et PIN-kood ühtib\nconnected: Ühendatud\ndisconnect: Katkesta ühendus\ndisplay_pin: Sisestage see PIN-kood seadmesse\nlowenergy: Madal energia\nmore: Rohkem Bluetoothi ​​seadeid\nno_adapter: Bluetoothi ​​adapterit ei leitud\nnot_found: Ühtegi seadet ei leitud\npair: Paari\npaired: Paaritud\npairing: Sidumine...\npairing_failed: Sidumine ebaõnnestus\npassword: Parool\nprovide_address: Sisestage aadress\nprovide_password: Sisesta kasutajanimi ja parool\nprovide_pin: Sisestage PIN-kood\nscanning: Seadmete otsimine...\nunpair: Katkestage sidumine\nunpair_failed: Sidumine ebaõnnestus\nusername: Kasutajanimi\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/eu.yml",
    "content": "available: Eskuragarri dauden gailuak\ncancel: Utzi\nconfirm: Berretsi\nconfirm_only: Berretsi parekatzea\nconfirm_pin: Ziurtatu PINa bat datorrela\nconnected: Konektatuta\ndisconnect: Deskonektatu\ndisplay_pin: Sartu PIN hau gailuan\nlowenergy: Energia baxua\nmore: Bluetooth ezarpen gehiago\nno_adapter: Ez da Bluetooth egokitzailerik aurkitu\nnot_found: Ez da gailurik aurkitu\npair: Bikoteka\npaired: Parekatuta\npairing: Parekatzea...\npairing_failed: Parekatzeak huts egin du\npassword: Pasahitza\nprovide_address: Sartu helbidea\nprovide_password: Sartu erabiltzaile-izena eta pasahitza\nprovide_pin: Sartu PINa\nscanning: Gailuak bilatzen...\nunpair: Desparekatu\nunpair_failed: Ezin izan da desparekatzean\nusername: Erabiltzaile izena\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/fa.yml",
    "content": "available: دستگاه های موجود\ncancel: لغو کنید\nconfirm: تایید کنید\nconfirm_only: جفت شدن را تایید کنید\nconfirm_pin: تأیید کنید که پین ​​مطابقت دارد\nconnected: متصل شد\ndisconnect: قطع کن\ndisplay_pin: این پین را در دستگاه وارد کنید\nlowenergy: کم انرژی\nmore: تنظیمات بلوتوث بیشتر\nno_adapter: هیچ آداپتور بلوتوثی پیدا نشد\nnot_found: دستگاهی پیدا نشد\npair: جفت کردن\npaired: جفت شد\npairing: جفت شدن...\npairing_failed: جفت شدن ناموفق بود\npassword: رمز عبور\nprovide_address: آدرس را وارد کنید\nprovide_password: نام کاربری و رمز عبور را وارد کنید\nprovide_pin: پین را وارد کنید\nscanning: در حال اسکن دستگاه ها...\nunpair: لغو جفت کردن\nunpair_failed: لغو مرتبط‌سازی انجام نشد\nusername: نام کاربری\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/fi.yml",
    "content": "available: Käytettävissä olevat laitteet\ncancel: Peruuttaa\nconfirm: Vahvistaa\nconfirm_only: Vahvista pariliitos\nconfirm_pin: Varmista, että PIN-koodi vastaa\nconnected: Yhdistetty\ndisconnect: Katkaise yhteys\ndisplay_pin: Syötä tämä PIN-koodi laitteeseen\nlowenergy: Matala energia\nmore: Lisää Bluetooth-asetuksia\nno_adapter: Bluetooth-sovitinta ei löydy\nnot_found: Laitteita ei löytynyt\npair: Pari\npaired: Paritettu\npairing: Muodostetaan laiteparia...\npairing_failed: Pariliitos epäonnistui\npassword: Salasana\nprovide_address: Anna osoite\nprovide_password: Anna käyttäjätunnus ja salasana\nprovide_pin: Anna PIN-koodi\nscanning: Haetaan laitteita...\nunpair: Poista pariliitos\nunpair_failed: Pariliitoksen purkaminen epäonnistui\nusername: Käyttäjätunnus\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/fr.yml",
    "content": "available: Appareils disponibles\ncancel: Annuler\nconfirm: Confirmer\nconfirm_only: Confirmer l'appairage\nconfirm_pin: Confirmez que le code PIN correspond\nconnected: Connecté\ndisconnect: Déconnecter\ndisplay_pin: Entrez ce code PIN sur l'appareil\nlowenergy: Faible consommation d'énergie\nmore: Plus de paramètres Bluetooth\nno_adapter: Aucun adaptateur Bluetooth trouvé\nnot_found: Aucun appareil trouvé\npair: Paire\npaired: Jumelé\npairing: L'appariement...\npairing_failed: Échec du couplage\npassword: Mot de passe\nprovide_address: Entrez l'adresse\nprovide_password: Entrez le nom d'utilisateur et le mot de passe\nprovide_pin: Entrez le code PIN\nscanning: Recherche d'appareils...\nunpair: Dissocier\nunpair_failed: Échec de la dissociation\nusername: Nom d'utilisateur\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/gu.yml",
    "content": "available: ઉપલબ્ધ ઉપકરણો\ncancel: રદ કરો\nconfirm: પુષ્ટિ કરો\nconfirm_only: જોડી બનાવવાની પુષ્ટિ કરો\nconfirm_pin: પુષ્ટિ કરો કે PIN મેળ ખાય છે\nconnected: કનેક્ટેડ\ndisconnect: ડિસ્કનેક્ટ કરો\ndisplay_pin: ઉપકરણ પર આ PIN દાખલ કરો\nlowenergy: ઓછી ઉર્જા\nmore: વધુ બ્લૂટૂથ સેટિંગ્સ\nno_adapter: કોઈ બ્લૂટૂથ ઍડપ્ટર મળ્યું નથી\nnot_found: કોઈ ઉપકરણો મળ્યાં નથી\npair: જોડી\npaired: જોડી બનાવી\npairing: જોડી બનાવી રહ્યું છે...\npairing_failed: જોડાણ નિષ્ફળ થયું\npassword: પાસવર્ડ\nprovide_address: સરનામું દાખલ કરો\nprovide_password: વપરાશકર્તા નામ અને પાસવર્ડ દાખલ કરો\nprovide_pin: PIN દાખલ કરો\nscanning: ઉપકરણો માટે સ્કેન કરી રહ્યું છે...\nunpair: અનપેયર કરો\nunpair_failed: અનપેયરિંગ નિષ્ફળ થયું\nusername: વપરાશકર્તા નામ\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/he.yml",
    "content": "available: מכשירים זמינים\ncancel: לְבַטֵל\nconfirm: לְאַשֵׁר\nconfirm_only: אשר את ההתאמה\nconfirm_pin: אשר שה-PIN תואם\nconnected: מְחוּבָּר\ndisconnect: לְנַתֵק\ndisplay_pin: הזן PIN זה במכשיר\nlowenergy: אנרגיה נמוכה\nmore: הגדרות Bluetooth נוספות\nno_adapter: לא נמצא מתאם Bluetooth\nnot_found: לא נמצאו מכשירים\npair: זוּג\npaired: מְזוּוָג\npairing: צִמוּד...\npairing_failed: ההתאמה נכשלה\npassword: סִיסמָה\nprovide_address: הזן כתובת\nprovide_password: הזן שם משתמש וסיסמה\nprovide_pin: הזן PIN\nscanning: סורק לאיתור מכשירים...\nunpair: בטל התאמה\nunpair_failed: ביטול ההתאמה נכשל\nusername: שם משתמש\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/hi.yml",
    "content": "available: उपलब्ध उपकरण\ncancel: रद्द करना\nconfirm: पुष्टि करना\nconfirm_only: युग्मन की पुष्टि करें\nconfirm_pin: पुष्टि करें कि पिन मेल खाता है\nconnected: जुड़े हुए\ndisconnect: डिस्कनेक्ट\ndisplay_pin: इस पिन को डिवाइस पर दर्ज करें\nlowenergy: कम ऊर्जा\nmore: अधिक ब्लूटूथ सेटिंग्स\nno_adapter: कोई ब्लूटूथ एडाप्टर नहीं मिला\nnot_found: कोई यंत्र नहीं मिले\npair: जोड़ा\npaired: युग्मित\npairing: जोड़ी...\npairing_failed: युग्मन विफल\npassword: पासवर्ड\nprovide_address: पता दर्ज करें\nprovide_password: उपयोगकर्ता नाम और पासवर्ड दर्ज करें\nprovide_pin: पिन दर्ज करें\nscanning: डिवाइसों के लिए स्कैन किया जा रहा है...\nunpair: अयुग्मित\nunpair_failed: अयुग्मित करना विफल रहा\nusername: उपयोगकर्ता नाम\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/hr.yml",
    "content": "available: Dostupni uređaji\ncancel: Otkazati\nconfirm: Potvrdi\nconfirm_only: Potvrdite uparivanje\nconfirm_pin: Potvrdite podudaranje PIN-a\nconnected: Povezan\ndisconnect: Prekini vezu\ndisplay_pin: Unesite ovaj PIN na uređaj\nlowenergy: Niska energija\nmore: Više Bluetooth postavki\nno_adapter: Nije pronađen Bluetooth adapter\nnot_found: Nema pronađenih uređaja\npair: Par\npaired: Uparen\npairing: Uparivanje...\npairing_failed: Uparivanje nije uspjelo\npassword: Lozinka\nprovide_address: Unesite adresu\nprovide_password: Unesite korisničko ime i lozinku\nprovide_pin: Unesite PIN\nscanning: Traženje uređaja...\nunpair: Poništi uparivanje\nunpair_failed: Poništavanje uparivanja nije uspjelo\nusername: Korisničko ime\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/hu.yml",
    "content": "available: Elérhető eszközök\ncancel: Mégsem\nconfirm: Erősítse meg\nconfirm_only: Erősítse meg a párosítást\nconfirm_pin: Győződjön meg arról, hogy a PIN egyezik\nconnected: Csatlakozva\ndisconnect: Leválasztás\ndisplay_pin: Írja be ezt a PIN-kódot az eszközön\nlowenergy: Alacsony energia\nmore: További Bluetooth beállítások\nno_adapter: Nem található Bluetooth adapter\nnot_found: Nem találhatók eszközök\npair: Pár\npaired: Párosítva\npairing: Párosítás...\npairing_failed: A párosítás nem sikerült\npassword: Jelszó\nprovide_address: Adja meg a címet\nprovide_password: Adja meg a felhasználónevet és a jelszót\nprovide_pin: Írja be a PIN-kódot\nscanning: Eszközök keresése...\nunpair: Párosítás megszüntetése\nunpair_failed: A párosítás megszüntetése nem sikerült\nusername: Felhasználónév\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/hy.yml",
    "content": "available: Հասանելի սարքեր\ncancel: Չեղարկել\nconfirm: Հաստատել\nconfirm_only: Հաստատեք զուգավորումը\nconfirm_pin: Հաստատեք, որ PIN-ը համընկնում է\nconnected: Միացված է\ndisconnect: Անջատել\ndisplay_pin: Մուտքագրեք այս PIN կոդը սարքի վրա\nlowenergy: Ցածր էներգիա\nmore: Ավելի շատ Bluetooth կարգավորումներ\nno_adapter: Bluetooth ադապտեր չի գտնվել\nnot_found: Սարքեր չեն գտնվել\npair: Զույգ\npaired: Զուգակցված\npairing: Զուգավորում...\npairing_failed: Չհաջողվեց զուգակցել\npassword: Գաղտնաբառ\nprovide_address: Մուտքագրեք հասցեն\nprovide_password: Մուտքագրեք օգտվողի անունը և գաղտնաբառը\nprovide_pin: Մուտքագրեք PIN կոդը\nscanning: Սարքերի սկանավորում...\nunpair: Ապազույգացնել\nunpair_failed: Չհաջողվեց չեղարկել զուգակցումը\nusername: Օգտվողի անունը\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/id.yml",
    "content": "available: Perangkat yang Tersedia\ncancel: Membatalkan\nconfirm: Mengonfirmasi\nconfirm_only: Konfirmasikan penyandingan\nconfirm_pin: Konfirmasikan bahwa PIN cocok\nconnected: Terhubung\ndisconnect: Memutuskan\ndisplay_pin: Masukkan PIN ini pada perangkat\nlowenergy: Energi Rendah\nmore: Pengaturan Bluetooth Lainnya\nno_adapter: Tidak ada adaptor Bluetooth yang ditemukan\nnot_found: Tidak ada perangkat yang ditemukan\npair: Pasangan\npaired: Dipasangkan\npairing: Memasangkan...\npairing_failed: Penyandingan gagal\npassword: Kata sandi\nprovide_address: Masukkan alamat\nprovide_password: Masukkan nama pengguna dan kata sandi\nprovide_pin: Masukkan PIN\nscanning: Memindai perangkat...\nunpair: Putuskan pasangan\nunpair_failed: Gagal melepas pasangan\nusername: Nama belakang\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/is.yml",
    "content": "available: Tiltæk tæki\ncancel: Hætta við\nconfirm: Staðfesta\nconfirm_only: Staðfestu pörun\nconfirm_pin: Staðfestu að PIN-númerið passi\nconnected: Tengdur\ndisconnect: Aftengdu\ndisplay_pin: Sláðu inn þetta PIN-númer á tækinu\nlowenergy: Lág orku\nmore: Fleiri Bluetooth stillingar\nno_adapter: Enginn Bluetooth millistykki fannst\nnot_found: Engin tæki fundust\npair: Par\npaired: Pöruð\npairing: Pörun...\npairing_failed: Pörun mistókst\npassword: Lykilorð\nprovide_address: Sláðu inn heimilisfang\nprovide_password: Sláðu inn notandanafn og lykilorð\nprovide_pin: Sláðu inn PIN-númer\nscanning: Leitar að tækjum...\nunpair: Afpörun\nunpair_failed: Afpörun mistókst\nusername: Notandanafn\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/it.yml",
    "content": "available: Dispositivi disponibili\ncancel: Cancellare\nconfirm: Confermare\nconfirm_only: Conferma l'accoppiamento\nconfirm_pin: Confermare che il PIN corrisponda\nconnected: Collegata\ndisconnect: Disconnetti\ndisplay_pin: Inserisci questo PIN sul dispositivo\nlowenergy: Bassa energia\nmore: Altre impostazioni Bluetooth\nno_adapter: Nessun adattatore Bluetooth trovato\nnot_found: Nessun dispositivo trovato\npair: Paio\npaired: Accoppiato\npairing: Associazione...\npairing_failed: Associazione non riuscita\npassword: Password\nprovide_address: Inserisci l'indirizzo\nprovide_password: Inserisci nome utente e password\nprovide_pin: Inserisci il PIN\nscanning: Ricerca dispositivi in ​​corso...\nunpair: Disaccoppia\nunpair_failed: Disaccoppiamento non riuscito\nusername: Nome utente\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ja.yml",
    "content": "available: 利用可能なデバイス\ncancel: キャンセル\nconfirm: 確認する\nconfirm_only: ペアリングを確認する\nconfirm_pin: PIN が一致することを確認します\nconnected: 接続済み\ndisconnect: 切断する\ndisplay_pin: デバイスにこの PIN を入力してください\nlowenergy: 低エネルギー\nmore: その他の Bluetooth 設定\nno_adapter: Bluetooth アダプターが見つかりません\nnot_found: デバイスが見つかりません\npair: ペア\npaired: ペアリング済み\npairing: ペアリング中...\npairing_failed: ペアリングに失敗しました\npassword: パスワード\nprovide_address: アドレスを入力してください\nprovide_password: ユーザー名とパスワードを入力してください\nprovide_pin: PINを入力してください\nscanning: デバイスをスキャンしています...\nunpair: ペアリングを解除する\nunpair_failed: ペアリングの解除に失敗しました\nusername: ユーザー名\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ka.yml",
    "content": "available: ხელმისაწვდომი მოწყობილობები\ncancel: გაუქმება\nconfirm: დაადასტურეთ\nconfirm_only: დაადასტურეთ დაწყვილება\nconfirm_pin: დაადასტურეთ, რომ PIN ემთხვევა\nconnected: დაკავშირებულია\ndisconnect: გათიშვა\ndisplay_pin: შეიყვანეთ ეს PIN მოწყობილობაზე\nlowenergy: დაბალი ენერგია\nmore: მეტი Bluetooth პარამეტრები\nno_adapter: Bluetooth ადაპტერი ვერ მოიძებნა\nnot_found: მოწყობილობები არ მოიძებნა\npair: წყვილი\npaired: დაწყვილებული\npairing: დაწყვილება...\npairing_failed: დაწყვილება ვერ მოხერხდა\npassword: პაროლი\nprovide_address: შეიყვანეთ მისამართი\nprovide_password: შეიყვანეთ მომხმარებლის სახელი და პაროლი\nprovide_pin: შეიყვანეთ PIN\nscanning: მიმდინარეობს მოწყობილობების სკანირება...\nunpair: გაუქმება\nunpair_failed: დაწყვილების გაუქმება ვერ მოხერხდა\nusername: მომხმარებლის სახელი\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/km.yml",
    "content": "available: ឧបករណ៍ដែលមាន\ncancel: លប់ចោល\nconfirm: បហ្ជាក់\nconfirm_only: បញ្ជាក់គូ\nconfirm_pin: បញ្ជាក់ថាម្ជុលផ្គូផ្គង\nconnected: ខាងចមដេល\ndisconnect: ផ្ដាច់\ndisplay_pin: បញ្ចូលលេខសម្ងាត់នេះនៅលើឧបករណ៍\nlowenergy: ថាមពលទាប\nmore: ការកំណត់ប៊្លូធូសច្រើនទៀត\nno_adapter: រកមិនឃើញអាដាប់ទ័រប៊្លូធូសទេ\nnot_found: រកមិនឃើញឧបករណ៍\npair: មយយកុ\npaired: រតួរ\npairing: ភ្ជាប់ ...\npairing_failed: ការផ្គូរផ្គងបានបរាជ័យ\npassword: ហក្យសមងាត់\nprovide_address: បញ្ចូលអាសយដ្ឋាន\nprovide_password: បញ្ចូលឈ្មោះអ្នកប្រើនិងពាក្យសម្ងាត់\nprovide_pin: បញ្ចូលលេខសម្ងាត់\nscanning: ស្កេនរកឧបករណ៍ ...\nunpair: ដេលរសករ\nunpair_failed: ការដាក់មិនបានបរាជ័យ\nusername: ឈ្មោះអ្នក\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ko.yml",
    "content": "available: 사용 가능한 장치\ncancel: 취소\nconfirm: 확인하다\nconfirm_only: 페어링 확인\nconfirm_pin: PIN이 일치하는지 확인하세요.\nconnected: 연결됨\ndisconnect: 연결 끊기\ndisplay_pin: 기기에 이 PIN을 입력하세요.\nlowenergy: 저에너지\nmore: 추가 블루투스 설정\nno_adapter: Bluetooth 어댑터를 찾을 수 없습니다.\nnot_found: 기기를 찾을 수 없습니다.\npair: 쌍\npaired: 페어링됨\npairing: 편성...\npairing_failed: 페어링 실패\npassword: 비밀번호\nprovide_address: 주소를 입력하세요\nprovide_password: 사용자 이름과 비밀번호를 입력하세요\nprovide_pin: PIN 입력\nscanning: 기기 검색 중...\nunpair: 페어링 해제\nunpair_failed: 페어링 해제 실패\nusername: 사용자 이름\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ku.yml",
    "content": "available: Amûrên Berdest\ncancel: Bişûndekirin\nconfirm: Tesdîqkirin\nconfirm_only: Pejirandinê piştrast bikin\nconfirm_pin: Piştrast bikin ku PIN lihevhatî ye\nconnected: Girêdayî\ndisconnect: Hevqetandin\ndisplay_pin: Vê PIN-ê li ser cîhazê binivîse\nlowenergy: Enerjiya kêm\nmore: More Settings Bluetooth\nno_adapter: Adapterek Bluetooth nehat dîtin\nnot_found: Amûrek nehat dîtin\npair: Cot\npaired: Paired\npairing: Hevberkirin...\npairing_failed: Hevberkirin têk çû\npassword: Şîfre\nprovide_address: Navnîşanê binivîse\nprovide_password: Navê bikarhêner û şîfreya xwe binivîse\nprovide_pin: PIN binivîse\nscanning: Lêgerîna cîhazan...\nunpair: Pair\nunpair_failed: Rakirin bi ser neket\nusername: Navê bikarhêner\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/lb.yml",
    "content": "available: Verfügbar Geräter\ncancel: Ofbriechen\nconfirm: Confirméieren\nconfirm_only: Confirméieren Koppelen\nconfirm_pin: Bestätegt datt de PIN entsprécht\nconnected: Ugeschloss\ndisconnect: Trennen\ndisplay_pin: Gitt dës PIN um Apparat\nlowenergy: Niddereg Energie\nmore: Méi Bluetooth Astellungen\nno_adapter: Kee Bluetooth Adapter fonnt\nnot_found: Keng Apparater fonnt\npair: Pair\npaired: Gepaart\npairing: Koppelen ...\npairing_failed: D'Koppel ass gescheitert\npassword: Passwuert\nprovide_address: Gitt Adress\nprovide_password: Gitt Benotzernumm a Passwuert\nprovide_pin: Gitt PIN\nscanning: Scannen no Geräter ...\nunpair: Unpairen\nunpair_failed: Auspairen ass gescheitert\nusername: Benotzernumm\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/lo.yml",
    "content": "available: ອຸປະກອນທີ່ມີຢູ່\ncancel: ຍົກເລີກ\nconfirm: ຢືນຢັນ\nconfirm_only: ຢືນຢັນການຈັບຄູ່\nconfirm_pin: ຢືນຢັນວ່າ PIN Matches\nconnected: ເຊື່ອມໂຍງ\ndisconnect: ຕັດຂາດ\ndisplay_pin: ໃສ່ PIN ນີ້ໃນອຸປະກອນ\nlowenergy: ພະລັງງານຕ່ໍາ\nmore: ການຕັ້ງຄ່າ Bluetooth ເພີ່ມເຕີມ\nno_adapter: ບໍ່ພົບຜູ້ດັດແປງ bluetooth\nnot_found: ບໍ່ພົບອຸປະກອນໃດ\npair: ຄູ່\npaired: ຈັບຄູ່\npairing: ຄູ່ ...\npairing_failed: ການຈັບຄູ່ສົບຜົນສໍາເລັດ\npassword: ລະຫັດຜ່ານ\nprovide_address: ໃສ່ທີ່ຢູ່\nprovide_password: ໃສ່ຊື່ຜູ້ໃຊ້ແລະລະຫັດຜ່ານ\nprovide_pin: ໃສ່ເຂັມ\nscanning: ສະແກນສໍາລັບອຸປະກອນ ...\nunpair: ບໍ່ເອົາໃຈໃສ່ Unpair\nunpair_failed: ລົ້ມເຫລວທີ່ບໍ່ສາມາດຍົກເວັ້ນໄດ້\nusername: ສັນຍາລັກ\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/lt.yml",
    "content": "available: Galimi įrenginiai\ncancel: Atšaukti\nconfirm: Patvirtinti\nconfirm_only: Patvirtinkite susiejimą\nconfirm_pin: Patvirtinkite, kad PIN kodas sutampa\nconnected: Prisijungta\ndisconnect: Atsijungti\ndisplay_pin: Įrenginyje įveskite šį PIN kodą\nlowenergy: Maža energija\nmore: Daugiau Bluetooth nustatymų\nno_adapter: Nerastas Bluetooth adapteris\nnot_found: Nerasta jokių įrenginių\npair: Suporuoti\npaired: Suporuotas\npairing: Susiejama...\npairing_failed: Susieti nepavyko\npassword: Slaptažodis\nprovide_address: Įveskite adresą\nprovide_password: Įveskite vartotojo vardą ir slaptažodį\nprovide_pin: Įveskite PIN kodą\nscanning: Nuskaitoma įrenginių...\nunpair: Atsieti\nunpair_failed: Atsieti nepavyko\nusername: Vartotojo vardas\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/lv.yml",
    "content": "available: Pieejamās ierīces\ncancel: Atcelt\nconfirm: Apstiprināt\nconfirm_only: Apstipriniet savienošanu pārī\nconfirm_pin: Apstipriniet, ka PIN atbilst\nconnected: Savienots\ndisconnect: Atvienot\ndisplay_pin: Ievadiet šo PIN ierīcē\nlowenergy: Zema enerģija\nmore: Vairāk Bluetooth iestatījumu\nno_adapter: Nav atrasts neviens Bluetooth adapteris\nnot_found: Netika atrasta neviena ierīce\npair: Pāris\npaired: Sapārots\npairing: Notiek savienošana pārī...\npairing_failed: Savienošana pārī neizdevās\npassword: Parole\nprovide_address: Ievadiet adresi\nprovide_password: Ievadiet lietotājvārdu un paroli\nprovide_pin: Ievadiet PIN\nscanning: Notiek ierīču meklēšana...\nunpair: Atvienot pārī\nunpair_failed: Atvienošana pārī neizdevās\nusername: Lietotājvārds\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/mk.yml",
    "content": "available: Достапни уреди\ncancel: Откажи\nconfirm: Потврди\nconfirm_only: Потврдете го спарувањето\nconfirm_pin: Потврдете дека PIN-кодот се совпаѓа\nconnected: Поврзан\ndisconnect: Исклучете се\ndisplay_pin: Внесете го овој PIN на уредот\nlowenergy: Ниска енергија\nmore: Повеќе поставки за Bluetooth\nno_adapter: Не е пронајден адаптер за Bluetooth\nnot_found: Не се пронајдени уреди\npair: Пар\npaired: Спарени\npairing: Спарување...\npairing_failed: Спарувањето не успеа\npassword: Лозинка\nprovide_address: Внесете адреса\nprovide_password: Внесете корисничко име и лозинка\nprovide_pin: Внесете PIN\nscanning: Скенирање уреди...\nunpair: Откажи го\nunpair_failed: Откажувањето на спарувањето не успеа\nusername: Корисничко име\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/mn.yml",
    "content": "available: Байгаа төхөөрөмжүүд\ncancel: Цуаах\nconfirm: Батлах\nconfirm_only: Баталж баталгаажуулах\nconfirm_pin: ПИН-ийн тоглолтыг баталгаажуулна уу\nconnected: Холбоотой\ndisconnect: Тусгаарлах\ndisplay_pin: Энэ PIN-г төхөөрөмж дээр оруулна уу\nlowenergy: Бага эрчим хүч\nmore: Илүү их Bluetooth Settings\nno_adapter: Блютүүт эмнэлэгт олдсонгүй\nnot_found: Ямар ч төхөөрөмж олдсонгүй\npair: Хос\npaired: Хосчилсон\npairing: Хүлээж ...\npairing_failed: Хугацаа нь\npassword: Нууц үг\nprovide_address: Хаягаа оруулна гэрт оруулна уу\nprovide_password: Хэрэглэгчийн нэр, нууц үгийг оруулна уу\nprovide_pin: PIN кодыг оруулна уу\nscanning: Төхөөрөмжийг скан хийх ...\nunpair: Суллал\nunpair_failed: Унтарч ажил амжилтгүй боллоо\nusername: Хэрэглэгчийн\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ms.yml",
    "content": "available: Peranti yang ada\ncancel: Batalkan\nconfirm: Mengesahkan\nconfirm_only: Sahkan pasangan\nconfirm_pin: Sahkan bahawa pin sepadan\nconnected: Bersambung\ndisconnect: Putuskan sambungan\ndisplay_pin: Masukkan pin ini pada peranti\nlowenergy: Tenaga rendah\nmore: Lebih banyak tetapan Bluetooth\nno_adapter: Tiada penyesuai Bluetooth dijumpai\nnot_found: Tiada peranti yang dijumpai\npair: Pasangan\npaired: Berpasangan\npairing: Berpasangan ...\npairing_failed: Pasangan gagal\npassword: Kata laluan\nprovide_address: Masukkan alamat\nprovide_password: Masukkan Nama Pengguna dan Kata Laluan\nprovide_pin: Masukkan pin\nscanning: Mengimbas peranti ...\nunpair: Unpair\nunpair_failed: Tidak berpasangan gagal\nusername: Nama pengguna\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/mt.yml",
    "content": "available: Apparati Disponibbli\ncancel: Ikkanċella\nconfirm: Ikkonferma\nconfirm_only: Ikkonferma t-tqabbil\nconfirm_pin: Ikkonferma li l-PIN jaqbel\nconnected: Konnessi\ndisconnect: Skonnettja\ndisplay_pin: Daħħal dan il-PIN fuq l-apparat\nlowenergy: Enerġija Baxxa\nmore: Aktar Settings tal-Bluetooth\nno_adapter: Ma nstab l-ebda adapter Bluetooth\nnot_found: Ma nstab l-ebda apparat\npair: Par\npaired: Imqabbad\npairing: Tqabbil...\npairing_failed: It-tqabbil falla\npassword: Password\nprovide_address: Daħħal l-indirizz\nprovide_password: Daħħal username u password\nprovide_pin: Daħħal il-PIN\nscanning: Skannjar għal apparati...\nunpair: Unpair\nunpair_failed: L-unpairing falla\nusername: Isem tal-utent\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ne.yml",
    "content": "available: उपलब्ध यन्त्रहरू\ncancel: रद्द गर्नुहोस्\nconfirm: पुष्टि गर्नुहोस्\nconfirm_only: जोडी पुष्टि गर्नुहोस्\nconfirm_pin: पुष्टि गर्नुहोस् कि PIN मेल खान्छ\nconnected: जडान भयो\ndisconnect: जडान विच्छेद गर्नुहोस्\ndisplay_pin: यन्त्रमा यो PIN प्रविष्ट गर्नुहोस्\nlowenergy: कम ऊर्जा\nmore: थप ब्लुटुथ सेटिङहरू\nno_adapter: कुनै ब्लुटुथ एडाप्टर फेला परेन\nnot_found: कुनै पनि उपकरण फेला परेन\npair: जोडी\npaired: जोडा बनाइयो\npairing: जोडा बनाउँदै...\npairing_failed: जोडा बनाउन असफल भयो\npassword: पासवर्ड\nprovide_address: ठेगाना प्रविष्ट गर्नुहोस्\nprovide_password: प्रयोगकर्ता नाम र पासवर्ड प्रविष्ट गर्नुहोस्\nprovide_pin: PIN प्रविष्ट गर्नुहोस्\nscanning: यन्त्रहरू स्क्यान गर्दै...\nunpair: जोडा हटाउनुहोस्\nunpair_failed: जोडा हटाउन असफल भयो\nusername: प्रयोगकर्ता नाम\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/nl.yml",
    "content": "available: Beschikbare apparaten\ncancel: Annuleren\nconfirm: Bevestigen\nconfirm_only: Bevestig het koppelen\nconfirm_pin: Controleer of de pincode overeenkomt\nconnected: Aangesloten\ndisconnect: Verbreek de verbinding\ndisplay_pin: Voer deze pincode in op het apparaat\nlowenergy: Lage energie\nmore: Meer Bluetooth-instellingen\nno_adapter: Geen Bluetooth-adapter gevonden\nnot_found: Geen apparaten gevonden\npair: Paar\npaired: Gekoppeld\npairing: Koppelen...\npairing_failed: Koppelen is mislukt\npassword: Wachtwoord\nprovide_address: Voer adres in\nprovide_password: Voer gebruikersnaam en wachtwoord in\nprovide_pin: Voer pincode in\nscanning: Scannen naar apparaten...\nunpair: Ontkoppelen\nunpair_failed: Ontkoppelen is mislukt\nusername: Gebruikersnaam\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/no.yml",
    "content": "available: Tilgjengelige enheter\ncancel: Kansellere\nconfirm: Bekrefte\nconfirm_only: Bekreft sammenkobling\nconfirm_pin: Bekreft at PIN-koden stemmer overens\nconnected: Tilkoblet\ndisconnect: Frakople\ndisplay_pin: Skriv inn denne PIN-koden på enheten\nlowenergy: Lavenergi\nmore: Flere Bluetooth-innstillinger\nno_adapter: Finner ingen Bluetooth-adapter\nnot_found: Ingen enheter funnet\npair: Par\npaired: Sammenkoblet\npairing: Sammenkobling...\npairing_failed: Sammenkobling mislyktes\npassword: Passord\nprovide_address: Skriv inn adresse\nprovide_password: Skriv inn brukernavn og passord\nprovide_pin: Skriv inn PIN\nscanning: Skanner etter enheter...\nunpair: Fjern paring\nunpair_failed: Oppheving av paring mislyktes\nusername: Brukernavn\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/pa.yml",
    "content": "available: ਉਪਲਬਧ ਡਿਵਾਈਸਾਂ\ncancel: ਰੱਦ ਕਰੋ\nconfirm: ਪੁਸ਼ਟੀ ਕਰੋ\nconfirm_only: ਜੋੜਾ ਬਣਾਉਣ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ\nconfirm_pin: ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਪਿੰਨ ਮੇਲ ਖਾਂਦਾ ਹੈ\nconnected: ਜੁੜਿਆ\ndisconnect: ਡਿਸਕਨੈਕਟ ਕਰੋ\ndisplay_pin: ਡਿਵਾਈਸ 'ਤੇ ਇਹ ਪਿੰਨ ਦਾਖਲ ਕਰੋ\nlowenergy: ਘੱਟ ਊਰਜਾ\nmore: ਹੋਰ ਬਲੂਟੁੱਥ ਸੈਟਿੰਗਾਂ\nno_adapter: ਕੋਈ ਬਲੂਟੁੱਥ ਅਡਾਪਟਰ ਨਹੀਂ ਮਿਲਿਆ\nnot_found: ਕੋਈ ਡੀਵਾਈਸ ਨਹੀਂ ਮਿਲੇ\npair: ਜੋੜਾ\npaired: ਪੇਅਰ ਕੀਤਾ\npairing: ਜੋੜਾ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ...\npairing_failed: ਜੋੜਾ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ\npassword: ਪਾਸਵਰਡ\nprovide_address: ਪਤਾ ਦਰਜ ਕਰੋ\nprovide_password: ਉਪਭੋਗਤਾ ਨਾਮ ਅਤੇ ਪਾਸਵਰਡ ਦਰਜ ਕਰੋ\nprovide_pin: ਪਿੰਨ ਦਾਖਲ ਕਰੋ\nscanning: ਡਿਵਾਈਸਾਂ ਲਈ ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\nunpair: ਅਨਪੇਅਰ ਕਰੋ\nunpair_failed: ਅਨਪੇਅਰ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ\nusername: ਯੂਜ਼ਰਨੇਮ\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/pl.yml",
    "content": "available: Dostępne urządzenia\ncancel: Anulować\nconfirm: Potwierdzać\nconfirm_only: Potwierdź parowanie\nconfirm_pin: Potwierdź, że kod PIN jest zgodny\nconnected: Połączony\ndisconnect: Odłączyć\ndisplay_pin: Wprowadź ten PIN na urządzeniu\nlowenergy: Niska energia\nmore: Więcej ustawień Bluetooth\nno_adapter: Nie znaleziono adaptera Bluetooth\nnot_found: Nie znaleziono żadnych urządzeń\npair: Para\npaired: Sparowane\npairing: Łączenie w pary...\npairing_failed: Parowanie nie powiodło się\npassword: Hasło\nprovide_address: Wpisz adres\nprovide_password: Wprowadź nazwę użytkownika i hasło\nprovide_pin: Wprowadź PIN\nscanning: Skanowanie w poszukiwaniu urządzeń...\nunpair: Rozparuj\nunpair_failed: Rozparowanie nie powiodło się\nusername: Nazwa użytkownika\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ps.yml",
    "content": "available: شته وسایل\ncancel: لغوه کړئ\nconfirm: تایید کړه\nconfirm_only: جوړه کول تایید کړئ\nconfirm_pin: تایید کړئ چې PIN سره سمون لري\nconnected: نښلول شوی\ndisconnect: منحل کول\ndisplay_pin: دا PIN په وسیله کې دننه کړئ\nlowenergy: کمه انرژي\nmore: د بلوتوټ نور ترتیبات\nno_adapter: هیڅ بلوتوث اډاپټر ونه موندل شو\nnot_found: هیڅ وسایل ونه موندل شول\npair: جوړه\npaired: جوړه شوې\npairing: جوړه کول...\npairing_failed: جوړه ناکامه شوه\npassword: رمز\nprovide_address: ادرس دننه کړئ\nprovide_password: کارن نوم او پټنوم دننه کړئ\nprovide_pin: PIN داخل کړئ\nscanning: د وسایلو سکین کول...\nunpair: بې جوړې\nunpair_failed: یوځای کول ناکام شول\nusername: کارن نوم\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/pt-BR.yml",
    "content": "available: Dispositivos disponíveis\ncancel: Cancelar\nconfirm: Confirmar\nconfirm_only: Confirmar emparelhamento\nconfirm_pin: Confirme se o PIN corresponde\nconnected: Conectada\ndisconnect: Desconectar\ndisplay_pin: Insira este PIN no dispositivo\nlowenergy: Baixa energia\nmore: Mais configurações de Bluetooth\nno_adapter: Nenhum adaptador Bluetooth encontrado\nnot_found: Nenhum dispositivo encontrado\npair: Par\npaired: Emparelhada\npairing: Emparelhamento...\npairing_failed: Falha no pareamento\npassword: Senha\nprovide_address: Insira o endereço\nprovide_password: Digite nome de usuário e senha\nprovide_pin: Insira o PIN\nscanning: Procurando dispositivos...\nunpair: Desemparelhar\nunpair_failed: Falha ao desemparelhar\nusername: Nome de usuário\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/pt-PT.yml",
    "content": "available: Dispositivos disponíveis\ncancel: Cancelar\nconfirm: Confirmar\nconfirm_only: Confirmar emparelhamento\nconfirm_pin: Confirme se o PIN corresponde\nconnected: Conectada\ndisconnect: Desconectar\ndisplay_pin: Insira este PIN no dispositivo\nlowenergy: Baixa energia\nmore: Mais configurações de Bluetooth\nno_adapter: Nenhum adaptador Bluetooth encontrado\nnot_found: Nenhum dispositivo encontrado\npair: Par\npaired: Emparelhada\npairing: Emparelhamento...\npairing_failed: Falha no pareamento\npassword: Palavra-passe\nprovide_address: Insira o endereço\nprovide_password: Digite nome de usuário e senha\nprovide_pin: Insira o PIN\nscanning: Procurando dispositivos...\nunpair: Desemparelhar\nunpair_failed: Falha ao desemparelhar\nusername: Nome de usuário\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ro.yml",
    "content": "available: Dispozitive disponibile\ncancel: Anula\nconfirm: Confirma\nconfirm_only: Confirmați asocierea\nconfirm_pin: Confirmați că PIN-ul se potrivește\nconnected: Conectat\ndisconnect: Deconecta\ndisplay_pin: Introduceți acest PIN pe dispozitiv\nlowenergy: Energie scăzută\nmore: Mai multe setări Bluetooth\nno_adapter: Nu a fost găsit niciun adaptor Bluetooth\nnot_found: Nu s-au găsit dispozitive\npair: Pereche\npaired: Împerecheate\npairing: Asociere...\npairing_failed: Asocierea nu a reușit\npassword: Parolă\nprovide_address: Introdu adresa\nprovide_password: Introduceți numele de utilizator și parola\nprovide_pin: Introduceți codul PIN\nscanning: Se scanează pentru dispozitive...\nunpair: Deconectați\nunpair_failed: Deconectarea nu a reușit\nusername: Nume de utilizator\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ru.yml",
    "content": "available: Доступные устройства\ncancel: Отмена\nconfirm: Подтверждать\nconfirm_only: Подтвердить сопряжение\nconfirm_pin: Подтвердите, что PIN-код совпадает\nconnected: Подключено\ndisconnect: Отключить\ndisplay_pin: Введите этот PIN-код на устройстве\nlowenergy: Низкая энергия\nmore: Дополнительные настройки Bluetooth\nno_adapter: Bluetooth-адаптер не найден\nnot_found: Устройства не найдены\npair: Пара\npaired: Парный\npairing: Сопряжение...\npairing_failed: Сопряжение не удалось\npassword: Пароль\nprovide_address: Введите адрес\nprovide_password: Введите имя пользователя и пароль\nprovide_pin: Введите PIN-код\nscanning: Сканирование устройств...\nunpair: Отсоединить\nunpair_failed: Отключение не удалось\nusername: Имя пользователя\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/si.yml",
    "content": "available: පවතින උපාංග\ncancel: අවලංගු කරන්න\nconfirm: තහවුරු කරන්න\nconfirm_only: යුගල කිරීම තහවුරු කරන්න\nconfirm_pin: PIN එක ගැළපෙන බව තහවුරු කරන්න\nconnected: සම්බන්ධයි\ndisconnect: විසන්ධි කරන්න\ndisplay_pin: උපාංගයේ මෙම PIN ඇතුලත් කරන්න\nlowenergy: අඩු ශක්තිය\nmore: තවත් බ්ලූටූත් සැකසුම්\nno_adapter: බ්ලූටූත් ඇඩැප්ටරයක් ​​හමු නොවීය\nnot_found: උපාංග කිසිවක් හමු නොවීය\npair: යුගල\npaired: යුගල කර ඇත\npairing: යුගල කිරීම...\npairing_failed: යුගල කිරීම අසාර්ථක විය\npassword: මුරපදය\nprovide_address: ලිපිනය ඇතුලත් කරන්න\nprovide_password: පරිශීලක නාමය සහ මුරපදය ඇතුළත් කරන්න\nprovide_pin: PIN ඇතුලත් කරන්න\nscanning: උපාංග සඳහා පරිලෝකනය කරමින්...\nunpair: යුගල ඉවත් කරන්න\nunpair_failed: යුගල ඉවත් කිරීම අසාර්ථක විය\nusername: පරිශීලක නාමය\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/sk.yml",
    "content": "available: Dostupné zariadenia\ncancel: Zrušiť\nconfirm: Potvrďte\nconfirm_only: Potvrďte spárovanie\nconfirm_pin: Potvrďte, že sa PIN zhoduje\nconnected: Pripojené\ndisconnect: Odpojiť\ndisplay_pin: Zadajte tento PIN na zariadení\nlowenergy: Nízka energia\nmore: Ďalšie nastavenia Bluetooth\nno_adapter: Nenašiel sa žiadny adaptér Bluetooth\nnot_found: Nenašli sa žiadne zariadenia\npair: Spárovať\npaired: Spárované\npairing: Párovanie...\npairing_failed: Párovanie zlyhalo\npassword: heslo\nprovide_address: Zadajte adresu\nprovide_password: Zadajte používateľské meno a heslo\nprovide_pin: Zadajte PIN\nscanning: Hľadajú sa zariadenia...\nunpair: Zrušiť párovanie\nunpair_failed: Zrušenie párovania zlyhalo\nusername: Používateľské meno\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/so.yml",
    "content": "available: Aaladda la heli karo\ncancel: Burin\nconfirm: Xaqiijin\nconfirm_only: Xaqiiji lammaanaha\nconfirm_pin: Xaqiiji in PIN-ka PIN\nconnected: Ku xiran\ndisconnect: Ka furid\ndisplay_pin: Gali PIN-kan aaladda\nlowenergy: Tamar hooseeya\nmore: Meelo badan oo Bluetooth ah\nno_adapter: Adaphoth-ka Bluetooth ah ee la helay\nnot_found: Wax aalad ah lama helin\npair: Labo\npaired: Lammaane\npairing: Lammaanaha ...\npairing_failed: Lammaane ayaa fashilmay\npassword: Eray sir\nprovide_address: Geli cinwaanka\nprovide_password: Gali magaca isticmaale iyo erayga sirta ah\nprovide_pin: Gali PIN\nscanning: Iskaanka aaladda ...\nunpair: Aan dawlo lahayn\nunpair_failed: Iskaashadda oo aan fiicnayn\nusername: Isticmaale\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/sr.yml",
    "content": "available: Доступни уређаји\ncancel: Откажи\nconfirm: Потврди\nconfirm_only: Потврдите упаривање\nconfirm_pin: Потврдите да се ПИН подудара\nconnected: Повезано\ndisconnect: Прекини везу\ndisplay_pin: Унесите овај ПИН на уређају\nlowenergy: Ниска енергија\nmore: Још Блуетоотх подешавања\nno_adapter: Није пронађен Блуетоотх адаптер\nnot_found: Није пронађен ниједан уређај\npair: Пар\npaired: Упарено\npairing: Упаривање...\npairing_failed: Упаривање није успело\npassword: Лозинка\nprovide_address: Унесите адресу\nprovide_password: Унесите корисничко име и лозинку\nprovide_pin: Унесите ПИН\nscanning: Скенирање уређаја...\nunpair: Унпаир\nunpair_failed: Опозивање упаривања није успело\nusername: Корисничко име\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/sv.yml",
    "content": "available: Tillgängliga enheter\ncancel: Avboka\nconfirm: Bekräfta\nconfirm_only: Bekräfta ihopparningen\nconfirm_pin: Bekräfta att PIN-koden matchar\nconnected: Ansluten\ndisconnect: Koppla från\ndisplay_pin: Ange denna PIN-kod på enheten\nlowenergy: Låg energi\nmore: Fler Bluetooth-inställningar\nno_adapter: Ingen Bluetooth-adapter hittades\nnot_found: Inga enheter hittades\npair: Par\npaired: Parat\npairing: Parning...\npairing_failed: Det gick inte att koppla ihop\npassword: Lösenord\nprovide_address: Ange adress\nprovide_password: Ange användarnamn och lösenord\nprovide_pin: Ange PIN-kod\nscanning: Söker efter enheter...\nunpair: Ta bort paret\nunpair_failed: Det gick inte att ta bort parkopplingen\nusername: Användarnamn\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/sw.yml",
    "content": "available: Vifaa vinavyopatikana\ncancel: Ghairi\nconfirm: Thibitisha\nconfirm_only: Thibitisha pairing\nconfirm_pin: Thibitisha kuwa pini inalingana\nconnected: Imeunganishwa\ndisconnect: Kukatwa\ndisplay_pin: Ingiza pini hii kwenye kifaa\nlowenergy: Nishati ya chini\nmore: Mipangilio zaidi ya Bluetooth\nno_adapter: Hakuna adapta ya Bluetooth iliyopatikana\nnot_found: Hakuna vifaa vilivyopatikana\npair: Jozi\npaired: Jozi\npairing: Kuogelea ...\npairing_failed: Pairing ilishindwa\npassword: Nenosiri\nprovide_address: Ingiza anwani\nprovide_password: Ingiza jina la mtumiaji na nywila\nprovide_pin: Ingiza pini\nscanning: Skanning kwa vifaa ...\nunpair: Unpair\nunpair_failed: Unpairing ilishindwa\nusername: Jina la mtumiaji\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ta.yml",
    "content": "available: கிடைக்கும் சாதனங்கள்\ncancel: ரத்து செய்\nconfirm: உறுதிப்படுத்தவும்\nconfirm_only: இணைவதை உறுதிப்படுத்தவும்\nconfirm_pin: பின் பொருந்துகிறதா என்பதை உறுதிப்படுத்தவும்\nconnected: இணைக்கப்பட்டது\ndisconnect: துண்டிக்கவும்\ndisplay_pin: சாதனத்தில் இந்த பின்னை உள்ளிடவும்\nlowenergy: குறைந்த ஆற்றல்\nmore: மேலும் புளூடூத் அமைப்புகள்\nno_adapter: புளூடூத் அடாப்டர் இல்லை\nnot_found: சாதனங்கள் எதுவும் இல்லை\npair: ஜோடி\npaired: ஜோடியாக\npairing: இணைத்தல்...\npairing_failed: இணைத்தல் தோல்வியடைந்தது\npassword: கடவுச்சொல்\nprovide_address: முகவரியை உள்ளிடவும்\nprovide_password: பயனர்பெயர் மற்றும் கடவுச்சொல்லை உள்ளிடவும்\nprovide_pin: பின்னை உள்ளிடவும்\nscanning: சாதனங்களை ஸ்கேன் செய்கிறது...\nunpair: ஜோடியை நீக்கவும்\nunpair_failed: இணைப்பை நீக்குவது தோல்வியடைந்தது\nusername: பயனர் பெயர்\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/te.yml",
    "content": "available: అందుబాటులో ఉన్న పరికరాలు\ncancel: రద్దు చేయి\nconfirm: నిర్ధారించండి\nconfirm_only: జత చేయడాన్ని నిర్ధారించండి\nconfirm_pin: పిన్ సరిపోలినట్లు నిర్ధారించండి\nconnected: కనెక్ట్ చేయబడింది\ndisconnect: డిస్‌కనెక్ట్ చేయండి\ndisplay_pin: పరికరంలో ఈ PINని నమోదు చేయండి\nlowenergy: తక్కువ శక్తి\nmore: మరిన్ని బ్లూటూత్ సెట్టింగ్‌లు\nno_adapter: బ్లూటూత్ అడాప్టర్ ఏదీ కనుగొనబడలేదు\nnot_found: పరికరాలు ఏవీ కనుగొనబడలేదు\npair: జత\npaired: జత చేయబడింది\npairing: జత చేస్తోంది...\npairing_failed: జత చేయడం విఫలమైంది\npassword: పాస్వర్డ్\nprovide_address: చిరునామాను నమోదు చేయండి\nprovide_password: వినియోగదారు పేరు మరియు పాస్‌వర్డ్‌ను నమోదు చేయండి\nprovide_pin: PINని నమోదు చేయండి\nscanning: పరికరాల కోసం స్కాన్ చేస్తోంది...\nunpair: జతని తీసివేయండి\nunpair_failed: జతని తీసివేయడం విఫలమైంది\nusername: వినియోగదారు పేరు\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/tg.yml",
    "content": "available: Дастгоҳҳои дастрас\ncancel: Манъ кардан\nconfirm: Тасдищ кардан\nconfirm_only: Тасдиқро тасдиқ кунед\nconfirm_pin: Тасдиқ кунед, ки мувофиқати PIN\nconnected: Пайваст\ndisconnect: Ьудо кардан\ndisplay_pin: Инро дар дастгоҳ ворид кунед\nlowenergy: Энергияи паст\nmore: Танзимоти бештар Bluetooth\nno_adapter: Не адаптерҳои Bluetooth ёфт нашуд\nnot_found: Ягон дастгоҳ ёфт нашуд\npair: Ьуфт\npaired: Ҷуфт карда шуд\npairing: Ҷуфти ...\npairing_failed: Ҷуфт нашуд\npassword: Номи махфӣ\nprovide_address: Суроға ворид кунед\nprovide_password: Номи корбар ва паролро ворид кунед\nprovide_pin: PIN ворид кунед\nscanning: Ҷустуҷӯи дастгоҳҳо ...\nunpair: Возеҳ\nunpair_failed: Unprair ноком шуд\nusername: Номи корбар\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/th.yml",
    "content": "available: อุปกรณ์ที่มีอยู่\ncancel: ยกเลิก\nconfirm: ยืนยัน\nconfirm_only: ยืนยันการจับคู่\nconfirm_pin: ยืนยันว่า PIN ตรงกัน\nconnected: เชื่อมต่อแล้ว\ndisconnect: ตัดการเชื่อมต่อ\ndisplay_pin: ป้อน PIN นี้บนอุปกรณ์\nlowenergy: พลังงานต่ำ\nmore: การตั้งค่าบลูทูธเพิ่มเติม\nno_adapter: ไม่พบอะแดปเตอร์บลูทูธ\nnot_found: ไม่พบอุปกรณ์\npair: คู่\npaired: จับคู่แล้ว\npairing: กำลังจับคู่...\npairing_failed: การจับคู่ล้มเหลว\npassword: รหัสผ่าน\nprovide_address: กรอกที่อยู่\nprovide_password: กรอกชื่อผู้ใช้และรหัสผ่าน\nprovide_pin: ป้อนรหัส PIN\nscanning: กำลังสแกนหาอุปกรณ์...\nunpair: เลิกจับคู่\nunpair_failed: การเลิกจับคู่ล้มเหลว\nusername: ชื่อผู้ใช้\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/tl.yml",
    "content": "available: Mga magagamit na aparato\ncancel: Kanselahin\nconfirm: Kumpirmahin\nconfirm_only: Kumpirmahin ang pagpapares\nconfirm_pin: Kumpirma na ang mga tugma ng PIN\nconnected: Konektado\ndisconnect: Idiskonekta\ndisplay_pin: Ipasok ang pin na ito sa aparato\nlowenergy: Mababang enerhiya\nmore: Marami pang mga setting ng Bluetooth\nno_adapter: Walang nahanap na adapter ng Bluetooth\nnot_found: Walang nahanap na mga aparato\npair: Pares\npaired: Ipinares\npairing: Pagpapares ...\npairing_failed: Nabigo ang pagpapares\npassword: Password\nprovide_address: Ipasok ang address\nprovide_password: Ipasok ang username at password\nprovide_pin: Ipasok ang pin\nscanning: Pag -scan para sa mga aparato ...\nunpair: Walang bayad\nunpair_failed: Nabigo ang walang bayad\nusername: Username\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/tr.yml",
    "content": "available: Mevcut Cihazlar\ncancel: İptal etmek\nconfirm: Onaylamak\nconfirm_only: Eşleştirmeyi onaylayın\nconfirm_pin: PIN'in eşleştiğini doğrulayın\nconnected: Bağlı\ndisconnect: Bağlantıyı kes\ndisplay_pin: Bu PIN'i cihaza girin\nlowenergy: Düşük Enerji\nmore: Daha Fazla Bluetooth Ayarları\nno_adapter: Bluetooth adaptörü bulunamadı\nnot_found: Hiçbir cihaz bulunamadı\npair: Çift\npaired: Eşleştirilmiş\npairing: Eşleştiriliyor...\npairing_failed: Eşleştirme başarısız oldu\npassword: Şifre\nprovide_address: Adresi girin\nprovide_password: Kullanıcı adını ve şifreyi girin\nprovide_pin: PIN'i girin\nscanning: Cihazlar taranıyor...\nunpair: Eşlemeyi kaldır\nunpair_failed: Eşleştirme kaldırılamadı\nusername: Kullanıcı adı\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/uk.yml",
    "content": "available: Доступні пристрої\ncancel: Скасувати\nconfirm: Підтвердити\nconfirm_only: Підтвердьте створення пари\nconfirm_pin: Переконайтеся, що PIN-код збігається\nconnected: Підключено\ndisconnect: Відключити\ndisplay_pin: Введіть цей PIN-код на пристрої\nlowenergy: Низька енергія\nmore: Більше налаштувань Bluetooth\nno_adapter: Адаптер Bluetooth не знайдено\nnot_found: Пристроїв не знайдено\npair: Пара\npaired: Парні\npairing: Створення пари...\npairing_failed: Не вдалося створити пару\npassword: Пароль\nprovide_address: Введіть адресу\nprovide_password: Введіть ім'я користувача та пароль\nprovide_pin: Введіть PIN-код\nscanning: Пошук пристроїв...\nunpair: Розірвати пару\nunpair_failed: Помилка роз’єднання\nusername: Ім'я користувача\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/ur.yml",
    "content": "available: دستیاب آلات\ncancel: منسوخ کریں\nconfirm: تصدیق کریں\nconfirm_only: جوڑی کی تصدیق کریں\nconfirm_pin: تصدیق کریں کہ پن میچ کرتا ہے\nconnected: منسلک\ndisconnect: منقطع\ndisplay_pin: اس پن کو ڈیوائس پر درج کریں\nlowenergy: کم توانائی\nmore: مزید بلوٹوتھ کی ترتیبات\nno_adapter: کوئی بلوٹوتھ اڈاپٹر نہیں ملا\nnot_found: کوئی آلات نہیں ملے\npair: جوڑی\npaired: جوڑا\npairing: جوڑی ...\npairing_failed: جوڑا ناکام ہوگیا\npassword: پاس ورڈ\nprovide_address: ایڈریس درج کریں\nprovide_password: صارف نام اور پاس ورڈ درج کریں\nprovide_pin: پن درج کریں\nscanning: آلات کے لئے اسکیننگ ...\nunpair: انپر\nunpair_failed: انپیرنگ ناکام ہوگئی\nusername: صارف نام\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/uz.yml",
    "content": "available: Mavjud qurilmalar\ncancel: Bekor qilmoq\nconfirm: Tasdiqlamoq\nconfirm_only: Juftlashtirishni tasdiqlang\nconfirm_pin: Pin o'yinlarini tasdiqlang\nconnected: Ulangan\ndisconnect: Ajratmoq\ndisplay_pin: Qurilmada ushbu PIN kodini kiriting\nlowenergy: Kam energiya\nmore: Bluetooth sozlamalari\nno_adapter: Bluetooth adapteri topilmadi\nnot_found: Hech qanday qurilmalar topilmadi\npair: Juftlik\npaired: Juft\npairing: Ulanish ...\npairing_failed: Ulanish muvaffaqiyatsiz tugadi\npassword: Parol\nprovide_address: Manzilni kiriting\nprovide_password: Foydalanuvchi nomi va parolni kiriting\nprovide_pin: PIN kodini kiriting\nscanning: Qurilmalar uchun skanerlash ...\nunpair: Yechmoq\nunpair_failed: Biror ishlamay qolmadi\nusername: Foydalanuvchi nomi\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/vi.yml",
    "content": "available: Thiết bị có sẵn\ncancel: Hủy bỏ\nconfirm: Xác nhận\nconfirm_only: Xác nhận ghép nối\nconfirm_pin: Xác nhận rằng mã PIN khớp\nconnected: Đã kết nối\ndisconnect: Ngắt kết nối\ndisplay_pin: Nhập mã PIN này vào thiết bị\nlowenergy: Năng lượng thấp\nmore: Thêm cài đặt Bluetooth\nno_adapter: Không tìm thấy bộ chuyển đổi Bluetooth\nnot_found: Không tìm thấy thiết bị nào\npair: Đôi\npaired: Đã ghép nối\npairing: Đang ghép nối...\npairing_failed: Ghép nối không thành công\npassword: Mật khẩu\nprovide_address: Nhập địa chỉ\nprovide_password: Nhập tên người dùng và mật khẩu\nprovide_pin: Nhập mã PIN\nscanning: Đang quét tìm thiết bị...\nunpair: Hủy ghép nối\nunpair_failed: Hủy ghép nối không thành công\nusername: Tên người dùng\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/yo.yml",
    "content": "available: Awọn ẹrọ ti o wa\ncancel: Fagilee\nconfirm: Jẹrisi\nconfirm_only: Jẹrisi pọpọ\nconfirm_pin: Jẹrisi pe awọn ere idaraya PIN\nconnected: Sopọ\ndisconnect: Ge kuro\ndisplay_pin: Tẹ PIN sii lori ẹrọ naa\nlowenergy: Agbara kekere\nmore: Awọn eto Bluetooth diẹ sii\nno_adapter: Ko si ti o ni irapada Bluetooth kan ti a rii\nnot_found: Ko si awọn ẹrọ ti o rii\npair: Meji\npaired: Pọ si\npairing: Sipo pọ ...\npairing_failed: Pọpọ\npassword: Ọrọ igbaniwọle\nprovide_address: Tẹ adirẹsi sii\nprovide_password: Tẹ orukọ olumulo ati ọrọ igbaniwọle\nprovide_pin: Tẹ PIN\nscanning: Simuniṣini fun awọn ẹrọ ...\nunpair: Alaiyẹ\nunpair_failed: Unpairating kuna\nusername: Orukọ olumulo\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/zh-CN.yml",
    "content": "available: 可用设备\ncancel: 取消\nconfirm: 确认\nconfirm_only: 确认配对\nconfirm_pin: 确认 PIN 码匹配\nconnected: 已连接\ndisconnect: 断开\ndisplay_pin: 在设备上输入此 PIN 码\nlowenergy: 低能量\nmore: 更多蓝牙设置\nno_adapter: 未找到蓝牙适配器\nnot_found: 未找到设备\npair: 一对\npaired: 配对\npairing: 配对...\npairing_failed: 配对失败\npassword: 密码\nprovide_address: 输入地址\nprovide_password: 输入用户名和密码\nprovide_pin: 输入密码\nscanning: 正在扫描设备...\nunpair: 取消配对\nunpair_failed: 取消配对失败\nusername: 用户名\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/zh-TW.yml",
    "content": "available: 可用設備\ncancel: 取消\nconfirm: 確認\nconfirm_only: 確認配對\nconfirm_pin: 確認 PIN 碼匹配\nconnected: 已連接\ndisconnect: 斷開\ndisplay_pin: 在設備上輸入此 PIN 碼\nlowenergy: 低能量\nmore: 更多藍牙設置\nno_adapter: 未找到藍牙適配器\nnot_found: 未找到設備\npair: 一對\npaired: 配對\npairing: 配對...\npairing_failed: 配對失敗\npassword: 密碼\nprovide_address: 輸入地址\nprovide_password: 輸入用戶名和密碼\nprovide_pin: 輸入密碼\nscanning: 正在掃描設備...\nunpair: 取消配對\nunpair_failed: 取消配對失敗\nusername: 使用者名稱\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/i18n/translations/zu.yml",
    "content": "available: Amadivayisi atholakalayo\ncancel: Hlikihla\nconfirm: Gcizelela\nconfirm_only: Qinisekisa ukubhanqa\nconfirm_pin: Qinisekisa ukuthi i-PIN Matches\nconnected: Ixhunyiwe\ndisconnect: Thukulula\ndisplay_pin: Faka le PIN kudivayisi\nlowenergy: Amandla aphansi\nmore: Izilungiselelo eziningi ze-Bluetooth\nno_adapter: Ayikho i-adaptha ye-Bluetooth etholakele\nnot_found: Awekho amadivayisi atholakele\npair: Okubili\npaired: '-Abanbhanqwaha'\npairing: Ukubhanqa ...\npairing_failed: Ukubhanqa kwehlulekile\npassword: Igama lokuvunyelwa ukungena endaweni ethile\nprovide_address: Faka ikheli\nprovide_password: Faka igama lomsebenzisi nephasiwedi\nprovide_pin: Faka iPin\nscanning: Ukuskena amadivayisi ...\nunpair: Uphuphuthekile\nunpair_failed: Ukungahambi kahle kwehlulekile\nusername: Igama lomsebenzisi\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/icons.ts",
    "content": "import type {\n  BLEAppearance,\n  BLEAppearanceHumanInterfaceDeviceSubCategory,\n  BluetoothAudioVideoMinor,\n  BluetoothComputerMinor,\n  BluetoothDevice,\n  BluetoothHealthMinor,\n  BluetoothMinorClass,\n  BluetoothNetworkMinor,\n  BluetoothPeripheralMinor,\n  BluetoothPeripheralSubMinor,\n  BluetoothPhoneMinor,\n  BluetoothToyMinor,\n  BluetoothWearableMinor,\n} from \"@seelen-ui/lib/types\";\nimport { BluetoothImagingMinor } from \"@seelen-ui/lib/types\";\n\nconst UNKNOWN_ICON = \"TbDeviceUnknown\";\n\ntype IconsDict<T> = Record<Extract<T, string>, string>;\n\nconst COMPUTER_ICONS: IconsDict<BluetoothComputerMinor> = {\n  Uncategorized: \"IoDesktopOutline\",\n  Desktop: \"IoDesktopOutline\",\n  Server: \"IoServerOutline\",\n  Laptop: \"IoLaptopOutline\",\n  Handheld: \"IoMdPhoneLandscape\",\n  PalmSize: \"IoMdPhoneLandscape\",\n  Tablet: \"IoIosTabletPortrait\",\n  Wearable: \"IoMdWatch\",\n};\n\nconst PHONE_ICONS: IconsDict<BluetoothPhoneMinor> = {\n  Uncategorized: \"IoPhonePortraitOutline\",\n  Cellular: \"IoPhonePortraitOutline\",\n  Cordless: \"IoPhonePortraitOutline\",\n  SmartPhone: \"IoPhonePortraitOutline\",\n  Wired: \"BsModem\",\n  Isdn: \"LuPhone\",\n};\n\nconst NETWORK_ICONS: IconsDict<BluetoothNetworkMinor> = {\n  FullyAvailable: \"PiNetwork\",\n  Used01To17Percent: \"PiNetwork\",\n  Used17To33Percent: \"PiNetwork\",\n  Used33To50Percent: \"PiNetwork\",\n  Used50To67Percent: \"PiNetwork\",\n  Used67To83Percent: \"PiNetwork\",\n  Used83To99Percent: \"PiNetwork\",\n  NoServiceAvailable: \"PiNetworkX\",\n};\n\nconst AUDIO_VIDEO_ICONS: IconsDict<BluetoothAudioVideoMinor> = {\n  Uncategorized: \"LuSpeaker\",\n  Headset: \"IoHeadset\",\n  HandsFree: \"IoHeadset\",\n  Microphone: \"HiOutlineMicrophone\",\n  Loudspeaker: \"LuSpeaker\",\n  Headphones: \"IoHeadset\",\n  PortableAudio: \"LuSpeaker\",\n  CarAudio: \"BsPciCardSound\",\n  SetTopBox: \"CgModem\",\n  HiFiAudioDevice: \"IoHeadset\",\n  Vcr: \"TbCapProjecting\",\n  VideoCamera: \"HiOutlineVideoCamera\",\n  CamCorder: \"HiOutlineVideoCamera\",\n  VideoMonitor: \"PiMonitorPlay\",\n  VideoDisplayAndLoudspeaker: \"PiMonitorPlay\",\n  VideoConferencing: \"PiVideoConference\",\n  GamingToy: \"GiGameConsole\",\n};\n\nconst PERIPHERAL_SUBMINOR_ICONS: IconsDict<BluetoothPeripheralSubMinor> = {\n  Uncategorized: \"IoGameControllerOutline\",\n  Joystick: \"LuJoystick\",\n  Gamepad: \"IoGameControllerOutline\",\n  RemoteControl: \"RiRemoteControl2Line\",\n  Sensor: \"MdSensorOccupied\",\n  DigitizerTablet: \"IoTabletLandscapeOutline\",\n  CardReader: \"MdOutlineChromeReaderMode\",\n  DigitalPen: \"IoPencil\",\n  HandheldScanner: \"MdOutlineScanner\",\n  HandheldGestural: \"MdOutlineGesture\",\n};\n\nconst PERIPHERAL_MINOR_ICONS: IconsDict<BluetoothPeripheralMinor> = {\n  Uncategorized: PERIPHERAL_SUBMINOR_ICONS.Uncategorized,\n  Keyboard: \"BsKeyboard\",\n  Pointing: \"BsMouse\",\n  ComboKeyboardPointing: \"BsKeyboard\",\n};\n\nconst WEARABLE_ICONS: IconsDict<BluetoothWearableMinor> = {\n  Wristwatch: \"IoWatchOutline\",\n  Pager: \"FaPager\",\n  Jacket: \"TbJacket\",\n  Helmet: \"GiFullMotorcycleHelmet\",\n  Glasses: \"IoGlassesOutline\",\n  Pin: \"IoMdPricetag\",\n};\n\nconst TOY_ICONS: IconsDict<BluetoothToyMinor> = {\n  Robot: \"RiRobot3Line\",\n  Vehicle: \"FaCar\",\n  Doll: \"LiaBabySolid\",\n  Controller: \"IoGameControllerOutline\",\n  Game: \"CgGames\",\n};\n\nconst HEALTH_ICONS: IconsDict<BluetoothHealthMinor> = {\n  Undefined: \"MdOutlineMedication\",\n  BloodPressureMonitor: \"MdOutlineBloodtype\",\n  Thermometer: \"IoIosThermometer\",\n  WeighingScale: \"FaWeightScale\",\n  GlucoseMeter: \"PiSpeedometerBold\",\n  PulseOximeter: \"BsClipboardPulse\",\n  HeartPulseMonitor: \"BsHeartPulse\",\n  HealthDataDisplay: \"TbHeartRateMonitor\",\n  StepCounter: \"IoFootstepsSharp\",\n  BodyCompositionMonitor: \"IoBodyOutline\",\n  PeakFlowMonitor: \"TbHeartRateMonitor\",\n  MedicationMonitor: \"MdOutlineMedication\",\n  KneeProsthesis: \"GiRobotLeg\",\n  AnkleProsthesis: \"GiMechanicalArm\",\n  GenericHealthManager: \"MdOutlineMedication\",\n  PersonalMobilityDevice: \"FaWheelchair\",\n};\n\nconst FUNC_BY_MAJOR = {\n  Miscellaneous: () => UNKNOWN_ICON,\n  Computer: (minor: BluetoothComputerMinor) => {\n    if (typeof minor !== \"string\") return COMPUTER_ICONS.Uncategorized;\n    return COMPUTER_ICONS[minor];\n  },\n  Phone: (minor: BluetoothPhoneMinor) => {\n    if (typeof minor !== \"string\") return PHONE_ICONS.Uncategorized;\n    return PHONE_ICONS[minor];\n  },\n  NetworkAccessPoint: ([minor, _subminor]: [BluetoothNetworkMinor, string]) => {\n    return NETWORK_ICONS[minor];\n  },\n  AudioVideo: (minor: BluetoothAudioVideoMinor) => {\n    if (typeof minor !== \"string\") return AUDIO_VIDEO_ICONS.Uncategorized;\n    return AUDIO_VIDEO_ICONS[minor];\n  },\n  Peripheral: ([minor, subminor]: [BluetoothPeripheralMinor, BluetoothPeripheralSubMinor]) => {\n    if (typeof subminor === \"string\" && subminor !== \"Uncategorized\") {\n      return PERIPHERAL_SUBMINOR_ICONS[subminor];\n    }\n    return PERIPHERAL_MINOR_ICONS[minor];\n  },\n  Imaging: ([minors, _subminor]: [BluetoothImagingMinor[], string]) => {\n    if (minors.includes(BluetoothImagingMinor.Display)) return \"BsDisplay\";\n    if (\n      minors.includes(BluetoothImagingMinor.Scanner) ||\n      minors.includes(BluetoothImagingMinor.Printer)\n    ) {\n      return \"IoPrintOutline\";\n    }\n    if (minors.includes(BluetoothImagingMinor.Camera)) return \"IoCameraOutline\";\n    return \"IoImagesOutline\";\n  },\n  Wearable: (minor: BluetoothWearableMinor) => {\n    if (typeof minor !== \"string\") return WEARABLE_ICONS.Wristwatch;\n    return WEARABLE_ICONS[minor];\n  },\n  Toy: (minor: BluetoothToyMinor) => {\n    if (typeof minor !== \"string\") return \"LuToyBrick\";\n    return TOY_ICONS[minor];\n  },\n  Health: (minor: BluetoothHealthMinor) => {\n    if (typeof minor !== \"string\") return HEALTH_ICONS.Undefined;\n    return HEALTH_ICONS[minor];\n  },\n  Uncategorized: () => UNKNOWN_ICON,\n};\n\nconst HUMAN_INTERFACE_DEVICE_ICONS: IconsDict<\n  BLEAppearanceHumanInterfaceDeviceSubCategory\n> = {\n  Keyboard: \"BsKeyboard\",\n  Mouse: \"BsMouse\",\n  Joystick: \"LuJoystick\",\n  Gamepad: \"IoGameControllerOutline\",\n  DigitizerTablet: \"IoTabletLandscapeOutline\",\n  CardReader: \"MdOutlineChromeReaderMode\",\n  DigitalPen: \"IoPencil\",\n  BarcodeScanner: \"MdBarcodeReader\",\n  Touchpad: \"LuTouchpad\",\n  PresentationRemote: \"PiVideoConference\",\n};\n\ntype FuncByAppearance = {\n  [key in BLEAppearance[\"category\"]]:\n    | string\n    | ((\n      subcategory: Extract<BLEAppearance, { category: key }>[\"subcategory\"],\n    ) => string);\n};\n\nconst APPEARANCE_ICONS: FuncByAppearance = {\n  Unknown: UNKNOWN_ICON,\n  Phone: \"IoPhonePortraitOutline\",\n  Computer: \"IoDesktopOutline\",\n  Watch: \"IoWatchOutline\",\n  Clock: \"IoTimeOutline\",\n  Display: \"BsDisplay\",\n  RemoteControl: \"RiRemoteControl2Line\",\n  Eyeglasses: \"IoGlassesOutline\",\n  Tag: \"IoMdPricetag\",\n  Keyring: \"IoMdKey\",\n  MediaPlayer: \"PiMonitorPlay\",\n  BarcodeScanner: \"MdOutlineScanner\",\n  Thermometer: \"IoIosThermometer\",\n  HeartRateSensor: \"BsHeartPulse\",\n  BloodPressure: \"MdOutlineBloodtype\",\n  HumanInterfaceDevice: (sub) => {\n    if (typeof sub !== \"string\") return \"BsKeyboard\";\n    return HUMAN_INTERFACE_DEVICE_ICONS[sub];\n  },\n  GlucoseMeter: \"PiSpeedometerBold\",\n  RunningWalkingSensor: \"IoFootstepsSharp\",\n  Cycling: \"MdDirectionsBike\",\n  ControlDevice: \"RiRemoteControl2Line\",\n  NetworkDevice: \"PiNetwork\",\n  Sensor: \"MdSensorOccupied\",\n  LightFixtures: \"IoMdBulb\",\n  Fan: \"BsFan\",\n  HVAC: \"MdAcUnit\",\n  AirConditioning: \"MdAcUnit\",\n  Humidifier: \"TbDroplet\",\n  Heating: \"MdElectricBolt\",\n  AccessControl: \"MdLock\",\n  MotorizedDevice: \"MdOutlinePrecisionManufacturing\",\n  PowerDevice: \"MdPower\",\n  LightSource: \"IoMdBulb\",\n  WindowCovering: \"MdBlinds\",\n  AudioSink: \"LuSpeaker\",\n  AudioSource: \"HiOutlineMicrophone\",\n  MotorizedVehicle: \"FaCar\",\n  DomesticAppliance: \"MdMicrowave\",\n  WearableAudioDevice: \"IoHeadset\",\n  Aircraft: \"FaPlane\",\n  AVEquipment: \"PiMonitorPlay\",\n  DisplayEquipment: \"BsDisplay\",\n  Hearingaid: \"IoEarOutline\",\n  Gaming: \"IoGameControllerOutline\",\n  Signage: \"MdSignpost\",\n  PulseOximeter: \"BsClipboardPulse\",\n  WeightScale: \"FaWeightScale\",\n  PersonalMobilityDevice: \"FaWheelchair\",\n  ContinuousGlucoseMonitor: \"PiSpeedometerBold\",\n  InsulinPump: \"MdOutlineMedication\",\n  MedicationDelivery: \"MdOutlineMedication\",\n  Spirometer: \"GiThermometerScale\",\n  OutdoorSportsActivity: \"MdSportsBaseball\",\n  IndustrialMeasurementDevice: \"MdOutlinePrecisionManufacturing\",\n  IndustrialTools: \"MdBuild\",\n};\n\nexport function getIconByAppearance(appearance: BLEAppearance): string {\n  const icon = APPEARANCE_ICONS[appearance.category];\n  if (typeof icon === \"string\") {\n    return icon;\n  }\n  return icon(appearance.subcategory as any);\n}\n\nexport function getIconForBTDevice(device: BluetoothDevice): string {\n  if (device.appearance) {\n    return getIconByAppearance(device.appearance);\n  }\n\n  const Minor = device.minorClass as any;\n  const Major = Object.keys(Minor)[0]!;\n  const func = (FUNC_BY_MAJOR as any)[Major];\n  return func(Minor[Major]);\n}\n\nexport function getMinorAsString(minor: BluetoothMinorClass): string {\n  const MinorObj = minor as any;\n  const Major = Object.keys(MinorObj)[0]!;\n  const Minor = MinorObj[Major];\n  if (typeof Minor === \"string\") {\n    return Minor;\n  }\n  return JSON.stringify(Minor);\n}\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/bluetooth-popup/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { RadioDeviceKind } from \"@seelen-ui/lib/types\";\nimport { locale } from \"./i18n/index.ts\";\nimport { writable } from \"svelte/store\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nlet widget = Widget.getCurrent();\n\nlet settings = writable(await Settings.getAsync());\nSettings.onChange((s) => settings.set(s));\nsettings.subscribe((settings) => {\n  locale.set(settings.language || \"en\");\n});\n\nlet devices = lazyRune(() => invoke(SeelenCommand.GetBluetoothDevices));\nsubscribe(SeelenEvent.BluetoothDevicesChanged, devices.setByPayload);\n\nlet radios = lazyRune(() => invoke(SeelenCommand.GetRadios));\nsubscribe(SeelenEvent.RadiosChanged, radios.setByPayload);\n\nawait Promise.all([devices.init(), radios.init()]);\n\nlet isScanning = $state(false);\nlet selectedDeviceId = $state<string | null>(null);\n\nwidget.window.onFocusChanged(async (e) => {\n  if (!e.payload) {\n    await invoke(SeelenCommand.StartBluetoothScanning);\n    isScanning = true;\n  } else {\n    await invoke(SeelenCommand.StopBluetoothScanning);\n    isScanning = false;\n    selectedDeviceId = null;\n  }\n});\n\nclass State {\n  get bluetoothRadio() {\n    return radios.value.find((radio) => radio.kind === RadioDeviceKind.Bluetooth);\n  }\n\n  get devices() {\n    return devices.value;\n  }\n\n  get isScanning() {\n    return isScanning;\n  }\n  set isScanning(value: boolean) {\n    isScanning = value;\n  }\n\n  get selectedDeviceId() {\n    return selectedDeviceId;\n  }\n  set selectedDeviceId(value: string | null) {\n    selectedDeviceId = value;\n  }\n}\n\nexport const globalState = new State();\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/app.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"./state.svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import moment from \"moment\";\n\n  const today = moment();\n\n  const lang = $derived(globalState.settings.language || \"en\");\n  const startOfWeek = $derived.by(() => {\n    const startDayMap: Record<string, number> = {\n      Sunday: 0,\n      Monday: 1,\n      Saturday: 6,\n    };\n    const startDay = startDayMap[globalState.settings.startOfWeek] ?? 0;\n    return startDay;\n  });\n\n  // svelte-ignore state_referenced_locally\n  let date = $state(moment().locale(lang));\n  // svelte-ignore state_referenced_locally\n  let selectedDate = $state(moment().locale(lang));\n  $effect(() => {\n    moment.updateLocale(lang, {\n      week: {\n        dow: startOfWeek,\n      },\n    })\n    date = moment().locale(lang);\n    selectedDate = moment().locale(lang);\n  });\n\n  const weekDays = $derived.by(() => {\n    const weekStart = date.clone().startOf(\"week\");\n    return Array.from({ length: 7 }, (_, i) => weekStart.clone().add(i, \"days\").format(\"dd\"));\n  });\n\n  function handlePrevious() {\n    const newDate = date.clone().add(-1, globalState.viewMode === \"month\" ? \"months\" : \"years\");\n    date = newDate;\n  }\n\n  function handleNext() {\n    const newDate = date.clone().add(1, globalState.viewMode === \"month\" ? \"months\" : \"years\");\n    date = newDate;\n  }\n\n  function handleToday() {\n    date = moment().locale(lang);\n    selectedDate = moment().locale(lang);\n  }\n\n  function toggleViewMode() {\n    globalState.viewMode = globalState.viewMode === \"month\" ? \"year\" : \"month\";\n  }\n\n  function handleDateSelect(day: moment.Moment) {\n    selectedDate = day.clone();\n    date = day.clone();\n  }\n\n  function handleMonthSelect(month: moment.Moment) {\n    date = month;\n    globalState.viewMode = \"month\";\n  }\n\n  function handleWheel(e: WheelEvent) {\n    e.preventDefault();\n    e.stopPropagation();\n\n    const isUp = e.deltaY < 0;\n    date = date.clone().add(isUp ? 1 : -1, globalState.viewMode === \"month\" ? \"months\" : \"years\");\n  }\n\n  // Month view data\n  const monthViewData = $derived.by(() => {\n    const startOfMonth = date.clone().startOf(\"month\");\n    const endOfMonth = date.clone().endOf(\"month\");\n    const startDate = startOfMonth.clone().startOf(\"week\");\n    const endDate = endOfMonth.clone().endOf(\"week\");\n\n    const weeks: moment.Moment[][] = [];\n    let currentWeek: moment.Moment[] = [];\n    let currentDate = startDate.clone();\n\n    while (currentDate.isSameOrBefore(endDate, \"day\")) {\n      currentWeek.push(currentDate.clone());\n      if (currentWeek.length === 7) {\n        weeks.push(currentWeek);\n        currentWeek = [];\n      }\n      currentDate.add(1, \"day\");\n    }\n\n    return weeks;\n  });\n\n  // Year view data\n  const yearViewData = $derived.by(() => {\n    const months: moment.Moment[] = [];\n\n    for (let i = 0; i < 12; i++) {\n      months.push(date.clone().month(i).startOf(\"month\"));\n    }\n\n    return months;\n  });\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<div class=\"slu-standard-popover calendar-popup\">\n  <div class=\"calendar\" onwheel={handleWheel}>\n    <!-- Calendar Header -->\n    <div class=\"calendar-header\">\n      <span\n        class=\"calendar-date\"\n        onclick={toggleViewMode}\n        onkeydown={(e) => e.key === \"Enter\" && toggleViewMode()}\n        role=\"button\"\n        tabindex=\"0\"\n      >\n        {globalState.viewMode === \"month\" ? date.format(\"MMMM YYYY\") : date.format(\"YYYY\")}\n      </span>\n      <div class=\"calendar-actions\">\n        <button class=\"calendar-navigator\" onclick={handlePrevious}>\n          <Icon iconName=\"AiOutlineLeft\" />\n        </button>\n        <button class=\"calendar-navigator\" onclick={handleToday}>\n          <Icon iconName=\"AiOutlineHome\" />\n        </button>\n        <button class=\"calendar-navigator\" onclick={handleNext}>\n          <Icon iconName=\"AiOutlineRight\" />\n        </button>\n      </div>\n    </div>\n\n    {#if globalState.viewMode === \"month\"}\n      <!-- Month View -->\n      <div class=\"calendar-month-view\">\n        <div class=\"calendar-weekdays\">\n          {#each weekDays as day}\n            <div class=\"calendar-weekday\">{day}</div>\n          {/each}\n        </div>\n\n        <div class=\"calendar-days\">\n          {#each monthViewData as week}\n            <div class=\"calendar-week\">\n              {#each week as day}\n                {@const isToday = day.isSame(today, \"day\")}\n                {@const isSelected = day.isSame(selectedDate, \"day\")}\n                {@const isOffMonth = day.month() !== date.month()}\n                <div\n                  class=\"calendar-cell\"\n                  class:calendar-cell-today={isToday}\n                  class:calendar-cell-selected={isSelected}\n                  class:calendar-cell-off-month={isOffMonth}\n                  onclick={() => handleDateSelect(day)}\n                  onkeydown={(e) => e.key === \"Enter\" && handleDateSelect(day)}\n                  role=\"button\"\n                  tabindex=\"0\"\n                >\n                  {day.format(\"D\")}\n                </div>\n              {/each}\n            </div>\n          {/each}\n        </div>\n      </div>\n    {:else}\n      <!-- Year View -->\n      <div class=\"calendar-year-view\">\n        {#each yearViewData as month}\n          {@const isCurrentMonth = month.isSame(today, \"month\")}\n          <div\n            class=\"calendar-month-cell\"\n            class:calendar-month-cell-current={isCurrentMonth}\n            onclick={() => handleMonthSelect(month)}\n            onkeydown={(e) => e.key === \"Enter\" && handleMonthSelect(month)}\n            role=\"button\"\n            tabindex=\"0\"\n          >\n            {month.format(\"MMMM\")}\n          </div>\n        {/each}\n      </div>\n    {/if}\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/af.yml",
    "content": "month_view: Maandaansig\ntoday: Vandag\nyear_view: Jaaruitsig\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/am.yml",
    "content": "month_view: ወር እይታ\ntoday: ዛሬ\nyear_view: የዓመት እይታ\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ar.yml",
    "content": "month_view: عرض الشهر\ntoday: اليوم\nyear_view: عرض السنة\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/az.yml",
    "content": "month_view: Ay Görünüşü\ntoday: Bu gün\nyear_view: İl Görünüşü\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/bg.yml",
    "content": "month_view: Месечен изглед\ntoday: Днес\nyear_view: Годишен изглед\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/bn.yml",
    "content": "month_view: মাস ভিউ\ntoday: আজ\nyear_view: বছরের ভিউ\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/bs.yml",
    "content": "month_view: Month View\ntoday: Danas\nyear_view: Year View\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ca.yml",
    "content": "month_view: Vista mensual\ntoday: Avui\nyear_view: Vista de l'any\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/cs.yml",
    "content": "month_view: Zobrazení měsíce\ntoday: Dnes\nyear_view: Pohled na rok\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/cy.yml",
    "content": "month_view: Golwg Mis\ntoday: Heddiw\nyear_view: Golwg Blwyddyn\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/da.yml",
    "content": "month_view: Månedsvisning\ntoday: I dag\nyear_view: Årsvisning\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/de.yml",
    "content": "month_view: Monatsansicht\ntoday: Heute\nyear_view: Jahresansicht\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/el.yml",
    "content": "month_view: Προβολή μήνα\ntoday: Σήμερα\nyear_view: Προβολή έτους\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/en.yml",
    "content": "month_view: Month View\ntoday: Today\nyear_view: Year View\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/es.yml",
    "content": "month_view: Vista mensual\ntoday: Hoy\nyear_view: Vista del año\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/et.yml",
    "content": "month_view: Kuu vaade\ntoday: Täna\nyear_view: Aastavaade\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/eu.yml",
    "content": "month_view: Hileko ikuspegia\ntoday: Gaur\nyear_view: Urteko ikuspegia\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/fa.yml",
    "content": "month_view: نمای ماه\ntoday: امروز\nyear_view: نمای سال\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/fi.yml",
    "content": "month_view: Kuukausinäkymä\ntoday: Tänään\nyear_view: Vuosinäkymä\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/fr.yml",
    "content": "month_view: Vue mensuelle\ntoday: Aujourd'hui\nyear_view: Vue annuelle\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/gu.yml",
    "content": "month_view: મહિનો દૃશ્ય\ntoday: આજે\nyear_view: વર્ષ દૃશ્ય\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/he.yml",
    "content": "month_view: תצוגת חודש\ntoday: הַיוֹם\nyear_view: תצוגת שנה\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/hi.yml",
    "content": "month_view: माह दृश्य\ntoday: आज\nyear_view: वर्ष दृश्य\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/hr.yml",
    "content": "month_view: Mjesečni prikaz\ntoday: Danas\nyear_view: Pregled godine\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/hu.yml",
    "content": "month_view: Havi nézet\ntoday: Ma\nyear_view: Éves nézet\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/hy.yml",
    "content": "month_view: Ամսվա դիտում\ntoday: Այսօր\nyear_view: Տարվա դիտում\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/id.yml",
    "content": "month_view: Tampilan Bulan\ntoday: Hari ini\nyear_view: Tampilan Tahun\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/is.yml",
    "content": "month_view: Mánaðarsýn\ntoday: Í dag\nyear_view: Árssýn\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/it.yml",
    "content": "month_view: Visualizzazione mensile\ntoday: Oggi\nyear_view: Vista anno\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ja.yml",
    "content": "month_view: 月ごとの表示\ntoday: 今日\nyear_view: 年ごとの表示\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ka.yml",
    "content": "month_view: თვის ხედი\ntoday: დღეს\nyear_view: წლის ხედი\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/km.yml",
    "content": "month_view: ទិដ្ឋភាពខែ\ntoday: ថ្ងៃនេះ\nyear_view: ទិដ្ឋភាពឆ្នាំ\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ko.yml",
    "content": "month_view: 월별 보기\ntoday: 오늘\nyear_view: 연도 보기\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ku.yml",
    "content": "month_view: Dîtina meha\ntoday: Îro\nyear_view: Sala Nêrîn\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/lb.yml",
    "content": "month_view: Mount View\ntoday: Haut\nyear_view: Joer Vue\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/lo.yml",
    "content": "month_view: ເດືອນເບິ່ງ\ntoday: ມື້ນີ້\nyear_view: ປີເບິ່ງ\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/lt.yml",
    "content": "month_view: Mėnesio vaizdas\ntoday: Šiandien\nyear_view: Metų vaizdas\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/lv.yml",
    "content": "month_view: Mēneša skats\ntoday: Šodien\nyear_view: Gada skats\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/mk.yml",
    "content": "month_view: Месечен преглед\ntoday: Денес\nyear_view: Годишен поглед\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/mn.yml",
    "content": "month_view: Сар харах\ntoday: Өнөөдөр\nyear_view: Жилийн харах\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ms.yml",
    "content": "month_view: Pandangan Bulan\ntoday: Hari ini\nyear_view: Pandangan Tahun\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/mt.yml",
    "content": "month_view: Veduta tax-Xahar\ntoday: Illum\nyear_view: Veduta tas-Sena\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ne.yml",
    "content": "month_view: महिना दृश्य\ntoday: आज\nyear_view: वर्ष दृश्य\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/nl.yml",
    "content": "month_view: Maandweergave\ntoday: Vandaag\nyear_view: Jaaroverzicht\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/no.yml",
    "content": "month_view: Månedsvisning\ntoday: I dag\nyear_view: Årsvisning\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/pa.yml",
    "content": "month_view: ਮਹੀਨਾ ਦ੍ਰਿਸ਼\ntoday: ਅੱਜ\nyear_view: ਸਾਲ ਦਾ ਦ੍ਰਿਸ਼\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/pl.yml",
    "content": "month_view: Widok miesiąca\ntoday: Dzisiaj\nyear_view: Widok roku\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ps.yml",
    "content": "month_view: میاشت لید\ntoday: نن\nyear_view: د کال لید\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/pt-BR.yml",
    "content": "month_view: Visualização mensal\ntoday: Hoje\nyear_view: Visualização do ano\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/pt-PT.yml",
    "content": "month_view: Visualização mensal\ntoday: Hoje\nyear_view: Visualização do ano\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ro.yml",
    "content": "month_view: Vizualizare lunară\ntoday: Astăzi\nyear_view: Vizualizare an\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ru.yml",
    "content": "month_view: Просмотр месяца\ntoday: Сегодня\nyear_view: Годовой обзор\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/si.yml",
    "content": "month_view: මාසික දැක්ම\ntoday: අද\nyear_view: වසර දසුන\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/sk.yml",
    "content": "month_view: Zobrazenie mesiaca\ntoday: Dnes\nyear_view: Pohľad na rok\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/so.yml",
    "content": "month_view: Muuqaalka Bisha\ntoday: Maanta\nyear_view: Muuqaalka Sannadka\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/sr.yml",
    "content": "month_view: Монтх Виев\ntoday: данас\nyear_view: Иеар Виев\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/sv.yml",
    "content": "month_view: Månadsvy\ntoday: I dag\nyear_view: Årsvy\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/sw.yml",
    "content": "month_view: Mtazamo wa Mwezi\ntoday: Leo\nyear_view: Mtazamo wa Mwaka\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ta.yml",
    "content": "month_view: மாதப் பார்வை\ntoday: இன்று\nyear_view: ஆண்டு பார்வை\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/te.yml",
    "content": "month_view: నెల వీక్షణ\ntoday: ఈరోజు\nyear_view: సంవత్సరం వీక్షణ\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/tg.yml",
    "content": "month_view: Намоиши моҳона\ntoday: Имруз\nyear_view: Намоиши сол\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/th.yml",
    "content": "month_view: มุมมองเดือน\ntoday: วันนี้\nyear_view: มุมมองรายปี\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/tl.yml",
    "content": "month_view: View ng Buwan\ntoday: Ngayong araw\nyear_view: View ng Taon\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/tr.yml",
    "content": "month_view: Ay Görünümü\ntoday: Bugün\nyear_view: Yıl Görünümü\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/uk.yml",
    "content": "month_view: Перегляд місяця\ntoday: Сьогодні\nyear_view: Перегляд року\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/ur.yml",
    "content": "month_view: مہینہ کا نظارہ\ntoday: آج\nyear_view: سال کا نظارہ\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/uz.yml",
    "content": "month_view: Oy ko'rinishi\ntoday: Bugun\nyear_view: Yil ko'rinishi\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/vi.yml",
    "content": "month_view: Xem tháng\ntoday: Hôm nay\nyear_view: Xem năm\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/yo.yml",
    "content": "month_view: Wiwo oṣu\ntoday: Loni\nyear_view: Wiwo Ọdun\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/zh-CN.yml",
    "content": "month_view: 月视图\ntoday: 今天\nyear_view: 年视图\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/zh-TW.yml",
    "content": "month_view: 月視圖\ntoday: 今天\nyear_view: 年視圖\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/i18n/translations/zu.yml",
    "content": "month_view: Ukubuka Kwenyanga\ntoday: Namuhla\nyear_view: Ukubuka Konyaka\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\nimport \"moment/min/locales\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/calendar-popup/state.svelte.ts",
    "content": "import { Settings } from \"@seelen-ui/lib\";\nimport type { Settings as SettingsType } from \"@seelen-ui/lib/types\";\nimport { locale } from \"./i18n/index.ts\";\nimport moment from \"moment\";\n\nlet settings = $state<SettingsType>(await Settings.getAsync());\nSettings.onChange((s) => (settings = s));\n\n// Local reactive state\nlet viewMode = $state<\"month\" | \"year\">(\"month\");\n\nconst momentJsLangMap: { [key: string]: string } = {\n  no: \"nb\",\n  zh: \"zh-cn\",\n};\n\n$effect.root(() => {\n  $effect(() => {\n    const lang = settings.language || \"en\";\n    locale.set(lang);\n\n    const language = momentJsLangMap[lang] || lang;\n\n    // Update the start of week based on settings\n    const startDayMap: Record<string, number> = {\n      Sunday: 0,\n      Monday: 1,\n      Saturday: 6,\n    };\n    const startDay = startDayMap[settings.startOfWeek] ?? 0;\n\n    moment.updateLocale(language, {\n      week: {\n        dow: startDay,\n      },\n    });\n  });\n});\n\nclass State {\n  get viewMode() {\n    return viewMode;\n  }\n  set viewMode(value: \"month\" | \"year\") {\n    viewMode = value;\n  }\n  get settings() {\n    return settings;\n  }\n}\n\nexport const globalState = new State();\n"
  },
  {
    "path": "src/ui/svelte/context-menu/MenuItem.svelte",
    "content": "<script lang=\"ts\">\n  import { Widget } from \"@seelen-ui/lib\";\n  import type { ContextMenuItem } from \"@seelen-ui/lib/types\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { emitTo } from \"@tauri-apps/api/event\";\n  import { state as gState } from \"./state.svelte\";\n\n  interface MenuItemProps {\n    item: Extract<ContextMenuItem, { type: \"Item\" }>;\n  }\n\n  let { item }: MenuItemProps = $props();\n\n  // Optimistic state for checked\n  let internalChecked = $state(false);\n\n  $effect.pre(() => {\n    internalChecked = item.checked!!;\n  });\n\n  function handleClick() {\n    let target = gState.forwardTo || gState.owner;\n    if (item.disabled || !target) {\n      return;\n    }\n\n    if (item.checked !== null) {\n      // Toggle optimistic state\n      internalChecked = !internalChecked;\n      // Emit with the new checked value\n      emitTo(target, item.callbackEvent, { key: item.key, checked: internalChecked });\n    } else {\n      emitTo(target, item.callbackEvent, { key: item.key });\n      Widget.self.hide(true);\n    }\n  }\n</script>\n\n<button class=\"menu-item\" disabled={item.disabled} data-skin=\"transparent\" onclick={handleClick}>\n  {#if item.checked !== null}\n    <input type=\"checkbox\" checked={internalChecked} />\n  {/if}\n  <Icon iconName={item.icon as any} />\n  <span class=\"menu-item-label\">{item.label}</span>\n</button>\n"
  },
  {
    "path": "src/ui/svelte/context-menu/Submenu.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import type { ContextMenuItem } from \"@seelen-ui/lib/types\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { state } from \"./state.svelte\";\n\n  interface SubmenuProps {\n    item: Extract<ContextMenuItem, { type: \"Submenu\" }>;\n  }\n\n  let { item }: SubmenuProps = $props();\n\n  function handleClick() {\n    invoke(SeelenCommand.TriggerContextMenu, {\n      menu: {\n        identifier: item.identifier,\n        items: item.items,\n        alignX: state.data?.alignX,\n        alignY: state.data?.alignY,\n      },\n      forwardTo: state.forwardTo || state.owner,\n    });\n  }\n</script>\n\n<button class=\"menu-item\" data-skin=\"transparent\" onclick={handleClick}>\n  <Icon iconName={item.icon as any} />\n  <span class=\"menu-item-label\">{item.label}</span>\n  <Icon class=\"menu-item-chevron\" iconName=\"FaChevronRight\" />\n</button>\n"
  },
  {
    "path": "src/ui/svelte/context-menu/app.svelte",
    "content": "<script lang=\"ts\">\n  import { Widget } from \"@seelen-ui/lib\";\n  import { state } from \"./state.svelte\";\n  import MenuItem from \"./MenuItem.svelte\";\n  import Submenu from \"./Submenu.svelte\";\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<div class=\"slu-standard-popover context-menu\">\n  {#each state.data?.items || [] as item}\n    {#if item.type === \"Separator\"}\n      <hr />\n    {:else if item.type === \"Item\"}\n      <MenuItem {item} />\n    {:else if item.type === \"Submenu\"}\n      <Submenu {item} />\n    {/if}\n  {/each}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/context-menu/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nawait Widget.self.init({\n  autoSizeByContent: root,\n});\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/context-menu/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/context-menu/state.svelte.ts",
    "content": "import { Widget } from \"@seelen-ui/lib\";\nimport type { ContextMenu } from \"@seelen-ui/lib/types\";\n\nlet data = $state<ContextMenu | null>(null);\nlet owner = $state<string | null>(null);\nlet forwardTo = $state<string | null>(null);\n\nWidget.self.onTrigger(({ customArgs }) => {\n  data = (customArgs?.menu as any) || null;\n  owner = (customArgs?.owner as any) || null;\n  forwardTo = (customArgs?.forwardTo as any) || null;\n});\n\nclass State {\n  get data() {\n    return data;\n  }\n\n  get owner() {\n    return owner;\n  }\n\n  get forwardTo() {\n    return forwardTo;\n  }\n}\n\nexport const state = new State();\n"
  },
  {
    "path": "src/ui/svelte/flyouts/app/Brightness.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { brightnessIcon } from \"libs/ui/utils\";\n  import { throttle } from \"lodash\";\n  import type { MonitorBrightness } from \"@seelen-ui/lib/types\";\n\n  interface Props {\n    brightness: MonitorBrightness;\n    orientation: string;\n  }\n\n  let { brightness, orientation }: Props = $props();\n\n  const setBrightnessThrottled = throttle((instanceName: string, level: number) => {\n    invoke(SeelenCommand.SetMonitorBrightness, { instanceName, level });\n  }, 100);\n</script>\n\n<div class=\"brightness\">\n  <Icon iconName={brightnessIcon(brightness.currentBrightness)} />\n  <input\n    type=\"range\"\n    data-skin=\"flat\"\n    data-orientation={orientation}\n    value={brightness.currentBrightness}\n    oninput={(e) => {\n      setBrightnessThrottled(brightness.instanceName, Number(e.currentTarget.value));\n    }}\n    min={brightness.availableLevels[0]}\n    max={brightness.availableLevels[brightness.levels]}\n  />\n</div>\n"
  },
  {
    "path": "src/ui/svelte/flyouts/app/MediaDevices.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { outputVolumeIcon } from \"libs/ui/utils\";\n  import { throttle } from \"lodash\";\n  import type { MediaDevice } from \"@seelen-ui/lib/types\";\n\n  interface Props {\n    output: MediaDevice;\n    orientation: string;\n  }\n\n  let { output, orientation }: Props = $props();\n\n  const setVolumeThrottled = throttle((deviceId: string, level: number) => {\n    invoke(SeelenCommand.SetVolumeLevel, { deviceId, sessionId: null, level });\n  }, 100);\n\n  function toggleMute(deviceId: string) {\n    invoke(SeelenCommand.MediaToggleMute, { deviceId, sessionId: null });\n  }\n</script>\n\n<div class=\"volume\">\n  <Icon\n    iconName={outputVolumeIcon(output.muted, output.volume)}\n    onclick={() => toggleMute(output.id)}\n  />\n  <input\n    type=\"range\"\n    data-skin=\"flat\"\n    data-orientation={orientation}\n    value={output.volume}\n    oninput={(e) => {\n      setVolumeThrottled(output.id, Number(e.currentTarget.value));\n    }}\n    min={0}\n    max={1}\n    step={0.01}\n  />\n</div>\n"
  },
  {
    "path": "src/ui/svelte/flyouts/app/MediaPlaying.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon, SpecificIcon } from \"libs/ui/svelte/components/Icon\";\n  import { nanosecondsToPlayingTime } from \"libs/ui/utils\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import type { MediaPlayer } from \"@seelen-ui/lib/types\";\n\n  interface Props {\n    playing: MediaPlayer;\n  }\n\n  let { playing }: Props = $props();\n</script>\n\n<div class=\"player\">\n  <div class=\"player-thumbnail-container\">\n    {#if playing.thumbnail}\n      <img src={convertFileSrc(playing.thumbnail)} alt=\"\" />\n    {:else}\n      <SpecificIcon name=\"defaultPlayerThumbnail\" />\n    {/if}\n  </div>\n\n  <div class=\"player-info\">\n    <div class=\"player-title\">{playing.title}</div>\n    <div class=\"player-author\">{playing.author}</div>\n    <div class=\"player-timeline\">\n      <span>{nanosecondsToPlayingTime(playing.timeline.position as any)}</span>\n      <span>/</span>\n      <span>{nanosecondsToPlayingTime(playing.timeline.end as any)}</span>\n    </div>\n  </div>\n\n  <div class=\"player-controls\">\n    <button\n      data-skin=\"transparent\"\n      onclick={() => invoke(SeelenCommand.MediaPrev, { id: playing.umid })}\n    >\n      <Icon iconName=\"IoPlaySkipBack\" />\n    </button>\n    <button\n      data-skin=\"transparent\"\n      onclick={() => invoke(SeelenCommand.MediaTogglePlayPause, { id: playing.umid })}\n    >\n      <Icon iconName={playing.playing ? \"IoPause\" : \"IoPlay\"} />\n    </button>\n    <button\n      data-skin=\"transparent\"\n      onclick={() => invoke(SeelenCommand.MediaNext, { id: playing.umid })}\n    >\n      <Icon iconName=\"IoPlaySkipForward\" />\n    </button>\n  </div>\n\n  <progress\n    class=\"player-progress\"\n    value={playing.timeline.position as any}\n    max={playing.timeline.end as any}\n  >\n    <span>{nanosecondsToPlayingTime(playing.timeline.position as any)}</span>\n    <span>/</span>\n    <span>{nanosecondsToPlayingTime(playing.timeline.end as any)}</span>\n  </progress>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/flyouts/app/Workspace.svelte",
    "content": "<script lang=\"ts\">\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import type { DesktopWorkspace } from \"@seelen-ui/lib/types\";\n\n  interface Props {\n    workspace: DesktopWorkspace & { idx: number };\n  }\n\n  let { workspace }: Props = $props();\n</script>\n\n<div class=\"workspace\">\n  <span class=\"workspace-name\">\n    {workspace.name || `Workspace ${workspace.idx + 1}`}\n  </span>\n  <Icon iconName={(workspace.icon as any) || \"PiMonitorBold\"} />\n</div>\n"
  },
  {
    "path": "src/ui/svelte/flyouts/app.svelte",
    "content": "<script lang=\"ts\">\n  import { Widget } from \"@seelen-ui/lib\";\n  import { state as gState } from \"./state/mod.svelte\";\n  import { RendererState, setShowing } from \"./state/placement.svelte\";\n  import { ConfigState } from \"./state/config.svelte\";\n  import { debounce } from \"lodash\";\n  import Notification from \"../notifications/components/Notification.svelte\";\n  import Workspace from \"./app/Workspace.svelte\";\n  import MediaDevices from \"./app/MediaDevices.svelte\";\n  import MediaPlaying from \"./app/MediaPlaying.svelte\";\n  import Brightness from \"./app/Brightness.svelte\";\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n\n  let lastChanged = $state<string | null>(null);\n  let orientation = $derived(\n    [\"left\", \"right\"].includes(ConfigState.config.placement) ? \"vertical\" : \"horizontal\",\n  );\n\n  let output = $derived.by(() => {\n    return gState.mediaOutputs.find((o) => o.isDefaultMultimedia);\n  });\n\n  let recomendedPlayer = $derived.by(() => {\n    return gState.mediaPlaying.find((p) => p.default);\n  });\n\n  let vd = $derived.by(() => {\n    if (RendererState.primary) {\n      return gState.workspaces.monitors[RendererState.primary.id] || null;\n    }\n    return null;\n  });\n\n  let activeWorkspaceData = $derived.by(() => {\n    return vd?.workspaces\n      .map((workspace, idx) => ({ ...workspace, idx }))\n      .find((workspace) => workspace.id == vd.active_workspace);\n  });\n\n  // Use [0] (newest) instead of .pop() (oldest) on a descending-sorted array.\n  let notification = $derived.by(\n    () => gState.notifications.toSorted((a, b) => Number(b.date - a.date))[0],\n  );\n\n  let notificationId = $derived(notification?.id);\n  let volume = $derived(output?.volume || 0);\n  let playingTitle = $derived(recomendedPlayer?.title);\n  let brightnessLevel = $derived(gState.brightness?.currentBrightness);\n  let activeWorkspace = $derived(vd?.active_workspace);\n\n  // svelte-ignore state_referenced_locally\n  const prev = {\n    volume,\n    playingTitle,\n    brightnessLevel,\n    activeWorkspace,\n    notificationId,\n  };\n\n  const hideWithDelay = $derived(\n    debounce(() => {\n      setShowing(false);\n    }, ConfigState.config.timeToShow * 1000),\n  );\n\n  $effect(() => {\n    let somethingChanged = false;\n\n    // Guard with `output` so we never show an empty flyout when the device is removed.\n    if (\n      ConfigState.config.showVolumeChange &&\n      prev.volume.toFixed(2) !== volume.toFixed(2) &&\n      output\n    ) {\n      lastChanged = \"mediaDevices\";\n      somethingChanged = true;\n    }\n\n    // Guard with `playing` so we never show an empty flyout when the player is closed.\n    if (\n      ConfigState.config.showMediaPlayerChange &&\n      prev.playingTitle !== playingTitle &&\n      recomendedPlayer\n    ) {\n      lastChanged = \"mediaPlaying\";\n      somethingChanged = true;\n    }\n\n    // Guard with `gState.brightness` so we never show an empty flyout when brightness is unavailable.\n    if (\n      ConfigState.config.showBrightnessChange &&\n      prev.brightnessLevel !== brightnessLevel &&\n      gState.brightness\n    ) {\n      lastChanged = \"brightness\";\n      somethingChanged = true;\n    }\n\n    // Guard with `activeWorkspaceData` so we never show an empty flyout on workspace edge cases.\n    if (\n      ConfigState.config.showWorkspaceChange &&\n      prev.activeWorkspace !== activeWorkspace &&\n      activeWorkspaceData\n    ) {\n      lastChanged = \"workspace\";\n      somethingChanged = true;\n    }\n\n    // Guard with `notification` so we never show an empty flyout when the list becomes empty.\n    if (\n      ConfigState.config.showNotifications &&\n      prev.notificationId !== notificationId &&\n      notification\n    ) {\n      lastChanged = \"notification\";\n      somethingChanged = true;\n    }\n\n    if (somethingChanged) {\n      setShowing(true);\n      hideWithDelay();\n    }\n\n    // Hide immediately when the currently displayed flyout loses its data source.\n    if (\n      (lastChanged === \"mediaPlaying\" && !recomendedPlayer) ||\n      (lastChanged === \"notification\" && !notification)\n    ) {\n      hideWithDelay.cancel();\n      setShowing(false);\n      lastChanged = null;\n    }\n\n    prev.volume = volume;\n    prev.playingTitle = playingTitle;\n    prev.brightnessLevel = brightnessLevel;\n    prev.activeWorkspace = activeWorkspace;\n    prev.notificationId = notificationId;\n  });\n</script>\n\n<div\n  class=\"flyout\"\n  data-placement={ConfigState.config.placement}\n  data-showing={RendererState.showing}\n>\n  {#if lastChanged === \"notification\" && notification}\n    <Notification {notification} />\n  {/if}\n\n  {#if lastChanged === \"workspace\" && activeWorkspaceData}\n    <Workspace workspace={activeWorkspaceData} />\n  {/if}\n\n  {#if lastChanged === \"mediaDevices\" && output}\n    <MediaDevices {output} {orientation} />\n  {/if}\n\n  {#if lastChanged === \"brightness\" && gState.brightness}\n    <Brightness brightness={gState.brightness} {orientation} />\n  {/if}\n\n  {#if lastChanged === \"mediaPlaying\" && recomendedPlayer}\n    <MediaPlaying playing={recomendedPlayer} />\n  {/if}\n</div>\n\n<style>\n  .flyout[data-showing=\"false\"] {\n    opacity: 0;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/flyouts/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nawait Widget.self.init({\n  autoSizeByContent: root,\n});\nawait Widget.self.window.setFocusable(false);\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/flyouts/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/flyouts/state/config.svelte.ts",
    "content": "import { Settings, Widget } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\nimport z from \"zod\";\n\nlet settings = lazyRune(() => Settings.getAsync());\nawait Settings.onChange((s) => (settings.value = s));\nawait settings.init();\n\nconst WidgetConfigSchema = z.object({\n  placement: z.string(),\n  margin: z.number(),\n  timeToShow: z.number(),\n  showVolumeChange: z.boolean(),\n  showBrightnessChange: z.boolean(),\n  showMediaPlayerChange: z.boolean(),\n  showWorkspaceChange: z.boolean(),\n  showNotifications: z.boolean(),\n});\n\nconst widgetConfig = $derived.by(\n  () =>\n    WidgetConfigSchema.safeParse(settings.value.getCurrentWidgetConfig()).data ??\n      (Widget.self.getDefaultConfig() as unknown as z.infer<typeof WidgetConfigSchema>),\n);\n\nexport const ConfigState = {\n  get config() {\n    return widgetConfig;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/flyouts/state/mod.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\n\nlet mediaDevices = lazyRune(() => invoke(SeelenCommand.GetMediaDevices));\nsubscribe(SeelenEvent.MediaDevices, mediaDevices.setByPayload);\n\nlet mediaPlaying = lazyRune(() => invoke(SeelenCommand.GetMediaSessions));\nsubscribe(SeelenEvent.MediaSessions, mediaPlaying.setByPayload);\n\nlet brightness = lazyRune(() => invoke(SeelenCommand.GetAllMonitorsBrightness));\nsubscribe(SeelenEvent.SystemMonitorsBrightnessChanged, brightness.setByPayload);\n\nlet workspaces = lazyRune(() => invoke(SeelenCommand.StateGetVirtualDesktops));\nsubscribe(SeelenEvent.VirtualDesktopsChanged, workspaces.setByPayload);\n\nlet notifications = lazyRune(() => invoke(SeelenCommand.GetNotifications));\nsubscribe(SeelenEvent.Notifications, notifications.setByPayload);\n\nawait Promise.all([\n  mediaDevices.init(),\n  mediaPlaying.init(),\n  brightness.init(),\n  workspaces.init(),\n  notifications.init(),\n]);\n\nexport const state = {\n  get mediaInputs() {\n    return mediaDevices.value[0];\n  },\n  get mediaOutputs() {\n    return mediaDevices.value[1];\n  },\n  get mediaPlaying() {\n    return mediaPlaying.value;\n  },\n  get brightness() {\n    return brightness.value[0] || null;\n  },\n  get workspaces() {\n    return workspaces.value;\n  },\n  get notifications() {\n    return notifications.value;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/flyouts/state/placement.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\nimport { ConfigState } from \"./config.svelte\";\nimport { PhysicalPosition } from \"@tauri-apps/api/window\";\n\nlet showing = $state(false);\n\nconst monitors = lazyRune(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\n\nconst widgetSize = lazyRune(() => Widget.self.window.outerSize());\nawait Widget.self.window.onResized(widgetSize.setByPayload);\n\nawait Promise.all([monitors.init(), widgetSize.init()]);\n\nconst primaryMonitor = $derived.by(() => {\n  return monitors.value.find((m) => m.isPrimary) || monitors.value[0];\n});\n\n$effect.root(() => {\n  $effect(() => {\n    const monitor = primaryMonitor;\n    if (!monitor) {\n      return;\n    }\n\n    const placement = ConfigState.config.placement;\n    const padding = ConfigState.config.margin * window.devicePixelRatio;\n\n    const monitorWidth = monitor.rect.right - monitor.rect.left;\n    const monitorHeight = monitor.rect.bottom - monitor.rect.top;\n\n    const monitorCenterX = monitor.rect.left + monitorWidth / 2;\n    const monitorCenterY = monitor.rect.top + monitorHeight / 2;\n\n    const { width, height } = widgetSize.value;\n\n    let x: number, y: number;\n    if (placement === \"left\") {\n      x = monitor.rect.left + padding;\n      y = Math.round(monitorCenterY - height / 2);\n    } else if (placement === \"top\") {\n      x = Math.round(monitorCenterX - width / 2);\n      y = monitor.rect.top + padding;\n    } else if (placement === \"right\") {\n      x = monitor.rect.right - width - padding;\n      y = Math.round(monitorCenterY - height / 2);\n    } else if (placement === \"top-left\") {\n      x = monitor.rect.left + padding;\n      y = monitor.rect.top + padding;\n    } else if (placement === \"top-right\") {\n      x = monitor.rect.right - width - padding;\n      y = monitor.rect.top + padding;\n    } else if (placement === \"bottom-left\") {\n      x = monitor.rect.left + padding;\n      y = monitor.rect.bottom - height - padding;\n    } else if (placement === \"bottom-right\") {\n      x = monitor.rect.right - width - padding;\n      y = monitor.rect.bottom - height - padding;\n    } else {\n      x = Math.round(monitorCenterX - width / 2);\n      y = monitor.rect.bottom - height - padding;\n    }\n\n    Widget.self.window.setPosition(new PhysicalPosition(Math.round(x), Math.round(y)));\n  });\n});\n\nexport function setShowing(show: boolean) {\n  if (showing != show) {\n    Widget.self.window.setIgnoreCursorEvents(!show);\n\n    if (show) {\n      invoke(SeelenCommand.BringSelfToTop);\n    }\n  }\n\n  showing = show;\n}\n\nexport const RendererState = {\n  get all() {\n    return monitors.value;\n  },\n  get primary() {\n    return primaryMonitor;\n  },\n  get showing() {\n    return showing;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/app.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { state } from \"./state.svelte\";\n  import { t } from \"./i18n\";\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n\n  function onKeyboardClick(id: string, handle: string) {\n    invoke(SeelenCommand.SystemSetKeyboardLayout, {\n      id,\n      handle,\n    });\n  }\n\n  function openKeyboardSettings() {\n    invoke(SeelenCommand.OpenFile, { path: \"ms-settings:keyboard\" });\n  }\n</script>\n\n<div class={[\"slu-standard-popover\", \"keyboard-selector\"]}>\n  <div class=\"keyboard-selector-header\">{$t(\"title\")}</div>\n  <div class=\"keyboard-selector-body\">\n    {#each state.langs as lang}\n      {#each lang.keyboardLayouts as keyboard (keyboard.handle)}\n        <button\n          class=\"layout\"\n          class:layout-active={keyboard.active}\n          onclick={() => onKeyboardClick(keyboard.id, keyboard.handle)}\n        >\n          <div class=\"layout-icon\">\n            <Icon iconName=\"FaRegKeyboard\" />\n          </div>\n          <div class=\"layout-info\">\n            <span class=\"layout-lang\">\n              {lang.name}\n            </span>\n            <span class=\"layout-keyboard\">\n              {keyboard.displayName}\n            </span>\n          </div>\n        </button>\n      {/each}\n    {/each}\n  </div>\n  <div class=\"keyboard-selector-footer\">\n    <button data-skin=\"transparent\" onclick={openKeyboardSettings}>\n      {$t(\"more\")}\n    </button>\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/af.yml",
    "content": "more: Meer sleutelbordinstellings\ntitle: Sleutelbord uitleg\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/am.yml",
    "content": "more: ተጨማሪ የቁልፍ ሰሌዳ ቅንብሮች\ntitle: የቁልፍ ሰሌዳ አቀማመጥ\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ar.yml",
    "content": "more: المزيد من إعدادات لوحة المفاتيح\ntitle: تخطيط لوحة المفاتيح\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/az.yml",
    "content": "more: Daha çox klaviatura parametrləri\ntitle: Klaviatura düzümü\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/bg.yml",
    "content": "more: Още настройки на клавиатурата\ntitle: Подредба на клавиатурата\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/bn.yml",
    "content": "more: আরও কীবোর্ড সেটিংস\ntitle: কীবোর্ড লেআউট\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/bs.yml",
    "content": "more: Više podešavanja tastature\ntitle: Raspored tastature\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ca.yml",
    "content": "more: Més configuracions del teclat\ntitle: Disposició del teclat\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/cs.yml",
    "content": "more: Další nastavení klávesnice\ntitle: Rozložení klávesnice\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/cy.yml",
    "content": "more: Mwy o osodiadau bysellfwrdd\ntitle: Cynllun bysellfwrdd\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/da.yml",
    "content": "more: Flere tastaturindstillinger\ntitle: Tastatur layout\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/de.yml",
    "content": "more: Weitere Tastatureinstellungen\ntitle: Tastaturlayout\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/el.yml",
    "content": "more: Περισσότερες ρυθμίσεις πληκτρολογίου\ntitle: Διάταξη πληκτρολογίου\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/en.yml",
    "content": "more: More keyboard settings\ntitle: Keyboard layout\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/es.yml",
    "content": "more: Más configuraciones de teclado\ntitle: Distribución del teclado\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/et.yml",
    "content": "more: Rohkem klaviatuuri seadeid\ntitle: Klaviatuuri paigutus\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/eu.yml",
    "content": "more: Teklatuaren ezarpen gehiago\ntitle: Teklatuaren diseinua\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/fa.yml",
    "content": "more: تنظیمات بیشتر صفحه کلید\ntitle: چیدمان صفحه کلید\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/fi.yml",
    "content": "more: Lisää näppäimistöasetuksia\ntitle: Näppäimistön asettelu\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/fr.yml",
    "content": "more: Plus de paramètres du clavier\ntitle: Disposition du clavier\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/gu.yml",
    "content": "more: વધુ કીબોર્ડ સેટિંગ્સ\ntitle: કીબોર્ડ લેઆઉટ\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/he.yml",
    "content": "more: הגדרות מקלדת נוספות\ntitle: פריסת מקלדת\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/hi.yml",
    "content": "more: अधिक कीबोर्ड सेटिंग्स\ntitle: कीबोर्ड विन्यास\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/hr.yml",
    "content": "more: Više postavki tipkovnice\ntitle: Raspored tipkovnice\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/hu.yml",
    "content": "more: További billentyűzetbeállítások\ntitle: Billentyűzetkiosztás\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/hy.yml",
    "content": "more: Ստեղնաշարի ավելի շատ կարգավորումներ\ntitle: Ստեղնաշարի դասավորություն\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/id.yml",
    "content": "more: Pengaturan keyboard lainnya\ntitle: Tata letak papan ketik\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/is.yml",
    "content": "more: Fleiri lyklaborðsstillingar\ntitle: Uppsetning lyklaborðs\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/it.yml",
    "content": "more: Altre impostazioni della tastiera\ntitle: Disposizione della tastiera\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ja.yml",
    "content": "more: その他のキーボード設定\ntitle: キーボードのレイアウト\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ka.yml",
    "content": "more: კლავიატურის სხვა პარამეტრები\ntitle: კლავიატურის განლაგება\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/km.yml",
    "content": "more: ការកំណត់ក្តារចុចច្រើនទៀត\ntitle: ប្លង់ក្តារចុច\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ko.yml",
    "content": "more: 추가 키보드 설정\ntitle: 키보드 레이아웃\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ku.yml",
    "content": "more: Zêdetir mîhengên klavyeyê\ntitle: layout Klavyeya\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/lb.yml",
    "content": "more: Méi Keyboard Astellunge\ntitle: Keyboard Layout\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/lo.yml",
    "content": "more: ການຕັ້ງຄ່າແປ້ນພິມເພີ່ມເຕີມ\ntitle: ຮູບແບບແປ້ນພິມ\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/lt.yml",
    "content": "more: Daugiau klaviatūros nustatymų\ntitle: Klaviatūros išdėstymas\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/lv.yml",
    "content": "more: Vairāk tastatūras iestatījumu\ntitle: Tastatūras izkārtojums\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/mk.yml",
    "content": "more: Повеќе поставки за тастатура\ntitle: Распоред на тастатурата\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/mn.yml",
    "content": "more: Бусад гарын тохиргоо\ntitle: Гарны зохион байгуулалт\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ms.yml",
    "content": "more: Lagi tetapan papan kekunci\ntitle: Susun atur papan kekunci\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/mt.yml",
    "content": "more: Aktar settings tat-tastiera\ntitle: It-tqassim tat-tastiera\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ne.yml",
    "content": "more: थप किबोर्ड सेटिङहरू\ntitle: किबोर्ड लेआउट\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/nl.yml",
    "content": "more: Meer toetsenbordinstellingen\ntitle: Toetsenbordindeling\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/no.yml",
    "content": "more: Flere tastaturinnstillinger\ntitle: Tastaturoppsett\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/pa.yml",
    "content": "more: ਹੋਰ ਕੀਬੋਰਡ ਸੈਟਿੰਗਾਂ\ntitle: ਕੀਬੋਰਡ ਲੇਆਉਟ\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/pl.yml",
    "content": "more: Więcej ustawień klawiatury\ntitle: Układ klawiatury\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ps.yml",
    "content": "more: د کیبورډ نور ترتیبات\ntitle: د کیبورډ ترتیب\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/pt-BR.yml",
    "content": "more: Mais configurações de teclado\ntitle: Layout do teclado\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/pt-PT.yml",
    "content": "more: Mais configurações de teclado\ntitle: Layout do teclado\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ro.yml",
    "content": "more: Mai multe setări de tastatură\ntitle: Dispunerea tastaturii\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ru.yml",
    "content": "more: Дополнительные настройки клавиатуры\ntitle: Раскладка клавиатуры\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/si.yml",
    "content": "more: තවත් යතුරුපුවරු සැකසුම්\ntitle: යතුරුපුවරු පිරිසැලසුම\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/sk.yml",
    "content": "more: Ďalšie nastavenia klávesnice\ntitle: Rozloženie klávesnice\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/so.yml",
    "content": "more: Dejinta kiiboodhka oo badan\ntitle: Qaabaynta kiiboodhka\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/sr.yml",
    "content": "more: Више подешавања тастатуре\ntitle: Распоред тастатуре\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/sv.yml",
    "content": "more: Fler tangentbordsinställningar\ntitle: Tangentbordslayout\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/sw.yml",
    "content": "more: Mipangilio zaidi ya kibodi\ntitle: Mpangilio wa kibodi\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ta.yml",
    "content": "more: மேலும் விசைப்பலகை அமைப்புகள்\ntitle: விசைப்பலகை அமைப்பு\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/te.yml",
    "content": "more: మరిన్ని కీబోర్డ్ సెట్టింగ్‌లు\ntitle: కీబోర్డ్ లేఅవుట్\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/tg.yml",
    "content": "more: Танзимоти бештари клавиатура\ntitle: Тарҳбандии клавиатура\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/th.yml",
    "content": "more: การตั้งค่าแป้นพิมพ์เพิ่มเติม\ntitle: รูปแบบแป้นพิมพ์\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/tl.yml",
    "content": "more: Higit pang mga setting ng keyboard\ntitle: Layout ng keyboard\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/tr.yml",
    "content": "more: Daha fazla klavye ayarı\ntitle: Klavye düzeni\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/uk.yml",
    "content": "more: Більше налаштувань клавіатури\ntitle: Розкладка клавіатури\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/ur.yml",
    "content": "more: مزید کی بورڈ کی ترتیبات\ntitle: کی بورڈ لے آؤٹ\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/uz.yml",
    "content": "more: Qo'shimcha klaviatura sozlamalari\ntitle: Klaviatura tartibi\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/vi.yml",
    "content": "more: Thêm cài đặt bàn phím\ntitle: Bố cục bàn phím\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/yo.yml",
    "content": "more: Awọn eto keyboard diẹ sii\ntitle: Ifilelẹ bọtini itẹwe\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/zh-CN.yml",
    "content": "more: 更多键盘设置\ntitle: 键盘布局\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/zh-TW.yml",
    "content": "more: 更多鍵盤設置\ntitle: 鍵盤佈局\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/i18n/translations/zu.yml",
    "content": "more: Izilungiselelo zekhibhodi ezengeziwe\ntitle: Isakhiwo sekhibhodi\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/keyboard-selector/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\nimport { locale } from \"./i18n/index.ts\";\n\nlet settings = lazyRune(() => invoke(SeelenCommand.StateGetSettings, { path: null }));\nsubscribe(SeelenEvent.StateSettingsChanged, settings.setByPayload);\nawait settings.init();\n$effect.root(() => {\n  $effect(() => {\n    locale.set(settings.value.language || \"en\");\n  });\n});\n\nconst langs = lazyRune(() => invoke(SeelenCommand.SystemGetLanguages));\nsubscribe(SeelenEvent.SystemLanguagesChanged, langs.setByPayload);\nawait langs.init();\n\nexport const state = {\n  get langs() {\n    return langs.value;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/media-popup/app.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"./state.svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n  import MainView from \"./components/MainView.svelte\";\n  import DeviceView from \"./components/DeviceView.svelte\";\n\n  function handleBack() {\n    globalState.view = \"main\";\n    globalState.selectedDeviceId = null;\n  }\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<div class=\"slu-standard-popover media-popup\">\n  {#if globalState.view === \"main\"}\n    <MainView />\n  {:else}\n    <DeviceView onBack={handleBack} />\n  {/if}\n</div>\n\n<style>\n  .media-popup {\n    background: var(--config-background-color, var(--color-gray-100));\n    color: var(--config-foreground-color, var(--color-gray-900));\n    border-radius: 8px;\n    overflow: hidden;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/components/DeviceView.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"../state.svelte\";\n  import { t } from \"../i18n\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon, FileIcon } from \"libs/ui/svelte/components/Icon\";\n  import VolumeControl from \"./VolumeControl.svelte\";\n\n  interface Props {\n    onBack: () => void;\n  }\n\n  let { onBack }: Props = $props();\n\n  const device = $derived(globalState.selectedDevice);\n\n  const sessions = $derived.by(() => {\n    if (!device || !device.sessions) return [];\n\n    const sessionsIds = new Set();\n    return device.sessions\n      .toSorted((a, b) => a.name.localeCompare(b.name))\n      .filter((s) => {\n        const previousContains = sessionsIds.has(s.id);\n        sessionsIds.add(s.id);\n        return !previousContains;\n      });\n  });\n\n  const iconName = $derived(device?.type === \"input\" ? \"BiMicrophone\" : \"IoVolumeHighOutline\");\n  const mutedIconName = $derived(\n    device?.type === \"input\" ? \"BiMicrophoneOff\" : \"IoVolumeMuteOutline\",\n  );\n\n  function openDeviceSettings() {\n    if (device) {\n      invoke(SeelenCommand.OpenFile, {\n        path: `ms-settings:sound-properties?endpointId=${device.id}`,\n      });\n    }\n  }\n</script>\n\n<div class=\"media-device-view\">\n  <div class=\"media-device-header\">\n    <button data-skin=\"transparent\" onclick={onBack}>\n      <Icon iconName=\"IoArrowBack\" />\n    </button>\n    <span class=\"media-device-title\">{device ? device.name : $t(\"device.missing\")}</span>\n  </div>\n\n  {#if !!device}\n    <span class=\"media-control-label\">{$t(\"device.volume\")}</span>\n    <VolumeControl\n      deviceId={device.id}\n      value={device.volume}\n      icon={device.muted ? mutedIconName : iconName}\n      mutedIcon={mutedIconName}\n      muted={device.muted}\n    />\n\n    {#if device.type !== \"input\" && sessions.length > 0}\n      <span class=\"media-control-label\">{$t(\"device.mixer\")}</span>\n      <div class=\"media-device-mixer\">\n        {#each sessions as channel (channel.id)}\n          <div class=\"media-device-mixer-entry\">\n            <div class=\"media-device-mixer-entry-icon\">\n              {#if channel.isSystem}\n                <Icon iconName=\"BsSpeaker\" size={24} />\n              {:else}\n                <FileIcon path={channel.iconPath} />\n              {/if}\n            </div>\n            <VolumeControl\n              value={channel.volume}\n              deviceId={device.id}\n              sessionName={channel.isSystem ? $t(\"device.channel.system\") : channel.name}\n              sessionId={channel.id}\n              icon={channel.muted ? mutedIconName : iconName}\n              mutedIcon={mutedIconName}\n              muted={channel.muted}\n            />\n          </div>\n        {/each}\n      </div>\n    {/if}\n\n    <div class=\"media-device-footer\">\n      <button data-skin=\"transparent\" onclick={openDeviceSettings}>\n        {$t(\"device.settings\")}\n      </button>\n    </div>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/components/MainView.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"../state.svelte\";\n  import { t } from \"../i18n\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import VolumeControl from \"./VolumeControl.svelte\";\n  import MediaDevice from \"./MediaDevice.svelte\";\n  import MediaPlayer from \"./MediaPlayer.svelte\";\n\n  function setViewDeviceId(id: string) {\n    globalState.selectedDeviceId = id;\n    globalState.view = \"mixer\";\n  }\n</script>\n\n<div class=\"media-main-view\">\n  {#if globalState.defaultOutput || globalState.defaultInput}\n    <span class=\"media-control-label\">\n      {$t(\"default_multimedia_volume\")}\n    </span>\n\n    {#if globalState.defaultOutput}\n      <VolumeControl\n        value={globalState.defaultOutput.volume}\n        deviceId={globalState.defaultOutput.id}\n        icon={globalState.defaultOutput.muted ? \"IoVolumeMuteOutline\" : \"IoVolumeHighOutline\"}\n        mutedIcon=\"IoVolumeMuteOutline\"\n        muted={globalState.defaultOutput.muted}\n        onRightAction={() => setViewDeviceId(globalState.defaultOutput!.id)}\n      />\n    {/if}\n\n    {#if globalState.defaultInput}\n      <VolumeControl\n        value={globalState.defaultInput.volume}\n        deviceId={globalState.defaultInput.id}\n        icon={globalState.defaultInput.muted ? \"BiMicrophoneOff\" : \"BiMicrophone\"}\n        mutedIcon=\"BiMicrophoneOff\"\n        muted={globalState.defaultInput.muted}\n        onRightAction={() => setViewDeviceId(globalState.defaultInput!.id)}\n      />\n    {/if}\n  {/if}\n\n  {#if globalState.outputs.length > 0}\n    <span class=\"media-control-label\">\n      {$t(\"output_devices\")}\n    </span>\n    <div class=\"media-device-group\">\n      {#each globalState.outputs as device (device.id)}\n        <MediaDevice {device} {setViewDeviceId} />\n      {/each}\n    </div>\n  {/if}\n\n  {#if globalState.inputs.length > 0}\n    <span class=\"media-control-label\">\n      {$t(\"input_devices\")}\n    </span>\n    <div class=\"media-device-group\">\n      {#each globalState.inputs as device (device.id)}\n        <MediaDevice {device} {setViewDeviceId} />\n      {/each}\n    </div>\n  {/if}\n\n  {#if globalState.sessions.length > 0}\n    <span class=\"media-control-label\">{$t(\"players\")}</span>\n    <div class=\"media-control-session-list\">\n      {#each globalState.sessions as session (session.umid)}\n        <MediaPlayer {session} />\n      {/each}\n    </div>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/components/MediaDevice.svelte",
    "content": "<script lang=\"ts\">\n  import type { MediaDevice } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"../i18n\";\n\n  interface Props {\n    device: MediaDevice;\n    setViewDeviceId: (id: string) => void;\n  }\n\n  let { device, setViewDeviceId }: Props = $props();\n\n  async function onClickMultimedia() {\n    if (!device.isDefaultMultimedia) {\n      try {\n        await invoke(SeelenCommand.MediaSetDefaultDevice, {\n          id: device.id,\n          role: \"multimedia\",\n        });\n        await invoke(SeelenCommand.MediaSetDefaultDevice, {\n          id: device.id,\n          role: \"console\",\n        });\n      } catch (e) {\n        console.error(e);\n      }\n    }\n  }\n\n  async function onClickCommunications() {\n    if (!device.isDefaultCommunications) {\n      try {\n        await invoke(SeelenCommand.MediaSetDefaultDevice, {\n          id: device.id,\n          role: \"communications\",\n        });\n      } catch (e) {\n        console.error(e);\n      }\n    }\n  }\n</script>\n\n<div class=\"media-device\">\n  <div data-behavior=\"group\">\n    <button\n      data-skin={device.isDefaultMultimedia ? \"solid\" : \"default\"}\n      onclick={onClickMultimedia}\n      title={$t(\"device.multimedia\")}\n    >\n      <Icon iconName=\"IoMusicalNotes\" size={14} />\n    </button>\n    <button\n      data-skin={device.isDefaultCommunications ? \"solid\" : \"default\"}\n      onclick={onClickCommunications}\n      title={$t(\"device.comunications\")}\n    >\n      <Icon iconName=\"FaPhoneFlip\" size={12} />\n    </button>\n  </div>\n  <span class=\"media-device-name\">{device.name}</span>\n  <button data-skin=\"transparent\" onclick={() => setViewDeviceId(device.id)}>\n    <Icon iconName=\"RiEqualizerLine\" />\n  </button>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/components/MediaPlayer.svelte",
    "content": "<script lang=\"ts\">\n  import type { MediaPlayer } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon, FileIcon } from \"libs/ui/svelte/components/Icon\";\n  import { path } from \"@tauri-apps/api\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import { defaultThumbnail } from \"../state.svelte\";\n\n  interface Props {\n    session: MediaPlayer;\n  }\n\n  let { session }: Props = $props();\n\n  const MAX_LUMINANCE = 210;\n  const MIN_LUMINANCE = 40;\n  const BRIGHTNESS_MULTIPLIER = 1.5;\n\n  let luminance = $state(0);\n\n  let thumbnailSrc = $derived(convertFileSrc(session?.thumbnail || defaultThumbnail));\n\n  function calcLuminance(imageUrl: string): Promise<number> {\n    return new Promise((resolve, reject) => {\n      const img = new Image();\n      img.crossOrigin = \"Anonymous\";\n      img.src = imageUrl;\n\n      img.onload = () => {\n        const canvas = document.createElement(\"canvas\");\n        const ctx = canvas.getContext(\"2d\");\n\n        if (!ctx) {\n          reject(new Error(\"Unable to get canvas context\"));\n          return;\n        }\n\n        canvas.width = img.width;\n        canvas.height = img.height;\n        ctx.drawImage(img, 0, 0, img.width, img.height);\n\n        const imageData = ctx.getImageData(0, 0, img.width, img.height);\n        const data = imageData.data;\n        let totalLuminance = 0;\n\n        for (let i = 0; i < data.length; i += 4) {\n          const r = data[i] || 0;\n          const g = data[i + 1] || 0;\n          const b = data[i + 2] || 0;\n\n          const lum = 0.299 * r + 0.587 * g + 0.114 * b;\n          totalLuminance += lum;\n        }\n\n        const avgLuminance = totalLuminance / (data.length / 4);\n        resolve(avgLuminance);\n      };\n\n      img.onerror = (err) => {\n        reject(err);\n      };\n    });\n  }\n\n  $effect(() => {\n    calcLuminance(thumbnailSrc)\n      .then((lum) => (luminance = lum))\n      .catch(console.error);\n  });\n\n  const filteredLuminance = $derived(\n    Math.max(Math.min(luminance * BRIGHTNESS_MULTIPLIER, MAX_LUMINANCE), MIN_LUMINANCE),\n  );\n\n  const color = $derived(filteredLuminance < 125 ? \"#efefef\" : \"#222222\");\n\n  function onClickBtn(cmd: SeelenCommand) {\n    invoke(cmd, { id: session.umid }).catch(console.error);\n  }\n</script>\n\n<div\n  class=\"media-session\"\n  style:background-color=\"rgb({filteredLuminance}, {filteredLuminance}, {filteredLuminance})\"\n>\n  <img class=\"media-session-blurred-thumbnail\" src={thumbnailSrc} alt=\"\" />\n  <div class=\"media-session-thumbnail-container\">\n    <FileIcon class=\"media-session-app-icon\" umid={session.umid} title={session.owner.name} />\n    <img class=\"media-session-thumbnail\" src={thumbnailSrc} alt=\"\" />\n  </div>\n\n  <div class=\"media-session-info\" style:color>\n    <h4 class=\"media-session-title\">{session.title}</h4>\n    <span class=\"media-session-author\">{session.author}</span>\n    <div class=\"media-session-actions\">\n      <button data-skin=\"transparent\" onclick={() => onClickBtn(SeelenCommand.MediaPrev)}>\n        <Icon iconName=\"IoPlaySkipBack\" {color} />\n      </button>\n      <button\n        data-skin=\"transparent\"\n        onclick={() => onClickBtn(SeelenCommand.MediaTogglePlayPause)}\n      >\n        <Icon iconName={session.playing ? \"IoPause\" : \"IoPlay\"} {color} />\n      </button>\n      <button data-skin=\"transparent\" onclick={() => onClickBtn(SeelenCommand.MediaNext)}>\n        <Icon iconName=\"IoPlaySkipForward\" {color} />\n      </button>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/components/VolumeControl.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { throttle } from \"lodash\";\n\n  interface Props {\n    value: number;\n    icon: string;\n    deviceId: string;\n    sessionName?: string;\n    sessionId?: string;\n    onRightAction?: () => void;\n    withPercentage?: boolean;\n    mutedIcon?: string;\n    muted?: boolean;\n  }\n\n  let {\n    value,\n    icon,\n    deviceId,\n    sessionName,\n    sessionId,\n    onRightAction,\n    withPercentage = false,\n    mutedIcon,\n    muted = false,\n  }: Props = $props();\n\n  let internalValue = $state(0);\n\n  $effect(() => {\n    internalValue = value;\n  });\n\n  const onExternalChange = throttle((value: number) => {\n    invoke(SeelenCommand.SetVolumeLevel, {\n      deviceId,\n      sessionId: sessionId || null,\n      level: value,\n    }).catch(console.error);\n  }, 100);\n\n  function onInternalChange(value: number) {\n    internalValue = value;\n    onExternalChange(value);\n  }\n\n  function handleSliderChange(e: Event) {\n    const target = e.target as HTMLInputElement;\n    onInternalChange(Number(target.value));\n  }\n\n  function handleWheel(e: WheelEvent) {\n    e.preventDefault();\n    const isUp = e.deltaY < 0;\n    const level = Math.max(0, Math.min(1, internalValue + (isUp ? 0.02 : -0.02)));\n    onInternalChange(level);\n  }\n\n  async function toggleMute() {\n    await invoke(SeelenCommand.MediaToggleMute, {\n      deviceId,\n      sessionId: sessionId || null,\n    });\n  }\n</script>\n\n<div class=\"media-control-volume\">\n  <button data-skin=\"transparent\" onclick={toggleMute} title={sessionName}>\n    <Icon iconName={(muted && mutedIcon ? mutedIcon : icon) as any} />\n  </button>\n\n  <input\n    type=\"range\"\n    data-skin=\"flat\"\n    value={internalValue}\n    min={0}\n    max={1}\n    step={0.01}\n    onchange={handleSliderChange}\n  />\n\n  {#if onRightAction}\n    <button data-skin=\"transparent\" onclick={onRightAction}>\n      <Icon iconName=\"RiEqualizerLine\" />\n    </button>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/af.yml",
    "content": "default_multimedia_volume: Multimedia Volume\ndevice:\n  channel:\n    system: Stelselklanke\n  comunications: Kommunikasie\n  missing: Toestel ontbreek\n  mixer: Volumemenger\n  multimedia: Multimedia\n  settings: Meer toestelinstellings\n  volume: Toestelvolume\ninput_devices: Invoer toestelle\noutput_devices: Uitset toestelle\nplayers: Mediaspelers\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/am.yml",
    "content": "default_multimedia_volume: የመልቲሚዲያ ድምጽ\ndevice:\n  channel:\n    system: የስርዓት ድምፆች\n  comunications: ግንኙነቶች\n  missing: የጠፋ መሳሪያ\n  mixer: የድምጽ ማደባለቅ\n  multimedia: መልቲሚዲያ\n  settings: ተጨማሪ የመሣሪያ ቅንብሮች\n  volume: የመሳሪያው መጠን\ninput_devices: የግቤት መሳሪያዎች\noutput_devices: የውጤት መሳሪያዎች\nplayers: የሚዲያ ተጫዋቾች\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ar.yml",
    "content": "default_multimedia_volume: حجم الوسائط المتعددة\ndevice:\n  channel:\n    system: أصوات النظام\n  comunications: الاتصالات\n  missing: جهاز مفقود\n  mixer: خلاط الحجم\n  multimedia: الوسائط المتعددة\n  settings: المزيد من إعدادات الجهاز\n  volume: حجم الجهاز\ninput_devices: أجهزة الإدخال\noutput_devices: أجهزة الإخراج\nplayers: مشغلات الوسائط\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/az.yml",
    "content": "default_multimedia_volume: Multimedia həcmi\ndevice:\n  channel:\n    system: Sistem səsləri\n  comunications: Rabitə\n  missing: Çatışmayan Cihaz\n  mixer: Həcmi qarışdırıcı\n  multimedia: Multimedia\n  settings: Daha çox Cihaz Parametrləri\n  volume: Cihazın həcmi\ninput_devices: Giriş cihazları\noutput_devices: Çıxış cihazları\nplayers: Media pleyerləri\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/bg.yml",
    "content": "default_multimedia_volume: Мултимедиен обем\ndevice:\n  channel:\n    system: Системни звуци\n  comunications: Комуникации\n  missing: Липсващо устройство\n  mixer: Миксер за обем\n  multimedia: Мултимедия\n  settings: Още настройки на устройството\n  volume: Сила на звука на устройството\ninput_devices: Входни устройства\noutput_devices: Изходни устройства\nplayers: Медийни плейъри\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/bn.yml",
    "content": "default_multimedia_volume: মাল্টিমিডিয়া ভলিউম\ndevice:\n  channel:\n    system: সিস্টেম শব্দ\n  comunications: যোগাযোগ\n  missing: অনুপস্থিত ডিভাইস\n  mixer: ভলিউম মিক্সার\n  multimedia: মাল্টিমিডিয়া\n  settings: আরও ডিভাইস সেটিংস\n  volume: ডিভাইসের ভলিউম\ninput_devices: ইনপুট ডিভাইস\noutput_devices: আউটপুট ডিভাইস\nplayers: মিডিয়া প্লেয়ার\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/bs.yml",
    "content": "default_multimedia_volume: Multimedijalni volumen\ndevice:\n  channel:\n    system: Sistemski zvuci\n  comunications: Komunikacije\n  missing: Nedostaje uređaj\n  mixer: Volume Mixer\n  multimedia: Multimedija\n  settings: Više postavki uređaja\n  volume: Jačina zvuka uređaja\ninput_devices: Ulazni uređaji\noutput_devices: Izlazni uređaji\nplayers: Media Players\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ca.yml",
    "content": "default_multimedia_volume: Volum multimèdia\ndevice:\n  channel:\n    system: Sons del sistema\n  comunications: Comunicacions\n  missing: Falta el dispositiu\n  mixer: Mesclador de volum\n  multimedia: Multimèdia\n  settings: Més configuració del dispositiu\n  volume: Volum del dispositiu\ninput_devices: Dispositius d'entrada\noutput_devices: Dispositius de sortida\nplayers: Reproductors multimèdia\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/cs.yml",
    "content": "default_multimedia_volume: Multimediální svazek\ndevice:\n  channel:\n    system: Systémové zvuky\n  comunications: Komunikace\n  missing: Chybějící zařízení\n  mixer: Objemový směšovač\n  multimedia: Multimédia\n  settings: Další nastavení zařízení\n  volume: Hlasitost zařízení\ninput_devices: Vstupní zařízení\noutput_devices: Výstupní zařízení\nplayers: Přehrávače médií\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/cy.yml",
    "content": "default_multimedia_volume: Cyfrol Amlgyfrwng\ndevice:\n  channel:\n    system: Synau system\n  comunications: Cyfathrebu\n  missing: Dyfais ar Goll\n  mixer: Cymysgydd Cyfrol\n  multimedia: Amlgyfrwng\n  settings: Mwy o Gosodiadau Dyfais\n  volume: Cyfrol Dyfais\ninput_devices: Dyfeisiau mewnbwn\noutput_devices: Dyfeisiau allbwn\nplayers: Chwaraewyr Cyfryngau\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/da.yml",
    "content": "default_multimedia_volume: Multimedievolumen\ndevice:\n  channel:\n    system: Systemet lyder\n  comunications: Kommunikation\n  missing: Manglende enhed\n  mixer: Volumen mixer\n  multimedia: Multimedier\n  settings: Flere enhedsindstillinger\n  volume: Enhedsvolumen\ninput_devices: Input-enheder\noutput_devices: Udgangsenheder\nplayers: Medieafspillere\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/de.yml",
    "content": "default_multimedia_volume: Multimedia-Lautstärke\ndevice:\n  channel:\n    system: Systemgeräusche\n  comunications: Kommunikation\n  missing: Fehlendes Gerät\n  mixer: Lautstärkemischer\n  multimedia: Multimedia\n  settings: Weitere Geräteeinstellungen\n  volume: Gerätelautstärke\ninput_devices: Eingabegeräte\noutput_devices: Ausgabegeräte\nplayers: Medienplayer\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/el.yml",
    "content": "default_multimedia_volume: Ένταση πολυμέσων\ndevice:\n  channel:\n    system: Ήχοι συστήματος\n  comunications: Διαβιβάσεις\n  missing: Λείπει συσκευή\n  mixer: Μίκτης έντασης ήχου\n  multimedia: Πολυμέσα\n  settings: Περισσότερες ρυθμίσεις συσκευής\n  volume: Ένταση της συσκευής\ninput_devices: Συσκευές εισόδου\noutput_devices: Συσκευές εξόδου\nplayers: Media Players\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/en.yml",
    "content": "default_multimedia_volume: Multimedia Volume\ndevice:\n  channel:\n    system: System sounds\n  comunications: Communications\n  missing: Missing Device\n  mixer: Volume Mixer\n  multimedia: Multimedia\n  settings: More Device Settings\n  volume: Device Volume\ninput_devices: Input devices\noutput_devices: Output devices\nplayers: Media Players\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/es.yml",
    "content": "default_multimedia_volume: Volumen multimedia\ndevice:\n  channel:\n    system: Sonidos del sistema\n  comunications: Comunicaciones\n  missing: Dispositivo faltante\n  mixer: Mezclador de volumen\n  multimedia: Multimedia\n  settings: Más configuraciones del dispositivo\n  volume: Volumen del dispositivo\ninput_devices: Dispositivos de entrada\noutput_devices: Dispositivos de salida\nplayers: Reproductores multimedia\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/et.yml",
    "content": "default_multimedia_volume: Multimeedia helitugevus\ndevice:\n  channel:\n    system: Süsteemi helid\n  comunications: Side\n  missing: Puuduv seade\n  mixer: Helitugevuse mikser\n  multimedia: Multimeedia\n  settings: Rohkem seadme seadeid\n  volume: Seadme helitugevus\ninput_devices: Sisendseadmed\noutput_devices: Väljundseadmed\nplayers: Meediamängijad\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/eu.yml",
    "content": "default_multimedia_volume: Multimedia Bolumena\ndevice:\n  channel:\n    system: Sistemaren soinuak\n  comunications: Komunikazioak\n  missing: Gailua falta da\n  mixer: Bolumen nahasgailua\n  multimedia: Multimedia\n  settings: Gailuaren ezarpen gehiago\n  volume: Gailuaren bolumena\ninput_devices: Sarrerako gailuak\noutput_devices: Irteerako gailuak\nplayers: Multimedia erreproduzitzaileak\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/fa.yml",
    "content": "default_multimedia_volume: حجم چند رسانه ای\ndevice:\n  channel:\n    system: صداهای سیستم\n  comunications: ارتباطات\n  missing: دستگاه گم شده\n  mixer: ولوم میکسر\n  multimedia: چند رسانه ای\n  settings: تنظیمات بیشتر دستگاه\n  volume: میزان صدای دستگاه\ninput_devices: دستگاه های ورودی\noutput_devices: دستگاه های خروجی\nplayers: پخش کننده های رسانه ای\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/fi.yml",
    "content": "default_multimedia_volume: Multimedian äänenvoimakkuus\ndevice:\n  channel:\n    system: Järjestelmän äänet\n  comunications: Viestintä\n  missing: Puuttuva laite\n  mixer: Äänenvoimakkuuden mikseri\n  multimedia: Multimedia\n  settings: Lisää laiteasetuksia\n  volume: Laitteen äänenvoimakkuus\ninput_devices: Syöttölaitteet\noutput_devices: Tulostuslaitteet\nplayers: Mediasoittimet\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/fr.yml",
    "content": "default_multimedia_volume: Volume multimédia\ndevice:\n  channel:\n    system: Sons du système\n  comunications: Communications\n  missing: Appareil manquant\n  mixer: Mélangeur de volume\n  multimedia: Multimédia\n  settings: Plus de paramètres de l'appareil\n  volume: Volume de l'appareil\ninput_devices: Périphériques d'entrée\noutput_devices: Périphériques de sortie\nplayers: Lecteurs multimédias\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/gu.yml",
    "content": "default_multimedia_volume: મલ્ટીમીડિયા વોલ્યુમ\ndevice:\n  channel:\n    system: સિસ્ટમ અવાજો\n  comunications: કોમ્યુનિકેશન્સ\n  missing: ઉપકરણ ખૂટે છે\n  mixer: વોલ્યુમ મિક્સર\n  multimedia: મલ્ટીમીડિયા\n  settings: વધુ ઉપકરણ સેટિંગ્સ\n  volume: ઉપકરણ વોલ્યુમ\ninput_devices: ઇનપુટ ઉપકરણો\noutput_devices: આઉટપુટ ઉપકરણો\nplayers: મીડિયા પ્લેયર્સ\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/he.yml",
    "content": "default_multimedia_volume: נפח מולטימדיה\ndevice:\n  channel:\n    system: צלילי מערכת\n  comunications: תקשורת\n  missing: מכשיר חסר\n  mixer: מיקסר נפח\n  multimedia: מולטימדיה\n  settings: הגדרות מכשיר נוספות\n  volume: עוצמת הקול של המכשיר\ninput_devices: התקני קלט\noutput_devices: התקני פלט\nplayers: נגני מדיה\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/hi.yml",
    "content": "default_multimedia_volume: मल्टीमीडिया वॉल्यूम\ndevice:\n  channel:\n    system: सिस्टम ध्वनियाँ\n  comunications: संचार\n  missing: गुम डिवाइस\n  mixer: वॉल्यूम मिक्सर\n  multimedia: मल्टीमीडिया\n  settings: अधिक डिवाइस सेटिंग्स\n  volume: डिवाइस वॉल्यूम\ninput_devices: आगत यंत्र\noutput_devices: आउटपुट डिवाइस\nplayers: मीडिया प्लेयर्स\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/hr.yml",
    "content": "default_multimedia_volume: Multimedijski volumen\ndevice:\n  channel:\n    system: Zvukovi sustava\n  comunications: Komunikacije\n  missing: Nedostaje uređaj\n  mixer: Mikser za volumen\n  multimedia: Multimedija\n  settings: Više postavki uređaja\n  volume: Glasnoća uređaja\ninput_devices: Ulazni uređaji\noutput_devices: Izlazni uređaji\nplayers: Media Playeri\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/hu.yml",
    "content": "default_multimedia_volume: Multimédiás kötet\ndevice:\n  channel:\n    system: Rendszerhangok\n  comunications: Kommunikáció\n  missing: Hiányzó Eszköz\n  mixer: Volume Mixer\n  multimedia: Multimédia\n  settings: További eszközbeállítások\n  volume: Eszköz hangereje\ninput_devices: Bemeneti eszközök\noutput_devices: Kimeneti eszközök\nplayers: Médialejátszók\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/hy.yml",
    "content": "default_multimedia_volume: Մուլտիմեդիա Ծավալ\ndevice:\n  channel:\n    system: Համակարգի հնչյուններ\n  comunications: Հաղորդակցություններ\n  missing: Սարքը բացակայում է\n  mixer: Volume Mixer\n  multimedia: Մուլտիմեդիա\n  settings: Սարքի լրացուցիչ կարգավորումներ\n  volume: Սարքի ձայնը\ninput_devices: Ներածման սարքեր\noutput_devices: Ելքային սարքեր\nplayers: Մեդիա նվագարկիչներ\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/id.yml",
    "content": "default_multimedia_volume: Volume Multimedia\ndevice:\n  channel:\n    system: Suara sistem\n  comunications: Komunikasi\n  missing: Perangkat Hilang\n  mixer: Pengaduk Volume\n  multimedia: Multimedia\n  settings: Pengaturan Perangkat Lainnya\n  volume: Volume Perangkat\ninput_devices: Perangkat masukan\noutput_devices: Perangkat keluaran\nplayers: Pemutar Media\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/is.yml",
    "content": "default_multimedia_volume: Margmiðlunarmagn\ndevice:\n  channel:\n    system: Kerfishljóð\n  comunications: Fjarskipti\n  missing: Tækið vantar\n  mixer: Rúmmálsblandari\n  multimedia: Margmiðlun\n  settings: Fleiri tækisstillingar\n  volume: Rúmmál tækis\ninput_devices: Inntakstæki\noutput_devices: Úttakstæki\nplayers: Fjölmiðlaspilarar\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/it.yml",
    "content": "default_multimedia_volume: Volume multimediale\ndevice:\n  channel:\n    system: Suoni di sistema\n  comunications: Comunicazioni\n  missing: Dispositivo mancante\n  mixer: Miscelatore del volume\n  multimedia: Multimedia\n  settings: Altre impostazioni del dispositivo\n  volume: Volume del dispositivo\ninput_devices: Dispositivi di input\noutput_devices: Dispositivi di uscita\nplayers: Lettori multimediali\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ja.yml",
    "content": "default_multimedia_volume: デフォルトの音量\ndevice:\n  channel:\n    system: システム音量\n  comunications: コミュニケーション\n  missing: デバイスが見つかりません\n  mixer: ボリュームミキサー\n  multimedia: マルチメディア音量\n  settings: その他のデバイス設定\n  volume: デバイス音量\ninput_devices: 入力デバイス\noutput_devices: 出力デバイス\nplayers: メディアプレーヤー\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ka.yml",
    "content": "default_multimedia_volume: მულტიმედიური მოცულობა\ndevice:\n  channel:\n    system: სისტემის ხმები\n  comunications: კომუნიკაციები\n  missing: მოწყობილობა აკლია\n  mixer: მოცულობის მიქსერი\n  multimedia: მულტიმედია\n  settings: სხვა მოწყობილობის პარამეტრები\n  volume: მოწყობილობის მოცულობა\ninput_devices: შეყვანის მოწყობილობები\noutput_devices: გამომავალი მოწყობილობები\nplayers: მედია ფლეერები\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/km.yml",
    "content": "default_multimedia_volume: កម្រិតសំឡេងពហុព័ត៌មាន\ndevice:\n  channel:\n    system: សំឡេងប្រព័ន្ធ\n  comunications: ទំនាក់ទំនង\n  missing: ឧបករណ៍ដែលបាត់\n  mixer: ឧបករណ៍លាយកម្រិតសំឡេង\n  multimedia: ពហុព័ត៌មាន\n  settings: ការកំណត់ឧបករណ៍ច្រើនទៀត\n  volume: បរិមាណឧបករណ៍\ninput_devices: ឧបករណ៍បញ្ចូល\noutput_devices: ឧបករណ៍បញ្ចេញ\nplayers: អ្នកលេងប្រព័ន្ធផ្សព្វផ្សាយ\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ko.yml",
    "content": "default_multimedia_volume: 멀티미디어 볼륨\ndevice:\n  channel:\n    system: 시스템 소리\n  comunications: 연락\n  missing: 분실된 장치\n  mixer: 볼륨 믹서\n  multimedia: 멀티미디어\n  settings: 추가 장치 설정\n  volume: 장치 볼륨\ninput_devices: 입력 장치\noutput_devices: 출력 장치\nplayers: 미디어 플레이어\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ku.yml",
    "content": "default_multimedia_volume: Volume Multimedia\ndevice:\n  channel:\n    system: Sîstema dengên\n  comunications: Communications\n  missing: Amûra winda\n  mixer: Volume Mixer\n  multimedia: Multimedia\n  settings: More Settings Device\n  volume: Volume Device\ninput_devices: cîhazên Input\noutput_devices: Amûrên derketinê\nplayers: Players Media\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/lb.yml",
    "content": "default_multimedia_volume: Multimedia Volumen\ndevice:\n  channel:\n    system: System Kläng\n  comunications: Kommunikatiounen\n  missing: Vermësst Apparat\n  mixer: Volume Mixer\n  multimedia: Multimedia\n  settings: Méi Apparat Astellunge\n  volume: Apparat Volumen\ninput_devices: Input Apparater\noutput_devices: Ausgang Apparater\nplayers: Medien Spiller\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/lo.yml",
    "content": "default_multimedia_volume: ປະລິມານມັນຕິມີເດຍ\ndevice:\n  channel:\n    system: ສຽງລະບົບ\n  comunications: ການສື່ສານ\n  missing: ຂາດອຸປະກອນ\n  mixer: ເຄື່ອງປະສົມລະດັບສຽງ\n  multimedia: ມັນຕິມີເດຍ\n  settings: ການຕັ້ງຄ່າອຸປະກອນເພີ່ມເຕີມ\n  volume: ປະລິມານອຸປະກອນ\ninput_devices: ອຸປະກອນປ້ອນ\noutput_devices: ອຸປະກອນອອກ\nplayers: ເຄື່ອງຫຼິ້ນສື່\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/lt.yml",
    "content": "default_multimedia_volume: Multimedijos garsumas\ndevice:\n  channel:\n    system: Sistemos garsai\n  comunications: Ryšiai\n  missing: Trūksta įrenginio\n  mixer: Tūrio maišytuvas\n  multimedia: Multimedija\n  settings: Daugiau įrenginio nustatymų\n  volume: Įrenginio garsumas\ninput_devices: Įvesties įrenginiai\noutput_devices: Išvesties įrenginiai\nplayers: Medijos grotuvai\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/lv.yml",
    "content": "default_multimedia_volume: Multivides apjoms\ndevice:\n  channel:\n    system: Sistēmas skaņas\n  comunications: Komunikācijas\n  missing: Trūkst ierīces\n  mixer: Skaļuma mikseris\n  multimedia: Multivide\n  settings: Vairāk ierīces iestatījumu\n  volume: Ierīces skaļums\ninput_devices: Ievades ierīces\noutput_devices: Izvades ierīces\nplayers: Multivides atskaņotāji\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/mk.yml",
    "content": "default_multimedia_volume: Мултимедијален волумен\ndevice:\n  channel:\n    system: Системски звуци\n  comunications: Комуникации\n  missing: Недостасува уред\n  mixer: Миксер за јачина на звук\n  multimedia: Мултимедија\n  settings: Повеќе поставки за уредот\n  volume: Јачина на звук на уредот\ninput_devices: Влезни уреди\noutput_devices: Излезни уреди\nplayers: Медиа плеери\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/mn.yml",
    "content": "default_multimedia_volume: Мультимедиа дууны хэмжээ\ndevice:\n  channel:\n    system: Системийн дуу чимээ\n  comunications: Харилцаа холбоо\n  missing: Төхөөрөмж дутуу байна\n  mixer: Эзлэхүүн холигч\n  multimedia: Мультимедиа\n  settings: Төхөөрөмжийн нэмэлт тохиргоо\n  volume: Төхөөрөмжийн эзлэхүүн\ninput_devices: Оролтын төхөөрөмжүүд\noutput_devices: Гаралтын төхөөрөмжүүд\nplayers: Медиа тоглуулагч\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ms.yml",
    "content": "default_multimedia_volume: Kelantangan Multimedia\ndevice:\n  channel:\n    system: Bunyi sistem\n  comunications: Komunikasi\n  missing: Peranti Tiada\n  mixer: Pengadun Isipadu\n  multimedia: Multimedia\n  settings: Lagi Tetapan Peranti\n  volume: Kelantangan Peranti\ninput_devices: Peranti input\noutput_devices: Peranti output\nplayers: Pemain Media\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/mt.yml",
    "content": "default_multimedia_volume: Volum Multimedia\ndevice:\n  channel:\n    system: Ħsejjes tas-sistema\n  comunications: Komunikazzjonijiet\n  missing: Apparat Nieqes\n  mixer: Volum Mixer\n  multimedia: Multimedia\n  settings: Aktar Settings tal-Apparat\n  volume: Volum tal-Apparat\ninput_devices: Apparat tad-dħul\noutput_devices: Apparat tal-ħruġ\nplayers: Media Players\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ne.yml",
    "content": "default_multimedia_volume: मल्टिमिडिया भोल्युम\ndevice:\n  channel:\n    system: प्रणाली ध्वनिहरू\n  comunications: सञ्चार\n  missing: हराएको यन्त्र\n  mixer: भोल्युम मिक्सर\n  multimedia: मल्टिमिडिया\n  settings: थप यन्त्र सेटिङहरू\n  volume: यन्त्र भोल्युम\ninput_devices: इनपुट उपकरणहरू\noutput_devices: आउटपुट उपकरणहरू\nplayers: मिडिया प्लेयरहरू\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/nl.yml",
    "content": "default_multimedia_volume: Multimediavolume\ndevice:\n  channel:\n    system: Systeemgeluiden\n  comunications: Communicatie\n  missing: Ontbrekend apparaat\n  mixer: Volumemixer\n  multimedia: Multimediaal\n  settings: Meer apparaatinstellingen\n  volume: Apparaatvolume\ninput_devices: Invoerapparaten\noutput_devices: Uitvoerapparaten\nplayers: Mediaspelers\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/no.yml",
    "content": "default_multimedia_volume: Multimedievolum\ndevice:\n  channel:\n    system: Systemet lyder\n  comunications: Kommunikasjon\n  missing: Manglende enhet\n  mixer: Volummikser\n  multimedia: Multimedia\n  settings: Flere enhetsinnstillinger\n  volume: Enhetsvolum\ninput_devices: Inndataenheter\noutput_devices: Utgangsenheter\nplayers: Mediespillere\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/pa.yml",
    "content": "default_multimedia_volume: ਮਲਟੀਮੀਡੀਆ ਵਾਲੀਅਮ\ndevice:\n  channel:\n    system: ਸਿਸਟਮ ਧੁਨੀਆਂ\n  comunications: ਸੰਚਾਰ\n  missing: ਗੁੰਮ ਜੰਤਰ\n  mixer: ਵਾਲੀਅਮ ਮਿਕਸਰ\n  multimedia: ਮਲਟੀਮੀਡੀਆ\n  settings: ਹੋਰ ਡਿਵਾਈਸ ਸੈਟਿੰਗਾਂ\n  volume: ਡਿਵਾਈਸ ਵਾਲੀਅਮ\ninput_devices: ਇਨਪੁਟ ਡਿਵਾਈਸਾਂ\noutput_devices: ਆਉਟਪੁੱਟ ਜੰਤਰ\nplayers: ਮੀਡੀਆ ਪਲੇਅਰ\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/pl.yml",
    "content": "default_multimedia_volume: Głośność multimediów\ndevice:\n  channel:\n    system: Dźwięki systemowe\n  comunications: Komunikacja\n  missing: Brakujące urządzenie\n  mixer: Mikser głośności\n  multimedia: Multimedia\n  settings: Więcej ustawień urządzenia\n  volume: Głośność urządzenia\ninput_devices: Urządzenia wejściowe\noutput_devices: Urządzenia wyjściowe\nplayers: Odtwarzacze multimedialne\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ps.yml",
    "content": "default_multimedia_volume: ملټي میډیا حجم\ndevice:\n  channel:\n    system: د سیسټم غږونه\n  comunications: مخابراتو\n  missing: ورک شوی وسیله\n  mixer: د حجم مکسر\n  multimedia: ملټي میډیا\n  settings: د وسیلې نور ترتیبات\n  volume: د وسیلې حجم\ninput_devices: د ننوتلو وسایل\noutput_devices: د تولید وسایل\nplayers: د رسنیو لوبغاړي\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/pt-BR.yml",
    "content": "default_multimedia_volume: Volume multimídia\ndevice:\n  channel:\n    system: Sons do sistema\n  comunications: Comunicações\n  missing: Dispositivo ausente\n  mixer: Misturador de volume\n  multimedia: Multimídia\n  settings: Mais configurações do dispositivo\n  volume: Volume do dispositivo\ninput_devices: Dispositivos de entrada\noutput_devices: Dispositivos de saída\nplayers: Reprodutores de mídia\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/pt-PT.yml",
    "content": "default_multimedia_volume: Volume multimídia\ndevice:\n  channel:\n    system: Sons do sistema\n  comunications: Comunicações\n  missing: Dispositivo ausente\n  mixer: Misturador de volume\n  multimedia: Multimídia\n  settings: Mais configurações do dispositivo\n  volume: Volume do dispositivo\ninput_devices: Dispositivos de entrada\noutput_devices: Dispositivos de saída\nplayers: Reprodutores de mídia\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ro.yml",
    "content": "default_multimedia_volume: Volumul multimedia\ndevice:\n  channel:\n    system: Sunete de sistem\n  comunications: Comunicatii\n  missing: Dispozitiv lipsă\n  mixer: Mixer de volum\n  multimedia: Multimedia\n  settings: Mai multe setări pentru dispozitiv\n  volume: Volumul dispozitivului\ninput_devices: Dispozitive de intrare\noutput_devices: Dispozitive de ieșire\nplayers: Playere media\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ru.yml",
    "content": "default_multimedia_volume: Мультимедийный объем\ndevice:\n  channel:\n    system: Системные звуки\n  comunications: Связь\n  missing: Отсутствует устройство\n  mixer: Микшер громкости\n  multimedia: Мультимедиа\n  settings: Дополнительные настройки устройства\n  volume: Громкость устройства\ninput_devices: Устройства ввода\noutput_devices: Устройства вывода\nplayers: Медиаплееры\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/si.yml",
    "content": "default_multimedia_volume: බහුමාධ්ය පරිමාව\ndevice:\n  channel:\n    system: පද්ධති ශබ්ද\n  comunications: සන්නිවේදන\n  missing: උපාංගය අතුරුදහන්\n  mixer: වෙළුම් මිශ්රකය\n  multimedia: බහුමාධ්ය\n  settings: තවත් උපාංග සැකසුම්\n  volume: උපාංග පරිමාව\ninput_devices: ආදාන උපාංග\noutput_devices: ප්රතිදාන උපාංග\nplayers: මාධ්ය වාදකයන්\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/sk.yml",
    "content": "default_multimedia_volume: Multimediálny objem\ndevice:\n  channel:\n    system: Systémové zvuky\n  comunications: komunikácie\n  missing: Chýbajúce zariadenie\n  mixer: Objemový mixér\n  multimedia: Multimédiá\n  settings: Ďalšie nastavenia zariadenia\n  volume: Hlasitosť zariadenia\ninput_devices: Vstupné zariadenia\noutput_devices: Výstupné zariadenia\nplayers: Prehrávače médií\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/so.yml",
    "content": "default_multimedia_volume: Mugga Warbaahinta\ndevice:\n  channel:\n    system: Codka nidaamka\n  comunications: Isgaarsiinta\n  missing: Qalab maqan\n  mixer: Isku-dhafka mugga\n  multimedia: Multimedia\n  settings: Dejinta Aaladaha oo badan\n  volume: Qalabka Qalabka\ninput_devices: Qalabka wax gelinta\noutput_devices: Qalabka wax soo saarka\nplayers: Ciyaartoyda Warbaahinta\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/sr.yml",
    "content": "default_multimedia_volume: Мултимедиа Волуме\ndevice:\n  channel:\n    system: Системски звуци\n  comunications: Комуникације\n  missing: Недостаје уређај\n  mixer: Волуме Микер\n  multimedia: Мултимедија\n  settings: Још подешавања уређаја\n  volume: Јачина звука уређаја\ninput_devices: Улазни уређаји\noutput_devices: Излазни уређаји\nplayers: Медиа Плаиерс\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/sv.yml",
    "content": "default_multimedia_volume: Multimedia volym\ndevice:\n  channel:\n    system: Systemet låter\n  comunications: Kommunikationer\n  missing: Enhet saknas\n  mixer: Volymmixer\n  multimedia: Multimedia\n  settings: Fler enhetsinställningar\n  volume: Enhetsvolym\ninput_devices: Inmatningsenheter\noutput_devices: Utdataenheter\nplayers: Mediaspelare\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/sw.yml",
    "content": "default_multimedia_volume: Sauti ya Multimedia\ndevice:\n  channel:\n    system: Sauti za mfumo\n  comunications: Mawasiliano\n  missing: Kifaa hakipo\n  mixer: Mchanganyiko wa Kiasi\n  multimedia: Multimedia\n  settings: Mipangilio Zaidi ya Kifaa\n  volume: Kiasi cha Kifaa\ninput_devices: Vifaa vya kuingiza\noutput_devices: Vifaa vya pato\nplayers: Vicheza media\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ta.yml",
    "content": "default_multimedia_volume: மல்டிமீடியா தொகுதி\ndevice:\n  channel:\n    system: கணினி ஒலிகள்\n  comunications: தொடர்புகள்\n  missing: சாதனம் இல்லை\n  mixer: தொகுதி கலவை\n  multimedia: மல்டிமீடியா\n  settings: மேலும் சாதன அமைப்புகள்\n  volume: சாதனத்தின் அளவு\ninput_devices: உள்ளீட்டு சாதனங்கள்\noutput_devices: வெளியீட்டு சாதனங்கள்\nplayers: மீடியா பிளேயர்கள்\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/te.yml",
    "content": "default_multimedia_volume: మల్టీమీడియా వాల్యూమ్\ndevice:\n  channel:\n    system: సిస్టమ్ శబ్దాలు\n  comunications: కమ్యూనికేషన్స్\n  missing: పరికరం లేదు\n  mixer: వాల్యూమ్ మిక్సర్\n  multimedia: మల్టీమీడియా\n  settings: మరిన్ని పరికర సెట్టింగ్‌లు\n  volume: పరికర వాల్యూమ్\ninput_devices: ఇన్‌పుట్ పరికరాలు\noutput_devices: అవుట్పుట్ పరికరాలు\nplayers: మీడియా ప్లేయర్స్\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/tg.yml",
    "content": "default_multimedia_volume: Ҳаҷми мултимедиявӣ\ndevice:\n  channel:\n    system: Овозҳои система\n  comunications: Муошират\n  missing: Дастгоҳи гумшуда\n  mixer: Миксери ҳаҷми\n  multimedia: Мултимедиа\n  settings: Танзимоти бештари дастгоҳ\n  volume: Ҳаҷми дастгоҳ\ninput_devices: Дастгоҳҳои воридотӣ\noutput_devices: Дастгоҳҳои баромад\nplayers: Плеери медиа\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/th.yml",
    "content": "default_multimedia_volume: ปริมาณมัลติมีเดีย\ndevice:\n  channel:\n    system: เสียงของระบบ\n  comunications: การสื่อสาร\n  missing: อุปกรณ์ที่หายไป\n  mixer: มิกเซอร์ระดับเสียง\n  multimedia: มัลติมีเดีย\n  settings: การตั้งค่าอุปกรณ์เพิ่มเติม\n  volume: ระดับเสียงของอุปกรณ์\ninput_devices: อุปกรณ์อินพุต\noutput_devices: อุปกรณ์เอาท์พุต\nplayers: เครื่องเล่นสื่อ\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/tl.yml",
    "content": "default_multimedia_volume: Dami ng Multimedia\ndevice:\n  channel:\n    system: Mga tunog ng system\n  comunications: Komunikasyon\n  missing: Nawawalang Device\n  mixer: Panghalo ng Dami\n  multimedia: Multimedia\n  settings: Higit pang Mga Setting ng Device\n  volume: Dami ng Device\ninput_devices: Mga input device\noutput_devices: Mga aparatong output\nplayers: Mga Media Player\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/tr.yml",
    "content": "default_multimedia_volume: Multimedya Sesi\ndevice:\n  channel:\n    system: Sistem sesleri\n  comunications: İletişim\n  missing: Eksik Cihaz\n  mixer: Hacim Karıştırıcı\n  multimedia: Multimedya\n  settings: Diğer Cihaz Ayarları\n  volume: Cihaz Sesi\ninput_devices: Giriş cihazları\noutput_devices: Çıkış cihazları\nplayers: Medya Oynatıcıları\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/uk.yml",
    "content": "default_multimedia_volume: Обсяг мультимедіа\ndevice:\n  channel:\n    system: Системні звуки\n  comunications: Комунікації\n  missing: Відсутній пристрій\n  mixer: Міксер гучності\n  multimedia: Мультимедіа\n  settings: Більше налаштувань пристрою\n  volume: Гучність пристрою\ninput_devices: Пристрої введення\noutput_devices: Пристрої виведення\nplayers: Медіаплеєри\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/ur.yml",
    "content": "default_multimedia_volume: ملٹی میڈیا حجم\ndevice:\n  channel:\n    system: سسٹم کی آوازیں\n  comunications: مواصلات\n  missing: لاپتہ آلہ\n  mixer: حجم مکسر\n  multimedia: ملٹی میڈیا\n  settings: مزید آلہ کی ترتیبات\n  volume: ڈیوائس کا حجم\ninput_devices: ان پٹ ڈیوائسز\noutput_devices: آؤٹ پٹ ڈیوائسز\nplayers: میڈیا پلیئرز\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/uz.yml",
    "content": "default_multimedia_volume: Multimedia tovushi\ndevice:\n  channel:\n    system: Tizim tovushlari\n  comunications: Aloqa\n  missing: Qurilma yetishmayapti\n  mixer: Ovoz mikser\n  multimedia: Multimedia\n  settings: Qo'shimcha qurilma sozlamalari\n  volume: Qurilma ovozi\ninput_devices: Kirish qurilmalari\noutput_devices: Chiqish qurilmalari\nplayers: Media pleerlar\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/vi.yml",
    "content": "default_multimedia_volume: Khối lượng đa phương tiện\ndevice:\n  channel:\n    system: Âm thanh hệ thống\n  comunications: Truyền thông\n  missing: Thiếu thiết bị\n  mixer: Bộ trộn âm lượng\n  multimedia: đa phương tiện\n  settings: Thêm cài đặt thiết bị\n  volume: Âm lượng thiết bị\ninput_devices: Thiết bị đầu vào\noutput_devices: Thiết bị đầu ra\nplayers: Trình phát phương tiện\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/yo.yml",
    "content": "default_multimedia_volume: Multimedia Iwọn didun\ndevice:\n  channel:\n    system: Awọn ohun eto\n  comunications: Awọn ibaraẹnisọrọ\n  missing: Ohun elo ti o padanu\n  mixer: Adapọ iwọn didun\n  multimedia: Multimedia\n  settings: Die Device Eto\n  volume: Iwọn ẹrọ\ninput_devices: Awọn ẹrọ ti nwọle\noutput_devices: Awọn ẹrọ ti njade\nplayers: Awọn ẹrọ orin Media\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/zh-CN.yml",
    "content": "default_multimedia_volume: 多媒体音量\ndevice:\n  channel:\n    system: 系统声音\n  comunications: 通讯\n  missing: 丢失设备\n  mixer: 音量混合器\n  multimedia: 多媒体\n  settings: 更多设备设置\n  volume: 设备音量\ninput_devices: 输入设备\noutput_devices: 输出设备\nplayers: 媒体播放器\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/zh-TW.yml",
    "content": "default_multimedia_volume: 多媒體音量\ndevice:\n  channel:\n    system: 系統聲音\n  comunications: 通訊\n  missing: 丟失設備\n  mixer: 音量混合器\n  multimedia: 多媒體\n  settings: 更多設備設置\n  volume: 設備音量\ninput_devices: 輸入設備\noutput_devices: 輸出設備\nplayers: 媒體播放器\n"
  },
  {
    "path": "src/ui/svelte/media-popup/i18n/translations/zu.yml",
    "content": "default_multimedia_volume: I-Multimedia Volume\ndevice:\n  channel:\n    system: Imisindo yesistimu\n  comunications: Ezokuxhumana\n  missing: Idivayisi engekho\n  mixer: I-Volume Mixer\n  multimedia: Multimedia\n  settings: Izilungiselelo Zedivayisi Ezengeziwe\n  volume: Ivolumu Yedivayisi\ninput_devices: Amadivayisi okokufaka\noutput_devices: Amadivayisi okukhiphayo\nplayers: Abadlali Bemidiya\n"
  },
  {
    "path": "src/ui/svelte/media-popup/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/media-popup/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/media-popup/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe, Widget } from \"@seelen-ui/lib\";\nimport type { MediaDevice, MediaPlayer } from \"@seelen-ui/lib/types\";\nimport { locale } from \"./i18n/index.ts\";\nimport { writable } from \"svelte/store\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\nimport { path } from \"@tauri-apps/api\";\n\nlet widget = Widget.getCurrent();\n\nlet settings = writable(await Settings.getAsync());\nSettings.onChange((s) => settings.set(s));\nsettings.subscribe((settings) => {\n  locale.set(settings.language || \"en\");\n});\n\nlet mediaDevices = lazyRune(() => invoke(SeelenCommand.GetMediaDevices));\nsubscribe(SeelenEvent.MediaDevices, mediaDevices.setByPayload);\n\nlet mediaSessions = lazyRune(() => invoke(SeelenCommand.GetMediaSessions));\nsubscribe(SeelenEvent.MediaSessions, mediaSessions.setByPayload);\n\nawait Promise.all([mediaDevices.init(), mediaSessions.init()]);\n\nlet currentView = $state<\"main\" | \"mixer\">(\"main\");\nlet selectedDeviceId = $state<string | null>(null);\n\nwidget.window.onFocusChanged((e) => {\n  if (!e.payload) {\n    // Reset state when popup loses focus\n    currentView = \"main\";\n    selectedDeviceId = null;\n    widget.hide();\n  }\n});\n\nclass State {\n  get inputs(): MediaDevice[] {\n    return mediaDevices.value[0];\n  }\n\n  get outputs(): MediaDevice[] {\n    return mediaDevices.value[1];\n  }\n\n  get sessions(): MediaPlayer[] {\n    return mediaSessions.value;\n  }\n\n  get defaultInput(): MediaDevice | undefined {\n    return this.inputs.find((d) => d.isDefaultMultimedia);\n  }\n\n  get defaultOutput(): MediaDevice | undefined {\n    return this.outputs.find((d) => d.isDefaultMultimedia);\n  }\n\n  get view() {\n    return currentView;\n  }\n\n  set view(value: \"main\" | \"mixer\") {\n    currentView = value;\n  }\n\n  get selectedDeviceId() {\n    return selectedDeviceId;\n  }\n\n  set selectedDeviceId(value: string | null) {\n    selectedDeviceId = value;\n  }\n\n  get selectedDevice(): MediaDevice | undefined {\n    if (!selectedDeviceId) return undefined;\n    return (\n      this.inputs.find((d) => d.id === selectedDeviceId) ||\n      this.outputs.find((d) => d.id === selectedDeviceId)\n    );\n  }\n}\n\nexport const globalState = new State();\n\nexport const defaultThumbnail = await path.resolve(\n  await path.resourceDir(),\n  \"static\",\n  \"icons\",\n  \"music_thumbnail.jpg\",\n);\n"
  },
  {
    "path": "src/ui/svelte/network-popup/app.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"./state.svelte\";\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n  import { RadioDeviceKind, type WlanBssEntry } from \"@seelen-ui/lib/types\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"./i18n\";\n  import WlanEntry from \"./components/WlanEntry.svelte\";\n\n  // Group and sort entries\n  const { connected, known, unknown, hidden } = $derived.by(() => {\n    const entries = globalState.wlanBssEntries;\n\n    // Filter hidden networks (no SSID)\n    let hidden = entries.filter((e) => !e.ssid).toSorted((a, b) => b.signal - a.signal);\n\n    // Group entries by SSID\n    let grouped = entries.reduce(\n      (groups, entry) => {\n        if (!entry.ssid) {\n          return groups;\n        }\n        if (!groups[entry.ssid]) {\n          groups[entry.ssid] = [entry];\n          return groups;\n        }\n        groups[entry.ssid]!.push(entry);\n        groups[entry.ssid]!.sort((e1, e2) => e2.signal - e1.signal);\n        return groups;\n      },\n      {} as Record<string, [WlanBssEntry, ...WlanBssEntry[]]>,\n    );\n\n    // Sort groups by signal strength\n    let sorted = Object.values(grouped).toSorted((a, b) => {\n      let signalA = Math.max(...a.map((e) => e.signal));\n      let signalB = Math.max(...b.map((e) => e.signal));\n      return signalB - signalA;\n    });\n\n    let connected = sorted.find((group) => group.some((e) => e.connected));\n    let known = sorted.filter(\n      (group) => group.every((e) => !e.connected) && group.some((e) => e.known),\n    );\n    let unknown = sorted.filter(\n      (group) => group.every((e) => !e.connected) && group.every((e) => !e.known),\n    );\n\n    return { connected, known, unknown, hidden };\n  });\n\n  const wifiRadio = $derived(globalState.wifiRadio);\n\n  function openNetworkSettings() {\n    invoke(SeelenCommand.OpenFile, { path: \"ms-settings:network\" });\n  }\n\n  async function toggleWifiRadio() {\n    if (wifiRadio) {\n      await invoke(SeelenCommand.SetRadioState, {\n        kind: RadioDeviceKind.WiFi,\n        enabled: !wifiRadio.is_enabled,\n      });\n    }\n  }\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<div class=\"slu-standard-popover network-popup\">\n  {#if !wifiRadio}\n    <div class=\"network-no-adapter\">\n      {$t(\"no_adapter\")}\n    </div>\n  {:else}\n    <div class=\"network-radio-control\">\n      <div class=\"network-radio-label\">\n        <Icon iconName=\"FaWifi\" />\n        <span>Wi-Fi</span>\n      </div>\n      <label class=\"network-radio-switch\">\n        <input\n          type=\"checkbox\"\n          data-skin=\"switch\"\n          checked={wifiRadio.is_enabled}\n          onchange={toggleWifiRadio}\n        />\n      </label>\n    </div>\n  {/if}\n\n  {#if wifiRadio?.is_enabled}\n    {#if connected}\n      <div class=\"network-section\">\n        <div class=\"network-section-title\">{$t(\"connected\")}</div>\n        <div class=\"network-section-entries\">\n          <WlanEntry group={connected} />\n        </div>\n      </div>\n    {/if}\n\n    {#if known.length > 0}\n      <div class=\"network-section\">\n        <div class=\"network-section-title\">{$t(\"saved\")}</div>\n        <div class=\"network-section-entries\">\n          {#each known as group (group[0].ssid)}\n            <WlanEntry {group} />\n          {/each}\n        </div>\n      </div>\n    {/if}\n\n    <div class=\"network-section\">\n      <div class=\"network-section-title\">\n        <span>{$t(\"available\")}</span>\n        {#if globalState.isScanning}\n          <div class=\"network-scanning\">\n            <Icon iconName=\"TbRefresh\" size={12} />\n          </div>\n        {/if}\n      </div>\n      <div class=\"network-section-entries\">\n        {#if unknown.length === 0 && hidden.length === 0}\n          <div class=\"network-empty\">{$t(\"not_found\")}</div>\n        {:else}\n          {#each unknown as group (group[0].ssid)}\n            <WlanEntry {group} />\n          {/each}\n          {#if hidden.length > 0}\n            <WlanEntry group={hidden} />\n          {/if}\n        {/if}\n      </div>\n    </div>\n\n    {#if wifiRadio?.is_enabled}\n      <div class=\"network-footer\">\n        <button data-skin=\"transparent\" onclick={openNetworkSettings}>\n          {$t(\"more\")}\n        </button>\n      </div>\n    {/if}\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/network-popup/components/WlanEntry.svelte",
    "content": "<script lang=\"ts\">\n  import type { WlanBssEntry } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import type { IconName } from \"libs/ui/icons\";\n  import { t } from \"../i18n\";\n  import { useTransition } from \"libs/ui/svelte/utils/hooks.svelte\";\n  import { globalState } from \"../state.svelte\";\n\n  interface Props {\n    group: WlanBssEntry[];\n  }\n\n  let { group }: Props = $props();\n\n  const transition = useTransition();\n\n  let entry = $derived(group[0]!);\n  let isHiddenGroup = $derived(!entry.ssid);\n  let selected = $derived(\n    globalState.selectedSsid !== null &&\n      (globalState.selectedSsid === entry.ssid ||\n        (isHiddenGroup && globalState.selectedSsid === \"__HIDDEN_SSID__\"))\n  );\n\n  let loading = $derived(transition.loading);\n  let error = $derived(transition.error);\n\n  let showFields = $state(false);\n  let ssid = $state(\"\");\n  let password = $state(\"\");\n  let ssidInputRef: HTMLInputElement | undefined = $state();\n  let passwordInputRef: HTMLInputElement | undefined = $state();\n\n  // Reset state when selection changes\n  $effect(() => {\n    if (!selected) {\n      showFields = false;\n      ssid = entry.ssid || \"\";\n      password = \"\";\n      transition.clearError();\n    } else {\n      showFields = !entry.known && (!entry.ssid || entry.secured);\n      ssid = entry.ssid || \"\";\n      // Focus inputs when shown\n      if (showFields) {\n        setTimeout(() => {\n          if (!entry.ssid && ssidInputRef) {\n            ssidInputRef.focus();\n          } else if (passwordInputRef) {\n            passwordInputRef.focus();\n          }\n        }, 0);\n      }\n    }\n  });\n\n  async function onConnection() {\n    transition.start(async () => {\n      if (entry.connected) {\n        await invoke(SeelenCommand.WlanDisconnect);\n        return;\n      }\n\n      if (showFields) {\n        const success = await invoke(SeelenCommand.WlanConnect, {\n          ssid,\n          password,\n          hidden: !entry.ssid,\n        });\n        if (!success) {\n          throw new Error(\"Connection failed\");\n        }\n        return;\n      }\n\n      const profiles = await invoke(SeelenCommand.WlanGetProfiles);\n      const profile = profiles.find((p) => p.ssid === entry.ssid);\n\n      if (!profile) {\n        showFields = true;\n        return;\n      }\n\n      const success = await invoke(SeelenCommand.WlanConnect, {\n        ssid: profile.ssid,\n        password: profile.password,\n        hidden: !entry.ssid,\n      });\n      if (!success) {\n        throw new Error(\"Connection failed\");\n      }\n    });\n  }\n\n  function handleKeydown(e: KeyboardEvent) {\n    if (e.key === \"Enter\") {\n      onConnection();\n    }\n  }\n\n  const signalIcon = $derived.by((): IconName => {\n    if (entry.signal > 75) return \"GrWifi\";\n    if (entry.signal > 50) return \"GrWifiMedium\";\n    if (entry.signal > 25) return \"GrWifiLow\";\n    return \"GrWifiNone\";\n  });\n\n  const frequencies = $derived.by(() => {\n    const FREQUENCY_BANDS = [\n      { name: \"2.4G\", min: 2_400_000, max: 2_484_000 },\n      { name: \"5G\", min: 5_000_000, max: 5_850_000 },\n      { name: \"6G\", min: 5_925_000, max: 7_125_000 },\n    ];\n\n    const detectedBands = new Set<string>();\n    for (const e of group) {\n      for (const band of FREQUENCY_BANDS) {\n        if (e.channelFrequency >= band.min && e.channelFrequency <= band.max) {\n          detectedBands.add(band.name);\n          break;\n        }\n      }\n    }\n    return Array.from(detectedBands);\n  });\n</script>\n\n<div\n  class=\"wlan-entry\"\n  class:wlan-entry-selected={selected}\n  onclick={() => {\n    if (!selected) {\n      globalState.selectedSsid = isHiddenGroup ? \"__HIDDEN_SSID__\" : entry.ssid!;\n    }\n  }}\n  role=\"button\"\n  tabindex=\"0\"\n  onkeydown={(e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      globalState.selectedSsid = isHiddenGroup ? \"__HIDDEN_SSID__\" : entry.ssid!;\n    }\n  }}\n>\n  <div class=\"wlan-entry-info\">\n    <Icon iconName={signalIcon} size={20} />\n    <span class=\"wlan-entry-ssid\">\n      {entry.ssid || `${$t(\"hidden\")} (${group.length})`}\n    </span>\n    {#if !isHiddenGroup && frequencies.length > 0}\n      <div class=\"wlan-entry-band\">{frequencies.join(\"/\")}</div>\n    {/if}\n    {#if !isHiddenGroup && entry.secured}\n      <Icon iconName=\"PiPasswordFill\" title={$t(\"secured\")} />\n    {/if}\n  </div>\n\n  {#if selected}\n    {#if showFields && !loading}\n      <div class=\"wlan-entry-fields\">\n        {#if !entry.ssid}\n          <input\n            type=\"text\"\n            data-skin=\"default\"\n            class:error\n            bind:value={ssid}\n            bind:this={ssidInputRef}\n            placeholder=\"SSID\"\n          />\n        {/if}\n        <input\n          type=\"password\"\n          data-skin=\"default\"\n          class:error\n          bind:value={password}\n          bind:this={passwordInputRef}\n          placeholder={$t(\"password\")}\n          onkeydown={handleKeydown}\n        />\n      </div>\n    {/if}\n\n    {#if !loading}\n      <div class=\"wlan-entry-actions\">\n        <button\n          data-skin={entry.connected ? \"default\" : \"solid\"}\n          onclick={onConnection}\n          disabled={loading}\n        >\n          {entry.connected ? $t(\"disconnect\") : $t(\"connect\")}\n        </button>\n      </div>\n    {/if}\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/af.yml",
    "content": "available: Beskikbare netwerke\nconnect: Koppel\nconnected: Gekoppel aan\ndisconnect: Ontkoppel\nhidden: Versteekte netwerk\nmore: Meer netwerkinstellings\nno_adapter: Geen Wi-Fi-adapter gevind nie\nnot_found: Geen Wi-Fi-netwerke gevind nie\npassword: Wagwoord\nsaved: Gestoorde netwerke\nscanning: Soek vir netwerke\nsecured: Geënkripteerde netwerk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/am.yml",
    "content": "available: የሚገኙ አውታረ መረቦች\nconnect: ተገናኝ\nconnected: ጋር ተገናኝቷል።\ndisconnect: ግንኙነት አቋርጥ\nhidden: የተደበቀ አውታረ መረብ\nmore: ተጨማሪ የአውታረ መረብ ቅንብሮች\nno_adapter: ምንም የWi-Fi አስማሚ አልተገኘም።\nnot_found: ምንም የWi-Fi አውታረ መረቦች አልተገኙም።\npassword: የይለፍ ቃል\nsaved: የተቀመጡ አውታረ መረቦች\nscanning: አውታረ መረቦችን በመቃኘት ላይ\nsecured: የተመሰጠረ አውታረ መረብ\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ar.yml",
    "content": "available: الشبكات المتاحة\nconnect: يتصل\nconnected: متصل ب\ndisconnect: قطع الاتصال\nhidden: الشبكة المخفية\nmore: المزيد من إعدادات الشبكة\nno_adapter: لم يتم العثور على محول Wi-Fi\nnot_found: لم يتم العثور على شبكات Wi-Fi\npassword: كلمة المرور\nsaved: الشبكات المحفوظة\nscanning: المسح للشبكات\nsecured: شبكة مشفرة\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/az.yml",
    "content": "available: Mövcud şəbəkələr\nconnect: Qoşun\nconnected: qoşuldu\ndisconnect: Bağlantını kəsin\nhidden: Gizli şəbəkə\nmore: Daha çox Şəbəkə Parametrləri\nno_adapter: Wi-Fi adapteri tapılmadı\nnot_found: Heç bir Wi-Fi şəbəkəsi tapılmadı\npassword: parol\nsaved: Saxlanan şəbəkələr\nscanning: Şəbəkələrin skan edilməsi\nsecured: Şifrələnmiş şəbəkə\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/bg.yml",
    "content": "available: Налични мрежи\nconnect: Свържете се\nconnected: Свързан с\ndisconnect: Прекъснете връзката\nhidden: Скрита мрежа\nmore: Още мрежови настройки\nno_adapter: Не е намерен Wi-Fi адаптер\nnot_found: Няма намерени Wi-Fi мрежи\npassword: Парола\nsaved: Запазени мрежи\nscanning: Сканиране за мрежи\nsecured: Криптирана мрежа\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/bn.yml",
    "content": "available: উপলব্ধ নেটওয়ার্ক\nconnect: সংযোগ করুন\nconnected: সাথে সংযুক্ত\ndisconnect: সংযোগ বিচ্ছিন্ন করুন\nhidden: লুকানো নেটওয়ার্ক\nmore: আরও নেটওয়ার্ক সেটিংস\nno_adapter: কোনো Wi-Fi অ্যাডাপ্টার পাওয়া যায়নি৷\nnot_found: কোনো Wi-Fi নেটওয়ার্ক পাওয়া যায়নি৷\npassword: পাসওয়ার্ড\nsaved: সংরক্ষিত নেটওয়ার্ক\nscanning: নেটওয়ার্কের জন্য স্ক্যান করা হচ্ছে\nsecured: এনক্রিপ্ট করা নেটওয়ার্ক\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/bs.yml",
    "content": "available: Dostupne mreže\nconnect: Povežite se\nconnected: Povezano na\ndisconnect: Prekini vezu\nhidden: Skrivena mreža\nmore: Više mrežnih postavki\nno_adapter: Nije pronađen Wi-Fi adapter\nnot_found: Nije pronađena nijedna Wi-Fi mreža\npassword: Lozinka\nsaved: Sačuvane mreže\nscanning: Skeniranje za mreže\nsecured: Šifrovana mreža\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ca.yml",
    "content": "available: Xarxes disponibles\nconnect: Connecta't\nconnected: Connectat a\ndisconnect: Desconnecta\nhidden: Xarxa oculta\nmore: Més configuració de xarxa\nno_adapter: No s'ha trobat cap adaptador Wi-Fi\nnot_found: No s'han trobat xarxes Wi-Fi\npassword: Contrasenya\nsaved: Xarxes guardades\nscanning: Escaneig de xarxes\nsecured: Xarxa xifrada\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/cs.yml",
    "content": "available: Dostupné sítě\nconnect: Připojit\nconnected: Připojeno k\ndisconnect: Odpojit\nhidden: Skrytá síť\nmore: Další nastavení sítě\nno_adapter: Nebyl nalezen žádný adaptér Wi-Fi\nnot_found: Nebyly nalezeny žádné sítě Wi-Fi\npassword: Heslo\nsaved: Uložené sítě\nscanning: Vyhledávání sítí\nsecured: Šifrovaná síť\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/cy.yml",
    "content": "available: Rhwydweithiau sydd ar gael\nconnect: Cyswllt\nconnected: Wedi'i gysylltu â\ndisconnect: Datgysylltu\nhidden: Rhwydwaith Cudd\nmore: Mwy o Gosodiadau Rhwydwaith\nno_adapter: Ni chanfuwyd addasydd Wi-Fi\nnot_found: Ni chanfuwyd unrhyw rwydweithiau Wi-Fi\npassword: Cyfrinair\nsaved: Rhwydweithiau wedi'u cadw\nscanning: Sganio am rwydweithiau\nsecured: Rhwydwaith wedi'i amgryptio\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/da.yml",
    "content": "available: Tilgængelige netværk\nconnect: Forbinde\nconnected: Forbundet til\ndisconnect: Afbryde\nhidden: Skjult netværk\nmore: Flere netværksindstillinger\nno_adapter: Ingen Wi-Fi-adapter fundet\nnot_found: Ingen Wi-Fi-netværk fundet\npassword: Adgangskode\nsaved: Gemte netværk\nscanning: Scanning efter netværk\nsecured: Krypteret netværk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/de.yml",
    "content": "available: Verfügbare Netzwerke\nconnect: Verbinden\nconnected: Verbunden mit\ndisconnect: Trennen\nhidden: Verstecktes Netzwerk\nmore: Weitere Netzwerkeinstellungen\nno_adapter: Kein WLAN-Adapter gefunden\nnot_found: Keine WLAN-Netzwerke gefunden\npassword: Passwort\nsaved: Gespeicherte Netzwerke\nscanning: Nach Netzwerken suchen\nsecured: Verschlüsseltes Netzwerk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/el.yml",
    "content": "available: Διαθέσιμα δίκτυα\nconnect: Συνδέω\nconnected: Συνδεδεμένο με\ndisconnect: Αποσυνδέω\nhidden: Κρυφό Δίκτυο\nmore: Περισσότερες ρυθμίσεις δικτύου\nno_adapter: Δεν βρέθηκε προσαρμογέας Wi-Fi\nnot_found: Δεν βρέθηκαν δίκτυα Wi-Fi\npassword: Σύνθημα\nsaved: Αποθηκευμένα δίκτυα\nscanning: Σάρωση για δίκτυα\nsecured: Κρυπτογραφημένο δίκτυο\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/en.yml",
    "content": "available: Available networks\nconnect: Connect\nconnected: Connected to\ndisconnect: Disconnect\nhidden: Hidden Network\nmore: More Network Settings\nno_adapter: No Wi-Fi adapter found\nnot_found: No Wi-Fi networks found\npassword: Password\nsaved: Saved networks\nscanning: Scanning for networks\nsecured: Encrypted network\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/es.yml",
    "content": "available: Redes disponibles\nconnect: Conectar\nconnected: Conectada a\ndisconnect: Desconectar\nhidden: Red oculta\nmore: Más configuraciones de red\nno_adapter: No se encontró ningún adaptador Wi-Fi\nnot_found: No se encontraron redes Wi-Fi\npassword: Contraseña\nsaved: Redes guardadas\nscanning: Escaneo de redes\nsecured: Red cifrada\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/et.yml",
    "content": "available: Saadaolevad võrgud\nconnect: Ühendage\nconnected: 'Ühendatud:'\ndisconnect: Katkesta ühendus\nhidden: Varjatud võrk\nmore: Rohkem võrgusätteid\nno_adapter: Wi-Fi-adapterit ei leitud\nnot_found: WiFi-võrke ei leitud\npassword: Parool\nsaved: Salvestatud võrgud\nscanning: Võrkude otsimine\nsecured: Krüpteeritud võrk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/eu.yml",
    "content": "available: Eskuragarri dauden sareak\nconnect: Konektatu\nconnected: Honekin konektatuta\ndisconnect: Deskonektatu\nhidden: Ezkutuko Sarea\nmore: Sare ezarpen gehiago\nno_adapter: Ez da WiFi egokitzailerik aurkitu\nnot_found: Ez da WiFi sarerik aurkitu\npassword: Pasahitza\nsaved: Gordetako sareak\nscanning: Sareak bilatzen\nsecured: Sare zifratua\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/fa.yml",
    "content": "available: شبکه های موجود\nconnect: اتصال\nconnected: متصل به\ndisconnect: قطع کن\nhidden: شبکه پنهان\nmore: تنظیمات شبکه بیشتر\nno_adapter: هیچ آداپتور Wi-Fi پیدا نشد\nnot_found: هیچ شبکه Wi-Fi یافت نشد\npassword: رمز عبور\nsaved: شبکه های ذخیره شده\nscanning: اسکن شبکه ها\nsecured: شبکه رمزگذاری شده\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/fi.yml",
    "content": "available: Käytettävissä olevat verkot\nconnect: Yhdistä\nconnected: 'Yhdistetty:'\ndisconnect: Katkaise yhteys\nhidden: Piilotettu verkko\nmore: Lisää verkkoasetuksia\nno_adapter: Wi-Fi-sovitinta ei löydy\nnot_found: Wi-Fi-verkkoja ei löytynyt\npassword: Salasana\nsaved: Tallennetut verkot\nscanning: Etsii verkkoja\nsecured: Salattu verkko\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/fr.yml",
    "content": "available: Réseaux disponibles\nconnect: Connecter\nconnected: Connecté à\ndisconnect: Déconnecter\nhidden: Réseau caché\nmore: Plus de paramètres réseau\nno_adapter: Aucun adaptateur Wi-Fi trouvé\nnot_found: Aucun réseau Wi-Fi trouvé\npassword: Mot de passe\nsaved: Réseaux enregistrés\nscanning: Recherche de réseaux\nsecured: Réseau crypté\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/gu.yml",
    "content": "available: ઉપલબ્ધ નેટવર્ક્સ\nconnect: કનેક્ટ કરો\nconnected: સાથે જોડાયેલ છે\ndisconnect: ડિસ્કનેક્ટ કરો\nhidden: છુપાયેલ નેટવર્ક\nmore: વધુ નેટવર્ક સેટિંગ્સ\nno_adapter: કોઈ Wi-Fi એડેપ્ટર મળ્યું નથી\nnot_found: કોઈ Wi-Fi નેટવર્ક મળ્યું નથી\npassword: પાસવર્ડ\nsaved: સાચવેલા નેટવર્ક્સ\nscanning: નેટવર્ક્સ માટે સ્કેનિંગ\nsecured: એન્ક્રિપ્ટેડ નેટવર્ક\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/he.yml",
    "content": "available: רשתות זמינות\nconnect: לְחַבֵּר\nconnected: מחובר ל\ndisconnect: לְנַתֵק\nhidden: רשת נסתרת\nmore: הגדרות רשת נוספות\nno_adapter: לא נמצא מתאם Wi-Fi\nnot_found: לא נמצאו רשתות Wi-Fi\npassword: סִיסמָה\nsaved: רשתות שמורות\nscanning: סריקה לאיתור רשתות\nsecured: רשת מוצפנת\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/hi.yml",
    "content": "available: उपलब्ध नेटवर्क\nconnect: जोड़ना\nconnected: से जुड़ा\ndisconnect: डिस्कनेक्ट\nhidden: छिपा हुआ नेटवर्क\nmore: अधिक नेटवर्क सेटिंग्स\nno_adapter: कोई वाई-फ़ाई एडाप्टर नहीं मिला\nnot_found: कोई वाई-फ़ाई नेटवर्क नहीं मिला\npassword: पासवर्ड\nsaved: सहेजे गए नेटवर्क\nscanning: नेटवर्क के लिए स्कैनिंग\nsecured: एन्क्रिप्टेड नेटवर्क\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/hr.yml",
    "content": "available: Dostupne mreže\nconnect: Poveži se\nconnected: Povezan s\ndisconnect: Prekini vezu\nhidden: Skrivena mreža\nmore: Više mrežnih postavki\nno_adapter: Wi-Fi adapter nije pronađen\nnot_found: Wi-Fi mreže nisu pronađene\npassword: Lozinka\nsaved: Spremljene mreže\nscanning: Traženje mreža\nsecured: Šifrirana mreža\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/hu.yml",
    "content": "available: Elérhető hálózatok\nconnect: Csatlakozás\nconnected: Csatlakozva a következőhöz\ndisconnect: Leválasztás\nhidden: Rejtett hálózat\nmore: További hálózati beállítások\nno_adapter: Nem található Wi-Fi adapter\nnot_found: Nem található Wi-Fi hálózat\npassword: Jelszó\nsaved: Mentett hálózatok\nscanning: Hálózatok keresése\nsecured: Titkosított hálózat\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/hy.yml",
    "content": "available: Հասանելի ցանցեր\nconnect: Միացնել\nconnected: Միացված է\ndisconnect: Անջատել\nhidden: Թաքնված ցանց\nmore: Լրացուցիչ ցանցային կարգավորումներ\nno_adapter: Wi-Fi ադապտեր չի գտնվել\nnot_found: Wi-Fi ցանցեր չեն գտնվել\npassword: Գաղտնաբառ\nsaved: Պահպանված ցանցեր\nscanning: Ցանցերի սկանավորում\nsecured: Կոդավորված ցանց\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/id.yml",
    "content": "available: Jaringan yang tersedia\nconnect: Menghubungkan\nconnected: Terhubung ke\ndisconnect: Memutuskan\nhidden: Jaringan Tersembunyi\nmore: Pengaturan Jaringan Lainnya\nno_adapter: Tidak ditemukan adaptor Wi-Fi\nnot_found: Tidak ada jaringan Wi-Fi yang ditemukan\npassword: Kata sandi\nsaved: Jaringan yang disimpan\nscanning: Memindai jaringan\nsecured: Jaringan terenkripsi\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/is.yml",
    "content": "available: Tiltæk netkerfi\nconnect: Tengdu\nconnected: Tengdur við\ndisconnect: Aftengdu\nhidden: Falið net\nmore: Fleiri netstillingar\nno_adapter: Enginn Wi-Fi millistykki fannst\nnot_found: Ekkert Wi-Fi net fannst\npassword: Lykilorð\nsaved: Vistað netkerfi\nscanning: Leitar að netkerfum\nsecured: Dulkóðað net\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/it.yml",
    "content": "available: Reti disponibili\nconnect: Collegare\nconnected: Connesso a\ndisconnect: Disconnetti\nhidden: Rete nascosta\nmore: Altre impostazioni di rete\nno_adapter: Nessun adattatore Wi-Fi trovato\nnot_found: Nessuna rete Wi-Fi trovata\npassword: Password\nsaved: Reti salvate\nscanning: Scansione delle reti\nsecured: Rete crittografata\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ja.yml",
    "content": "available: 利用可能なネットワーク\nconnect: 接続\nconnected: 接続済み\ndisconnect: 切断\nhidden: 隠れたネットワーク\nmore: その他のネットワーク設定\nno_adapter: Wi-Fiアダプターが見つかりません\nnot_found: Wi-Fi ネットワークが見つかりません\npassword: パスワード\nsaved: 保存済みネットワーク\nscanning: ネットワークのスキャン\nsecured: 暗号化されたネットワーク\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ka.yml",
    "content": "available: ხელმისაწვდომი ქსელები\nconnect: დაკავშირება\nconnected: დაკავშირებულია\ndisconnect: გათიშვა\nhidden: დამალული ქსელი\nmore: მეტი ქსელის პარამეტრები\nno_adapter: Wi-Fi ადაპტერი ვერ მოიძებნა\nnot_found: Wi-Fi ქსელები ვერ მოიძებნა\npassword: პაროლი\nsaved: შენახული ქსელები\nscanning: ქსელების სკანირება\nsecured: დაშიფრული ქსელი\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/km.yml",
    "content": "available: បណ្តាញដែលមាន\nconnect: ភ្ជាប់\nconnected: បានភ្ជាប់ទៅ\ndisconnect: ផ្តាច់\nhidden: បណ្តាញលាក់\nmore: ការកំណត់បណ្តាញច្រើនទៀត\nno_adapter: រកមិនឃើញអាដាប់ទ័រវ៉ាយហ្វាយទេ។\nnot_found: រកមិនឃើញបណ្តាញ Wi-Fi ទេ។\npassword: ពាក្យសម្ងាត់\nsaved: បណ្តាញដែលបានរក្សាទុក\nscanning: កំពុងស្កេនរកបណ្តាញ\nsecured: បណ្តាញដែលបានអ៊ិនគ្រីប\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ko.yml",
    "content": "available: 사용 가능한 네트워크\nconnect: 연결하다\nconnected: 연결됨\ndisconnect: 연결 끊기\nhidden: 숨겨진 네트워크\nmore: 추가 네트워크 설정\nno_adapter: Wi-Fi 어댑터를 찾을 수 없습니다.\nnot_found: Wi-Fi 네트워크를 찾을 수 없습니다.\npassword: 비밀번호\nsaved: 저장된 네트워크\nscanning: 네트워크 검색 중\nsecured: 암호화된 네트워크\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ku.yml",
    "content": "available: Torên berdest\nconnect: Bihevgirêdan\nconnected: Girêdayî\ndisconnect: Hevqetandin\nhidden: Tora veşartî\nmore: More Settings Network\nno_adapter: Adapterek Wi-Fi nehat dîtin\nnot_found: Tora Wi-Fi nehat dîtin\npassword: Şîfre\nsaved: torên Saved\nscanning: Ji bo toran lêgerîn\nsecured: Tora şîfrekirî\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/lb.yml",
    "content": "available: Verfügbar Netzwierker\nconnect: Connect\nconnected: Verbonne mat\ndisconnect: Trennen\nhidden: Hidden Network\nmore: Méi Network Settings\nno_adapter: Kee Wi-Fi Adapter fonnt\nnot_found: Keng Wi-Fi Netzwierker fonnt\npassword: Passwuert\nsaved: Gespäichert Netzwierker\nscanning: Scannen fir Netzwierker\nsecured: Verschlësselte Reseau\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/lo.yml",
    "content": "available: ເຄືອຂ່າຍທີ່ມີຢູ່\nconnect: ເຊື່ອມຕໍ່\nconnected: ເຊື່ອມຕໍ່ກັບ\ndisconnect: ຕັດການເຊື່ອມຕໍ່\nhidden: ເຄືອຂ່າຍທີ່ເຊື່ອງໄວ້\nmore: ການຕັ້ງຄ່າເຄືອຂ່າຍເພີ່ມເຕີມ\nno_adapter: ບໍ່ພົບອະແດັບເຕີ Wi-Fi\nnot_found: ບໍ່ພົບເຄືອຂ່າຍ Wi-Fi\npassword: ລະຫັດຜ່ານ\nsaved: ເຄືອຂ່າຍທີ່ບັນທຶກໄວ້\nscanning: ກຳລັງສະແກນຫາເຄືອຂ່າຍ\nsecured: ເຄືອຂ່າຍເຂົ້າລະຫັດ\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/lt.yml",
    "content": "available: Galimi tinklai\nconnect: Prisijunkite\nconnected: Prisijungta prie\ndisconnect: Atsijungti\nhidden: Paslėptas tinklas\nmore: Daugiau tinklo nustatymų\nno_adapter: Wi-Fi adapteris nerastas\nnot_found: Nerasta jokių „Wi-Fi“ tinklų\npassword: Slaptažodis\nsaved: Išsaugoti tinklai\nscanning: Tinklų nuskaitymas\nsecured: Šifruotas tinklas\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/lv.yml",
    "content": "available: Pieejamie tīkli\nconnect: Savienot\nconnected: Savienots ar\ndisconnect: Atvienot\nhidden: Slēpts tīkls\nmore: Vairāk tīkla iestatījumu\nno_adapter: Wi-Fi adapteris netika atrasts\nnot_found: Nav atrasts neviens Wi-Fi tīkls\npassword: Parole\nsaved: Saglabātie tīkli\nscanning: Tīklu meklēšana\nsecured: Šifrēts tīkls\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/mk.yml",
    "content": "available: Достапни мрежи\nconnect: Поврзете се\nconnected: Поврзан со\ndisconnect: Исклучете се\nhidden: Скриена мрежа\nmore: Повеќе мрежни поставки\nno_adapter: Не е пронајден адаптер за Wi-Fi\nnot_found: Не се пронајдени Wi-Fi мрежи\npassword: Лозинка\nsaved: Зачувани мрежи\nscanning: Скенирање за мрежи\nsecured: Шифрирана мрежа\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/mn.yml",
    "content": "available: Боломжтой сүлжээнүүд\nconnect: Холбох\nconnected: '-д холбогдсон'\ndisconnect: Салгах\nhidden: Далд сүлжээ\nmore: Нэмэлт сүлжээний тохиргоо\nno_adapter: Wi-Fi адаптер олдсонгүй\nnot_found: Wi-Fi сүлжээ олдсонгүй\npassword: Нууц үг\nsaved: Хадгалсан сүлжээнүүд\nscanning: Сүлжээг хайж байна\nsecured: Шифрлэгдсэн сүлжээ\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ms.yml",
    "content": "available: Rangkaian yang tersedia\nconnect: Sambung\nconnected: Disambungkan ke\ndisconnect: Putuskan sambungan\nhidden: Rangkaian Tersembunyi\nmore: Lagi Tetapan Rangkaian\nno_adapter: Tiada penyesuai Wi-Fi ditemui\nnot_found: Tiada rangkaian Wi-Fi ditemui\npassword: Kata laluan\nsaved: Rangkaian yang disimpan\nscanning: Mengimbas untuk rangkaian\nsecured: Rangkaian yang disulitkan\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/mt.yml",
    "content": "available: Netwerks disponibbli\nconnect: Qabbad\nconnected: Konnessi ma'\ndisconnect: Skonnettja\nhidden: Netwerk Moħbi\nmore: Aktar Settings tan-Netwerk\nno_adapter: Ma nstab l-ebda adapter Wi-Fi\nnot_found: Ma nstab l-ebda netwerk Wi-Fi\npassword: Password\nsaved: Netwerks salvati\nscanning: Skennjar għal netwerks\nsecured: Netwerk kriptat\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ne.yml",
    "content": "available: उपलब्ध नेटवर्कहरू\nconnect: जडान गर्नुहोस्\nconnected: मा जोडिएको छ\ndisconnect: जडान विच्छेद गर्नुहोस्\nhidden: लुकेको नेटवर्क\nmore: थप नेटवर्क सेटिङहरू\nno_adapter: कुनै Wi-Fi एडाप्टर फेला परेन\nnot_found: कुनै Wi-Fi नेटवर्क फेला परेन\npassword: पासवर्ड\nsaved: सुरक्षित नेटवर्कहरू\nscanning: नेटवर्कहरूको लागि स्क्यान गर्दै\nsecured: ईन्क्रिप्टेड नेटवर्क\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/nl.yml",
    "content": "available: Beschikbare netwerken\nconnect: Verbinden\nconnected: Verbonden met\ndisconnect: Verbreek de verbinding\nhidden: Verborgen netwerk\nmore: Meer netwerkinstellingen\nno_adapter: Geen wifi-adapter gevonden\nnot_found: Geen wifi-netwerken gevonden\npassword: Wachtwoord\nsaved: Opgeslagen netwerken\nscanning: Scannen naar netwerken\nsecured: Versleuteld netwerk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/no.yml",
    "content": "available: Tilgjengelige nettverk\nconnect: Koble til\nconnected: Koblet til\ndisconnect: Frakople\nhidden: Skjult nettverk\nmore: Flere nettverksinnstillinger\nno_adapter: Finner ingen Wi-Fi-adapter\nnot_found: Finner ingen Wi-Fi-nettverk\npassword: Passord\nsaved: Lagrede nettverk\nscanning: Skanner etter nettverk\nsecured: Kryptert nettverk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/pa.yml",
    "content": "available: ਉਪਲਬਧ ਨੈੱਟਵਰਕ\nconnect: ਜੁੜੋ\nconnected: ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ\ndisconnect: ਡਿਸਕਨੈਕਟ ਕਰੋ\nhidden: ਲੁਕਿਆ ਹੋਇਆ ਨੈੱਟਵਰਕ\nmore: ਹੋਰ ਨੈੱਟਵਰਕ ਸੈਟਿੰਗਾਂ\nno_adapter: ਕੋਈ Wi-Fi ਅਡਾਪਟਰ ਨਹੀਂ ਮਿਲਿਆ\nnot_found: ਕੋਈ Wi-Fi ਨੈੱਟਵਰਕ ਨਹੀਂ ਮਿਲਿਆ\npassword: ਪਾਸਵਰਡ\nsaved: ਸੁਰੱਖਿਅਤ ਕੀਤੇ ਨੈੱਟਵਰਕ\nscanning: ਨੈੱਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ\nsecured: ਐਨਕ੍ਰਿਪਟਡ ਨੈੱਟਵਰਕ\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/pl.yml",
    "content": "available: Dostępne sieci\nconnect: Łączyć\nconnected: Połączony z\ndisconnect: Odłączyć\nhidden: Ukryta sieć\nmore: Więcej ustawień sieciowych\nno_adapter: Nie znaleziono adaptera Wi-Fi\nnot_found: Nie znaleziono sieci Wi-Fi\npassword: Hasło\nsaved: Zapisane sieci\nscanning: Skanowanie w poszukiwaniu sieci\nsecured: Szyfrowana sieć\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ps.yml",
    "content": "available: شته شبکې\nconnect: نښلول\nconnected: سره نښلول شوی\ndisconnect: منحل کول\nhidden: پټه شبکه\nmore: نور د شبکې ترتیبات\nno_adapter: هیڅ Wi-Fi اډاپټر ونه موندل شو\nnot_found: هیڅ Wi-Fi شبکه ونه موندل شوه\npassword: رمز\nsaved: خوندي شوي شبکې\nscanning: د شبکو لپاره سکین کول\nsecured: کوډ شوی شبکه\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/pt-BR.yml",
    "content": "available: Redes disponíveis\nconnect: Conectar\nconnected: Conectado a\ndisconnect: Desconectar\nhidden: Rede Oculta\nmore: Mais configurações de rede\nno_adapter: Nenhum adaptador Wi-Fi encontrado\nnot_found: Nenhuma rede Wi-Fi encontrada\npassword: Senha\nsaved: Redes salvas\nscanning: Procurando redes\nsecured: Rede criptografada\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/pt-PT.yml",
    "content": "available: Redes disponíveis\nconnect: Ligar\nconnected: Conectado a\ndisconnect: Desconectar\nhidden: Rede Oculta\nmore: Mais configurações de rede\nno_adapter: Nenhum adaptador Wi-Fi encontrado\nnot_found: Nenhuma rede Wi-Fi encontrada\npassword: Palavra-passe\nsaved: Redes salvas\nscanning: Procurando redes\nsecured: Rede criptografada\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ro.yml",
    "content": "available: Rețele disponibile\nconnect: Conectați-vă\nconnected: Conectat la\ndisconnect: Deconecta\nhidden: Rețea ascunsă\nmore: Mai multe setări de rețea\nno_adapter: Nu a fost găsit niciun adaptor Wi-Fi\nnot_found: Nu au fost găsite rețele Wi-Fi\npassword: Parolă\nsaved: Rețele salvate\nscanning: Scanare pentru rețele\nsecured: Rețea criptată\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ru.yml",
    "content": "available: Доступные сети\nconnect: Соединять\nconnected: Подключено к\ndisconnect: Отключить\nhidden: Скрытая сеть\nmore: Дополнительные настройки сети\nno_adapter: Адаптер Wi-Fi не найден\nnot_found: Сети Wi-Fi не найдены\npassword: Пароль\nsaved: Сохраненные сети\nscanning: Сканирование сетей\nsecured: Зашифрованная сеть\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/si.yml",
    "content": "available: පවතින ජාල\nconnect: සම්බන්ධ කරන්න\nconnected: වෙත සම්බන්ධ කර ඇත\ndisconnect: විසන්ධි කරන්න\nhidden: සැඟවුණු ජාලය\nmore: තවත් ජාල සැකසුම්\nno_adapter: Wi-Fi ඇඩැප්ටරයක් ​​හමු නොවීය\nnot_found: Wi-Fi ජාල කිසිවක් හමු නොවීය\npassword: මුරපදය\nsaved: සුරැකි ජාල\nscanning: ජාල සඳහා ස්කෑන් කිරීම\nsecured: සංකේතාත්මක ජාලය\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/sk.yml",
    "content": "available: Dostupné siete\nconnect: Pripojte sa\nconnected: Pripojené k\ndisconnect: Odpojiť\nhidden: Skrytá sieť\nmore: Ďalšie nastavenia siete\nno_adapter: Nenašiel sa žiadny adaptér Wi-Fi\nnot_found: Nenašli sa žiadne siete Wi-Fi\npassword: heslo\nsaved: Uložené siete\nscanning: Skenovanie sietí\nsecured: Šifrovaná sieť\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/so.yml",
    "content": "available: Shabakado la heli karo\nconnect: Ku xidhnow\nconnected: Ku xidhan\ndisconnect: Xiriirka ka jar\nhidden: Shabakadda Qarsoon\nmore: Dejinta Shabakad badan\nno_adapter: Lama helin adabtarada Wi-Fi\nnot_found: Lama helin shabakadaha Wi-Fi\npassword: Furaha\nsaved: Shabakado la keydiyay\nscanning: Sawirka shabakadaha\nsecured: Shabakadda qarsoon\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/sr.yml",
    "content": "available: Доступне мреже\nconnect: Повежите се\nconnected: Повезано са\ndisconnect: Прекини везу\nhidden: Скривена мрежа\nmore: Више мрежних подешавања\nno_adapter: Није пронађен Ви-Фи адаптер\nnot_found: Није пронађена ниједна Ви-Фи мрежа\npassword: Лозинка\nsaved: Сачуване мреже\nscanning: Скенирање за мреже\nsecured: Шифрована мрежа\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/sv.yml",
    "content": "available: Tillgängliga nätverk\nconnect: Ansluta\nconnected: Ansluten till\ndisconnect: Koppla från\nhidden: Dolt nätverk\nmore: Fler nätverksinställningar\nno_adapter: Ingen Wi-Fi-adapter hittades\nnot_found: Inga Wi-Fi-nätverk hittades\npassword: Lösenord\nsaved: Sparade nätverk\nscanning: Söker efter nätverk\nsecured: Krypterat nätverk\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/sw.yml",
    "content": "available: Mitandao inayopatikana\nconnect: Unganisha\nconnected: Imeunganishwa kwa\ndisconnect: Tenganisha\nhidden: Mtandao Uliofichwa\nmore: Mipangilio Zaidi ya Mtandao\nno_adapter: Hakuna adapta ya Wi-Fi iliyopatikana\nnot_found: Hakuna mitandao ya Wi-Fi iliyopatikana\npassword: Nenosiri\nsaved: Mitandao iliyohifadhiwa\nscanning: Inatafuta mitandao\nsecured: Mtandao uliosimbwa kwa njia fiche\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ta.yml",
    "content": "available: கிடைக்கும் நெட்வொர்க்குகள்\nconnect: இணைக்கவும்\nconnected: இணைக்கப்பட்டது\ndisconnect: துண்டிக்கவும்\nhidden: மறைக்கப்பட்ட நெட்வொர்க்\nmore: மேலும் நெட்வொர்க் அமைப்புகள்\nno_adapter: வைஃபை அடாப்டர் இல்லை\nnot_found: வைஃபை நெட்வொர்க்குகள் எதுவும் இல்லை\npassword: கடவுச்சொல்\nsaved: சேமிக்கப்பட்ட நெட்வொர்க்குகள்\nscanning: நெட்வொர்க்குகளை ஸ்கேன் செய்கிறது\nsecured: மறைகுறியாக்கப்பட்ட நெட்வொர்க்\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/te.yml",
    "content": "available: అందుబాటులో ఉన్న నెట్‌వర్క్‌లు\nconnect: కనెక్ట్ చేయండి\nconnected: కి కనెక్ట్ చేయబడింది\ndisconnect: డిస్‌కనెక్ట్ చేయండి\nhidden: దాచిన నెట్‌వర్క్\nmore: మరిన్ని నెట్‌వర్క్ సెట్టింగ్‌లు\nno_adapter: Wi-Fi అడాప్టర్ కనుగొనబడలేదు\nnot_found: Wi-Fi నెట్‌వర్క్‌లు ఏవీ కనుగొనబడలేదు\npassword: పాస్వర్డ్\nsaved: సేవ్ చేసిన నెట్‌వర్క్‌లు\nscanning: నెట్‌వర్క్‌ల కోసం స్కాన్ చేస్తోంది\nsecured: ఎన్‌క్రిప్టెడ్ నెట్‌వర్క్\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/tg.yml",
    "content": "available: Шабакаҳои дастрас\nconnect: Пайваст кунед\nconnected: Ба\ndisconnect: Қатъ кунед\nhidden: Шабакаи пинҳонӣ\nmore: Танзимоти бештари шабака\nno_adapter: Ягон адаптери Wi-Fi ёфт нашуд\nnot_found: Ягон шабакаҳои Wi-Fi ёфт нашуд\npassword: Рамз\nsaved: Шабакаҳои захирашуда\nscanning: Сканкунии шабакаҳо\nsecured: Шабакаи рамзгузорӣ\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/th.yml",
    "content": "available: เครือข่ายที่มีอยู่\nconnect: เชื่อมต่อ\nconnected: เชื่อมต่อกับ\ndisconnect: ตัดการเชื่อมต่อ\nhidden: เครือข่ายที่ซ่อนอยู่\nmore: การตั้งค่าเครือข่ายเพิ่มเติม\nno_adapter: ไม่พบอแด็ปเตอร์ Wi-Fi\nnot_found: ไม่พบเครือข่าย Wi-Fi\npassword: รหัสผ่าน\nsaved: เครือข่ายที่บันทึกไว้\nscanning: กำลังสแกนหาเครือข่าย\nsecured: เครือข่ายที่เข้ารหัส\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/tl.yml",
    "content": "available: Magagamit na mga network\nconnect: Kumonekta\nconnected: Nakakonekta sa\ndisconnect: Idiskonekta\nhidden: Nakatagong Network\nmore: Higit pang Mga Setting ng Network\nno_adapter: Walang nakitang Wi-Fi adapter\nnot_found: Walang nakitang mga Wi-Fi network\npassword: Password\nsaved: Mga naka-save na network\nscanning: Pag-scan para sa mga network\nsecured: Naka-encrypt na network\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/tr.yml",
    "content": "available: Mevcut ağlar\nconnect: Bağlamak\nconnected: 'Şuraya bağlanıldı:'\ndisconnect: Bağlantıyı kes\nhidden: Gizli Ağ\nmore: Diğer Ağ Ayarları\nno_adapter: Wi-Fi bağdaştırıcısı bulunamadı\nnot_found: Hiçbir Wi-Fi ağı bulunamadı\npassword: Şifre\nsaved: Kayıtlı ağlar\nscanning: Ağlar taranıyor\nsecured: Şifreli ağ\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/uk.yml",
    "content": "available: Доступні мережі\nconnect: Підключитися\nconnected: Підключено до\ndisconnect: Відключити\nhidden: Прихована мережа\nmore: Більше налаштувань мережі\nno_adapter: Адаптер Wi-Fi не знайдено\nnot_found: Немає мереж Wi-Fi\npassword: Пароль\nsaved: Збережені мережі\nscanning: Сканування мереж\nsecured: Зашифрована мережа\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/ur.yml",
    "content": "available: دستیاب نیٹ ورک\nconnect: جڑیں\nconnected: سے منسلک\ndisconnect: منقطع\nhidden: پوشیدہ نیٹ ورک\nmore: نیٹ ورک کی مزید ترتیبات\nno_adapter: کوئی وائی فائی اڈاپٹر نہیں ملا\nnot_found: کوئی وائی فائی نیٹ ورک نہیں ملا\npassword: پاس ورڈ\nsaved: محفوظ کردہ نیٹ ورکس\nscanning: نیٹ ورکس کے لئے اسکیننگ\nsecured: خفیہ کردہ نیٹ ورک\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/uz.yml",
    "content": "available: Mavjud tarmoqlar\nconnect: Ulanish\nconnected: ga ulangan\ndisconnect: Ulanishni uzing\nhidden: Yashirin tarmoq\nmore: Qo'shimcha tarmoq sozlamalari\nno_adapter: Hech qanday Wi-Fi adapteri topilmadi\nnot_found: Wi-Fi tarmoqlari topilmadi\npassword: Parol\nsaved: Saqlangan tarmoqlar\nscanning: Tarmoqlarni skanerlash\nsecured: Shifrlangan tarmoq\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/vi.yml",
    "content": "available: Mạng có sẵn\nconnect: Kết nối\nconnected: Đã kết nối với\ndisconnect: Ngắt kết nối\nhidden: Mạng ẩn\nmore: Thêm cài đặt mạng\nno_adapter: Không tìm thấy bộ chuyển đổi Wi-Fi\nnot_found: Không tìm thấy mạng Wi-Fi\npassword: Mật khẩu\nsaved: Mạng đã lưu\nscanning: Đang quét mạng\nsecured: Mạng được mã hóa\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/yo.yml",
    "content": "available: Awọn nẹtiwọki ti o wa\nconnect: Sopọ\nconnected: Ti sopọ si\ndisconnect: Ge asopọ\nhidden: Nẹtiwọọki ti o farasin\nmore: Die Network Eto\nno_adapter: Ko si ohun ti nmu badọgba Wi-Fi ri\nnot_found: Ko ri awọn nẹtiwọki Wi-Fi\npassword: Ọrọigbaniwọle\nsaved: Awọn nẹtiwọki ti a fipamọ\nscanning: Ṣiṣayẹwo fun awọn nẹtiwọki\nsecured: Nẹtiwọọki ti paroko\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/zh-CN.yml",
    "content": "available: 可用网络\nconnect: 连接\nconnected: 连接到\ndisconnect: 断开\nhidden: 隐藏网络\nmore: 更多网络设置\nno_adapter: 找不到 Wi-Fi 适配器\nnot_found: 未找到 Wi-Fi 网络\npassword: 密码\nsaved: 已保存的网络\nscanning: 扫描网络\nsecured: 加密网络\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/zh-TW.yml",
    "content": "available: 可用網絡\nconnect: 連接\nconnected: 連接到\ndisconnect: 斷開\nhidden: 隱藏網絡\nmore: 更多網絡設置\nno_adapter: 找不到 Wi-Fi 適配器\nnot_found: 未找到 Wi-Fi 網絡\npassword: 密碼\nsaved: 已保存的網絡\nscanning: 掃描網絡\nsecured: 加密網絡\n"
  },
  {
    "path": "src/ui/svelte/network-popup/i18n/translations/zu.yml",
    "content": "available: Amanethiwekhi atholakalayo\nconnect: Xhuma\nconnected: Kuxhunywe ku\ndisconnect: Nqamula\nhidden: Inethiwekhi Efihliwe\nmore: Izilungiselelo Zenethiwekhi Eziningi\nno_adapter: Ayikho i-adaptha ye-Wi-Fi etholakele\nnot_found: Awekho amanethiwekhi e-Wi-Fi atholiwe\npassword: Iphasiwedi\nsaved: Amanethiwekhi alondoloziwe\nscanning: Iskenela amanethiwekhi\nsecured: Inethiwekhi ebethelwe\n"
  },
  {
    "path": "src/ui/svelte/network-popup/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/network-popup/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/network-popup/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { RadioDeviceKind, type WlanBssEntry } from \"@seelen-ui/lib/types\";\nimport { locale } from \"./i18n/index.ts\";\nimport { writable } from \"svelte/store\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nlet widget = Widget.getCurrent();\n\nlet settings = writable(await Settings.getAsync());\nSettings.onChange((s) => settings.set(s));\nsettings.subscribe((settings) => {\n  locale.set(settings.language || \"en\");\n});\n\nlet wlanBssEntries = $state<WlanBssEntry[]>([]);\nsubscribe(SeelenEvent.NetworkWlanScanned, ({ payload }) => {\n  wlanBssEntries = payload;\n});\n\nlet radios = lazyRune(() => invoke(SeelenCommand.GetRadios));\nsubscribe(SeelenEvent.RadiosChanged, radios.setByPayload);\nawait radios.init();\n\nlet isScanning = $state(false);\nlet selectedSsid = $state<string | null>(null);\n\nwidget.window.onFocusChanged(async (e) => {\n  if (e.payload) {\n    await invoke(SeelenCommand.WlanStartScanning);\n    isScanning = true;\n  } else {\n    await invoke(SeelenCommand.WlanStopScanning);\n    isScanning = false;\n    selectedSsid = null;\n  }\n});\n\nclass State {\n  get wifiRadio() {\n    return radios.value.find((radio) => radio.kind === RadioDeviceKind.WiFi);\n  }\n\n  get wlanBssEntries() {\n    return wlanBssEntries;\n  }\n\n  get isScanning() {\n    return isScanning;\n  }\n  set isScanning(value: boolean) {\n    isScanning = value;\n  }\n\n  get selectedSsid() {\n    return selectedSsid;\n  }\n  set selectedSsid(value: string | null) {\n    selectedSsid = value;\n  }\n}\n\nexport const globalState = new State();\n"
  },
  {
    "path": "src/ui/svelte/notifications/app.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"./state.svelte\";\n  import { Widget, invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { t } from \"./i18n/index.ts\";\n  import Notification from \"./components/Notification.svelte\";\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n\n  async function handleClearAll() {\n    try {\n      await invoke(SeelenCommand.NotificationsCloseAll);\n    } catch (error) {\n      console.error(\"Failed to clear notifications:\", error);\n    }\n  }\n\n  async function handleOpenSettings() {\n    try {\n      await invoke(SeelenCommand.OpenFile, {\n        path: \"ms-settings:notifications\",\n      });\n    } catch (error) {\n      console.error(\"Failed to open notification settings:\", error);\n    }\n  }\n</script>\n\n<div class=\"slu-standard-popover notifications-popup\">\n  <div class=\"notifications-popup-header\">\n    <span>{$t(\"title\")}</span>\n    <button data-skin=\"default\" onclick={handleClearAll}>\n      {$t(\"clear\")}\n    </button>\n  </div>\n\n  <div class=\"notifications-popup-body\">\n    {#each globalState.notifications as notification (notification.id)}\n      <Notification {notification} />\n    {/each}\n\n    {#if globalState.notifications.length === 0}\n      <div class=\"notifications-popup-empty\">\n        <p>{$t(\"empty\")}</p>\n      </div>\n    {/if}\n  </div>\n\n  <div class=\"notifications-popup-footer\">\n    <button data-skin=\"transparent\" onclick={handleOpenSettings}>\n      {$t(\"settings\")}\n    </button>\n  </div>\n</div>\n\n<style>\n  .notifications-popup {\n    background: var(--config-background-color, var(--color-gray-100));\n    color: var(--config-foreground-color, var(--color-gray-900));\n    border-radius: 8px;\n    overflow: hidden;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/notifications/components/Notification.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import type {\n    AppNotification,\n    ToastActionActivationType,\n    ToastActionsChild,\n    ToastBindingChild,\n    ToastImage,\n  } from \"@seelen-ui/lib/types\";\n  import { WindowsDateFileTimeToDate } from \"libs/ui/svelte/utils\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import FileIcon from \"libs/ui/svelte/components/Icon/FileIcon.svelte\";\n  import moment from \"moment\";\n\n  interface Props {\n    notification: AppNotification;\n    onClose?: () => void;\n  }\n\n  let { notification, onClose }: Props = $props();\n\n  let inputData = $state<Record<string, string>>({});\n\n  const toastContent = $derived(splitToastContent(notification));\n  const logoImage = $derived(toastContent.logoImage);\n  const heroImage = $derived(toastContent.heroImage);\n  const body = $derived(toastContent.body);\n  const actions = $derived(toastContent.actions);\n\n  function handleInputChange(key: string, value: string) {\n    inputData = { ...inputData, [key]: value };\n  }\n\n  async function handleAction(args: string, method: ToastActionActivationType) {\n    try {\n      switch (method) {\n        case \"Protocol\":\n          await invoke(SeelenCommand.OpenFile, { path: args });\n          break;\n        default:\n          await invoke(SeelenCommand.ActivateNotification, {\n            umid: notification.appUmid,\n            args,\n            inputData,\n          });\n      }\n    } catch (error) {\n      console.error(\"Failed to handle notification action:\", error);\n    }\n  }\n\n  async function handleClose(e: MouseEvent) {\n    e.stopPropagation();\n    try {\n      await invoke(SeelenCommand.NotificationsClose, { id: notification.id });\n      onClose?.();\n    } catch (error) {\n      console.error(\"Failed to close notification:\", error);\n    }\n  }\n\n  function handleNotificationClick() {\n    if (notification.content[\"@launch\"]) {\n      handleAction(notification.content[\"@launch\"], notification.content[\"@activationType\"]);\n    }\n  }\n\n  function splitToastContent(notification: AppNotification) {\n    const toast = notification.content;\n    const template = toast.visual.binding[\"@template\"];\n    const actions = toast.actions?.$value || [];\n\n    let logoImage: ToastImage | null = null;\n    let heroImage: ToastImage | null = null;\n    const body: ToastBindingChild[] = [];\n\n    for (const entry of toast.visual.binding.$value) {\n      if (\"image\" in entry) {\n        if (\n          entry.image[\"@placement\"] === \"AppLogoOverride\" ||\n          (!entry.image[\"@placement\"] && !logoImage && template.startsWith(\"ToastImageAndText\"))\n        ) {\n          logoImage = entry.image;\n          continue;\n        }\n\n        if (entry.image[\"@placement\"] === \"Hero\") {\n          heroImage = entry.image;\n          continue;\n        }\n      }\n      body.push(entry);\n    }\n\n    return { logoImage, heroImage, body, actions };\n  }\n</script>\n\n<div\n  class=\"notification\"\n  role=\"button\"\n  tabindex=\"0\"\n  onclick={handleNotificationClick}\n  onkeydown={(e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      handleNotificationClick();\n    }\n  }}\n>\n  <div class=\"notification-header\">\n    <div class=\"notification-header-info\">\n      <FileIcon class=\"notification-icon\" umid={notification.appUmid} />\n      <div>{notification.appName}</div>\n      <span>-</span>\n      <div>\n        {moment(WindowsDateFileTimeToDate(notification.date)).fromNow()}\n      </div>\n    </div>\n    <button data-skin=\"transparent\" onclick={handleClose}>\n      <Icon iconName=\"IoClose\" />\n    </button>\n  </div>\n\n  <div class=\"notification-body\">\n    {#if logoImage && logoImage[\"@src\"]}\n      <img\n        src={logoImage[\"@src\"]}\n        alt={logoImage[\"@alt\"] || \"\"}\n        class=\"notification-body-logo-image\"\n        class:notification-body-logo-image-circle={logoImage[\"@hint-crop\"] === \"Circle\"}\n      />\n    {/if}\n\n    <div class=\"notification-body-content\">\n      {#each body as entry, index (index)}\n        {#if \"text\" in entry}\n          <p>{entry.text.$value}</p>\n        {:else if \"image\" in entry && entry.image[\"@src\"]}\n          {#if entry.image[\"@placement\"] !== \"AppLogoOverride\" && entry.image[\"@placement\"] !== \"Hero\"}\n            <img src={entry.image[\"@src\"]} alt={entry.image[\"@alt\"] || \"\"} />\n          {/if}\n        {:else if \"group\" in entry}\n          <div class=\"notification-group\">\n            {#each entry.group.subgroup as subgroup, subIndex (subIndex)}\n              <div class=\"notification-subgroup\">\n                {#each subgroup.$value as subEntry, subEntryIndex (subEntryIndex)}\n                  {#if \"text\" in subEntry}\n                    <p>{subEntry.text.$value}</p>\n                  {:else if \"image\" in subEntry && subEntry.image[\"@src\"]}\n                    <img src={subEntry.image[\"@src\"]} alt={subEntry.image[\"@alt\"] || \"\"} />\n                  {/if}\n                {/each}\n              </div>\n            {/each}\n          </div>\n        {/if}\n      {/each}\n    </div>\n\n    {#if heroImage && heroImage[\"@src\"]}\n      <img\n        src={heroImage[\"@src\"]}\n        alt={heroImage[\"@alt\"] || \"\"}\n        class=\"notification-body-hero-image\"\n      />\n    {/if}\n  </div>\n\n  {#if actions.length > 0}\n    <div class=\"notification-actions\">\n      {#each actions as entry, index (index)}\n        {#if \"input\" in entry}\n          {@const input = entry.input}\n          {#if input[\"@type\"] === \"Text\"}\n            <input\n              type=\"text\"\n              data-skin=\"default\"\n              placeholder={input[\"@placeHolderContent\"] || \"\"}\n              value={inputData[input[\"@id\"]] || \"\"}\n              onclick={(e) => e.stopPropagation()}\n              oninput={(e) => handleInputChange(input[\"@id\"], e.currentTarget.value)}\n            />\n          {:else if input[\"@type\"] === \"Selection\"}\n            <select\n              data-skin=\"default\"\n              value={inputData[input[\"@id\"]]}\n              onclick={(e) => e.stopPropagation()}\n              onchange={(e) => handleInputChange(input[\"@id\"], e.currentTarget.value)}\n              placeholder={input[\"@placeHolderContent\"]}\n            >\n              {#each input.selection as opt}\n                <option value={opt[\"@id\"]}>{opt[\"@content\"]}</option>\n              {/each}\n            </select>\n          {/if}\n        {:else if \"action\" in entry}\n          {@const action = entry.action}\n          {#if action[\"@placement\"] !== \"ContextMenu\"}\n            <button\n              data-skin=\"default\"\n              title={action[\"@hint-toolTip\"] || \"\"}\n              onclick={(e) => {\n                e.stopPropagation();\n                handleAction(action[\"@arguments\"], action[\"@activationType\"]);\n              }}\n            >\n              {action[\"@content\"]}\n            </button>\n          {/if}\n        {/if}\n      {/each}\n    </div>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/en.yml\"),\n    de: await import(\"./translations/en.yml\"),\n    \"zh-CN\": await import(\"./translations/en.yml\"),\n    \"zh-TW\": await import(\"./translations/en.yml\"),\n    ko: await import(\"./translations/en.yml\"),\n    fr: await import(\"./translations/en.yml\"),\n    ar: await import(\"./translations/en.yml\"),\n    ru: await import(\"./translations/en.yml\"),\n    \"pt-BR\": await import(\"./translations/en.yml\"),\n    \"pt-PT\": await import(\"./translations/en.yml\"),\n    ja: await import(\"./translations/en.yml\"),\n    hi: await import(\"./translations/en.yml\"),\n    it: await import(\"./translations/en.yml\"),\n    nl: await import(\"./translations/en.yml\"),\n    tr: await import(\"./translations/en.yml\"),\n    pl: await import(\"./translations/en.yml\"),\n    uk: await import(\"./translations/en.yml\"),\n    id: await import(\"./translations/en.yml\"),\n    cs: await import(\"./translations/en.yml\"),\n    th: await import(\"./translations/en.yml\"),\n    vi: await import(\"./translations/en.yml\"),\n    ms: await import(\"./translations/en.yml\"),\n    he: await import(\"./translations/en.yml\"),\n    ro: await import(\"./translations/en.yml\"),\n    el: await import(\"./translations/en.yml\"),\n    sv: await import(\"./translations/en.yml\"),\n    no: await import(\"./translations/en.yml\"),\n    fi: await import(\"./translations/en.yml\"),\n    da: await import(\"./translations/en.yml\"),\n    hu: await import(\"./translations/en.yml\"),\n    lt: await import(\"./translations/en.yml\"),\n    bg: await import(\"./translations/en.yml\"),\n    sk: await import(\"./translations/en.yml\"),\n    hr: await import(\"./translations/en.yml\"),\n    lv: await import(\"./translations/en.yml\"),\n    et: await import(\"./translations/en.yml\"),\n    tl: await import(\"./translations/en.yml\"),\n    ca: await import(\"./translations/en.yml\"),\n    af: await import(\"./translations/en.yml\"),\n    bn: await import(\"./translations/en.yml\"),\n    fa: await import(\"./translations/en.yml\"),\n    pa: await import(\"./translations/en.yml\"),\n    sw: await import(\"./translations/en.yml\"),\n    ta: await import(\"./translations/en.yml\"),\n    ur: await import(\"./translations/en.yml\"),\n    cy: await import(\"./translations/en.yml\"),\n    am: await import(\"./translations/en.yml\"),\n    hy: await import(\"./translations/en.yml\"),\n    az: await import(\"./translations/en.yml\"),\n    eu: await import(\"./translations/en.yml\"),\n    bs: await import(\"./translations/en.yml\"),\n    ka: await import(\"./translations/en.yml\"),\n    gu: await import(\"./translations/en.yml\"),\n    is: await import(\"./translations/en.yml\"),\n    km: await import(\"./translations/en.yml\"),\n    ku: await import(\"./translations/en.yml\"),\n    lo: await import(\"./translations/en.yml\"),\n    lb: await import(\"./translations/en.yml\"),\n    mk: await import(\"./translations/en.yml\"),\n    mt: await import(\"./translations/en.yml\"),\n    mn: await import(\"./translations/en.yml\"),\n    ne: await import(\"./translations/en.yml\"),\n    ps: await import(\"./translations/en.yml\"),\n    sr: await import(\"./translations/en.yml\"),\n    si: await import(\"./translations/en.yml\"),\n    so: await import(\"./translations/en.yml\"),\n    tg: await import(\"./translations/en.yml\"),\n    te: await import(\"./translations/en.yml\"),\n    uz: await import(\"./translations/en.yml\"),\n    yo: await import(\"./translations/en.yml\"),\n    zu: await import(\"./translations/en.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/af.yml",
    "content": "clear: Vee alles uit\nempty: Geen kennisgewings nie\nsettings: Meer kennisgewinginstellings\ntitle: Kennisgewings\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/am.yml",
    "content": "clear: ሁሉንም አጽዳ\nempty: ምንም ማሳወቂያዎች የሉም\nsettings: ተጨማሪ የማሳወቂያዎች ቅንብሮች\ntitle: ማሳወቂያዎች\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ar.yml",
    "content": "clear: مسح الكل\nempty: لا الإخطارات\nsettings: المزيد من إعدادات الإخطارات\ntitle: إشعارات\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/az.yml",
    "content": "clear: Hamısını silin\nempty: Bildiriş yoxdur\nsettings: Daha çox Bildiriş Parametrləri\ntitle: Bildirişlər\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/bg.yml",
    "content": "clear: Изчисти всички\nempty: Няма известия\nsettings: Още настройки за известия\ntitle: Известия\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/bn.yml",
    "content": "clear: সব সাফ করুন\nempty: কোনো বিজ্ঞপ্তি নেই\nsettings: আরও বিজ্ঞপ্তি সেটিংস\ntitle: বিজ্ঞপ্তি\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/bs.yml",
    "content": "clear: Obriši sve\nempty: Nema obavještenja\nsettings: Više postavki obavještenja\ntitle: Obavještenja\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ca.yml",
    "content": "clear: Esborra-ho tot\nempty: No hi ha notificacions\nsettings: Més configuracions de notificacions\ntitle: Notificacions\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/cs.yml",
    "content": "clear: Vymazat vše\nempty: Žádná upozornění\nsettings: Další nastavení oznámení\ntitle: Oznámení\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/cy.yml",
    "content": "clear: Clirio'r cyfan\nempty: Dim hysbysiadau\nsettings: Mwy o Gosodiadau Hysbysiadau\ntitle: Hysbysiadau\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/da.yml",
    "content": "clear: Ryd alle\nempty: Ingen meddelelser\nsettings: Flere meddelelsesindstillinger\ntitle: Meddelelser\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/de.yml",
    "content": "clear: Alles löschen\nempty: Keine Benachrichtigungen\nsettings: Weitere Benachrichtigungseinstellungen\ntitle: Benachrichtigungen\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/el.yml",
    "content": "clear: Καθαρίστε όλα\nempty: Δεν υπάρχουν ειδοποιήσεις\nsettings: Περισσότερες ρυθμίσεις ειδοποιήσεων\ntitle: Ειδοποιήσεις\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/en.yml",
    "content": "clear: Clear all\nempty: No notifications\nsettings: More Notifications Settings\ntitle: Notifications\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/es.yml",
    "content": "clear: Borrar todo\nempty: Sin notificaciones\nsettings: Más configuraciones de notificaciones\ntitle: Notificaciones\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/et.yml",
    "content": "clear: Tühjenda kõik\nempty: Märguandeid pole\nsettings: Rohkem märguannete seadeid\ntitle: Märguanded\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/eu.yml",
    "content": "clear: Garbitu dena\nempty: Ez dago jakinarazpenik\nsettings: Jakinarazpenen ezarpen gehiago\ntitle: Jakinarazpenak\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/fa.yml",
    "content": "clear: پاک کردن همه\nempty: بدون اطلاعیه\nsettings: تنظیمات بیشتر اعلان‌ها\ntitle: اطلاعیه ها\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/fi.yml",
    "content": "clear: Tyhjennä kaikki\nempty: Ei ilmoituksia\nsettings: Lisää ilmoitusasetuksia\ntitle: Ilmoitukset\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/fr.yml",
    "content": "clear: Tout effacer\nempty: Aucune notification\nsettings: Plus de paramètres de notifications\ntitle: Notifications\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/gu.yml",
    "content": "clear: બધા સાફ કરો\nempty: કોઈ સૂચનાઓ નથી\nsettings: વધુ સૂચના સેટિંગ્સ\ntitle: સૂચનાઓ\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/he.yml",
    "content": "clear: נקה הכל\nempty: אין הודעות\nsettings: הגדרות התראות נוספות\ntitle: התראות\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/hi.yml",
    "content": "clear: सभी साफ करें\nempty: कोई सूचना नहीं\nsettings: अधिक सूचना सेटिंग्स\ntitle: सूचनाएं\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/hr.yml",
    "content": "clear: Obriši sve\nempty: Nema obavijesti\nsettings: Više postavki obavijesti\ntitle: Obavijesti\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/hu.yml",
    "content": "clear: Minden törlése\nempty: Nincsenek értesítések\nsettings: További értesítési beállítások\ntitle: Értesítések\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/hy.yml",
    "content": "clear: Մաքրել բոլորը\nempty: Ծանուցումներ չկան\nsettings: Լրացուցիչ ծանուցումների կարգավորումներ\ntitle: Ծանուցումներ\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/id.yml",
    "content": "clear: Hapus semuanya\nempty: Tidak ada pemberitahuan\nsettings: Pengaturan Notifikasi Lainnya\ntitle: Pemberitahuan\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/is.yml",
    "content": "clear: Hreinsaðu allt\nempty: Engar tilkynningar\nsettings: Fleiri tilkynningastillingar\ntitle: Tilkynningar\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/it.yml",
    "content": "clear: Cancella tutto\nempty: Nessuna notifica\nsettings: Altre impostazioni di notifica\ntitle: Notifiche\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ja.yml",
    "content": "clear: すべてクリア\nempty: 通知はありません\nsettings: その他の通知設定\ntitle: 通知\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ka.yml",
    "content": "clear: გაასუფთავე ყველა\nempty: არ არის შეტყობინებები\nsettings: მეტი შეტყობინებების პარამეტრები\ntitle: შეტყობინებები\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/km.yml",
    "content": "clear: ជម្រះទាំងអស់។\nempty: គ្មានការជូនដំណឹងទេ។\nsettings: ការកំណត់ការជូនដំណឹងបន្ថែម\ntitle: ការជូនដំណឹង\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ko.yml",
    "content": "clear: 모두 지우기\nempty: 알림 없음\nsettings: 추가 알림 설정\ntitle: 알림\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ku.yml",
    "content": "clear: Hemî paqij bike\nempty: No notifications\nsettings: More Settings Notifications\ntitle: Notifications\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/lb.yml",
    "content": "clear: Alles läschen\nempty: Keng Notifikatiounen\nsettings: Méi Notifikatiounen Astellunge\ntitle: Notifikatiounen\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/lo.yml",
    "content": "clear: ລຶບລ້າງທັງໝົດ\nempty: ບໍ່ມີການແຈ້ງເຕືອນ\nsettings: ການຕັ້ງຄ່າການແຈ້ງເຕືອນເພີ່ມເຕີມ\ntitle: ການແຈ້ງເຕືອນ\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/lt.yml",
    "content": "clear: Išvalyti viską\nempty: Jokių pranešimų\nsettings: Daugiau pranešimų nustatymų\ntitle: Pranešimai\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/lv.yml",
    "content": "clear: Notīrīt visu\nempty: Nav paziņojumu\nsettings: Citi paziņojumu iestatījumi\ntitle: Paziņojumi\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/mk.yml",
    "content": "clear: Исчистете ги сите\nempty: Нема известувања\nsettings: Повеќе поставки за известувања\ntitle: Известувања\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/mn.yml",
    "content": "clear: Бүгдийг арилгах\nempty: Мэдэгдэл алга\nsettings: Бусад мэдэгдлийн тохиргоо\ntitle: Мэдэгдэл\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ms.yml",
    "content": "clear: Kosongkan semua\nempty: Tiada pemberitahuan\nsettings: Lagi Tetapan Pemberitahuan\ntitle: Pemberitahuan\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/mt.yml",
    "content": "clear: Ċara kollox\nempty: Ebda notifiki\nsettings: Aktar Notifiki Settings\ntitle: Notifiki\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ne.yml",
    "content": "clear: सबै खाली गर्नुहोस्\nempty: कुनै सूचना छैन\nsettings: थप सूचना सेटिङहरू\ntitle: सूचनाहरू\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/nl.yml",
    "content": "clear: Alles wissen\nempty: Geen meldingen\nsettings: Meer meldingsinstellingen\ntitle: Meldingen\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/no.yml",
    "content": "clear: Fjern alle\nempty: Ingen varsler\nsettings: Flere varslingsinnstillinger\ntitle: Varsler\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/pa.yml",
    "content": "clear: ਸਭ ਸਾਫ਼ ਕਰੋ\nempty: ਕੋਈ ਸੂਚਨਾਵਾਂ ਨਹੀਂ\nsettings: ਹੋਰ ਸੂਚਨਾਵਾਂ ਸੈਟਿੰਗਾਂ\ntitle: ਸੂਚਨਾਵਾਂ\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/pl.yml",
    "content": "clear: Wyczyść wszystko\nempty: Brak powiadomień\nsettings: Więcej ustawień powiadomień\ntitle: Powiadomienia\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ps.yml",
    "content": "clear: ټول پاک کړئ\nempty: هیڅ خبرتیا نشته\nsettings: نور د خبرتیا ترتیبات\ntitle: خبرتیاوې\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/pt-BR.yml",
    "content": "clear: Limpar tudo\nempty: Sem notificações\nsettings: Mais configurações de notificações\ntitle: Notificações\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/pt-PT.yml",
    "content": "clear: Limpar tudo\nempty: Sem notificações\nsettings: Mais configurações de notificações\ntitle: Notificações\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ro.yml",
    "content": "clear: Ștergeți totul\nempty: Fără notificări\nsettings: Mai multe setări de notificări\ntitle: Notificări\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ru.yml",
    "content": "clear: Очистить все\nempty: Нет уведомлений\nsettings: Дополнительные настройки уведомлений\ntitle: Уведомления\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/si.yml",
    "content": "clear: සියල්ල හිස් කරන්න\nempty: දැනුම්දීම් නැත\nsettings: තවත් දැනුම්දීම් සැකසීම්\ntitle: දැනුම්දීම්\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/sk.yml",
    "content": "clear: Vymazať všetko\nempty: Žiadne upozornenia\nsettings: Ďalšie nastavenia upozornení\ntitle: Upozornenia\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/so.yml",
    "content": "clear: Nadiifi dhammaan\nempty: Ogeysiis ma jiro\nsettings: Dejinta Ogeysiisyo badan\ntitle: Ogeysiisyada\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/sr.yml",
    "content": "clear: Обриши све\nempty: Нема обавештења\nsettings: Још подешавања обавештења\ntitle: Обавештења\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/sv.yml",
    "content": "clear: Rensa alla\nempty: Inga aviseringar\nsettings: Fler aviseringsinställningar\ntitle: Aviseringar\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/sw.yml",
    "content": "clear: Futa zote\nempty: Hakuna arifa\nsettings: Mipangilio Zaidi ya Arifa\ntitle: Arifa\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ta.yml",
    "content": "clear: அனைத்தையும் அழிக்கவும்\nempty: அறிவிப்புகள் இல்லை\nsettings: மேலும் அறிவிப்புகள் அமைப்புகள்\ntitle: அறிவிப்புகள்\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/te.yml",
    "content": "clear: అన్నీ క్లియర్ చేయండి\nempty: నోటిఫికేషన్‌లు లేవు\nsettings: మరిన్ని నోటిఫికేషన్‌ల సెట్టింగ్‌లు\ntitle: నోటిఫికేషన్‌లు\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/tg.yml",
    "content": "clear: Ҳамаро тоза кунед\nempty: Огоҳиҳо нест\nsettings: Танзимоти огоҳиномаҳои бештар\ntitle: Огоҳиномаҳо\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/th.yml",
    "content": "clear: เคลียร์ทั้งหมด\nempty: ไม่มีการแจ้งเตือน\nsettings: การตั้งค่าการแจ้งเตือนเพิ่มเติม\ntitle: การแจ้งเตือน\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/tl.yml",
    "content": "clear: I-clear lahat\nempty: Walang notification\nsettings: Higit pang Mga Setting ng Notification\ntitle: Mga abiso\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/tr.yml",
    "content": "clear: Tümünü temizle\nempty: Bildirim yok\nsettings: Diğer Bildirim Ayarları\ntitle: Bildirimler\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/uk.yml",
    "content": "clear: Очистити все\nempty: Немає повідомлень\nsettings: Більше налаштувань сповіщень\ntitle: Сповіщення\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/ur.yml",
    "content": "clear: سب کو صاف کریں\nempty: کوئی اطلاعات نہیں\nsettings: مزید اطلاعات کی ترتیبات\ntitle: اطلاعات\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/uz.yml",
    "content": "clear: Hammasini tozalang\nempty: Hech qanday bildirishnoma yo'q\nsettings: Qo'shimcha bildirishnomalar sozlamalari\ntitle: Bildirishnomalar\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/vi.yml",
    "content": "clear: Xóa tất cả\nempty: Không có thông báo\nsettings: Thêm cài đặt thông báo\ntitle: Thông báo\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/yo.yml",
    "content": "clear: Ko gbogbo rẹ kuro\nempty: Ko si awọn iwifunni\nsettings: Awọn Eto Awọn iwifunni diẹ sii\ntitle: Awọn iwifunni\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/zh-CN.yml",
    "content": "clear: 全部清除\nempty: 无通知\nsettings: 更多通知设置\ntitle: 通知\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/zh-TW.yml",
    "content": "clear: 全部清除\nempty: 無通知\nsettings: 更多通知設置\ntitle: 通知\n"
  },
  {
    "path": "src/ui/svelte/notifications/i18n/translations/zu.yml",
    "content": "clear: Sula konke\nempty: Azikho izaziso\nsettings: Izilungiselelo Zezaziso Eziningi\ntitle: Izaziso\n"
  },
  {
    "path": "src/ui/svelte/notifications/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/notifications/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/notifications/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe, Widget } from \"@seelen-ui/lib\";\nimport type { AppNotification } from \"@seelen-ui/lib/types\";\nimport { locale } from \"./i18n/index.ts\";\nimport { writable } from \"svelte/store\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nlet widget = Widget.getCurrent();\n\nlet settings = writable(await Settings.getAsync());\nSettings.onChange((s) => settings.set(s));\nsettings.subscribe((settings) => {\n  locale.set(settings.language || \"en\");\n});\n\nlet notifications = lazyRune(() => invoke(SeelenCommand.GetNotifications));\nsubscribe(SeelenEvent.Notifications, notifications.setByPayload);\nawait notifications.init();\n\nwidget.window.onFocusChanged((e) => {\n  if (!e.payload) {\n    widget.hide();\n  }\n});\n\nclass State {\n  get notifications(): AppNotification[] {\n    return notifications.value;\n  }\n}\n\nexport const globalState = new State();\n"
  },
  {
    "path": "src/ui/svelte/power-menu/app.svelte",
    "content": "<script lang=\"ts\">\n  import { onMount } from \"svelte\";\n  import { options } from \"./options\";\n  import { state } from \"./state.svelte\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n  import { t } from \"./i18n\";\n  import { MissingIcon } from \"libs/ui/svelte/components/Icon\";\n\n  onMount(() => {\n    Widget.getCurrent().ready();\n  });\n\n  function onCancel() {\n    Widget.self.hide();\n  }\n\n  const menu = $derived.by(() => {\n    if (!state.primaryMonitor) {\n      return null;\n    }\n\n    const {\n      primaryMonitor: { rect, scaleFactor },\n    } = state;\n\n    return {\n      x: rect.left,\n      y: rect.top,\n      // we reduce the size and later scale it, to get the correct display by dpi aware\n      width: (rect.right - rect.left) / scaleFactor,\n      height: (rect.bottom - rect.top) / scaleFactor,\n      scale: scaleFactor,\n    };\n  });\n</script>\n\n<div\n  class=\"power-menu-overlay\"\n  role=\"menu\"\n  tabindex=\"-1\"\n  onclick={onCancel}\n  onkeydown={(e) => {\n    if (e.key === \"Escape\") {\n      onCancel();\n    }\n  }}\n>\n  {#if menu}\n    <div\n      class=\"power-menu\"\n      style:position=\"fixed\"\n      style:left={menu.x + \"px\"}\n      style:top={menu.y + \"px\"}\n      style:width={menu.width + \"px\"}\n      style:height={menu.height + \"px\"}\n      style:transform={`scale(${menu.scale})`}\n      style:transform-origin=\"left top\"\n    >\n      <div class=\"power-menu-user\">\n        {#if state.user.profilePicturePath}\n          <img\n            class=\"power-menu-user-profile\"\n            src={convertFileSrc(state.user.profilePicturePath)}\n            alt=\"\"\n          />\n        {:else}\n          <MissingIcon class=\"power-menu-user-profile\" />\n        {/if}\n        <div class=\"power-menu-user-email\">\n          {state.user.email}\n        </div>\n      </div>\n      <div class=\"power-menu-bye-bye\">{$t(\"goodbye\", { 0: state.user.name })}</div>\n      <ul class=\"power-menu-list\">\n        {#each options as option}\n          <li>\n            <button onclick={option.onClick} class=\"power-menu-item\">\n              <Icon iconName={option.icon as any} />\n              <span class=\"power-menu-item-label\">{$t(option.key)}</span>\n            </button>\n          </li>\n        {/each}\n      </ul>\n      <!-- <div class=\"power-menu-uptime\">{$t(\"uptime\")}: 2 hours 30 minutes</div> -->\n    </div>\n  {/if}\n</div>\n\n<style>\n  :global(body) {\n    background-color: transparent;\n    overflow: hidden;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/af.yml",
    "content": "goodbye: Totsiens {{0}}\nhibernate: Hiberneer\nlock: Sluit sessie\nlog_out: Teken uit\nreboot: Herlaai\nshutdown: Krag af\nsuspend: Opskort\nuptime: Optyd\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/am.yml",
    "content": "goodbye: ደህና {{0}}}}\nhibernate: ጩኸት\nlock: የመቆለፊያ ክፍለ ጊዜ\nlog_out: ውጣ\nreboot: ድጋሚ አስነሳ\nshutdown: ኃይል ጠፍቷል\nsuspend: ማገድ\nuptime: ወደ ላይ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ar.yml",
    "content": "goodbye: وداعا {{0}}\nhibernate: السبات\nlock: قفل الجلسة\nlog_out: تسجيل الخروج\nreboot: إعادة التشغيل\nshutdown: خارج السلطة\nsuspend: تعليق\nuptime: الجهوزية\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/az.yml",
    "content": "goodbye: Əlvida {{0}}\nhibernate: Qışqırmaq\nlock: Kilid sessiyası\nlog_out: Çıxmaq\nreboot: Yenidən başlatmaq\nshutdown: Gücləndirmək\nsuspend: Dayandırmaq\nuptime: İş vaxtı\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/bg.yml",
    "content": "goodbye: Довиждане {{0}}\nhibernate: Хибернация\nlock: Заключване на сесията\nlog_out: Излезте\nreboot: Рестартирайте\nshutdown: Изключване\nsuspend: Спиране\nuptime: Работно време\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/bn.yml",
    "content": "goodbye: বিদায় {{0}}\nhibernate: হাইবারনেট\nlock: লক সেশন\nlog_out: লগ আউট করুন\nreboot: রিবুট করুন\nshutdown: পাওয়ার অফ\nsuspend: সাসপেন্ড\nuptime: আপটাইম\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/bs.yml",
    "content": "goodbye: zbogom {{0}}\nhibernate: Hibernacija\nlock: Zaključaj sesiju\nlog_out: Odjavite se\nreboot: Ponovo pokreni\nshutdown: Isključite napajanje\nsuspend: Suspend\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ca.yml",
    "content": "goodbye: Adéu {{0}}\nhibernate: Hibernar\nlock: Bloquejar la sessió\nlog_out: Tanca la sessió\nreboot: Reinicieu\nshutdown: Apagueu\nsuspend: Suspensió\nuptime: Temps de funcionament\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/cs.yml",
    "content": "goodbye: Sbohem {{0}}\nhibernate: Přezimovat\nlock: Uzamknout relaci\nlog_out: Odhlaste se\nreboot: Restartujte\nshutdown: Vypněte napájení\nsuspend: Pozastavit\nuptime: Doba provozuschopnosti\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/cy.yml",
    "content": "goodbye: Hwyl fawr {{0}}\nhibernate: gaeafgysgu\nlock: Sesiwn cloi\nlog_out: Allgofnodi\nreboot: Ailgychwyn\nshutdown: Pŵer i ffwrdd\nsuspend: Atal\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/da.yml",
    "content": "goodbye: Farvel {{0}}\nhibernate: Gå i dvale\nlock: Lås session\nlog_out: Log ud\nreboot: Genstart\nshutdown: Sluk\nsuspend: Suspendere\nuptime: Oppetid\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/de.yml",
    "content": "goodbye: Auf Wiedersehen {{0}}\nhibernate: Überwintern\nlock: Sitzung sperren\nlog_out: Abmelden\nreboot: Neustart\nshutdown: Ausschalten\nsuspend: Aussetzen\nuptime: Betriebszeit\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/el.yml",
    "content": "goodbye: Αντίο {{0}}\nhibernate: Διαχειμάζω\nlock: Κλείδωμα συνεδρίας\nlog_out: Αποσυνδεθείτε\nreboot: Επανεκκίνηση\nshutdown: Απενεργοποιήστε\nsuspend: Αναστέλλω\nuptime: Χρόνος λειτουργίας\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/en.yml",
    "content": "goodbye: Goodbye {{0}}\nhibernate: Hibernate\nlock: Lock session\nlog_out: Log out\nreboot: Reboot\nshutdown: Power off\nsuspend: Suspend\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/es.yml",
    "content": "goodbye: Adiós {{0}}\nhibernate: Hibernar\nlock: Bloquear sesión\nlog_out: Finalizar la sesión\nreboot: Reiniciar\nshutdown: Apagar\nsuspend: Suspender\nuptime: tiempo de actividad\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/et.yml",
    "content": "goodbye: Hüvasti {{0}}\nhibernate: Hibernate\nlock: Lukusta seanss\nlog_out: Logi välja\nreboot: Taaskäivitage\nshutdown: Toide välja\nsuspend: Peatada\nuptime: Tööaeg\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/eu.yml",
    "content": "goodbye: Agur {{0}}\nhibernate: Hibernatu\nlock: Blokeatu saioa\nlog_out: Amaitu saioa\nreboot: Berrabiarazi\nshutdown: Itzali\nsuspend: Eten\nuptime: Epea\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/fa.yml",
    "content": "goodbye: خداحافظ {{0}}\nhibernate: خواب زمستانی\nlock: قفل کردن جلسه\nlog_out: از سیستم خارج شوید\nreboot: راه اندازی مجدد\nshutdown: خاموش کنید\nsuspend: تعلیق کند\nuptime: آپتایم\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/fi.yml",
    "content": "goodbye: Hyvästi {{0}}\nhibernate: Lepotila\nlock: Lukitse istunto\nlog_out: Kirjaudu ulos\nreboot: Käynnistä uudelleen\nshutdown: Virta pois päältä\nsuspend: Keskeyttää\nuptime: Päällä\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/fr.yml",
    "content": "goodbye: Au revoir {{0}}\nhibernate: Hiberner\nlock: Session de verrouillage\nlog_out: Se déconnecter\nreboot: Redémarrer\nshutdown: Éteindre\nsuspend: Suspendre\nuptime: Temps de disponibilité\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/gu.yml",
    "content": "goodbye: ગુડબાય {{0}}\nhibernate: હાઇબરનેટ\nlock: લૉક સત્ર\nlog_out: લોગ આઉટ કરો\nreboot: રીબૂટ કરો\nshutdown: પાવર બંધ\nsuspend: સસ્પેન્ડ કરો\nuptime: અપટાઇમ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/he.yml",
    "content": "goodbye: להתראות {{0}}\nhibernate: שינה\nlock: נעילת הפעלה\nlog_out: התנתק\nreboot: לְאַתחֵל\nshutdown: כבה\nsuspend: לְהַשְׁעוֹת\nuptime: זמן פעולה\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/hi.yml",
    "content": "goodbye: अलविदा {{0}}\nhibernate: हाइबरनेट\nlock: सत्र लॉक करें\nlog_out: लॉग आउट\nreboot: रीबूट\nshutdown: बिजली बंद\nsuspend: निलंबित करें\nuptime: अपटाइम\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/hr.yml",
    "content": "goodbye: Zbogom {{0}}\nhibernate: Hibernacija\nlock: Zaključaj sesiju\nlog_out: Odjavi se\nreboot: Ponovno pokretanje\nshutdown: Isključenje\nsuspend: Obustaviti\nuptime: Vrijeme rada\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/hu.yml",
    "content": "goodbye: Viszlát {{0}}\nhibernate: Hibernálni\nlock: Munkamenet zárolása\nlog_out: Jelentkezzen ki\nreboot: Indítsa újra\nshutdown: Kapcsolja ki\nsuspend: Felfüggeszteni\nuptime: Üzemidő\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/hy.yml",
    "content": "goodbye: Ցտեսություն {{0}}\nhibernate: Ձմեռել\nlock: Կողպեք նիստը\nlog_out: Դուրս գալ\nreboot: Վերագործարկեք\nshutdown: Անջատեք\nsuspend: Կասեցնել\nuptime: Գործողության ժամանակ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/id.yml",
    "content": "goodbye: Selamat tinggal {{0}}\nhibernate: Hibernasi\nlock: Sesi kunci\nlog_out: Keluar\nreboot: Menyalakan ulang\nshutdown: Matikan\nsuspend: Menskors\nuptime: Waktu aktif\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/is.yml",
    "content": "goodbye: Bless {{0}}\nhibernate: Leggðu í dvala\nlock: Læstu lotu\nlog_out: Skráðu þig út\nreboot: Endurræstu\nshutdown: Slökkvið á\nsuspend: Fresta\nuptime: Spenntur\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/it.yml",
    "content": "goodbye: Arrivederci {{0}}\nhibernate: Ibernazione\nlock: Blocca la sessione\nlog_out: Esci\nreboot: Riavviare\nshutdown: Spegnimento\nsuspend: Sospendere\nuptime: Tempo di attività\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ja.yml",
    "content": "goodbye: さようなら{{0}}\nhibernate: 休止状態\nlock: ロックセッション\nlog_out: ログアウト\nreboot: 再起動\nshutdown: 電源を切る\nsuspend: スリープ\nuptime: 稼働時間\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ka.yml",
    "content": "goodbye: ნახვამდის {{0}}\nhibernate: ჰიბერნაცია\nlock: სესიის ჩაკეტვა\nlog_out: გასვლა\nreboot: გადატვირთვა\nshutdown: გამორთეთ\nsuspend: შეაჩერე\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/km.yml",
    "content": "goodbye: លាហើយ {{0}}\nhibernate: សមលាក់សម្ងួរ\nlock: ចាក់សោសម័យ\nlog_out: ចេញ\nreboot: ចាប់ផ្ដើមដំណើរការឡើងវិញ\nshutdown: បិទថាមពល\nsuspend: ប្យួរ\nuptime: អីនឹងមានតោហេ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ko.yml",
    "content": "goodbye: 안녕히 계세요 {{0}}\nhibernate: 최대 절전 모드\nlock: 세션 잠금\nlog_out: 로그아웃\nreboot: 재부팅\nshutdown: 전원 끄기\nsuspend: 유예하다\nuptime: 가동 시간\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ku.yml",
    "content": "goodbye: Bi xatirê te {{0}}\nhibernate: Hibernate\nlock: Danişîna girtinê\nlog_out: Derkeve\nreboot: Reboot\nshutdown: Power off\nsuspend: Dardekirin\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/lb.yml",
    "content": "goodbye: Äddi {{0}}\nhibernate: Wanterschlof\nlock: Spär Sëtzung\nlog_out: Ausloggen\nreboot: Restart\nshutdown: Kraaft aus\nsuspend: Suspendéieren\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/lo.yml",
    "content": "goodbye: goodbye {{0}}\nhibernate: ຮູດ\nlock: LOTAL SINECT\nlog_out: ອອກຈາກລະບົບ\nreboot: reboot\nshutdown: ພະລັງງານປິດ\nsuspend: ແຂວນໄວ້\nuptime: ເຖິງເວລາ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/lt.yml",
    "content": "goodbye: Atsisveikink {{0}}\nhibernate: Užmigti\nlock: Užrakinti sesiją\nlog_out: Atsijungti\nreboot: Perkraukite\nshutdown: Išjungti maitinimą\nsuspend: Sustabdyti\nuptime: Veiklos laikas\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/lv.yml",
    "content": "goodbye: Uz redzēšanos {{0}}\nhibernate: Pārziemot\nlock: Bloķēt sesiju\nlog_out: Atteikties\nreboot: Reboot\nshutdown: Izslēdziet barošanu\nsuspend: Apturēt\nuptime: Darbības laiks\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/mk.yml",
    "content": "goodbye: Збогум {{0}}\nhibernate: Хибернирај\nlock: Заклучи сесија\nlog_out: Одјави се\nreboot: Рестартирај\nshutdown: Исклучете го\nsuspend: Суспендирај\nuptime: Време на работа\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/mn.yml",
    "content": "goodbye: Баяртай {{0}}\nhibernate: Хусах\nlock: Түгжээний уулзалт\nlog_out: Гарах\nreboot: Дахин ачаалал\nshutdown: Унтраах\nsuspend: Олгох\nuptime: Үргэлж ажиллах\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ms.yml",
    "content": "goodbye: Selamat tinggal {{0}}\nhibernate: Hibernate\nlock: Sesi kunci\nlog_out: Log keluar\nreboot: Reboot\nshutdown: Power Off\nsuspend: Menggantung\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/mt.yml",
    "content": "goodbye: Adieu {{0}}\nhibernate: Iberna\nlock: Lock sessjoni\nlog_out: Log out\nreboot: Reboot\nshutdown: Power off\nsuspend: Issospendi\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ne.yml",
    "content": "goodbye: अलविदा {{0}}\nhibernate: हाइबरनेट\nlock: लक सत्र\nlog_out: लग आउट गर्नुहोस्\nreboot: रिबुट गर्नुहोस्\nshutdown: पावर बन्द\nsuspend: निलम्बन गर्नुहोस्\nuptime: अपटाइम\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/nl.yml",
    "content": "goodbye: Tot ziens {{0}}\nhibernate: Overwinteren\nlock: Sessie vergrendelen\nlog_out: Uitloggen\nreboot: Opnieuw opstarten\nshutdown: Uitschakelen\nsuspend: Opschorten\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/no.yml",
    "content": "goodbye: Farvel {{0}}\nhibernate: Gå i dvale\nlock: Lås økten\nlog_out: Logg ut\nreboot: Start på nytt\nshutdown: Slå av\nsuspend: Henge\nuptime: Oppetid\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/pa.yml",
    "content": "goodbye: ਅਲਵਿਦਾ {{0}}\nhibernate: ਹਾਈਬਰਨੇਟ\nlock: ਲਾਕ ਸੈਸ਼ਨ\nlog_out: ਲੌਗ ਆਊਟ ਕਰੋ\nreboot: ਰੀਬੂਟ ਕਰੋ\nshutdown: ਪਾਵਰ ਬੰਦ\nsuspend: ਮੁਅੱਤਲ\nuptime: ਅਪਟਾਈਮ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/pl.yml",
    "content": "goodbye: Do widzenia {{0}}\nhibernate: Hibernować\nlock: Zablokuj sesję\nlog_out: Wyloguj się\nreboot: Ponowne uruchomienie\nshutdown: Wyłącz\nsuspend: Wstrzymać\nuptime: Czas pracy\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ps.yml",
    "content": "goodbye: الوداع {{0}}\nhibernate: هیبرنیټ\nlock: ناسته بنده کړه\nlog_out: وتل\nreboot: ریبوټ\nshutdown: بریښنا بنده\nsuspend: ځنډول\nuptime: وخت\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/pt-BR.yml",
    "content": "goodbye: Adeus {{0}}\nhibernate: Hibernar\nlock: Bloquear sessão\nlog_out: Sair\nreboot: Reinício\nshutdown: Desligar\nsuspend: Suspender\nuptime: Tempo de atividade\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/pt-PT.yml",
    "content": "goodbye: Adeus {{0}}\nhibernate: Hibernar\nlock: Bloquear sessão\nlog_out: Sair\nreboot: Reinício\nshutdown: Desligar\nsuspend: Suspender\nuptime: Tempo de atividade\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ro.yml",
    "content": "goodbye: La revedere {{0}}\nhibernate: Hibernează\nlock: Blocați sesiunea\nlog_out: Deconectați-vă\nreboot: Reporniți\nshutdown: Opriți\nsuspend: Suspenda\nuptime: Timp de funcționare\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ru.yml",
    "content": "goodbye: До свидания {{0}}\nhibernate: Спящий режим\nlock: Блокировка сеанса\nlog_out: Выйти\nreboot: Перезагрузить\nshutdown: Выключить питание\nsuspend: Приостановить\nuptime: Время работы\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/si.yml",
    "content": "goodbye: ආයුබෝවන් {{0}}\nhibernate: හයිබර්නේට්\nlock: අගුළු සැසිය\nlog_out: ලොග් අවුට් වෙන්න\nreboot: නැවත ආරම්භ කරන්න\nshutdown: විදුලිය විසන්ධි කරන්න\nsuspend: අත්හිටුවන්න\nuptime: අතිකාල\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/sk.yml",
    "content": "goodbye: Dovidenia {{0}}\nhibernate: Hibernácia\nlock: Uzamknúť reláciu\nlog_out: Odhláste sa\nreboot: Reštartujte\nshutdown: Vypnúť\nsuspend: Pozastaviť\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/so.yml",
    "content": "goodbye: Nabad gelyo {{0}}\nhibernate: Hiiberhanate\nlock: Kalfadhiga qufulka\nlog_out: Ka bax\nreboot: Dib u bilaabid\nshutdown: Koronto\nsuspend: Soo laalaadin\nuptime: U qaadid\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/sr.yml",
    "content": "goodbye: Збогом {{0}}\nhibernate: Хибернате\nlock: Закључај сесију\nlog_out: Одјавите се\nreboot: Поново покрени\nshutdown: Искључите напајање\nsuspend: Суспенд\nuptime: Уптиме\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/sv.yml",
    "content": "goodbye: Adjö {{0}}\nhibernate: Övervintra\nlock: Lås session\nlog_out: Logga ut\nreboot: Starta om\nshutdown: Ström av\nsuspend: Uppskjuta\nuptime: Upptid\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/sw.yml",
    "content": "goodbye: Kwaheri {{0}}\nhibernate: Hibernate\nlock: Kikao cha kufuli\nlog_out: Ingia nje\nreboot: Reboot\nshutdown: Nguvu mbali\nsuspend: Kusimamisha\nuptime: Wakati wa juu\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ta.yml",
    "content": "goodbye: குட்பை {{0}}\nhibernate: உறக்கநிலை\nlock: அமர்வை பூட்டு\nlog_out: வெளியேறு\nreboot: மறுதொடக்கம்\nshutdown: பவர் ஆஃப்\nsuspend: சஸ்பெண்ட்\nuptime: இயக்க நேரம்\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/te.yml",
    "content": "goodbye: వీడ్కోలు {{0}}\nhibernate: హైబర్నేట్\nlock: సెషన్ లాక్ చేయండి\nlog_out: లాగ్ అవుట్ చేయండి\nreboot: రీబూట్ చేయండి\nshutdown: పవర్ ఆఫ్\nsuspend: సస్పెండ్ చేయండి\nuptime: సమయము\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/tg.yml",
    "content": "goodbye: Хайрбод {{0}}\nhibernate: Мунтазир\nlock: Сессияи қулф\nlog_out: Баромадан\nreboot: Бозоғоз кунед\nshutdown: Қудрат\nsuspend: Боздоштан\nuptime: Давомнокӣ\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/th.yml",
    "content": "goodbye: ลาก่อน {{0}}\nhibernate: ไฮเบอร์เนต\nlock: ล็อคเซสชัน\nlog_out: ออกจากระบบ\nreboot: รีบูต\nshutdown: ปิดเครื่อง\nsuspend: ระงับ\nuptime: เวลาทำงาน\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/tl.yml",
    "content": "goodbye: Paalam {{0}}\nhibernate: Hibernate\nlock: Session ng lock\nlog_out: Mag -log out\nreboot: Reboot\nshutdown: Power Off\nsuspend: Suspindihin\nuptime: Uptime\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/tr.yml",
    "content": "goodbye: Güle güle {{0}}\nhibernate: Hazırda bekletme\nlock: Oturumu kilitle\nlog_out: Oturumu kapat\nreboot: Yeniden başlat\nshutdown: Kapat\nsuspend: Askıya almak\nuptime: Çalışma süresi\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/uk.yml",
    "content": "goodbye: До побачення {{0}}\nhibernate: сплячий режим\nlock: Сеанс блокування\nlog_out: Вийти\nreboot: Перезавантаження\nshutdown: Вимкніть живлення\nsuspend: Призупинити\nuptime: Час роботи\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/ur.yml",
    "content": "goodbye: الوداع {{0}}\nhibernate: ہائبرنیٹ\nlock: لاک سیشن\nlog_out: لاگ آؤٹ\nreboot: ربوٹ\nshutdown: پاور آف\nsuspend: معطل\nuptime: اپ ٹائم\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/uz.yml",
    "content": "goodbye: Xayrlashish {{0}}\nhibernate: Xunlamoq\nlock: Qulf sessiyasi\nlog_out: Kirish\nreboot: Qaytadan yoqmoq\nshutdown: Quvvat\nsuspend: To'xtatib yubormoq\nuptime: Uy\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/vi.yml",
    "content": "goodbye: Tạm biệt {{0}}\nhibernate: Ngủ đông\nlock: Khóa phiên\nlog_out: Đăng xuất\nreboot: Khởi động lại\nshutdown: Tắt nguồn\nsuspend: Đình chỉ\nuptime: Thời gian hoạt động\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/yo.yml",
    "content": "goodbye: O dà {{0}\nhibernate: Hibernate\nlock: Titii pa\nlog_out: Wọle\nreboot: Atunbere\nshutdown: Ipa pipa\nsuspend: Da duro\nuptime: Iduro\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/zh-CN.yml",
    "content": "goodbye: 再见 {{0}}\nhibernate: 休眠\nlock: 锁定会话\nlog_out: 注销\nreboot: 重启\nshutdown: 关机\nsuspend: 挂起\nuptime: 运行时间\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/zh-TW.yml",
    "content": "goodbye: 再見{{0}}\nhibernate: 休眠\nlock: 鎖定會話\nlog_out: 退出\nreboot: 重新啟動\nshutdown: 關閉電源\nsuspend: 暫停\nuptime: 正常運行時間\n"
  },
  {
    "path": "src/ui/svelte/power-menu/i18n/translations/zu.yml",
    "content": "goodbye: Goodbye {{0}}\nhibernate: Umgebha\nlock: Iseshini yokukhiya\nlog_out: Phuma\nreboot: Qala\nshutdown: Ukucishwa amandla\nsuspend: Hlehlisa\nuptime: Isikhathi\n"
  },
  {
    "path": "src/ui/svelte/power-menu/index.ts",
    "content": "import { getRootContainer } from \"libs/ui/react/utils/index.ts\";\nimport { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\nimport { debounce } from \"lodash\";\n\nawait loadTranslations();\n\nconst widget = Widget.getCurrent();\nwidget.onTrigger(async () => {\n  await widget.show();\n  await widget.focus();\n});\n\nconst hide = debounce(() => {\n  widget.hide(true);\n}, 100);\n\nwidget.window.onFocusChanged(({ payload: focused }) => {\n  if (focused) {\n    hide.cancel();\n  } else {\n    hide();\n  }\n});\n\nawait widget.init();\nawait widget.window.setResizable(false);\n\n// play with zoom level to reset device pixel ratio to 1:1\nlet lastDPR = window.devicePixelRatio;\nawait widget.webview.setZoom(1 / lastDPR);\nwidget.window.onScaleChanged(() => {\n  if (window.devicePixelRatio !== lastDPR) {\n    // when zoom was set dpr changed, so in case of change this is accomulative unit\n    lastDPR = lastDPR * window.devicePixelRatio;\n    widget.webview.setZoom(1 / (lastDPR * window.devicePixelRatio));\n  }\n});\n\nmount(App, {\n  target: getRootContainer(),\n});\n"
  },
  {
    "path": "src/ui/svelte/power-menu/options.ts",
    "content": "import { invoke } from \"@tauri-apps/api/core\";\nimport { SeelenCommand } from \"@seelen-ui/lib\";\n\ninterface Option {\n  key: string;\n  icon: string;\n  onClick: () => void;\n}\n\nexport const options: Option[] = [\n  {\n    key: \"lock\",\n    icon: \"IoLockClosed\",\n    onClick() {\n      invoke(SeelenCommand.Lock);\n    },\n  },\n  {\n    key: \"log_out\",\n    icon: \"IoLogOutOutline\",\n    onClick() {\n      invoke(SeelenCommand.LogOut);\n    },\n  },\n  {\n    key: \"shutdown\",\n    icon: \"IoPower\",\n    onClick() {\n      invoke(SeelenCommand.Shutdown);\n    },\n  },\n  {\n    key: \"reboot\",\n    icon: \"MdRestartAlt\",\n    onClick() {\n      invoke(SeelenCommand.Restart);\n    },\n  },\n  {\n    key: \"suspend\",\n    icon: \"BiMoon\",\n    onClick() {\n      invoke(SeelenCommand.Suspend);\n    },\n  },\n  {\n    key: \"hibernate\",\n    icon: \"TbZzz\",\n    onClick() {\n      invoke(SeelenCommand.Hibernate);\n    },\n  },\n];\n"
  },
  {
    "path": "src/ui/svelte/power-menu/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/power-menu/state.svelte.ts",
    "content": "import { invoke, type Rect, SeelenCommand, SeelenEvent, Settings, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { locale } from \"./i18n/index.ts\";\nimport { writable } from \"svelte/store\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nlet settings = writable(await Settings.getAsync());\nSettings.onChange((s) => settings.set(s));\nsettings.subscribe((settings) => {\n  locale.set(settings.language || \"en\");\n});\n\nlet monitors = lazyRune(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\n\nconst [userInit] = await Promise.all([invoke(SeelenCommand.GetUser), monitors.init()]);\n\nlet desktopRect = $derived.by(() => {\n  let rect: Rect = { top: 0, left: 0, right: 0, bottom: 0 };\n  for (const monitor of monitors.value) {\n    rect.left = Math.min(rect.left, monitor.rect.left);\n    rect.top = Math.min(rect.top, monitor.rect.top);\n    rect.right = Math.max(rect.right, monitor.rect.right);\n    rect.bottom = Math.max(rect.bottom, monitor.rect.bottom);\n  }\n  return rect;\n});\n\n$effect.root(() => {\n  $effect(() => {\n    Widget.self.setPosition(desktopRect);\n  });\n});\n\nlet relativePrimaryMonitor = $derived.by(() => {\n  let primary = monitors.value.find((m) => m.isPrimary) || monitors.value[0];\n  if (primary) {\n    return {\n      ...primary,\n      rect: {\n        top: primary.rect.top - desktopRect.top,\n        left: primary.rect.left - desktopRect.left,\n        right: primary.rect.right - desktopRect.left,\n        bottom: primary.rect.bottom - desktopRect.top,\n      },\n    };\n  }\n  return null;\n});\n\nlet user = $state(userInit);\nsubscribe(SeelenEvent.UserChanged, (e) => {\n  user = e.payload;\n});\n\nexport type State = typeof state;\nexport const state = {\n  get primaryMonitor() {\n    return relativePrimaryMonitor;\n  },\n  get user() {\n    return user;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/app.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n  import type { WidgetId } from \"@seelen-ui/lib/types\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import BrightnessControl from \"./components/BrightnessControl.svelte\";\n  import MediaDevices from \"./components/MediaDevices.svelte\";\n  import RadioButtons from \"./components/RadioButtons.svelte\";\n\n  function openAppSettings() {\n    invoke(SeelenCommand.TriggerWidget, {\n      payload: { id: \"@seelen/settings\" as WidgetId },\n    });\n  }\n\n  function openPowerMenu() {\n    invoke(SeelenCommand.TriggerWidget, {\n      payload: { id: \"@seelen/power-menu\" as WidgetId },\n    });\n  }\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<div class={[\"slu-standard-popover\", \"quick-settings\"]}>\n  <RadioButtons />\n  <BrightnessControl />\n  <MediaDevices />\n\n  <div class=\"quick-settings-footer\">\n    <button data-skin=\"transparent\" onclick={openAppSettings} title=\"App Settings\">\n      <Icon iconName=\"RiSettings4Fill\" />\n    </button>\n\n    <button data-skin=\"transparent\" onclick={openPowerMenu} title=\"Power\">\n      <Icon iconName=\"IoPower\" />\n    </button>\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/components/BrightnessControl.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { state } from \"../state.svelte\";\n  import { brightnessIcon } from \"libs/ui/utils\";\n  import { throttle } from \"lodash\";\n\n  const setBrightnessThrottled = throttle((instanceName: string, level: number) => {\n    invoke(SeelenCommand.SetMonitorBrightness, { instanceName, level });\n  }, 100);\n</script>\n\n{#each state.brightness as brightness}\n  <span class=\"quick-settings-label\">Brightness</span>\n  <div class=\"quick-settings-item\">\n    <button data-skin=\"transparent\">\n      <Icon iconName={brightnessIcon(brightness.currentBrightness)} />\n    </button>\n    <input\n      type=\"range\"\n      data-skin=\"flat\"\n      value={brightness.currentBrightness}\n      oninput={(e) => {\n        setBrightnessThrottled(brightness.instanceName, Number(e.currentTarget.value));\n      }}\n      min={brightness.availableLevels[0]}\n      max={brightness.availableLevels[brightness.levels]}\n    />\n  </div>\n{/each}\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/components/MediaDevices.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { state } from \"../state.svelte\";\n  import { throttle } from \"lodash\";\n\n  let defaultOutput = $derived(state.mediaOutputs.find((d) => d.isDefaultMultimedia));\n  let defaultInput = $derived(state.mediaInputs.find((d) => d.isDefaultMultimedia));\n\n  const setVolumeThrottled = throttle((deviceId: string, level: number) => {\n    invoke(SeelenCommand.SetVolumeLevel, {\n      deviceId,\n      sessionId: null,\n      level,\n    });\n  }, 100);\n\n  function toggleMute(deviceId: string) {\n    invoke(SeelenCommand.MediaToggleMute, { deviceId, sessionId: null });\n  }\n</script>\n\n{#if defaultInput || defaultOutput}\n  <span class=\"quick-settings-label\">Default Multimedia Volume</span>\n{/if}\n\n{#if defaultOutput}\n  <div class=\"quick-settings-item\">\n    <button data-skin=\"transparent\" onclick={() => toggleMute(defaultOutput!.id)}>\n      <Icon\n        iconName={defaultOutput.muted ? \"IoVolumeMuteOutline\" : \"IoVolumeHighOutline\"}\n        size={20}\n      />\n    </button>\n    <input\n      type=\"range\"\n      data-skin=\"flat\"\n      value={defaultOutput.volume}\n      oninput={(e) => {\n        setVolumeThrottled(defaultOutput.id, Number(e.currentTarget.value));\n      }}\n      min={0}\n      max={1}\n      step={0.01}\n    />\n    <span class=\"quick-settings-percentage\">\n      {Math.round(defaultOutput.volume * 100)}%\n    </span>\n  </div>\n{/if}\n\n{#if defaultInput}\n  <div class=\"quick-settings-item\">\n    <button data-skin=\"transparent\" onclick={() => toggleMute(defaultInput!.id)}>\n      <Icon iconName={defaultInput.muted ? \"BiMicrophoneOff\" : \"BiMicrophone\"} size={20} />\n    </button>\n    <input\n      type=\"range\"\n      data-skin=\"flat\"\n      value={defaultInput.volume}\n      oninput={(e) => {\n        setVolumeThrottled(defaultInput.id, Number(e.currentTarget.value));\n      }}\n      min={0}\n      max={1}\n      step={0.01}\n    />\n    <span class=\"quick-settings-percentage\">\n      {Math.round(defaultInput.volume * 100)}%\n    </span>\n  </div>\n{/if}\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/components/RadioButtons.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { RadioDeviceKind, type RadioDevice } from \"@seelen-ui/lib/types\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { state } from \"../state.svelte\";\n  import type { IconName } from \"libs/ui/icons\";\n\n  function getRadioIcon(kind: RadioDeviceKind): IconName {\n    switch (kind) {\n      case RadioDeviceKind.WiFi:\n        return \"IoWifiSharp\";\n      case RadioDeviceKind.Bluetooth:\n        return \"IoBluetooth\";\n      case RadioDeviceKind.MobileBroadband:\n        return \"IoPhonePortraitSharp\";\n      case RadioDeviceKind.FM:\n        return \"IoRadio\";\n      case RadioDeviceKind.Other:\n        return \"IoRadioButtonOnSharp\";\n    }\n  }\n\n  function getRadioLabel(kind: RadioDeviceKind): string {\n    switch (kind) {\n      case \"WiFi\":\n        return \"Wi-Fi\";\n      case \"Bluetooth\":\n        return \"Bluetooth\";\n      case \"MobileBroadband\":\n        return \"Mobile Broadband\";\n      case \"FM\":\n        return \"FM Radio\";\n      default:\n        return \"Unknown\";\n    }\n  }\n\n  async function toggleRadio(radio: RadioDevice) {\n    await invoke(SeelenCommand.SetRadioState, {\n      kind: radio.kind,\n      enabled: !radio.is_enabled,\n    });\n  }\n</script>\n\n{#if state.radios.length > 0}\n  <div class=\"radio-buttons-container\">\n    {#each state.radios as radio (radio.id)}\n      <button\n        class=\"radio-button\"\n        class:radio-button-enabled={radio.is_enabled}\n        class:radio-button-disabled={!radio.is_enabled}\n        onclick={() => toggleRadio(radio)}\n        title={`${radio.name} - ${radio.is_enabled ? \"Enabled\" : \"Disabled\"}`}\n      >\n        <Icon iconName={getRadioIcon(radio.kind)} size=\"2rem\" />\n        <span class=\"radio-button-label\">{getRadioLabel(radio.kind)}</span>\n      </button>\n    {/each}\n  </div>\n{/if}\n\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/quick-settings/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport type { MediaDevice, RadioDevice } from \"@seelen-ui/lib/types\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\n\n// Initialize lazy signals\nconst brightness = lazyRune(() => invoke(SeelenCommand.GetAllMonitorsBrightness));\nsubscribe(SeelenEvent.SystemMonitorsBrightnessChanged, brightness.setByPayload);\n\nconst mediaDevices = lazyRune(async () => {\n  const [inputs, outputs] = await invoke(SeelenCommand.GetMediaDevices);\n  return { inputs, outputs };\n});\nsubscribe(SeelenEvent.MediaDevices, ({ payload: [inputs, outputs] }) => {\n  mediaDevices.value = { inputs, outputs };\n});\n\nconst radios = lazyRune(() => invoke(SeelenCommand.GetRadios));\nsubscribe(SeelenEvent.RadiosChanged, radios.setByPayload);\n\nawait Promise.all([brightness.init(), mediaDevices.init(), radios.init()]);\n\nclass State {\n  get brightness() {\n    return brightness.value;\n  }\n\n  get mediaInputs(): MediaDevice[] {\n    return mediaDevices.value.inputs;\n  }\n  get mediaOutputs(): MediaDevice[] {\n    return mediaDevices.value.outputs;\n  }\n  get radios(): RadioDevice[] {\n    return radios.value;\n  }\n}\nexport const state = new State();\n"
  },
  {
    "path": "src/ui/svelte/system-tray/app.svelte",
    "content": "<script lang=\"ts\">\n  import { SystrayIconAction, type SysTrayIconId } from \"@seelen-ui/lib/types\";\n  import { state } from \"./state.svelte\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n  import { MissingIcon } from \"libs/ui/svelte/components/Icon\";\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n\n  function onClick(event: MouseEvent, id: SysTrayIconId) {\n    // prevent be triggered by double click\n    if (event.detail === 2) {\n      return;\n    }\n\n    let action = SystrayIconAction.LeftClick;\n\n    if (event.button === 1) {\n      action = SystrayIconAction.MiddleClick;\n    } else if (event.button === 2) {\n      action = SystrayIconAction.RightClick;\n    }\n\n    invoke(SeelenCommand.SendSystemTrayIconAction, {\n      id,\n      action,\n    });\n  }\n\n  function onDoubleClick(e: MouseEvent, id: SysTrayIconId) {\n    e.preventDefault();\n    e.stopPropagation();\n    invoke(SeelenCommand.SendSystemTrayIconAction, {\n      id,\n      action: SystrayIconAction.LeftDoubleClick,\n    });\n  }\n\n  const GUIDS_TO_IGNORE = [\n    \"7820ae73-23e3-4229-82c1-e41cb67d5b9c\", // speaker volument icon\n    \"7820ae74-23e3-4229-82c1-e41cb67d5b9c\", // network icon\n    \"7820ae75-23e3-4229-82c1-e41cb67d5b9c\", // battery icon\n  ];\n</script>\n\n<div class={[\"slu-standard-popover\", \"system-tray\"]}>\n  {#each state.trayItems as item}\n    {#if item.is_visible && (!item.guid || !GUIDS_TO_IGNORE.includes(item.guid))}\n      <button\n        class=\"system-tray-item\"\n        onclick={(e) => onClick(e, item.stable_id)}\n        ondblclick={(e) => onDoubleClick(e, item.stable_id)}\n        oncontextmenu={(e) => onClick(e, item.stable_id)}\n        onmouseenter={() => {\n          /* invoke(SeelenCommand.SendSystemTrayIconAction, {\n            id: item.stable_id,\n            action: SystrayIconAction.HoverEnter,\n          }); */\n        }}\n        onmousemove={() => {\n          /* invoke(SeelenCommand.SendSystemTrayIconAction, {\n            id: item.stable_id,\n            action: SystrayIconAction.HoverMove,\n          }); */\n        }}\n        onmouseleave={() => {\n          /* invoke(SeelenCommand.SendSystemTrayIconAction, {\n            id: item.stable_id,\n            action: SystrayIconAction.HoverLeave,\n          }); */\n        }}\n      >\n        <div class=\"system-tray-item-icon-box\">\n          {#if !!item.icon_path}\n            <img\n              class=\"system-tray-item-icon\"\n              src={convertFileSrc(item.icon_path) + `?hash=${item.icon_image_hash || \"null\"}`}\n              alt=\"\"\n            />\n          {:else}\n            <MissingIcon class=\"system-tray-item-icon\" />\n          {/if}\n        </div>\n        <span class=\"system-tray-item-label\">\n          {item.tooltip || item.guid || `${item.window_handle?.toString(16)}::${item.uid}`}\n        </span>\n      </button>\n    {/if}\n  {/each}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/system-tray/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/system-tray/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/system-tray/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\n\nexport const state = $state({\n  trayItems: await invoke(SeelenCommand.GetSystemTrayIcons),\n});\n\nsubscribe(SeelenEvent.SystemTrayChanged, (e) => {\n  state.trayItems = e.payload;\n});\n"
  },
  {
    "path": "src/ui/svelte/task-switcher/App.svelte",
    "content": "<script lang=\"ts\">\n  import { Widget } from \"@seelen-ui/lib\";\n  import { globalState } from \"./state.svelte\";\n  import TaskItem from \"./components/TaskItem.svelte\";\n\n  $effect(() => {\n    Widget.self.ready({ show: false });\n  });\n</script>\n\n<div\n  class=\"task-switcher-overlay\"\n  role=\"dialog\"\n  tabindex=\"-1\"\n  onclick={() => {\n    globalState.showing = false;\n  }}\n  onkeypress={() => {}}\n>\n  <div class=\"task-switcher\">\n    {#each globalState.windows as window, index (window.hwnd)}\n      <TaskItem task={window} {index} />\n    {/each}\n  </div>\n</div>\n\n<style>\n  :global(body) {\n    overflow: hidden;\n    background: transparent;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/task-switcher/components/TaskItem.svelte",
    "content": "<script lang=\"ts\">\n  import type { UserAppWindow } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { globalState } from \"../state.svelte\";\n  import { FileIcon, Icon } from \"libs/ui/svelte/components/Icon\";\n  import MissingIcon from \"libs/ui/svelte/components/Icon/MissingIcon.svelte\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n\n  interface Props {\n    task: UserAppWindow;\n    index: number;\n  }\n\n  let { task, index }: Props = $props();\n\n  let boxRef: HTMLDivElement | undefined = $state();\n  const isSelected = $derived(task.hwnd === globalState.selectedWindow);\n  const preview = $derived(globalState.previews[task.hwnd]);\n\n  // Focus button when selected\n  $effect(() => {\n    if (isSelected && boxRef) {\n      boxRef.focus();\n    }\n  });\n\n  function handleKeyDown(e: KeyboardEvent) {\n    // Handle Enter key to activate the window\n    if (e.key === \"Enter\" || e.key === \" \") {\n      e.preventDefault();\n      boxRef?.click();\n      return;\n    }\n\n    // Handle navigation keys\n    const isNavigationKey = e.key === \"Tab\" || e.key === \"ArrowRight\" || e.key === \"ArrowLeft\";\n\n    if (isNavigationKey) {\n      e.preventDefault();\n\n      const direction =\n        e.key === \"ArrowLeft\" || (e.key === \"Tab\" && e.shiftKey) ? \"previous\" : \"next\";\n\n      navigateToItem(direction, index);\n    }\n  }\n\n  function handleClick() {\n    globalState.showing = false;\n    invoke(SeelenCommand.WegToggleWindowState, {\n      hwnd: task.hwnd,\n      wasFocused: false,\n    });\n    // Optimistically reorder UI before backend updates\n    globalState.moveSelectedToFront(task.hwnd);\n  }\n\n  function handleFocus() {\n    globalState.selectedWindow = task.hwnd;\n  }\n\n  // Navigation helper functions\n  function getNextIndex(currentIndex: number, totalItems: number): number {\n    return (currentIndex + 1) % totalItems;\n  }\n\n  function getPreviousIndex(currentIndex: number, totalItems: number): number {\n    return (currentIndex - 1 + totalItems) % totalItems;\n  }\n\n  function navigateToItem(direction: \"next\" | \"previous\", currentIndex: number): void {\n    const windows = globalState.windows;\n    const totalItems = windows.length;\n\n    if (totalItems === 0) return;\n\n    const nextIndex =\n      direction === \"next\"\n        ? getNextIndex(currentIndex, totalItems)\n        : getPreviousIndex(currentIndex, totalItems);\n\n    globalState.selectedWindow = windows[nextIndex]?.hwnd || null;\n  }\n</script>\n\n<div\n  bind:this={boxRef}\n  class=\"task\"\n  role=\"button\"\n  tabindex=\"0\"\n  onkeydown={handleKeyDown}\n  onclick={handleClick}\n  onfocus={handleFocus}\n>\n  <div class=\"task-header\">\n    <FileIcon class=\"task-icon\" path={task.relaunch?.icon || task.process.path} umid={task.umid} />\n    <div class=\"task-title\">{task.title}</div>\n    <button\n      data-skin=\"transparent\"\n      onclick={(e) => {\n        e.stopPropagation();\n        invoke(SeelenCommand.WegCloseApp, { hwnd: task.hwnd });\n      }}\n    >\n      <Icon iconName=\"TbX\" />\n    </button>\n  </div>\n  <div class=\"task-preview-container\">\n    {#if preview}\n      <img class=\"task-preview\" src={convertFileSrc(preview.path) + \"?v=\" + preview.hash} alt=\"\" />\n    {:else}\n      <MissingIcon class=\"task-no-preview\" />\n    {/if}\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/task-switcher/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./App.svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/colors.css\";\nimport \"@shared/styles/reset.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init();\nawait widget.window.setResizable(false);\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/task-switcher/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/task-switcher/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nlet widget = Widget.getCurrent();\n\nlet showing = $state(false);\nlet autoConfirm = $state(false);\n\nlet windows = lazyRune(() => invoke(SeelenCommand.GetUserAppWindows));\nsubscribe(SeelenEvent.UserAppWindowsChanged, windows.setByPayload);\n\nlet previews = lazyRune(() => invoke(SeelenCommand.GetUserAppWindowsPreviews));\nsubscribe(SeelenEvent.UserAppWindowsPreviewsChanged, previews.setByPayload);\n\nlet focusedWinId = lazyRune(async () => (await invoke(SeelenCommand.GetFocusedApp)).hwnd);\nsubscribe(SeelenEvent.GlobalFocusChanged, (e) => {\n  focusedWinId.value = e.payload.hwnd;\n});\n\nlet monitors = lazyRune(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\n\nawait Promise.all([windows.init(), previews.init(), focusedWinId.init(), monitors.init()]);\n\nlet selectedWindow = $state<number | null>(focusedWinId.value);\n\n// Only sync with focused window when task switcher is hidden\n$effect.root(() => {\n  $effect(() => {\n    if (!showing) {\n      const win = windows.value.find((w) => w.hwnd === focusedWinId.value);\n      selectedWindow = win?.hwnd || null;\n    }\n  });\n});\n\nclass State {\n  get showing() {\n    return showing;\n  }\n\n  set showing(value: boolean) {\n    showing = value;\n  }\n\n  get windows() {\n    return windows.value;\n  }\n\n  get previews() {\n    return previews.value;\n  }\n\n  get selectedWindow() {\n    return selectedWindow;\n  }\n  set selectedWindow(value: number | null) {\n    selectedWindow = value;\n  }\n\n  /**\n   * Optimistically moves the selected window to the front of the array\n   * for fast UI responsiveness. The backend will send the updated order\n   * via events, but this provides immediate visual feedback.\n   */\n  moveSelectedToFront(hwnd: number) {\n    const currentWindows = windows.value;\n    const selectedIndex = currentWindows.findIndex((w) => w.hwnd === hwnd);\n\n    if (selectedIndex > 0) {\n      // Only reorder if not already at the front\n      const reordered = [\n        currentWindows[selectedIndex]!,\n        ...currentWindows.slice(0, selectedIndex),\n        ...currentWindows.slice(selectedIndex + 1),\n      ];\n      windows.value = reordered;\n    }\n  }\n}\n\nexport const globalState = new State();\n\n// +++++++++++++++++++++++ Triggering +++++++++++++++++++++++\n\n$effect.root(() => {\n  $effect(() => {\n    if (showing) {\n      widget.show().then(() => widget.focus());\n    } else {\n      widget.hide();\n    }\n  });\n});\n\nwidget.onTrigger((payload) => {\n  const direction: string = (payload.customArgs?.direction as string) || \"next\";\n  const autoConfirmValue: boolean = (payload.customArgs?.autoConfirm as boolean) || false;\n\n  // Don't show if there are no windows\n  if (windows.value.length === 0) {\n    return;\n  }\n\n  // If switcher was hidden, use focused window as starting point\n  const currentHwnd = showing ? selectedWindow : focusedWinId.value;\n\n  // Only set autoConfirm on first show (when switcher was hidden)\n  if (!showing) {\n    autoConfirm = autoConfirmValue;\n  }\n  showing = true;\n\n  let index = windows.value.findIndex((w) => w.hwnd === currentHwnd);\n  if (direction === \"next\") {\n    if (index === -1) {\n      index = windows.value.length - 1; // Will cycle to 0 with (index + 1) % length\n    }\n    selectedWindow = windows.value[(index + 1) % windows.value.length]?.hwnd || null;\n  } else if (direction === \"previous\") {\n    if (index === -1) {\n      index = 0; // Will cycle to last with (index - 1 + length) % length\n    }\n    selectedWindow = windows.value[(index - 1 + windows.value.length) % windows.value.length]?.hwnd || null;\n  }\n});\n\nwidget.window.onFocusChanged(({ payload }) => {\n  if (payload) {\n    invoke(SeelenCommand.GetKeyState, { key: \"Alt\" }).then((isPressing) => {\n      if (!isPressing) {\n        window.dispatchEvent(new KeyboardEvent(\"keyup\", { key: \"Alt\" }));\n      }\n    });\n  } else {\n    showing = false;\n  }\n});\n\nwindow.onkeydown = (e) => {\n  if (e.key === \"Escape\") {\n    showing = false;\n  }\n};\n\nwindow.onkeyup = (e) => {\n  if (e.key === \"Alt\" && showing && selectedWindow && autoConfirm) {\n    showing = false;\n    invoke(SeelenCommand.WegToggleWindowState, {\n      hwnd: selectedWindow,\n      wasFocused: false,\n    });\n    // Optimistically reorder UI before backend updates\n    globalState.moveSelectedToFront(selectedWindow);\n  }\n};\n\n// +++++++++++++++++++++++ Sizing +++++++++++++++++++++++\n\nlet primaryMonitor = $derived.by(() => {\n  return monitors.value.find((m) => m.isPrimary) || monitors.value[0];\n});\n\n$effect.root(() => {\n  $effect(() => {\n    if (primaryMonitor) {\n      Widget.getCurrent().setPosition(primaryMonitor.rect);\n    }\n  });\n});\n"
  },
  {
    "path": "src/ui/svelte/user-menu/app.svelte",
    "content": "<script lang=\"ts\">\n  import { globalState } from \"./state/mod.svelte\";\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"./i18n\";\n  import UserProfile from \"./components/UserProfile.svelte\";\n  import UserFolder from \"./components/UserFolder.svelte\";\n  import { path } from \"@tauri-apps/api\";\n  import { knownFolders } from \"./state/knownFolders.svelte\";\n\n  $effect(() => {\n    Widget.getCurrent().ready();\n  });\n\n  async function openInstallationFolder() {\n    invoke(SeelenCommand.OpenFile, { path: await path.appDataDir() });\n  }\n\n  async function openLogFolder() {\n    invoke(SeelenCommand.OpenFile, { path: await path.appLogDir() });\n  }\n</script>\n\n<div class=\"slu-standard-popover user-popup\">\n  <UserProfile user={globalState.user} />\n\n  <hr />\n  <span class=\"user-label\">{$t(\"folders.title\")}</span>\n  {#each Object.entries(knownFolders.value) as folder (folder[0])}\n    <UserFolder type={folder[0] as any} {...folder[1]} />\n  {/each}\n\n  <hr />\n  <span class=\"user-label\">{$t(\"seelen_options.title\")}</span>\n  <div class=\"user-seelen-options\">\n    <button class=\"user-seelen-option-item\" onclick={openInstallationFolder}>\n      <Icon iconName=\"TbFolderCog\" />\n      <span class=\"user-seelen-option-item-title\">\n        {$t(\"seelen_options.open_installation_folder\")}\n      </span>\n    </button>\n    <button class=\"user-seelen-option-item\" onclick={openLogFolder}>\n      <Icon iconName=\"TbLogs\" />\n      <span class=\"user-seelen-option-item-title\">\n        {$t(\"seelen_options.open_log_folder\")}\n      </span>\n    </button>\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/user-menu/components/EmptyList.svelte",
    "content": "<script lang=\"ts\">\n  import { t } from \"../i18n\";\n</script>\n\n<div class=\"user-empty-list\">{$t(\"empty_list\")}</div>\n"
  },
  {
    "path": "src/ui/svelte/user-menu/components/FilePreview.svelte",
    "content": "<script lang=\"ts\">\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import FileIcon from \"libs/ui/svelte/components/Icon/FileIcon.svelte\";\n\n  interface Props {\n    path: string;\n    displayName: string;\n  }\n\n  let { path, displayName }: Props = $props();\n\n  function selectFileOnExplorer() {\n    invoke(SeelenCommand.SelectFileOnExplorer, { path });\n  }\n</script>\n\n<button class=\"user-file\" onclick={selectFileOnExplorer} title={path}>\n  <FileIcon class=\"user-file-icon\" {path} lazy />\n  <div class=\"user-file-label\">{displayName}</div>\n  <!-- <div class=\"user-file-date\">{lastAccessTime}</div> -->\n</button>\n"
  },
  {
    "path": "src/ui/svelte/user-menu/components/UserFolder.svelte",
    "content": "<script lang=\"ts\">\n  import { FolderType } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { path } from \"@tauri-apps/api\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"../i18n\";\n  import { globalState } from \"../state/mod.svelte\";\n  import FilePreview from \"./FilePreview.svelte\";\n  import EmptyList from \"./EmptyList.svelte\";\n\n  interface Props {\n    type: FolderType;\n    icon: string;\n    content: { path: string; displayName: string }[];\n  }\n\n  let { type, icon, content }: Props = $props();\n\n  const isOpen = $derived(globalState.openCategory === type);\n\n  async function getPathByFolderType(folderType: FolderType): Promise<string> {\n    switch (folderType) {\n      case FolderType.Recent:\n        return (await path.dataDir()) + \"\\\\Microsoft\\\\Windows\\\\Recent\";\n      case FolderType.Desktop:\n        return await path.desktopDir();\n      case FolderType.Documents:\n        return await path.documentDir();\n      case FolderType.Downloads:\n        return await path.downloadDir();\n      case FolderType.Music:\n        return await path.audioDir();\n      case FolderType.Pictures:\n        return await path.pictureDir();\n      case FolderType.Videos:\n        return await path.videoDir();\n    }\n  }\n\n  async function openOnExplorer() {\n    invoke(SeelenCommand.OpenFile, {\n      path: await getPathByFolderType(type),\n    });\n  }\n\n  function onClickChevron(e: MouseEvent) {\n    e.stopPropagation();\n    globalState.openCategory = isOpen ? null : type;\n  }\n\n  function getFolderTranslationKey(type: FolderType): string {\n    return `folders.${type.toLowerCase()}`;\n  }\n</script>\n\n<div class=\"user-directory\">\n  <div\n    class=\"user-directory-summary\"\n    tabindex=\"0\"\n    role=\"button\"\n    onclick={openOnExplorer}\n    onkeydown={(e) => {\n      if (e.key === \"Enter\" || e.key === \" \") {\n        e.currentTarget.click();\n      }\n    }}\n  >\n    <Icon iconName={icon as any} class=\"user-directory-icon\" />\n    <span class=\"user-directory-title\">{$t(getFolderTranslationKey(type))}</span>\n    <button data-skin=\"transparent\" onclick={onClickChevron}>\n      <Icon\n        iconName=\"IoIosArrowDown\"\n        class={[\n          \"chevron\",\n          {\n            \"chevron-active\": isOpen,\n          },\n        ]}\n      />\n    </button>\n  </div>\n\n  {#if isOpen}\n    <div class=\"file-list\">\n      {#if content.length === 0}\n        <EmptyList />\n      {/if}\n\n      {#each content as file (file.path)}\n        <FilePreview path={file.path} displayName={file.displayName} />\n      {/each}\n    </div>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/user-menu/components/UserProfile.svelte",
    "content": "<script lang=\"ts\">\n  import type { User } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import { path } from \"@tauri-apps/api\";\n  import Icon from \"libs/ui/svelte/components/Icon/Icon.svelte\";\n  import { t } from \"../i18n\";\n\n  interface Props {\n    user: User;\n  }\n\n  let { user }: Props = $props();\n\n  async function openUserFolder() {\n    invoke(SeelenCommand.OpenFile, { path: await path.homeDir() });\n  }\n\n  function openOneDrive() {\n    if (user.oneDrivePath) {\n      invoke(SeelenCommand.OpenFile, { path: user.oneDrivePath });\n    }\n  }\n\n  function openAccountSettings() {\n    invoke(SeelenCommand.OpenFile, { path: \"ms-settings:accounts\" });\n  }\n\n  function logOut() {\n    invoke(SeelenCommand.LogOut);\n  }\n</script>\n\n<div class=\"user-profile-container\">\n  <div class=\"user-profile-picture-container\">\n    {#if user.profilePicturePath}\n      <img\n        class=\"user-profile-picture\"\n        src={convertFileSrc(user.profilePicturePath)}\n        alt={user.name}\n      />\n    {:else}\n      <div class=\"user-profile-picture-fallback\">\n        <Icon iconName=\"PiFolderUser\" />\n      </div>\n    {/if}\n    <button\n      class=\"user-profile-lock-button\"\n      onclick={logOut}\n      title={$t(\"profile.log_out\")}\n    >\n      <Icon iconName=\"BiLogOut\" />\n    </button>\n    <button\n      class=\"user-profile-settings-button\"\n      onclick={openAccountSettings}\n      title={$t(\"profile.accounts\")}\n    >\n      <Icon iconName=\"RiSettings3Fill\" />\n    </button>\n  </div>\n\n  <div class=\"user-profile-information\">\n    <div class=\"user-profile-name\">\n      <span>{user.name}</span>\n      <button\n        class=\"user-profile-action-button\"\n        onclick={openUserFolder}\n        title={$t(\"profile.open_user_folder\")}\n      >\n        <Icon iconName=\"PiFolderUser\" />\n      </button>\n    </div>\n\n    {#if user.email}\n      <div class=\"user-profile-email\">{user.email}</div>\n    {/if}\n\n    {#if user.oneDrivePath}\n      <button\n        class=\"user-profile-action-button\"\n        onclick={openOneDrive}\n        title={$t(\"profile.open_onedrive\")}\n      >\n        <Icon iconName=\"ImOnedrive\" />\n        <span>OneDrive</span>\n      </button>\n    {/if}\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/af.yml",
    "content": "empty_list: Geen items\nfolders:\n  desktop: Desktop\n  documents: Dokumente\n  downloads: Aflaaie\n  more_items: Wys meer...\n  music: Musiek\n  pictures: Prente\n  recent: Onlangs gebruik\n  reduce_items: Wys minder...\n  title: Gebruikersgidse\n  videos: Video's\nprofile:\n  accounts: Maak rekeninginstellings oop\n  log_out: Teken uit\n  open_onedrive: Maak OneDrive-gids oop\n  open_user_folder: Maak gebruikersgids oop\nseelen_options:\n  open_installation_folder: Maak Seelen-instellingsmap oop\n  open_log_folder: Maak Seelen-loglêergids oop\n  title: Seelen-gidse\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/am.yml",
    "content": "empty_list: ምንም እቃዎች የሉም\nfolders:\n  desktop: ዴስክቶፕ\n  documents: ሰነዶች\n  downloads: ውርዶች\n  more_items: ተጨማሪ አሳይ...\n  music: ሙዚቃ\n  pictures: ስዕሎች\n  recent: በቅርብ ጊዜ ጥቅም ላይ የዋለ\n  reduce_items: ያነሰ አሳይ...\n  title: የተጠቃሚ ማውጫዎች\n  videos: ቪዲዮዎች\nprofile:\n  accounts: የመለያ ቅንብሮችን ይክፈቱ\n  log_out: ውጣ\n  open_onedrive: OneDrive ማውጫን ክፈት\n  open_user_folder: የተጠቃሚ ማውጫን ክፈት\nseelen_options:\n  open_installation_folder: የ Seelen ቅንብሮች አቃፊን ይክፈቱ\n  open_log_folder: የ Seelen ሎግ አቃፊን ክፈት\n  title: የታዩ ማውጫዎች\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ar.yml",
    "content": "empty_list: لا توجد عناصر\nfolders:\n  desktop: سطح المكتب\n  documents: وثائق\n  downloads: التنزيلات\n  more_items: عرض المزيد...\n  music: موسيقى\n  pictures: الصور\n  recent: استخدمت مؤخرا\n  reduce_items: عرض أقل...\n  title: أدلة المستخدم\n  videos: فيديوهات\nprofile:\n  accounts: افتح إعدادات الحساب\n  log_out: تسجيل الخروج\n  open_onedrive: افتح دليل OneDrive\n  open_user_folder: افتح دليل المستخدم\nseelen_options:\n  open_installation_folder: افتح مجلد إعدادات Seelen\n  open_log_folder: افتح مجلد سجل Seelen\n  title: أدلة سيلين\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/az.yml",
    "content": "empty_list: Element yoxdur\nfolders:\n  desktop: İş masası\n  documents: Sənədlər\n  downloads: Yükləmələr\n  more_items: Daha çox göstər...\n  music: Musiqi\n  pictures: Şəkillər\n  recent: Bu yaxınlarda istifadə olunub\n  reduce_items: Daha az göstər...\n  title: İstifadəçi kataloqları\n  videos: Videolar\nprofile:\n  accounts: Hesab parametrlərini açın\n  log_out: Çıxın\n  open_onedrive: OneDrive qovluğunu açın\n  open_user_folder: İstifadəçi kataloqunu açın\nseelen_options:\n  open_installation_folder: Seelen parametrləri qovluğunu açın\n  open_log_folder: Seelen log qovluğunu açın\n  title: Görünən kataloqlar\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/bg.yml",
    "content": "empty_list: Няма елементи\nfolders:\n  desktop: Работен плот\n  documents: Документи\n  downloads: Изтегляния\n  more_items: Покажи още...\n  music: Музика\n  pictures: Снимки\n  recent: Наскоро използван\n  reduce_items: Покажи по-малко...\n  title: Потребителски директории\n  videos: Видеоклипове\nprofile:\n  accounts: Отворете настройките на акаунта\n  log_out: Излезте\n  open_onedrive: Отворете директорията на OneDrive\n  open_user_folder: Отворете потребителската директория\nseelen_options:\n  open_installation_folder: Отворете папката с настройки на Seelen\n  open_log_folder: Отворете папката с журнал на Seelen\n  title: Seelen директории\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/bn.yml",
    "content": "empty_list: কোনো আইটেম নেই\nfolders:\n  desktop: ডেস্কটপ\n  documents: নথিপত্র\n  downloads: ডাউনলোড\n  more_items: আরো দেখান...\n  music: সঙ্গীত\n  pictures: ছবি\n  recent: সম্প্রতি ব্যবহৃত\n  reduce_items: কম দেখান...\n  title: ব্যবহারকারী ডিরেক্টরি\n  videos: ভিডিও\nprofile:\n  accounts: অ্যাকাউন্ট সেটিংস খুলুন\n  log_out: লগ আউট করুন\n  open_onedrive: OneDrive ডিরেক্টরি খুলুন\n  open_user_folder: ব্যবহারকারী ডিরেক্টরি খুলুন\nseelen_options:\n  open_installation_folder: Seelen সেটিংস ফোল্ডার খুলুন\n  open_log_folder: Seelen লগ ফোল্ডার খুলুন\n  title: Seelen ডিরেক্টরি\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/bs.yml",
    "content": "empty_list: Nema stavki\nfolders:\n  desktop: Desktop\n  documents: Dokumenti\n  downloads: Preuzimanja\n  more_items: Prikaži više...\n  music: Muzika\n  pictures: Slike\n  recent: Nedavno korišteno\n  reduce_items: Prikaži manje...\n  title: Korisnički imenici\n  videos: Videos\nprofile:\n  accounts: Otvorite postavke računa\n  log_out: Odjavite se\n  open_onedrive: Otvorite OneDrive direktorij\n  open_user_folder: Otvorite korisnički imenik\nseelen_options:\n  open_installation_folder: Otvorite fasciklu podešavanja Seelen\n  open_log_folder: Otvorite Seelen log folder\n  title: Seelen imenici\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ca.yml",
    "content": "empty_list: No hi ha articles\nfolders:\n  desktop: Escriptori\n  documents: Documents\n  downloads: Descàrregues\n  more_items: Mostra més...\n  music: Música\n  pictures: Imatges\n  recent: Usat recentment\n  reduce_items: Mostra menys...\n  title: Directoris d'usuaris\n  videos: Vídeos\nprofile:\n  accounts: Obriu la configuració del compte\n  log_out: Tanca la sessió\n  open_onedrive: Obriu el directori OneDrive\n  open_user_folder: Obriu el directori d'usuaris\nseelen_options:\n  open_installation_folder: Obriu la carpeta de configuració de Seelen\n  open_log_folder: Obriu la carpeta de registre de Seelen\n  title: Directoris de Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/cs.yml",
    "content": "empty_list: Žádné položky\nfolders:\n  desktop: Desktop\n  documents: Dokumenty\n  downloads: Stahování\n  more_items: Zobrazit více...\n  music: Hudba\n  pictures: obrázky\n  recent: Nedávno použité\n  reduce_items: Zobrazit méně...\n  title: Uživatelské adresáře\n  videos: videa\nprofile:\n  accounts: Otevřete nastavení účtu\n  log_out: Odhlaste se\n  open_onedrive: Otevřete adresář OneDrive\n  open_user_folder: Otevřete adresář uživatele\nseelen_options:\n  open_installation_folder: Otevřete složku nastavení Seelen\n  open_log_folder: Otevřete složku protokolu Seelen\n  title: Seelen adresáře\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/cy.yml",
    "content": "empty_list: Dim eitemau\nfolders:\n  desktop: Penbwrdd\n  documents: Dogfennau\n  downloads: Lawrlwythiadau\n  more_items: Dangos mwy...\n  music: Cerddoriaeth\n  pictures: Lluniau\n  recent: Defnyddiwyd yn ddiweddar\n  reduce_items: Dangos llai...\n  title: Cyfeiriaduron defnyddwyr\n  videos: Fideos\nprofile:\n  accounts: Agor gosodiadau cyfrif\n  log_out: Allgofnodi\n  open_onedrive: Agor cyfeiriadur OneDrive\n  open_user_folder: Agor cyfeiriadur defnyddiwr\nseelen_options:\n  open_installation_folder: Agor ffolder gosodiadau Seelen\n  open_log_folder: Agor ffolder log Seelen\n  title: Cyfeiriaduron Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/da.yml",
    "content": "empty_list: Ingen varer\nfolders:\n  desktop: Desktop\n  documents: Dokumenter\n  downloads: Downloads\n  more_items: Vis mere...\n  music: Musik\n  pictures: Billeder\n  recent: Nyligt brugt\n  reduce_items: Vis mindre...\n  title: Brugermapper\n  videos: Videoer\nprofile:\n  accounts: Åbn kontoindstillinger\n  log_out: Log ud\n  open_onedrive: Åbn OneDrive-biblioteket\n  open_user_folder: Åbn brugermappe\nseelen_options:\n  open_installation_folder: Åbn mappen Seelen-indstillinger\n  open_log_folder: Åbn Seelen-logmappen\n  title: Seelen mapper\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/de.yml",
    "content": "empty_list: Keine Artikel\nfolders:\n  desktop: Desktop\n  documents: Unterlagen\n  downloads: Downloads\n  more_items: Mehr anzeigen...\n  music: Musik\n  pictures: Bilder\n  recent: Kürzlich verwendet\n  reduce_items: Weniger anzeigen...\n  title: Benutzerverzeichnisse\n  videos: Videos\nprofile:\n  accounts: Kontoeinstellungen öffnen\n  log_out: Abmelden\n  open_onedrive: Öffnen Sie das OneDrive-Verzeichnis\n  open_user_folder: Benutzerverzeichnis öffnen\nseelen_options:\n  open_installation_folder: Öffnen Sie den Seelen-Einstellungsordner\n  open_log_folder: Öffnen Sie den Seelen-Protokollordner\n  title: Seelen-Verzeichnisse\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/el.yml",
    "content": "empty_list: Δεν υπάρχουν στοιχεία\nfolders:\n  desktop: Επιτραπέζιος υπολογιστής\n  documents: Εγγραφα\n  downloads: Λήψεις\n  more_items: Εμφάνιση περισσότερων...\n  music: Μουσική\n  pictures: Εικόνες\n  recent: Χρησιμοποιήθηκε πρόσφατα\n  reduce_items: Εμφάνιση λιγότερων...\n  title: Καταλόγους χρηστών\n  videos: Βίντεο\nprofile:\n  accounts: Ανοίξτε τις ρυθμίσεις λογαριασμού\n  log_out: Αποσυνδεθείτε\n  open_onedrive: Ανοίξτε τον κατάλογο του OneDrive\n  open_user_folder: Ανοίξτε τον κατάλογο χρήστη\nseelen_options:\n  open_installation_folder: Ανοίξτε το φάκελο ρυθμίσεων Seelen\n  open_log_folder: Ανοίξτε το φάκελο καταγραφής Seelen\n  title: Καταλόγους Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/en.yml",
    "content": "empty_list: No items\nfolders:\n  desktop: Desktop\n  documents: Documents\n  downloads: Downloads\n  more_items: Show more...\n  music: Music\n  pictures: Pictures\n  recent: Recently used\n  reduce_items: Show less...\n  title: User directories\n  videos: Videos\nprofile:\n  accounts: Open account settings\n  log_out: Log out\n  open_onedrive: Open OneDrive directory\n  open_user_folder: Open user directory\nseelen_options:\n  open_installation_folder: Open Seelen settings folder\n  open_log_folder: Open Seelen log folder\n  title: Seelen directories\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/es.yml",
    "content": "empty_list: Sin artículos\nfolders:\n  desktop: De oficina\n  documents: Documentos\n  downloads: Descargas\n  more_items: Mostrar más...\n  music: Música\n  pictures: Fotos\n  recent: Usado recientemente\n  reduce_items: Mostrar menos...\n  title: Directorios de usuarios\n  videos: Vídeos\nprofile:\n  accounts: Abrir configuración de cuenta\n  log_out: Finalizar la sesión\n  open_onedrive: Abrir directorio de OneDrive\n  open_user_folder: Abrir directorio de usuarios\nseelen_options:\n  open_installation_folder: Abra la carpeta de configuración de Seelen\n  open_log_folder: Abrir la carpeta de registro de Seelen\n  title: Directorios de Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/et.yml",
    "content": "empty_list: Üksusi pole\nfolders:\n  desktop: Töölaud\n  documents: Dokumendid\n  downloads: Allalaadimised\n  more_items: Näita rohkem...\n  music: Muusika\n  pictures: Pildid\n  recent: Hiljuti kasutatud\n  reduce_items: Näita vähem...\n  title: Kasutajate kataloogid\n  videos: Videod\nprofile:\n  accounts: Avage konto seaded\n  log_out: Logi välja\n  open_onedrive: Avage OneDrive'i kataloog\n  open_user_folder: Ava kasutajakataloog\nseelen_options:\n  open_installation_folder: Avage Seeleni seadete kaust\n  open_log_folder: Avage Seeleni logikaust\n  title: Seeleni kataloogid\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/eu.yml",
    "content": "empty_list: Ez dago elementurik\nfolders:\n  desktop: Mahaigaina\n  documents: Dokumentuak\n  downloads: Deskargak\n  more_items: Erakutsi gehiago...\n  music: Musika\n  pictures: Irudiak\n  recent: Duela gutxi erabilia\n  reduce_items: Erakutsi gutxiago...\n  title: Erabiltzaileen direktorioa\n  videos: Bideoak\nprofile:\n  accounts: Ireki kontuaren ezarpenak\n  log_out: Amaitu saioa\n  open_onedrive: Ireki OneDrive direktorioa\n  open_user_folder: Ireki erabiltzailearen direktorioa\nseelen_options:\n  open_installation_folder: Ireki Seelen ezarpenen karpeta\n  open_log_folder: Ireki Seelen erregistro-karpeta\n  title: Seelen direktorioak\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/fa.yml",
    "content": "empty_list: هیچ موردی وجود ندارد\nfolders:\n  desktop: دسکتاپ\n  documents: اسناد\n  downloads: دانلودها\n  more_items: نمایش بیشتر...\n  music: موسیقی\n  pictures: تصاویر\n  recent: اخیرا استفاده شده است\n  reduce_items: نشان دادن کمتر...\n  title: دایرکتوری های کاربر\n  videos: ویدیوها\nprofile:\n  accounts: تنظیمات حساب را باز کنید\n  log_out: از سیستم خارج شوید\n  open_onedrive: دایرکتوری OneDrive را باز کنید\n  open_user_folder: دایرکتوری کاربر را باز کنید\nseelen_options:\n  open_installation_folder: پوشه تنظیمات Seelen را باز کنید\n  open_log_folder: پوشه log Seelen را باز کنید\n  title: دایرکتوری های Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/fi.yml",
    "content": "empty_list: Ei kohteita\nfolders:\n  desktop: Työpöytä\n  documents: Asiakirjat\n  downloads: Lataukset\n  more_items: Näytä lisää...\n  music: Musiikki\n  pictures: Kuvia\n  recent: Äskettäin käytetty\n  reduce_items: Näytä vähemmän...\n  title: Käyttäjähakemistot\n  videos: Videot\nprofile:\n  accounts: Avaa tilin asetukset\n  log_out: Kirjaudu ulos\n  open_onedrive: Avaa OneDrive-hakemisto\n  open_user_folder: Avaa käyttäjähakemisto\nseelen_options:\n  open_installation_folder: Avaa Seelen-asetuskansio\n  open_log_folder: Avaa Seelen-lokikansio\n  title: Seelen-hakemistot\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/fr.yml",
    "content": "empty_list: Aucun article\nfolders:\n  desktop: Bureau\n  documents: Documents\n  downloads: Téléchargements\n  more_items: Afficher plus...\n  music: Musique\n  pictures: Photos\n  recent: Récemment utilisé\n  reduce_items: Montrer moins...\n  title: Annuaires des utilisateurs\n  videos: Vidéos\nprofile:\n  accounts: Ouvrir les paramètres du compte\n  log_out: Se déconnecter\n  open_onedrive: Ouvrir le répertoire OneDrive\n  open_user_folder: Ouvrir le répertoire des utilisateurs\nseelen_options:\n  open_installation_folder: Ouvrir le dossier des paramètres de Seelen\n  open_log_folder: Ouvrir le dossier du journal Seelen\n  title: Annuaires Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/gu.yml",
    "content": "empty_list: કોઈ વસ્તુઓ નથી\nfolders:\n  desktop: ડેસ્કટોપ\n  documents: દસ્તાવેજો\n  downloads: ડાઉનલોડ્સ\n  more_items: વધુ બતાવો...\n  music: સંગીત\n  pictures: ચિત્રો\n  recent: તાજેતરમાં વપરાયેલ\n  reduce_items: ઓછું બતાવો...\n  title: વપરાશકર્તા ડિરેક્ટરીઓ\n  videos: વિડિઓઝ\nprofile:\n  accounts: એકાઉન્ટ સેટિંગ્સ ખોલો\n  log_out: લોગ આઉટ કરો\n  open_onedrive: OneDrive ડિરેક્ટરી ખોલો\n  open_user_folder: વપરાશકર્તા ડિરેક્ટરી ખોલો\nseelen_options:\n  open_installation_folder: સીલેન સેટિંગ્સ ફોલ્ડર ખોલો\n  open_log_folder: સીલેન લોગ ફોલ્ડર ખોલો\n  title: સીલેન ડિરેક્ટરીઓ\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/he.yml",
    "content": "empty_list: אין פריטים\nfolders:\n  desktop: שולחן עבודה\n  documents: מסמכים\n  downloads: הורדות\n  more_items: הצג עוד...\n  music: מוּסִיקָה\n  pictures: תמונות\n  recent: בשימוש לאחרונה\n  reduce_items: הצג פחות...\n  title: ספריות משתמשים\n  videos: סרטונים\nprofile:\n  accounts: פתח את הגדרות החשבון\n  log_out: התנתק\n  open_onedrive: פתח את ספריית OneDrive\n  open_user_folder: פתח את ספריית המשתמש\nseelen_options:\n  open_installation_folder: פתח את תיקיית ההגדרות של Seelen\n  open_log_folder: פתח את תיקיית היומן של Seelen\n  title: ספריות של Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/hi.yml",
    "content": "empty_list: कोई आइटम नहीं\nfolders:\n  desktop: डेस्कटॉप\n  documents: दस्तावेज़\n  downloads: डाउनलोड\n  more_items: और दिखाएँ...\n  music: संगीत\n  pictures: चित्र\n  recent: हाल ही में प्रयोग किया गया\n  reduce_items: कम दिखाओ...\n  title: उपयोगकर्ता निर्देशिकाएँ\n  videos: वीडियो\nprofile:\n  accounts: खाता सेटिंग खोलें\n  log_out: लॉग आउट\n  open_onedrive: वनड्राइव निर्देशिका खोलें\n  open_user_folder: उपयोगकर्ता निर्देशिका खोलें\nseelen_options:\n  open_installation_folder: सीलेन सेटिंग्स फ़ोल्डर खोलें\n  open_log_folder: सीलेन लॉग फ़ोल्डर खोलें\n  title: सीलेन निर्देशिकाएँ\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/hr.yml",
    "content": "empty_list: Nema predmeta\nfolders:\n  desktop: Radna površina\n  documents: Dokumenti\n  downloads: Preuzimanja\n  more_items: Prikaži više...\n  music: Glazba\n  pictures: Slike\n  recent: Nedavno korišteno\n  reduce_items: Prikaži manje...\n  title: Korisnički imenici\n  videos: Video zapisi\nprofile:\n  accounts: Otvorite postavke računa\n  log_out: Odjavi se\n  open_onedrive: Otvorite OneDrive direktorij\n  open_user_folder: Otvori korisnički imenik\nseelen_options:\n  open_installation_folder: Otvorite mapu postavki Seelen\n  open_log_folder: Otvorite mapu dnevnika Seelen\n  title: Seelen imenici\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/hu.yml",
    "content": "empty_list: Nincsenek tételek\nfolders:\n  desktop: Asztali\n  documents: Dokumentumok\n  downloads: Letöltések\n  more_items: Mutass többet...\n  music: Zene\n  pictures: Képek\n  recent: Nemrég használt\n  reduce_items: Mutass kevesebbet...\n  title: Felhasználói könyvtárak\n  videos: Videók\nprofile:\n  accounts: Nyissa meg a fiókbeállításokat\n  log_out: Jelentkezzen ki\n  open_onedrive: Nyissa meg a OneDrive könyvtárat\n  open_user_folder: Nyissa meg a felhasználói könyvtárat\nseelen_options:\n  open_installation_folder: Nyissa meg a Seelen beállítások mappát\n  open_log_folder: Nyissa meg a Seelen naplómappát\n  title: Seelen könyvtárak\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/hy.yml",
    "content": "empty_list: Նյութեր չկան\nfolders:\n  desktop: Սեղան\n  documents: Փաստաթղթեր\n  downloads: Ներբեռնումներ\n  more_items: Ցույց տալ ավելին...\n  music: Երաժշտություն\n  pictures: Նկարներ\n  recent: Վերջերս օգտագործված\n  reduce_items: Ցույց տալ ավելի քիչ...\n  title: Օգտագործողի գրացուցակներ\n  videos: Տեսանյութեր\nprofile:\n  accounts: Բացեք հաշվի կարգավորումները\n  log_out: Դուրս գալ\n  open_onedrive: Բացեք OneDrive գրացուցակը\n  open_user_folder: Բացեք օգտվողի գրացուցակը\nseelen_options:\n  open_installation_folder: Բացեք Seelen կարգավորումների պանակը\n  open_log_folder: Բացեք Seelen log թղթապանակը\n  title: Seelen դիրեկտորիաներ\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/id.yml",
    "content": "empty_list: Tidak ada item\nfolders:\n  desktop: Desktop\n  documents: Dokumen\n  downloads: Unduhan\n  more_items: Tampilkan lebih banyak...\n  music: Musik\n  pictures: Gambar\n  recent: Baru-baru ini digunakan\n  reduce_items: Tampilkan lebih sedikit...\n  title: Direktori pengguna\n  videos: Video\nprofile:\n  accounts: Buka pengaturan akun\n  log_out: Keluar\n  open_onedrive: Buka direktori OneDrive\n  open_user_folder: Buka direktori pengguna\nseelen_options:\n  open_installation_folder: Buka folder pengaturan Seelen\n  open_log_folder: Buka folder log Seelen\n  title: Direktori Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/is.yml",
    "content": "empty_list: Engir hlutir\nfolders:\n  desktop: Skrifborð\n  documents: Skjöl\n  downloads: Niðurhal\n  more_items: Sýna meira...\n  music: Tónlist\n  pictures: Myndir\n  recent: Nýlega notað\n  reduce_items: Sýna minna...\n  title: Notendaskrár\n  videos: Myndbönd\nprofile:\n  accounts: Opnaðu reikningsstillingar\n  log_out: Skráðu þig út\n  open_onedrive: Opnaðu OneDrive möppuna\n  open_user_folder: Opnaðu notendaskrá\nseelen_options:\n  open_installation_folder: Opnaðu Seelen stillingamöppuna\n  open_log_folder: Opnaðu Seelen log möppu\n  title: Seelen möppur\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/it.yml",
    "content": "empty_list: Nessun articolo\nfolders:\n  desktop: Desktop\n  documents: Documenti\n  downloads: Download\n  more_items: Mostra altro...\n  music: Musica\n  pictures: Immagini\n  recent: Usato di recente\n  reduce_items: Mostra meno...\n  title: Directory utente\n  videos: Video\nprofile:\n  accounts: Apri le impostazioni dell'account\n  log_out: Esci\n  open_onedrive: Apri la directory di OneDrive\n  open_user_folder: Apri la directory utente\nseelen_options:\n  open_installation_folder: Apri la cartella delle impostazioni di Seelen\n  open_log_folder: Apri la cartella di registro di Seelen\n  title: Directory Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ja.yml",
    "content": "empty_list: アイテムがありません\nfolders:\n  desktop: デスクトップ\n  documents: ドキュメント\n  downloads: ダウンロード\n  more_items: もっと見る...\n  music: ミュージック\n  pictures: ピクチャ\n  recent: 最近使用した項目\n  reduce_items: 表示を減らす...\n  title: ユーザーフォルダ\n  videos: ビデオ\nprofile:\n  accounts: アカウント設定を開く\n  log_out: ログアウト\n  open_onedrive: OneDrive フォルダを開く\n  open_user_folder: ユーザーフォルダを開く\nseelen_options:\n  open_installation_folder: SeelenUI 設定フォルダを開く\n  open_log_folder: SeelenUI ログフォルダを開く\n  title: SeelenUI フォルダ\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ka.yml",
    "content": "empty_list: არ არის ელემენტი\nfolders:\n  desktop: სამუშაო მაგიდა\n  documents: დოკუმენტები\n  downloads: ჩამოტვირთვები\n  more_items: მეტის ჩვენება...\n  music: მუსიკა\n  pictures: სურათები\n  recent: ცოტა ხნის წინ გამოყენებული\n  reduce_items: ნაკლების ჩვენება...\n  title: მომხმარებლის დირექტორიები\n  videos: ვიდეოები\nprofile:\n  accounts: გახსენით ანგარიშის პარამეტრები\n  log_out: გასვლა\n  open_onedrive: გახსენით OneDrive დირექტორია\n  open_user_folder: გახსენით მომხმარებლის დირექტორია\nseelen_options:\n  open_installation_folder: გახსენით Seelen პარამეტრების საქაღალდე\n  open_log_folder: გახსენით Seelen ჟურნალის საქაღალდე\n  title: Seelen დირექტორიები\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/km.yml",
    "content": "empty_list: គ្មានធាតុ\nfolders:\n  desktop: ផ្ទៃតុ\n  documents: ឯកសារ\n  downloads: ការទាញយក\n  more_items: បង្ហាញបន្ថែម...\n  music: តន្ត្រី\n  pictures: រូបភាព\n  recent: បានប្រើថ្មីៗនេះ\n  reduce_items: បង្ហាញតិច...\n  title: ថតអ្នកប្រើប្រាស់\n  videos: វីដេអូ\nprofile:\n  accounts: បើកការកំណត់គណនី\n  log_out: ចេញ\n  open_onedrive: បើកថត OneDrive\n  open_user_folder: បើកបញ្ជីអ្នកប្រើប្រាស់\nseelen_options:\n  open_installation_folder: បើកថតការកំណត់ Seelen\n  open_log_folder: បើកថតឯកសារ Seelen\n  title: បញ្ជីឈ្មោះ Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ko.yml",
    "content": "empty_list: 항목 없음\nfolders:\n  desktop: 데스크탑\n  documents: 서류\n  downloads: 다운로드\n  more_items: 더 보기...\n  music: 음악\n  pictures: 영화\n  recent: 최근에 사용됨\n  reduce_items: 간략히 보기...\n  title: 사용자 디렉토리\n  videos: 비디오\nprofile:\n  accounts: 계정 설정 열기\n  log_out: 로그아웃\n  open_onedrive: OneDrive 디렉터리 열기\n  open_user_folder: 사용자 디렉토리 열기\nseelen_options:\n  open_installation_folder: Seelen 설정 폴더 열기\n  open_log_folder: Seelen 로그 폴더 열기\n  title: Seelen 디렉토리\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ku.yml",
    "content": "empty_list: Tiştek tune\nfolders:\n  desktop: Desktop\n  documents: Documents\n  downloads: Daxistin\n  more_items: Zêdetir nîşan bide...\n  music: Mûzîk\n  pictures: Wêne\n  recent: Di demên dawî de tê bikaranîn\n  reduce_items: Kêmtir nîşan bide...\n  title: Derhênerên bikarhêner\n  videos: Videos\nprofile:\n  accounts: Mîhengên hesabê vekin\n  log_out: Derkeve\n  open_onedrive: Peldanka OneDrive vekin\n  open_user_folder: Peldanka bikarhêner vekin\nseelen_options:\n  open_installation_folder: Peldanka mîhengên Seelen vekin\n  open_log_folder: Peldanka têketinê ya Seelen veke\n  title: derhênerên Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/lb.yml",
    "content": "empty_list: Keng Elementer\nfolders:\n  desktop: Desktop\n  documents: Dokumenter\n  downloads: Downloads\n  more_items: Méi weisen ...\n  music: Musek\n  pictures: Biller\n  recent: Kuerzem benotzt\n  reduce_items: Weist manner ...\n  title: Benotzer Verzeechnes\n  videos: Videoen\nprofile:\n  accounts: Open Kont Astellunge\n  log_out: Ausloggen\n  open_onedrive: Open OneDrive Verzeechnes\n  open_user_folder: Öffnen vum Benotzerverzeichnis\nseelen_options:\n  open_installation_folder: Open Seelen Astellungen Dossier\n  open_log_folder: Open Seelen Log Dossier\n  title: Seelen directory\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/lo.yml",
    "content": "empty_list: ບໍ່ມີລາຍການ\nfolders:\n  desktop: ເດັສທັອບ\n  documents: ເອກະສານ\n  downloads: ດາວໂຫຼດ\n  more_items: ສະແດງເພີ່ມເຕີມ...\n  music: ດົນຕີ\n  pictures: ຮູບພາບ\n  recent: ໃຊ້ບໍ່ດົນມານີ້\n  reduce_items: ສະແດງໜ້ອຍລົງ...\n  title: ບັນຊີຜູ້ໃຊ້\n  videos: ວິດີໂອ\nprofile:\n  accounts: ເປີດການຕັ້ງຄ່າບັນຊີ\n  log_out: ອອກຈາກລະບົບ\n  open_onedrive: ເປີດໄດເລກະທໍລີ OneDrive\n  open_user_folder: ເປີດໄດເລກະທໍລີຜູ້ໃຊ້\nseelen_options:\n  open_installation_folder: ເປີດໂຟນເດີ Seelen settings\n  open_log_folder: ເປີດໂຟນເດີບັນທຶກ Seelen\n  title: Seelen directory\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/lt.yml",
    "content": "empty_list: Nėra elementų\nfolders:\n  desktop: Darbalaukis\n  documents: Dokumentai\n  downloads: Atsisiuntimai\n  more_items: Rodyti daugiau...\n  music: Muzika\n  pictures: Paveikslėliai\n  recent: Neseniai naudotas\n  reduce_items: Rodyti mažiau...\n  title: Vartotojų katalogai\n  videos: Vaizdo įrašai\nprofile:\n  accounts: Atidarykite paskyros nustatymus\n  log_out: Atsijungti\n  open_onedrive: Atidarykite OneDrive katalogą\n  open_user_folder: Atidarykite vartotojo katalogą\nseelen_options:\n  open_installation_folder: Atidarykite Seelen nustatymų aplanką\n  open_log_folder: Atidarykite Seelen žurnalo aplanką\n  title: Seelen katalogai\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/lv.yml",
    "content": "empty_list: Nav vienumu\nfolders:\n  desktop: Darbvirsma\n  documents: Dokumenti\n  downloads: Lejupielādes\n  more_items: Rādīt vairāk...\n  music: Mūzika\n  pictures: Bildes\n  recent: Nesen lietots\n  reduce_items: Rādīt mazāk...\n  title: Lietotāju katalogi\n  videos: Videoklipi\nprofile:\n  accounts: Atveriet konta iestatījumus\n  log_out: Atteikties\n  open_onedrive: Atveriet OneDrive direktoriju\n  open_user_folder: Atveriet lietotāja direktoriju\nseelen_options:\n  open_installation_folder: Atveriet Seelen iestatījumu mapi\n  open_log_folder: Atveriet Seelen žurnāla mapi\n  title: Seelen katalogi\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/mk.yml",
    "content": "empty_list: Нема ставки\nfolders:\n  desktop: Десктоп\n  documents: Документи\n  downloads: Преземања\n  more_items: Прикажи повеќе...\n  music: Музика\n  pictures: Слики\n  recent: Неодамна користен\n  reduce_items: Прикажи помалку...\n  title: Кориснички директориуми\n  videos: Видеа\nprofile:\n  accounts: Отворете ги поставките за сметката\n  log_out: Одјави се\n  open_onedrive: Отворете го директориумот OneDrive\n  open_user_folder: Отворете го корисничкиот директориум\nseelen_options:\n  open_installation_folder: Отворете ја папката за поставки Seelen\n  open_log_folder: Отворете ја папката за дневник Seelen\n  title: Селенски директориуми\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/mn.yml",
    "content": "empty_list: Ямар ч зүйл алга\nfolders:\n  desktop: Ширээний компьютер\n  documents: Баримт бичиг\n  downloads: Татаж авсан зүйлс\n  more_items: Дэлгэрэнгүй харуулах...\n  music: Хөгжим\n  pictures: Зураг\n  recent: Саяхан ашигласан\n  reduce_items: Бага харуулах...\n  title: Хэрэглэгчийн лавлахууд\n  videos: Видеонууд\nprofile:\n  accounts: Бүртгэлийн тохиргоог нээнэ үү\n  log_out: Гарах\n  open_onedrive: OneDrive лавлахыг нээнэ үү\n  open_user_folder: Хэрэглэгчийн лавлахыг нээх\nseelen_options:\n  open_installation_folder: Seelen тохиргооны хавтсыг нээнэ үү\n  open_log_folder: Seelen бүртгэлийн хавтсыг нээнэ үү\n  title: Seelen лавлахууд\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ms.yml",
    "content": "empty_list: Tiada barang\nfolders:\n  desktop: Desktop\n  documents: Dokumen\n  downloads: Muat turun\n  more_items: Tunjukkan lagi...\n  music: Muzik\n  pictures: Gambar\n  recent: Baru-baru ini digunakan\n  reduce_items: Kurangkan tunjukkan...\n  title: Direktori pengguna\n  videos: Video\nprofile:\n  accounts: Buka tetapan akaun\n  log_out: Log keluar\n  open_onedrive: Buka direktori OneDrive\n  open_user_folder: Buka direktori pengguna\nseelen_options:\n  open_installation_folder: Buka folder tetapan Seelen\n  open_log_folder: Buka folder log Seelen\n  title: Direktori Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/mt.yml",
    "content": "empty_list: Ebda oġġetti\nfolders:\n  desktop: Desktop\n  documents: Dokumenti\n  downloads: Downloads\n  more_items: Uri aktar...\n  music: Mużika\n  pictures: Stampi\n  recent: Użat reċentement\n  reduce_items: Uri inqas...\n  title: Direttorji tal-utenti\n  videos: Vidjows\nprofile:\n  accounts: Iftaħ is-settings tal-kont\n  log_out: Log out\n  open_onedrive: Iftaħ id-direttorju OneDrive\n  open_user_folder: Iftaħ id-direttorju tal-utent\nseelen_options:\n  open_installation_folder: Iftaħ il-folder tas-settings ta' Seelen\n  open_log_folder: Iftaħ il-fowlder tal-log Seelen\n  title: Direttorji Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ne.yml",
    "content": "empty_list: कुनै वस्तुहरू छैनन्\nfolders:\n  desktop: डेस्कटप\n  documents: कागजातहरू\n  downloads: डाउनलोडहरू\n  more_items: थप देखाउनुहोस्...\n  music: संगीत\n  pictures: चित्रहरू\n  recent: हालै प्रयोग गरिएको\n  reduce_items: कम देखाउनुहोस्...\n  title: प्रयोगकर्ता निर्देशिकाहरू\n  videos: भिडियोहरू\nprofile:\n  accounts: खाता सेटिङहरू खोल्नुहोस्\n  log_out: लग आउट गर्नुहोस्\n  open_onedrive: OneDrive डाइरेक्टरी खोल्नुहोस्\n  open_user_folder: प्रयोगकर्ता निर्देशिका खोल्नुहोस्\nseelen_options:\n  open_installation_folder: Seelen सेटिंग्स फोल्डर खोल्नुहोस्\n  open_log_folder: Seelen लग फोल्डर खोल्नुहोस्\n  title: Seelen निर्देशिकाहरू\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/nl.yml",
    "content": "empty_list: Geen artikelen\nfolders:\n  desktop: Bureaublad\n  documents: Documenten\n  downloads: Downloads\n  more_items: Toon meer...\n  music: Muziek\n  pictures: Afbeeldingen\n  recent: Recent gebruikt\n  reduce_items: Toon minder...\n  title: Gebruikersmappen\n  videos: Video's\nprofile:\n  accounts: Open accountinstellingen\n  log_out: Uitloggen\n  open_onedrive: Open de OneDrive-map\n  open_user_folder: Gebruikersmap openen\nseelen_options:\n  open_installation_folder: Open de map Seelen-instellingen\n  open_log_folder: Open de Seelen-logboekmap\n  title: Seelen-gidsen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/no.yml",
    "content": "empty_list: Ingen varer\nfolders:\n  desktop: Skrivebord\n  documents: Dokumenter\n  downloads: Nedlastinger\n  more_items: Vis mer...\n  music: Musikk\n  pictures: Bilder\n  recent: Nylig brukt\n  reduce_items: Vis mindre...\n  title: Brukerkataloger\n  videos: Videoer\nprofile:\n  accounts: Åpne kontoinnstillinger\n  log_out: Logg ut\n  open_onedrive: Åpne OneDrive-katalogen\n  open_user_folder: Åpne brukerkatalogen\nseelen_options:\n  open_installation_folder: Åpne mappen Seelen-innstillinger\n  open_log_folder: Åpne Seelen-loggmappen\n  title: Seelen-kataloger\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/pa.yml",
    "content": "empty_list: ਕੋਈ ਆਈਟਮਾਂ ਨਹੀਂ\nfolders:\n  desktop: ਡੈਸਕਟਾਪ\n  documents: ਦਸਤਾਵੇਜ਼\n  downloads: ਡਾਊਨਲੋਡ\n  more_items: ਹੋਰ ਦਿਖਾਓ...\n  music: ਸੰਗੀਤ\n  pictures: ਤਸਵੀਰਾਂ\n  recent: ਹਾਲ ਹੀ ਵਿੱਚ ਵਰਤਿਆ\n  reduce_items: ਘੱਟ ਦਿਖਾਓ...\n  title: ਉਪਭੋਗਤਾ ਡਾਇਰੈਕਟਰੀਆਂ\n  videos: ਵੀਡੀਓਜ਼\nprofile:\n  accounts: ਖਾਤਾ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ\n  log_out: ਲੌਗ ਆਊਟ ਕਰੋ\n  open_onedrive: OneDrive ਡਾਇਰੈਕਟਰੀ ਖੋਲ੍ਹੋ\n  open_user_folder: ਉਪਭੋਗਤਾ ਡਾਇਰੈਕਟਰੀ ਖੋਲ੍ਹੋ\nseelen_options:\n  open_installation_folder: ਸੀਲੇਨ ਸੈਟਿੰਗਜ਼ ਫੋਲਡਰ ਖੋਲ੍ਹੋ\n  open_log_folder: ਸੀਲੇਨ ਲੌਗ ਫੋਲਡਰ ਖੋਲ੍ਹੋ\n  title: ਸੀਲਨ ਡਾਇਰੈਕਟਰੀਆਂ\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/pl.yml",
    "content": "empty_list: Brak przedmiotów\nfolders:\n  desktop: Pulpit\n  documents: Dokumenty\n  downloads: Pliki do pobrania\n  more_items: Pokaż więcej...\n  music: Muzyka\n  pictures: Kino\n  recent: Ostatnio używany\n  reduce_items: Pokaż mniej...\n  title: Katalogi użytkowników\n  videos: Filmy\nprofile:\n  accounts: Otwórz ustawienia konta\n  log_out: Wyloguj się\n  open_onedrive: Otwórz katalog OneDrive\n  open_user_folder: Otwórz katalog użytkownika\nseelen_options:\n  open_installation_folder: Otwórz folder ustawień Seelen\n  open_log_folder: Otwórz folder dziennika Seelen\n  title: Katalogi Seelena\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ps.yml",
    "content": "empty_list: هیڅ توکي نشته\nfolders:\n  desktop: ډیسټاپ\n  documents: اسناد\n  downloads: ډاونلوډونه\n  more_items: نور ښکاره کړئ ...\n  music: موسیقي\n  pictures: انځورونه\n  recent: په دې وروستیو کې کارول کیږي\n  reduce_items: لږ وښایاست...\n  title: د کارن لارښود\n  videos: ویډیوګانې\nprofile:\n  accounts: د حساب ترتیبات خلاص کړئ\n  log_out: وتل\n  open_onedrive: د OneDrive لارښود خلاص کړئ\n  open_user_folder: د کارونکي لارښود خلاص کړئ\nseelen_options:\n  open_installation_folder: د سیلین ترتیبات فولډر خلاص کړئ\n  open_log_folder: د سیلین لاګ فولډر خلاص کړئ\n  title: سیلین لارښودونه\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/pt-BR.yml",
    "content": "empty_list: Nenhum item\nfolders:\n  desktop: Área de trabalho\n  documents: Documentos\n  downloads: Transferências\n  more_items: Mostrar mais...\n  music: Música\n  pictures: Fotos\n  recent: Usado recentemente\n  reduce_items: Mostrar menos...\n  title: Diretórios de usuários\n  videos: Vídeos\nprofile:\n  accounts: Abra as configurações da conta\n  log_out: Sair\n  open_onedrive: Abra o diretório OneDrive\n  open_user_folder: Abra o diretório do usuário\nseelen_options:\n  open_installation_folder: Abra a pasta de configurações do Seelen\n  open_log_folder: Abra a pasta de log do Seelen\n  title: Diretórios Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/pt-PT.yml",
    "content": "empty_list: Nenhum item\nfolders:\n  desktop: Área de trabalho\n  documents: Documentos\n  downloads: Transferências\n  more_items: Mostrar mais...\n  music: Música\n  pictures: Fotos\n  recent: Usado recentemente\n  reduce_items: Mostrar menos...\n  title: Diretórios de usuários\n  videos: Vídeos\nprofile:\n  accounts: Abra as configurações da conta\n  log_out: Sair\n  open_onedrive: Abra o diretório OneDrive\n  open_user_folder: Abra o diretório do usuário\nseelen_options:\n  open_installation_folder: Abra a pasta de configurações do Seelen\n  open_log_folder: Abra a pasta de log do Seelen\n  title: Diretórios Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ro.yml",
    "content": "empty_list: Fără articole\nfolders:\n  desktop: Desktop\n  documents: Documente\n  downloads: Descărcări\n  more_items: Arată mai multe...\n  music: Muzică\n  pictures: Poze\n  recent: Folosit recent\n  reduce_items: Arată mai puțin...\n  title: Directoare de utilizatori\n  videos: Videoclipuri\nprofile:\n  accounts: Deschideți setările contului\n  log_out: Deconectați-vă\n  open_onedrive: Deschideți directorul OneDrive\n  open_user_folder: Deschideți directorul de utilizatori\nseelen_options:\n  open_installation_folder: Deschideți folderul de setări Seelen\n  open_log_folder: Deschideți folderul de jurnal Seelen\n  title: directoarele Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ru.yml",
    "content": "empty_list: Нет товаров\nfolders:\n  desktop: Рабочий стол\n  documents: Документы\n  downloads: Загрузки\n  more_items: Показать больше...\n  music: Музыка\n  pictures: Картинки\n  recent: Недавно использовано\n  reduce_items: Показать меньше...\n  title: Каталоги пользователей\n  videos: Видео\nprofile:\n  accounts: Открыть настройки аккаунта\n  log_out: Выйти\n  open_onedrive: Открыть каталог OneDrive\n  open_user_folder: Открыть каталог пользователя\nseelen_options:\n  open_installation_folder: Открыть папку настроек Seelen\n  open_log_folder: Открыть папку журнала Seelen\n  title: Зеелен каталоги\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/si.yml",
    "content": "empty_list: අයිතම නැත\nfolders:\n  desktop: ඩෙස්ක්ටොප්\n  documents: ලේඛන\n  downloads: බාගැනීම්\n  more_items: තව පෙන්වන්න...\n  music: සංගීතය\n  pictures: පින්තූර\n  recent: මෑතකදී භාවිතා කරන ලදී\n  reduce_items: අඩුවෙන් පෙන්වන්න...\n  title: පරිශීලක නාමාවලි\n  videos: වීඩියෝ\nprofile:\n  accounts: ගිණුම් සැකසුම් විවෘත කරන්න\n  log_out: ලොග් අවුට් වෙන්න\n  open_onedrive: OneDrive නාමාවලිය විවෘත කරන්න\n  open_user_folder: පරිශීලක නාමාවලිය විවෘත කරන්න\nseelen_options:\n  open_installation_folder: Seelen සැකසුම් ෆෝල්ඩරය විවෘත කරන්න\n  open_log_folder: සීලෙන් ලොග් ෆෝල්ඩරය විවෘත කරන්න\n  title: සීලෙන් නාමාවලි\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/sk.yml",
    "content": "empty_list: Žiadne položky\nfolders:\n  desktop: Desktop\n  documents: dokumenty\n  downloads: sťahovanie\n  more_items: Zobraziť viac...\n  music: Hudba\n  pictures: Obrázky\n  recent: Nedávno použité\n  reduce_items: Zobraziť menej...\n  title: Používateľské adresáre\n  videos: Videá\nprofile:\n  accounts: Otvorte nastavenia účtu\n  log_out: Odhláste sa\n  open_onedrive: Otvorte adresár OneDrive\n  open_user_folder: Otvorte užívateľský adresár\nseelen_options:\n  open_installation_folder: Otvorte priečinok nastavení Seelen\n  open_log_folder: Otvorte priečinok protokolu Seelen\n  title: Seelen adresáre\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/so.yml",
    "content": "empty_list: Ma jiraan wax\nfolders:\n  desktop: Desktop\n  documents: Dukumentiyada\n  downloads: Soo dejinta\n  more_items: Wax badan muuji...\n  music: Muusiga\n  pictures: Sawirro\n  recent: Dhawaan la isticmaalay\n  reduce_items: Muuji wax yar...\n  title: Hagaha isticmaalaha\n  videos: Muuqaalo\nprofile:\n  accounts: Fur goobaha akoonnada\n  log_out: Ka bax\n  open_onedrive: Fur buugga OneDrive\n  open_user_folder: Fur hagaha isticmaalaha\nseelen_options:\n  open_installation_folder: Fur Seelen settings folder\n  open_log_folder: Fur Seelen log folder\n  title: Tilmaamaha Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/sr.yml",
    "content": "empty_list: Нема ставки\nfolders:\n  desktop: Десктоп\n  documents: Документи\n  downloads: Преузимања\n  more_items: Прикажи још...\n  music: Музика\n  pictures: Слике\n  recent: Недавно коришћен\n  reduce_items: Прикажи мање...\n  title: Кориснички именици\n  videos: Видеос\nprofile:\n  accounts: Отворите подешавања налога\n  log_out: Одјавите се\n  open_onedrive: Отворите ОнеДриве директоријум\n  open_user_folder: Отворите кориснички именик\nseelen_options:\n  open_installation_folder: Отворите фасциклу подешавања Сеелен\n  open_log_folder: Отворите фасциклу дневника Сеелен\n  title: Сеелен дирецториес\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/sv.yml",
    "content": "empty_list: Inga föremål\nfolders:\n  desktop: Skrivbord\n  documents: Dokument\n  downloads: Nedladdningar\n  more_items: Visa mer...\n  music: Musik\n  pictures: Bilder\n  recent: Nyligen använd\n  reduce_items: Visa mindre...\n  title: Användarkataloger\n  videos: Videor\nprofile:\n  accounts: Öppna kontoinställningar\n  log_out: Logga ut\n  open_onedrive: Öppna OneDrive-katalogen\n  open_user_folder: Öppna användarkatalogen\nseelen_options:\n  open_installation_folder: Öppna mappen Seelen-inställningar\n  open_log_folder: Öppna Seelen-loggmappen\n  title: Seelen kataloger\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/sw.yml",
    "content": "empty_list: Hakuna vitu\nfolders:\n  desktop: Eneo-kazi\n  documents: Nyaraka\n  downloads: Vipakuliwa\n  more_items: Onyesha zaidi...\n  music: Muziki\n  pictures: Picha\n  recent: Iliyotumiwa hivi karibuni\n  reduce_items: Onyesha kidogo...\n  title: Saraka za watumiaji\n  videos: Video\nprofile:\n  accounts: Fungua mipangilio ya akaunti\n  log_out: Toka nje\n  open_onedrive: Fungua saraka ya OneDrive\n  open_user_folder: Fungua saraka ya mtumiaji\nseelen_options:\n  open_installation_folder: Fungua folda ya mipangilio ya Seelen\n  open_log_folder: Fungua folda ya kumbukumbu ya Seelen\n  title: Saraka za Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ta.yml",
    "content": "empty_list: பொருட்கள் இல்லை\nfolders:\n  desktop: டெஸ்க்டாப்\n  documents: ஆவணங்கள்\n  downloads: பதிவிறக்கங்கள்\n  more_items: மேலும் காட்டு...\n  music: இசை\n  pictures: படங்கள்\n  recent: சமீபத்தில் பயன்படுத்தப்பட்டது\n  reduce_items: குறைவாகக் காட்டு...\n  title: பயனர் கோப்பகங்கள்\n  videos: வீடியோக்கள்\nprofile:\n  accounts: கணக்கு அமைப்புகளைத் திறக்கவும்\n  log_out: வெளியேறு\n  open_onedrive: OneDrive கோப்பகத்தைத் திறக்கவும்\n  open_user_folder: பயனர் கோப்பகத்தைத் திறக்கவும்\nseelen_options:\n  open_installation_folder: சீலன் அமைப்புகள் கோப்புறையைத் திறக்கவும்\n  open_log_folder: சீலன் பதிவு கோப்புறையைத் திறக்கவும்\n  title: சீலன் கோப்பகங்கள்\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/te.yml",
    "content": "empty_list: అంశాలు లేవు\nfolders:\n  desktop: డెస్క్‌టాప్\n  documents: పత్రాలు\n  downloads: డౌన్‌లోడ్‌లు\n  more_items: మరింత చూపించు...\n  music: సంగీతం\n  pictures: చిత్రాలు\n  recent: ఇటీవల ఉపయోగించబడింది\n  reduce_items: తక్కువ చూపు...\n  title: వినియోగదారు డైరెక్టరీలు\n  videos: వీడియోలు\nprofile:\n  accounts: ఖాతా సెట్టింగ్‌లను తెరవండి\n  log_out: లాగ్ అవుట్ చేయండి\n  open_onedrive: OneDrive డైరెక్టరీని తెరవండి\n  open_user_folder: వినియోగదారు డైరెక్టరీని తెరవండి\nseelen_options:\n  open_installation_folder: సీలెన్ సెట్టింగ్‌ల ఫోల్డర్‌ని తెరవండి\n  open_log_folder: సీలెన్ లాగ్ ఫోల్డర్‌ను తెరవండి\n  title: సీలెన్ డైరెక్టరీలు\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/tg.yml",
    "content": "empty_list: Не ашё\nfolders:\n  desktop: Мизи корӣ\n  documents: Ҳуҷҷатҳо\n  downloads: Боргириҳо\n  more_items: Намоиши бештар...\n  music: Мусиқӣ\n  pictures: Суратҳо\n  recent: Ба наздикӣ истифода бурда мешавад\n  reduce_items: Камтар нишон диҳед...\n  title: Феҳристҳои корбар\n  videos: Видеоҳо\nprofile:\n  accounts: Танзимоти ҳисобро кушоед\n  log_out: Бароед\n  open_onedrive: Феҳристи OneDrive-ро кушоед\n  open_user_folder: Феҳристи корбарро кушоед\nseelen_options:\n  open_installation_folder: Папкаи танзимоти Seelenро кушоед\n  open_log_folder: Папкаи гузориши Seelenро кушоед\n  title: Феҳристҳои дидашуда\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/th.yml",
    "content": "empty_list: ไม่มีรายการ\nfolders:\n  desktop: เดสก์ท็อป\n  documents: เอกสาร\n  downloads: ดาวน์โหลด\n  more_items: แสดงเพิ่มเติม...\n  music: ดนตรี\n  pictures: รูปภาพ\n  recent: ใช้ล่าสุด\n  reduce_items: แสดงน้อยลง...\n  title: ไดเร็กทอรีผู้ใช้\n  videos: วิดีโอ\nprofile:\n  accounts: เปิดการตั้งค่าบัญชี\n  log_out: ออกจากระบบ\n  open_onedrive: เปิดไดเรกทอรี OneDrive\n  open_user_folder: เปิดไดเร็กทอรีผู้ใช้\nseelen_options:\n  open_installation_folder: เปิดโฟลเดอร์การตั้งค่า Seelen\n  open_log_folder: เปิดโฟลเดอร์บันทึก Seelen\n  title: ไดเร็กทอรี Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/tl.yml",
    "content": "empty_list: Walang item\nfolders:\n  desktop: Desktop\n  documents: Mga dokumento\n  downloads: Mga download\n  more_items: Magpakita ng higit pa...\n  music: Musika\n  pictures: Mga larawan\n  recent: Ginamit kamakailan\n  reduce_items: Magpakita ng mas kaunti...\n  title: Mga direktoryo ng gumagamit\n  videos: Mga video\nprofile:\n  accounts: Buksan ang mga setting ng account\n  log_out: Log out\n  open_onedrive: Buksan ang direktoryo ng OneDrive\n  open_user_folder: Buksan ang direktoryo ng gumagamit\nseelen_options:\n  open_installation_folder: Buksan ang folder ng mga setting ng Seelen\n  open_log_folder: Buksan ang Seelen log folder\n  title: Mga direktoryo ng Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/tr.yml",
    "content": "empty_list: Öğe yok\nfolders:\n  desktop: Masaüstü\n  documents: Belgeler\n  downloads: İndirilenler\n  more_items: Daha fazlasını göster...\n  music: Müzik\n  pictures: Resimler\n  recent: Son kullanılan\n  reduce_items: Daha az göster...\n  title: Kullanıcı dizinleri\n  videos: Videolar\nprofile:\n  accounts: Hesap ayarlarını aç\n  log_out: Oturumu kapat\n  open_onedrive: OneDrive dizinini aç\n  open_user_folder: Kullanıcı dizinini aç\nseelen_options:\n  open_installation_folder: Seelen ayarlar klasörünü aç\n  open_log_folder: Seelen günlük klasörünü aç\n  title: Seelen dizinleri\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/uk.yml",
    "content": "empty_list: Немає елементів\nfolders:\n  desktop: Робочий стіл\n  documents: Документи\n  downloads: Завантаження\n  more_items: Показати більше...\n  music: музика\n  pictures: Картинки\n  recent: Нещодавно використаний\n  reduce_items: Показати менше...\n  title: Каталоги користувачів\n  videos: Відео\nprofile:\n  accounts: Відкрийте налаштування облікового запису\n  log_out: Вийти\n  open_onedrive: Відкрийте каталог OneDrive\n  open_user_folder: Відкрити каталог користувача\nseelen_options:\n  open_installation_folder: Відкрийте папку налаштувань Seelen\n  open_log_folder: Відкрийте папку журналу Seelen\n  title: Довідники Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/ur.yml",
    "content": "empty_list: کوئی اشیاء نہیں\nfolders:\n  desktop: ڈیسک ٹاپ\n  documents: دستاویزات\n  downloads: ڈاؤن لوڈ\n  more_items: مزید دکھائیں ...\n  music: موسیقی\n  pictures: تصاویر\n  recent: حال ہی میں استعمال ہوا\n  reduce_items: کم دکھائیں ...\n  title: صارف ڈائریکٹریز\n  videos: ویڈیوز\nprofile:\n  accounts: اکاؤنٹ کی ترتیبات کھولیں\n  log_out: لاگ آؤٹ\n  open_onedrive: اوپن ریو ڈائرکٹری کھولیں\n  open_user_folder: صارف کی ڈائرکٹری کھولیں\nseelen_options:\n  open_installation_folder: سیلن کی ترتیبات کا فولڈر کھولیں\n  open_log_folder: سیلن لاگ فولڈر کھولیں\n  title: سیلن ڈائریکٹریز\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/uz.yml",
    "content": "empty_list: Elementlar yoʻq\nfolders:\n  desktop: Ish stoli\n  documents: Hujjatlar\n  downloads: Yuklashlar\n  more_items: Batafsil ko'rsatish...\n  music: Musiqa\n  pictures: Rasmlar\n  recent: Yaqinda ishlatilgan\n  reduce_items: Kamroq ko'rsatish...\n  title: Foydalanuvchi kataloglari\n  videos: Videolar\nprofile:\n  accounts: Hisob sozlamalarini oching\n  log_out: Chiqish\n  open_onedrive: OneDrive katalogini oching\n  open_user_folder: Foydalanuvchi katalogini oching\nseelen_options:\n  open_installation_folder: Seelen sozlamalari papkasini oching\n  open_log_folder: Seelen jurnali papkasini oching\n  title: Ko'rilgan kataloglar\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/vi.yml",
    "content": "empty_list: Không có mục nào\nfolders:\n  desktop: Máy tính để bàn\n  documents: Tài liệu\n  downloads: Tải xuống\n  more_items: Hiển thị thêm...\n  music: Âm nhạc\n  pictures: Hình ảnh\n  recent: Được sử dụng gần đây\n  reduce_items: Hiển thị ít hơn...\n  title: Thư mục người dùng\n  videos: Video\nprofile:\n  accounts: Mở cài đặt tài khoản\n  log_out: Đăng xuất\n  open_onedrive: Mở thư mục OneDrive\n  open_user_folder: Mở thư mục người dùng\nseelen_options:\n  open_installation_folder: Mở thư mục cài đặt Seelen\n  open_log_folder: Mở thư mục nhật ký Seelen\n  title: Thư mục Seelen\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/yo.yml",
    "content": "empty_list: Ko si nkan\nfolders:\n  desktop: Ojú-iṣẹ\n  documents: Awọn iwe aṣẹ\n  downloads: Awọn igbasilẹ\n  more_items: Ṣafihan diẹ sii...\n  music: Orin\n  pictures: Awọn aworan\n  recent: Laipe lo\n  reduce_items: Ṣe afihan kere si...\n  title: Awọn ilana olumulo\n  videos: Awọn fidio\nprofile:\n  accounts: Ṣii awọn eto akọọlẹ\n  log_out: Jade jade\n  open_onedrive: Ṣii itọsọna OneDrive\n  open_user_folder: Ṣii itọsọna olumulo\nseelen_options:\n  open_installation_folder: Ṣii folda eto Seelen\n  open_log_folder: Ṣii folda Seelen log\n  title: Seelen awọn ilana\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/zh-CN.yml",
    "content": "empty_list: 没有商品\nfolders:\n  desktop: 桌面\n  documents: 文件\n  downloads: 下载\n  more_items: 显示更多...\n  music: 音乐\n  pictures: 图片\n  recent: 最近使用过\n  reduce_items: 显示较少...\n  title: 用户目录\n  videos: 视频\nprofile:\n  accounts: 开设账户设置\n  log_out: 退出\n  open_onedrive: 打开 OneDrive 目录\n  open_user_folder: 打开用户目录\nseelen_options:\n  open_installation_folder: 打开 Seelen 设置文件夹\n  open_log_folder: 打开 Seelen 日志文件夹\n  title: 塞伦目录\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/zh-TW.yml",
    "content": "empty_list: 沒有商品\nfolders:\n  desktop: 桌面\n  documents: 文件\n  downloads: 下載\n  more_items: 顯示更多...\n  music: 音樂\n  pictures: 圖片\n  recent: 最近使用過\n  reduce_items: 顯示較少...\n  title: 用戶目錄\n  videos: 影片\nprofile:\n  accounts: 開設賬戶設置\n  log_out: 退出\n  open_onedrive: 打開 OneDrive 目錄\n  open_user_folder: 打開用戶目錄\nseelen_options:\n  open_installation_folder: 打開 Seelen 設置文件夾\n  open_log_folder: 打開 Seelen 日誌文件夾\n  title: 塞倫目錄\n"
  },
  {
    "path": "src/ui/svelte/user-menu/i18n/translations/zu.yml",
    "content": "empty_list: Azikho izinto\nfolders:\n  desktop: Ideskithophu\n  documents: Amadokhumenti\n  downloads: Okulandiwe\n  more_items: Bonisa okwengeziwe...\n  music: Umculo\n  pictures: Izithombe\n  recent: Okusanda kusetshenziswa\n  reduce_items: Bonisa okuncane...\n  title: Izinkomba zomsebenzisi\n  videos: Amavidiyo\nprofile:\n  accounts: Vula izilungiselelo ze-akhawunti\n  log_out: Phuma\n  open_onedrive: Vula umkhombandlela we-OneDrive\n  open_user_folder: Vula uhla lwemibhalo lomsebenzisi\nseelen_options:\n  open_installation_folder: Vula ifolda yezilungiselelo ze-Seelen\n  open_log_folder: Vula ifolda yelogi ye-Seelen\n  title: Seelen Izincwajana\n"
  },
  {
    "path": "src/ui/svelte/user-menu/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst root = document.getElementById(\"root\")!;\n\nconst widget = Widget.getCurrent();\nawait widget.init({\n  autoSizeByContent: root,\n});\n\nawait loadTranslations();\n\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/user-menu/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/user-menu/state/knownFolders.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, subscribe } from \"@seelen-ui/lib\";\nimport { FolderType } from \"@seelen-ui/lib/types\";\n\nconst [desktopInit, downloadsInit, documentsInit, musicInit, picturesInit, videosInit] = await Promise.all([\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Desktop }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Downloads }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Documents }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Music }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Pictures }),\n  invoke(SeelenCommand.GetUserFolderContent, { folderType: FolderType.Videos }),\n]);\n\nlet desktop = $state(desktopInit);\nlet downloads = $state(downloadsInit);\nlet documents = $state(documentsInit);\nlet music = $state(musicInit);\nlet pictures = $state(picturesInit);\nlet videos = $state(videosInit);\n\nsubscribe(SeelenEvent.UserFolderChanged, ({ payload: { ofFolder, content } }) => {\n  switch (ofFolder) {\n    case FolderType.Desktop:\n      desktop = content;\n      break;\n    case FolderType.Downloads:\n      downloads = content;\n      break;\n    case FolderType.Documents:\n      documents = content;\n      break;\n    case FolderType.Music:\n      music = content;\n      break;\n    case FolderType.Pictures:\n      pictures = content;\n      break;\n    case FolderType.Videos:\n      videos = content;\n      break;\n  }\n});\n\nfunction pathAsItem(path: string) {\n  return {\n    path,\n    displayName: path.split(/[\\\\/]/g).pop() || \"\",\n  };\n}\n\nfunction predicate(path: string): boolean {\n  let lowercased = path.toLowerCase();\n  return !lowercased.endsWith(\".ini\") && !lowercased.endsWith(\".tmp\");\n}\n\nconst _knownFolders: Record<FolderType, FolderData> = $derived.by(() => {\n  return {\n    [FolderType.Recent]: {\n      icon: \"MdOutlineHistory\",\n      content: [],\n    },\n    [FolderType.Desktop]: {\n      icon: \"HiOutlineDesktopComputer\",\n      content: desktop.filter(predicate).map(pathAsItem),\n    },\n    [FolderType.Downloads]: {\n      icon: \"PiDownloadSimpleBold\",\n      content: downloads.filter(predicate).map(pathAsItem),\n    },\n    [FolderType.Documents]: {\n      icon: \"IoDocumentsOutline\",\n      content: documents.filter(predicate).map(pathAsItem),\n    },\n    [FolderType.Music]: {\n      icon: \"BsFileEarmarkMusic\",\n      content: music.filter(predicate).map(pathAsItem),\n    },\n    [FolderType.Pictures]: {\n      icon: \"IoImageOutline\",\n      content: pictures.filter(predicate).map(pathAsItem),\n    },\n    [FolderType.Videos]: {\n      icon: \"PiVideo\",\n      content: videos.filter(predicate).map(pathAsItem),\n    },\n  };\n});\n\nexport interface FolderData {\n  icon: string;\n  content: { path: string; displayName: string }[];\n}\n\nexport const knownFolders = {\n  get value() {\n    return _knownFolders;\n  },\n};\n"
  },
  {
    "path": "src/ui/svelte/user-menu/state/mod.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe, Widget } from \"@seelen-ui/lib\";\nimport type { FolderType, User } from \"@seelen-ui/lib/types\";\nimport { locale } from \"../i18n/index.ts\";\nimport { writable } from \"svelte/store\";\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nconst widget = Widget.getCurrent();\n\nconst settings = writable(await Settings.getAsync());\nSettings.onChange((s) => settings.set(s));\nsettings.subscribe((settings) => {\n  locale.set(settings.language || \"en\");\n});\n\nconst user = lazyRune(() => invoke(SeelenCommand.GetUser));\nsubscribe(SeelenEvent.UserChanged, user.setByPayload);\nawait user.init();\n\nlet openCategory = $state<FolderType | null>(null);\nwidget.window.onFocusChanged((e) => {\n  if (!e.payload) {\n    openCategory = null;\n  }\n});\n\nclass State {\n  get user(): User {\n    return user.value;\n  }\n\n  get openCategory(): FolderType | null {\n    return openCategory;\n  }\n  set openCategory(value: FolderType | null) {\n    openCategory = value;\n  }\n}\n\nexport const globalState = new State();\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/app.svelte",
    "content": "<script lang=\"ts\">\n  import { onMount } from \"svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n  import MonitorContainers from \"./modules/Monitor/infra.svelte\";\n\n  onMount(() => {\n    // Manually show the widget to avoid the focus call on ready,\n    // as this unfocusable widget causes the wallpaper to not be correctly\n    // positioned under the desktop.\n    Widget.self.ready({ show: false }).then(() => {\n      Widget.self.show();\n    });\n  });\n</script>\n\n<MonitorContainers />\n\n<style>\n  :global(body) {\n    width: 100vw;\n    height: 100vh;\n    overflow: hidden;\n    background: #000;\n  }\n\n  :global(#root) {\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n  }\n</style>\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/index.ts",
    "content": "import type { SupportedLanguagesCode } from \"@seelen-ui/lib\";\nimport { locale, setMessages, t } from \"libs/ui/svelte/utils\";\nimport yaml from \"js-yaml\";\n\nexport async function loadTranslations() {\n  const translations: Record<SupportedLanguagesCode, { default: string }> = {\n    en: await import(\"./translations/en.yml\"),\n    es: await import(\"./translations/es.yml\"),\n    de: await import(\"./translations/de.yml\"),\n    \"zh-CN\": await import(\"./translations/zh-CN.yml\"),\n    \"zh-TW\": await import(\"./translations/zh-TW.yml\"),\n    ko: await import(\"./translations/ko.yml\"),\n    fr: await import(\"./translations/fr.yml\"),\n    ar: await import(\"./translations/ar.yml\"),\n    ru: await import(\"./translations/ru.yml\"),\n    \"pt-BR\": await import(\"./translations/pt-BR.yml\"),\n    \"pt-PT\": await import(\"./translations/pt-PT.yml\"),\n    ja: await import(\"./translations/ja.yml\"),\n    hi: await import(\"./translations/hi.yml\"),\n    it: await import(\"./translations/it.yml\"),\n    nl: await import(\"./translations/nl.yml\"),\n    tr: await import(\"./translations/tr.yml\"),\n    pl: await import(\"./translations/pl.yml\"),\n    uk: await import(\"./translations/uk.yml\"),\n    id: await import(\"./translations/id.yml\"),\n    cs: await import(\"./translations/cs.yml\"),\n    th: await import(\"./translations/th.yml\"),\n    vi: await import(\"./translations/vi.yml\"),\n    ms: await import(\"./translations/ms.yml\"),\n    he: await import(\"./translations/he.yml\"),\n    ro: await import(\"./translations/ro.yml\"),\n    el: await import(\"./translations/el.yml\"),\n    sv: await import(\"./translations/sv.yml\"),\n    no: await import(\"./translations/no.yml\"),\n    fi: await import(\"./translations/fi.yml\"),\n    da: await import(\"./translations/da.yml\"),\n    hu: await import(\"./translations/hu.yml\"),\n    lt: await import(\"./translations/lt.yml\"),\n    bg: await import(\"./translations/bg.yml\"),\n    sk: await import(\"./translations/sk.yml\"),\n    hr: await import(\"./translations/hr.yml\"),\n    lv: await import(\"./translations/lv.yml\"),\n    et: await import(\"./translations/et.yml\"),\n    tl: await import(\"./translations/tl.yml\"),\n    ca: await import(\"./translations/ca.yml\"),\n    af: await import(\"./translations/af.yml\"),\n    bn: await import(\"./translations/bn.yml\"),\n    fa: await import(\"./translations/fa.yml\"),\n    pa: await import(\"./translations/pa.yml\"),\n    sw: await import(\"./translations/sw.yml\"),\n    ta: await import(\"./translations/ta.yml\"),\n    ur: await import(\"./translations/ur.yml\"),\n    cy: await import(\"./translations/cy.yml\"),\n    am: await import(\"./translations/am.yml\"),\n    hy: await import(\"./translations/hy.yml\"),\n    az: await import(\"./translations/az.yml\"),\n    eu: await import(\"./translations/eu.yml\"),\n    bs: await import(\"./translations/bs.yml\"),\n    ka: await import(\"./translations/ka.yml\"),\n    gu: await import(\"./translations/gu.yml\"),\n    is: await import(\"./translations/is.yml\"),\n    km: await import(\"./translations/km.yml\"),\n    ku: await import(\"./translations/ku.yml\"),\n    lo: await import(\"./translations/lo.yml\"),\n    lb: await import(\"./translations/lb.yml\"),\n    mk: await import(\"./translations/mk.yml\"),\n    mt: await import(\"./translations/mt.yml\"),\n    mn: await import(\"./translations/mn.yml\"),\n    ne: await import(\"./translations/ne.yml\"),\n    ps: await import(\"./translations/ps.yml\"),\n    sr: await import(\"./translations/sr.yml\"),\n    si: await import(\"./translations/si.yml\"),\n    so: await import(\"./translations/so.yml\"),\n    tg: await import(\"./translations/tg.yml\"),\n    te: await import(\"./translations/te.yml\"),\n    uz: await import(\"./translations/uz.yml\"),\n    yo: await import(\"./translations/yo.yml\"),\n    zu: await import(\"./translations/zu.yml\"),\n  };\n\n  let temp: Record<string, any> = {};\n  for (const [key, value] of Object.entries(translations)) {\n    temp[key] = yaml.load(value.default);\n  }\n\n  setMessages(temp);\n}\n\nexport { locale, t };\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/af.yml",
    "content": "paused_by_performance_mode: Onderbreek deur prestasiemodus\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/am.yml",
    "content": "paused_by_performance_mode: በአፈፃፀም ሁኔታ ለአፍታ ቆሟል\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ar.yml",
    "content": "paused_by_performance_mode: تم الإيقاف مؤقتًا بواسطة وضع الأداء\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/az.yml",
    "content": "paused_by_performance_mode: Performans rejimi ilə durdu\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/bg.yml",
    "content": "paused_by_performance_mode: Поставено на пауза от режим на изпълнение\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/bn.yml",
    "content": "paused_by_performance_mode: কর্মক্ষমতা মোড দ্বারা বিরতি\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/bs.yml",
    "content": "paused_by_performance_mode: Pauzirano u načinu rada\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ca.yml",
    "content": "paused_by_performance_mode: Posat en pausa pel mode de rendiment\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/cs.yml",
    "content": "paused_by_performance_mode: Pozastaveno režimem výkonu\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/cy.yml",
    "content": "paused_by_performance_mode: Wedi'i seibio yn ôl modd perfformiad\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/da.yml",
    "content": "paused_by_performance_mode: Pause af ydeevnetilstand\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/de.yml",
    "content": "paused_by_performance_mode: Angehalten durch Leistungsmodus\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/el.yml",
    "content": "paused_by_performance_mode: Σε παύση από τη λειτουργία απόδοσης\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/en.yml",
    "content": "paused_by_performance_mode: Paused by performance mode\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/es.yml",
    "content": "paused_by_performance_mode: En pausa por modo de rendimiento\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/et.yml",
    "content": "paused_by_performance_mode: Peatatud jõudlusrežiimi tõttu\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/eu.yml",
    "content": "paused_by_performance_mode: Errendimendu moduak pausatu du\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/fa.yml",
    "content": "paused_by_performance_mode: با حالت عملکرد متوقف شد\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/fi.yml",
    "content": "paused_by_performance_mode: Keskeytetty suoritustilan vuoksi\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/fr.yml",
    "content": "paused_by_performance_mode: Suspendu par le mode performance\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/gu.yml",
    "content": "paused_by_performance_mode: પ્રદર્શન મોડ દ્વારા થોભાવેલ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/he.yml",
    "content": "paused_by_performance_mode: מושהה על ידי מצב ביצועים\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/hi.yml",
    "content": "paused_by_performance_mode: प्रदर्शन मोड द्वारा रोका गया\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/hr.yml",
    "content": "paused_by_performance_mode: Pauzirano načinom izvedbe\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/hu.yml",
    "content": "paused_by_performance_mode: Teljesítménymód miatt szünetel\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/hy.yml",
    "content": "paused_by_performance_mode: Դադարեցված է կատարման ռեժիմով\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/id.yml",
    "content": "paused_by_performance_mode: Dijeda oleh mode kinerja\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/is.yml",
    "content": "paused_by_performance_mode: Gert hlé á frammistöðuham\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/it.yml",
    "content": "paused_by_performance_mode: In pausa dalla modalità performance\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ja.yml",
    "content": "paused_by_performance_mode: パフォーマンスモードにより一時停止しました\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ka.yml",
    "content": "paused_by_performance_mode: შეჩერებულია შესრულების რეჟიმით\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/km.yml",
    "content": "paused_by_performance_mode: បានផ្អាកដោយរបៀបដំណើរការ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ko.yml",
    "content": "paused_by_performance_mode: 실적 모드로 인해 일시중지됨\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ku.yml",
    "content": "paused_by_performance_mode: Ji hêla moda performansê ve hate sekinandin\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/lb.yml",
    "content": "paused_by_performance_mode: Paus duerch Leeschtung Modus\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/lo.yml",
    "content": "paused_by_performance_mode: ຢຸດຊົ່ວຄາວໂດຍຮູບແບບການປະຕິບັດ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/lt.yml",
    "content": "paused_by_performance_mode: Pristabdyta dėl našumo režimo\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/lv.yml",
    "content": "paused_by_performance_mode: Apturēts veiktspējas režīma dēļ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/mk.yml",
    "content": "paused_by_performance_mode: Паузирано од режимот на изведба\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/mn.yml",
    "content": "paused_by_performance_mode: Гүйцэтгэлийн горимоор түр зогсоов\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ms.yml",
    "content": "paused_by_performance_mode: Jeda dengan mod prestasi\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/mt.yml",
    "content": "paused_by_performance_mode: Imwaqqaf mill-modalità tal-prestazzjoni\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ne.yml",
    "content": "paused_by_performance_mode: कार्यसम्पादन मोडद्वारा रोकियो\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/nl.yml",
    "content": "paused_by_performance_mode: Gepauzeerd door prestatiemodus\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/no.yml",
    "content": "paused_by_performance_mode: Pauses av ytelsesmodus\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/pa.yml",
    "content": "paused_by_performance_mode: ਪ੍ਰਦਰਸ਼ਨ ਮੋਡ ਦੁਆਰਾ ਰੋਕਿਆ ਗਿਆ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/pl.yml",
    "content": "paused_by_performance_mode: Wstrzymane w trybie wydajności\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ps.yml",
    "content": "paused_by_performance_mode: د فعالیت حالت لخوا ځنډول شوی\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/pt-BR.yml",
    "content": "paused_by_performance_mode: Pausado pelo modo de desempenho\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/pt-PT.yml",
    "content": "paused_by_performance_mode: Pausado pelo modo de desempenho\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ro.yml",
    "content": "paused_by_performance_mode: Întrerupt de modul de performanță\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ru.yml",
    "content": "paused_by_performance_mode: Приостановлено из-за режима производительности\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/si.yml",
    "content": "paused_by_performance_mode: කාර්ය සාධන මාදිලිය මගින් විරාම කරන ලදී\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/sk.yml",
    "content": "paused_by_performance_mode: Pozastavené v režime výkonu\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/so.yml",
    "content": "paused_by_performance_mode: Joojiya qaab waxqabadka\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/sr.yml",
    "content": "paused_by_performance_mode: Паузирано у режиму перформанси\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/sv.yml",
    "content": "paused_by_performance_mode: Pausad av prestandaläge\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/sw.yml",
    "content": "paused_by_performance_mode: Imesimamishwa na hali ya utendaji\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ta.yml",
    "content": "paused_by_performance_mode: செயல்திறன் பயன்முறையால் இடைநிறுத்தப்பட்டது\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/te.yml",
    "content": "paused_by_performance_mode: పనితీరు మోడ్ ద్వారా పాజ్ చేయబడింది\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/tg.yml",
    "content": "paused_by_performance_mode: Бо ҳолати иҷрои\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/th.yml",
    "content": "paused_by_performance_mode: หยุดชั่วคราวตามโหมดประสิทธิภาพ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/tl.yml",
    "content": "paused_by_performance_mode: Naka -pause sa pamamagitan ng mode ng pagganap\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/tr.yml",
    "content": "paused_by_performance_mode: Performans modu tarafından duraklatıldı\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/uk.yml",
    "content": "paused_by_performance_mode: Призупинено в режимі продуктивності\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/ur.yml",
    "content": "paused_by_performance_mode: پرفارمنس موڈ کے ذریعہ رک گیا\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/uz.yml",
    "content": "paused_by_performance_mode: Ishlash rejimida to'xtatildi\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/vi.yml",
    "content": "paused_by_performance_mode: Bị tạm dừng theo chế độ hiệu suất\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/yo.yml",
    "content": "paused_by_performance_mode: Pause nipasẹ ipo iṣẹ\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/zh-CN.yml",
    "content": "paused_by_performance_mode: 由性能模式暂停\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/zh-TW.yml",
    "content": "paused_by_performance_mode: 由性能模式暫停\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/i18n/translations/zu.yml",
    "content": "paused_by_performance_mode: Ukumiswa okwesikhashana ngemodi yokusebenza\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/index.ts",
    "content": "import { mount } from \"svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\nimport App from \"./app.svelte\";\nimport { loadTranslations } from \"./i18n/index.ts\";\n\nimport \"@shared/styles/colors.css\";\nimport \"@shared/styles/reset.css\";\n\nawait Widget.getCurrent().init();\nWidget.self.window.setFocusable(false);\n\nawait loadTranslations();\n\nconst root = document.getElementById(\"root\")!;\nmount(App, { target: root });\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/modules/Monitor/Monitor.svelte",
    "content": "<script lang=\"ts\">\n  import type { PhysicalMonitor } from \"@seelen-ui/lib/types\";\n  import { Wallpaper } from \"libs/ui/svelte/components/Wallpaper\";\n  import { gState } from \"../../state.svelte.ts\";\n  import { t } from \"../../i18n/index.ts\";\n\n  let { monitor, extended = false }: { monitor: PhysicalMonitor; extended?: boolean } = $props();\n\n  let renderOld = $state(false);\n  let currentWasLoaded = $state(false);\n\n  const wallpaperId = $derived.by(() => {\n    const monitorData = gState.virtualDesktops.monitors[monitor.id];\n    if (!monitorData) {\n      return null;\n    }\n\n    const activeWorkspace = monitorData.workspaces.find(\n      (ws) => ws.id === monitorData.active_workspace,\n    );\n    return activeWorkspace?.wallpaper || null;\n  });\n\n  let oldId = $state<string | null>(null);\n  // svelte-ignore state_referenced_locally\n  let lastActiveRef: { value: string | null } = { value: wallpaperId };\n\n  // Watch for active wallpaper changes and trigger transition\n  $effect(() => {\n    const lastWallpaperId = lastActiveRef.value;\n    if (lastWallpaperId !== wallpaperId) {\n      lastActiveRef.value = wallpaperId;\n      oldId = lastWallpaperId;\n      renderOld = true;\n      currentWasLoaded = false;\n    }\n  });\n\n  // Unrender old wallpaper after 1s once current has loaded\n  $effect(() => {\n    if (!renderOld || !currentWasLoaded) return;\n    const timeoutId = setTimeout(() => {\n      renderOld = false;\n    }, 1000);\n    return () => clearTimeout(timeoutId);\n  });\n\n  const oldWallpaper = $derived(gState.findWallpaper(oldId));\n  const wallpaper = $derived(gState.findWallpaper(wallpaperId));\n\n  const left = $derived(extended ? \"0\" : `${monitor.rect.left / globalThis.devicePixelRatio}px`);\n  const top = $derived(extended ? \"0\" : `${monitor.rect.top / globalThis.devicePixelRatio}px`);\n  const width = $derived(\n    extended\n      ? \"100%\"\n      : `${(monitor.rect.right - monitor.rect.left) / globalThis.devicePixelRatio}px`,\n  );\n  const height = $derived(\n    extended\n      ? \"100%\"\n      : `${(monitor.rect.bottom - monitor.rect.top) / globalThis.devicePixelRatio}px`,\n  );\n</script>\n\n<div class=\"monitor\" style:position=\"fixed\" style:left style:top style:width style:height>\n  {#if renderOld}\n    {#key oldWallpaper?.id ?? \"themed\"}\n      <Wallpaper\n        definition={oldWallpaper}\n        config={oldWallpaper ? gState.settings.byWallpaper[oldWallpaper.id] : undefined}\n        paused\n        out={currentWasLoaded}\n      />\n    {/key}\n  {/if}\n\n  {#key wallpaper?.id ?? \"themed\"}\n    <Wallpaper\n      definition={wallpaper}\n      config={wallpaper ? gState.settings.byWallpaper[wallpaper.id] : undefined}\n      onLoad={() => (currentWasLoaded = true)}\n      paused={gState.paused}\n      muted={gState.muted || !monitor.isPrimary}\n      pausedMessage={gState.performanceMode !== \"Disabled\"\n        ? $t(\"paused_by_performance_mode\")\n        : undefined}\n    />\n  {/key}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/modules/Monitor/infra.svelte",
    "content": "<script lang=\"ts\">\n  import { MultimonitorBehaviour } from \"@seelen-ui/lib/types\";\n  import { gState } from \"../../state.svelte.ts\";\n  import Monitor from \"./Monitor.svelte\";\n\n  const isExtendMode = $derived(\n    gState.settings.multimonitorBehaviour === MultimonitorBehaviour.Extend,\n  );\n\n  const primaryMonitor = $derived(\n    gState.monitors.find((m) => m.isPrimary) ?? gState.monitors[0],\n  );\n</script>\n\n{#if isExtendMode}\n  {#if primaryMonitor}\n    <Monitor monitor={primaryMonitor} extended />\n  {/if}\n{:else}\n  {#each gState.relativeMonitors as monitor (monitor.id)}\n    <Monitor {monitor} />\n  {/each}\n{/if}\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/wallpaper_manager/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe } from \"@seelen-ui/lib\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\nimport { locale } from \"./i18n/index.ts\";\nimport { debounce } from \"lodash\";\n\nlet _settings = $state(await Settings.getAsync());\nSettings.onChange((s) => (_settings = s));\n\nconst settings = $derived.by(() => ({\n  ..._settings.byWidget[\"@seelen/wallpaper-manager\"],\n  byWallpaper: _settings.byWallpaper,\n  byMonitor: _settings.monitorsV3,\n}));\n\n$effect.root(() => {\n  $effect(() => {\n    locale.set(_settings.language || \"en\");\n  });\n});\n\nconst focused = lazyRune(() => invoke(SeelenCommand.GetFocusedApp));\nsubscribe(SeelenEvent.GlobalFocusChanged, focused.setByPayload);\n\nconst monitors = lazyRune(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\n\nconst virtualDesktops = lazyRune(() => invoke(SeelenCommand.StateGetVirtualDesktops));\nsubscribe(SeelenEvent.VirtualDesktopsChanged, virtualDesktops.setByPayload);\n\nconst wallpapers = lazyRune(() => invoke(SeelenCommand.StateGetWallpapers));\nsubscribe(SeelenEvent.StateWallpapersChanged, wallpapers.setByPayload);\n\nconst performanceMode = lazyRune(() => invoke(SeelenCommand.StateGetPerformanceMode));\nsubscribe(SeelenEvent.StatePerformanceModeChanged, performanceMode.setByPayload);\n\nawait Promise.all([\n  focused.init(),\n  monitors.init(),\n  virtualDesktops.init(),\n  wallpapers.init(),\n  performanceMode.init(),\n]);\n\nlet idle = $state(false);\nconst setAsIdle = debounce(() => {\n  idle = true;\n}, 1000 * 60 * 3); // 3 min\nsubscribe(SeelenEvent.GlobalMouseMove, () => {\n  if (idle) idle = false;\n  setAsIdle();\n});\n\nconst muted = $derived(![\"Progman\", \"SysListView32\"].includes(focused.value.class));\n\nconst paused = $derived(\n  idle ||\n    (focused.value.isFullscreened &&\n      !focused.value.exe?.toLowerCase().endsWith(\"explorer.exe\")) ||\n    performanceMode.value !== \"Disabled\",\n);\n\nconst desktopRect = $derived.by(() => {\n  let rect = { top: 0, left: 0, right: 0, bottom: 0 };\n  for (const monitor of monitors.value) {\n    rect.left = Math.min(rect.left, monitor.rect.left);\n    rect.top = Math.min(rect.top, monitor.rect.top);\n    rect.right = Math.max(rect.right, monitor.rect.right);\n    rect.bottom = Math.max(rect.bottom, monitor.rect.bottom);\n  }\n  return rect;\n});\n\nconst relativeMonitors = $derived.by(() => {\n  return monitors.value.map((monitor) => ({\n    ...monitor,\n    rect: {\n      ...monitor.rect,\n      left: monitor.rect.left - desktopRect.left,\n      top: monitor.rect.top - desktopRect.top,\n      right: monitor.rect.right - desktopRect.left,\n      bottom: monitor.rect.bottom - desktopRect.top,\n    },\n  }));\n});\n\nclass State {\n  get settings() {\n    return settings;\n  }\n  get monitors() {\n    return monitors.value;\n  }\n  get relativeMonitors() {\n    return relativeMonitors;\n  }\n  get virtualDesktops() {\n    return virtualDesktops.value;\n  }\n  get wallpapers() {\n    return wallpapers.value;\n  }\n  get performanceMode() {\n    return performanceMode.value;\n  }\n  get muted() {\n    return muted;\n  }\n  get paused() {\n    return paused;\n  }\n\n  findWallpaper(wallpaperId: string | null | undefined) {\n    if (!wallpaperId) return undefined;\n    return this.wallpapers.find((w) => w.id === wallpaperId);\n  }\n}\n\nexport const gState = new State();\n"
  },
  {
    "path": "src/ui/svelte/window_manager/App.svelte",
    "content": "<script lang=\"ts\">\n  import { onMount } from \"svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n  import Layout from \"./layout/infra/Layout.svelte\";\n\n  onMount(() => {\n    Widget.getCurrent().ready();\n  });\n</script>\n\n<Layout monitorId={Widget.getCurrent().decoded.monitorId!} />\n"
  },
  {
    "path": "src/ui/svelte/window_manager/index.ts",
    "content": "import { mount } from \"svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\nimport { getRootContainer } from \"libs/ui/react/utils\";\nimport { declareDocumentAsLayeredHitbox } from \"libs/ui/react/utils/layered\";\n\nimport App from \"./App.svelte\";\n\nimport \"@shared/styles/colors.css\";\nimport \"@shared/styles/reset.css\";\nimport \"./styles/global.css\";\n\nconst widget = Widget.getCurrent();\nawait widget.init();\n\nawait declareDocumentAsLayeredHitbox((e) => e.getAttribute(\"data-allow-mouse-events\") === \"true\");\n\nconst container = getRootContainer();\nmount(App, {\n  target: container,\n});\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/application.ts",
    "content": "import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\nimport type { Rect } from \"@seelen-ui/lib/types\";\nimport { toPhysicalPixels } from \"libs/ui/react/utils\";\nimport { getCurrentWindow } from \"@tauri-apps/api/window\";\nimport type { State } from \"../state.svelte\";\n\nexport async function requestPositioningOfLeaves(state: State) {\n  const { x: windowX, y: windowY } = await getCurrentWindow().outerPosition();\n\n  let elements = document.querySelectorAll(\"[data-hwnd]\");\n  let positions: Record<string, Rect> = {};\n\n  // Svelte 5 runes: direct access to state\n  const borderConfig = state.settings.border;\n  elements.forEach((element) => {\n    let hwnd = (element as HTMLDivElement).dataset.hwnd!;\n    const border = borderConfig.enabled ? borderConfig.width + borderConfig.offset : 0;\n\n    const domRect = element.getBoundingClientRect();\n    const top = windowY + toPhysicalPixels(domRect.top + border);\n    const left = windowX + toPhysicalPixels(domRect.left + border);\n\n    positions[hwnd] = {\n      top,\n      left,\n      right: left + toPhysicalPixels(domRect.width - border * 2),\n      bottom: top + toPhysicalPixels(domRect.height - border * 2),\n    };\n  });\n\n  await invoke(SeelenCommand.SetAppWindowsPositions, { positions });\n}\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/domain.ts",
    "content": "import type { WmNode } from \"@seelen-ui/lib/types\";\n\nexport enum Reservation {\n  Left = \"Left\",\n  Right = \"Right\",\n  Top = \"Top\",\n  Bottom = \"Bottom\",\n  Stack = \"Stack\",\n  Float = \"Float\",\n}\n\nexport enum Sizing {\n  Increase = \"Increase\",\n  Decrease = \"Decrease\",\n}\n\nexport type WmFallbackNode = Extract<WmNode, { type: \"Fallback\" }>;\nexport type WmHorizontalNode = Extract<WmNode, { type: \"Horizontal\" }>;\nexport type WmVerticalNode = Extract<WmNode, { type: \"Vertical\" }>;\nexport type WmLeafNode = Extract<WmNode, { type: \"Leaf\" }>;\nexport type WmStackNode = Extract<WmNode, { type: \"Stack\" }>;\n\nexport type BranchNode = WmVerticalNode | WmHorizontalNode;\nexport type Node = WmNode;\n\nexport const MAX_ALLOWED_ELEMENTS_PER_ROW = 10;\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/infra/Container.svelte",
    "content": "<script lang=\"ts\">\n  import { WmNodeKind } from \"@seelen-ui/lib/types\";\n  import type { Node } from \"../domain.ts\";\n  import { NodeUtils } from \"../../utils.ts\";\n  import Leaf from \"./containers/Leaf.svelte\";\n  import Stack from \"./containers/Stack.svelte\";\n  import Container from \"./Container.svelte\";\n\n  interface Props {\n    node: Node;\n    overlayVisible: boolean;\n  }\n\n  let { node, overlayVisible }: Props = $props();\n</script>\n\n{#if !NodeUtils.isEmpty(node)}\n  {#if node.type === WmNodeKind.Stack}\n    <Stack {node} {overlayVisible} />\n  {:else if node.type === WmNodeKind.Leaf && node.active}\n    <Leaf hwnd={node.active} growFactor={node.growFactor} />\n  {:else if node.type === WmNodeKind.Horizontal || node.type === WmNodeKind.Vertical}\n    <div\n      style:flex-grow={node.growFactor}\n      class={[\"wm-container\", `wm-${node.type.toLowerCase()}`]}\n    >\n      {#each node.children as child, idx (idx)}\n        <Container node={child} {overlayVisible}/>\n      {/each}\n    </div>\n  {/if}\n{/if}\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/infra/Layout.svelte",
    "content": "<script lang=\"ts\">\n  import { requestPositioningOfLeaves } from \"../application.ts\";\n  import { state } from \"../../state.svelte.ts\";\n  import Container from \"./Container.svelte\";\n  import { NodeUtils } from \"../../utils.ts\";\n\n  interface Props {\n    monitorId: string;\n  }\n\n  let { monitorId }: Props = $props();\n\n  let layout = $derived(state.getLayout(monitorId));\n\n  let someIsMaximizedOnBg = $derived.by(() => {\n    return state.interactables.some(\n      (app) => app.monitor === monitorId && (app.isZoomed || app.isFullscreen),\n    );\n  });\n\n  let overlayVisible = $derived.by(() => {\n    if (!layout || someIsMaximizedOnBg) {\n      return false;\n    }\n\n    if (\n      state.focusedApp.isSeelenOverlay ||\n      [\"Progman\", \"SysListView32\"].includes(state.focusedApp.class)\n    ) {\n      return true;\n    }\n\n    if (!NodeUtils.contains(layout.structure, state.focusedApp.hwnd)) {\n      return false;\n    }\n\n    return true;\n  });\n\n  // Retrigger repositioning when dependencies change\n  $effect(() => {\n    layout;\n    state.forceRepositioning;\n    if (!someIsMaximizedOnBg) {\n      requestPositioningOfLeaves(state);\n    }\n  });\n\n  // Update body opacity based on overlay visibility\n  $effect(() => {\n    document.body.style.opacity = overlayVisible ? \"1\" : \"0\";\n  });\n</script>\n\n{#if layout}\n  <Container node={layout.structure} {overlayVisible} />\n{/if}\n\n<style>\n  @import \"./index.css\";\n</style>\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/infra/containers/Leaf.svelte",
    "content": "<script lang=\"ts\">\n  import { state } from \"../../../state.svelte.ts\";\n  // import { ReservedContainer } from './reserved';\n\n  interface Props {\n    hwnd: number;\n    growFactor?: number;\n  }\n\n  let { hwnd, growFactor }: Props = $props();\n\n  // Svelte 5 runes: $derived for computed values\n  let isFocused = $derived(state.focusedApp.hwnd === hwnd);\n</script>\n\n<div\n  data-hwnd={hwnd}\n  style:flex-grow={growFactor}\n  class=\"wm-container wm-leaf\"\n  class:wm-leaf-focused={isFocused}\n  class:wm-leaf-with-borders={state.settings.border.enabled}\n>\n  <!-- {#if reservation && isFocused}\n    <ReservedContainer {reservation} />\n  {/if} -->\n</div>\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/infra/containers/Reserved.svelte",
    "content": "<script lang=\"ts\">\n  import { Reservation } from \"../../domain.ts\";\n  import { state } from \"../../../state.svelte.ts\";\n\n  interface Props {\n    reservation: Reservation;\n  }\n\n  let { reservation }: Props = $props();\n\n  // Svelte 5 runes: $derived for computed values\n  let floating = $derived(state.settings.floating);\n</script>\n\n<div\n  class={[\"wm-container\", \"wm-reserved\", `wm-reserved-${reservation.toLowerCase()}`]}\n  style:width={reservation === Reservation.Float ? floating.width : undefined}\n  style:height={reservation === Reservation.Float ? floating.height : undefined}\n></div>\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/infra/containers/Stack.svelte",
    "content": "<script lang=\"ts\">\n  import type { WmNode } from \"@seelen-ui/lib/types\";\n  import { state } from \"../../../state.svelte.ts\";\n  import Leaf from \"./Leaf.svelte\";\n  import FileIcon from \"libs/ui/svelte/components/Icon/FileIcon.svelte\";\n\n  interface Props {\n    node: WmNode;\n    overlayVisible: boolean;\n  }\n\n  let { node, overlayVisible }: Props = $props();\n</script>\n\n<div style:flex-grow={node.growFactor} class={[\"wm-container\", \"wm-stack\"]}>\n  {#if node.windows.length > 1}\n    <div class=\"wm-stack-bar\" data-allow-mouse-events={overlayVisible}>\n      {#each node.windows as winId (winId)}\n        {@const info = state.interactables.find((app) => app.hwnd === winId)}\n        <div\n          class={[\n            \"wm-stack-bar-item\",\n            {\n              \"wm-stack-bar-item-active\": winId === node.active,\n            },\n          ]}\n          data-allow-mouse-events={overlayVisible}\n        >\n          <FileIcon\n            path={info?.relaunch?.icon || info?.process?.path}\n            umid={info?.umid}\n            class=\"wm-stack-bar-item-icon\"\n            data-allow-mouse-events={overlayVisible}\n          />\n          <span class=\"wm-stack-bar-item-title\" data-allow-mouse-events={overlayVisible}>\n            {info?.title || `0x${winId.toString(16)}`}\n          </span>\n        </div>\n      {/each}\n    </div>\n  {/if}\n  {#if node.active}\n    <Leaf hwnd={node.active} />\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/window_manager/layout/infra/index.css",
    "content": "#root {\n  /* Root container always should use all the screen, independently of the grow factor */\n  > .wm-container {\n    width: 100%;\n    height: 100%;\n  }\n}\n\n.wm-container {\n  flex-grow: 1;\n  flex-shrink: 1;\n  flex-basis: 0%;\n  min-height: 0;\n  min-width: 0;\n  gap: var(--config-containers-gap);\n\n  /* if no siblings, ignore grow factor */\n  &:first-child:last-child {\n    flex-grow: 1 !important;\n  }\n\n  &.wm-leaf {\n    border-radius: 10px; /* windows 11 border radius */\n  }\n\n  &.wm-horizontal {\n    display: flex;\n  }\n\n  &.wm-vertical {\n    display: flex;\n    flex-direction: column;\n  }\n\n  &.wm-reserved {\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    border-radius: 8px;\n    opacity: 0.8;\n\n    &:not(.wm-reserved-float) {\n      transition-property: width, height, left, top;\n      transition-duration: 100ms;\n      transition-timing-function: ease-in;\n    }\n\n    &.wm-reserved-float {\n      position: fixed;\n      left: 50%;\n      top: 50%;\n      translate: -50% -50%;\n      opacity: 0;\n      animation: fromCenter 200ms ease-in forwards;\n      animation-delay: 10ms;\n    }\n\n    &.wm-reserved-left {\n      width: 50%;\n    }\n\n    &.wm-reserved-right {\n      left: 50%;\n      width: 50%;\n    }\n\n    &.wm-reserved-top {\n      height: 50%;\n    }\n\n    &.wm-reserved-bottom {\n      top: 50%;\n      height: 50%;\n    }\n  }\n}\n\n@keyframes fromCenter {\n  0% {\n    scale: 0;\n    opacity: 0;\n  }\n  100% {\n    scale: 1;\n    opacity: 0.8;\n  }\n}\n"
  },
  {
    "path": "src/ui/svelte/window_manager/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/window_manager/state.svelte.ts",
    "content": "import { invoke, SeelenCommand, SeelenEvent, Settings, subscribe } from \"@seelen-ui/lib\";\nimport type { FocusedApp, WindowManagerSettings } from \"@seelen-ui/lib/types\";\n\nimport { lazyRune } from \"libs/ui/svelte/utils/LazyRune.svelte.ts\";\n\nlet layouts = lazyRune(() => invoke(SeelenCommand.WmGetRenderTree));\nsubscribe(SeelenEvent.WMTreeChanged, layouts.setByPayload);\n\nlet interactables = lazyRune(() => invoke(SeelenCommand.GetUserAppWindows));\nsubscribe(SeelenEvent.UserAppWindowsChanged, interactables.setByPayload);\n\nlet forceRepositioning = $state(0);\nsubscribe(SeelenEvent.WMForceRetiling, () => {\n  forceRepositioning++;\n});\n\nconst [focusedAppInit, settingsInit] = await Promise.all([\n  invoke(SeelenCommand.GetFocusedApp),\n  Settings.getAsync(),\n  layouts.init(),\n  interactables.init(),\n]);\n\nlet focusedApp = $state<FocusedApp>(focusedAppInit);\nsubscribe(SeelenEvent.GlobalFocusChanged, (e) => {\n  focusedApp = e.payload;\n});\n\nlet settings = $state<WindowManagerSettings>(settingsInit.byWidget[\"@seelen/window-manager\"]);\nSettings.onChange((s) => (settings = s.byWidget[\"@seelen/window-manager\"]));\n\n// =================================================\n//                  CSS variables\n// =================================================\n\n$effect.root(() => {\n  $effect(() => {\n    const styles = document.documentElement.style;\n\n    styles.setProperty(\"--config-padding\", `${settings.workspacePadding}px`);\n    styles.setProperty(\"--config-containers-gap\", `${settings.workspaceGap}px`);\n\n    styles.setProperty(\"--config-margin-top\", `${settings.workspaceMargin.top}px`);\n    styles.setProperty(\"--config-margin-left\", `${settings.workspaceMargin.left}px`);\n    styles.setProperty(\"--config-margin-right\", `${settings.workspaceMargin.right}px`);\n    styles.setProperty(\"--config-margin-bottom\", `${settings.workspaceMargin.bottom}px`);\n\n    styles.setProperty(\"--config-border-offset\", `${settings.border.offset}px`);\n    styles.setProperty(\"--config-border-width\", `${settings.border.width}px`);\n  });\n});\n\n// =================================================\n//               Exported State Getters\n// =================================================\n\nexport type State = _State;\nclass _State {\n  getLayout(monitorId: string) {\n    return layouts.value[monitorId] || null;\n  }\n  get forceRepositioning() {\n    return forceRepositioning;\n  }\n  get interactables() {\n    return interactables.value;\n  }\n  get focusedApp() {\n    return focusedApp;\n  }\n  get settings() {\n    return settings;\n  }\n}\n\nexport const state = new _State();\n"
  },
  {
    "path": "src/ui/svelte/window_manager/styles/global.css",
    "content": "body {\n  height: 100vh;\n  width: 100vw;\n  overflow: hidden;\n  background: transparent;\n  opacity: 1;\n  transition: opacity 300ms ease;\n}\n\n#root {\n  height: 100%;\n  width: 100%;\n  padding-top: calc(var(--config-padding) + var(--config-margin-top));\n  padding-left: calc(var(--config-padding) + var(--config-margin-left));\n  padding-right: calc(var(--config-padding) + var(--config-margin-right));\n  padding-bottom: calc(var(--config-padding) + var(--config-margin-bottom));\n}\n"
  },
  {
    "path": "src/ui/svelte/window_manager/utils.ts",
    "content": "import { type WmNode, WmNodeKind } from \"@seelen-ui/lib/types\";\n\nexport class NodeUtils {\n  static isEmpty(node: WmNode): boolean {\n    switch (node.type) {\n      case WmNodeKind.Leaf:\n        return !node.active;\n      case WmNodeKind.Stack:\n        return node.windows.length === 0;\n      case WmNodeKind.Horizontal:\n      case WmNodeKind.Vertical:\n        return node.children.every(NodeUtils.isEmpty);\n    }\n  }\n\n  static contains(node: WmNode, searchingWindow: number): boolean {\n    switch (node.type) {\n      case WmNodeKind.Leaf:\n        return node.active === searchingWindow;\n      case WmNodeKind.Stack:\n        return node.windows.includes(searchingWindow);\n      case WmNodeKind.Horizontal:\n      case WmNodeKind.Vertical:\n        return node.children.some((child) => NodeUtils.contains(child, searchingWindow));\n    }\n  }\n\n  static some(node: WmNode, predicate: (window: number) => boolean): boolean {\n    switch (node.type) {\n      case WmNodeKind.Leaf:\n        return predicate(node.active!);\n      case WmNodeKind.Stack:\n        if (node.active) {\n          return predicate(node.active);\n        }\n        return false;\n      case WmNodeKind.Horizontal:\n      case WmNodeKind.Vertical:\n        return node.children.some((child) => NodeUtils.some(child, predicate));\n    }\n  }\n}\n"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/app/Monitor.svelte",
    "content": "<script lang=\"ts\">\n  import Window from \"./Window.svelte\";\n  import Workspace from \"./Workspace.svelte\";\n  import { state } from \"../state.svelte\";\n  import type { PhysicalMonitor } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand } from \"@seelen-ui/lib\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { Wallpaper } from \"libs/ui/svelte/components/Wallpaper\";\n\n  const { monitor } = $props<{ monitor: PhysicalMonitor } >();\n\n  const width = $derived((monitor.rect.right - monitor.rect.left) / monitor.scaleFactor);\n  const height = $derived((monitor.rect.bottom - monitor.rect.top) / monitor.scaleFactor);\n\n  const vdMonitor = $derived(state.workspaces.monitors[monitor.id]);\n  const activeWorkspace = $derived(\n    vdMonitor?.workspaces.find((w) => w.id === vdMonitor?.active_workspace)\n  );\n\n  const activeWallpaper = $derived(state.findWallpaper(activeWorkspace?.wallpaper));\n\n  async function createWorkspace(e: MouseEvent) {\n    e.stopPropagation();\n    try {\n      await invoke(SeelenCommand.CreateWorkspace, { monitorId: monitor.id });\n    } catch (error) {\n      console.error(\"Failed to create workspace:\", error);\n    }\n  }\n</script>\n\n<div\n  class=\"monitor\"\n  style:position=\"fixed\"\n  style:left={monitor.rect.left + \"px\"}\n  style:top={monitor.rect.top + \"px\"}\n  style:width={width + \"px\"}\n  style:height={height + \"px\"}\n  style:transform={`scale(${monitor.scaleFactor})`}\n  style:transform-origin=\"left top\"\n>\n  <div class=\"active-wallpaper\">\n    <Wallpaper definition={activeWallpaper} static muted />\n  </div>\n\n  <div class=\"workspaces\">\n    {#if vdMonitor}\n      {#each vdMonitor.workspaces as workspace, index}\n        <Workspace active={vdMonitor.active_workspace === workspace.id} {workspace} {index} />\n      {/each}\n    {/if}\n\n    <button class=\"add-workspace\" onclick={createWorkspace}>\n      <Icon iconName=\"IoAdd\" />\n    </button>\n  </div>\n\n  {#if activeWorkspace}\n    <div class=\"windows\">\n      {#each activeWorkspace.windows as hwnd}\n        <Window {hwnd} />\n      {/each}\n    </div>\n  {/if}\n</div>\n"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/app/Window.svelte",
    "content": "<script lang=\"ts\">\n  import type { UserAppWindow } from \"@seelen-ui/lib/types\";\n  import { state } from \"../state.svelte\";\n  import { convertFileSrc } from \"@tauri-apps/api/core\";\n  import { Icon, MissingIcon, FileIcon } from \"libs/ui/svelte/components/Icon\";\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n\n  interface Props {\n    hwnd: number;\n  }\n\n  let { hwnd }: Props = $props();\n\n  const windowData = $derived(state.windows.find((w: UserAppWindow) => w.hwnd === hwnd));\n  const preview = $derived(state.previews[hwnd]);\n  const aspectRatio = $derived(preview ? preview.width / preview.height : 16 / 9);\n</script>\n\n<div\n  class=\"window\"\n  role=\"button\"\n  tabindex=\"0\"\n  onclick={(e) => {\n    e.stopPropagation();\n    invoke(SeelenCommand.WegToggleWindowState, { hwnd, wasFocused: false });\n    Widget.self.hide();\n  }}\n  onkeydown={(e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      e.currentTarget?.click();\n    }\n  }}\n>\n  <div class=\"window-header\">\n    <FileIcon umid={windowData?.umid} path={windowData?.relaunch?.icon || windowData?.process?.path} />\n    <div class=\"window-title\">\n      {windowData?.title || hwnd.toString(16)}\n    </div>\n    <button\n      data-skin=\"transparent\"\n      onclick={(e) => {\n        e.stopPropagation();\n        invoke(SeelenCommand.WegCloseApp, { hwnd });\n      }}\n    >\n      <Icon iconName=\"TbX\" />\n    </button>\n  </div>\n  <div class=\"window-preview-container\" style=\"aspect-ratio: {aspectRatio}\">\n    {#if preview}\n      <img\n        class=\"window-preview\"\n        src={convertFileSrc(preview.path) + \"?v=\" + preview.hash}\n        alt=\"\"\n      />\n    {:else}\n      <MissingIcon class=\"window-no-preview\" />\n    {/if}\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/app/Workspace.svelte",
    "content": "<script lang=\"ts\">\n  import type { DesktopWorkspace } from \"@seelen-ui/lib/types\";\n  import { invoke, SeelenCommand, Widget } from \"@seelen-ui/lib\";\n  import { Icon } from \"libs/ui/svelte/components/Icon\";\n  import { Wallpaper } from \"libs/ui/svelte/components/Wallpaper\";\n  import { state as store } from \"../state.svelte\";\n\n  interface Props {\n    index: number;\n    workspace: DesktopWorkspace;\n    active: boolean;\n  }\n\n  let { workspace, index, active }: Props = $props();\n\n  const wallpaper = $derived(store.findWallpaper(workspace.wallpaper));\n\n  let workspaceName = $state(\"\");\n\n  $effect(() => {\n    workspaceName = workspace.name || \"\";\n  });\n\n  async function switchWorkspace() {\n    if (active) return;\n    // hide first to allow show the change animation to the user\n    await Widget.self.hide();\n    await invoke(SeelenCommand.SwitchWorkspace, {\n      workspaceId: workspace.id,\n    });\n  }\n\n  async function destroyWorkspace(e: MouseEvent) {\n    e.stopPropagation();\n    await invoke(SeelenCommand.DestroyWorkspace, {\n      workspaceId: workspace.id,\n    });\n  }\n\n  async function handleNameChange() {\n    const newName = workspaceName.trim();\n    if (newName === (workspace.name || \"\")) return;\n\n    try {\n      await invoke(SeelenCommand.RenameWorkspace, {\n        workspaceId: workspace.id,\n        name: newName || null,\n      });\n    } catch (error) {\n      console.error(\"Failed to rename workspace:\", error);\n      workspaceName = workspace.name || \"\";\n    }\n  }\n\n  function handleKeyDown(e: KeyboardEvent) {\n    if (e.key === \"Enter\") {\n      handleNameChange();\n    } else if (e.key === \"Escape\") {\n      workspaceName = workspace.name || \"\";\n    }\n  }\n</script>\n\n<div\n  class=\"workspace\"\n  class:workspace-active={active}\n  role=\"button\"\n  tabindex=\"0\"\n  onclick={(e) => {\n    e.stopPropagation();\n    switchWorkspace();\n  }}\n  onkeydown={(e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      switchWorkspace();\n    }\n  }}\n>\n  <div class=\"workspace-header\">\n    <input\n      type=\"text\"\n      bind:value={workspaceName}\n      data-skin=\"transparent\"\n      class=\"workspace-name-input\"\n      placeholder={`Workspace ${index + 1}`}\n      onblur={handleNameChange}\n      onkeydown={handleKeyDown}\n      onclick={(e) => e.stopPropagation()}\n    />\n    <button data-skin=\"transparent\" onclick={destroyWorkspace}>\n      <Icon iconName=\"TbX\" />\n    </button>\n  </div>\n\n  <div class=\"workspace-preview\">\n    <Wallpaper definition={wallpaper} static muted />\n  </div>\n</div>\n"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/app.svelte",
    "content": "<script lang=\"ts\">\n  import Monitor from \"./app/Monitor.svelte\";\n  import { state } from \"./state.svelte\";\n  import { Widget } from \"@seelen-ui/lib\";\n\n  $effect(() => {\n    Widget.self.ready();\n  });\n\n  function onCancel() {\n    Widget.self.hide();\n  }\n</script>\n\n<div\n  class=\"workspaces-viewer\"\n  role=\"menu\"\n  tabindex=\"-1\"\n  onclick={onCancel}\n  onkeydown={(e) => {\n    if (e.key === \"Escape\") {\n      onCancel();\n    }\n  }}\n>\n  {#each state.monitors as monitor}\n    <Monitor {monitor} />\n  {/each}\n</div>\n\n<style>\n  :global(body) {\n    overflow: hidden;\n    background: transparent;\n  }\n</style>"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/index.ts",
    "content": "import { mount } from \"svelte\";\nimport App from \"./app.svelte\";\nimport { Widget } from \"@seelen-ui/lib\";\n\nimport \"@shared/styles/reset.css\";\nimport \"@shared/styles/colors.css\";\n\nconst widget = Widget.getCurrent();\nawait widget.init();\n\n// play with zoom level to reset device pixel ratio to 1:1\nlet lastDPR = window.devicePixelRatio;\nawait widget.webview.setZoom(1 / lastDPR);\nwidget.window.onScaleChanged(() => {\n  if (window.devicePixelRatio !== lastDPR) {\n    // when zoom was set dpr changed, so in case of change this is accomulative unit\n    lastDPR = lastDPR * window.devicePixelRatio;\n    widget.webview.setZoom(1 / (lastDPR * window.devicePixelRatio));\n  }\n});\n\nwidget.window.setResizable(false);\nwidget.onTrigger(async () => {\n  if (await widget.window.isVisible()) {\n    widget.hide(true);\n  } else {\n    await widget.show();\n    await widget.focus();\n  }\n});\n\nconst root = document.getElementById(\"root\")!;\nmount(App, {\n  target: root,\n});\n"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/svelte/workspaces-viewer/state.svelte.ts",
    "content": "import { invoke, type Rect, SeelenCommand, SeelenEvent, subscribe, Widget } from \"@seelen-ui/lib\";\nimport type { Wallpaper } from \"@seelen-ui/lib/types\";\nimport { lazyRune } from \"libs/ui/svelte/utils\";\n\nlet monitors = lazyRune(() => invoke(SeelenCommand.SystemGetMonitors));\nsubscribe(SeelenEvent.SystemMonitorsChanged, monitors.setByPayload);\n\nlet wallpapers = lazyRune(() => invoke(SeelenCommand.StateGetWallpapers));\nsubscribe(SeelenEvent.StateWallpapersChanged, wallpapers.setByPayload);\n\nlet workspaces = lazyRune(() => invoke(SeelenCommand.StateGetVirtualDesktops));\nsubscribe(SeelenEvent.VirtualDesktopsChanged, workspaces.setByPayload);\n\nlet windows = lazyRune(() => invoke(SeelenCommand.GetUserAppWindows));\nsubscribe(SeelenEvent.UserAppWindowsChanged, windows.setByPayload);\n\nlet previews = lazyRune(() => invoke(SeelenCommand.GetUserAppWindowsPreviews));\nsubscribe(SeelenEvent.UserAppWindowsPreviewsChanged, previews.setByPayload);\n\nawait Promise.all([\n  monitors.init(),\n  wallpapers.init(),\n  workspaces.init(),\n  windows.init(),\n  previews.init(),\n]);\n\nlet desktopRect = $derived.by(() => {\n  let rect: Rect = { top: 0, left: 0, right: 0, bottom: 0 };\n  for (const monitor of monitors.value) {\n    rect.left = Math.min(rect.left, monitor.rect.left);\n    rect.top = Math.min(rect.top, monitor.rect.top);\n    rect.right = Math.max(rect.right, monitor.rect.right);\n    rect.bottom = Math.max(rect.bottom, monitor.rect.bottom);\n  }\n  return rect;\n});\n\nconst relativeMonitors = $derived.by(() => {\n  return monitors.value.map((monitor) => {\n    return {\n      ...monitor,\n      rect: {\n        ...monitor.rect,\n        left: monitor.rect.left - desktopRect.left,\n        top: monitor.rect.top - desktopRect.top,\n        right: monitor.rect.right - desktopRect.left,\n        bottom: monitor.rect.bottom - desktopRect.top,\n      },\n    };\n  });\n});\n\n$effect.root(() => {\n  $effect(() => {\n    Widget.self.setPosition(desktopRect);\n  });\n});\n\nclass State {\n  get monitors() {\n    return relativeMonitors;\n  }\n  get workspaces() {\n    return workspaces.value;\n  }\n  get windows() {\n    return windows.value;\n  }\n  get previews() {\n    return previews.value;\n  }\n  get wallpapers() {\n    return wallpapers.value;\n  }\n\n  findWallpaper(wallpaperId: string | undefined | null): Wallpaper | undefined {\n    if (!wallpaperId) return undefined;\n    return this.wallpapers.find((w) => w.id === wallpaperId);\n  }\n}\n\nexport const state = new State();\n"
  },
  {
    "path": "src/ui/vanilla/entry-point/ConsoleWrapper.ts",
    "content": "import * as logger from \"./_ConsoleWrapper.ts\";\nimport inspect from \"object-inspect\";\n\nfunction StringifyParams(params: any[]): string {\n  return params.reduce((acc: string, current: unknown) => {\n    if (typeof current === \"string\") {\n      return acc + \" \" + current;\n    }\n\n    if (typeof current === \"object\") {\n      let stringObj = inspect(current, { indent: 2, quoteStyle: \"double\" });\n      return acc + \" \" + stringObj;\n    }\n\n    return acc + \" \" + `${current}`;\n  }, \"\");\n}\n\nfunction forwardConsole(\n  fnName: \"log\" | \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\",\n  logger: (message: string) => Promise<void>,\n) {\n  const original = console[fnName];\n  console[fnName] = (...params: any[]) => {\n    original(...params);\n\n    let message = StringifyParams(params);\n    /// ignore Ant Design Warnings\n    if (message.includes(\"[Ant Design CSS-in-JS]\")) {\n      return;\n    }\n    logger(message);\n  };\n}\n\nforwardConsole(\"trace\", logger.trace);\nforwardConsole(\"debug\", logger.debug);\nforwardConsole(\"info\", logger.info);\nforwardConsole(\"warn\", logger.warn);\nforwardConsole(\"error\", logger.error);\n\nglobalThis.addEventListener(\"unhandledrejection\", (event) => {\n  console.error(\"Unhandled Rejection\", event.reason);\n});\n\nglobalThis.addEventListener(\n  \"error\",\n  (event) => {\n    // could be undefined on fetch errors\n    if (event.error || event.message) {\n      console.error(\"Uncaught Error\", event.error || event.message);\n    }\n  },\n  true,\n);\n"
  },
  {
    "path": "src/ui/vanilla/entry-point/_ConsoleWrapper.ts",
    "content": "// this file is a modification of https://github.com/tauri-apps/tauri-plugin-log/blob/v2/guest-js/index.ts\nimport { _invoke, WebviewInformation } from \"./_tauri.ts\";\n\nexport interface LogOptions {\n  file?: string;\n  line?: number;\n  keyValues?: Record<string, string | undefined>;\n}\n\nexport enum LogLevel {\n  /**\n   * The \"trace\" level.\n   *\n   * Designates very low priority, often extremely verbose, information.\n   */\n  Trace = 1,\n  /**\n   * The \"debug\" level.\n   *\n   * Designates lower priority information.\n   */\n  Debug,\n  /**\n   * The \"info\" level.\n   *\n   * Designates useful information.\n   */\n  Info,\n  /**\n   * The \"warn\" level.\n   *\n   * Designates hazardous situations.\n   */\n  Warn,\n  /**\n   * The \"error\" level.\n   *\n   * Designates very serious errors.\n   */\n  Error,\n}\n\nconst webviewInfo = new WebviewInformation();\nasync function log(\n  level: LogLevel,\n  message: string,\n  _options?: LogOptions,\n): Promise<void> {\n  // we use the webview label as the location, instead of call stack as the stack on the Seelen UI case\n  // will be always the same because of the console wrapper\n  const location = webviewInfo.label;\n  await _invoke(\"log_from_webview\", { level, message, location });\n}\n\n/**\n * Logs a message at the error level.\n *\n * @param message\n *\n * # Examples\n *\n * ```js\n * import { error } from '@tauri-apps/plugin-log';\n *\n * const err_info = \"No connection\";\n * const port = 22;\n *\n * error(`Error: ${err_info} on port ${port}`);\n * ```\n */\nexport async function error(\n  message: string,\n  options?: LogOptions,\n): Promise<void> {\n  await log(LogLevel.Error, message, options);\n}\n\n/**\n * Logs a message at the warn level.\n *\n * @param message\n *\n * # Examples\n *\n * ```js\n * import { warn } from '@tauri-apps/plugin-log';\n *\n * const warn_description = \"Invalid Input\";\n *\n * warn(`Warning! {warn_description}!`);\n * ```\n */\nexport async function warn(\n  message: string,\n  options?: LogOptions,\n): Promise<void> {\n  await log(LogLevel.Warn, message, options);\n}\n\n/**\n * Logs a message at the info level.\n *\n * @param message\n *\n * # Examples\n *\n * ```js\n * import { info } from '@tauri-apps/plugin-log';\n *\n * const conn_info = { port: 40, speed: 3.20 };\n *\n * info(`Connected to port {conn_info.port} at {conn_info.speed} Mb/s`);\n * ```\n */\nexport async function info(\n  message: string,\n  options?: LogOptions,\n): Promise<void> {\n  await log(LogLevel.Info, message, options);\n}\n\n/**\n * Logs a message at the debug level.\n *\n * @param message\n *\n * # Examples\n *\n * ```js\n * import { debug } from '@tauri-apps/plugin-log';\n *\n * const pos = { x: 3.234, y: -1.223 };\n *\n * debug(`New position: x: {pos.x}, y: {pos.y}`);\n * ```\n */\nexport async function debug(\n  message: string,\n  options?: LogOptions,\n): Promise<void> {\n  await log(LogLevel.Debug, message, options);\n}\n\n/**\n * Logs a message at the trace level.\n *\n * @param message\n *\n * # Examples\n *\n * ```js\n * import { trace } from '@tauri-apps/plugin-log';\n *\n * let pos = { x: 3.234, y: -1.223 };\n *\n * trace(`Position is: x: {pos.x}, y: {pos.y}`);\n * ```\n */\nexport async function trace(\n  message: string,\n  options?: LogOptions,\n): Promise<void> {\n  await log(LogLevel.Trace, message, options);\n}\n"
  },
  {
    "path": "src/ui/vanilla/entry-point/_tauri.ts",
    "content": "// this file is used to reduce bundle size on widget loader, to mantain the minimal bundle size possible\n// this file can't use dependencies\nimport type { InvokeArgs, InvokeOptions } from \"@tauri-apps/api/core\";\n\nexport class WebviewInformation {\n  _label: string | null = null;\n\n  get rawLabel() {\n    let label = window.__TAURI_INTERNALS__?.metadata?.currentWebview?.label;\n    if (!label) {\n      throw new Error(\"Missing webview label\");\n    }\n    return label;\n  }\n\n  get label() {\n    if (this._label) {\n      return this._label;\n    }\n\n    const viewLabel = window.__TAURI_INTERNALS__?.metadata?.currentWebview?.label;\n    this._label = viewLabel ? decodeUrlSafeBase64(viewLabel) : \"Unknown\";\n    return this._label;\n  }\n\n  get widgetId() {\n    const [id, _] = this.label.split(\"?\");\n    if (!id) {\n      throw new Error(\"Invalid widget id\");\n    }\n    return id;\n  }\n}\n\nfunction decodeUrlSafeBase64(base64Str: string) {\n  let standardBase64 = base64Str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n  const padLength = (4 - (standardBase64.length % 4)) % 4;\n  standardBase64 += \"=\".repeat(padLength);\n  return atob(standardBase64);\n}\n\nexport function _invoke<T>(cmd: string, args?: InvokeArgs, options?: InvokeOptions): Promise<T> {\n  return window.__TAURI_INTERNALS__!.invoke(cmd, args, options);\n}\n"
  },
  {
    "path": "src/ui/vanilla/entry-point/index.ts",
    "content": "// before tauri v2.5 this script was done as a workaround to https://github.com/tauri-apps/tauri/issues/12348\n// but was fixed on https://github.com/tauri-apps/wry/pull/1531 so now this script is used to initialize\n// the console logger to capture any error on the main script\n\nimport \"./ConsoleWrapper.ts\";\n\nimport type { FocusedApp, Widget } from \"@seelen-ui/lib/types\";\nimport { emitTo, listen } from \"@tauri-apps/api/event\";\nimport { _invoke, WebviewInformation } from \"src/ui/vanilla/entry-point/_tauri.ts\";\n\nimport { hookLocalStorage, removeDefaultWebviewActions } from \"src/ui/vanilla/entry-point/setup.ts\";\n\nconst indexJsCode = fetch(\"./index.js\").then((res) => res.text());\n\n// initialize global widget variable, needed by slu-lib\nconst info = new WebviewInformation();\nconst currentWidgetId = info.widgetId;\nconst widgetList = await _invoke<Widget[]>(\"state_get_widgets\");\nwindow.__SLU_WIDGET = widgetList.find((widget) => widget.id === currentWidgetId)!;\n\nif (!window.__SLU_WIDGET) {\n  throw new Error(`Widget definition not found for ${currentWidgetId}`);\n}\n\n// remove default browser actions, we don't need them\nremoveDefaultWebviewActions();\nhookLocalStorage(currentWidgetId);\n\n// trigger garbage collection\nsetInterval(() => {\n  window.gc?.();\n}, 5000);\n\nif (!window.__SLU_WIDGET.noMemoryLeakWorkaround) {\n  // workaround for tauri/webview2 memory leak\n  setInterval(async () => {\n    const app = await _invoke<FocusedApp>(\"get_focused_app\");\n    // avoid reload the UI while playing as this can cause fps drops\n    if (app.isFullscreened || app.exe?.endsWith(\"seelen-ui.exe\")) {\n      return;\n    }\n\n    console.trace(\"Reloading widget.\");\n    location.search = `r=${Date.now()}`; // add a query hash to force be a new page\n  }, 60_000 * 10); // every 10 minutes\n}\n\nlisten(\"internal::session_resumed\", () => {\n  console.trace(\"Reloading widget.\");\n  location.search = `r=${Date.now()}`; // add a query hash to force be a new page\n});\n\nlisten<string>(\n  \"internal::liveness-ping\",\n  () => {\n    emitTo(info.rawLabel, \"internal::liveness-pong\");\n  },\n  {\n    target: {\n      kind: \"WebviewWindow\",\n      label: info.rawLabel,\n    },\n  },\n);\n\n// load index.js\nconst script = document.createElement(\"script\");\nscript.type = \"module\";\nscript.textContent = await indexJsCode;\ndocument.head.appendChild(script);\n"
  },
  {
    "path": "src/ui/vanilla/entry-point/setup.ts",
    "content": "export function removeDefaultWebviewActions(): void {\n  globalThis.addEventListener(\"keydown\", function (event): void {\n    // Prevent refresh\n    if (event.key === \"F5\" || (event.ctrlKey && event.key === \"r\")) {\n      event.preventDefault();\n    }\n\n    // Prevent closing the window (Alt+F4 / Cmd+Q on macOS)\n    if ((event.altKey && event.key === \"F4\") || (event.metaKey && event.key === \"q\")) {\n      event.preventDefault();\n    }\n\n    // Prevent common Ctrl/Cmd shortcuts\n    if (event.ctrlKey || event.metaKey) {\n      switch (event.key) {\n        case \"n\": // New window\n        case \"t\": // New tab\n        case \"w\": // Close tab\n        case \"f\": // Find\n        case \"g\": // Find next\n        case \"p\": // print\n        case \"s\": // Save\n        case \"o\": // Open file\n        case \"j\": // downloads\n        case \"u\": // View source\n        case \"tab\": // Switch tabs\n          event.preventDefault();\n          break;\n      }\n    }\n  });\n\n  // prevent browser context menu\n  globalThis.addEventListener(\"contextmenu\", (e) => e.preventDefault(), {\n    capture: true,\n  });\n\n  // Prevent drag-and-drop (files, links, images)\n  globalThis.addEventListener(\"drop\", (e) => e.preventDefault());\n  globalThis.addEventListener(\"dragover\", (e) => e.preventDefault());\n  globalThis.addEventListener(\"dragstart\", (e) => e.preventDefault());\n}\n\nexport function applyUserExperienceImprovements(): void {\n  document.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n    if (e.defaultPrevented) return;\n    if (e.key !== \"Enter\" && e.key !== \" \") return;\n\n    const target = e.target as HTMLElement;\n    if (target.getAttribute(\"role\") !== \"button\") return;\n\n    target.dataset[\"ux-active\"] = \"true\";\n  });\n\n  document.addEventListener(\"keyup\", (e: KeyboardEvent) => {\n    if (e.defaultPrevented) return;\n    if (e.key !== \"Enter\" && e.key !== \" \") return;\n\n    const target = e.target as HTMLElement;\n    if (target.getAttribute(\"role\") !== \"button\") return;\n\n    if (target.dataset[\"ux-active\"] !== \"true\") {\n      target.dataset[\"ux-active\"] = \"false\";\n\n      if (\"click\" in target) {\n        target.click();\n      }\n    }\n  });\n}\n\n/** The purpose of this is avoid collition of keys, taking in care that all widgets share same origin */\nexport function hookLocalStorage(widgetId: string) {\n  const nativeLocalStorage = window.localStorage;\n\n  class MyLocalStorage implements Storage {\n    get length() {\n      return nativeLocalStorage.length;\n    }\n\n    setItem(key: string, value: string) {\n      nativeLocalStorage.setItem(`${widgetId}:${key}`, value);\n    }\n\n    getItem(key: string) {\n      return nativeLocalStorage.getItem(`${widgetId}:${key}`);\n    }\n\n    removeItem(key: string) {\n      nativeLocalStorage.removeItem(`${widgetId}:${key}`);\n    }\n\n    key(index: number) {\n      return nativeLocalStorage.key(index);\n    }\n\n    clear() {\n      nativeLocalStorage.clear();\n    }\n  }\n\n  Object.defineProperty(window, \"localStorage\", {\n    value: new MyLocalStorage(),\n    writable: true,\n  });\n}\n"
  },
  {
    "path": "src/ui/vanilla/integrity/index.ts",
    "content": "console.debug(\"Integrity window loaded\");\n"
  },
  {
    "path": "src/ui/vanilla/integrity/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <body>\n    This is just a test window to check integrity of webview runtime state\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/vanilla/third_party/index.ts",
    "content": "import \"@shared/styles/colors.css\";\nimport \"./reset.css\";\n\nconst { js, css, html } = window.__SLU_WIDGET;\n\nif (html) {\n  document.body.innerHTML = html;\n}\n\nif (css) {\n  const style = document.createElement(\"style\");\n  style.textContent = css;\n  document.head.appendChild(style);\n}\n\nif (js) {\n  const script = document.createElement(\"script\");\n  script.type = \"module\";\n  script.textContent = js;\n  document.head.appendChild(script);\n}\n"
  },
  {
    "path": "src/ui/vanilla/third_party/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\" />\n    <link rel=\"stylesheet\" href=\"./index.css\" />\n    <script src=\"/vanilla/entry-point/index.js\" type=\"module\"></script>\n  </head>\n  <body>\n    <!-- widget will fill this -->\n  </body>\n</html>\n"
  },
  {
    "path": "src/ui/vanilla/third_party/reset.css",
    "content": "@layer basic-widget-reset {\n  :root {\n    font-size: 100%; /* determines the size of the rem unit */\n    color-scheme: light dark;\n    interpolate-size: allow-keywords;\n  }\n\n  *,\n  *:after,\n  *:before {\n    margin: 0;\n    padding: 0;\n    border: 0;\n    outline: none;\n    box-sizing: border-box;\n    vertical-align: baseline;\n    user-select: none;\n  }\n\n  input[type=\"text\"],\n  input[type=\"password\"],\n  input[type=\"email\"],\n  input[type=\"search\"],\n  input[type=\"number\"],\n  input[type=\"tel\"],\n  input[type=\"url\"],\n  textarea {\n    user-select: text;\n\n    &::after,\n    &::before {\n      user-select: text;\n    }\n  }\n\n  body {\n    font-family: system-ui;\n  }\n\n  img,\n  image,\n  picture,\n  video,\n  iframe,\n  figure {\n    max-height: 100%;\n    max-width: 100%;\n    width: 100%;\n    display: block;\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\n      \"ESNext\",\n      \"DOM\"\n    ],\n    \"target\": \"ESNext\",\n    \"module\": \"esnext\",\n    \"verbatimModuleSyntax\": true,\n    \"moduleResolution\": \"bundler\",\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"allowImportingTsExtensions\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"preact\",\n    \"types\": [\n      \"react\",\n      \"node\"\n    ],\n    \"skipLibCheck\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@icons\": [\n        \"libs/ui/react/components/Icon/icons.ts\"\n      ],\n      \"@shared\": [\n        \"libs/ui/react/utils/index.ts\"\n      ],\n      \"@shared/*\": [\n        \"libs/widgets-shared/*\"\n      ],\n      \"react\": [\n        \"./node_modules/preact/compat/\"\n      ],\n      \"react/jsx-runtime\": [\n        \"./node_modules/preact/jsx-runtime\"\n      ],\n      \"react-dom\": [\n        \"./node_modules/preact/compat/\"\n      ],\n      \"react-dom/*\": [\n        \"./node_modules/preact/compat/*\"\n      ]\n    }\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"dist\",\n    \"libs/core/**\"\n  ]\n}\n"
  }
]