T-SK5T-stacked-left-panel
State: planning/proposed · Impact: high · Complexity: large
The secondary region today renders exactly one thing at a time — either a page’s registered
content or the shell’s Browse fallback — so Agent Manager’s tree, the shell’s Browse, and an
eventual Git section cannot coexist in the left column the way the mock groups them. This task
builds a stacked, collapsible-section container for that region so several sources of content
sit together as named sections instead of competing for the one slot.
| Location | Role today |
|---|---|
packages/ts/app-frame/app/composables/usePaneRegistry.ts | Region-to-one-render-fn channel; a region holds exactly one filler |
packages/ts/app-frame/app/components/PaneContent.vue | Registers a page’s slot content into one named region on mount, clears it on unmount |
packages/ts/app-frame/app/components/PaneOutlet.vue | Reads and renders whatever one render fn a region currently holds |
packages/ts/app-frame/app/layouts/default.vue | secondary pane has two mutually exclusive fillers: a page-registered PaneOutlet, or the shell’s own fallback |
packages/ts/node-tree/src/NodeTreePane.vue | A role: 'section' row is a header inside one flat scrollable tree, not a separate collapsible container |
solutions/determined/docs/planning/desktop-framework/multi-vault-ux/f2-stacked-sidebar.html | Prior-art mock of the stacked sections this task builds toward |
solutions/determined/docs/planning/desktop-framework/multi-vault-ux/README.md | Narrates the mock set the stacked-sidebar mock belongs to |
solutions/determined/packages/shell/app/components/ShellVaultBrowsePane.vue | Today’s sole occupant of the secondary region’s shell-fallback slot |
solutions/determined/experiences/agent-pants/app/components/ApComponentTree.vue | Agent Manager’s tree; today fills the region directly through PaneContent, not as one of several sections |
Proposed
Section titled “Proposed”A new PaneSectionStack.vue in @sksizer/app-frame renders inside the secondary pane as
several independently collapsible sections, fed by a usePaneSections() registry that mirrors
usePaneRegistry’s shape — a region maps to an ordered list of sections rather than to one render
fn — with each section’s collapsed state persisted the way usePaneGroup persists pane state.
Agent Manager registers its component tree as one section, the shell registers Browse as another,
and the git task registers a third. The chosen density and grouping variant comes from
stories-shell-chrome-directions.
Approach
Section titled “Approach”- Design
usePaneSections(): a registry of{region, id, title, render, order}entries, provided above the layout the wayusePaneRegistryis, with register/unregister calls a descendant page or the shell’s ownapp.vuecan make. - Persist per-section collapsed state — either a small wrapper over
usePaneGroup’s existing storage-key pattern, or an extension lettingusePaneGrouptrack several sub-panes within one region; name the choice as an open decision. - Build
PaneSectionStack.vue: renders every registered section for its region in order, each with a header (title and collapse chevron) and a body that mounts the section’s render fn while expanded. layouts/default.vue: mountPaneSectionStackfor thesecondaryregion in place of the current single-PaneOutlet-or-fallback branch; a host that registers no section renders nothing, as today.ShellVaultBrowsePane.vue: register itself as a “Browse” section through the new API instead of filling the region directly.ApComponentTree.vue: register itself as an “Agent Manager” section through the same API.- Leave the registration point open for the git section (
git-panel-commit-with-validation) to fill later; do not build the git section’s content here. - Capture light and dark screenshots of the stacked panel, collapsed and expanded.
| Area | Note |
|---|---|
packages/ts/app-frame/app/composables/usePaneSections.ts | Per-region ordered section registry |
packages/ts/app-frame/app/components/PaneSectionStack.vue | Renders a region’s registered sections, each collapsible |
packages/ts/app-frame/app/layouts/default.vue | Mount PaneSectionStack for secondary |
solutions/determined/packages/shell/app/components/ShellVaultBrowsePane.vue | Register as a section instead of filling the region directly |
solutions/determined/experiences/agent-pants/app/components/ApComponentTree.vue | Register as a section |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: The
secondaryregion rendersPaneSectionStackwith the Agent Manager and Browse sections both present and independently collapsible. - AC-2: Collapsing one section leaves the other’s expanded state untouched, and both persist across a reload.
- AC-3: A host that registers no section renders the region exactly as before, so
usePaneRegistry’s existing single-filler contract stays intact formainandaux. - AC-4:
ShellVaultBrowsePane.vuerenders through the new section registry rather than fillingsecondarydirectly. - AC-5: Light and dark screenshots of the stacked panel, collapsed and expanded, are added to the S-0012 contact sheet.
Out of scope
Section titled “Out of scope”- The Git section’s own content — T-L7YP-git-panel-commit-with-validation registers it once this stack exists.
- Moving
mainorauxto the same stacked model; onlysecondarychanges here. - Drag-to-reorder sections.
Dependencies
Section titled “Dependencies”- T-PL39-stories-shell-chrome-directions supplies the density and grouping variant, with
f2-stacked-sidebar.htmlas prior art.
Discovery context
Section titled “Discovery context”From the user’s planning request of 2026-09-07.
Depends on
Section titled “Depends on”T-PL39-stories-shell-chrome-directions