Design system
Docs
Surfaces

Session list and sidebar

The rail: workspaces, session rows, filters, previews, context menus, drag to split, and the resize handle

HostImplementationNote
Primitive@nyte-ai/ui/collapsible, /toggle, /button, /preview-card, /context-menu
Desktopchrome/sidebar.tsxSidebarPane owns geometry; Sidebar owns rows; WorkspaceControls owns filters
Terminaltree-selector.tscommit tree picker; no session list (later pass)

Parts

ComponentFilePure?Purpose
SidebarPanechrome/sidebar-pane.tsxno"The rail's seat in the stage owns visibility and the resize handle, so the rail itself only ever renders at its full width."
Sidebarchrome/sidebar.tsxno"The rail: new chat, search, and customize on top, then a persistent workspace collection."
WorkspaceControlschrome/sidebar-filter.tsxyesFilter menu (grouping, ordering, show, status, PR, environment, source) and folder actions.
SessionPreviewCardchrome/sidebar-session-preview.tsxyesHover card with title and context, wrapping the row and its context menu.
SettingsNavigationchrome/settings-navigation.tsxnoReplaces the rail on /settings/$section.

Geometry

From sidebar-pane.tsx: 220px by default, clamped 190–400px, an 8px pointer target on the trailing edge, 8px keyboard steps. The width is written to --nyte-sidebar-width on every pointer move and committed to the store on release; cancel restores the start width. Hidden, the rail stays mounted with aria-hidden and inert, so hiding "changes geometry immediately."

Resize handle: role="separator", tabIndex=0, aria-label="Resize sidebar", aria-orientation="vertical", aria-valuemin/max/now.

KeyEffect
ArrowLeft / ArrowRight±8px
Shift + arrow±32px
Home / Endmin / max

Rows

SessionRow is an @nyte-ai/ui/button rendered as the row, with aria-current="page" when active, a StatusDot, and a hover-revealed action cluster (data-nyte-session-row-actions). WorkspaceRow is a Collapsible header with aria-current="location".

  • Hover intent: 50ms after pointerenter the row calls warmThread; pointerdown and focus warm immediately. "A short hover intent warms local thread data."
  • Rename: an input that commits on Enter, cancels on Escape, selects all on focus. Edit state lives at the rail root "so a directory refetch cannot drop an in-progress rename."
  • Layout animation uses motion's LayoutGroup; layoutId="selected-session" is set only when the panel is visible, because "hidden panels must never become shared-layout destinations." Duration and easing are read from --_sidebar-motion-duration / --_sidebar-motion-easing and honor prefers-reduced-motion live.

Context menus

Per session (ContextMenu from Menu): Pin/Unpin, Rename, Open to the side, Copy title, Archive/Restore, Delete (danger). Per workspace: New chat, Archive all chats, Remove from sidebar (danger). Delete and archive-all open a ConfirmDialog.

"Open to the side" is the keyboard path to splitting: the drag source is pointer-only, from layout/session-dnd.tsx:

The row itself is the drag source, so only the pointer sensor is wired: a keyboard sensor would claim Enter and Space before the row's click. Keyboard users split panes from the row's context menu instead.

Drag to split

SessionDndProvider wraps the stage with a DndContext (pointer sensor, activation distance SESSION_DRAG_ACTIVATION_DISTANCE, no auto-scroll). A custom collision detector maps the pointer to center | top | bottom | left | right per pane and refuses a drop onto the pane already showing the session. DragOverlay shows the title at zIndex 10_000; the wrapper sets data-nyte-session-drag-active. useSessionPaneDropTarget(paneId) registers five droppables on one element; useSessionDropTarget() drives the DropPreview in the thread screen.

Filters

WorkspaceControls opens a Menu (side="right") of MenuSubmenus with MenuRadioGroup/MenuRadioItem (closeOnClick={false}) and MenuCheckboxItems, plus a MenuGroup "Filters" with a Reset action when any filter is active. The trigger sets data-nyte-active in that case.

Preview card

SessionPreviewCard wraps the row in @nyte-ai/ui/preview-card: delay 600, closeDelay 100, side="right", align="start", popup aria-label="Details for {title}". If a contextMenu is provided the trigger is also wrapped in a ContextMenu labelled Actions for {title}.

AccountFooterMenu: a Menu with the GitHub account and settings entry; the settings toggle carries aria-keyshortcuts for ⌘,.