Navigation — pages, deep-links & how you move [Flow]
From the Meridians Wiki · Public · Maintained · joint
How the app's routes connect, and how a deep-link intent travels from the URL to a selected artifact.
The URL [id] is the source of truth for the active domain; src/lib/core/routes.ts is the single
source of URL truth — every path and query deep-link is built there, and consumed through one seam.
flowchart TB
Home["**/** — Home console<br/>composer: **Note · Ask · Build**<br/>Domains · Extraction · Games · People · Invitations"]
Onboard["/onboarding — guided setup<br/>6-step stepper"]
Wizard["CreationWizard<br/>new domain from a premise"]
Extract["/extraction — Text Extraction"]
Const["/constellation/[id]<br/>portfolio container — ?tab= surface"]
Work["/domain/[id] — **THE WORKSPACE**"]
Extend["/extensions/[id] — add corpus"]
Play["/scenario/[gameId] — Scenario seat<br/>(guest-pass, one seat)"]
Exchange["/exchange — the shelf<br/>(shared page, was /library)"]
Home -->|open a folder| Const
Home -->|open a domain| Work
Const -->|open a member domain| Work
Home -->|new domain| Wizard --> Work
Home -->|Onboarding| Onboard ==>|"builds a Constellation<br/>+ its Domains"| Const
Home -->|"Build (paste corpus)"| Extract -->|creates DomainState| Work
Home -->|GamesLobby → join| Play
Home -.->|"**Ask** — AI routes to best-fit expert<br/>?ask= seeds + auto-sends the chat"| Work
Home -.->|"Ask → constellation<br/>?tab=chat&ask="| Const
Home -.->|"**Note** — routes into an expert's queue<br/>?view=notes¬e="| Work
Work -->|add corpus| Extend -->|APPLY_EXTENSION| Work
Home --> Exchange
The deep-link lifecycle — one builder, one consumer, one applier
flowchart LR
Routes["**routes.ts** — the builders<br/>domain(id, opts) · constellation(id, {tab, ask}) ·<br/>stream/merge/tutor/projection/cycle/note ·<br/>artifact(id, NavTarget) · extraction · extensions"]
Routes -->|"?open=reading:RDG-1 · ?signal=stream:S-1 · ?view= ·<br/>?projection= · ?note= · ?inspect= · ?ask= · ?slides= · ?game="| URL[/"the URL — intents compose;<br/>each param has its own consumer"/]
URL --> Intent["**useUrlIntent(params, onIntent, {enabled})**<br/>the ONE consumer: latch once ·<br/>strip via takeParams (siblings survive) ·<br/>hold until the surface is ready"]
Intent --> Nav["**navigateTo(NavTarget)** — the ONE applier<br/>resolveNav (pure) → scene cursor →<br/>stage view → inspector → one-shot navFocus"]
Nav --> Surface["destination surface consumes navFocus,<br/>selects + descends, then clears it"]
Hist["**Stage history** — browser-style back/forward<br/>NavPoint = view + focus + inspector + cursors;<br/>stepping back re-dispatches the same plan"]
Nav <-->|"push / replay"| Hist
Phone["**phone-route.ts** — pure projection of a<br/>navigation onto the phone pill's leaves<br/>(notes → Outline's Notes lens)"]
Nav -.->|"on isPhone"| Phone
- Home spine (in
GatewayShell, present on every page) carries the role-gated verbs — New domain, People, Usage, Hosting, Environment — plus identity / sign-out. - The Home composer routes by intent. Note commits into one domain's Notes queue and lands on
routes.note(...); Ask routes a message to the best-fit expert (AI-proposed, operator-confirmed) and opens its chat via?ask=(a constellation ask implies?tab=chat); Build stages a corpus and lands on/extraction?new=1. ?open=is the general cross-domain drill. It carries a wholeNavTarget(?open=reading:RDG-1,?open=note:entry-1,?open=view:runs), so a PORTFOLIO tap — "Open in workspace" on a constellation Story's attached material — lands inside the owning domain on that artifact, not merely at its Stories inbox. One param covers every navigable kind because it encodes the same target an in-workspace drill applies, anduseDomainDeepLinksconsumes it before the narrower?view=family so a link naming an artifact is never downgraded to its surface. The narrower params stay for the links that predate it.- Intents compose, so consumption is surgical.
?slides=1&signal=stream:S-1&ask=hiis one legal link with three consumers on three surfaces.takeParamsremoves only the params a consumer owns;useUrlIntentlatches (fires once), strips before the handler runs, andenabledholds the intent until its surface can act (domain live, chat grounded). - One applier. No surface hand-rolls the multi-dispatch:
resolveNav(pure, tested) maps aNavTargetto a plan;useNavigateapplies it in order (scene cursor before view; one-shotnavFocusarmed last). Stage back/forward replays the same plan shape from aNavPoint, so history restores the exact item, not just the tab. - Onboarding is the one path that creates a Constellation + its member Domains + their maintenance in one flow (onboarding).
- Providers wrap every route:
ThemeProvider → StoreProvider → WizardProvider → ToastProvider → (ScenarioHost) → AccessProvider → LogsProvider; the workspace route addsPropositionClassificationProvider → AudioPlayerProvider. - The surfaces themselves: home → ui/home-console, workspace → ui/workspace-shell, constellation → ui/constellation, phone → ui/mobile.
Where it lives: src/lib/core/routes.ts (builders + takeParams + decodeConstellationView) ·
src/hooks/useUrlIntent.ts (the consumer) · src/hooks/useNavigate.ts + src/lib/core/program/stories/nav.ts
(resolveNav, pure) · src/shell/layout/useDomainDeepLinks.ts (the workspace's three intents) ·
src/hooks/useStageHistory.ts + src/types/domain/nav.ts (NavPoint) · src/shell/mobile/phone-route.ts.