2026-07-27 23:07:42 +03:00
|
|
|
# Context Surfaces
|
|
|
|
|
|
|
|
|
|
## Purpose
|
|
|
|
|
|
|
|
|
|
`packages/ui/src/lib/surfaces` owns the declarative registry of context panel
|
|
|
|
|
surfaces — the desktop workspaces switched by the vertical rail on the right
|
|
|
|
|
edge (`components/layout/ContextPanelRail.tsx`) and rendered by
|
|
|
|
|
`components/layout/ContextPanel.tsx`.
|
|
|
|
|
|
|
|
|
|
## Model
|
|
|
|
|
|
|
|
|
|
- A surface maps 1:1 to a `ContextPanelMode` tab mode in `useUIStore`.
|
|
|
|
|
- `availability: 'always'` surfaces are always present on the rail.
|
2026-08-13 22:44:13 +03:00
|
|
|
`availability: 'has-content'` surfaces (chat) are hidden from the
|
2026-07-27 23:07:42 +03:00
|
|
|
rail until a tab of their mode exists, and stay visible for as long as one
|
|
|
|
|
does — they must not disappear while in use.
|
|
|
|
|
- `defaultWidthFraction` is the panel width as a fraction of the content area,
|
|
|
|
|
used until the user manually resizes that surface (manual widths are stored
|
|
|
|
|
per mode in `useUIStore.contextPanelByDirectory[dir].widthByMode`).
|
|
|
|
|
- Rail order is user-reorderable and persisted globally in
|
|
|
|
|
`useUIStore.contextRailOrder`; `sortContextSurfaces` applies it on top of the
|
|
|
|
|
registry's default order and appends any missing surfaces.
|
2026-08-04 20:39:45 +03:00
|
|
|
- `getVisibleContextRailSurfaces` is the single visibility filter shared by the
|
|
|
|
|
rail and the global surface-switch shortcut (`switch_context_surface` in
|
2026-08-26 16:18:15 +03:00
|
|
|
`lib/shortcuts`): it drops surfaces the user hid
|
|
|
|
|
(`useUIStore.contextRailHiddenSurfaces`, edited from the rail's trailing
|
|
|
|
|
configure button — `ContextRailSurfacesDialog`), drops the plan surface
|
|
|
|
|
unless plan mode is enabled,
|
2026-08-30 02:18:40 +03:00
|
|
|
drops the walkthrough on VS Code and below `WALKTHROUGH_MIN_WIDTH`, hides
|
2026-08-30 14:17:12 +03:00
|
|
|
Linear unless a workspace is connected, hides the pull-request surface
|
|
|
|
|
unless GitHub is connected (OAuth or `gh` CLI — signed in from Settings →
|
|
|
|
|
Integrations), and hides `has-content` surfaces
|
2026-08-30 02:18:40 +03:00
|
|
|
until a tab of their mode exists. Both consumers use it so the digit shown
|
|
|
|
|
on a rail badge always maps to the same surface the shortcut opens.
|
2026-07-27 23:07:42 +03:00
|
|
|
|
|
|
|
|
## Adding a surface
|
|
|
|
|
|
|
|
|
|
1. Add a `ContextPanelMode` value in `useUIStore` (type union plus the
|
|
|
|
|
sanitizer whitelist in `sanitizeContextPanelTabs`).
|
|
|
|
|
2. Register a descriptor here (icon, label key, availability, width fraction).
|
|
|
|
|
3. Render the mode in `ContextPanel.tsx` (content dispatch, label, icon).
|
|
|
|
|
4. Add label/hint i18n keys to every locale dictionary.
|
|
|
|
|
|
|
|
|
|
No new header buttons: the rail and `openContextSurface` are the only entry
|
|
|
|
|
points for opening surfaces directly; deep links from chat/palette go through
|
|
|
|
|
the `openContext*` actions in `useUIStore`.
|
|
|
|
|
|
|
|
|
|
## Invariants
|
|
|
|
|
|
|
|
|
|
- Opening a surface must never require a control outside the rail, the
|
|
|
|
|
command palette, or an in-content link.
|
2026-08-11 15:11:25 +02:00
|
|
|
- Multi-instance and session-holding surfaces (file/editor, diff, browser,
|
|
|
|
|
terminal) are keep-alive panes in `ContextPanel.tsx`. Switching these
|
2026-07-27 23:07:42 +03:00
|
|
|
surfaces must not reset their state (open tabs, xterm session, scroll
|
2026-08-11 15:11:25 +02:00
|
|
|
positions). Chat tab records stay open, but only the active chat iframe is
|
|
|
|
|
mounted while the panel is open. A selected chat restores its state from
|
|
|
|
|
the session stores. A closed panel mounts no chat iframe.
|
2026-08-30 02:18:40 +03:00
|
|
|
Singleton surfaces (git, pr, linear, notes, plan, context) remount on switch. These
|
2026-08-13 22:44:13 +03:00
|
|
|
surfaces must restore their state from stores or snapshots.
|
2026-07-27 23:07:42 +03:00
|
|
|
- Runtime scope: desktop/web `MainLayout` only. VS Code and the dedicated
|
|
|
|
|
mobile shell have their own layouts and do not consume this registry.
|
2026-08-30 02:18:40 +03:00
|
|
|
Linear is a desktop/web singleton on this rail. VS Code and mobile omit it
|
|
|
|
|
(no this registry, and VS Code has no `RuntimeAPIs.linear`). The Linear
|
|
|
|
|
rail icon is hidden until a Linear workspace is connected. A persisted Linear
|
|
|
|
|
tab stays open across reload until auth has resolved; only a confirmed
|
|
|
|
|
disconnect closes the panel. The surface lists
|
|
|
|
|
issues with status (All, Backlog, To Do, In Progress, In Review, Done, Canceled, Duplicate), assignee, team, and priority filters, can switch
|
|
|
|
|
the current workspace, and keeps Start session in a footer on the issue card.
|
|
|
|
|
Those filters restore from `useUIStore` when the surface remounts. Non-default
|
|
|
|
|
status, assignee, team, priority, and search tint the filter icon `text-primary`,
|
|
|
|
|
same as the context rail; one control clears them. Workspace switch is not a
|
|
|
|
|
filter. Work-status Context sources
|
|
|
|
|
can open a specific issue here through `linearIssueFocus`. Below 520px
|
|
|
|
|
search and the filters other than status drop to icons; status keeps its label. The card
|
|
|
|
|
shows priority and labels. Changing filters keeps the previous list
|
|
|
|
|
until the next page arrives.
|