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.
This commit is contained in:
Bohdan Triapitsyn
2026-08-04 20:39:45 +03:00
parent 67965ced2f
commit f3dd894209
32 changed files with 574 additions and 124 deletions
+37
View File
@@ -152,6 +152,10 @@ export const CONTEXT_SURFACES: readonly ContextSurfaceDescriptor[] = [
const SURFACE_BY_ID = new Map(CONTEXT_SURFACES.map((surface) => [surface.id, surface]));
const FRACTION_BY_MODE = new Map(CONTEXT_SURFACES.map((surface) => [surface.mode, surface.defaultWidthFraction]));
// Tablet width and up: below this the walkthrough cannot show a stop and its
// code side by side, which is the whole point of the surface.
export const WALKTHROUGH_MIN_WIDTH = 768;
export const getContextSurfaceWidthFraction = (mode: ContextPanelMode): number => {
return FRACTION_BY_MODE.get(mode) ?? 1 / 2;
};
@@ -187,3 +191,36 @@ export const sortContextSurfaces = (railOrder: readonly string[]): ContextSurfac
return ordered;
};
type VisibleRailSurfacesOptions = {
railOrder: readonly string[];
planModeEnabled: boolean;
isVSCode: boolean;
screenWidth: number;
tabs: readonly { mode: ContextPanelMode }[];
};
/**
* The context panel rail's visible, user-ordered surfaces. Shared by the rail
* (for rendering and number badges) and the global surface-switch shortcut so
* both agree on which surface each digit maps to.
*
* Content-driven surfaces are hidden (not disabled) until content exists; an
* existing tab keeps them visible even if the content source went away.
*/
export const getVisibleContextRailSurfaces = (options: VisibleRailSurfacesOptions): ContextSurfaceDescriptor[] => {
return sortContextSurfaces(options.railOrder).filter((surface) => {
if (surface.id === 'plan' && !options.planModeEnabled) {
return false;
}
// The walkthrough needs room for a stop list beside real code, and its
// diffs come from OpenChamber's Git routes, which VS Code does not serve.
if (surface.id === 'walkthrough' && (options.isVSCode || options.screenWidth < WALKTHROUGH_MIN_WIDTH)) {
return false;
}
if (surface.availability === 'has-content') {
return options.tabs.some((tab) => tab.mode === surface.mode);
}
return true;
});
};