Files
paseo/docs/explorer-sidebar.md
T
Mohamed Boudraandpaseo-bot[bot] e135e9c101 Open files and subagents in a closable sheet on phones (#6827)
* feat(app): open files and subagents in a closable sheet on compact

On phones, opening a file, a provider subagent, or a Paseo subagent now shows it
in a bottom sheet over the current tab instead of adding a tab, so the tab strip
stays for terminals and root agents. Paseo subagents get "Open as tab" to move
the same tab (same tabId and state) into a full tab with a composer; agents in a
sheet hide the composer.

The decision lives in the workspace layout store's openTab, driven by a
per-kind `compactSheet` rule in the panel manifest, so every open path (chat
links, Explorer, subagents track, notifications, plugins) follows it without
call-site branching, and a new kind opts in with one manifest entry. The app
shell publishes the layout mode (compact / single-pane / split-panes) once, so
callers stop passing isCompact for placement.

A sheet holding unsaved edits is never implicitly displaced; later opens go to
tabs underneath, and closing it goes through the existing discard guard. The
editor host is position: sticky because CodeMirror's scrollIntoView otherwise
scrolls the bottom sheet's container and drags the sheet to full height.

* test(app): fail the compact sheet snap check when the sheet is oversized

---------

Co-authored-by: paseo-bot[bot] <paseo-bot[bot]@users.noreply.github.com>
2026-10-14 00:20:10 +07:00

7.5 KiB
Raw Blame History

Explorer sidebar and side pane

The Explorer sidebar and the side pane share panel implementations, but they have different shell contracts.

Surface Purpose Lifecycle
Explorer sidebar Files and Changes navigation Cmd+E shows or hides the dedicated dock
Side pane Ordinary workspace content Created and closed like any workspace pane
Workspace sheet Compact look-ups Holds one tab; closes or becomes a tab

Panel host contract

Every desktop panel registers its supported PaneHost values and presentation. Launchers derive fixed-target labels and icons from that registration, filter by host, and never substitute one panel type for another. Tab moves reject unsupported destinations, and placement resolves only to a compatible pane.

Files and Changes are the Explorer defaults. Their panel manifests mark them as singletons, so a pane’s + menu omits each while that pane already contains it. Closing one makes its menu item available again. Other compatible tabs, including agents, terminals, files, and diffs, can move between Explorer and main panes. Keep panel implementations independent of either shell. WorkspacePanelHost owns mounting and retention, while each shell owns its tabs, focus, dragging, resizing, and shortcuts.

Workspace sheet

On compact layouts, files and subagents open in a closable sheet over the workspace, so the tab switcher keeps only agents and terminals. A panel kind opts in with compactSheet in its manifest (packages/app/src/panels/panel-manifest.ts), and the layout store's openTab decides between sheet and tab, so callers open targets the same way on every layout. The sheet holds one ordinary WorkspaceTab, which is not persisted.

The sheet is modal and covers the whole compact column, header included. It renders in the workspace tree rather than through IsolatedBottomSheetModal: its panel needs the navigator and every workspace provider an ordinary pane has, and a portal drops them (floating-panels.md Gotcha 7).

A sheet holding unsaved work is never implicitly displaced; only its guarded close removes it. Moving its tab to another host remounts the panel, and the file editor keeps unsaved text in the mounted view, so the move would drop it. Widening the layout is the known exception: it still moves the sheet's tab into a pane, and edits the autosave has not started writing are lost.

Explorer sidebar

packages/app/src/workspace-tabs/explorer-sidebar.ts owns show, hide, toggle, and view selection. On desktop, the shell is rendered outside the workspace split canvas so it divides the full workspace, including the header. It has its own persisted width and resize handle. Main-pane splits never read or modify that width.

packages/app/src/workspace-tabs/open-supporting-view.ts owns semantic Changes and pull-request opens. Compact and wide native layouts select the matching Explorer tab. Desktop Changes opens follow the shared diff preference. Desktop pull requests use their Main panel, On the side, or Explorer sidebar setting. Automatic PR discovery follows that preference once per workspace without interrupting the user's work. Closing the tab opts that workspace out of future automatic opens, even for a different PR; moving or reordering it remains the user's choice. Callers request the content and never choose the shell. The composer Changes pill is a two-stage desktop action: it first reveals Explorer on Changes, then routes later presses to the working diff through the shared diff preference.

The persisted layout still contains the Explorer pane so tabs survive reloads. The renderer removes that pane from the workspace split tree and docks it separately. Persisted identifiers retain the literal "explorer" pane id and explorerPaneIdByWorkspace key for compatibility.

Explorer uses the shared workspace tab row and ordinary tab context menus. Files and Changes hide their close buttons through the panel manifest; close them from the tab context menu. Other tabs reveal the close control on hover. The + menu opens compatible panels in the dock and omits Agent and terminal profiles. Agents and terminals can still be dragged into Explorer. Bulk-close actions apply only to the dock's tabs. Explorer tabs can be reordered and dragged between compatible panes, but the dock cannot be split or maximized. Selecting an Explorer tab does not change workspace focus.

Cmd+E shows or hides Explorer without changing its selected view. Compact layouts use the combined full-screen Explorer overlay for Changes, Files, and pull requests, and close it after a file opens. Compact Changes has no tree rail; its overview is the Jump to file action (packages/app/src/git/jump-to-file/), a sheet over the same changed-files tree the desktop rail renders. Wide native layouts without pane splits use the same combined content in a resizable inline dock; opening a file leaves that dock visible. Both presentations keep their selection in the panel store and reuse the layout store's per-workspace Explorer width. They do not create a second Explorer lifecycle.

Side pane

packages/app/src/workspace-tabs/open-beside.ts owns content opened beside the user's work. The layout store remembers one ordinary pane per workspace. The first side open creates a full-height right split around the workspace root; later side opens reuse it. Only the split-panes layout mode opens beside. Compact and single-pane layouts (touch tablets) open the same target in the main pane, so callers never check whether splits render.

Removing a pane clears its remembered id; a later side open creates a new pane. The last visible ordinary pane stays when its final tab closes and shows the New launcher. An empty workspace does not automatically create an agent draft tab; choosing Agent opens one. Explorer cannot replace the workspace canvas, even when visible. Restoring a saved layout enforces the same rule while preserving Explorer and saved tab content. There is no hidden side-pane lifecycle.

Placement intent still controls existing tabs:

Mode New target Existing target
pane opens in the requested pane moves to the requested pane
prefer opens in the requested pane stays where the user placed it
focused opens in the focused pane focuses it where it already lives
ambient opens in a compatible pane focuses it where it already lives

Explicit Open to Side uses pane. Implicit opens use prefer, so a preference affects only a new target and never yanks an existing tab out of a user-selected pane.

Routing preferences

Desktop Settings → Layout → Open location has independent Main panel or On the side choices for Explorer Files, diffs, chat files, files opened from diffs, and subagents. They default to Main panel. Mobile ignores them.

Pull requests have a three-way open location: Main panel, On the side, or Explorer sidebar. Explorer sidebar is the default. Compact layouts always open pull requests in Explorer regardless of this desktop preference.

Panels request an implicit open through the narrow openPreferredTarget(target, source) pane contract. Entry points outside panels use openPreferredWorkspaceTarget. Do not branch on a specific shell inside a panel.