fix(nav): the address follows the view through overlays, reloads and Back (review round 1)

- A URL mirror names the view again on every entry Back or Forward lands on, so a change made
  under a dialog, a drawer or a sub-view is not undone by the next reload.
- A view pushed from the entry a reload under a dialog started from no longer inherits its overlay
  mark; a reload under two overlays closes onto the page in one step.
- The linked player reopens over a live search, gives itself up when its list moves first, and
  plays alone over an emptied playlist; a capped Files list keeps the link it cannot resolve.
- The Feed press over a channel ends the search; leaving a search ends its asked mark; Files and
  Playlists searches end with their page; the feed always names its view and debounces its search.
- Search-box keys are declared per module and kept out of the client log below TRACE.
- Writers outside lib/ go through codecs; the URL contract scans computed keys, overlay writers and
  two-line reads, and holds every page to a bookmark round trip (Config, Activity, Playlists and the
  Files sort and viewer added).
This commit is contained in:
2026-10-27 13:52:42 +01:00
parent 7b4e007680
commit 0d5c6273f1
50 changed files with 928 additions and 157 deletions
+9 -6
View File
@@ -49,11 +49,13 @@ A frontend page is one folder, `frontend/src/modules/<id>/` (the layering is in
address-bar key its view writes (the URL is the view: a bookmark reopens it), its list kept in `lib/` beside
its codec; the shell clears them on a page switch and opens a page without another page's keys.
Every setting the page shows — a tab, a sort, a filter, a search, the item open in it — goes there: a codec
in `lib/<id>UrlState.ts` (read the params into the view, write the view as updates, a default left out),
written with `useUrlMirror` (a sub-view through `useHistorySubview`'s `url`, an overlay through
`useBackToClose`'s), read back first on mount (a link wins over anything remembered). Add its round trip to
`e2e/bookmark.spec.ts`. Only what an address cannot promise to reopen stays out, said in its codec (Home's
draw, `lib/homeUrlState`).
in `lib/<id>UrlState.ts` (read the params into the view, write the view as updates, a default left out —
or always written, when an address naming none falls back to a view remembered elsewhere), written with
`useUrlMirror` (a sub-view through `useHistorySubview`'s `url`, an overlay through `useBackToClose`'s),
always as the codec's result: a writer outside `lib/` computes no key of its own. Read back first on mount
(a link wins over anything remembered). A search box's key is also in `typedUrlKeys` (the client log keeps
what was typed out). Add its round trip to `e2e/bookmark.spec.ts`. Only what an address cannot promise to
reopen stays out, said in its codec (Home's draw, `lib/homeUrlState`).
3. `index.ts`, the module's public face: the definition, and only what other folders must read (its store's
hooks). Another folder reaches the module through this file or not at all.
4. The id in `lib/urlState`'s `PAGES`, and the definition in `features/navigation/moduleRegistry.ts`, in
@@ -67,7 +69,8 @@ A frontend page is one folder, `frontend/src/modules/<id>/` (the layering is in
What holds it: `moduleRegistry.contract.test.ts` (every id defined once, from its own folder, lazily),
`moduleStores.contract.test.tsx` (no store reads another module), `moduleUrl.contract.test.ts` (each URL key
belongs to one module and matches what its codec writes; no undeclared key literal), the `local/layers` lint rule (imports only go
belongs to one module and matches what its codec writes; a writer outside `lib/` computes no key; no key literal
written or read from the live query undeclared; every page with keys in the bookmark net), the `local/layers` lint rule (imports only go
down, a module only through its face) and `chunkGuard` in the build (no lazy page on the boot path), plus the
i18n parity test.