Layout: regions and docks
This is a guide, not the contract. What the platform guarantees is specified under
openspec/specs/. For this page:shell-layout. Where this page and a specification disagree, the specification is right, and that is a defect in this page: change the behaviour there, then explain it here.
provideLayout declares which regions sit where in the border topology. A region has a type
(its anatomy) and a dock (where it sits):
- Docks:
top·bottom·left·right·center. - Region types:
-
bar: a thin strip of items instart | center | endslots (top bar, status bar). A bar that cannot show every item folds the ones that do not fit into a More control at its end. The last item of theendslot folds first, highestorderfirst, thencenter, thenstart. The control opens the folded items in a tray, as they are. On a narrow viewport, the one at which the sidebars become overlays, the workbench’s own top-bar items take a compact form: the product’s mark without its name, the language switcher as its symbol alone. -
rail: the rail, which the workbench labels Activity bar, holding icon triggers for commands. -
panel: a sidebar surface that hosts views (the host auto-tabs multiple views). A panel is the one region a person resizes, so it is the one that may declare its widths in pixels, each optional. Itswidthis what it shows until someone resizes it and what resetting the layout returns to. ItsminWidthandmaxWidthbound dragging, the keyboard and a width set from code. Undeclared values stay at 256, 180 and 480. A width a person released is kept even if you later changewidth, and one stored above a newmaxWidthis shown at the new maximum.On a narrow viewport the panel becomes an overlay, and its
overlayWidthsizes it there (default 288). The workbench caps that at the screen’s width less a margin, so the overlay never runs off the screen and there is room to dismiss it. The overlay width is not dragged or stored, and the widths beside the content do not reach it.{ id: 'right-panel', type: 'panel', dock: 'right', width: 360, minWidth: 280, maxWidth: 640, overlayWidth: 400 }The type only allows these fields on a panel region (
PanelRegion; a bar, rail or content region is aNonPanelRegion). A panel declaring any of the four widths as something other than a positive number, whoseminWidthexceeds itsmaxWidth, or whosewidthlies outside its own bounds, makesprovideLayoutthrow, naming the region. -
content: the main content area (dockscenter). URL-addressed (routes), not views.
-
A weaver targets a region by its id, never by its dock or type: registerSurface({ docks: ['left-panel'] }), registerRailItem({ rail: 'primary' }) and registerBarItem({ bar: 'status-bar' })
name the ids the scaffold declares. Left and right are symmetric; shell
anatomy lists the ids.
Non-routable surfaces render only in
panelregions. A surface’s home dock (docks[0]) may name any region id, but one docked into acontent(orbar/rail) region is a silent no-op (dev-mode warns). The content area is routed: a weaver fills it with a surface that declaresroutable: { path }(see The content area).
Collapsing, resizing and hiding views in the sidebars from your own code is SidebarService in the
the Distribution API.
Panes and splits
Every dock (centre + both sidebars) is a tree of tab-group panes. Users split a pane by dragging a tab to its edge or via a tab’s Split right / Split down menu, move tabs between groups by dragging onto a strip, and resize with the dividers. Exactly one centre pane is the address pane (it drives deep links / back-forward); the rest are workspace state. The whole arrangement (pane trees, sizes, active tabs) is persisted user-locally and reload-safe. Each of these gestures has its switch in Switching capabilities off.
Curating a sidebar
The user curates a sidebar the way they curate the rail. A right-click on a view tab offers Move to other sidebar and Hide; a right-click on the strip offers Customize views, which opens a dialog listing every view with where it sits: hidden, left, or right. Picking a place moves it there, so the dialog does the hiding and the moving in one control, and a view hidden on the left comes back wherever you send it. The dialog has a search field and scrolls, because a product with many views would otherwise be a wall of rows. Which views a sidebar holds is part of the workspace, so switching workspaces changes it; the rail’s own curation stays put.
The dialog is the command shell.views.customize, so it is reachable from the command palette,
bindable to a shortcut, callable from an item of your own, and removable with
provideShell({ omit: ['shell.views.customize'] }). The menu entry is a contribution of its own
(menu:shell.views.customize), so you can drop the entry and keep the command.
Where next
- Sidebars: collapse, resize, hide and show by region id from your own code.
- Panes: the panes your tabs sit in, read and driven from your own code.
- Surfaces and panes: why a pane is a tab group and a surface can sit anywhere.