Skip to content

Sidebar views

A menu is an activity-bar container. Inside it live the container’s own tree, plus any number of tree views (defineTreeView) and subpanels (defineSubpanel). This page covers what you control about how they’re arranged and what they can do.

Ordering

Subpanels and tree views share one ordering inside a container, so the two kinds can be interleaved deliberately:

src/treeViews/catalog.ts
export default defineTreeView({ title: 'Catalog', menu: 'trainer', order: 1, getChildren });
src/subpanels/ask.ts
export default defineSubpanel({ title: 'Ask', menu: 'trainer', order: 2 });

Low order first. Views without an order keep their discovery order, after every ordered one. gen writes the resulting sequence into package.json#contributes.views, so re-run it after changing an order.

Title-bar buttons

titleActions pins commands to a view’s title row:

src/treeViews/catalog.ts
export default defineTreeView({
title: 'Catalog',
menu: 'trainer',
titleActions: [
{ command: 'refreshCatalog' }, // icon button
{ command: 'exportCatalog', group: 'overflow' }, // … menu
{ command: 'addProblem', when: 'config.myExt.editing' },
],
getChildren,
});
FieldDefaultMeaning
commandThe id you gave defineCommandwithout the extension prefix.
group'navigation'navigation renders an inline icon button; anything else drops into the overflow menu.
whenANDed with the view match rather than replacing it.

gen writes these to contributes.menus['view/title'] as view == <viewId> && <your when>.

titleActions works the same on subpanels.

Tree nodes: leaves vs. lazy children

A node is collapsible when it has children, or when it says it will load them later:

getChildren: async (parent) => {
if (!parent) {
return [
{ label: 'Arrays & Hashing', icon: 'symbol-array', expandable: true }, // lazy
{ label: 'README.md', icon: 'file' }, // leaf
{ label: 'Group', collapsed: 'expanded', children: [{ label: 'child' }] },
];
}
return loadProblems(parent.id ?? parent.label); // called on expand
},
Node shapeRenders as
children: [...] (non-empty)collapsible, children already in hand
expandable: truecollapsible, getChildren(node) runs on expand
neitherleaf — no expand arrow

expandable exists because an omitted children cannot mean both “leaf” and “load later”: most leaves never set the field, so treating undefined as lazy would put a useless expand arrow on every one of them.

collapsed: 'expanded' | 'collapsed' sets the initial state (default collapsed), and showCollapseAll on the view toggles the Collapse All button (default true).

Clicks

A node can carry panel, command, or run. When it carries a command, the clicked node is forwarded to the handler — a data-driven tree’s command is almost always about which item was clicked:

run: (vscode, ctx, node) => openProblem((node as TreeNode).id),

Command references from tree nodes, menu items and status-bar items resolve by either the registry key (the filename) or the id declared on the def. So a file src/commands/refresh.ts exporting defineCommand({ id: 'refreshCatalog' }) can be referenced as either name without a runtime “unknown command” error.

Keeping a view live

watch receives a refresh callback and returns an unsubscribe — the same shape used by status bar items and decorations:

watch: (refresh) => watchEntity(Problems, refresh),

See Reactivity for the store and watchEntity side of it.