Commit Graph

301 Commits

Author SHA1 Message Date
adlee-was-taken
446949c5ce fix(ext/popup): auto-popout for attachment types, keep login/note in popup
- Login and secure_note types stay in popup without attachment UI
- All other types (identity, card, key, totp, document) auto-redirect
  to full tab when selected
- Attachments only shown for login/secure_note when opened in tab

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:42:35 -04:00
adlee-was-taken
c59e6892d8 feat(ext/popup): add pop-out to tab for forms
Forms can now be opened in a full browser tab via the ⤴ button,
solving Chrome's popup closure on file picker interaction. Deep
linking via URL params preserves view, item type, and item ID.

Also removes the unused dropdown picker code from item-list.ts.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:32:39 -04:00
adlee-was-taken
39db697ce5 fix(ext/popup): replace item type dropdown with selection view
Clicking "+ new" now navigates to a type selection view instead of
showing a dropdown that gets clipped by popup bounds. The selection
view displays all item types as buttons in a scrollable list.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:21:14 -04:00
adlee-was-taken
eb14946f06 feat(ext/setup): add device name step to setup wizard
New step 4 after vault creation: enter device name (defaults to
"Chrome on Linux" based on detected browser/OS). Generates ed25519
keypair, stores private key in chrome.storage.local, registers
device with vault. Wizard is now 5 steps (was 4).

Also adds generate_device_keypair() to wasm.d.ts type declarations.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:04:10 -04:00
adlee-was-taken
abfc5aed42 feat(ext/popup): wire navigation for trash, devices, field-history screens
Adds View variants, render cases, teardown calls, and entry points
in settings menu for trash and devices.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:42:53 -04:00
adlee-was-taken
b55c59bd35 feat(ext/popup): add attachment cap setting to vault settings
Dropdown with 5/10/25/50 MB presets for per_attachment_max_bytes.
Other caps remain at defaults.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:37:43 -04:00
adlee-was-taken
2fa54e2144 feat(ext/popup): add "View history" link to login detail view
Shows button when item.field_history is non-empty. Navigates to
field-history screen with historyItemId set.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:32:16 -04:00
adlee-was-taken
3b4788e5dc feat(ext/popup): field history view — masked values with reveal toggle
Shows current + historical values for tracked fields (password/concealed).
Click to reveal, copy button per entry (plaintext stored in a module-level
Map, never embedded in the DOM). Grouped by field name if multiple tracked
fields exist. Adds historyItemId to PopupState and 'field-history' to View.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:23:54 -04:00
adlee-was-taken
7fe54472b3 feat(ext/popup): devices view — list devices with revoke actions
Shows registered devices with "← you" indicator on current device.
Revoke button on other devices. Unregistered banner if current
device not in list.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:19:59 -04:00
adlee-was-taken
9fbf9bb3ee feat(ext/popup): trash view — list trashed items with restore/purge
Shows trashed items sorted newest-first with restore buttons.
Empty trash button purges all items + orphan blobs. Header shows
count and days until oldest auto-purges.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 19:28:56 -04:00
adlee-was-taken
39a8e12438 feat(ext/sw): get_field_history handler
Decrypts item and calls WASM get_field_history to extract tracked
field history for the popup's history view.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 17:49:59 -04:00
adlee-was-taken
d2cb6d8461 feat(ext/sw): trash operations — listTrashed, restoreItem, purgeItem, purgeAllTrash
listTrashed filters manifest for trashed_at != null, sorted newest-first.
restoreItem clears trashed_at. purgeItem deletes item + attachments.
purgeAllTrash also scans for orphan blobs in attachments/ directory.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 15:57:08 -04:00
adlee-was-taken
0003c3e658 feat(ext/sw): device management — devices.ts + router handlers
Adds readDevices, addDevice, revokeDevice helpers that read/write
.relicario/devices.json. Router handlers: list_devices, add_device,
revoke_device.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 15:53:08 -04:00
adlee-was-taken
5a001a805c feat(ext/shared): add Device + FieldHistory types + 8 new message types
Device: name, public_key (hex), added_at.
FieldHistoryView: field_id, field_name, current_value, entries[].
Messages: list_devices, add_device, revoke_device, list_trashed,
restore_item, purge_item, purge_all_trash, get_field_history.

Also adds stub cases in popup-only.ts switch to keep tsc happy until
Tasks 3-5 wire up the real handlers.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 15:49:01 -04:00
adlee-was-taken
caebe9f97e feat(wasm): add generate_device_keypair + get_field_history bindings
generate_device_keypair returns an ed25519 keypair as JSON with hex pubkey
and base64 private key. get_field_history extracts tracked field history
from a decrypted item for the popup's history view.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 15:44:04 -04:00
adlee-was-taken
af050f176c docs(plan): Plan 1C-γ₂ — device registration + trash + history + caps
13 tasks, bottom-up layering:
1. WASM bindings (generate_device_keypair, get_field_history)
2. Shared types + messages
3-5. Service worker handlers (devices, trash, field history)
6-8. Popup screens (trash, devices, field-history)
9. Item detail "View history" link
10. Vault settings attachment cap
11. Popup navigation wiring
12. Setup wizard device name step
13. Manual browser testing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-26 15:39:19 -04:00
adlee-was-taken
3372358b31 docs(spec): Plan 1C-γ₂ — device registration + trash + field history + attachment caps
Four features completing Plan 1C: device ed25519 keypair registration
during setup wizard, device management UI, trash view with restore/purge
(including orphan blob cleanup), per-item field history view, and
per-attachment size cap setting in vault settings.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-26 15:32:28 -04:00
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
b9c495cdea fix(ext/sw): clarify cap layering + harden download path
Two small follow-ups from code review of 5217d04:

1. Document the cap-enforcement layering in the upload handler. SW
   enforces per_attachment_max_bytes via WASM (defense-in-depth);
   per_item_max_count and per-vault caps are enforced client-side
   in the popup (Task 7's attachments-disclosure).

2. Use ref.id (the validated value found on the item) instead of
   msg.attachmentId for blobPath construction in download_attachment.
   Eliminates a theoretical path-traversal surface even though the
   handler is popup-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:11:49 -04:00
adlee-was-taken
5217d04034 feat(ext/sw): upload_attachment + download_attachment router handlers
Both popup-only. upload_attachment encrypts via WASM, putBlobs via
GitHost (Git Data API fallback for >900 KB), persists the AttachmentRef
on the item + manifest summaries. Duplicate uploads (same content =
same id from sha256) return the existing ref without a re-upload.
download_attachment reads + decrypts and returns plaintext bytes for
the popup to wrap in a Blob. 4 new router tests (accept × 2, reject × 2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:04:06 -04:00
adlee-was-taken
559c881dca feat(ext/sw): vault helpers for attachment add/remove
addAttachmentToItem appends an AttachmentRef + re-syncs the manifest
entry's attachment_summaries. removeAttachmentsFromItem returns the
removed refs so the caller can deleteBlob() the underlying bytes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:57:14 -04:00
adlee-was-taken
27ca91234f feat(ext/sw): GiteaHost.putBlob with Git Data API fallback
Same shape as GitHubHost (commit dc660c4) — Gitea v1 has /api/v1/
prefix, otherwise the endpoint shapes are identical. 2 new tests;
total 5 git-host tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:46:02 -04:00
adlee-was-taken
dc660c4ce8 fix(ext/sw): consistent error detail across all 6 putBlob throw paths
The two GET steps (get-ref, get-commit) used resp.statusText, which is
often empty on HTTP/2. Now they read resp.text() like the other 4 throw
paths so every error message includes GitHub's response body for
debugging.

Plus a test assertion for calls[2] in the Git Data API path so a
transposition of GET ref / GET commit would be caught.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:42:19 -04:00
adlee-was-taken
63fcfae72c feat(ext/sw): GitHubHost.putBlob with Git Data API fallback
Blobs ≤ BLOB_THRESHOLD_BYTES (900 KB) take the Contents API path
(same as writeFile). Larger blobs use the Git Data API: POST blob,
GET ref + commit, POST tree (with base_tree), POST commit, PATCH ref.
Tests cover both paths plus error propagation.

getBlob/deleteBlob are thin wrappers over readFile/deleteFile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:36:10 -04:00
adlee-was-taken
511d533de0 feat(ext/sw): extend GitHost interface with putBlob/getBlob/deleteBlob
Adds the three blob ops to the interface and a BLOB_THRESHOLD_BYTES
constant. Both GitHubHost and GiteaHost ship temporary stubs so the
build stays green until tasks 3-4 fill in real implementations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 09:46:24 -04:00
adlee-was-taken
71c182af9a fix(ext/shared): correct AttachmentCaps field names to match Rust core
The previous commit (f963ae3) used per_item_max_bytes and per_vault_*_max_bytes
which don't match the Rust core's struct (per_item_max_count and
per_vault_*_cap_bytes). Also fixes the per-item semantics: it's a COUNT of
attachments per item, not a byte sum.

Spec and plan docs updated in-place so future Task 7 cap-enforcement
implementation uses the correct names + semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 09:42:51 -04:00
adlee-was-taken
f963ae33af feat(ext/shared): tighten VaultSettings.attachment_caps to AttachmentCaps
All four cap fields optional; undefined means uncapped. γ₁ enforces;
γ₂ adds the configuration UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 01:54:40 -04:00
adlee-was-taken
0589fe3123 docs(plan): Plan 1C-γ₁ — attachments + Document type implementation
11 tasks, ~10 commits. Bottom-up layering:
- T1: tighten AttachmentCaps type
- T2: GitHost interface extension (putBlob/getBlob/deleteBlob)
- T3: GitHubHost impl with Git Data API fallback + tests
- T4: GiteaHost impl + tests
- T5: SW vault helpers (addAttachmentToItem, removeAttachmentsFromItem)
- T6: SW router handlers (upload/download_attachment) + tests
- T7: shared attachments-disclosure component + CSS + tests
- T8: wire disclosure into 6 type forms + 📎 list indicator
- T9: Document type form + signature-block detail + CSS + tests
- T10: dispatcher routes Document
- T11: build + verify + manual smoke

Test count target: 145 (was 128 + ~17 new across git-host, router,
disclosure, document.save).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 01:52:20 -04:00
adlee-was-taken
6f5ef43fe1 docs(spec): Plan 1C-γ₁ — attachments + Document type
Wires Rust attachment-encrypt surface into the extension. Adds GitHost
putBlob/getBlob/deleteBlob ops with Git Data API fallback for blobs
>900 KB (Contents API base64-bloats and rejects past ~1 MB). Adds the
Document item type (deferred from β₁ — needs primary_attachment).

UX: compact disclosure for attachments on every typed-item form (matches
β₂ custom-fields pattern). Image-mime rows get 16×16 thumb-icons (lazy
decrypt + object-URL lifecycle). Document detail promotes the primary
attachment to a gold "signature block" matching Totp's pattern. Item-list
gets a 📎 indicator (no count) for items with attachments.

γ₂ (later) covers trash + field-history + device + caps UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 01:43:54 -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
3c0f8d2c5c docs(plan): generator UX redesign — inline panel + trigger
4 tasks, ~3 commits. Task 1 polishes labels (lowercase + gold *).
Task 2 git-mvs the popover module to generator-panel. Task 3 rewrites
the panel with new API (parent + trigger + context), updates both
callers (login.ts, settings-vault.ts) for  + inline mount, swaps
CSS, adapts existing tests + adds 3 new ones (aria-expanded, auto-gen,
Escape). Task 4 verifies build + tests + manual smoke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:13:43 -04:00
adlee-was-taken
9add305a10 docs(spec): generator UX redesign — inline panel + trigger
Replaces the right-anchored popover (which clips off the popup edge)
with an inline panel that injects into the form below the password row.
Trigger becomes a  icon button (gold-bg). "save default" demoted to
secondary link; single gold "use" CTA. Bundles label-casing polish
(drop CAPS LOCK, gold required marker) since .label is shared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:06:56 -04:00
adlee-was-taken
f32fe93202 feat(ext/setup): sweep inline colors for palette refresh
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:23:34 -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
976db85a45 feat(ext/popup): swap blue accent palette for burnished gold
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:10:03 -04:00
adlee-was-taken
61b16779ab fix(icons): cap PNG bit depth at 8 per channel
ImageMagick defaults to 16-bit/channel; web/extension icons should be
8-bit/channel. Cuts ~30-40% off each icon's file size with zero visual
difference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:05:20 -04:00
adlee-was-taken
5e04fcf1ca feat(icons): regenerate PNGs from refreshed SVG masters
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:01:36 -04:00
adlee-was-taken
ae6b025435 feat(icons): replace 16px logo with bare medallion variant
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:54:19 -04:00
adlee-was-taken
a3f13fd2af feat(icons): replace master logo with reliquary theca + fleur
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:50:18 -04:00