▦ trash · ⌬ devices · ⚙ settings · ⏻ lock — all imported from the new
shared/glyphs module so popup and fullscreen stay in sync. Regression
test scans the source for the old escape-coded emoji to prevent
backsliding.
Plan 2026-04-30 fullscreen UX phase 1 task 5.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code-review feedback on Task 4:
- Test expanded from login-only to it.each across all 7 type forms
(14 assertions total). A future revert to <span class="req">*</span>
in any form now fails CI.
- .label .req rule removed from popup/styles.css and vault/vault.css —
zero consumers after the REQUIRED_PILL_HTML migration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces ten <span class="req">*</span> sites across all seven type
forms with the shared REQUIRED_PILL_HTML snippet ('required' badge).
Adds a regression test pinning the new HTML in the login form.
Plan 2026-04-30 fullscreen UX phase 1 task 4.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code-review feedback on Task 3: vault button focus was the last
hardcoded #d2ab43 + bare :focus rule not yet migrated. Brings vault
button focus into parity with popup (which Task 2 already migrated)
and removes the last raw accent literal from the focus-related rules.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same :root block and .req-pill rule as popup/styles.css so the two
stylesheets share visual tokens. Vault input focus migrated to
:focus-visible + box-shadow ring.
Plan 2026-04-30 fullscreen UX phase 1 task 3.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Establishes :root CSS custom properties (accent, surfaces, status, focus
ring) and applies the focus ring to inputs/buttons via :focus-visible.
Adds .req-pill class used by Task 4 to replace the bare-asterisk required
marker. Existing .label .req kept for backward compatibility during the
migration window.
Plan 2026-04-30 fullscreen UX phase 1 task 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code-review feedback on Task 1: the _HTML suffix makes the 'this is raw
HTML, do not escape' contract obvious at every call site. Cheap to do
now (zero consumers); would be 8 diffs once Tasks 4-6 wire the constant
into the type forms.
Plan updated in lockstep so Task 4 references the new name.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Centralizes the unicode glyphs used by sidebar nav and form action buttons
so popup and fullscreen surfaces stay in sync. Includes the REQUIRED_PILL
snippet used to replace the trailing-asterisk required-field marker.
Plan 2026-04-30 fullscreen UX phase 1 task 1.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In the fullscreen UX, clicking '+ new item' set the hash to '#/add'
(no type) and called renderPane. The user then clicks a type button;
its handler calls setState({ newType: type }), which in vault.ts
triggers renderPane again. renderPane was unconditionally re-deriving
state.newType from the URL hash — clobbering the just-selected type
back to null. Result: the type-selection screen kept re-rendering and
no item could be created.
Fix: prefer route.type when present (deep-link case); otherwise keep
the in-memory state.newType. Same field order, same one-line touch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The wasm-bindgen binding for generate_device_keypair uses
serde-wasm-bindgen and returns a plain JsValue (object), not a JSON
string. Two consumers were calling JSON.parse on it, causing the
runtime error 'SyntaxError: "[object Object]" is not valid JSON' which
broke device registration end-to-end.
Fixes:
- wasm.d.ts: return type now { public_key_hex; private_key_base64 }
matching the rate_passphrase pattern (also a JsValue-returning
binding).
- popup-only.ts (register_this_device handler) and setup.ts (initial
device wire-up): drop JSON.parse, use the object directly.
- router.test.ts: pin the contract — mock generate_device_keypair as a
function returning an object (matching real binding behavior) and
assert register_this_device returns ok and forwards the public key.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eight bite-sized tasks for the visual baseline: shared/glyphs.ts module,
color-token & focus-ring CSS in popup and vault, .req-pill class, migration
of all ten required-marker sites and ten emoji glyph sites to the shared
constants, gating of the popout-to-tab button on !isInTab(), and a static
"esc to cancel" subtitle in fullscreen forms.
Each task pairs a failing test with a minimal implementation; ends with a
commit. Sets the visual language that phases 2-4 build on.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Captures the brainstorm output for the fullscreen vault tab: two-column login
form with sticky save bar, monospace-coherent glyph buttons, eight smart-input
affordances (fill-from-tab, hostname chip, group autocomplete, password reveal
& strength, TOTP live preview, TOTP-from-QR, notes monospace), and seven
power-user features (three-pane shell, keyboard nav, ⌘K palette, unsaved guard,
multi-select bulk ops, drag-drop attach, recent items).
Includes a CLI-parity section pairing each extension capability with its CLI
counterpart so the surfaces ship together.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- import_lastpass.rs: note that password and extra are intentionally
not trimmed (leading/trailing whitespace is significant for both).
- cmd_import_lastpass: document the coupling between the
ImportWarning message strings and the CLI summary's "skipped"
filter — partial-import warnings (TOTP/URL) must not contain
the word "skipped".
Comment-only; no behavior change. Catches I1 and M5 from the
final code review without taking on the cross-cut WarningKind
enum refactor (deferred to a follow-up if it ever ships).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Documents `relicario import lastpass <csv>` and the vault-tab
Import panel under Unreleased / Added.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mocks sendMessage. Covers: file-picker fires
parse_lastpass_csv, preview text matches the parsed counts,
confirm fires import_lastpass_commit with the parsed items,
warnings render after import, cancel clears the preview.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New vault.html#import panel with a file picker, parse-preview
("N logins, M notes, K skipped — proceed?"), confirm/cancel
buttons, inline progress, and a post-import warnings list. The
popup's settings-vault view links to it via a new
"LastPass CSV →" button next to "Backup & restore →".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
parse_lastpass_csv is a pure pass-through to the WASM bridge.
import_lastpass_commit re-mints each item's ID via
state.wasm.new_item_id() (same pattern as add_item), encrypts
and writes per-item via git.writeFile, then writes the manifest
last. Per-item commits + a final manifest commit — extension
GitHost has no atomic-batch API, so the single-commit semantics
the CLI provides aren't replicable here.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds parse_lastpass_csv (preview) and import_lastpass_commit
(write) to the popup-only message set, plus typed response
helpers. SW handlers + UI follow in Tasks 12-14.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Returns { items: [Item], warnings: [ImportWarning] } as a JSON
string. The items already have fresh IDs + timestamps; the SW
caller encrypts and writes them through the existing
item_encrypt + manifest_encrypt bridges.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Unlocks the vault, parses the CSV, encrypts each item, writes
items/<id>.enc and manifest.enc, then a single
`git add … && git commit` covers all of them. Stderr progress
every 50 items + final summary. Exit non-zero only when zero
items imported.
Adds the Import command group with a Lastpass subcommand.
Stub returns `not implemented` so the help text is reachable
ahead of the body landing in Task 8.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bad URLs in login rows downgrade to url: None with a warning
rather than skipping the row. Header mismatches (extra columns,
wrong order) surface ImportCsvHeader. Quoted commas, multi-line
extra, unicode all parse cleanly via the csv crate's defaults.
Rows with url == "http://sn" map to SecureNoteCore with extra
copied verbatim into the body. LastPass-packed structured data
(credit cards, addresses) flows through unparsed — users can
re-categorize manually post-import.
SecureNote rows skip the password-required check that applies
to Logins.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Successful base32 decode attaches a SHA1/6/30s Totp config to
LoginCore.totp. Bad base32 emits a warning and imports the login
without TOTP rather than skipping the row entirely.
Refactors map_row to return (Option<Item>, Option<ImportWarning>)
so a single row can produce both an item and a warning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Map LastPass grouping/fav/extra columns to relicario item metadata.
Grouping becomes item.group, fav="1" sets item.favorite, extra becomes item.notes.
Multi-line extra via CSV quoting round-trips correctly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pins the parse_lastpass_csv signature and ImportWarning shape.
A single LastPass row with name/url/username/password round-trips
to a Login item with a freshly-minted ID. Header validation
rejects shape mismatches with a clear message.
TOTP, grouping, fav, SecureNote rows, and error paths land in
Tasks 3-6.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds csv = "1" to relicario-core; introduces
ImportCsvHeader and ImportCsvFormat. Foundation for the
import_lastpass module landing in Task 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The repo uses bun (bun.lock present, no pnpm/npm available).
Replaces all pnpm references in the plan with bun equivalents.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
23 commits implementing the .relbak format (XChaCha20-Poly1305 +
Argon2id, zstd-compressed JSON envelope, opt-in image and git
history), the CLI 'relicario backup export/restore' commands, the
WASM bridge, the SW handlers, the vault-tab Backup & Restore panel,
and tests at every layer.
Final test sweep: cargo 0 failed (~150 Rust tests); vitest 205
passed (27 files); tsc clean.
Plan 3A: backup & restore — drives the feature branch landing in
the next commit (merge of feature/backup-restore).
Pre-v0.3.0 audit checklist: manual smoke-test list for the v0.2.x
audit-pass commits (TOTP edit, history, detach, status, generator
defaults, vault-tab parity, sync button) — to walk through before
the v0.3.0 tag.
Single set({vaultConfig, imageBase64?}) instead of two sequential sets,
so a partial-write window can't leave vaultConfig pointing to the new
remote while imageBase64 still references the old vault.
Unpacks .relbak via WASM, writes every vault artifact to the
user-specified fresh remote via writeFileCreateOnly (refuses to
clobber), and updates chrome.storage.local so subsequent unlocks
hit the restored vault. The reference image — when bundled — is
restored to imageBase64; otherwise the user keeps using their
existing reference.jpg.
Reads vault state via GitHost, calls pack_backup_json in WASM, returns
the .relbak bytes back to the panel for chrome.downloads.download.
Reference image inclusion comes from chrome.storage.local.imageBase64.
Git history is never bundled from the extension (CLI is the source of
full backups).
Locks the singular vs plural transition (1 minute ago vs 2 minutes
ago) and each bucket boundary (59→60s minutes, 3599→3600s hours,
86400→86400×2 days, etc.) so future tweaks can't silently regress
the user-facing labels.
Drop the dead `stdout.contains("last export:")` + `.to_lowercase()` fallback
in status_shows_last_backup_line and status_shows_recent_backup_after_export;
assert `stdout.contains("Last export:")` verbatim instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads .relicario/last_backup (written by cmd_backup_export). Format:
'never' for fresh vaults, '4 days ago' otherwise. Closes the
'is my backup stale?' question without leaving the terminal.
Refuses non-empty target, prompts for backup passphrase, writes the
full vault layout, untars .git/ when bundled or git-inits a fresh
'restore from backup <iso8601>' commit otherwise.
Also tightens error context on tar_directory's builder.finish().