Files
openchamber/packages/ui/src/lib/surfaces/DOCUMENTATION.md
T
Bohdan Triapitsyn f3dd894209 feat(ui): numbered context-panel surface switching with configurable prefix
- Add switch_context_surface shortcut (default Cmd/Ctrl + 1..9, 0 for the 10th
  surface) that opens/closes/switches context panel rail surfaces by their
  visible order, configurable and persisted in Settings -> Shortcuts.
- Show order-number badges on rail icons while the modifier is held >500ms;
  dismiss on release, blur, or a number press until the next press-and-hold.
- Remove the legacy mod+2/3/4 (diff/terminal/git) and switch_tab_1..9 bindings
  so numbered surface switching goes only through the new mechanism.
- Replace the help-dialog 'Switch Project' row with the surface-switch row and
  update the shortcuts footer/header icons to the command icon.
2026-08-04 20:39:45 +03:00

2.7 KiB

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. availability: 'has-content' surfaces (preview, chat) are hidden from the 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.
  • getVisibleContextRailSurfaces is the single visibility filter shared by the rail and the global surface-switch shortcut (switch_context_surface in lib/shortcuts.ts): it drops the plan surface unless plan mode is enabled, drops the walkthrough on VS Code and below WALKTHROUGH_MIN_WIDTH, and hides has-content surfaces 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.

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.
  • Multi-instance and session-holding surfaces (file/editor, chat, diff, browser, terminal) are keep-alive panes in ContextPanel.tsx: switching surfaces must not reset their state (open tabs, xterm session, scroll positions). Singleton surfaces (git, pr, notes, plan, context) and preview tabs intentionally remount on switch and must restore themselves from their stores/snapshots instead.
  • Runtime scope: desktop/web MainLayout only. VS Code and the dedicated mobile shell have their own layouts and do not consume this registry.