Sidebar widths and collapsed state

Both sidebars share width, drag and collapse behavior. Each sidebar owns only its navigation, state sources and main content.

State constraints

StateWidth rulesBehavior
Left expanded180–480pxDrag from the right edge; stop changing at the range limit
Right expanded240–720pxDrag from the left edge; stop changing at the range limit
CollapsedFixed at 49pxBoth sides use the same 150ms width animation
Expand againRestore the valid width from before collapseUse the same width animation and retain the most recently adjusted width

Implementation boundaries

components/layout/use-resizable-rail.ts is the sole implementation of width behavior: it saves expanded widths, enforces limits, handles pointer capture, generates expanded/collapsed styles and controls transitions. The sidebars supply their own limits and growth directions instead of maintaining separate event listeners. Handles share the rail-resize-handle style in base.css, with a hit area extending about 8px on either side of the divider without affecting layout width. The shell permits handle overflow while the inner content container remains clipped. Only the primary button starts a drag; touch panning and text selection on the handle are disabled.

Component boundaries

FileResponsibility
sidebar/sidebar.tsxLeft shell, persisted toggle, New chat, favorites and session composition
sidebar/sidebar-primary-nav.tsxFixed navigation, route highlighting, navigation icon animation and Programs refresh
right-sidebar/right-sidebar.tsxRight shell, file entry and view container
right-sidebar/detail-panel.tsxDetails/Context switching and detail rendering for the selected DAG node

Do not split the session list or AppShell in this restructuring: their independent state and interaction boundaries require a change separate from moving sidebar shells.

Acceptance

Drag each sidebar to its limit and collapse it: it should smoothly become 49px wide within 150ms. Expanding again restores its previous width. Dragging can begin within about 8px on either side of the divider. Hovering and dragging change only the cursor, with no extra highlight. Dragging continues after the pointer leaves the handle. Release, pointer cancellation or window blur must restore the collapse animation. Window-level end events supplement captured handle events; a move event that detects a released button also ends the drag.

Implementation status

Implemented: both sidebars use the same width behavior module. Fixed navigation and detail rendering have moved out of the shell files; shell components only compose them.