refactor(frontend): each page fills the floating header itself; the header knows no page

The header used to pick its pills by `page === …` and import five module controls, and Plex's
Discover/Browse toggle needed a context bridge to reach the sub-view that lives in PlexBrowse.
Now the header is an empty band: PageShell hands its root box out as a slot, a page portals its
own pills into it (PageHeader), and the palette's Jump-to pill shows where no page does
(PageHeaderDefault, rendered inside the page's Suspense so a loading page shows no stand-in).
Plex's toggles take the sub-view as a prop; PlexViewBridge and its context are gone, and with
them the header's five layer suppressions.

No visible change: every header pill's box measured identical against the previous build on eight
pages, in Hungarian and English.
This commit is contained in:
2026-10-23 00:23:38 +02:00
parent 0b24f2d8fa
commit 4ef963b005
36 changed files with 530 additions and 316 deletions
+5 -5
View File
@@ -19,12 +19,12 @@ import type { Page } from "./urlState";
// ── The module registry ───────────────────────────────────────────────────────────────────────
// One ordered source of truth for every navigable module: its nav label, icon, admin/system
// placement, whether it carries a live badge, and its availability gate. The nav rail, the header's
// ◀/▶ stepper, and (as the registry grows) the page render all read from here, so a new or newly
// placement, whether it carries a live badge, and its availability gate. The nav rail, the command
// palette's module group, and (as the registry grows) the page render all read from here, so a new or newly
// gated module shows up everywhere at once — nothing hard-codes the sequence or duplicates the icon.
export type ModuleDef = {
id: Page;
// i18n key for the SHORT display name — the same label the nav rail and the header pill show.
// i18n key for the SHORT display name — the label the nav rail and the palette show.
labelKey: string;
// The SHORT name under the icon when the user turns the rail labels on (B7): the rail is 3.75rem
// wide with labels or without, so it has to fit there ("Alerts", "Listák"). The full `labelKey`
@@ -47,10 +47,10 @@ export type ModuleDef = {
const isAdmin = (me: Me) => me.role === "admin";
// Ordered in nav-rail order — this array IS the sequence the rail and the stepper walk.
// Ordered in nav-rail order — this array IS the sequence the rail and the palette list.
const MODULES: readonly ModuleDef[] = [
// Home leads: it is the landing (see navigationInit.loadInitialPage), and the module order IS the
// nav order and the header stepper's sequence. The feed takes the YouTube mark its own toolbar
// nav order and the palette's. The feed takes the YouTube mark its own toolbar
// already uses, so the two are apart by shape and not just by position.
{
id: "home",