Commit Graph

101 Commits

Author SHA1 Message Date
adlee-was-taken
ab36dbd31a feat(ext/popup): wire Document type into form + detail + list dispatchers
Document is no longer 'coming soon' — the type chooser unlocks it,
form dispatcher routes to documentType.renderForm, detail dispatcher
routes to documentType.renderDetail. teardown chains include documentType.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:46:26 -04:00
adlee-was-taken
9c481422ad fix(ext/popup): revoke object URLs in Document detail teardown
Two leaks from 705b171:
1. Lazy-load thumb for image-mime primary attachments created
   URL.createObjectURL but never revoked. Now tracked in a
   module-level registry, revoked on teardown.
2. 🔍 preview toggle's object URL same issue. Now tracked, revoked
   on teardown + on toggle-off (when user clicks the preview button
   to collapse).

Download button's URL (already self-cleaning via setTimeout) left
untracked — no change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:41:34 -04:00
adlee-was-taken
705b171553 feat(ext/popup): Document item type — form + signature-block detail
Form requires title + primary_attachment; the primary-row picker is
compact in edit mode (dashed-border when empty, filename row when
filled). Detail view promotes the primary to a gold signature block
(48×60 thumb + filename + meta + ↓ download · 🔍 preview). For image-
mime primaries, the thumb lazy-loads via decrypt + object-URL; the
preview button toggles an inline expanded view.

Supplementary attachments use the standard compact disclosure (Task 7)
when present.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 18:58:52 -04:00
adlee-was-taken
6ef7aaca53 feat(ext/popup): wire attachments disclosure into 6 type forms + 📎 list indicator
Each existing type form (Login, SecureNote, Identity, Card, Key, TOTP)
renders + wires the attachments-disclosure in both edit and view modes.
Form save reads from attachmentsDraft; teardown revokes any image
object URLs. Item-list rows show a 📎 glyph for items with at least
one attachment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:33:21 -04:00
adlee-was-taken
dcb1590391 fix(ext/popup): guard against sendMessage returning undefined; doc re-wire contract
Two follow-ups from code review of c5f0449:

1. In MV3 the SW can be killed mid-message; sendMessage then resolves
   to undefined. Add `(!resp || !resp.ok)` guards at 4 call sites
   (fetchThumbUrl, settings fetch, upload, download) plus optional
   chaining on error accessors.

2. JSDoc on wireAttachmentsDisclosure documents the "call once per DOM
   instance" contract — Task 8's re-wire pattern works because it
   replaces outerHTML before re-attaching, destroying old listeners
   via GC.

Module-level objectUrlRegistry concern (concurrent disclosure
instances) deferred — current popup architecture renders one item at
a time, so the issue doesn't manifest today.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:23:42 -04:00
adlee-was-taken
c5f0449843 feat(ext/popup): attachments-disclosure shared component
Compact disclosure rendering attachment rows with an action column
(× in edit, ↓ in view). Image-mime rows lazily decrypt + show a 16×16
thumb via object URLs; teardown revokes them on disclosure close. Edit
mode adds a "+ attach file" button wired to a hidden file input that
checks vault caps client-side before sending upload_attachment to SW.
6 new tests; total ~143.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:16:57 -04:00
adlee-was-taken
6904f729dc fix(ext/popup): update stale generator-popover mock names in settings-vault test
The mock in settings-vault.test.ts referenced the old function names
openGeneratorPopover and closeGeneratorPopover, which were renamed to
openGeneratorPanel and closeGeneratorPanel during the refactor. Update
the mock to use the current function names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 01:18:38 -04:00
adlee-was-taken
010c4263ba fix(ext/popup): stop Escape from leaking past the generator panel
Two related bugs from the gen-panel rewrite (ac15f06):

1. Escape key was bubbling to view-level keydown handlers in login.ts
   and settings-vault.ts, causing the press that closed the panel to
   also navigate the user away from the form/settings. Fix: call
   e.stopPropagation() in the panel's escHandler before closing.

2. settings-vault.teardown() didn't close any open generator panel,
   leaving the panel's escHandler registered and activePanel state
   stale across view transitions. Fix: call closeGeneratorPanel()
   first in teardown.

Plus a configure-defaults context test for the action-row composition
(no use/cancel buttons in that context).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:36:10 -04:00
adlee-was-taken
ac15f060e9 feat(ext/popup): rewrite generator as inline panel with trigger
The popover (which clipped off the popup edge) becomes an inline panel
that mounts inside the form (login.ts) or settings section
(settings-vault.ts). Trigger button is  with aria-expanded toggling.
Action row varies by context: fill-field has cancel+use; configure-
defaults has only the save-default link. Escape key closes the panel.
Tests adapted to new API; 3 new tests for aria-expanded, auto-generate,
and Escape behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:30:55 -04:00
adlee-was-taken
b03058abd9 refactor(ext/popup): update import paths after generator-popover → generator-panel rename
Update all import statements to reference the new generator-panel module name.
- generator-panel.test.ts: update internal import
- settings-vault.test.ts: update mock import
- settings-vault.ts: update import
- types/login.ts: update import

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:21:00 -04:00
adlee-was-taken
c9cd3696ae refactor(ext/popup): rename generator-popover module to generator-panel
Pure rename via git-mv (preserves history). Function names and behavior
unchanged. Sets up the API rewrite in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:20:50 -04:00
adlee-was-taken
083b01aa91 feat(ext/popup): lowercase form labels + gold required marker
.label drops text-transform: uppercase and tightens letter-spacing.
The `*` required marker gets wrapped in <span class="req"> so it
picks up the gold accent color (matches palette refresh).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:15:44 -04:00
adlee-was-taken
bbafe7fb7e feat(ext): sweep inline blue/red colors to gold/theca-red
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:20:16 -04:00
adlee-was-taken
5bc75c9f8a feat(ext/popup): rename sig-block--blue to --gold for accuracy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:15:46 -04:00
adlee-was-taken
fba50b89e8 feat(ext/popup): ⚙ picker → device/vault settings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:32:07 -04:00
adlee-was-taken
15fcaf9797 feat(ext/popup): vault-settings screen (retention + generator + origin-ack revoke)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:31:17 -04:00
adlee-was-taken
531af03ff1 feat(ext/popup): login gen-btn opens generator popover; teardown closes it 2026-04-24 19:25:52 -04:00
adlee-was-taken
8a16482b9c feat(ext/popup): generator-popover component (Random + BIP39) 2026-04-24 19:24:19 -04:00
adlee-was-taken
6ba9ccfa4c fix(ext/popup): preserve unsupported-kind fields + totp expanded state
Two fixes from the T3+T4 code review:

C1 (Critical): renderSectionBlock previously rendered all fields
regardless of kind. For fields with kind url/date/month_year/totp/etc.
(from CLI-created items), the editor showed a blank value input; if
the user typed anything, the input handler cast the kind to the
wrong thing and silently overwrote the structured value with a
string — destroying data. Fix: filter editor to supported kinds
(text/password/concealed); key data-* attributes by field.id (not
by index) so handlers look up the correct field regardless of what
the render loop emitted. Unsupported-kind fields survive save
untouched. A small muted note "N fields of unsupported kind (edit
via CLI)" flags preserved entries. +2 tests.

I1 (Important): totp.ts's kind-toggle reRender read the module-
scope sectionsExpanded flag which was only updated on structural
mutations — so toggling the disclosure open without adding/removing
anything left the flag stale, and clicking Random/BIP39 collapsed
the disclosure. Fix: read data-expanded from the live DOM before
innerHTML swap.
2026-04-24 18:51:23 -04:00
adlee-was-taken
e1d32b0379 feat(ext/popup): wire custom-field editor into all 6 type forms
Each typed-item form now mounts the collapsible sections editor before
the form-actions. Save functions accept sectionsDraft and persist it
via Item.sections so custom fields round-trip correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:17:22 -04:00
adlee-was-taken
3264cccb60 feat(ext/popup): renderSectionsEditor + wireSectionsEditor helpers
Adds the collapsible custom-fields editor (disclosure toggle, add/remove
sections + fields, in-place label/value mutation). Module-level helpers
only: caller owns the sectionsDraft and triggers rerender on structural
changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:10:09 -04:00
adlee-was-taken
553d9d7ca9 feat(ext/popup): render custom sections in all 6 type detail views 2026-04-24 10:35:46 -04:00
adlee-was-taken
3f12543c81 feat(ext/popup): renderSections helper for custom-field detail rendering 2026-04-24 10:28:10 -04:00
adlee-was-taken
706051530e fix(ext/popup): bind form escHandlers to teardown to stop listener leak 2026-04-23 23:09:52 -04:00
adlee-was-taken
23759dc163 feat(ext/popup): + New picker with all 7 item types (Document disabled) 2026-04-23 23:07:33 -04:00
adlee-was-taken
3c0b4c1589 fix(ext): get_totp handles Totp items, not just Login
Critical bug caught in T8 code review: the SW's get_totp handler
gated on core.type === 'login' and referenced core.totp, so the
standalone Totp item type (which lands in T8 with core.type === 'totp'
and core.config) had its detail-view ticker silently rejected with
'no_totp' every second. Ticker swallowed the error; rotating code
display stayed at placeholder forever.

Fix: extend the handler to resolve TotpConfig from either carrier:
- Login items: item.core.totp (optional subfield)
- Totp items:  item.core.config (required)

Also:
- Add 3 router tests covering both paths + the empty case
- Remove stale '……' placeholder check in types/totp.ts's \`t\`
  keyboard shortcut (dead code — the placeholder is '·····' or
  '······', never horizontal ellipses)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:04:27 -04:00
adlee-was-taken
673981379e feat(ext/popup): Totp view + form (countdown ring, Steam toggle)
Detail view renders a signature block with a large monospace rotating code and
a thin SVG countdown ring that sweeps via CSS transition. The ticker polls
get_totp every second and is stopped on teardown (back/edit/trash/Escape/e/d/t).

Form has a two-button kind toggle (TOTP / Steam Guard) that re-renders in place
while preserving entered values. TOTP uses digits=6 kind='totp'; Steam uses
digits=5 kind='steam'. Both default to algorithm='sha1' period_seconds=30.

Keyboard shortcuts on detail: Escape=back, e=edit, d=trash, t=copy-code.
Guarded against stealing keystrokes from editable targets.

Wires totp.renderDetail / totp.renderForm into both dispatchers and calls
totp.teardown() alongside the other types so tickers can't leak across views.

Closes T8 of the extension 1C-β1 plan (5/5 typed-item modules in place;
only T9 picker and T10 acceptance remain).
2026-04-23 22:54:49 -04:00
adlee-was-taken
e084790756 feat(ext/popup): Key view + form (concealed monospace signature block) 2026-04-23 22:42:48 -04:00
adlee-was-taken
560a3c63c4 feat(ext/popup): Card view + form (card-silhouette signature, MM/YY selects) 2026-04-23 22:39:21 -04:00
adlee-was-taken
113b0b690a feat(ext/popup): Identity view + form (profile-card signature block) 2026-04-23 22:29:04 -04:00
adlee-was-taken
99d689b9b0 feat(ext/popup): SecureNote view + form on shared helpers 2026-04-23 22:26:49 -04:00
adlee-was-taken
23d4f736e1 fix(ext/popup): close 3 critical regressions from slice-2 code review
- C1: escapeHtml now escapes " and ' so values stored in data-field-value
  attributes (concealed rows, copyable rows) round-trip correctly. Prior
  impl silently truncated passwords containing quotes. +3 regression tests.
- C2: centralize view-teardown. login.ts exports teardown() that stops
  the TOTP ticker and removes the active keydown handler; item-detail.ts
  and item-form.ts dispatchers call it before rendering the next view;
  each button handler also calls teardown() locally for belt-and-suspenders.
- C3: restore alpha's keyboard shortcuts on login detail view: c
  (copy username), p (copy password), t (copy TOTP), f (autofill), e
  (edit), d (trash), plus Escape (back). All gated by the
  is-editable-target guard so they don't eat keystrokes inside form fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:21:40 -04:00
adlee-was-taken
11c274053b refactor(ext/popup): extract Login to types/login.ts on shared helpers 2026-04-23 21:57:53 -04:00
adlee-was-taken
24a99ba07a feat(ext/popup): field-row + concealed-row + signature-block helpers 2026-04-23 21:55:36 -04:00
adlee-was-taken
9139dd78a0 fix(ext/popup): normalize url field + humanize cryptic error messages
Bug: typing "Test" into an add-login form's URL field produced
"item json: relative URL without a base: "Test" at line 1 column 227"
in the UI banner — a serde-internal error message that no user should
ever see.

Two fixes:

1. Client-side URL normalization in the add/edit Login form
   (item-form.ts:normalizeUrl):
   - Empty string stays empty (URL is optional).
   - Scheme-less inputs get "https://" prepended so "github.com"
     becomes "https://github.com".
   - The result is run through the JS URL constructor. If that rejects
     OR if the result has no host, show a targeted message like
     "URL must include a host (e.g. https://example.com)".
   - Prevents the Rust-side url::Url::parse failure from ever firing
     for a form-shaped error.

2. Popup-side error humanizer (popup.ts:humanizeError):
   - Applied inside sendMessage so every UI-visible error passes
     through it before the state banner gets the string.
   - Translates: "relative URL without a base" → "URL must start with
     https://...", generic "item json:" / "settings json:" → form-
     field or corruption messages, and the sender/origin gates
     (vault_locked, origin_mismatch, unauthorized_sender,
     tab_navigated, captured_tab_gone) to user-action prompts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:45:55 -04:00
adlee-was-taken
357455d979 fix(ext/popup): don't eat '/' and other keystrokes while typing in inputs
Bug: item-list's global "/" shortcut (focus search) and "+" shortcut
(new item) fired even when focus was inside any input/textarea other
than the list's own search field. This ate forward-slashes typed into
the setup wizard's host-url field and the add/edit form's notes area,
and would have done the same for any printable shortcut in a future
text field.

Root cause: the handler was attached to `document`, stays attached
when the user opens an item (and its click-handler navigated without
removing the listener), and only excluded the search field by id.

Fix:
- Add isEditableTarget() helper — returns true for
  INPUT/TEXTAREA/SELECT and contenteditable elements. Global shortcut
  handlers bail early when this fires, passing the keystroke through
  to the field.
- Apply the same guard in item-detail.ts (previously only guarded
  against INPUT, missing TEXTAREA + contenteditable).
- Remove handleListKeydown on row-click so it doesn't linger on
  detail/edit views even before the route-transition keydown
  listeners install.
- Escape in the list view still works from inside an editable
  field — only the printable-character interceptions are gated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:43:43 -04:00
adlee-was-taken
3238ef4dd4 refactor(ext/popup): remove last @ts-nocheck, align to typed-item types
Clears the final four transitional @ts-nocheck shields:
- popup.ts (already mostly updated in Slice 6 prior tasks; nocheck just
  removed and the init fallback switched to list_items / ItemId typing)
- unlock.ts (list_entries → list_items; ManifestEntry typing)
- settings.ts (RelicarioSettings → DeviceSettings; pure type rename, UX
  unchanged)

Also drops the stale `idfoto-extension` name in bun.lock (workspace was
renamed; lock file still carried the old name).

Verification:
  git grep -n '@ts-nocheck' extension/src/  → 0 hits
  bun run build + build:firefox             → both green
  bun run test                              → 52/52 passing
  cargo test --workspace                    → green

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:44:12 -04:00
adlee-was-taken
76bb61aa10 feat(ext/popup): Login add/edit form on typed-item API
Rewrites item-form.ts for the typed-item Item shape. Login is the only
editable type in Slice 6; other types fall through to coming-soon.

Form fields: title (required) + url + username + password (with gen
button backed by DEFAULT_PASSWORD_REQUEST) + totp (base32) + group +
notes. TOTP base32 is decoded via shared/base32 and wrapped as a
number[] into FieldValue-shape TotpConfig { secret, algorithm: sha1,
digits: 6, period_seconds: 30, kind: 'totp' }. Decode failure sets
state.error and aborts.

Save constructs a full Item envelope (id, title, type, tags, favorite,
group, notes, created, modified, trashed_at, core, sections,
attachments, field_history). On edit we preserve the existing item's
metadata but EXPLICITLY set trashed_at: undefined — carry-forward
from Slice 5 review M3, so an edit cannot accidentally preserve stale
trash state.

@ts-nocheck removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:12:14 -04:00
adlee-was-taken
bc95b047a2 feat(ext/popup): Login detail view + coming-soon for other types
Rewrites item-detail.ts to dispatch on item.type: login gets the full
detail view (url, username, masked password + copy, TOTP with 30s
countdown, notes, group, autofill/edit/trash/back buttons). Non-login
types get a coming-soon placeholder; those grow full UIs in later slices.

Fixes Slice 4 review I1: the old autofill path sent a malformed
fill_credentials payload ({ username, password } — no id/capturedTab).
The new handler uses the (capturedTabId, capturedUrl) pair snapshotted
at popup-open and calls fill_credentials with { id, capturedTabId,
capturedUrl }, matching the SW's handler signature that enforces the
M5 + TOCTOU checks.

TOTP poll now calls get_totp on a 1s timer and renders the 30s countdown
bar against expires_at. @ts-nocheck removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:10:41 -04:00
adlee-was-taken
dc8097589e feat(ext/popup): typed-item list view
Rewrites item-list.ts to render the typed-item ManifestEntry v2
surface: title + type-icon emoji (🔑/📝/🪪/💳/🗝/📄/⏱) + icon_hint
as the meta line. Toolbar now has +new, sync, settings, lock. Keyboard
nav unchanged (/, +, arrows, Enter).

Clicking a row fires list_items → get_item (the new typed-item
messages) and stores the full Item in state.selectedItem before
navigating to 'detail'.

Also updates popup.ts PopupState:
- entries now typed Array<[ItemId, ManifestEntry]>
- selectedEntry → selectedItem (Item)
- init() uses list_items not list_entries

Trashed items (trashed_at set) are filtered out of the visible list.
@ts-nocheck removed from item-list.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:09:28 -04:00
adlee-was-taken
d090fc421e refactor(ext/popup): rename entry-* → item-* components
Git-moves the three popup components so history survives the content
rewrite that follows in Tasks 22–24:
- entry-list.ts   → item-list.ts
- entry-detail.ts → item-detail.ts
- entry-form.ts   → item-form.ts

Also renames the exported render functions (renderEntryList →
renderItemList, etc.) and updates popup.ts imports + render switch.
The files still wear @ts-nocheck and reference the old Entry type;
content rewriting happens in Tasks 22–24.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:01:50 -04:00
adlee-was-taken
fbb64729ce feat(ext/popup): open setup via chrome.tabs.create, drop setup view from popup
The popup is too constrained for multi-step setup (Chrome closes it when
focus shifts to a file picker). Previously the popup rendered a pass-through
setup-wizard component that itself opened setup.html in a tab. Cut the
middleman: if not configured, directly chrome.tabs.create the setup page
and window.close() the popup.

- Remove 'setup' from the View union and the setup case from render().
- Delete setup-wizard component entirely — setup.html is the canonical flow.
- Drop renderSetupWizard import.

The @ts-nocheck stays on popup.ts until Slice 6 (item-* rewrites).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:33:49 -04:00
adlee-was-taken
c0fba2a8dc chore(ext): silence popup/content errors until slice 6 2026-04-20 19:57:32 -04:00
adlee-was-taken
519a6f0e36 chore: rename project from idfoto to relicario
Sweeping rename across crates, CLI binary, WASM bindings, extension, docs,
and vault metadata paths. Git remote updated to relicario.git.

- crates/idfoto-{core,cli,wasm} -> crates/relicario-{core,cli,wasm}
- IdfotoError -> RelicarioError
- IDFOTO_IMAGE env var -> RELICARIO_IMAGE
- ~/.config/idfoto -> ~/.config/relicario
- .idfoto/ vault metadata dir -> .relicario/ (breaking; pre-release)
- Binary name idfoto -> relicario
- Extension wasm module idfoto_wasm -> relicario_wasm
- Storage key idfotoSettings -> relicarioSettings
- All doc filenames and content references updated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:47:02 -04:00
adlee-was-taken
20ff1d9f47 feat: add logo and polish icon presentation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:44:04 -04:00
adlee-was-taken
ff19faff03 feat: add settings view with capture toggle and blacklist management
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:25:25 -04:00
adlee-was-taken
c50285c4a5 refactor: replace popup setup wizard with link to setup.html
The popup is too constrained for multi-step setup (file pickers
close it, fields duplicate the init wizard). Now it just shows
a single button that opens the full-page setup wizard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:55:07 -04:00
adlee-was-taken
4c26b4c534 fix: remove file picker from popup setup wizard
Chrome closes popups when file pickers steal focus. Instead, check
chrome.storage.local for an existing image (pushed by init wizard),
and redirect to the full-page setup.html if no image is found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:52:35 -04:00
adlee-was-taken
0551efe69e fix: avoid full re-render on image upload in setup wizard
Calling setState() after FileReader.onload triggered a full popup
re-render which could crash or close the popup with large images.
Update DOM elements in place instead, and add error handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:44:32 -04:00
adlee-was-taken
8093649757 fix: vault paths, TOTP caching, and keyboard nav on filtered list
- Fix .idfoto/ prefix for salt and params.json in vault.ts
- Cache TOTP secrets by entry ID to avoid re-fetching every second
- Fix keyboard navigation to use filtered entries, not unfiltered
- Add window.close() on Escape from entry list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 09:48:48 -04:00