diff --git a/.gitignore b/.gitignore index cf334193..6563c3f8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ dist-ssr coverage playwright-report .playwright-mcp +.playwright-cli reports src-tauri/mutants.out src-tauri/mutants.out.old @@ -54,3 +55,6 @@ target # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# Local agent worktrees and runtime metadata +.claude/ diff --git a/.prettierignore b/.prettierignore index faf47222..0bdb5b16 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,3 +9,6 @@ src-tauri/mutants.out src-tauri/mutants.out.old src-tauri/target test-results + +# Handoff design package — source-of-truth, not authored by us +docs/design/handoff/ diff --git a/TEST_PLAN.md b/TEST_PLAN.md new file mode 100644 index 00000000..bab83a23 --- /dev/null +++ b/TEST_PLAN.md @@ -0,0 +1,1473 @@ +# PathKeep Behavior Safety Net Test Plan + +Date: 2026-05-26 MST + +Scope: repository-wide behavior safety-net audit for the active +`WORK-V03-PAPER-REDESIGN-A` block. The goal is mutation-resistant behavior +coverage, not numeric coverage theater. + +## Rules For This Pass + +- One module at a time. After each module: run the targeted tests, run that + module's coverage command, record coverage movement and suspected bugs, then + make a checkpoint commit before continuing. +- Product bugs are not silently fixed in this pass. If a correct behavioral + expectation fails, keep the failing test evidence and report the bug for + confirmation before changing production code. +- Every added test must assert behavior that would fail under plausible + mutations such as boundary flips, inverted guards, dropped error handling, + stale cache reuse, or changed return values. +- Coverage is a floor. Branches with no meaningful assertion remain gaps even + if line coverage is high. + +## Reproducible Baseline Commands + +```sh +bun run coverage:js +bun run coverage:rust +``` + +Actual baseline captured on 2026-05-26: + +```text +bun run coverage:js +Test Files 276 passed (276) +Tests 2023 passed (2023) +All files statements 99 | branches 98 | functions 99.51 | lines 99.5 + +bun run coverage:rust +Rust coverage verified at 100% for 35246 instrumented source lines and +1634 source functions. +``` + +Important gate drift: + +- `docs/plan/program/quality-matrix.md` says `bun run coverage:js` requires + 100% statements / branches / functions / lines. +- `vitest.config.ts` currently enforces only statements 99 / branches 98 / + functions 99 / lines 99. +- The JS baseline therefore passes the current config while failing the + documented shipping contract. Treat this as a safety-net gap, not as done. + +Rust note: `coverage:rust` uses the project verifier's semantic source-line +and function rules. It does not provide Rust branch coverage; branch confidence +must come from focused tests plus later `cargo mutants`. + +## Module Coverage And Risk Map + +Risk levels: + +- P0: data loss, privacy/security, archive mutation, large-archive performance, + desktop command contract. +- P1: user-visible workflow state, route navigation, migration/import/export, + AI/provider-gated behavior. +- P2: presentational components or fully pinned deterministic helpers. + +### Frontend JS/TS Runtime Modules + +| Module | Files | Lines | Branches | Funcs | Missing branches | Risk | Primary gap | +| ------------------------------------------- | ----: | -----: | -------: | -----: | ---------------: | ---- | ------------------------------------------------------------------------------------ | +| `src/pages/explorer` | 31 | 97.81 | 93.43 | 97.95 | 95 | P0 | Browse/search route state, infinite pages, annotations, paper view branches. | +| `src/components/explorer-paper` | 35 | 99.00 | 94.53 | 98.73 | 40 | P1 | Paper contact sheet/detail/search result render branches and empty/error states. | +| `src/app` | 15 | 99.87 | 97.50 | 100.00 | 13 | P1 | Router/shell fallback and runtime state branches. | +| `src/components/shell` | 7 | 99.50 | 94.50 | 100.00 | 12 | P1 | Navigation history, search palette, status/topbar fallbacks. | +| `src/pages/dashboard` | 10 | 100.00 | 94.48 | 100.00 | 10 | P1 | Dashboard fallback/dashboard paper branches have line coverage but weak branch pins. | +| `src/lib/explorer-preferences.ts` | 1 | 94.59 | 83.33 | 100.00 | 4 | P1 | Persistent explorer view-mode parse and localStorage failure paths. | +| `src/lib/paper-preferences.ts` | 1 | 100.00 | 80.00 | 100.00 | 4 | P1 | Appearance preference parse/localStorage fallbacks. | +| `src/components/intelligence` | 19 | 100.00 | 99.07 | 100.00 | 5 | P1 | Browsing rhythm optional/empty branches. | +| `src/components/sidebar` | 3 | 100.00 | 97.32 | 100.00 | 3 | P1 | Background status variant branches. | +| `src/pages/assistant` | 6 | 99.30 | 98.00 | 98.08 | 3 | P1 | Provider-gated assistant page fallback and action branches. | +| `src/pages/import` | 8 | 99.76 | 99.31 | 99.09 | 3 | P0 | Import workflow edge branches and failure UX. | +| `src/components/primitives` | 9 | 100.00 | 98.41 | 100.00 | 2 | P2 | Background progress display branches. | +| `src/components/heatmap` | 2 | 100.00 | 95.56 | 100.00 | 2 | P2 | Calendar/heatmap presentational boundary branches. | +| `src/pages/audit` | 6 | 99.67 | 99.71 | 98.85 | 1 | P1 | Audit page fallback function branch. | +| `src/pages/intelligence` | 34 | 100.00 | 99.89 | 100.00 | 1 | P1 | Paper intelligence panel optional branch. | +| `src/pages/settings` | 32 | 100.00 | 99.89 | 100.00 | 1 | P1 | Data migration section branch. | +| `src/lib/backend-preview-support.ts` | 1 | 96.15 | 98.39 | 100.00 | 1 | P1 | Browser-preview support failure branch. | +| `src/lib/backend-preview-shell-commands.ts` | 1 | 100.00 | 98.91 | 100.00 | 1 | P1 | Shell command fixture branch. | +| `src/lib/backend.ts` | 1 | 99.02 | 100.00 | 98.72 | 0 | P1 | Legacy/browser-preview function residual. Do not expand surface. | +| `src/main.tsx` | 1 | 100.00 | 100.00 | 100.00 | 0 | P0 | Desktop entry is covered and mutation-gated by desktop-contract slice. | +| `src/lib/backend-client` | 15 | 100.00 | 100.00 | 100.00 | 0 | P0 | IPC client surface covered; keep mutation focus on contracts. | +| `src/lib/ipc` | 4 | 100.00 | 100.00 | 100.00 | 0 | P0 | Desktop bridge covered and mutation-gated by desktop-contract slice. | +| `src/lib/core-intelligence` | 10 | 100.00 | 100.00 | 100.00 | 0 | P1 | Deterministic API/client covered; later mutation still needed. | +| `src/lib/i18n` | 29 | 100.00 | 100.00 | 100.00 | 0 | P1 | Catalog parity covered by separate i18n gate. | +| `src/components/review` | 10 | 100.00 | 100.00 | 100.00 | 0 | P1 | PME review primitives covered. | +| `src/pages/schedule` | 4 | 100.00 | 100.00 | 100.00 | 0 | P0 | UI covered; Rust scheduler remains the higher-risk owner. | +| `src/pages/security` | 4 | 100.00 | 100.00 | 100.00 | 0 | P0 | UI covered; Rust app-lock/keyring remain higher-risk owners. | +| Other single-purpose UI/helper modules | 18 | 100.00 | 100.00 | 100.00 | 0 | P2 | Keep as regression surface; deprioritize until P0/P1 gaps close. | + +Top JS branch residual files: + +| File | Branches | Missing branches | Why it matters | +| ------------------------------------------------------------- | -------: | ---------------: | ---------------------------------------------------- | +| `src/pages/explorer/index.tsx` | 90.35 | 22 | Main Browse/Search route orchestrator. | +| `src/pages/explorer/hooks/use-explorer-infinite-pages.ts` | 77.66 | 21 | Large-archive infinite scroll and prefetch behavior. | +| `src/components/explorer-paper/paper-contact-sheet.tsx` | 87.27 | 14 | Browse rendering, virtualization entry point. | +| `src/app/shell.tsx` | 83.82 | 11 | Global shell runtime/fallback behavior. | +| `src/pages/explorer/paper-view.tsx` | 86.57 | 9 | Paper Browse/Search composition. | +| `src/pages/dashboard/index.tsx` | 72.41 | 8 | Dashboard fallback branches. | +| `src/components/explorer-paper/paper-day-insights-helpers.ts` | 89.74 | 8 | Day aggregate presentation parity. | +| `src/components/explorer-paper/paper-detail-panel.tsx` | 93.33 | 7 | Notes/tags/detail interaction states. | + +### Rust Modules + +The current Rust verifier reports 100% semantic source-line and function +coverage across the full `src-tauri/**/src/*.rs` workspace. Remaining Rust +gaps are therefore behavioral, branch, concurrency, I/O, and mutation gaps. + +| Module | Risk | Primary gap to audit next | +| ----------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------ | +| `vault-core/archive` | P0 | Ingest/write rollback, dedup edge cases, history query semantics, og-image fetch I/O failures. | +| `vault-core/takeout` | P0 | Malformed/partial payloads, zip read failures, source evidence limits. | +| `browser-history-parser/{chromium,firefox,safari,takeout}` | P0 | Real-browser schema drift, missing optional tables, malformed DBs, timestamp boundaries. | +| `browser-history-fixtures` | P0 | Sidecar table fixture gaps already tracked in BACKLOG. | +| `vault-core/migration` | P0 | Partial writes, manifest tamper, wrong key, forward schema migration, rollback recoverability. | +| `vault-core/app_lock` and `vault-platform/keyring` | P0 | Auth-disabled states, host keyring failures, session/key recovery boundaries. | +| `vault-platform/scheduler` | P0 | Host command failures, permission/mismatch branches, cross-platform semantics. | +| `pathkeep-desktop/dev_ipc_bridge` and `worker_bridge` | P0 | Desktop command truth, payload validation, poisoned state, CORS/localhost boundary. | +| `vault-worker/archive_flows` | P0 | Background job cancellation, queue concurrency, og-image worker throttling/retry behavior. | +| `vault-core/intelligence*` and `vault-worker/intelligence*` | P1 | Optional AI/provider gating, stale sidecars, queue replay/cancel, deterministic fallback parity. | +| `vault-core/annotations` | P1 | URL notes/tags limits, search dimensions, FTS behavior. | +| `vault-core/visit_taxonomy` | P1 | Regional taxonomy, URL normalization, CJK/script-aware tokenization. | + +## Ordered Work Queue + +1. `[x]` `src/pages/explorer/hooks/use-explorer-infinite-pages.ts` + - Reason: P0 large-archive performance and correctness owner; 21 missing JS + branches; recent BROWSE-VIRT changes make this a mutation-priority module. + - Behavior focus: no duplicate in-flight fetches, no prefetch past pageCount, + cache-token reset, silent background prefetch failure, load guards. +2. `[x]` `src/pages/explorer/index.tsx` + - Main route orchestrator; many branches likely need integration-level route + tests instead of shallow render assertions. +3. `[x]` `src/components/explorer-paper/paper-contact-sheet.tsx` + - Virtualization/render-state branches; must assert mounted/recycled/session + behavior rather than DOM snapshots alone. +4. `[x]` `src/components/explorer-paper/paper-detail-panel.tsx` + - Notes/tags persistence UX; assert disabled/error/loading states and + mutation-prone handler behavior. +5. `[x]` `src/lib/explorer-preferences.ts` and `src/lib/paper-preferences.ts` + - Persistent local preference parse/fallback failure paths. +6. `[x]` `src/app/shell.tsx` and `src/components/shell/*` + - Route history and global shell state branches. +7. `[x]` `src/pages/dashboard/index.tsx` + - Branch coverage is low despite line coverage; assert fallback semantics. +8. `[x]` Rust import/fixture sidecar backlog blocks + - Follow existing BACKLOG order: sidecar fixture extension, minor integrity + pins, parser ordering, concurrency. +9. `[x]` Rust migration/security/scheduler fault-injection sweep + - Add tests for partial I/O, permission errors, command failures, and + concurrent state transitions before full mutation. + - `[x]` 9A migration import fail-fast refusal paths. + - `[x]` 9B security/keyring refusal and recovery paths. + - `[x]` 9C scheduler host-command failure paths. +10. `[x]` JS coverage residual restoration + - Restored `coverage:js` from the temporary 99/98/99/99 floor back to the + documented 100/100/100/100 gate without excluding active runtime code. + - `[x]` 10A Search/Browse chip and result residuals. + - `[x]` 10B Explorer route and paper-view residuals. + - `[x]` 10C Dashboard/app shell residuals. + - `[x]` 10D Hook/helper branch residuals. + - `[x]` 10E Explorer runtime hooks and paper helper residuals. + - `[x]` 10F Explorer Paper component residuals. + - `[x]` 10G Shared component, preview facade, and route residuals. +11. `[x]` Rust parser mutation hardening + - Used focused `cargo-mutants` evidence to compensate for the unavailable + stable Rust branch metric without claiming branch coverage that the tool + does not report. + - Hardened `browser-history-parser` Chromium/Firefox/Safari/Takeout + streaming, schema observation, evidence chunk, source sniffing, native + key, warning, and merge-report contracts until the parser mutation slice + reached 0 missed mutants. + +## Bug / Drift Register + +| ID | Status | Evidence | Action | +| ---------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| QA-GAP-001 | closed | JS coverage command previously passed at 99/98/99/99 while quality matrix says 100/100/100/100. Module 10G restored `vitest.config.ts` thresholds to 100/100/100/100 after `bun run coverage:js` reported All files 100/100/100/100 and lcov had no uncovered JS lines, branches, or functions. | Resolved in Module 10G; keep 100/100/100/100 as the per-commit JS runtime gate. | +| QA-GAP-002 | closed | Rust coverage verifier has no branch metric, and `cargo llvm-cov --branch --manifest-path src-tauri/Cargo.toml --workspace --all-features --lcov --output-path coverage/rust-branch.lcov.info` fails on the pinned stable Rust 1.94.1 toolchain because `-Z coverage-options=branch` is nightly-only. Before hardening, `bun run mutation:rust:parser` reported 431 mutants with 78 missed, 298 caught, and 55 unviable. After focused parser tests, the same parser slice reported 431 mutants with 376 caught, 55 unviable, and 0 missed. | Resolved by mutation-resistant parser behavior tests. Do not claim Rust branch coverage from llvm-cov; keep Rust branch confidence tied to focused boundary/error tests plus `cargo mutants` evidence under the stable toolchain. | +| QA-GAP-003 | closed | `src/pages/explorer/index.tsx` still built a paginated `PaperExplorerView` prop, but the current render grammar routes every `infiniteDisabled` condition to Search, grouped views, invalid-regex callout, or locked/uninitialized states before `PaperExplorerView` can mount. | Resolved in Module 10B by deleting the route-only paginated prop, the redundant results render guard, and the unreachable infinite-scroll fallback. Component-level pagination remains covered in `paper-view.test.tsx`. | +| QA-GAP-004 | closed | `src/components/explorer-paper/paper-contact-sheet.tsx` retained defensive guards that targeted behavior could not naturally hit: toolbar ref missing after mount and `canLoadMore` false after the sentinel exists. | Resolved in Module 10F by simplifying the redundant guards and keeping contact-sheet behavior pinned through component and virtualization assertions. | +| QA-GAP-005 | closed | `src/components/explorer-paper/paper-detail-panel.tsx` retained unreachable defensive branches: layout-flush with `pendingFlushRef` but no active timer, and `LookFurtherRow` non-interactive rendering even though the parent filters out rows without handlers. | Resolved in Module 10F by simplifying the redundant branches and keeping visible detail-panel behavior pinned through focused component assertions. | +| QA-GAP-006 | closed | Shell residual branches were defensive or redundant under current public grammar: `shellStorage()` without `window`, `handleSearchQuery()` receiving a blank query after `PKSearchPalette` already suppresses blank searches, `archiveHealthy ?? false` after a boolean expression, `domainAbbreviation().split('.')[0] ?? cleaned`, and duplicate route-history location keys inside an effect keyed by `location.key`. | Resolved in Module 10C by deleting redundant shell/palette fallbacks, pinning palette whitespace suppression, and covering the route-history duplicate-key branch through a StrictMode render. | + +## Checkpoint Log + +### Baseline + +- JS: `bun run coverage:js` passed, 2023 tests, global 99 statements / 98 + branches / 99.51 functions / 99.5 lines. +- Rust: `bun run coverage:rust` passed, verifier 100% across 35246 semantic + source lines and 1634 source functions. +- No product bugs confirmed yet; only gate drift recorded. + +### Module 1: `src/pages/explorer/hooks/use-explorer-infinite-pages.ts` + +Added 5 focused behavior tests: + +- cache-token refresh clears accumulated page 2..N state even when the query + signature is unchanged. +- a second `loadMore()` while page 2 is in-flight does not issue a duplicate + foreground request. +- unconditional background prefetch does not request `pageCount + 1`. +- background prefetch rejection stays silent when the foreground page succeeds. +- downward `+2` prefetch does not request beyond the reported `pageCount`. + +Commands: + +```sh +bunx vitest run src/pages/explorer/hooks/use-explorer-infinite-pages.test.tsx +bunx vitest run src/pages/explorer/hooks/use-explorer-infinite-pages.test.tsx --coverage --coverage.include=src/pages/explorer/hooks/use-explorer-infinite-pages.ts --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Test Files 1 passed (1) +Tests 15 passed (15) + +use-explorer-infinite-pages.ts targeted coverage: +statements 92.02 | branches 79.78 | functions 95.65 | lines 99.01 +uncovered line: 345 +``` + +Movement from repository baseline for this file: + +- statements: 91.30 -> 92.02 +- branches: 77.66 -> 79.78 +- functions: 95.65 -> 95.65 +- lines: 99.01 -> 99.01 + +Suspected bugs: none. The uncovered residual is mostly the hard +`MAX_ACCUMULATED_PAGES` cap path, which is awkward to hit without either +exporting a test seam or driving hundreds of `loadMore()` transitions. Keep it +queued for a dedicated cap-boundary test rather than adding an artificial +coverage-only assertion. + +### Module 2: `src/pages/explorer/index.tsx` + +Added 4 route-shell behavior tests and tightened the paper-surface mocks: + +- Paper filter strip apply trims filled values, deletes blank values, clears + stale `page`, removes individual chips through `updateParam`, and delegates + clear-all to the URL-state hook. +- Legacy `config.ogImage.fetchEnabled = false` is folded into `fetchMode: +'off'` before the route calls `useExplorerOgImages`. +- Search-result URLs such as Google SERPs suppress misleading og:image + hydration even when the row/cache advertises an image. +- Detail panel "All of domain" resets the URL to a domain-only Browse state, + dropping search/date/profile/pagination context. + +Commands: + +```sh +bunx vitest run src/pages/explorer/index.test.tsx +bunx vitest run src/pages/explorer/index.test.tsx --coverage --coverage.include=src/pages/explorer/index.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +bun run typecheck +``` + +Actual output: + +```text +Test Files 1 passed (1) +Tests 17 passed (17) + +index.tsx targeted coverage from this test file: +statements 86.54 | branches 79.82 | functions 65.3 | lines 86.25 +uncovered lines include 606, 659-698, 867-933 +``` + +Suspected product bugs: none confirmed. Drift recorded as `QA-GAP-003`: the +paginated `PaperExplorerView` prop branch appears unreachable under the current +route grammar, so it should be resolved as product-code cleanup or restored as +an intentional mode rather than covered with artificial tests. + +### Module 3: `src/components/explorer-paper/paper-contact-sheet.tsx` + +Added a focused behavior test file with 10 assertions over the contact-sheet +branches most likely to survive mutation: + +- Cards-mode frame numbers continue across day boundaries, so virtualization + extraction cannot reset the filmstrip counter per day. +- Sticky day headers use the measured toolbar height both with and without + `ResizeObserver`, including when filter chips wrap the toolbar. +- Invalid visit timestamps render `--:--`; malformed/unrepresentable day keys + render raw labels instead of crashing. +- Null page titles fall back to sanitized URL text in both cards and list mode. +- Target clear is safe when the caller omits `onClearTarget`. +- Infinite-scroll cap guidance, non-intersecting sentinel, and load-error alert + states render with the intended copy and side effects. + +Commands: + +```sh +bunx vitest run src/components/explorer-paper/paper-contact-sheet.behavior.test.tsx +bunx vitest run src/components/explorer-paper/paper-contact-sheet.test.tsx src/components/explorer-paper/paper-contact-sheet.behavior.test.tsx src/components/explorer-paper/paper-contact-sheet.virt.test.tsx src/components/explorer-paper/paper-contact-sheet.spike.test.tsx --coverage --coverage.include=src/components/explorer-paper/paper-contact-sheet.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Behavior test file: +Test Files 1 passed (1) +Tests 10 passed (10) + +All paper-contact-sheet focused files: +Test Files 4 passed (4) +Tests 45 passed (45) + +paper-contact-sheet.tsx targeted coverage: +statements 98.14 | branches 98.18 | functions 100 | lines 100 +uncovered lines: 279, 677 + +Full checkpoint gate: +bun run check +JS coverage: statements 99.27 | branches 98.24 | functions 99.66 | lines 99.69 +Rust coverage: 100% for 35246 instrumented source lines and 1634 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. Drift recorded as `QA-GAP-004`: the +remaining guards are defensive branches that current render grammar does not +make observable through user behavior. + +### Module 4: `src/components/explorer-paper/paper-detail-panel.tsx` + +Added 5 behavior assertions around the detail panel's persistence and navigation +surface: + +- External notes refreshes update the textarea when no local edit is pending. +- Pending local notes survive a stale backend refresh and still debounce-save the + local draft. +- Look-further rows are suppressed when route handlers are not wired, avoiding + phantom navigation labels. +- Look-further rows remain clickable when count hints are omitted. +- Favicon and og:image media use default test ids when the caller does not pass + a panel `testId`. + +Commands: + +```sh +bunx vitest run src/components/explorer-paper/paper-detail-panel.test.tsx --coverage --coverage.include=src/components/explorer-paper/paper-detail-panel.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Test Files 1 passed (1) +Tests 28 passed (28) + +paper-detail-panel.tsx targeted coverage: +statements 100 | branches 96.19 | functions 100 | lines 100 +uncovered lines: 228, 757-765 + +Full checkpoint gate: +bun run check +JS coverage: statements 99.27 | branches 98.27 | functions 99.66 | lines 99.69 +Rust coverage: 100% for 35246 instrumented source lines and 1634 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. Drift recorded as `QA-GAP-005`: the +remaining branches are defensive paths that the current public render grammar +does not expose through user behavior. + +### Module 5: `src/lib/explorer-preferences.ts` and `src/lib/paper-preferences.ts` + +Added 9 behavior assertions over persistent local preference fallbacks: + +- Explorer view mode and clock format return defaults and no-op safely when + `window` is unavailable. +- Explorer read helpers return defaults when localStorage read access throws. +- Re-persisting the current clock format skips storage writes and does not emit + a redundant live-update event. +- Paper preferences return defaults and no-op safely when `window` is + unavailable. +- Unrecognized paper preference values normalize back to the shipped appearance. +- `applyPaperPreferences` returns the supplied candidate without touching + globals when both `window` and `document` are unavailable. +- `applyPaperPreferences` still updates document attributes and dispatches the + live-update event when localStorage persistence fails. + +Commands: + +```sh +bunx vitest run src/lib/explorer-preferences.test.ts src/lib/paper-preferences.test.ts --coverage --coverage.include=src/lib/explorer-preferences.ts --coverage.include=src/lib/paper-preferences.ts --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Test Files 2 passed (2) +Tests 37 passed (37) + +explorer-preferences.ts targeted coverage: +statements 100 | branches 100 | functions 100 | lines 100 + +paper-preferences.ts targeted coverage: +statements 100 | branches 100 | functions 100 | lines 100 + +Full checkpoint gate: +bun run check +JS coverage: statements 99.34 | branches 98.35 | functions 99.66 | lines 99.71 +Rust coverage: 100% for 35246 instrumented source lines and 1634 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. + +### Module 11: Rust parser mutation hardening + +Added or strengthened parser behavior tests around the Rust branch/mutation gap +that cannot be measured by stable `cargo llvm-cov --branch`: + +- Chromium and Firefox incremental URL queries now prove that a single zero + cursor does not accidentally take the first-import fast path. +- Chromium/Firefox/Safari streaming tests now assert exact batch boundaries, + retained vs streamed evidence movement, and capability populated/total row + counts. +- Shared schema observation tests pin required/missing table rows, optional + table status, row counts, primary-key ordinals, and `NOT NULL` detection. +- Source-evidence chunk tests pin the empty contract across search, + navigation, engagement, context, and native entity families. +- Takeout source tests now pin direct localized history recognition, whitespace + normalization, non-Chrome JSON no-sniff behavior, sniff byte limits, and the + `"Browser History"` + `"time_usec"` conjunction. +- Takeout payload tests now pin native primary key fallback/source fields, + missing-time warning absence/presence, callback-abort display text, + canonical-only adapter passthrough, merge-report count/capability/evidence + aggregation, and default optional `HistoryBatchConsumer` no-op methods. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p browser-history-parser +bun run mutation:rust:quality +bun run coverage:rust +bun run check +``` + +Actual output: + +```text +Focused parser tests: +browser-history-parser: 56 passed; 0 failed + +Rust branch metric probe: +cargo llvm-cov --branch ... failed on pinned stable Rust 1.94.1 because +`-Z coverage-options=branch` is nightly-only. + +Rust mutation quality: +bun run mutation:rust:quality +browser-history-parser: 431 mutants tested in 11m; 376 caught; 55 unviable; 0 missed +vault-core ai helper filter: Found 0 mutants to test + +Rust coverage: +Rust coverage verified at 100% for 35643 instrumented source lines and +1654 source functions. + +Full checkpoint gate: +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2162 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +Rust workspace tests: browser-history-parser 56 passed; vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +JS coverage: All files statements 100%, branches 100%, functions 100%, lines 100% +Rust coverage: verified at 100% for 35643 instrumented source lines and 1654 source functions +supply-chain/platform/release checks: passed +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. Drift closed as `QA-GAP-002`; Rust +branch coverage is still not claimed from llvm-cov under the stable toolchain, +and branch confidence for this slice is now backed by focused boundary/error +tests plus zero missed parser mutants. + +### Module 8A: `WORK-IMPORT-FIXTURE-SIDECARS-A` + +Added 6 focused Rust behavior tests: + +- Chromium fixture self-validation proves generated downloads, keyword search + terms, favicon bitmap bytes, and icon mappings round-trip through the + production parser. +- Chromium `Favicons` fixture writer overwrites an existing invalid file with a + queryable companion schema and does not retain duplicate rows on rewrite. +- T6 asserts Chromium `downloads` rows land in archive `downloads` with source + id, paths, byte counts, state, MIME fields, and Unix-ms start time preserved. +- T7 asserts `keyword_search_terms` rows land in archive `search_terms` linked + to the canonical URL with term text, normalized term, profile id, and keyword + id preserved. +- T8 asserts favicon page URLs match canonical URL rows and identical synthetic + PNG payload bytes deduplicate into one `favicon_blobs` row. +- T9 asserts multiple `icon_mapping` rows for one icon create separate page URL + favicon rows while preserving the shared icon URL. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p browser-history-fixtures write_favicons_overwrites_existing_file_with_companion_schema +cargo test --manifest-path src-tauri/Cargo.toml -p browser-history-fixtures chromium --tests +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core chromium_sidecars --lib +``` + +Actual output: + +```text +browser-history-fixtures: +write_favicons_overwrites_existing_file_with_companion_schema: 1 passed +chromium_roundtrip: 4 passed + +vault-core: +Test result: ok. 4 passed; 0 failed; 651 filtered out. +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: Test Files 277 passed (277), Tests 2076 passed (2076) +JS coverage: statements 99.37 | branches 98.59 | functions 99.66 | lines 99.72 +Rust coverage: 100% for 35596 instrumented source lines and 1643 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. + +### Module 8B: `WORK-IMPORT-TEST-MINOR-A` + +Added 5 focused Rust behavior tests: + +- E10 asserts Chromium URL `visit_count` and `typed_count` values round-trip for + both zero-count typed URLs and nonzero visited URLs. +- E11 asserts a dangling Chromium `from_visit` reference is preserved verbatim + instead of being rewritten to NULL or 0. +- E12 asserts Chromium `visits.visit_duration` lands unchanged in the current + archive `visits.visit_duration_ms` column. +- E13 asserts Safari `history_visits.synthesized` persists to the cold + source-evidence DB as `safari.synthesized` with source ids and `source_field` + intact. +- E14 asserts Firefox `moz_historyvisits.visit_type` values land in + `visits.transition_type` without Chromium normalization. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core e1 --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core --lib +``` + +Actual output: + +```text +e1 filter: 6 passed; 0 failed; 654 filtered out (existing E1 + new E10-E14) +vault-core lib: 660 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: Test Files 277 passed (277), Tests 2076 passed (2076) +JS coverage: statements 99.38 | branches 98.61 | functions 99.66 | lines 99.72 +Rust coverage: 100% for 35805 instrumented source lines and 1648 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. + +### Module 8C: `WORK-IMPORT-TEST-PARSER-ORDERING-A` + +Added 1 focused Rust behavior test: + +- `chunk_consumer_skips_visits_when_url_batch_has_not_populated_the_map` asserts + the current `ArchiveChunkConsumer::visits` contract when a parser emits a + visit before its URL batch: the visit is skipped silently, no canonical visit + row is inserted, skipped progress increments, imported/duplicate progress + stays at zero, and `last_visit_id` is not advanced. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core chunk_consumer_skips_visits_when_url_batch_has_not_populated_the_map --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core --lib +``` + +Actual output: + +```text +parser-ordering targeted: 1 passed; 0 failed; 660 filtered out +vault-core lib: 661 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: Test Files 277 passed (277), Tests 2076 passed (2076) +JS coverage: statements 99.37 | branches 98.59 | functions 99.66 | lines 99.72 +Rust coverage: 100% for 35835 instrumented source lines and 1649 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Gate note: the first full `bun run check` attempt hit a non-reproducible +existing `settings-shell-b` navigation timing failure under `coverage:js`. +The targeted test passed without and with coverage, standalone `coverage:js` +passed, and the subsequent full `bun run check` passed. + +Suspected product bugs: none confirmed. + +### Module 8D: `WORK-IMPORT-TEST-CONCURRENCY-A` + +Added 1 focused Rust behavior test: + +- `same_profile_writer_waits_for_committed_watermark` asserts same-profile + concurrent archive writers are serialized at the SQLite transaction boundary: + the second writer cannot read `profile_watermarks` while the first writer's + transaction is uncommitted, and it observes the committed cursor after the + first writer commits. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core same_profile_writer_waits_for_committed_watermark --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core --lib +``` + +Actual output: + +```text +concurrency targeted: 1 passed; 0 failed; 661 filtered out +vault-core lib: 662 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: 277 files passed; 2076 tests passed +JS desktop contract: 5 files passed; 26 tests passed; coverage 100% statements/branches/functions/lines +JS coverage: statements 99.38%; branches 98.61%; functions 99.66%; lines 99.72% +Rust workspace tests: vault-core 662 passed in base; vault-core 663 passed under coverage cfg +Rust coverage: 100% for 35835 instrumented source lines and 1649 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 score; 64 mutants; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. The audit found no separate app-level +ingest queue; the current same-profile guarantee is SQLite writer-lock +serialization after `upsert_source_profile`, documented in +`import-dedup-audit.md` §4.1. + +### Module 8E: `WORK-MAINT-IMPORT-EDGE-CASES-SPLIT-A` + +Maintained the import edge-case scenario safety net while reducing the oversized +owner file: + +- `dedup_scenarios_edge_cases.rs` now holds only shared fixture/env helpers and + child module declarations. +- Test bodies moved into focused owners: + `chromium_contracts`, `empty_and_resilience`, `time_and_nullable`, + `unicode_and_flags`, and `minor_data_integrity`. +- All 19 existing edge-case test names and assertions were preserved. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core archive::ingest::dedup_scenarios_edge_cases --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core --lib +``` + +Actual output: + +```text +edge-case targeted: 19 passed; 0 failed; 643 filtered out +vault-core lib: 662 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: 277 files passed; 2076 tests passed +JS desktop contract: 5 files passed; 26 tests passed; coverage 100% statements/branches/functions/lines +JS coverage: statements 99.37%; branches 98.59%; functions 99.66%; lines 99.72% +Rust workspace tests: vault-core 662 passed in base; vault-core 663 passed under coverage cfg +Rust coverage: 100% for 35835 instrumented source lines and 1660 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 score; 64 mutants; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. This was a maintainability-only split; +audit §4 / §6 links now point at the focused owner modules. + +### Module 8F: `WORK-MAINT-IMPORT-INGEST-FACADE-SPLIT-A` + +Maintained the ingest orchestrator safety net while reducing the oversized +facade: + +- `ingest/mod.rs` now contains the production facade only: profile selection, + stream dispatch, chunk consumer, watermark advancement, and source-evidence + plan persistence. +- The embedded low-level regression suite moved to `ingest/core_tests.rs`. +- A responsibility map in `import-dedup-audit.md` records why production + `ArchiveChunkConsumer` and source-evidence persistence stayed together for + now. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core archive::ingest::core_tests --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core --lib +``` + +Actual output: + +```text +core_tests targeted: 7 passed; 0 failed; 655 filtered out +vault-core lib: 662 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +unit: 277 files passed; 2076 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.38%, branches 98.61%, functions 99.66%, lines 99.72% +Rust workspace tests: vault-core 662 passed; vault-platform 46 passed; vault-worker 70 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. This was a maintainability-only split; +test assertions and product ingest semantics were preserved. + +### Module 9A: `vault-core::migration` import refusal fault paths + +Added two behavior assertions for whole-app import refusal ordering: + +- Wrong encrypted source key must fail with the typed invalid-key prefix while + preserving the existing target archive, derived marker, and all non-`.bak` + paths. +- Payload hash mismatch must fail before preservation/install renames, leaving + the existing target archive and derived marker untouched and creating no + `.bak-*` sidecars. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core migration::fault_tests --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core --lib +``` + +Actual output: + +```text +migration::fault_tests: 2 passed; 0 failed; 662 filtered out +vault-core lib: 664 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +unit: 277 files passed; 2076 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.38%, branches 98.61%, functions 99.66%, lines 99.72% +Rust workspace tests: vault-core 664 passed; vault-platform 46 passed; vault-worker 70 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. These tests pin the current intended +fail-fast behavior before the live project tree is renamed or overwritten. + +### Module 9B: `vault-core::app_lock` and `vault-platform::keyring` + +Added two behavior assertions for security refusal and recovery paths: + +- Malformed App Lock session state fails closed for status and unlock, malformed + secret hydration fails, and clearing the passcode removes the corrupted state + and secret files so the user can recover. +- Provider API keys do not satisfy database-key saved status, and clearing a + provider key does not remove the database key. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core malformed_app_lock_files_fail_closed_until_passcode_is_cleared --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-platform provider_key_does_not_satisfy_database_key_status_or_clear_database_secret --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-core app_lock --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-platform keyring --lib +``` + +Actual output: + +```text +app_lock targeted: 1 passed; 0 failed; 664 filtered out +keyring targeted: 1 passed; 0 failed; 46 filtered out +app_lock module: 5 passed; 0 failed; 660 filtered out +keyring module: 4 passed; 0 failed; 43 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +unit: 277 files passed; 2076 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.38%, branches 98.61%, functions 99.66%, lines 99.72% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. These tests pin fail-closed behavior for +corrupted lock files while keeping the documented recovery path available, and +they prevent provider API key storage from being mistaken for database key +storage. + +### Module 9C: `vault-platform::scheduler` + +Strengthened one host-command failure behavior: + +- macOS `launchctl bootstrap` failure must leave `apply_schedule` non-applied, + preserve the generated plist, write an audit artifact with the bootstrap + status, return an error verification check, and make the next + `schedule_status` report `permission-warning` instead of `installed`. + +Commands: + +```sh +cargo test --manifest-path src-tauri/Cargo.toml -p vault-platform macos_apply_schedule_reports_bootstrap_failures_without_erroring --lib +cargo test --manifest-path src-tauri/Cargo.toml -p vault-platform scheduler --lib +``` + +Actual output: + +```text +macOS bootstrap-failure targeted: 1 passed; 0 failed; 46 filtered out +scheduler module: 35 passed; 0 failed; 12 filtered out +``` + +Full checkpoint gate: + +```text +bun run check +unit: 277 files passed; 2076 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.38%, branches 98.61%, functions 99.66%, lines 99.72% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. The scheduler audit found existing +Windows/macOS host-command failure coverage for access denied, missing tasks, +mismatches, loaded-without-file states, and manual Linux paths; this checkpoint +tightens the macOS failed-bootstrap recovery/status contract. + +### Module 10A: JS Search/Browse chip and result residuals + +Added focused frontend behavior assertions and removed two dead defensive guards: + +- Advanced search help now has direct coverage for its default test id plus the + keyboard focus/blur popover path. +- Search-filter helpers now pin escaped quotes inside quoted operators, + smart-quoted operands, one-character operands, and quoted-empty operands that + must not surface as chips. +- Search result rows now assert unrelated keys do not select the row, missing + transition labels stay hidden, and title fallback proceeds from title to URL + to domain. +- Paper search panel now has a child-contract test proving stale remove ids are + ignored and focus scheduling tolerates a child that does not attach the input + ref. +- Deleted the unreachable `highlightQuery` regex `try/catch` and the + unreachable `appendFilterOperator` unchanged-query guard. Query tokens are + escaped before regex construction, and the panel only appends valid hard-coded + `tag` / `note` operators. + +Commands: + +```sh +bunx vitest run src/components/explorer-paper/paper-advanced-search-help.test.tsx src/components/explorer-paper/paper-search-hero.test.tsx src/pages/explorer/paper-search-filters.test.ts src/components/explorer-paper/paper-search-result.test.tsx src/pages/explorer/paper-search-panel.test.tsx src/pages/explorer/paper-search-panel.child-contract.test.tsx --coverage --coverage.include=src/components/explorer-paper/paper-advanced-search-help.tsx --coverage.include=src/pages/explorer/paper-search-filters.ts --coverage.include=src/components/explorer-paper/paper-search-result.tsx --coverage.include=src/pages/explorer/paper-search-panel.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Test Files 6 passed (6) +Tests 60 passed (60) +Targeted coverage for paper-advanced-search-help.tsx, paper-search-filters.ts, +paper-search-result.tsx, and paper-search-panel.tsx: +statements 100 | branches 100 | functions 100 | lines 100 +``` + +Full checkpoint gate: + +```text +bun run check +unit: 279 files passed; 2085 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.48%, branches 98.75%, functions 99.72%, lines 99.77% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Gate note: the first full `bun run check` attempt in the command sandbox failed +only in mockito-backed Rust tests because local test servers could not bind +sockets (`Operation not permitted`). Re-running the same command with normal +local permissions passed. + +Suspected product bugs: none confirmed. + +### Module 10B: JS Explorer route and paper-view residuals + +Added 10 focused frontend behavior assertions and removed route/helper branches +that the current public grammar cannot exercise: + +- `PaperExplorerView` now pins calendar day selection, non-Escape key handling, + invalid active-date labels, omitted `todayIso` current-day fallback, malformed + clock-format events, locale-format fallback/no-crash behavior, and decorated + pagination / infinite-scroll descriptors. +- `PaperDetailPanelMount` now asserts "All of domain" forwards the selected + domain and closes the panel. +- `ExplorerPage` now asserts enabled og:image settings forward their configured + fetch mode and desktop command transport selects the desktop annotation store. +- Deleted the route-only paginated `PaperExplorerView` prop, the redundant + `|| results` render guard, and the unreachable `infiniteDisabled ? undefined` + branch at the Browse route mount point. The route grammar already sends every + `infiniteDisabled` surface to Search, grouped views, invalid-regex callouts, + or locked/uninitialized states before Browse mounts. +- Simplified Paper search day-group sorting and Paper view density override + helpers to remove equality / `Map.has` branches that were unreachable under + the helper contracts. + +Commands: + +```sh +bunx vitest run src/pages/explorer/index.test.tsx src/pages/explorer/paper-view.test.tsx src/pages/explorer/paper-detail-panel-mount.test.tsx src/pages/explorer/paper-view-helpers.test.ts src/pages/explorer/paper-search-helpers.test.ts +bunx tsc -b --pretty false +bun run coverage:js +``` + +Actual output: + +```text +Targeted tests: +Test Files 5 passed (5) +Tests 94 passed (94) + +coverage:js: +Test Files 279 passed (279) +Tests 2095 passed (2095) +All files statements 99.6 | branches 99 | functions 99.84 | lines 99.88 + +Module 10B files now at 100/100/100/100: +src/pages/explorer/index.tsx +src/pages/explorer/paper-detail-panel-mount.tsx +src/pages/explorer/paper-search-helpers.ts +src/pages/explorer/paper-view-helpers.ts +src/pages/explorer/paper-view.tsx +``` + +Full checkpoint gate: + +```text +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2095 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.61%, branches 99.02%, functions 99.84%, lines 99.88% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. + +### Module 10C: JS Dashboard/app shell residuals + +Added 6 focused frontend behavior assertions and removed public-grammar +duplicate branches: + +- Exported and tested the route registry lookup guard so unknown route handles + fail loudly instead of silently picking the wrong shell screen. +- Pinned epigraph storage values that omit the date/index separator so daily + rotation overwrites malformed local state. +- Pinned `PKSearchPalette` whitespace-only queries so the palette stays on the + empty branch and does not call the backend search path. +- Pinned `useRouteHistoryNav` under `StrictMode` so duplicate effect mounts do + not move the shell history stack twice for the same location key. +- Pinned Dashboard On This Day target-date formatting for non-English resolved + locales. +- Pinned Dashboard archive-access fallback cleanup so a queued non-probe clear + cannot update state after unmount. +- Deleted redundant `shellStorage()` SSR fallback, the shell-level blank search + guard that `PKSearchPalette` already makes unreachable, the redundant + `archiveHealthy ?? false` fallback, and the unreachable + `domainAbbreviation().split('.')[0] ?? cleaned` fallback. + +Commands: + +```sh +bunx vitest run src/app/index-tests/router-structure.test.tsx src/app/shell-helpers.test.ts src/app/shell.test.tsx src/components/shell/pk-search-palette.test.tsx src/components/shell/use-route-history-nav.test.tsx src/pages/dashboard/on-this-day-card.test.tsx src/pages/dashboard/route-fallback.test.tsx +bunx tsc -b --pretty false +bunx vitest run src/app/index-tests/router-structure.test.tsx src/app/shell-helpers.test.ts src/app/shell.test.tsx src/components/shell/pk-search-palette.test.tsx src/components/shell/use-route-history-nav.test.tsx src/pages/dashboard/on-this-day-card.test.tsx src/pages/dashboard/route-fallback.test.tsx --coverage --coverage.include=src/app/router.tsx --coverage.include=src/app/shell-helpers.ts --coverage.include=src/app/shell.tsx --coverage.include=src/components/shell/pk-search-palette.tsx --coverage.include=src/components/shell/use-route-history-nav.ts --coverage.include=src/pages/dashboard/on-this-day-card.tsx --coverage.include=src/pages/dashboard/route-fallback-access.ts --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +bun run coverage:js +``` + +Actual output: + +```text +Targeted tests: +Test Files 7 passed (7) +Tests 109 passed (109) + +Targeted 10C coverage: +All files 100/100/100/100 +src/app/router.tsx 100/100/100/100 +src/app/shell-helpers.ts 100/100/100/100 +src/app/shell.tsx 100/100/100/100 +src/components/shell/pk-search-palette.tsx 100/100/100/100 +src/components/shell/use-route-history-nav.ts 100/100/100/100 +src/pages/dashboard/on-this-day-card.tsx 100/100/100/100 +src/pages/dashboard/route-fallback-access.ts 100/100/100/100 + +coverage:js: +Test Files 279 passed (279) +Tests 2101 passed (2101) +All files statements 99.63 | branches 99.09 | functions 99.84 | lines 99.89 + +Full coverage groups now at 100/100/100/100: +src/app +src/components/shell +src/pages/dashboard +``` + +Full checkpoint gate: + +```text +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2101 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.64%, branches 99.11%, functions 99.84%, lines 99.89% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. + +### Module 10D: JS Explorer hook/helper residuals + +Added 15 focused hook/helper behavior assertions and removed duplicate private +guards that the public hook grammar could not exercise: + +- `url-state-derivations` now pins keyword grouped views, positive page parsing, + empty query/cursor history payloads, null query signatures, semantic/session + active-filter chips, bare recent-search labels, and incomplete date shortcut + ranges. +- `useDesktopAnnotations` now treats missing backend `tags` as an empty list + during hydration instead of leaking `undefined` into the UI state. +- `useBrowseDayInsightsCache` now pins stale success/failure replies after a + refresh-key rotation and removed a private duplicate request guard already + made unreachable by `resolve()`'s cache-entry check. +- `useExplorerArchiveDensity` now pins stale success/failure replies, cancelled + not-ready reset microtasks, available-year-only bounds, and no-finite-year + responses. The removed sequence ref duplicated effect cleanup cancellation. +- `useExplorerData` now pins cached adjacent prefetch reuse, stale multi-page + prefetch cancellation, cancellation before the transition paint, and empty + semantic recall results without inventing a selected row. + +Commands: + +```sh +bunx vitest run src/pages/explorer/url-state-derivations.test.ts src/pages/explorer/use-desktop-annotations.test.tsx src/pages/explorer/hooks/use-browse-day-insights-cache.test.tsx src/pages/explorer/hooks/use-explorer-archive-density.test.tsx src/pages/explorer/hooks/use-explorer-data.test.tsx +bunx vitest run src/pages/explorer/url-state-derivations.test.ts src/pages/explorer/use-desktop-annotations.test.tsx src/pages/explorer/hooks/use-browse-day-insights-cache.test.tsx src/pages/explorer/hooks/use-explorer-archive-density.test.tsx src/pages/explorer/hooks/use-explorer-data.test.tsx --coverage --coverage.include=src/pages/explorer/url-state-derivations.ts --coverage.include=src/pages/explorer/use-desktop-annotations.ts --coverage.include=src/pages/explorer/hooks/use-browse-day-insights-cache.ts --coverage.include=src/pages/explorer/hooks/use-explorer-archive-density.ts --coverage.include=src/pages/explorer/hooks/use-explorer-data.ts --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Targeted tests: +Test Files 5 passed (5) +Tests 58 passed (58) + +Targeted 10D coverage: +All files 100/100/100/100 +src/pages/explorer/url-state-derivations.ts 100/100/100/100 +src/pages/explorer/use-desktop-annotations.ts 100/100/100/100 +src/pages/explorer/hooks/use-browse-day-insights-cache.ts 100/100/100/100 +src/pages/explorer/hooks/use-explorer-archive-density.ts 100/100/100/100 +src/pages/explorer/hooks/use-explorer-data.ts 100/100/100/100 +``` + +Full checkpoint gate: + +```text +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2114 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.69%, branches 99.21%, functions 99.84%, lines 99.91% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. + +### Module 10E: JS Explorer runtime hook and paper helper residuals + +Added 24 focused behavior assertions and removed redundant branches inside +Explorer runtime hooks/helpers: + +- `useExplorerInfinitePages` now pins disabled/no-head dormancy, same-key buffer + retention, cancelled rejected foreground pages, directional prefetch failure, + directional prefetch reuse, and the hard-cap/page-count decision through a + pure derivation helper. The hook now treats `pageCount` as the numbered-page + contract instead of mixing in cursor-style `hasNext`. +- `useExplorerOgImages` now pins empty result windows, cache-token reset of the + pending mark-shown batch, and unmount timer cleanup. Redundant enqueued-url + tracking was removed because the visible URL dedupe, inflight set, and cache + map already prevent duplicate local reads/refetch enqueues. +- `useScrollDirection` now pins stable same-direction samples, RAF dedupe, and + pending-frame cleanup. +- `useViewportMount` now pins no-ref setup, empty observer callbacks, + zero-height recycle, and re-entry measurement behavior. +- `groupEntriesByDay`/format helpers now pin out-of-range date fallback and + hour12 formatting. Private empty-session and manual date-comparator branches + were simplified under the public grouping contract. +- `ExplorerDetailPanel` now pins loading, empty, missing metadata, null-domain, + and null-profile detail-rail branches. + +Commands: + +```sh +bunx vitest run src/pages/explorer/hooks/use-explorer-infinite-pages.test.tsx src/pages/explorer/hooks/use-explorer-og-images.test.tsx src/pages/explorer/hooks/use-scroll-direction.test.tsx src/pages/explorer/hooks/use-viewport-mount.test.tsx src/pages/explorer/paper/group-entries.test.ts src/pages/explorer/panels/detail-panel.test.tsx +bunx vitest run src/pages/explorer/hooks/use-explorer-infinite-pages.test.tsx src/pages/explorer/hooks/use-explorer-og-images.test.tsx src/pages/explorer/hooks/use-scroll-direction.test.tsx src/pages/explorer/hooks/use-viewport-mount.test.tsx src/pages/explorer/paper/group-entries.test.ts src/pages/explorer/panels/detail-panel.test.tsx --coverage --coverage.include=src/pages/explorer/hooks/use-explorer-infinite-pages.ts --coverage.include=src/pages/explorer/hooks/use-explorer-og-images.ts --coverage.include=src/pages/explorer/hooks/use-scroll-direction.ts --coverage.include=src/pages/explorer/hooks/use-viewport-mount.ts --coverage.include=src/pages/explorer/paper/group-entries.ts --coverage.include=src/pages/explorer/panels/detail-panel.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Targeted tests: +Test Files 6 passed (6) +Tests 81 passed (81) + +Targeted 10E coverage: +All files 100/100/100/100 +src/pages/explorer/hooks/use-explorer-infinite-pages.ts 100/100/100/100 +src/pages/explorer/hooks/use-explorer-og-images.ts 100/100/100/100 +src/pages/explorer/hooks/use-scroll-direction.ts 100/100/100/100 +src/pages/explorer/hooks/use-viewport-mount.ts 100/100/100/100 +src/pages/explorer/paper/group-entries.ts 100/100/100/100 +src/pages/explorer/panels/detail-panel.tsx 100/100/100/100 +``` + +Full checkpoint gate: + +```text +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2138 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.87%, branches 99.56%, functions 99.87%, lines 99.92% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. + +### Module 10F: JS Explorer Paper component residuals + +Added 11 focused behavior assertions and removed redundant private branches in +Explorer Paper presentation/runtime components: + +- `PaperCalendarPopover` now pins low- and medium-density hover spark opacity + instead of relying on the year picker to incidentally walk density tiers. +- `aggregateDayInsights` now pins empty URL handling, empty domain+URL fallback, + invalid timestamp handling, exact-hour durations, and the `Intl.NumberFormat` + unit-format fallback. +- `PaperDayInsights` now pins a details disclosure with no peak-hour row, host + time-formatting failures, and long revisited-URL truncation. +- `PaperListRow` now pins og:image as the list icon fallback when favicon bytes + are absent. +- Redundant branches were simplified where the public render grammar already + guarantees the ref or handler: assistant auto-scroll, contact-sheet toolbar + measurement, infinite-scroll footer `canLoadMore`, detail-panel pending flush + and look-further rows, filter-strip outside-click container, and top-domain + max-visit lookup. + +Commands: + +```sh +bunx vitest run src/components/explorer-paper/paper-assistant-view.test.tsx src/components/explorer-paper/paper-calendar-popover.test.tsx src/components/explorer-paper/paper-contact-sheet.test.tsx src/components/explorer-paper/paper-contact-sheet.behavior.test.tsx src/components/explorer-paper/paper-contact-sheet.virt.test.tsx src/components/explorer-paper/paper-day-insights-helpers.test.ts src/components/explorer-paper/paper-day-insights.test.tsx src/components/explorer-paper/paper-detail-panel.test.tsx src/components/explorer-paper/paper-filter-strip.test.tsx src/components/explorer-paper/paper-browse-primitives.test.tsx +bunx vitest run src/components/explorer-paper/paper-assistant-view.test.tsx src/components/explorer-paper/paper-calendar-popover.test.tsx src/components/explorer-paper/paper-contact-sheet.test.tsx src/components/explorer-paper/paper-contact-sheet.behavior.test.tsx src/components/explorer-paper/paper-contact-sheet.virt.test.tsx src/components/explorer-paper/paper-day-insights-helpers.test.ts src/components/explorer-paper/paper-day-insights.test.tsx src/components/explorer-paper/paper-detail-panel.test.tsx src/components/explorer-paper/paper-filter-strip.test.tsx src/components/explorer-paper/paper-browse-primitives.test.tsx --coverage --coverage.include=src/components/explorer-paper/paper-assistant-view.tsx --coverage.include=src/components/explorer-paper/paper-calendar-popover.tsx --coverage.include=src/components/explorer-paper/paper-contact-sheet.tsx --coverage.include=src/components/explorer-paper/paper-day-insights-helpers.ts --coverage.include=src/components/explorer-paper/paper-day-insights.tsx --coverage.include=src/components/explorer-paper/paper-detail-panel.tsx --coverage.include=src/components/explorer-paper/paper-filter-strip.tsx --coverage.include=src/components/explorer-paper/paper-list-row.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Targeted tests: +Test Files 10 passed (10) +Tests 173 passed (173) + +Targeted 10F coverage: +All files 100/100/100/100 +src/components/explorer-paper/paper-assistant-view.tsx 100/100/100/100 +src/components/explorer-paper/paper-calendar-popover.tsx 100/100/100/100 +src/components/explorer-paper/paper-contact-sheet.tsx 100/100/100/100 +src/components/explorer-paper/paper-day-insights-helpers.ts 100/100/100/100 +src/components/explorer-paper/paper-day-insights.tsx 100/100/100/100 +src/components/explorer-paper/paper-detail-panel.tsx 100/100/100/100 +src/components/explorer-paper/paper-filter-strip.tsx 100/100/100/100 +src/components/explorer-paper/paper-list-row.tsx 100/100/100/100 +``` + +Full checkpoint gate: + +```text +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2149 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 99.94%, branches 99.76%, functions 99.87%, lines 99.95% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. + +### Module 10G: JS shared component, preview facade, and route residuals + +Added or extended 14 focused behavior assertions and removed two redundant +private branches in the final JS coverage residual set: + +- `YearHeatmap` click behavior now depends on a single observable clickable + predicate, while covered tests still pin count-bearing date selection. +- Browsing-rhythm calendar/card tests now pin disabled year navigation, + missing reset shortcuts, ready-state summaries, zero-visit cells, and + selection-prompt behavior. +- Background progress/status tests now pin empty labels, log-only detail rows, + AI queue fallback totals, and warning precedence. +- Browser-preview facade tests now pin og:image default hydration, dashboard + earliest/latest ordering stability, and the legacy unsupported + `prefetch_og_images` rejection instead of expanding `src/lib/backend.ts`. +- Paper assistant/audit/import route tests now pin paper-layout send behavior, + providerless fallback attribution, manifest-chain selection, and browser/file + method normalization. +- Intelligence and settings residuals now pin title fallback rendering and the + data-migration apply path without retaining an unreachable phase guard. + +Commands: + +```sh +bunx vitest run src/components/heatmap/year-heatmap.test.tsx src/components/intelligence/browsing-rhythm-calendar.test.tsx src/components/intelligence/browsing-rhythm-card.test.tsx src/components/primitives/background-progress.test.tsx src/components/sidebar/background-status.test.tsx src/lib/backend-preview-shell-commands.test.ts src/lib/backend-preview-showcase.test.ts src/lib/backend-tests/preview-workflows.test.ts src/pages/intelligence-surfaces/assistant-and-shell.test.tsx src/pages/audit/index.test.tsx src/pages/import/index.test.tsx src/pages/intelligence/paper-intelligence-panel.test.tsx src/pages/settings/data-migration-section.test.tsx +bunx vitest run src/lib/backend-preview-showcase.test.ts src/pages/import/index.test.tsx +bun run coverage:js +``` + +Actual output: + +```text +Focused 10G sweep: +Test Files 13 passed (13) +Tests 103 passed (103) + +Final residual recheck: +Test Files 2 passed (2) +Tests 17 passed (17) + +JS coverage: +Test Files 279 passed (279) +Tests 2162 passed (2162) +All files statements 100%, branches 100%, functions 100%, lines 100% +No uncovered JS lines, branches, or functions in lcov.info. +``` + +Full checkpoint gate: + +```text +bun run check +format/lint/i18n/typecheck: passed +unit: 279 files passed; 2162 tests passed +desktop contract: 5 files passed; 26 tests passed; coverage 100/100/100/100 +JS coverage: All files statements 100%, branches 100%, functions 100%, lines 100% +Rust workspace tests: vault-core 665 passed; vault-platform 47 passed; vault-worker 70 passed +Rust coverage cfg tests: vault-core 666 passed; vault-platform 49 passed; vault-worker 80 passed +Rust coverage: verified at 100% for 35459 instrumented source lines and 1652 source functions +supply-chain/platform/release checks: passed +build: passed +browser E2E: 4 passed +desktop bridge E2E: 3 passed +desktop contract mutation: 64 mutants; 100.00 score; 0 survived; 0 timed out +``` + +Suspected product bugs: none confirmed. Drift closed as `QA-GAP-001`; the JS +runtime coverage floor is now the documented 100/100/100/100 gate. + +### Module 6: `src/app/shell.tsx` and `src/components/shell/*` + +Added 15 behavior assertions across the global shell and shell chrome: + +- Background busy payloads render the non-blocking progress strip and do not + show the blocking overlay. +- Malformed preference-change events leave the current theme chrome unchanged. +- Dashboard totals, last-archive telemetry, and source color fallback order are + visible in the status bar. +- Backend palette errors and missing `items` payloads render the no-results + state instead of silently passing. +- URL-only, titleless, URL-less, and no-visit-date palette hits map to visible, + selectable results and route to Explorer as expected. +- Escape actually closes the palette; Manage Sources actually routes to + Settings; stale palette searches do not leak late success or failure results. +- Status bar epigraph/profile-label fallbacks, topbar navigator fallback, route + replace navigation, blank-UA shortcut labels, and `isContentEditable` shortcut + suppression are pinned with observable assertions. + +Commands: + +```sh +bunx vitest run src/app/shell.test.tsx src/components/shell/pk-search-palette.test.tsx src/components/shell/pk-status-bar.test.tsx src/components/shell/pk-topbar.test.tsx src/components/shell/use-route-history-nav.test.tsx --coverage --coverage.include=src/app/shell.tsx --coverage.include=src/components/shell/pk-search-palette.tsx --coverage.include=src/components/shell/pk-status-bar.tsx --coverage.include=src/components/shell/pk-topbar.tsx --coverage.include=src/components/shell/use-route-history-nav.ts --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Test Files 5 passed (5) +Tests 68 passed (68) + +Targeted shell coverage: +All files: statements 99.2 | branches 97.79 | functions 100 | lines 100 +shell.tsx: statements 98.8 | branches 95.58 | functions 100 | lines 100 +pk-search-palette.tsx: statements 100 | branches 97.14 | functions 100 | lines 100 +pk-status-bar.tsx: statements 100 | branches 100 | functions 100 | lines 100 +pk-topbar.tsx: statements 100 | branches 100 | functions 100 | lines 100 +use-route-history-nav.ts: statements 98.83 | branches 98.27 | functions 100 | lines 100 +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: Test Files 277 passed (277), Tests 2070 passed (2070) +JS coverage: statements 99.36 | branches 98.51 | functions 99.66 | lines 99.72 +Rust coverage: 100% for 35246 instrumented source lines and 1634 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. Drift recorded as `QA-GAP-006` for the +remaining defensive/redundant branches that are not naturally reachable through +the current public shell grammar. + +### Module 7: `src/pages/dashboard/index.tsx` + +Added 6 focused behavior tests: + +- archive span uses the current day when `latestVisitAt` is missing, instead of + rendering the missing-span placeholder. +- read-model derived archive state renders zero-size fallback, encrypted mode, + source count, database-path fallback, and missing manifest hash. +- `getOnThisDay()` null data renders the empty state instead of preserving stale + entries. +- stale successful and failed On This Day responses are discarded after the + route becomes uninitialized. +- On This Day entries/header actions, year heatmap actions, and active-thread + actions navigate to their route-level destinations. +- morning greeting branch is covered alongside the existing afternoon/evening + branches. + +Commands: + +```sh +bunx vitest run src/pages/dashboard/index.test.tsx --coverage --coverage.include=src/pages/dashboard/index.tsx --coverage.thresholds.lines=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 +``` + +Actual output: + +```text +Test Files 1 passed (1) +Tests 17 passed (17) + +Targeted dashboard coverage: +All files: statements 100 | branches 100 | functions 100 | lines 100 +index.tsx: statements 100 | branches 100 | functions 100 | lines 100 +``` + +Full checkpoint gate: + +```text +bun run check +JS unit: Test Files 277 passed (277), Tests 2076 passed (2076) +JS coverage: statements 99.38 | branches 98.61 | functions 99.66 | lines 99.72 +Rust coverage: 100% for 35246 instrumented source lines and 1634 source functions +Browser E2E: 4 passed +Desktop bridge E2E: 3 passed +Desktop contract mutation: 100.00 mutation score, 64 mutants, 0 survived +``` + +Suspected product bugs: none confirmed. diff --git a/bun.lock b/bun.lock index 36061e93..5da7e257 100644 --- a/bun.lock +++ b/bun.lock @@ -5,14 +5,22 @@ "": { "name": "chv-vite-06wcbxeu4a", "dependencies": { + "@fontsource/jetbrains-mono": "^5", + "@fontsource/newsreader": "^5", "@tauri-apps/api": "^2.11.0", "@tauri-apps/plugin-dialog": "^2.4.3", "@tauri-apps/plugin-log": "^2.8.0", "@tauri-apps/plugin-stronghold": "^2.3.1", - "clsx": "^2.1.1", + "class-variance-authority": "^0.7", + "clsx": "^2", + "cmdk": "^1.1.1", + "lucide-react": "^0.479", + "radix-ui": "^1.4.3", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router-dom": "^7.14.0", + "tailwind-merge": "^3", + "tw-animate-css": "^1", }, "devDependencies": { "@eslint/js": "^9.39.4", @@ -20,6 +28,7 @@ "@stryker-mutator/core": "^9.6.0", "@stryker-mutator/typescript-checker": "^9.6.0", "@stryker-mutator/vitest-runner": "^9.6.0", + "@tailwindcss/vite": "^4", "@tauri-apps/cli": "^2.11.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -36,6 +45,7 @@ "globals": "^17.4.0", "jsdom": "^29.0.1", "prettier": "^3.8.1", + "tailwindcss": "^4", "typescript": "~5.9.3", "typescript-eslint": "^8.58.0", "vite": "^8.0.3", @@ -160,6 +170,18 @@ "@exodus/bytes": ["@exodus/bytes@1.15.0", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ=="], + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@fontsource/jetbrains-mono": ["@fontsource/jetbrains-mono@5.2.8", "", {}, "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ=="], + + "@fontsource/newsreader": ["@fontsource/newsreader@5.2.10", "", {}, "sha512-TFaYzoFhDqarUyV2yYjgZZEwT4bpaj6sGBnXSnFknQ/QB8/9LzfY6IO9+inHOX4zzPp87Z7/KuG1OI5gr91Q3A=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], @@ -216,6 +238,126 @@ "@playwright/test": ["@playwright/test@1.59.1", "", { "dependencies": { "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" } }, "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.7", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-form": ["@radix-ui/react-form@0.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg=="], + + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="], + + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="], + + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="], + + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-toggle-group": "1.1.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="], "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg=="], @@ -266,6 +408,36 @@ "@stryker-mutator/vitest-runner": ["@stryker-mutator/vitest-runner@9.6.0", "", { "dependencies": { "@stryker-mutator/api": "9.6.0", "@stryker-mutator/util": "9.6.0", "tslib": "~2.8.0" }, "peerDependencies": { "@stryker-mutator/core": "9.6.0", "vitest": ">=2.0.0" } }, "sha512-/zyELz5jTDAiH0Hr23G6KSnBFl9XV+vn0T0qUAk4sPqJoP5NVm9jjpgt9EBACS/VTkVqSvXqBid4jmESPx11Sg=="], + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="], + "@tauri-apps/api": ["@tauri-apps/api@2.11.0", "", {}, "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA=="], "@tauri-apps/cli": ["@tauri-apps/cli@2.11.0", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.11.0", "@tauri-apps/cli-darwin-x64": "2.11.0", "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.0", "@tauri-apps/cli-linux-arm64-gnu": "2.11.0", "@tauri-apps/cli-linux-arm64-musl": "2.11.0", "@tauri-apps/cli-linux-riscv64-gnu": "2.11.0", "@tauri-apps/cli-linux-x64-gnu": "2.11.0", "@tauri-apps/cli-linux-x64-musl": "2.11.0", "@tauri-apps/cli-win32-arm64-msvc": "2.11.0", "@tauri-apps/cli-win32-ia32-msvc": "2.11.0", "@tauri-apps/cli-win32-x64-msvc": "2.11.0" }, "bin": { "tauri": "tauri.js" } }, "sha512-W5Wbuqsb2pHFPTj4TaRNKTj5rwXhDShPiLSY9T18y4ouSR/NNCptAEFxFsBtyNRgL6Vs1a/q9LzfqqYzEwC+Jw=="], @@ -376,6 +548,8 @@ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], @@ -406,10 +580,14 @@ "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -444,6 +622,8 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + "diff-match-patch": ["diff-match-patch@1.0.5", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="], "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], @@ -454,6 +634,8 @@ "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "enhanced-resolve": ["enhanced-resolve@5.21.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-wE4fDO8OjJhrPFH69HUQStq5oKvGRTNXEyW+k5C/pUQLASSsTu7obd2V3GvCDgPcY9AWjhJ4jz9Kh7iRvrxhJg=="], + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -530,6 +712,8 @@ "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], @@ -540,6 +724,8 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], @@ -588,6 +774,8 @@ "istanbul-reports": ["istanbul-reports@3.2.0", "", { "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA=="], + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + "js-md4": ["js-md4@0.3.2", "", {}, "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA=="], "js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="], @@ -644,6 +832,8 @@ "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], + "lucide-react": ["lucide-react@0.479.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], @@ -728,16 +918,24 @@ "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + "react-router": ["react-router@7.14.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ=="], "react-router-dom": ["react-router-dom@7.14.0", "", { "dependencies": { "react-router": "7.14.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ=="], + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -792,6 +990,12 @@ "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], + + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], "tinyexec": ["tinyexec@1.0.4", "", {}, "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw=="], @@ -816,6 +1020,8 @@ "tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="], + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "typed-inject": ["typed-inject@5.0.0", "", {}, "sha512-0Ql2ORqBORLMdAW89TQKZsb1PQkFGImFfVmncXWe7a+AA3+7dh7Se9exxZowH4kbnlvKEFkMxUYdHUpjYWFJaA=="], @@ -838,6 +1044,12 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + "vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="], "vitest": ["vitest@4.1.2", "", { "dependencies": { "@vitest/expect": "4.1.2", "@vitest/mocker": "4.1.2", "@vitest/pretty-format": "4.1.2", "@vitest/runner": "4.1.2", "@vitest/snapshot": "4.1.2", "@vitest/spy": "4.1.2", "@vitest/utils": "4.1.2", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.2", "@vitest/browser-preview": "4.1.2", "@vitest/browser-webdriverio": "4.1.2", "@vitest/ui": "4.1.2", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg=="], @@ -892,6 +1104,98 @@ "@eslint/eslintrc/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + "@radix-ui/react-accordion/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-aspect-ratio/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-checkbox/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-context-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-form/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menubar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-radio-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-select/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-slider/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-switch/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toast/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toggle/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toolbar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@tauri-apps/plugin-dialog/@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="], "@tauri-apps/plugin-log/@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="], @@ -918,6 +1222,8 @@ "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "radix-ui/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.12", "", {}, "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw=="], "@eslint/config-array/minimatch/brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="], diff --git a/components.json b/components.json new file mode 100644 index 00000000..23d5e3cf --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/tailwind.css", + "baseColor": "stone", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/cn", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/lib/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/docs/architecture/data-model.md b/docs/architecture/data-model.md index 39ab523c..5b828727 100644 --- a/docs/architecture/data-model.md +++ b/docs/architecture/data-model.md @@ -94,6 +94,7 @@ - 每季 checkpoint - 記錄每次備份的瀏覽器版本、schema 指紋、profile metadata。 - `favicons` 保存來源觀測到的 page-level favicon facts,包含 `page_url`、normalized `page_host`、`page_registrable_domain`、`last_updated_ms` 與去重後的 `image_blob_hash`;實際 icon bytes 由 `favicon_blobs` content-addressed 儲存去重。Explorer 只在 lazy hydration path 讀 favicon:不晚於 visit time 的 exact page icon 優先,其次才用 indexed host / registrable-domain fallback;fallback 必須是 miss-only staged lookup,每級用 indexed `LIMIT 1` 先縮到單一候選後才讀 blob,不得合成會排序大量候選或提前 join blob 的 monolithic SQL。這個 read-time fallback 不改寫 canonical visit 或舊 favicon fact,也不得在 archive open / schema bootstrap 時同步回填舊 favicon metadata。 +- `og_images` 保存每張卡片模式預覽圖的 fetch 結果(migration 012),包含 `page_url`、診斷用的 `page_host`、解析到的 `source_og_url`、`fetch_status` / `http_status`、`fetched_at`、`last_shown_at`(LRU 訊號)與 `refetch_after`(負緩存退避)。實際 image bytes 由 `og_image_blobs` 以 `sha256_hex(bytes)` content-addressed 去重儲存;不同 page 共用同一張預覽時只佔一份。**讀路徑明確不做 host fallback** — GitHub 與 Medium 同 host 不同 page 的社交卡截然不同,所以 lookup 一律是 exact-page-url。負緩存(`fetch_status='missing'` / `'http_error'` 等)也是正當 row,避免 retry storm。og:image 快取是 derived,**不進 backup export**,restore 後 lazy 重建;詳見 `docs/features/og-images.md`。 ### Storage planes @@ -177,19 +178,19 @@ - `compare_set_id` 現在是 compare-page aggregate 的 canonical reusable identity,`get_compare_set_detail` 會從這個 id 還原 compare-set summary、related trail/session context、pages 與 recent days;`path_flows` 則必須帶 stable `flow_id` 與 typed `steps`,前端不得再從 human-readable pattern label 逆推 route target。 - `get_intelligence_embed_cards`、`get_intelligence_widget_snapshot`、`get_intelligence_public_snapshot` 現在屬於 read-only backend payload providers。它們已經接到 Settings 的 manual review / copy-export surface,並作為 `preview_intelligence_local_host` / `build_intelligence_local_host` 的唯一資料來源;這仍不等於 OS widget、localhost API、或其他完整 embed/widget/public host integration 已完成。 - trusted external-output payload 現在可帶 structured `primaryTarget` / `secondaryTargets` 供 Settings 與 trusted local host 復用 app-link;`public snapshot` 則必須維持 redacted,不得下放 `family_id`、`trail_id`、`compare_set_id`、`canonical_url` 等 internal reusable IDs。 -- `preview_intelligence_local_host` 只在記憶體中 materialize 可 review 的 artifact;`build_intelligence_local_host` 則固定把 `browser-snippet-v1` 寫到 `app_root/integrations/core-intelligence/browser-snippet-v1/{index.html,bundle.json}`。這兩個檔案都是 rebuildable local artifact,不屬於 canonical archive、source-evidence archive、或 remote backup bundle contract。 +- `preview_intelligence_local_host` 只在記憶體中 materialize 可 review 的 artifact;`build_intelligence_local_host` 則固定把 `browser-snippet-v1` 寫到 `app_root/integrations/core-intelligence/browser-snippet-v1/{index.html,bundle.json}`。這兩個檔案都是 rebuildable local artifact,不屬於 canonical archive 或 source-evidence archive 的 contract。 - `bundle.json` 是 trusted local host 的 typed machine contract:至少包含 `bundleVersion`、`hostId`、`generatedAt`、`locale`、`dateRange`、`profileId`、`embedCards`、`widgetSnapshot`、`publicSnapshot`、`trustedOnlyCardIds`、`trustedOnlyCardCount` 與 `boundaryNotes`;`index.html` 直接內嵌同一份 bundle data 靜態渲染,不再從 `file://` 另行 fetch JSON。 - `deterministic_module_runtime` 是 module-registry trace table,不是 canonical truth。它只保存 module version、status、dependencies、derived tables、last run / built / invalidated time、stale reason 與 notes,供 Settings / Insights 誠實顯示 rebuild-required state。 - derived clear / rebuild 絕不能修改 canonical `visits`、`downloads`、`search_terms`、`runs`、`manifests` 或 rollback visibility 欄位。任何 derived maintenance 都只能留下 trace,不可改寫 source facts。2026-04-12 起,deterministic rebuild 的 live snapshot 也不得先清空再等待後續 commit;同 scope 的 derived rows、snapshot payload 與 module runtime 必須在同一個 intelligence transaction 內替換完成,避免留下半清空狀態。 - refetch freshness / fetch status / snippet / readable text 都屬 future derived evidence,而不是 source of truth。v0.2.0 不抓取網頁正文;後續版本若啟用,這些資料可因 plugin disable、full rebuild、clear derived state 或 pipeline version 升級而被重新計算或刪除。 -### Remote backup bundle contract +### Data migration bundle contract (`.pathkeep-bundle`) -- M4-A 的 remote backup artifact 是 `pathkeep.remote-backup.v1` zip bundle,不是直接把 live archive path 指向 object storage。bundle 至少包含 `archive/history-vault.sqlite`、`archive/source-evidence.sqlite`、`config/config.json`、`metadata/bundle-manifest.json`、`metadata/bundle-manifest.sha256`,並在存在時附帶 `audit/manifests/` 與 scheduler artifacts;derived search / intelligence DB 與 sidecars 都屬 rebuildable state,不進 canonical remote bundle。 -- `bundle-manifest.json` 是 bundle 內的 restore contract:必須記錄 `bundleVersion`、`appVersion`、`createdAt`、`archiveMode`、`bucket`、`objectKey` 與每個 entry 的 `relativePath` / `sha256` / `sizeBytes`。 -- Verify 不只檢查 zip 能不能打開;它還必須驗證 bundle version、required entries、manifest 宣告的 entry set 是否與實際 zip entry set 一致、detached manifest checksum 是否吻合、每個 manifest file 的 checksum / size,並嘗試用本機 restore path 打開打包後的 SQLite archive。encrypted bundle 驗證需要 session key;plaintext bundle 要留下明確 warning。 -- `bundle-manifest.sha256` 與 entry-set 檢查在 v1 的定位是 corruption / drift detection,不是 detached signing 或 remote authenticity attestation;PathKeep 目前仍不宣稱 bundle 已具備 cryptographic publisher proof。 -- remote object lifecycle 在 v1 仍是 manual-first。PathKeep 可以記錄 `lastUploadedAt`、`lastUploadedObjectKey`、`lastError` 與 verify report,但不在未完成 restore rehearsal 前自動 prune bucket 內容。 +- 整機 Export / Import 的 artifact 是 `.pathkeep-bundle` zip,由 `vault-core::migration` 寫入 / 讀回。zip 至少包含 `pathkeep-export-manifest.json`、`pathkeep-export-manifest.sha256` sidecar、`config/config.json`、`archive/history-vault.sqlite`(透過 `sqlcipher_export` 保留來源加密 key)以及 `archive/source-evidence.sqlite`;如果本機有 `derived/`、`audit/`、`raw-snapshots/`、`sidecars/intelligence-blobs/`、`sidecars/semantic-index/`,也一併打包。derived projections 屬 rebuildable,但 bundle 仍然會帶上以加速目標機 first-paint。 +- 明確排除:`vault.hold` / `stronghold-salt.txt`(App Lock secrets 留在來源機)、`logs/`、`diagnostics/`、`schedule/`(platform-specific scheduler artifacts)、`staging/`、`quarantine/`、`exports/`(避免遞迴打包)。`EXPORT_EXCLUSIONS_DOC` 把這份清單透傳到 UI,使用者匯入前能看到什麼東西不會跟過來。 +- `pathkeep-export-manifest.json` 是 import 的 restore contract:記錄 `formatVersion`、`appVersion`、`archiveSchemaVersion`、`archiveMode`、`exportedAt`、`exporterHostname`,以及每個 entry 的 `path` / `sha256` / `sizeBytes`。獨立的 `pathkeep-export-manifest.sha256` sidecar 提供 manifest 反竄改 hash。 +- Apply 路徑:先 re-validate manifest sha256;對每個 entry extract 到 staging 並重算 sha256;確認 `archiveSchemaVersion ≤ max_schema_version()`(refuse 來自更新 PathKeep 版本的 bundle);rename 既有 target subtree 為 `*.bak-` sibling 保留;atomic-move staged 進 live tree;對 imported archive 跑 forward schema migrations。 +- 這條 contract 取代了 2026-05-25 移除的 S3-based remote backup bundle(`pathkeep.remote-backup.v1`)。PathKeep 不再做 cloud upload;跨機器搬遷只走使用者主動發起、寫到使用者選的本地 `.pathkeep` 檔的 Export / Import bundle。 --- diff --git a/docs/architecture/desktop-command-surface.md b/docs/architecture/desktop-command-surface.md index 2a987bb2..81b92072 100644 --- a/docs/architecture/desktop-command-surface.md +++ b/docs/architecture/desktop-command-surface.md @@ -79,7 +79,7 @@ | `run_backup_now`, `query_history`, `load_dashboard_snapshot`, `load_audit_run_detail`, `export_history`, `preview_snapshot_restore`, `run_snapshot_restore`, `preview_retention_prune`, `run_retention_prune` | `archive.*`, `explorer.*`, `audit.*` | Dashboard、Explorer、Audit、Settings | 命名仍是 legacy single-command style,但 archive backup execute surface 現在必須維持 off-main-thread Tauri wrapper,讓 `pathkeep://backup-progress` 能在桌面真機持續 repaint;shell 會把 manual backup 轉成 global archive task,讓 Jobs / sidebar footer 顯示 live progress 與 bounded console log。`snapshot_restore` shipping surface 現在是對 saved raw-source checkpoint 的 preview / replay;archive-file safety snapshot 仍可能需要 manual recovery。 | | `inspect_takeout`, `import_takeout`, `inspect_browser_history`, `import_browser_history`, `preview_import_batch`, `revert_import_batch`, `restore_import_batch`, `doctor_report`, `repair_health` | `archive.import.*`, `archive.rollback.*`, `doctor.*` | Import、Audit、repair CTA | Takeout scan / execute 保留原 command;Browser Direct local DB scan / execute 走 `inspect_browser_history` / `import_browser_history`,避免 Safari `History.db`、Firefox `places.sqlite`、ChatGPT Atlas `History` 或 Perplexity Comet `History` 被送進 Takeout parser。Edge / Atlas / Comet 使用 Chromium-family parser 和 sidecar staging,但不得新增 command 或 request field;Edge product metadata 必須保留 `Microsoft Edge` / `Microsoft Edge Dev`。Firefox 使用 `places.sqlite` history-only staging,不偽造 Chromium sidecars。兩條 execute path 都必須維持 off-main-thread Tauri wrapper,並共用 `pathkeep://import-progress`、structured `logEvents`、shell-owned archive task handoff、import batch review、soft-hide rollback / restore、source evidence / audit artifact story;`restore` 與 `snapshot_restore` 需保持獨立語義。 | | `preview_schedule`, `apply_schedule`, `remove_schedule`, `repair_schedule`, `schedule_status` | `schedule.install.*`, `schedule.get_status` | Scheduled Backup Settings、Onboarding setup | macOS 有實際 apply / remove / detect,並會把 known legacy LaunchAgent labels 顯式標成 `legacy-install-detected` 而不是冒充 canonical installed 或 missing;Windows 使用 generated Task Scheduler XML + `schtasks /Create` / `schtasks /Query` / `schtasks /Delete` 走 apply / status / remove;Linux 維持 manual-review。`repair_schedule` 是 explicit user action,目前只清理 known macOS legacy LaunchAgent;typed issues / verification checks / manual steps 是 Schedule route 的正式 read model,UI 不應直接渲染 raw backend warning。Onboarding install path 復用同一組 commands,並在 archive 初始化完成後才 apply;skip path 不做 scheduler mutation。 | -| `preview_remote_backup`, `run_remote_backup`, `verify_remote_backup` | `remote-backup.*` | Settings remote backup | Preview / Manual / Execute / Verify 已完成;retention / prune 保持 manual-first。 | +| `export_app_data`, `preview_app_data_import`, `apply_app_data_import` | `migration.*` | Settings → Data migration | Whole-app Export packs the live project tree (config, archive databases via `sqlcipher_export`, derived projections, audit ledger, raw snapshots, intelligence + semantic sidecars) into a single `.pathkeep-bundle` zip with manifest sha256 anti-tamper. Preview validates the manifest + archive schema version (refuses bundles produced by _newer_ PathKeep builds). Apply requires explicit `confirmOverwrite`, preserves the previous tree as `.bak-` siblings, and runs forward schema migrations on the imported archive. App Lock secrets, scheduler artifacts, logs, diagnostics, staging, quarantine, and prior exports are deliberately excluded. Replaces the retired S3 remote-backup surface — PathKeep is local-first and never required a cloud-backup hand-off. | | `check_for_app_update`, `download_and_install_app_update`, `relaunch_after_update` | `app.update.*` | Settings updater review / install | 2026-04-10 起改由 typed desktop command surface 驅動;前端不再直接碰 updater / process plugin。`browser-desktop-bridge` 可透過 mirrored command transport 驗證 install / relaunch,download / install progress event 仍是 best-effort Tauri-only。 | | `test_ai_provider_connection`, `load_ai_queue_status`, `run_ai_queue_jobs`, `replay_ai_job`, `cancel_ai_job`, `build_ai_index`, `search_ai_history`, `ask_ai_assistant`, `load_ai_assistant_job`, `run_core_intelligence_now`, `queue_core_intelligence_rebuild`, `load_intelligence_runtime`, `retry_intelligence_job`, `cancel_intelligence_job`, `get_sessions`, `get_session_detail`, `get_search_trails`, `get_trail_detail`, `get_navigation_path`, `get_hub_pages`, `get_search_engine_ranking`, `get_top_search_concepts`, `get_query_families`, `get_top_sites`, `get_domain_trend`, `get_refind_pages`, `get_compare_set_detail`, `explain_refind`, `explain_entity`, `get_activity_mix`, `get_activity_mix_trend`, `get_digest_summary`, `get_stable_sources`, `get_search_effectiveness`, `get_friction_signals`, `get_reopened_investigations`, `get_domain_deep_dive`, `get_day_insights`, `get_browsing_rhythm`, `get_discovery_trend`, `get_intelligence_embed_cards`, `get_intelligence_widget_snapshot`, `get_intelligence_public_snapshot`, `preview_intelligence_local_host`, `build_intelligence_local_host`, `get_on_this_day`, `get_breadth_index`, `get_habit_patterns`, `get_interrupted_habits`, `get_path_flows`, `get_observed_interactions`, `get_compare_sets`, `get_multi_browser_diff`, `preview_ai_integrations`, `clear_derived_intelligence` | `intelligence.*` / `jobs.*` | Assistant、Intelligence、Explorer、Jobs、Settings、MCP | 2026-04-15 起,主產品 deterministic contract 已從 legacy `load_insights()` / `explain_insight()` hard-cut 到 Core Intelligence query surface。後續又補上 Phase 3 / 4 deterministic query surface、generic `explain_entity`、embed/widget/public snapshot payload providers,以及 profile-scoped staged queue follow-up;`WORK-CI-I` 再把第一個 reusable trusted local host `browser-snippet-v1` 接進 typed desktop commands,讓 Settings 可 preview/build/verify `index.html` + `bundle.json` artifact,而不必重建另一套 payload story。2026-04-19 的 `WORK-M6-A` 再新增 exact-day `get_day_insights`,把 `day` / `domain` 正式收斂成 shared entity route:`/intelligence/day/:date` 與 `/intelligence/domain/:domain` 都走同一套 scope / window / evidence grammar,而不是各卡片各自拼 Explorer deep-link。M8 follow-up 又新增 `get_compare_set_detail`、`flowId` / typed `path flow` steps,以及 trusted payload 的 structured entity targets,讓 compare-set promotion、focus highlighting 與 external-output app links 也進入同一條 desktop contract。最新 follow-up 也把 built-in modules 收斂成 trait-backed registry,讓 rebuild stage ownership、runtime module status、settings defaults 與 explainability ownership 共用同一個 catalog,所以 runtime review 現在必須以 Core Intelligence modules / jobs / rollups / staged rebuilds 誠實呈現,而不是再回頭依賴舊 `insight_*` snapshot 或把所有 rebuild 混成單一 `full-rebuild`。2026-04-19 M10 follow-up 再把前端 API、Tauri command facade 與 worker bridge intelligence helpers 按 `ai` / `core` / `runtime` ownership split;command names 與 payload shape 維持不變,只降低 mega-file drift。 | | `open_path_in_file_manager` | support helper / artifact reveal | Audit、Import、Schedule、Security、Settings | 這是安全邊界內的 reveal/open helper,不等於檔案搬移或 retention policy。 | diff --git a/docs/design/app-wide-review-grammar-tradeoff.md b/docs/design/app-wide-review-grammar-tradeoff.md index f7f9e25c..3b6560af 100644 --- a/docs/design/app-wide-review-grammar-tradeoff.md +++ b/docs/design/app-wide-review-grammar-tradeoff.md @@ -101,7 +101,7 @@ M6–M10 已把 shared insights entity、route grammar、shared composition、 **M11-B 立即抽取** -- Settings remote backup PME tabs / verify rows +- Settings Data migration Export / Import preview surface (replaced the earlier S3-backed remote-backup PME, retired 2026-05-25) - Settings AI integration preview generated files / review rows - Integrations external-output local host generated artifact viewer - Schedule PME tab chrome、generated file preview、verify result rows diff --git a/docs/design/design-tokens.md b/docs/design/design-tokens.md index dd16ce76..e47a6709 100644 --- a/docs/design/design-tokens.md +++ b/docs/design/design-tokens.md @@ -1,100 +1,129 @@ # Design Tokens -> Source of truth for the M0 product shell token layer. -> Visual direction comes from `reference/PathKeep — Desktop UI Design/style.css`, then gets normalized here for production CSS variables and TS token helpers. +> Source of truth for the v0.3 **Paper + Archival** product shell token layer. +> Visual direction comes from `docs/design/handoff/paper-redesign/project/pk-tokens.css` (the design handoff package), then gets normalized here for production CSS variables and Tailwind v4 `@theme` mapping. > Typography trade-off 與 fallback policy 見 [typography-and-font-fallback.md](./typography-and-font-fallback.md)。 +> History: v0.2 brutalist tokens (square geometry, orange accent, dark default) are retired in this branch. The pivot rationale lives in `docs/dev/HANDOFF-2026-05-19-paper-redesign.md`. CSS-variable legacy aliases (`--bg` / `--text` / `--font-ui`) stay in `src/styles/tokens.css` while v0.2 routes get rewritten, then get deleted alongside the last consumer. --- ## Theme Contract -- Default theme: `dark` -- Secondary theme: `light` -- Accent policy: one action color only (`--accent`) -- Shape policy: brutalist, square geometry (`--radius: 0px`) +- Default theme: `light` (named "paper") +- Secondary theme: `dark` (named "darkroom") +- Accent policy: one action color only (`--accent` — slate blue, not orange) +- Shape policy: paper geometry — `--radius: 3px`, `--radius-pill` for status chips. **Not** brutalist 0 px anymore (see [[feedback-brutalist-radius]]). - Typography policy: - - primary UI chrome、dense labels 與 body copy 使用 `--font-ui` / `--font-body` - - true monospace 只保留給 path、ID、command 與純 evidence values,使用 `--font-code` - - `--font-mono` 僅保留為 legacy shell alias;新 UI 不再把 monospace 當預設字體 - - runtime 不可再依賴 remote font import;`html[lang]` 必須與目前 locale 對齊 - -## Color Tokens - -### Dark - -- `--bg` `#0A0A0A` -- `--bg-elevated` `#111111` -- `--bg-surface` `#161616` -- `--bg-hover` `#1A1A1A` -- `--border` `#2A2A2A` -- `--border-active` `#3A3A3A` -- `--text` `#C8C8C8` -- `--text-muted` `#6A6A6A` -- `--text-faint` `#3E3E3E` -- `--text-bright` `#E8E8E8` -- `--accent` `#FF7832` -- `--accent-dim` `rgba(255, 120, 50, 0.15)` -- `--accent-hover` `#FF944D` -- `--accent-glow` `rgba(255, 120, 50, 0.08)` - -### Light - -- `--bg` `#F3F0EA` -- `--bg-elevated` `#FAF8F4` -- `--bg-surface` `#FFFFFF` -- `--bg-hover` `#EBE5DC` -- `--border` `#D6CFC3` -- `--border-active` `#BDB3A5` -- `--text` `#2D251D` -- `--text-muted` `#6C6256` -- `--text-faint` `#9F9384` -- `--text-bright` `#16110C` -- `--accent` `#D85F21` -- `--accent-dim` `rgba(216, 95, 33, 0.14)` -- `--accent-hover` `#EC7437` -- `--accent-glow` `rgba(216, 95, 33, 0.08)` - -## Semantic Tokens - -- `--success` `#4ADE80` / light `#2F8F4B` -- `--warning` `#FBBF24` / light `#B37A00` -- `--error` `#F87171` / light `#C84D4D` -- `--info` `#60A5FA` / light `#0D73C7` + - editorial headings + body copy use `--font-serif` (Newsreader Latin subset bundled at runtime) + - UI chrome / dense labels use `--font-sans` (system sans for the active locale) + - ASCII evidence — paths, IDs, commands, mono badges — uses `--font-mono` (JetBrains Mono Latin subset bundled at runtime) + - **CJK always falls back to the system stack**. Bundled fonts ship Latin subsets only. + - `html[lang]` must follow the runtime locale (en / zh-CN / zh-TW). +- Material policy: ~2.8 % paper noise (light) / 4 % noise + soft-light blend (dark) + 12 % darkroom vignette in dark mode. Settings → Appearance can disable both. + +## Color Tokens (paper palette) + +### Light ("paper") + +- `--bg-paper` `#ece7de` — main page background (cream paper) +- `--bg-card-paper` `#f6f3ed` — paper-card backgrounds +- `--bg-page` `#fdfcf9` — elevated surface / palette / detail panel +- `--bg-hover` `rgba(0, 0, 0, 0.04)` — hover overlay +- `--bg-accent-soft` `rgba(61, 90, 128, 0.12)` — accent tint / highlight chip +- `--border-light` `#e6e2d8` — paper card subtle border +- `--border-default` `#d6d1c4` — main border +- `--ink` `#171513` — primary text +- `--ink-secondary` `#3c3a36` — secondary body text +- `--ink-muted` `#6a655c` — muted helper text +- `--ink-faint` `#9a948a` — faint metadata +- `--ink-ghost` `#bdb6a9` — ghost text +- `--accent` `#3d5a80` — slate blue, single accent color +- `--accent-text` `#2c4360` — accent text color (slightly darker for legibility on paper) +- `--accent-soft` (alias for `--bg-accent-soft`) +- `--danger` `#a03821` — destructive action / inline error +- `--danger-soft` `rgba(160, 56, 33, 0.12)` + +### Dark ("darkroom") + +- `--bg-paper` `#110f0d` — main page background +- `--bg-card-paper` `#191614` — paper-card backgrounds +- `--bg-page` `#201c18` — elevated surface / palette +- `--bg-hover` `rgba(255, 255, 255, 0.06)` +- `--bg-accent-soft` `rgba(122, 156, 199, 0.18)` +- `--border-light` `#26221e` — paper card subtle border +- `--border-default` `#322c26` — main border +- `--ink` `#ede7d8` — primary text +- `--ink-secondary` `#c7c0b1` — secondary body text +- `--ink-muted` `#8a8478` — muted helper text +- `--ink-faint` `#5d574d` — faint metadata +- `--ink-ghost` `#3f3a32` — ghost text +- `--accent` `#7a9cc7` — slate blue (lighter for darkroom) +- `--accent-text` `#9bb6d6` +- `--danger` `#d65f3f` +- `--danger-soft` `rgba(214, 95, 63, 0.18)` + +> The exact runtime values live in `src/styles/tokens.css`; this document is the contract those values implement, not a duplicate copy. If `tokens.css` and this file disagree, fix `tokens.css` to match this contract — not the other way around. + +## Semantic Tone Tokens + +Used by `StatusCallout`, paper alerts, and badge primitives: + +- success — accent-soft tint over `--ink` (success copy uses neutral text on tinted paper, not a green accent) +- info — same as accent-soft tint +- warning — `--danger-soft` background, `--danger` text/border +- danger / blocked — `--danger` border + `--danger-soft` background + +The paper aesthetic intentionally collapses success / info into the same accent-soft surface. The user has reading-life materials in front of them, not a build dashboard. + +## Typography Tokens + +- `--font-serif` `'Newsreader', Georgia, serif` +- `--font-sans` system sans stack via `system-ui`, with platform fallbacks +- `--font-mono` `'JetBrains Mono', ui-monospace, SFMono-Regular, monospace` +- Bundled font subsets via `@fontsource/newsreader` and `@fontsource/jetbrains-mono` (Latin only). +- `data-fonts="system"` on `` swaps to system fallback (Settings → Appearance toggle). ## Spacing And Density -- Grid stays on the prototype's 4px rhythm: - - `--space-1` `4px` - - `--space-2` `8px` - - `--space-3` `12px` - - `--space-4` `16px` - - `--space-5` `20px` - - `--space-6` `24px` - - `--space-8` `32px` - - `--space-10` `40px` - - `--space-12` `48px` -- Desktop shell density tokens: - - `--layout-sidebar-width` `220px` +The paper grid stays on the same 4 px rhythm as v0.2 but adds new layout tokens for the shell: + +- Spacing scale (`--space-1` … `--space-12`) unchanged from v0.2: 4 / 8 / 12 / 16 / 20 / 24 / 32 / 40 / 48 px. +- Layout shell density: + - `--layout-sidebar-width` `216px` (expanded), `56px` (collapsed) - `--layout-topbar-height` `52px` - - `--density-panel-padding` `20px` - - `--density-content-gap` `20px` + - `--layout-status-bar-height` `28px` + - `--density-card-padding` `18px` + - `--density-card-gap` `16px` +- Settings → Appearance "Density" toggle picks between `comfortable` (default) and `compact` (-2 px on each `--density-*` token). ## Motion -- Shared transition token: `--transition: 120ms ease` -- Motion remains restrained: - - navigation hover / active state - - shell and page fade-in - - CTA state emphasis only when it improves affordance +- `--transition` `120ms ease` — shared default. Hover / active state, palette open/close, sidebar collapse. +- `--transition-detail` `200ms cubic-bezier(0.2, 0, 0.2, 1)` — detail panel slide-in. +- Motion stays restrained. No bouncy animations, no spring physics. Editorial restraint matches the paper aesthetic. + +## Radius + +- `--radius` `3px` — paper card corners, button corners, input corners. Three pixels reads as "deliberate edge" without feeling brutalist. +- `--radius-pill` `9999px` — status chips, source-picker pill, palette mode badge. +- `--radius-tight` `2px` — tiny chips (heatmap cells, source-color swatches). ## Implementation Files - CSS variables: `src/styles/tokens.css` -- App shell styling: `src/styles/app.css` -- TypeScript token helpers: `src/lib/tokens.ts` +- Paper-specific texture / noise / animation tokens: `src/styles/paper.css` +- Tailwind v4 `@theme` mapping → token bridge: `src/styles/tailwind.css` +- Bundled fonts CSS: `src/styles/fonts.css` +- Paper preferences (theme / font / density / paperTexture) persistence: `src/lib/paper-preferences.ts` ## Usage Rules -- New shell/page work should consume CSS variables directly or through `src/lib/tokens.ts`. +- New shell / page work consumes paper tokens via Tailwind utilities (`bg-paper`, `text-ink`, `border-border-light`, `text-accent`, `bg-accent-soft`, `font-serif`, `font-mono`) — **not** the raw CSS variables. +- Tailwind utilities map to paper tokens through the `@theme` block in `tailwind.css`. New tokens always update both files together. - Do not reintroduce per-page color constants into components. -- If a new feature needs another token, update this document and `src/styles/tokens.css` together. +- v0.2 routes that still consume legacy aliases (`--bg`, `--text`, `--font-ui`, `--border-active`, etc.) are migration debt; aliases live at the bottom of `tokens.css` and get deleted once the last consumer is rewritten. +- Settings → Appearance is the single user-facing surface that writes paper preferences (theme / fonts / density / paperTexture); it goes through `applyPaperPreferences()` which is idempotent on `` attributes. + +## Status + +Accepted (2026-05-19) — paper redesign supersedes the v0.2 brutalist tokens. diff --git a/docs/design/handoff/README.md b/docs/design/handoff/README.md new file mode 100644 index 00000000..80096cba --- /dev/null +++ b/docs/design/handoff/README.md @@ -0,0 +1,62 @@ +# Design Handoff — Source of Truth + +This directory holds **immutable design source files** received from external +design tooling. They are the visual contract that the v0.3 paper redesign +implements. Treat them as **read-only reference**. + +## Current handoff + +- [`paper-redesign/`](./paper-redesign/) — Paper + Archival aesthetic (M16). + Original Claude Design (claude.ai/design) handoff bundle. Contains: + - `README.md` — handoff cover sheet from the design tool + - `project/PathKeep Redesign.html` — entry HTML loading all JSX + - `project/pk-tokens.css` — every CSS class used in the prototype (3,978 lines) + - `project/pk-app.jsx` — root shell composition + routing + tweaks + - `project/pk-components.jsx` — `PKSidebar` / `PKStatusBar` / `PKDetailPanel` / + `PKSearchPalette` / `PKHeatmap` / glyph set + domain colour helpers + - `project/pk-views.jsx` — `HomeView` (Dashboard) editorial layout + - `project/pk-contactsheet.jsx` — Browse view (contact sheet, day sticky, + domain stacks, session insights, hourly sparkline) + - `project/pk-browse-nav.jsx` — `CalendarPopover` / `DayNavControl` / + `YearRail` / archive density helper / placeholder day skeleton + - `project/pk-search.jsx` — three-mode search hero, filter chips, day-grouped + results, "See in context" jump + - `project/pk-intelligence.jsx` — KPI strip, topic timeline, domain rank + list, sessions, refind shelf, LLM-needed callouts + - `project/pk-assistant.jsx` — chat surface with evidence panel + sample + prompts (provider-gated in production) + - `project/pk-import.jsx` — method picker + stepper wizard + preview stats + - `project/pk-audit.jsx` — manifest chain visualization, runs table, storage + breakdown, snapshots, export panel + - `project/tweaks-panel.jsx` — design-tool tweaks panel (development only, + not shipped) + - `project/pathkeep-mark.svg` — brand mark + +## How agents should use this + +1. **Treat the HTML/JSX as visual law, not code to copy.** The handoff renders + in a browser via Babel-standalone; production builds with React 19 + TS + + Tailwind v4 + shadcn primitives. Re-derive class names, structure, and + tokens — don't transplant the prototype's React-18-via-UMD plumbing. +2. **`pk-tokens.css` is the visual rule book.** Every measurement, every + colour, every animation curve. When in doubt, grep here first; it lists + exact pixel sizes (e.g. sidebar `216px`, topbar `52px`, statusbar `28px`, + day sticky offset `var(--cs-toolbar-h, 44px)`). +3. **Functional depth may exceed the prototype.** The prototype mocks data + and skips many production concerns (i18n, AI provider gating, regex + validation, locked archive states, error boundaries). Backfill these + using the existing PathKeep production patterns. +4. **The Settings page is not in the prototype.** Build it with the same + visual language; see `src/pages/settings/` for existing sections. + +## Authoritative project docs (override these as the redesign progresses) + +- `docs/design/design-tokens.md` — token catalogue (paper palette is current) +- `docs/design/screens-and-nav.md` — route contract per screen +- `docs/design/ux-principles.md` — PME, trust grammar, loading rules +- `docs/design/ui-review-guardrails.md` — review heuristics +- `docs/design/typography-and-font-fallback.md` — three-font policy + +The v0.3 redesign is **authorised to override** these docs where it +contradicts the brutalist v0.2 design system; update each accepted doc as +the route sweep lands. diff --git a/docs/design/handoff/paper-redesign/README.md b/docs/design/handoff/paper-redesign/README.md new file mode 100644 index 00000000..e7e8396b --- /dev/null +++ b/docs/design/handoff/paper-redesign/README.md @@ -0,0 +1,25 @@ +# CODING AGENTS: READ THIS FIRST + +This is a **handoff bundle** from Claude Design (claude.ai/design). + +A user mocked up designs in HTML/CSS/JS using an AI design tool, then exported this bundle so a coding agent can implement the designs for real. + +## What you should do — IMPORTANT + +**Read the chat transcripts first.** There are 6 chat transcript(s) in `pathkeep-redesign-c/chats/`. The transcripts show the full back-and-forth between the user and the design assistant — they tell you **what the user actually wants** and **where they landed** after iterating. Don't skip them. The final HTML files are the output, but the chat is where the intent lives. + +**Read `pathkeep-redesign-c/project/PathKeep Redesign.html` in full.** The user had this file open when they triggered the handoff, so it's almost certainly the primary design they want built. Read it top to bottom — don't skim. Then **follow its imports**: open every file it pulls in (shared components, CSS, scripts) so you understand how the pieces fit together before you start implementing. + +**If anything is ambiguous, ask the user to confirm before you start implementing.** It's much cheaper to clarify scope up front than to build the wrong thing. + +## About the design files + +The design medium is **HTML/CSS/JS** — these are prototypes, not production code. Your job is to **recreate them pixel-perfectly** in whatever technology makes sense for the target codebase (React, Vue, native, whatever fits). Match the visual output; don't copy the prototype's internal structure unless it happens to fit. + +**Don't render these files in a browser or take screenshots unless the user asks you to.** Everything you need — dimensions, colors, layout rules — is spelled out in the source. Read the HTML and CSS directly; a screenshot won't tell you anything they don't. + +## Bundle contents + +- `pathkeep-redesign-c/README.md` — this file +- `pathkeep-redesign-c/chats/` — conversation transcripts (read these!) +- `pathkeep-redesign-c/project/` — the `PathKeep-redesign-c` project files (HTML prototypes, assets, components) diff --git a/docs/design/handoff/paper-redesign/project/PathKeep Redesign.html b/docs/design/handoff/paper-redesign/project/PathKeep Redesign.html new file mode 100644 index 00000000..d68e154a --- /dev/null +++ b/docs/design/handoff/paper-redesign/project/PathKeep Redesign.html @@ -0,0 +1,36 @@ + + + + + + PathKeep — Redesign Prototype + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/docs/design/handoff/paper-redesign/project/pathkeep-mark.svg b/docs/design/handoff/paper-redesign/project/pathkeep-mark.svg new file mode 100644 index 00000000..77787491 --- /dev/null +++ b/docs/design/handoff/paper-redesign/project/pathkeep-mark.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/docs/design/handoff/paper-redesign/project/pk-app.jsx b/docs/design/handoff/paper-redesign/project/pk-app.jsx new file mode 100644 index 00000000..5d27551c --- /dev/null +++ b/docs/design/handoff/paper-redesign/project/pk-app.jsx @@ -0,0 +1,295 @@ +/* ═══════════════════════════════════════════════════════════ + PathKeep Redesign — Main App Shell + Routing, status bar, notes/tags persistence, tweaks + ═══════════════════════════════════════════════════════════ */ + +const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ + "accentColor": "#3d5a80", + "darkMode": false, + "serifFont": "Newsreader", + "density": "comfortable", + "paperTexture": true +}/*EDITMODE-END*/; + +// localStorage helpers for note/tag persistence (prototype only) +const NOTES_KEY = 'pk.notes'; +const TAGS_KEY = 'pk.tags'; +function loadMap(key) { + try { return JSON.parse(localStorage.getItem(key) || '{}'); } catch { return {}; } +} +function saveMap(key, map) { + try { localStorage.setItem(key, JSON.stringify(map)); } catch {} +} + +function PathKeepApp() { + const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); + const [activeView, setActiveView] = React.useState('home'); + const [selectedEntry, setSelectedEntry] = React.useState(null); + const [showSearch, setShowSearch] = React.useState(false); + const [showDetail, setShowDetail] = React.useState(false); + const [sidebarCollapsed, setSidebarCollapsed] = React.useState(() => { + try { return localStorage.getItem('pk.sidebar.collapsed') === 'true'; } catch { return false; } + }); + const [searchInitialQuery, setSearchInitialQuery] = React.useState(''); + const [browseTargetDate, setBrowseTargetDate] = React.useState(null); + const [browseTargetEntry, setBrowseTargetEntry] = React.useState(null); + const [browseTargetSource, setBrowseTargetSource] = React.useState(null); + const [browseTargetQuery, setBrowseTargetQuery] = React.useState(''); + const [sourceFilter, setSourceFilter] = React.useState(null); + + // Navigate with optional payload (e.g. {date, entryId, source, query} for browse jump-to) + const handleNavigate = React.useCallback((view, opts) => { + if (view === 'browse' && opts) { + if (opts.date) setBrowseTargetDate(opts.date); + setBrowseTargetEntry(opts.entryId || null); + setBrowseTargetSource(opts.source || (opts.date ? 'on-this-day' : null)); + setBrowseTargetQuery(opts.query || ''); + } else if (view !== 'browse') { + setBrowseTargetDate(null); + setBrowseTargetEntry(null); + setBrowseTargetSource(null); + setBrowseTargetQuery(''); + } + setActiveView(view); + }, []); + + const clearBrowseTarget = React.useCallback(() => { + setBrowseTargetDate(null); + setBrowseTargetEntry(null); + setBrowseTargetSource(null); + setBrowseTargetQuery(''); + }, []); + + const handleToggleSidebar = React.useCallback(() => { + setSidebarCollapsed(prev => { + const next = !prev; + try { localStorage.setItem('pk.sidebar.collapsed', String(next)); } catch {} + return next; + }); + }, []); + + // Notes & tags state — keyed by canonical URL + const [notesMap, setNotesMap] = React.useState(() => loadMap(NOTES_KEY)); + const [tagsMap, setTagsMap] = React.useState(() => loadMap(TAGS_KEY)); + + // Apply theme + React.useEffect(() => { + const html = document.documentElement; + html.setAttribute('data-theme', tweaks.darkMode ? 'dark' : 'light'); + html.style.setProperty('--accent-color', tweaks.accentColor); + + const serifMap = { + 'Newsreader': "'Newsreader', Georgia, 'Noto Serif', serif", + 'Source Serif': "'Source Serif 4', Georgia, serif", + 'System Serif': "Georgia, 'Noto Serif', 'PingFang TC', serif" + }; + html.style.setProperty('--font-serif', serifMap[tweaks.serifFont] || serifMap['Newsreader']); + + if (!tweaks.paperTexture) { + html.style.setProperty('--noise-opacity', '0'); + html.style.setProperty('--vignette-opacity', '0'); + } else { + html.style.removeProperty('--noise-opacity'); + html.style.removeProperty('--vignette-opacity'); + } + }, [tweaks]); + + // Keyboard: ⌘K + React.useEffect(() => { + function handleKey(e) { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + setShowSearch((s) => !s); + } + if (e.key === 'Escape') { + if (showSearch) setShowSearch(false); + else if (showDetail) { setShowDetail(false); setSelectedEntry(null); } + } + } + window.addEventListener('keydown', handleKey); + return () => window.removeEventListener('keydown', handleKey); + }, [showSearch, showDetail]); + + const handleSelectEntry = (entry) => { + setSelectedEntry(entry); + setShowDetail(true); + }; + + const handleOpenFullSearch = (q) => { + setSearchInitialQuery(q); + setActiveView('search'); + }; + + const handleUpdateNotes = (value) => { + if (!selectedEntry) return; + const key = selectedEntry.url || selectedEntry.id; + const next = { ...notesMap, [key]: value }; + setNotesMap(next); + saveMap(NOTES_KEY, next); + }; + + const handleUpdateTags = (tags) => { + if (!selectedEntry) return; + const key = selectedEntry.url || selectedEntry.id; + const next = { ...tagsMap, [key]: tags }; + setTagsMap(next); + saveMap(TAGS_KEY, next); + }; + + const allVisits = React.useMemo(() => getAllVisits(), []); + + const currentKey = selectedEntry ? (selectedEntry.url || selectedEntry.id) : null; + const currentNotes = currentKey ? notesMap[currentKey] : ''; + const currentTags = currentKey ? (tagsMap[currentKey] || []) : []; + + // Page titles + const PAGE_META = { + home: { title: 'Home', subtitle: 'Welcome back' }, + browse: { title: 'Browse', subtitle: 'A day at a time' }, + search: { title: 'Search', subtitle: 'Find what you read' }, + insights: { title: 'Intelligence', subtitle: 'Patterns & threads' }, + assistant: { title: 'Assistant', subtitle: 'Chat about your past' }, + import: { title: 'Import', subtitle: 'Recover what was lost' }, + archive: { title: 'Archive', subtitle: 'Audit & integrity' }, + schedule: { title: 'Schedule', subtitle: 'Backup cadence' }, + settings: { title: 'Settings', subtitle: 'Preferences' } + }; + + const meta = PAGE_META[activeView] || PAGE_META.home; + + const renderContent = () => { + switch (activeView) { + case 'home': + return ; + case 'browse': + return ; + case 'search': + return ; + case 'insights': + return ; + case 'assistant': + return ; + case 'import': + return ; + case 'archive': + return ; + case 'schedule': + return ; + case 'settings': + return ; + default: + return ; + } + }; + + return ( +
+ setTweak('darkMode', !tweaks.darkMode)} + collapsed={sidebarCollapsed} + onToggleCollapse={handleToggleSidebar} /> + +
+ {/* Topbar */} +
+
+

{meta.title}

+ {meta.subtitle} +
+
+ + +
+
+ + {/* Content */} +
+ {renderContent()} +
+ + {/* Status Bar */} + +
+ + {/* Detail panel */} + {showDetail && selectedEntry && + { setShowDetail(false); setSelectedEntry(null); }} + onNavigate={(v) => { setShowDetail(false); setSelectedEntry(null); handleNavigate(v); }} + notes={currentNotes} + tags={currentTags} + onUpdateNotes={handleUpdateNotes} + onUpdateTags={handleUpdateTags} + /> + } + + {/* Search palette */} + {showSearch && + setShowSearch(false)} + onSelect={handleSelectEntry} + searchData={allVisits} + onOpenFullSearch={handleOpenFullSearch} /> + } + + {/* Tweaks Panel */} + + + setTweak('darkMode', v)} /> + setTweak('paperTexture', v)} /> + + + setTweak('accentColor', v)} + options={['#3d5a80', '#6b4c3b', '#8b4049', '#2d6a4f']} /> + + + setTweak('serifFont', v)} + options={['Newsreader', 'Source Serif', 'System Serif']} /> + + + setTweak('density', v)} + options={['comfortable', 'compact']} /> + + +
); +} + +Object.assign(window, { PathKeepApp }); diff --git a/docs/design/handoff/paper-redesign/project/pk-assistant.jsx b/docs/design/handoff/paper-redesign/project/pk-assistant.jsx new file mode 100644 index 00000000..66db8cba --- /dev/null +++ b/docs/design/handoff/paper-redesign/project/pk-assistant.jsx @@ -0,0 +1,161 @@ +/* ═══════════════════════════════════════════════════════════ + PathKeep Redesign — Assistant View + Chat with grounded evidence from your archive + ═══════════════════════════════════════════════════════════ */ + +const SAMPLE_PROMPTS = [ + 'What was that paper about transformers I read last spring?', + 'When did I first start reading about Tauri?', + 'Show me everything I read about SQLite in the last month.', + 'What rabbit hole was I down on the weekend of March 14?', + 'Pages I keep coming back to but never finished.', + 'Was I researching anything about gaussian splatting last year?' +]; + +const SAMPLE_CONVERSATION = [ + { + role: 'user', + content: 'When did I first start reading about Tauri? What did I read first?' + }, + { + role: 'ai', + content: ( + <> +

You first opened Tauri's GitHub repository on April 5, 2025 — about 13 months ago.

+

In the two weeks that followed, you visited:

+
    +
  • Tauri's "Getting Started" and "Architecture" docs (14 visits total)
  • +
  • Several Tauri-vs-Electron comparison posts
  • +
  • Rust's WebView2 binding threads on GitHub
  • +
+

The pace picked up sharply around March 2026, when you started PathKeep itself. Tauri docs are now in your top-5 most-revisited domains.

+ + ), + evidence: [ + { date: '2025-04-05', title: 'tauri-apps/tauri: Build desktop apps with web technology', domain: 'github.com', url: 'https://github.com/tauri-apps/tauri' }, + { date: '2025-04-06', title: 'Getting Started | Tauri', domain: 'v2.tauri.app', url: 'https://v2.tauri.app/start/' }, + { date: '2025-04-08', title: 'Tauri vs Electron: Real-world Performance Comparison', domain: 'blog.logrocket.com', url: 'https://blog.logrocket.com/tauri-vs-electron/' }, + { date: '2025-04-12', title: 'Architecture | Tauri v2', domain: 'v2.tauri.app', url: 'https://v2.tauri.app/concept/architecture/' } + ] + } +]; + +function AssistantView({ onSelectEntry }) { + const [messages, setMessages] = React.useState(SAMPLE_CONVERSATION); + const [input, setInput] = React.useState(''); + const scrollRef = React.useRef(null); + + React.useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + } + }, [messages.length]); + + const handleSubmit = (e) => { + e?.preventDefault(); + const q = input.trim(); + if (!q) return; + setMessages(prev => [ + ...prev, + { role: 'user', content: q }, + { + role: 'ai', + content: ( +

+ … searching your archive. (Stub in the prototype — a real local LLM would answer here, grounded in pages you visited.) +

+ ), + evidence: [] + } + ]); + setInput(''); + }; + + const handleSuggestion = (q) => { + setInput(q); + }; + + const isEmpty = messages.length === 0; + + return ( +
+
+ {isEmpty ? ( +
+
What would you like to remember?
+
+ I can read your archive and tell you what's in it.
+ Try one of these — or write your own. +
+
+ {SAMPLE_PROMPTS.map((p, i) => ( +
handleSuggestion(p)}> + {p} +
+ ))} +
+
+ ) : ( + messages.map((msg, i) => ( +
+ {msg.role === 'ai' && ( +
+ Local · llama 3.2 +
+ )} +
+ {typeof msg.content === 'string' ?

{msg.content}

: msg.content} + {msg.evidence && msg.evidence.length > 0 && ( +
+
Evidence · {msg.evidence.length} records
+ {msg.evidence.map((e, j) => ( +
onSelectEntry({ + id: `evidence-${j}`, + title: e.title, + domain: e.domain, + url: e.url, + fullDate: e.date, + time: '14:23', + type: 'link', + visitCount: 8 + })}> + {e.date} + {e.title} · {e.domain} +
+ ))} +
+ )} +
+
+ )) + )} +
+ +
+
+