release: v0.7.0 — extension restructure complete (Plan C Phases 3/4/6)
Completes the extension restructure begun in v0.6.0. Phases 3 (setup wizard SW migration + step registry), 4 (vault.ts split + vault_locked lift), and 6 (get_vault_status + sidebar status indicator) all merged to main (9df2fee,3b8368d,397cc78) via three parallel worktree streams. This commit is the release-prep wrap-up: - Version bump to v0.7.0 across the three relicario crates + Cargo.lock, extension/package.json, and both extension manifests (the manifests had lagged at 0.5.0 — corrected here). - CHANGELOG.md v0.7.0 entry. - STATUS.md: extension restructure moved to shipped; Phases 3/4/6 landing section added. - ROADMAP.md: v0.7.0 row added; Up-next now command palette. - extension/ARCHITECTURE.md: all three phases integrated (new vault-* modules, setup-steps.ts, get_vault_status protocol + status indicator, vault_locked lift, git-host sync cache). - Plan completion checkboxes ticked. Task 7.1 verification: done-criteria sweep all green; 423/423 vitest; build:all clean (only the pre-existing 4MB WASM size warning). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ Firefox build (the vault tab is Chrome-only for the moment). Verify in
|
||||
| `service-worker` | `src/service-worker/index.ts` | extension SW / bg | yes — initialized lazily on first message |
|
||||
| `popup` | `src/popup/popup.ts` | popup.html | no — goes through SW |
|
||||
| `vault` | `src/vault/vault.ts` (Chrome only) | vault.html (tab) | no — goes through SW |
|
||||
| `setup` | `src/setup/setup.ts` | setup.html (tab) | yes — direct dynamic import (predates SW handle) |
|
||||
| `setup` | `src/setup/setup.ts` | setup.html (tab) | no — goes through SW (`create_vault`/`attach_vault`) |
|
||||
| `content` | `src/content/detector.ts` | host page (top frame only by router check) | no |
|
||||
|
||||
### What each bundle owns
|
||||
@@ -183,17 +183,51 @@ before any new render.
|
||||
|
||||
### `src/vault/`
|
||||
|
||||
- `vault.ts` — fullscreen tab entry. Hash-based router (`#detail/<id>`,
|
||||
`#add/<type>`, `#trash`, `#devices`, `#settings`, `#settings-vault`,
|
||||
`#history`, `#history/<id>`, `#backup`, `#import`). Legacy
|
||||
`#field-history/<id>` URLs are normalized to `#history/<id>` on
|
||||
`parseHash` (`vault.ts:139-173`); the internal view value stays
|
||||
`'field-history'` so the per-item pane renders unchanged. Sidebar
|
||||
bottom-nav: `+ new item · ▦ trash · ⌬ devices · ⚙ settings · ◷ history
|
||||
· ⏻ lock`. Registers itself as the StateHost so all
|
||||
`popup/components/*` renderers run unchanged. Maintains its own
|
||||
`selectedItem` cache so hash navigation between already-loaded items
|
||||
doesn't refetch.
|
||||
- `vault.ts` (194 lines) — fullscreen tab entry, now a thin
|
||||
routing + state shell after the Phase 4 split. Registers itself as
|
||||
the StateHost so all `popup/components/*` renderers run unchanged,
|
||||
maintains its own `selectedItem` cache so hash navigation between
|
||||
already-loaded items doesn't refetch, and delegates DOM scaffolding,
|
||||
navigation, list/drawer/form rendering, and route dispatch to the
|
||||
sibling modules below. The hash-route set is
|
||||
`#detail/<id>`, `#add/<type>`, `#trash`, `#devices`, `#settings`,
|
||||
`#settings-vault`, `#history`, `#history/<id>`, `#backup`, `#import`.
|
||||
- `vault-context.ts` — the `VaultController` contract plus the shared
|
||||
types and pure helpers the split modules depend on. Added so the
|
||||
split is acyclic: the rendering modules import the controller
|
||||
interface from here rather than from `vault.ts`.
|
||||
- `vault-router.ts` — hash routing + pane dispatch + data loading,
|
||||
extracted to keep `vault.ts` ≤250 LOC. Owns `parseHash`; legacy
|
||||
`#field-history/<id>` URLs are normalized to `#history/<id>` here, but
|
||||
the internal view value stays `'field-history'` so the per-item pane
|
||||
renders unchanged.
|
||||
- `vault-shell.ts` — DOM scaffolding, color-scheme apply, and the
|
||||
`onMessage` wiring for the tab.
|
||||
- `vault-sidebar.ts` — sidebar categories nav, 80ms-debounced search
|
||||
(`SEARCH_DEBOUNCE_MS`), and the bottom-nav
|
||||
(`+ new item · ▦ trash · ⌬ devices · ⚙ settings · ◷ history · ⏻ lock`).
|
||||
Also owns the footer: a `#vault-status-slot` plus a manual `↻` refresh
|
||||
button (`GLYPH_REFRESH`). `wireSidebar` calls `refreshStatus()` once on
|
||||
mount and again on the button's click — sending `get_vault_status` via
|
||||
`ctx.sendMessage` and rendering the result into the slot through
|
||||
`vault-status.ts`. There is **no timer polling**: the indicator only
|
||||
refreshes on mount + explicit button press, matching the spec's
|
||||
no-network-without-user-intent discipline (sync is user-initiated).
|
||||
- `vault-status.ts` — sidebar-footer sync indicator renderer.
|
||||
`renderStatusIndicator(el, status)` is pure DOM: it renders, by
|
||||
priority, `N pending` / `N ahead` / `N behind`, falling back to
|
||||
`in sync`, plus a `last sync <relativeTime>` / `never synced` line.
|
||||
Reuses `shared/glyphs.ts` (`GLYPH_PENDING`/`AHEAD`/`BEHIND`/`SYNCED`)
|
||||
and `shared/relative-time.ts`. `VaultStatus` is an alias of
|
||||
`GetVaultStatusResponse['data']`, so the renderer's input shape is
|
||||
single-sourced from the message contract and can't drift from the SW
|
||||
handler.
|
||||
- `vault-list.ts` — the list pane and its row rendering.
|
||||
- `vault-drawer.ts` — drawer open/close/render plus
|
||||
`ensureDrawerClosedForRoute`, which closes the drawer on any
|
||||
non-list navigation.
|
||||
- `vault-form-wrapper.ts` — `renderFormWrapped` plus the sticky bar and
|
||||
header that wrap form panes.
|
||||
- `vault.html` / `vault.css` — sidebar + pane layout.
|
||||
|
||||
### `src/vault/components/`
|
||||
@@ -211,12 +245,19 @@ exports `render…(app)` and a `teardown()`, same convention as
|
||||
|
||||
### `src/setup/`
|
||||
|
||||
- `setup.ts` (1137 lines) — the wizard state machine. Six steps
|
||||
(0..5): mode picker (new vault / attach this device), host type
|
||||
(Gitea/GitHub), host config + connection test + repo probe, the
|
||||
forking step 3 (create-vault vs attach-this-device), device name,
|
||||
finish. Loads WASM directly. State-coupled `updateStrengthUi` stays
|
||||
here because it walks the live wizard state.
|
||||
- `setup.ts` (58 lines) — a thin UI-only shell after the Phase 3
|
||||
split: the render loop + progress track + boot + re-exports. No longer
|
||||
imports `relicario-wasm`; the wizard now drives vault creation/attach
|
||||
through the SW. Binds `clearWizardState` to
|
||||
`window.addEventListener('beforeunload', clearWizardState)`
|
||||
(`setup.ts:53`) and also calls it on `goto('mode')` (`setup.ts:44`).
|
||||
- `setup-steps.ts` (extracted in Phase 3) — the setup step registry +
|
||||
wizard state + `clearWizardState` + `finishSetup`. One-directional
|
||||
import (`setup.ts` → `setup-steps.ts`, no cycle). Crypto orchestration
|
||||
no longer lives in the wizard: the device step (where `deviceName`
|
||||
exists) fires `create_vault` and `attach_vault` SW messages instead of
|
||||
calling WASM directly. State-coupled `updateStrengthUi` stays here
|
||||
because it walks the live wizard state.
|
||||
- `setup-helpers.ts` (84 lines, extracted in commit `f79a67b`) — pure
|
||||
helpers: `escapeHtml`, `ratePassphrase`, `scheduleRate` (150ms
|
||||
debounced zxcvbn round-trip), `STRENGTH_LABELS`, `entropyText`, the
|
||||
@@ -273,7 +314,23 @@ exports `render…(app)` and a `teardown()`, same convention as
|
||||
`session.getCurrent()`, load via `vault.fetchAndDecrypt*`, mutate,
|
||||
re-encrypt, and `gitHost.writeFile`. `fill_credentials` lives here
|
||||
with its own captured-tab verification (see Key flows). New in
|
||||
commit `a7dbf35`: `register_this_device`.
|
||||
commit `a7dbf35`: `register_this_device`. Phase 3 added
|
||||
`create_vault` and `attach_vault` (full SW-side vault
|
||||
creation/attach: embed/unlock, encrypt+push, `register_device` +
|
||||
`addDevice`, persist config+image, `session.setCurrent`; the failure
|
||||
path locks and frees the handle). The `lock` handler now also nulls
|
||||
`state.gitHost` (symmetric with session-expiry) so the status cache
|
||||
can't go stale across a lock→unlock. Phase 6 added `get_vault_status`
|
||||
(popup-only, read-only) — returns the cached sync summary
|
||||
`{ ahead, behind, lastSyncAt, pendingItems }` with **no network
|
||||
call**. `ahead`/`behind`/`lastSyncAt` are read straight off
|
||||
`state.gitHost` (populated by the `sync` handler, which records
|
||||
`lastSyncAt = Math.floor(Date.now()/1000)` — unix **seconds** — after
|
||||
a successful manifest fetch). `pendingItems` is a live count of active
|
||||
(non-trashed) manifest entries via `vault.listItems(manifest).length`.
|
||||
`ahead`/`behind` are structurally always `0` in the extension (it
|
||||
writes straight to the host via the Contents REST API; there is no
|
||||
local commit graph) and exist for parity with `relicario status`.
|
||||
- `router/content-callable.ts` — handler match arms for every
|
||||
`CONTENT_CALLABLE_TYPES` message. Origin always derived from
|
||||
`sender.tab.url`, never from message fields. `capture_save_login`
|
||||
@@ -287,7 +344,13 @@ exports `render…(app)` and a `teardown()`, same convention as
|
||||
no www-stripping, no public-suffix), trash helpers
|
||||
(`listTrashed`, `restoreItem`, `purgeItem`, `purgeAllTrash`), and
|
||||
attachment helpers (`addAttachmentToItem`, `removeAttachmentsFromItem`,
|
||||
with manifest summary sync).
|
||||
with manifest summary sync). Now also includes the
|
||||
`create_vault`/`attach_vault` orchestration handlers (Phase 3) and
|
||||
`handleGetVaultStatus(state)` (Phase 6) — synchronous, no network;
|
||||
returns the cached `{ ahead, behind, lastSyncAt, pendingItems }`. Its
|
||||
`Pick<GitHost,'lastSyncAt'|'ahead'|'behind'>`-typed param both breaks
|
||||
the `PopupState` import cycle and structurally forbids it from making
|
||||
a network call.
|
||||
- `session.ts` — single module-scope `SessionHandle | null`. α assumes
|
||||
one vault per install. Multi-vault would replace this with a `Map`
|
||||
keyed by vault id.
|
||||
@@ -301,6 +364,15 @@ exports `render…(app)` and a `teardown()`, same convention as
|
||||
`putBlob`, `getBlob`, `deleteBlob`) and the `createGitHost` factory.
|
||||
`BLOB_THRESHOLD_BYTES = 900*1024` is the cutover point at which
|
||||
attachment writes switch from the Contents API to the Git Data API.
|
||||
The `GitHost` interface also carries cached sync metadata —
|
||||
`lastSyncAt: number | null` (unix seconds), `ahead: number`,
|
||||
`behind: number` — initialized to `null`/`0`/`0` in both `GiteaHost`
|
||||
and `GitHubHost`. The cache rides the gitHost lifecycle: created on
|
||||
unlock and cleared whenever `state.gitHost` is nulled — on
|
||||
session-timer expiry (`index.ts`) **and** on the explicit `lock`
|
||||
message handler (`popup-only.ts`), which now nulls `state.gitHost`
|
||||
symmetrically so a lock→unlock cycle can't surface a stale
|
||||
`lastSyncAt`.
|
||||
- `gitea.ts` / `github.ts` — the two GitHost implementations. Both use
|
||||
the host's Contents API for files under threshold, and Git Data API
|
||||
(blobs + tree + commit) for large attachment uploads. Auth differs
|
||||
@@ -322,7 +394,9 @@ exports `render…(app)` and a `teardown()`, same convention as
|
||||
- `state.ts` — `StateHost` interface + module-scope singleton. Both
|
||||
`popup.ts` and `vault.ts` register themselves on boot. All
|
||||
`popup/components/*` import from here, never from popup.ts directly,
|
||||
so the same render code runs in both bundles.
|
||||
so the same render code runs in both bundles. Its `sendMessage`
|
||||
wrapper intercepts `vault_locked` responses (lifted out of `vault.ts`
|
||||
in Phase 4, so the intercept now applies uniformly to both bundles).
|
||||
- `types.ts` — TypeScript mirrors of the Rust core's serde shapes:
|
||||
`Item`, `ItemCore` (internally-tagged on `type`), `Field` and
|
||||
`FieldValue` (adjacently-tagged on `kind` / `value`), `Manifest`,
|
||||
|
||||
Reference in New Issue
Block a user