Repository navigation
app route view
Status: accepted (records decisions already implemented on wql-fix —
commits bfcd3cca, 2628347f, 14e1b9d0)
Playground routes were classified and rendered ad hoc: AppContent held a render ternary, every call site hand-built record URLs, list surfaces were a hardcoded path list, and the same view variations (which secondary rail, which default query, which empty message) were re-derived per page. Rebranding a route for a different note type meant touching many files. Target scheme and link rules: ../link-crosswalk.md and ../playground-routes.md.
-
Pure classification.
routeView.resolveRouteViewis a pure function (pathname + params + injected data → view).useRouteViewis its React adapter. AppContent mapsPageKind → elementthrough arenderInnerrecord; no routing logic lives in JSX closures. -
Stream composition via profiles. A stream surface is fully declared by a
StreamProfileinviews/stream/streamProfile.ts: route, default WQL, entity level, type options, shelf, empty message, secondary rail (secondary?: MenuSpec), and display title.QueriableStreamViewrenders any of them. Adding or rebranding a stream route = adding a profile; no view code changes. -
One stream registry.
isStreamRoute(streamProfile) is the single membership testrouteView.derivePageconsults for thelibrarypage kind — routeView keeps no parallel path list.streamRouteTitleis similarly the single source for stream display names inderiveWorkout. -
Secondary rails are profile-owned. AppContent renders
profile.secondary; no page-level constant invents a rail. Surfaces declare variations (sessions and playgrounds have their own rails). -
Storage ids and route slugs stay separate.
Entry.idandnoteIdretain the owning note identity.pageIdis the Page UUID;pageSlugaddresses named pages at/p/:slug. Search resolves named membership throughpage_notes, skipping calendar pages. Plain notes fall back to/notes/:noteId. Collection routes use declaredsourceIdor importedsourcePath, not UUIDs:/c/:catalogfor landings and/c/:catalog/:itemfor workouts. Block results preserve section anchors. -
Redirect components, not scattered literals. Each retired path family has one Navigate component in
lib/routeRedirects.tsx, mounted in the App route table.lib/routes.ts(pure, no React) keeps patterns, builders, and the legacy resolveRedirect matrix; nav derivation lives inlib/routeNav.ts. -
Unmatched routes stay visible. The route table always mounts a wildcard. Before the seeded root exists, it shows loading or re-seed recovery; after the root is available, it shows
NotFoundPagewith home navigation. Explicit note routes retain their own missing-note state.
- New stream surface = profile + route pattern; classification, membership, title, and secondary follow from the profile.
- Old paths keep working through redirects; behavior changes land in one component each.
- UUID-keyed collection members remain notes, not catalog landings. The catalog projection derives grouping and landing status from source routes.
- Runtime results from named pages retain the loaded note UUID, not the presentation slug.