* fix(app): keep file navigation in place
Retargeting a File tab allocated a new tab identity, causing the tab track to unmount and remount during navigation. Preserve the identity for File-to-File changes while retaining new instances for cross-kind replacements.
* feat(app): expand workspace panel actions
Keep the Command Center aligned with workspace tab and pane primitives. Add explicit panel placement, focused open behavior, keyboard navigation for empty panes, and a Changes pill that toggles the visible Side panel without closing its tab.
* feat(app): refine workspace pane actions
Prioritize Changes when available, keep Files useful before a selection, and reserve desktop diff clicks for selection so comments require the explicit affordance. Move the Changes shortcut to Shift+G to avoid consuming a likely copy variant.
* feat(app): improve access to workspace changes
* feat(app): let the file panel close its tree rail
The file panel's tree rail was permanent on desktop — the changes panel
had a folder-tree toggle in its toolbar, the file toolbar had nothing, so
the rail was there whether you wanted it or not.
The changes panel's private DesktopTreeToggle becomes a shared
TreeRailToggle so both toolbars render the same affordance; that also
drops the treeToggleStyle prop threaded through ChangesToolbarProps,
which recomputed exactly what the component already built internally.
The i18n keys move out of workspace.git.diff into workspace.tree, since a
shared component reaching into the git namespace would rot.
The two rails keep independent flags — the file rail in panel-store, the
changes rail in useChangesPreferences — so closing one leaves the other
alone. FileTreeToggle wires itself to the store rather than taking a
callback: the bar renders several components below FilePanel, where the
rail actually lives.
Both rails also get narrower (320 -> 260 default, 200 -> 180 minimum).
The persist version bump resets treeRailWidth on purpose: nearly every
existing install carries the old 320 default, so without the reset the
narrower rail would only ever reach new users. Anyone who dragged their
rail to a deliberate custom width loses it once.
Note that MIN_CONTENT_WIDTH still clamps the rail to
`containerWidth - 240`, so in a narrow pane the new default never
applies — that floor, not the default, is what sets the width there.
* feat(app): make the side panel an empty workspace surface
The fixed, hideable side panel keeps supporting context beside an agent without seeding content. Pane-local choices stay local, while implicit supporting opens follow a persisted routing preference.
* test(app): exercise side panel policy through its capability boundary
The browser rail journey already covers the real tree-toggle behavior. Side-panel policy tests now pass their platform capability through the public boundary instead of replacing a production module.
* test(app): expect an empty side panel after upgrade
* test(app): expect an empty first split pane
* test(app): account for the empty side panel in split retention
* feat(app): make workspace panel tabs independent
Give explicit New actions fresh tab instances, keep panel state opaque to tab management, preserve File navigation context, and stabilize uncached File transitions.
* fix(app): preserve file conflicts during preview refresh
Keep the current preview mounted for same-target reads and conversions so the editor retains its conflict observation state. Clear retained previews only when the file target changes.
Tapping a subagent or provider subagent from the composer track now opens its tab in the explorer companion pane when desktop split panes are active, keeping the parent agent visible in the primary pane.
Mobile and compact layouts continue using single-pane tab navigation, and cross-workspace subagents continue routing to their own workspace.
Layout measurement excluded pill margin on some renderers, collapsing the intended gap above visible tracks. Keep the two-pill rail on one line and derive its complete tail clearance from shared geometry so chat switching and layout callbacks cannot change the result.
Keep agent and task pills as a transparent transcript overlay while measuring their rendered height for scroll-tail clearance. Share geometry and spacing with the import-session pill so borders, type, corners, and composer separation match across platforms.
The subagents track and the task list were stacked cards above the
composer. Each one pushed the composer further down the pane and painted
an opaque band the timeline stopped at, and both spent a full row on a
count that fits in a pill.
Both are now pills that open a panel — a popover on wide screens, a sheet
on compact ones, from the existing menu engine rather than a fourth
floating-panel implementation. The row frame is shared, so a subagent row
and a task row sit on the same rail whatever their contents.
The bar mounts inside the transcript's animated container and pins itself
to the bottom edge, so scrolled content passes under the pills instead of
stopping above them. It stays a child of that container rather than a
sibling floating over the composer: on Android a view outside its parent's
bounds paints but takes no touches, and every pill is pressable. The
container carries the same keyboard transform the composer does, so the
pills track the composer's top edge when the keyboard moves.
The tracker state moves out of the composer with it. None of it was
composer state; it lived there because that is where it used to render.