feat: the redesigned Event Clock

The new reader app replaces the old UI: Checklist with a rail (focus
pills, Next to expire, today's dailies, pinned routines), Timeline in a
new and a classic look, Routines, Archive and a Settings area, in a
Structured (default) and a Relaxed interface. It follows the approved
Round 6 B and Round 7 mockups, corrected screen by screen with the owner.

Reader data moves to a v2 store (gacha-tracker:v2:*). Existing v1 data
is migrated on first load, a recovery copy is written first, and the v1
keys are never removed, so a rollback finds them unchanged. Redeem codes
for Genshin arrive as their own feed.

Also: live's urgency ramp, editable per theme; completion and
section-complete fireworks; a returning-user tour; the design mockups,
specs, plans, reviews and handoff record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-18 01:55:16 +02:00
co-authored by Claude Opus 5.5
parent ddd9673f86
commit 50c6e91b00
284 changed files with 45100 additions and 1628 deletions
+53
View File
@@ -364,3 +364,56 @@ other, so `test/update.test.tsx` pins both ends of the `skip-waiting` handshake
cleanly because state is in SQLite.
- **No server-side rendering.** The feed is small and cacheable; a static SPA is enough.
- **No websockets.** Events change on a scale of hours; a 5-minute cache is more than adequate.
## Reader application (2026-10-16 redesign)
`App.tsx` exports `ReaderApp`; only the v2 reader store writes production progress. Legacy hooks
remain as compatibility helpers, validators and tests, not mounted writers. ReaderContent coordinates
shared Checklist/Timeline/Routines/Archive/Settings, account-aware detail adapters and timed archive
transitions. `components/redesign/` contains the feature pages. `redesign.css` scopes Relaxed and
Structured via data-interface, with one application tree and independent theme/density.
`state/reader/` owns migration, schema, reducer, store, clocks, accounts, archive, backups, restore,
imports, routines and event selectors. Operations re-read the persisted envelope under Web Locks;
an IndexedDB readwrite transaction serializes browsers without Web Locks. Failed locking/storage
never publishes an optimistic success. Storage events update other tabs. Field timestamps protect
newer edits and tombstones; account writes rebase changed routine choices. Legacy deltas reconcile
on startup into the original migrated accounts; raw keys remain preserved.
Clock revisions freeze automatic metadata and overrides. Imported historical clocks are excluded
from active clock resolution; history resolves each explicit revision. Display timezone lives in
DisplayTimeContext and never changes the feed.
Events and redeem codes use separate validated static feeds. `build-codes.ts` reads pinned snapshots
or fixtures offline and preserves firstObservedAt in snapshots/codes-first-seen.json. The refresh
runner combines event adapters and count-only code sources while sharing robots, floors, per-host
spacing and snapshot gates. No fake GachaEvents represent codes. Both feeds are network-first in
the worker; manual refresh bypasses the browser cache. Shell update waiting/activation is unchanged.
Backups are outside the reader envelope. Restore validates first, writes a recovery export, prepares
a pending transaction, then replaces live state. Startup finishes an interrupted transaction before
opening the reader. Browser UI restore uses the same cross-tab lock as ordinary edits.
### Ordinary HTTP / WireGuard hosts
Reader identity minting uses `state/reader/ids.ts`: 128-bit `crypto.getRandomValues` tokens for
accounts, operations, reset revisions and backups. The redesign's initial `randomUUID` calls were a
startup bug on ordinary HTTP network hosts; unlike localhost, these are not browser secure contexts,
even when reached through a WireGuard tunnel. Existing IDs are unchanged and valid. Initialization
preserves migration recovery before minting, so the failed first visit did not discard legacy bytes.
Regression verification uses a Chrome test hostname mapped to loopback with isSecureContext false,
randomUUID unavailable and actual getRandomValues/IndexedDB available. Existing-save migration,
new setup, editing, additional servers, reset override/revert, backup and reload all pass there.
### Presentation correction (2026-10-17)
`ChecklistEvent`/`NextDeadline` render the approved list hierarchy; `TimelinePage` owns the shared
axis for event spans and routine periods. `RoutinesPage` groups by reset or account, with native
`RoutineEditor` dialogs. `ArchiveDetail` reads saved archive snapshots and never routes historical
progress through the live event editor. Settings keeps one shared tree for both interfaces, with
separate default-view/custom-content pages and direct game profiles. Structured is the absent-value
default at prepaint, preference normalization, setup and appearance application. No state keys,
event identities or stored clock revisions changed for this presentation correction.