refactor(frontend): each module defines itself in its folder; rails load lazily and keep their own state

A page used to be listed three times (lib/modules for its label, icon and gate; shell/moduleRegistry for
its page and rail; lib/pageMeta for its title), and App held every rail's layout and collapse flag.
Now each module's `module.ts` says it all — label, icon, gate, its lazy page and rail, its width —
handed out through its index.ts, and features/navigation/moduleRegistry lists them in nav order for
the rail, the palette, the tab title, the reachable set and the shell's render. A contract test holds
the registry to the page ids and each definition to its own folder, with no static import of a page.

Every rail is lazy now, loaded together with its page so a pinned rail never arrives after it and
shoves the content aside; the feed's and the playlists' rails took @dnd-kit off the boot path (boot
428.5 → 393.9 KiB gzip, the App chunk 122 → 32 KiB). A rail reads its own layout and collapse flag
(lib/useRail) and writes a layout change to the `me` cache too, since it now mounts anew on each
visit. App's session effects, its one-shot entry-URL handlers and the admin nudge move into shell
hooks; App is layout. The registry's three layer suppressions are gone: none is left.
This commit is contained in:
2026-10-23 04:00:58 +02:00
parent e83c5e2d97
commit cde5a98fd2
56 changed files with 890 additions and 685 deletions
+20
View File
@@ -0,0 +1,20 @@
import { YouTubeIcon } from "../../lib/brandIcons";
import { lazyWithPreload, lazyWithPreloadAlongside } from "../../lib/lazyWithPreload";
import type { ModuleDef } from "../../lib/moduleDef";
// The rail loads with the page (lazyWithPreloadAlongside), so it never arrives after it.
const Rail = lazyWithPreload(() => import("./Sidebar"));
// This page as the shell and the navigation know it (lib/moduleDef). Its page and rail load lazily.
export const feedModule: ModuleDef = {
id: "feed",
labelKey: "header.account.feed",
railKey: "nav.rail.feed",
icon: YouTubeIcon,
Rail,
Page: lazyWithPreloadAlongside(() => import("./FeedPage"), Rail),
};
// The channel page: a feed scoped to one channel, opened over any page (App renders it in place of the
// page). Lazy like a page, and preloaded with them.
export const ChannelPage = lazyWithPreload(() => import("./ChannelPage"));