Brand name uses capital R in user-facing text — extension UI strings,
CLI clap help / descriptions / error prose, markdown docs. Lowercase
preserved for the binary command, crate names, npm package, file
paths, env vars, and code identifiers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Expand get_active_tab_url protocol filter regex to include view-source:,
data:, devtools:, and other browser-internal/extension contexts that would
misbehave if autofilled. Add third regression test for view-source: URLs.
Wrap get_active_tab_url tests in dedicated describe block with beforeEach/
afterEach to snapshot/restore globalThis.chrome, preventing stub leakage
between tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
18 tasks across 8 phases covering all 8 form-level smart-input
affordances from spec section C (popup + fullscreen share login.ts) plus
CLI parity (rate, --totp-qr, completions + groups.cache). Cross-plan
coordination notes flag overlap with Phases 2B (recovery-QR) and 2C
(password coloring) — no conflicts, only shared APIs (rate_passphrase,
strength widget).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two implementation plans, one per spec landed in 00da7e7. Each plan
decomposes its spec into bite-sized TDD tasks with exact file paths,
complete code, and per-task commits.
- recovery-qr-and-entropy-floor.md (15 tasks, 6 phases): core crypto
module + wasm bindings + CLI subcommands (imgsecret embed, recovery-qr
generate/unlock, --force-weak-passphrase) + extension popup window
with canvas QR + vault-tab button + unlock-flow recovery link +
zxcvbn>=3 hard gate at init (CLI + setup wizard) + soft warning at
unlock for grandfathered weak vaults.
- password-coloring.md (9 tasks, 6 phases): pure colorizePassword()
utility + chrome.storage.sync round-trip + applyColorScheme() boot
step + four reveal-surface integrations (field history, popup item
detail, fullscreen item detail, generator preview) + settings UI
with color pickers and live-preview swatch. Task 6 (fullscreen)
flagged for coordination with in-flight Phase 1 UX work.
Both plans follow the subagent-driven execution preference per
feedback_subagent_default.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two design specs landed together because they're driven by the same
brainstorm session and target the same release window:
- 2026-05-01-recovery-qr-design.md: 1-of-2 disaster recovery via a
paper-or-photo QR carrying image_secret encrypted under Argon2id-of-
passphrase. Display-first UX (snap with phone), print as secondary.
Memory-only — architecturally no API path produces a file. Includes
domain-separation tag, type-level KDF params floor, shared NFC
normalization helper, and a passphrase entropy floor (zxcvbn >= 3)
enforced at vault init.
- 2026-05-01-password-coloring-design.md: 1Password-style character-
class coloring on revealed passwords (digits/symbols/letters with
user-customizable colors via chrome.storage.sync). Single shared
colorizePassword() helper, default scheme blue/red/inherit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
14 commits establishing the shared visual language for the fullscreen UX
redesign:
- New shared/glyphs.ts (10 monochrome glyph constants + REQUIRED_PILL_HTML).
- Color tokens (:root vars), :focus-visible ring, .req-pill, .form-header,
.form-subtitle in both popup/styles.css and vault/vault.css (kept identical).
- All 10 required-marker sites migrated from <span class="req">*</span> to
REQUIRED_PILL_HTML across the 7 type forms.
- Sidebar nav emoji replaced with glyph constants (vault sidebar + popup
settings panel).
- Popout-to-tab button gated on !isInTab() across 8 form files.
- Static "esc to cancel" subtitle below fullscreen form headers (suppressed
in popup); .form-header CSS owns spacing via :has(+ .form-subtitle).
- renderFormHeader({ titleText }) shared helper consumed by all 7 type forms.
- TYPED_FORMS shared list parameterizes 5 it.each test files for automatic
coverage of any new typed form.
268/268 tests pass; webpack production build clean. Foundation for Phase 2
(smart inputs), Phase 3 (three-pane shell + keymap + unsaved guard), and
Phase 4 (command palette + multi-select + drag-drop).
Plan: docs/superpowers/plans/2026-04-30-fullscreen-ux-phase-1-visual-foundation.md
Spec: docs/superpowers/specs/2026-04-30-relicario-fullscreen-ux-redesign-design.md
Whole-branch review recommendation: switch renderFormHeader's signature
from positional (titleText) to options ({ titleText }) so Phase 3 can
add 'dirty' (and any future hooks like a save-keybinding hint) without
touching all 7 call sites in lockstep with the unsaved-guard work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code-review feedback on Task 8: the conditional empty
<div style="margin-bottom:16px;"> spacer was an inline-styled magic
number and the 6-line header pattern was duplicated across all 7 typed
forms.
Now:
- .form-header class owns the bottom margin in both stylesheets.
- :has(+ .form-subtitle) selector drops the margin when a subtitle
follows, so spacing tokens stay in CSS instead of inline styles.
- renderFormHeader(titleText) shared helper collapses the 6-line
duplication to a one-liner per form. item-form.ts (type-selection
screen) is unaffected — it uses a different header structure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
All seven type forms plus the type-selection screen now show a small
'esc to cancel' subtitle under the heading when rendered in the
fullscreen vault tab (isInTab() === true). The subtitle is suppressed
in the popup, where esc has the more general meaning of closing the
popup. .form-subtitle class is shared between popup and vault
stylesheets so future hooks can reuse it.
Dynamic dirty-state ('unsaved · esc to cancel') wiring is deferred to
Phase 3 (unsaved-changes guard).
Plan 2026-04-30 fullscreen UX phase 1 task 8.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code-review feedback on Task 7: the same Array<[name, renderForm]> of
all 7 typed forms appeared in three test files (required-pill,
popout-button, popout-button-fullscreen). A new typed form would have
required updating all three.
Now defined once in __tests__/_typed-forms.ts. Future typed-form
additions get regression coverage automatically.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The ⤴ popout button is meaningless when the form is already in
vault.html — gate it on !isInTab(). Affects all seven type forms plus
the type-selection screen. Regression tests cover both popup (button
present) and fullscreen (button absent) contexts via it.each across
all 7 forms.
Plan 2026-04-30 fullscreen UX phase 1 task 7.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
▦ trash and ⌬ devices in the popup settings panel now match the
fullscreen sidebar's glyph language. Lowercased labels match the brand.
Plan 2026-04-30 fullscreen UX phase 1 task 6.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
▦ 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>