Skip to content

Navigation state

Navigation state

Application navigation state is the shared record-detail navigation platform inside @norbital-ai/pod. The runtime bootstraps PageSurfaceState (an internal class name, not a tenant-authored layout component) and persists record navigation in the URL.

What people see

The Pod shell renders a sidebar and a main app surface. Platform and Applications are uppercase muted section labels in the sidebar — host plugins and tenant apps remain visually distinct. They are not navigation items themselves.

Opening a record does not navigate to a new document route. The app stays on /app/{appName}; the selected record stack lives in ?stack= and the shell renders a detail sheet when viewMode === 'sidesheet'.

┌─ TenantWorkspaceShell ─────────────────────────────────────┐
│ [Sidebar]                       │  Main app surface       │
│  PLATFORM — host plugins        │  CollectionTable / …    │
│  APPLICATIONS — tenant apps   │       │ click row        │
│                                 │       ▼                  │
│                                 │  pushes ?stack= in URL   │
└─────────────────────────────────┴─────────────────────────┘
                                          │
                    viewMode=sidesheet    ▼
              ┌──────────────────────────────────────┐
              │  Right sheet (DetailSurfaceStack)     │
              │  Record detail snippet + breadcrumbs │
              └──────────────────────────────────────┘

Stack contract

Navigation state is a single selected record stack serialized in ?stack=:

type NavState = {
  stack: NavStackItem[];
};

type NavStackItem = {
  collection_name: string;
  record_id: string;
  node_id: string; // which registered detail snippet to render
  viewMode: 'page' | 'sidesheet';
  expand?: string[];
};

Tenant apps register their own routeKey values through DetailSurfaceService. CollectionTable and CollectionKanban register detail snippets and open records via the surface navigation API — they build a nav target, merge it into the current URL stack, and the shell renders the registered snippet.

Route context

Lightweight route context only identifies where the URL belongs: organization, optional appName, and optional workspace-manifest mode. It does not decide rendering behavior. When switching apps or clearing selection, the shell uses that context to preserve or reset ?stack= while staying on the current app URL.

Forms in detail surfaces

Create, display, and edit in table, kanban, and detail views share one compiler-owned collection role: +representation.svelte. Collection surfaces resolve it from the generated static map and pass record={null} for create or the existing row for display/edit — call sites do not register or override record components. See Form system.

Builder-facing surface
This guide describes the authoring contract and UX model. Internal hydration, scope remotes, and shell implementation details stay inside Pod — you interact through collection surfaces and app entry components.
  • Apps — tenant application entry components the shell loads
  • Collections — models that table and kanban surfaces navigate into
  • Form system — inline forms in table, kanban, and detail