diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6fa4147..955635d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,16 +1,28 @@ -# Version updates. Alerts and security updates are repository settings and need nothing -# here; this file is only the scheduled "bump things that are behind" pull requests. +# Version updates. Alerts are a repository setting and need nothing here; this file is only +# the scheduled "bump things that are behind" pull requests. # -# Two settings below are load-bearing rather than taste, and both are explained where they -# appear: `target-branch`, because the default would aim at the release branch, and the -# `groups` blocks, because an ungrouped cargo update is expensive in a way that is not -# obvious from the diff. +# Only the workflows are covered. npm and cargo were here once and were taken out on purpose, +# so do not add them back without reading why: +# +# - Version updates only move what package.json and Cargo.toml name directly. Every alert +# the repository actually had was in a transitive crate (openssl, rustls-webpki, tar...), +# which these pull requests never touch - the grouped cargo one fixed none of the 20 open +# alerts it sat next to. +# - Each major arrives as its own pull request that fails CI until someone does the +# migration, and each cargo one rewrites Cargo.lock, which busts the Rust cache and +# compiles all 799 crates cold on three operating systems. +# - Security updates would target the alerts, but they always aim at the default branch, +# `main`, where a merge is the release decision. `target-branch` does not apply to them, +# so they stay switched off in the repository settings. +# +# Instead, dependencies are refreshed by hand on `develop` before a release (`cargo update`, +# `npm update`, then check the open alerts). Alerts read the lockfiles on `main`, so they +# close when that release is promoted. version: 2 updates: - # The workflows themselves. This is the cheapest of the three and arguably the most - # useful: it is what would have flagged actions-rs/toolchain being archived in 2023, - # which test.yml had to notice by hand. + # The workflows themselves. This is cheap and useful: it is what would have flagged + # actions-rs/toolchain being archived in 2023, which test.yml had to notice by hand. - package-ecosystem: "github-actions" directory: "/" # Dependabot aims at the default branch unless told otherwise, and the default branch @@ -28,30 +40,3 @@ updates: # ones that need reading. actions-minor-and-patch: update-types: ["minor", "patch"] - - - package-ecosystem: "npm" - directory: "/" - target-branch: "develop" - schedule: - interval: "monthly" - open-pull-requests-limit: 3 - groups: - npm-minor-and-patch: - update-types: ["minor", "patch"] - - # The Rust crate. Grouping matters most here, and the reason is the Rust cache: its key - # includes a hash of Cargo.lock, so *any* crate bump is a cache miss, and the pull request - # then compiles all 799 crates cold on three operating systems. One grouped pull request a - # month pays that once; a pull request per crate would pay it over and over. - # - # The `drag` dependency is a git dependency (crabnebula-dev/drag-rs) and Dependabot does - # not update cargo git sources, so that one stays manual. - - package-ecosystem: "cargo" - directory: "/src-tauri" - target-branch: "develop" - schedule: - interval: "monthly" - open-pull-requests-limit: 3 - groups: - cargo-minor-and-patch: - update-types: ["minor", "patch"] diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b61b995..6c711e6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,8 +28,52 @@ concurrency: cancel-in-progress: true jobs: + # The rust leg is the whole length of this workflow: six to eight minutes against about one + # for each of the other two, and caching the cargo registry was measured to save nothing. + # Almost none of that time is this repository's code. The extractor spends it resolving the + # 799 crates in Cargo.lock and reading their signatures, and the queries spend it on one + # shared pass over the database that builds - which is why all 37 finish together, and why + # dropping queries would not help. So a pull request that touches no Rust skips it. + # + # Only pull requests. The weekly schedule and a manual dispatch always analyse everything, + # which is what keeps the Security tab's baseline for main complete. A skipped PR shows a + # warning in the Code scanning check that the rust configuration was not found; that is the + # expected cost of skipping, not a failure. + rust-changes: + name: Rust changed? + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read + outputs: + analyze: ${{ steps.check.outputs.analyze }} + steps: + - name: Look for changes the rust analysis would see + id: check + env: + GH_TOKEN: ${{ github.token }} + EVENT: ${{ github.event_name }} + PR: ${{ github.event.pull_request.number }} + run: | + analyze=true + # Every doubt resolves to analysing. A failed lookup reads as "no files", which + # would otherwise mean "no Rust" and skip the one thing this workflow is slow for. + if [ "$EVENT" = pull_request ] \ + && files=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR/files" --paginate --jq '.[].filename'); then + count=$(printf '%s\n' "$files" | grep -c . || true) + # The files endpoint stops at 3000. Past that the list is incomplete. + if [ "$count" -gt 0 ] && [ "$count" -lt 3000 ] \ + && ! printf '%s\n' "$files" | grep -qE '^(src-tauri/|\.github/workflows/codeql\.yml$)'; then + analyze=false + fi + echo "$count files changed; rust analysis: $analyze" + fi + echo "analyze=$analyze" >> "$GITHUB_OUTPUT" + analyze: name: Analyze (${{ matrix.language }}) + needs: rust-changes runs-on: ubuntu-latest timeout-minutes: 30 permissions: @@ -60,17 +104,30 @@ jobs: - language: rust build-mode: none + # Decided per step rather than per job, because a job-level `if` is evaluated before the + # matrix expands and cannot see `matrix.language`. The skipped rust leg still reports, as a + # green job with a note in its summary, rather than vanishing from the PR. + env: + SKIP: ${{ matrix.language == 'rust' && needs.rust-changes.outputs.analyze != 'true' }} + steps: + - name: Note the skip + if: env.SKIP == 'true' + run: echo "No Rust changed in this pull request, so the rust analysis was skipped. The weekly scheduled run still covers it." >> "$GITHUB_STEP_SUMMARY" + - name: Checkout code + if: env.SKIP != 'true' uses: actions/checkout@v7 - name: Initialize CodeQL + if: env.SKIP != 'true' uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Analyze + if: env.SKIP != 'true' uses: github/codeql-action/analyze@v4 with: category: "/language:${{ matrix.language }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index ede6a4e..6413c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,34 @@ popover, not for the person who wrote the commit. ## [Unreleased] +## [1.8.8] - 2026-09-25 + +### Changed +- **Approving a new computer is a tick, not a retyped code.** You had to read the sixteen + characters off one screen and type them into the other before the approve button would + appear. Your eyes had already done the comparing; the typing only added a chance to + mistype it and be told the codes did not match when they did. The code is shown large + enough to read across a desk, and you confirm it matches with a checkbox +- **Long stashes fold up in the queue.** A plan an agent filed through the MCP server, or a + stash after AI enhancement, could run to several screens, and scrolling past one to reach + the next item was most of the work of using the queue. A stash that is clearly taller + than the cap now stops at a fixed height with a fade, and Show more opens it, Show less + folds it back. Short stashes look exactly as before + +### Fixed +- **A computer waiting to be let in notices when it has been.** After approving it from the + other machine, the one that was waiting went on saying it was waiting - the only way to + find out it had worked was to close the settings and open them again, which reads as the + approval having failed. It now picks the key up on its own, within a few seconds + +### Security +- **The libraries built into the app are brought up to date with their security fixes.** This + covers the framework that hosts the app's window, the updater's archive unpacking, and the + TLS and certificate checks behind every connection to the sync server. None of the fixed + problems could be reached through anything Stashpad does today; this closes them before a + future change could make one reachable. The Linux build keeps one older GTK component with + a minor warning against it, until the framework moves off it + ## [1.8.7] - 2026-09-24 ### Fixed diff --git a/TESTING.md b/TESTING.md index 1037485..c40a282 100644 --- a/TESTING.md +++ b/TESTING.md @@ -30,12 +30,6 @@ npm run test:ui # Run tests with coverage report npm run test:coverage - -# Run only unit tests -npm run test:unit - -# Run only integration tests -npm run test:integration ``` ### Backend Tests diff --git a/package-lock.json b/package-lock.json index 7a54bb1..16d17ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stashpad", - "version": "1.6.10", + "version": "1.8.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "stashpad", - "version": "1.6.10", + "version": "1.8.7", "license": "AGPL-3.0", "dependencies": { "@tailwindcss/typography": "^0.5.19", @@ -34,8 +34,8 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", "@tailwindcss/postcss": "^4.1.17", - "@tauri-apps/api": "^2.10.1", - "@tauri-apps/cli": "^2.10.0", + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/cli": "^2.11.5", "@testing-library/jest-dom": "^6.9.1", "@testing-library/svelte": "^5.3.1", "@testing-library/user-event": "^14.6.1", @@ -1547,9 +1547,9 @@ } }, "node_modules/@tauri-apps/api": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", - "integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -1557,9 +1557,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.0.tgz", - "integrity": "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.5.tgz", + "integrity": "sha512-YbeJ6tctNoo40purO4u3Eeo3RVqmsjJr0NoKQRu+4pvO4gMLzkjta7x0k96SxSLmx+rQ+8um1ThOlw+HLpgFug==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -1573,23 +1573,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.10.0", - "@tauri-apps/cli-darwin-x64": "2.10.0", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.0", - "@tauri-apps/cli-linux-arm64-gnu": "2.10.0", - "@tauri-apps/cli-linux-arm64-musl": "2.10.0", - "@tauri-apps/cli-linux-riscv64-gnu": "2.10.0", - "@tauri-apps/cli-linux-x64-gnu": "2.10.0", - "@tauri-apps/cli-linux-x64-musl": "2.10.0", - "@tauri-apps/cli-win32-arm64-msvc": "2.10.0", - "@tauri-apps/cli-win32-ia32-msvc": "2.10.0", - "@tauri-apps/cli-win32-x64-msvc": "2.10.0" + "@tauri-apps/cli-darwin-arm64": "2.11.5", + "@tauri-apps/cli-darwin-x64": "2.11.5", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.5", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.5", + "@tauri-apps/cli-linux-arm64-musl": "2.11.5", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.5", + "@tauri-apps/cli-linux-x64-gnu": "2.11.5", + "@tauri-apps/cli-linux-x64-musl": "2.11.5", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.5", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.5", + "@tauri-apps/cli-win32-x64-msvc": "2.11.5" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.0.tgz", - "integrity": "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.5.tgz", + "integrity": "sha512-tggOiVOohjIHdiElbjeBfB41s6cmfsq+ZQ0PX0fEtCdsDdZ9cCi54e5gNK7tdPbR6JbdDe7RA4kbQdyyBUUk/Q==", "cpu": [ "arm64" ], @@ -1604,9 +1604,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.0.tgz", - "integrity": "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.5.tgz", + "integrity": "sha512-SrO+KCbqvG1IvVPdlzQX4GEGZRQDISrmDY9bGcW68DLVFBCC06H+Loz6oyUZETUxsSMR6B4oDfvbQVgD4hxsqQ==", "cpu": [ "x64" ], @@ -1621,9 +1621,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.0.tgz", - "integrity": "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.5.tgz", + "integrity": "sha512-qLrOaCa8hLD5QVPQRjAgOXvhlTFKXmTliVF773GgvADYcWZSdmncnF5zBFAkdu1uQK8KfQbDlBUwqQYZqBfBRQ==", "cpu": [ "arm" ], @@ -1638,13 +1638,16 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.0.tgz", - "integrity": "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.5.tgz", + "integrity": "sha512-Tq32xpQjiQEdIrvwDq7lwHhXemn070CXWdbCvr9rdi9rsv9NFTbQ6izVSIfNE+59QaerxuwtUtZ5IxDNWaoYBw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1655,13 +1658,16 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.0.tgz", - "integrity": "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.5.tgz", + "integrity": "sha512-+OdWKj5Aq+C8HXypQ4t926V8cpVnc0ZcJIdhTXWjVAJHkaNuo8EuQR6KRZKHWIbJJgKdMPCDosvwzIFaBBqHKQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1672,13 +1678,16 @@ } }, "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.0.tgz", - "integrity": "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.5.tgz", + "integrity": "sha512-Q0V2hjpllyDW8dWQZSsITuQDfcY9BpBZuhXkMa2T5AFLZAn+FJPXQSyvnxWNVxRowdQ59Xn0QVtGm6HEHNUEvw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1689,13 +1698,16 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.0.tgz", - "integrity": "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.5.tgz", + "integrity": "sha512-Xz1s87gFjZJZMQKAhLa77YeiCJM6yVog3f9OxDgVd/KxDIoB1dA5V2xOnMM+SRkOGk9juwIBqesVAjV0kUB0DA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1706,13 +1718,16 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.0.tgz", - "integrity": "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.5.tgz", + "integrity": "sha512-6A88wqAZXZHgMaBrGiy/Bpa4GgUnftXM7uquKOeOXCuK78/XdBxEECicK/1oHgrT179XBFOaA6lQEHysjRmGig==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1723,9 +1738,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.0.tgz", - "integrity": "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.5.tgz", + "integrity": "sha512-sRGgF/ObRfLcS3PtUdyTbJtkGq3E+PNZTknvAURwMNUtHw9DU/BF6S5si43y9lS5yEO9bWJCA+GGYvnn6cphQw==", "cpu": [ "arm64" ], @@ -1740,9 +1755,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.0.tgz", - "integrity": "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.5.tgz", + "integrity": "sha512-bHOv/yxqfKpI7ioIFQi5wOfZ7jkbPzJD/UhXY52I/hrmE3qNh71cohIPyqZB5GQn3LZeJWdCUOqmIg/w6XX+Wg==", "cpu": [ "ia32" ], @@ -1757,9 +1772,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.0.tgz", - "integrity": "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.5.tgz", + "integrity": "sha512-vaaOmavqdCeS7oMib2banBZemgrC5v54tSkwoBV+bxrXQZhQZrOwE2EO2i4x+CHNtvQUM/6bSs3iymZMAsycWg==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 428873b..e86bb4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stashpad", - "version": "1.8.7", + "version": "1.8.8", "description": "The staging area for your AI context.", "author": { "name": "Nico Wiedemann", @@ -39,15 +39,13 @@ "test": "vitest run", "test:ui": "vitest --ui", "test:watch": "vitest", - "test:coverage": "vitest run --coverage", - "test:unit": "vitest run --testPathPattern=\\.test\\.", - "test:integration": "vitest run --testPathPattern=\\.integration\\." + "test:coverage": "vitest run --coverage" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", "@tailwindcss/postcss": "^4.1.17", - "@tauri-apps/api": "^2.10.1", - "@tauri-apps/cli": "^2.10.0", + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/cli": "^2.11.5", "@testing-library/jest-dom": "^6.9.1", "@testing-library/svelte": "^5.3.1", "@testing-library/user-event": "^14.6.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index d572755..d2f771b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -472,12 +472,27 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.117", "which", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -613,6 +628,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -1029,6 +1053,19 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-graphics-types 0.2.0", + "foreign-types 0.5.0", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.1.3" @@ -1118,6 +1155,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -1130,14 +1180,20 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.9" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" dependencies = [ - "quote", - "syn 2.0.117", + "ctor-proc-macro", + "dtor", ] +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + [[package]] name = "ctr" version = "0.9.2" @@ -1175,9 +1231,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ "darling_core", "darling_macro", @@ -1185,11 +1241,10 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", @@ -1199,9 +1254,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", @@ -1243,6 +1298,37 @@ dependencies = [ "zeroize", ] +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "deranged" version = "0.5.8" @@ -1277,6 +1363,27 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + [[package]] name = "devtools-core" version = "0.3.6" @@ -1371,12 +1478,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - [[package]] name = "dispatch2" version = "0.3.1" @@ -1432,6 +1533,21 @@ dependencies = [ "const-random", ] +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash 0.2.0", + "html5ever 0.38.0", + "precomputed-hash", + "selectors 0.36.1", + "tendril 0.5.1", +] + [[package]] name = "dpi" version = "0.1.2" @@ -1475,6 +1591,21 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "dunce" version = "1.0.5" @@ -1734,6 +1865,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2326,7 +2463,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -2403,10 +2540,20 @@ checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.14.1", "match_token", ] +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever 0.38.0", +] + [[package]] name = "http" version = "0.2.12" @@ -2878,6 +3025,60 @@ dependencies = [ "system-deps", ] +[[package]] +name = "jiff" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.21.1" @@ -2965,10 +3166,10 @@ version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ - "cssparser", - "html5ever", + "cssparser 0.29.6", + "html5ever 0.29.1", "indexmap 2.13.0", - "selectors", + "selectors 0.24.0", ] [[package]] @@ -3143,9 +3344,20 @@ dependencies = [ "log", "phf 0.11.3", "phf_codegen 0.11.3", - "string_cache", - "string_cache_codegen", - "tendril", + "string_cache 0.8.9", + "string_cache_codegen 0.5.4", + "tendril 0.4.3", +] + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril 0.5.1", + "web_atoms", ] [[package]] @@ -3256,9 +3468,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.17.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" dependencies = [ "crossbeam-channel", "dpi", @@ -3269,10 +3481,10 @@ dependencies = [ "objc2-core-foundation", "objc2-foundation 0.3.2", "once_cell", - "png 0.17.16", + "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3307,12 +3519,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -3559,17 +3765,10 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.11.0", "block2 0.6.2", - "libc", "objc2 0.6.4", - "objc2-cloud-kit", - "objc2-core-data 0.3.2", "objc2-core-foundation", "objc2-core-graphics", - "objc2-core-image 0.3.2", - "objc2-core-text", - "objc2-core-video", "objc2-foundation 0.3.2", - "objc2-quartz-core 0.3.2", ] [[package]] @@ -3601,7 +3800,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.11.0", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -3653,28 +3851,25 @@ dependencies = [ ] [[package]] -name = "objc2-core-text" +name = "objc2-core-location" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" dependencies = [ - "bitflags 2.11.0", "objc2 0.6.4", - "objc2-core-foundation", - "objc2-core-graphics", + "objc2-foundation 0.3.2", ] [[package]] -name = "objc2-core-video" +name = "objc2-core-text" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ "bitflags 2.11.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", - "objc2-io-surface", ] [[package]] @@ -3728,16 +3923,6 @@ dependencies = [ "objc2-core-foundation", ] -[[package]] -name = "objc2-javascript-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586" -dependencies = [ - "objc2 0.6.4", - "objc2-core-foundation", -] - [[package]] name = "objc2-metal" version = "0.2.2" @@ -3788,25 +3973,33 @@ dependencies = [ ] [[package]] -name = "objc2-security" +name = "objc2-ui-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", + "block2 0.6.2", "objc2 0.6.4", + "objc2-cloud-kit", + "objc2-core-data 0.3.2", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.2", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "objc2-user-notifications", ] [[package]] -name = "objc2-ui-kit" +name = "objc2-user-notifications" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ - "bitflags 2.11.0", "objc2 0.6.4", - "objc2-core-foundation", "objc2-foundation 0.3.2", ] @@ -3822,8 +4015,6 @@ dependencies = [ "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", - "objc2-javascript-core", - "objc2-security", ] [[package]] @@ -3852,15 +4043,14 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ "bitflags 2.11.0", "cfg-if", "foreign-types 0.3.2", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -3884,9 +4074,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -4053,10 +4243,20 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -4077,6 +4277,16 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -4094,7 +4304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared 0.10.0", - "rand 0.8.5", + "rand 0.8.8", ] [[package]] @@ -4104,7 +4314,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.8", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", ] [[package]] @@ -4123,12 +4343,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", "syn 2.0.117", @@ -4161,6 +4381,15 @@ dependencies = [ "siphasher 1.0.2", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.2", +] + [[package]] name = "pin-project" version = "1.1.11" @@ -4534,9 +4763,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4545,9 +4774,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4923,7 +5152,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand 0.8.5", + "rand 0.8.8", "rkyv", "serde", "serde_json", @@ -4935,6 +5164,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -5034,9 +5269,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -5149,7 +5384,7 @@ dependencies = [ "hkdf", "num", "once_cell", - "rand 0.8.5", + "rand 0.8.8", "serde", "sha2", "zbus 4.4.0", @@ -5198,14 +5433,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", - "cssparser", - "derive_more", + "cssparser 0.29.6", + "derive_more 0.99.20", "fxhash", "log", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.11.0", + "cssparser 0.36.0", + "derive_more 2.1.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash 2.1.3", + "servo_arc 0.4.3", "smallvec", ] @@ -5328,15 +5582,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.17.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ "base64 0.22.1", + "bs58", "chrono", "hex", "indexmap 1.9.3", "indexmap 2.13.0", + "jiff", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -5347,9 +5603,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.17.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ "darling", "proc-macro2", @@ -5402,6 +5658,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5593,7 +5858,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stashpad" -version = "1.8.7" +version = "1.8.8" dependencies = [ "active-win-pos-rs", "aes-gcm", @@ -5610,7 +5875,7 @@ dependencies = [ "keyring", "log", "mime_guess", - "rand 0.8.5", + "rand 0.8.8", "regex", "reqwest 0.12.28", "rusqlite", @@ -5663,6 +5928,18 @@ dependencies = [ "serde", ] +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + [[package]] name = "string_cache_codegen" version = "0.5.4" @@ -5675,6 +5952,18 @@ dependencies = [ "quote", ] +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5782,35 +6071,35 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.5" +version = "0.35.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" dependencies = [ "bitflags 2.11.0", "block2 0.6.2", "core-foundation 0.10.1", - "core-graphics 0.24.0", + "core-graphics 0.25.0", "crossbeam-channel", - "dispatch", + "dbus", + "dispatch2", "dlopen2", "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", "jni", - "lazy_static", "libc", "log", "ndk", - "ndk-context", "ndk-sys", "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", + "objc2-ui-kit", "once_cell", "parking_lot", + "percent-encoding", "raw-window-handle", - "scopeguard", "tao-macros", "unicode-segmentation", "url", @@ -5839,9 +6128,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -5856,9 +6145,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.10.2" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463ae8677aa6d0f063a900b9c41ecd4ac2b7ca82f0b058cc4491540e55b20129" +checksum = "6fa5bacdb9bbad5954af3d1bd6cf6ae9192cab1b2e270f4a07f904610b9e85f4" dependencies = [ "anyhow", "bytes", @@ -5909,9 +6198,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.5" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca7bd893329425df750813e95bd2b643d5369d929438da96d5bbb7cc2c918f74" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" dependencies = [ "anyhow", "cargo_toml", @@ -5925,15 +6214,14 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.12+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.5.4" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac423e5859d9f9ccdd32e3cf6a5866a15bedbf25aa6630bcb2acde9468f6ae3" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" dependencies = [ "base64 0.22.1", "brotli", @@ -5958,9 +6246,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.4" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6a1bd2861ff0c8766b1d38b32a6a410f6dc6532d4ef534c47cfb2236092f59" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -6245,9 +6533,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.10.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b885ffeac82b00f1f6fd292b6e5aabfa7435d537cef57d11e38a489956535651" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" dependencies = [ "cookie", "dpi", @@ -6270,9 +6558,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.10.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5204682391625e867d16584fedc83fc292fb998814c9f7918605c789cd876314" +checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" dependencies = [ "gtk", "http 1.4.0", @@ -6280,7 +6568,6 @@ dependencies = [ "log", "objc2 0.6.4", "objc2-app-kit 0.3.2", - "objc2-foundation 0.3.2", "once_cell", "percent-encoding", "raw-window-handle", @@ -6298,24 +6585,26 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.8.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcd169fccdff05eff2c1033210b9b94acd07a47e6fa9a3431cf09cfd4f01c87e" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" dependencies = [ "anyhow", "brotli", "cargo_metadata", "ctor", + "dom_query", "dunce", "glob", - "html5ever", + "html5ever 0.29.1", "http 1.4.0", "infer", "json-patch", "kuchikiki", "log", "memchr", - "phf 0.11.3", + "phf 0.13.1", + "plist", "proc-macro2", "quote", "regex", @@ -6369,6 +6658,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tendril" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" +dependencies = [ + "new_debug_unreachable", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -6760,7 +7058,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand 0.8.5", + "rand 0.8.8", "slab", "tokio", "tokio-util", @@ -6896,9 +7194,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.21.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" +checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" dependencies = [ "crossbeam-channel", "dirs 6.0.0", @@ -6910,10 +7208,10 @@ dependencies = [ "objc2-core-graphics", "objc2-foundation 0.3.2", "once_cell", - "png 0.17.16", + "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6934,7 +7232,7 @@ dependencies = [ "httparse", "log", "native-tls", - "rand 0.9.2", + "rand 0.9.5", "sha1", "thiserror 2.0.18", "utf-8", @@ -7326,6 +7624,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", +] + [[package]] name = "webkit2gtk" version = "2.0.2" @@ -8208,24 +8518,23 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wry" -version = "0.54.2" +version = "0.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" dependencies = [ "base64 0.22.1", "block2 0.6.2", "cookie", "crossbeam-channel", "dirs 6.0.0", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever", "http 1.4.0", "javascriptcore-rs", "jni", - "kuchikiki", "libc", "ndk", "objc2 0.6.4", @@ -8389,7 +8698,7 @@ dependencies = [ "hex", "nix", "ordered-stream", - "rand 0.8.5", + "rand 0.8.8", "serde", "serde_repr", "sha1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2fb31be..4edfa68 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stashpad" -version = "1.8.7" +version = "1.8.8" description = "The staging area for your AI context." authors = ["Nico Wiedemann "] repository = "https://github.com/EarMaster/stashpad" @@ -21,7 +21,7 @@ tauri-build = { version = "2.5.3", features = [] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" -tauri = { version = "2.9.4", features = ["protocol-asset"] } +tauri = { version = "2.11.1", features = ["protocol-asset"] } tauri-plugin-log = "2" tauri-plugin-updater = "2" tauri-plugin-process = "2" diff --git a/src-tauri/src/e2ee_enrol.rs b/src-tauri/src/e2ee_enrol.rs index 8699182..6cb04e4 100644 --- a/src-tauri/src/e2ee_enrol.rs +++ b/src-tauri/src/e2ee_enrol.rs @@ -180,8 +180,14 @@ pub async fn unlock_this_installation( // high). Dismissing that on a crypto path to keep a value a log file has // no use for is the wrong trade - the comparison happens in the interface. Ok(_fingerprint) => log::info!( - "Published this installation's key - approve it from an \ - installation that can already read your stashes" + // concat! rather than a `\` line continuation. The continuation left + // the indentation inside the string here, so the sentence reached the + // log with a run of spaces in the middle of it. concat! has no such + // ambiguity, whatever the cause was. + concat!( + "Published this installation's key - approve it from an ", + "installation that can already read your stashes", + ) ), // Not fatal: the recovery code still works, and the next start tries again. Err(e) => log::warn!("Could not publish this installation's key: {}", e), diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2b86ef8..f51f177 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "stashpad", - "version": "1.8.7", + "version": "1.8.8", "identifier": "org.stashpad", "build": { "frontendDist": "../dist", diff --git a/src/lib/components/EncryptionPanel.svelte b/src/lib/components/EncryptionPanel.svelte index 8d19eb6..771b8da 100644 --- a/src/lib/components/EncryptionPanel.svelte +++ b/src/lib/components/EncryptionPanel.svelte @@ -38,6 +38,9 @@ See the GNU Affero General Public License for more details. const adapter = new DesktopStorageAdapter(); + /// What the panel is polling for, when it is polling at all. + type Waiting = "progress" | "approval" | null; + let status = $state(null); let busy = $state(false); let error = $state(""); @@ -70,17 +73,24 @@ See the GNU Affero General Public License for more details. const passphraseMode = $derived<"unset" | "locked">( keyStore === "locked" ? "locked" : "unset", ); - let progressTimer: ReturnType | null = null; + let pollTimer: ReturnType | null = null; + let pollingFor = $state(null); - const POLL_MS = 2000; + /// The progress count is a local database query, so it can be asked for often. The + /// approval check is a request to the server, so it is not. + const PROGRESS_POLL_MS = 2000; + const APPROVAL_POLL_MS = 4000; /// Records already through, for the bar. Clamped because `total` is counted fresh and /// a record deleted mid-sweep can otherwise make this exceed `total`. const done = $derived(Math.max(0, Math.min(total, total - remaining))); const percent = $derived(total > 0 ? Math.round((done / total) * 100) : 0); - let approving = $state(null); - let approvalFingerprint = $state(""); + /// The installation whose codes the user has ticked off as matching. + /// + /// One at a time: approving is a deliberate act per machine, and a tick that survived + /// from the previous card would be the wrong kind of convenience. + let confirmedDevice = $state(null); let recoveryInput = $state(""); const pending = $derived(status?.devices.filter((d) => d.status === "pending") ?? []); @@ -94,22 +104,46 @@ See the GNU Affero General Public License for more details. onDestroy(stopPolling); - /// Poll only while there is something to watch, and stop as soon as there is not: - /// an interval left running behind a closed settings page would query the database - /// every two seconds for the life of the process. - function syncPolling(state: string | undefined) { - if (state === "migrating" && !progressTimer) { - progressTimer = setInterval(readProgress, POLL_MS); - } else if (state !== "migrating") { - stopPolling(); + /// What the panel is waiting on, if anything. + /// + /// Both of these move somewhere the panel cannot see. The sweep is carried by ordinary + /// syncs, and approval happens on another machine entirely - so nothing tells this + /// panel when either has happened and it has to look. An installation that had just + /// been let in used to go on saying it was waiting until Settings was closed and + /// reopened, which reads as the approval not having worked. + /// Waiting to be let in comes first. An installation that cannot open the key can do + /// nothing about the sweep either, so polling its progress would watch the one number + /// that cannot change for it while missing the one that can. + function waitingOn(next: E2eeStatus | null): Waiting { + if (!next || next.state === "off") return null; + if (!next.unlocked) return "approval"; + if (next.state === "migrating") return "progress"; + return null; + } + + /// Poll only while there is something to watch, and stop as soon as there is not: an + /// interval left running behind a closed settings page would keep asking for the life + /// of the process. Re-entering with the same answer leaves the existing timer alone, + /// so a poll that refreshes cannot reset its own interval. + function syncPolling(next: Waiting) { + if (next === pollingFor) return; + stopPolling(); + pollingFor = next; + if (next === "progress") { + pollTimer = setInterval(readProgress, PROGRESS_POLL_MS); + } else if (next === "approval") { + // e2ee_status opens this installation's wrap if the server is holding one, so + // asking is also the act of picking the key up the moment it is granted. + pollTimer = setInterval(() => void refresh({ quiet: true }), APPROVAL_POLL_MS); } } function stopPolling() { - if (progressTimer) { - clearInterval(progressTimer); - progressTimer = null; + if (pollTimer) { + clearInterval(pollTimer); + pollTimer = null; } + pollingFor = null; } async function readProgress() { @@ -128,13 +162,21 @@ See the GNU Affero General Public License for more details. } } - async function refresh() { + /// Read the whole panel back. + /// + /// `quiet` is for the poll, and must touch neither `busy` nor `error`: flipping `busy` + /// every few seconds would grey out the buttons under the person's cursor, and + /// clearing `error` would wipe a message they are still reading. A failed poll says + /// nothing the next one will not say again. + async function refresh(options: { quiet?: boolean } = {}) { + const quiet = options.quiet === true; // Sets `busy` itself so the retry button below can disable while it runs; `run()` // calls this too, and setting the flag twice is harmless. - busy = true; + if (!quiet) busy = true; try { - status = await adapter.e2eeStatus(); - error = ""; + const next = await adapter.e2eeStatus(); + status = next; + if (!quiet) error = ""; try { keyStore = await adapter.localKeyStatus(); } catch { @@ -143,14 +185,14 @@ See the GNU Affero General Public License for more details. } // Read the count straight away rather than waiting a poll interval, so // reopening the page mid-sweep shows the real figure instead of a zero. - if (status.state === "migrating") { + if (next.state === "migrating") { await readProgress(); } - syncPolling(status.state); + syncPolling(waitingOn(next)); } catch (e) { - error = errorText(e); + if (!quiet) error = errorText(e); } finally { - busy = false; + if (!quiet) busy = false; } } @@ -260,7 +302,7 @@ See the GNU Affero General Public License for more details. {#if error} - {:else} - - {/if} + onchange={(e) => + (confirmedDevice = e.currentTarget.checked + ? device.deviceId + : null)} + class="mt-0.5" + /> + + {$_("encryption.confirmMatch")} + {$_("encryption.confirmMatchHint")} + + + + {/each} diff --git a/src/lib/components/StashCard.svelte b/src/lib/components/StashCard.svelte index 795a929..1ab438d 100644 --- a/src/lib/components/StashCard.svelte +++ b/src/lib/components/StashCard.svelte @@ -49,6 +49,8 @@ ArrowLeftRight, X, Tags, + ChevronDown, + ChevronUp, } from "lucide-svelte"; import Editor from "./Editor.svelte"; import FilePreviewTooltip from "./FilePreviewTooltip.svelte"; @@ -157,6 +159,35 @@ showEnhanced && hasEnhancedVersion ? item.enhancedContent! : item.content, ); + // Long stashes (agent plans, AI-enhanced text) are clamped to a maximum + // height so the queue stays navigable. A stash only collapses when it is + // clearly longer than the cap, so nobody expands a card to reveal one line. + const COLLAPSED_MAX_PX = 192; + const COLLAPSE_SLACK_PX = 48; + let contentRef = $state(); + let contentHeight = $state(0); + let expanded = $state(false); + let isLong = $derived(contentHeight > COLLAPSED_MAX_PX + COLLAPSE_SLACK_PX); + let isClamped = $derived(isLong && !expanded); + + $effect(() => { + if (!contentRef) return; + const el = contentRef; + const observer = new ResizeObserver(() => { + contentHeight = el.scrollHeight; + }); + observer.observe(el); + return () => observer.disconnect(); + }); + + function toggleExpanded() { + expanded = !expanded; + // Collapsing a stash read to its end would leave the reader far below it. + if (!expanded && cardRef && cardRef.getBoundingClientRect().top < 0) { + cardRef.scrollIntoView({ block: "start" }); + } + } + let stashData = $derived(extractTagsAndColors(item.content)); let stashTags = $derived(() => stashData.tags); let stashColors = $derived(() => stashData.colors); @@ -552,15 +583,48 @@ {/if} + {#if isLong} + + {/if} {:else}
{$_("stashCard.emptyStash")} diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 0d0da4b..77e04cf 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -303,9 +303,10 @@ "lockedApproveAlternative": "Oder gib diese Installation von einer frei, die sie schon lesen kann – dann brauchst du den Code nicht.", "useRecoveryCode": "Wiederherstellungscode verwenden", "pendingTitle": "Wartet auf Freigabe", - "compareInstruction": "Prüfe, ob das mit dem übereinstimmt, was die andere Installation anzeigt. Wenn nicht, gib sie nicht frei.", - "letItIn": "Freigeben", - "approve": "Die Codes stimmen überein, freigeben", + "compareInstruction": "Öffne auf dem anderen Rechner die Verschlüsselungs-Einstellungen und vergleiche den dort angezeigten Code mit diesem.", + "confirmMatch": "Ich habe beide Codes verglichen und sie sind identisch.", + "confirmMatchHint": "Wenn sie sich unterscheiden, gib nicht frei — dann versucht möglicherweise jemand anderes hereinzukommen.", + "approve": "Diese Installation freigeben", "thisInstallation": "Diese Installation", "activeCount": "{count} Installation(en) können deine Stashes lesen." }, @@ -435,6 +436,8 @@ "stashAdded": "Stash hinzugefügt" }, "stashCard": { + "showMore": "Mehr anzeigen", + "showLess": "Weniger anzeigen", "emptyStash": "-- leer --", "copied": "Kopiert!", "attachment": "{count} Anhang ({size})", diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index b6dbe0b..0a1b2de 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -303,9 +303,10 @@ "lockedApproveAlternative": "Or approve this installation from one that can already read them, without typing the code.", "useRecoveryCode": "Use recovery code", "pendingTitle": "Waiting to be let in", - "compareInstruction": "Check this matches what the other installation shows. If it does not, do not approve it.", - "letItIn": "Let it in", - "approve": "The codes match, approve", + "compareInstruction": "Open the encryption settings on the other computer and compare the code it shows with this one.", + "confirmMatch": "I have compared both codes and they are identical.", + "confirmMatchHint": "If they differ, do not approve — someone else may be trying to get in.", + "approve": "Approve this installation", "thisInstallation": "This installation", "activeCount": "{count} installation(s) can read your stashes." }, @@ -435,6 +436,8 @@ } }, "stashCard": { + "showMore": "Show more", + "showLess": "Show less", "emptyStash": "-- empty --", "attachment": "{count} attachment ({size})", "attachments": "{count} attachments ({size})",