docs: implementation plan for vault-tab management surfaces revamp
12 tasks covering settings/devices/trash/history pane revamps, plus groundwork (glyph constants, relative-time util, ssh-fingerprint util, shared CSS classes) and routing/nav wiring. Tasks are TDD where the work is testable (utils) and bite-sized manual-smoke where it's UI. Spec corrections folded in: - Devices revoke is upgrade (text+confirm → glyph+inline), not greenfield - Fingerprint via webcrypto in extension, no SW shape change, no WASM - Routing keeps 'field-history' as internal dispatch key; only user-facing hash normalizes #field-history → #history for backward compat Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
Four "management" surfaces in the extension — **Settings**, **Devices**, **Trash**, and **field history** — all shipped in the 1C-β₂ / device-auth waves but in the *pre-fullscreen-redesign* visual language. They read as popup-derived forms stretched across the vault tab, with inconsistent typography, no glyph buttons, no focus rings, and no visual section grouping. Several functional gaps remain alongside the visual debt:
|
||||
|
||||
- **Settings**: per-device session-timeout config UI was specced in the vault-tab design (2026-04-27) but never built; the only way to change session behavior today is to edit `chrome.storage.local` directly.
|
||||
- **Devices**: the `revoke_device` SW message handler exists but no UI surfaces it — revocation is CLI-only. Device entries don't expose the SHA256 fingerprint (used for verifying against the server-side `devices.json`) or the `added_by` field that's already in `DeviceEntry`.
|
||||
- **Devices**: revocation works via a plain text "revoke" button + browser `confirm()` dialog — functional but inconsistent with the rest of the extension's UX. Device entries don't expose the SHA256 fingerprint (used for verifying against the server-side `devices.json`) or the `added_by` field that's already in `DeviceEntry`.
|
||||
- **Trash**: per-item purge countdown isn't shown — users see "trashed N days ago" but have to mentally add the retention window to figure out when it'll be gone.
|
||||
- **History**: the per-item field-history viewer (`field-history.ts`) is only reachable from an item detail page; there's no entry point to discover *which* items have history.
|
||||
|
||||
@@ -37,7 +37,7 @@ This spec applies the fullscreen visual-language tokens to all four panes and cl
|
||||
| Surface | Files touched | New? |
|
||||
|---|---|---|
|
||||
| Settings | `popup/components/settings-vault.ts`, `vault.css`/`popup.css` | modify |
|
||||
| Devices | `popup/components/devices.ts`, SW response shape | modify |
|
||||
| Devices | `popup/components/devices.ts`, new `shared/ssh-fingerprint.ts` | modify + 1 new util |
|
||||
| Trash | `popup/components/trash.ts` | modify |
|
||||
| History — index | `popup/components/item-history-index.ts` | **NEW** |
|
||||
| History — per-item | `popup/components/field-history.ts` | polish only (no rename) |
|
||||
@@ -85,7 +85,7 @@ extension/src/
|
||||
| List trashed, restore, purge | `list_trashed` / `restore_item` / `purge_item` / `purge_all_trash` | exists |
|
||||
| Per-item field history | `get_field_history` | exists (reused for index + per-item) |
|
||||
|
||||
**Single shape change:** extend `ListDevicesResponse` to include `fingerprint: string` per entry — SHA256 of the device's ed25519 public key, computed via the existing `core::device::fingerprint()` function. No new message round-trip.
|
||||
**No SW shape changes.** Fingerprint is computed client-side in `devices.ts` via `crypto.subtle.digest('SHA-256', …)` against the base64-decoded ed25519 key blob from `DeviceEntry.public_key`. Result is formatted as `SHA256:<base64-no-pad>` to match the SSH convention (and what `relicario device list` prints from `core::device::fingerprint()`). Pure extension change — no message round-trip, no WASM export, no Rust change.
|
||||
|
||||
### Shared CSS utility classes
|
||||
|
||||
@@ -393,16 +393,16 @@ TOTP_SECRET · 1 entry
|
||||
type VaultView =
|
||||
| 'list' | 'detail' | 'add' | 'edit'
|
||||
| 'trash' | 'devices' | 'settings' | 'settings-vault'
|
||||
| 'field-history' // existing — per-item view (internal dispatch key kept)
|
||||
| 'history' // NEW — index pane only
|
||||
| 'backup' | 'import'
|
||||
| 'history' // NEW — covers both index and per-item, payload distinguishes
|
||||
```
|
||||
|
||||
The existing `'field-history'` view value is **removed** from the union — the hash-parse layer normalizes any `#field-history/<id>` URL to `#history/<id>` before view resolution, so consumers only ever see the new value. The per-item component (`field-history.ts`) is unchanged in identity; only its dispatch key changes.
|
||||
The user-facing hash changes (`#history` is the new entry point, `#history/<id>` is the per-item view), but the internal dispatch keeps `'field-history'` for the per-item view to minimize the diff to working code. Normalization happens in `parseHash`:
|
||||
|
||||
Hash routes:
|
||||
- `#history` → index pane (`item-history-index.ts`)
|
||||
- `#history/<itemId>` → per-item view (`field-history.ts`)
|
||||
- `#field-history/<itemId>` → 301-style redirect to `#history/<itemId>` (one release of backward compat for any bookmarked URLs)
|
||||
- `#history` → `{ view: 'history' }` → index pane (`item-history-index.ts`)
|
||||
- `#history/<itemId>` → `{ view: 'field-history', id: <itemId> }` → per-item view (`field-history.ts`)
|
||||
- `#field-history/<itemId>` → rewritten to `#history/<itemId>` in the address bar, then resolved as above (one release of backward compat for any bookmarked URLs)
|
||||
|
||||
### Sidebar bottom-nav
|
||||
|
||||
|
||||
Reference in New Issue
Block a user