MeridiansMeridians

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&note="| Work
    Work -->|add corpus| Extend -->|APPLY_EXTENSION| Work
    Home --> Exchange
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 whole NavTarget (?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, and useDomainDeepLinks consumes 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=hi is one legal link with three consumers on three surfaces. takeParams removes only the params a consumer owns; useUrlIntent latches (fires once), strips before the handler runs, and enabled holds the intent until its surface can act (domain live, chat grounded).
  • One applier. No surface hand-rolls the multi-dispatch: resolveNav (pure, tested) maps a NavTarget to a plan; useNavigate applies it in order (scene cursor before view; one-shot navFocus armed last). Stage back/forward replays the same plan shape from a NavPoint, 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 adds PropositionClassificationProvider → 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.