sessions: address CCR feedback on single-pane refinements

- remove dead singlePaneEditorTitleAuxiliaryBarOrder const + void stmt
- remove unused ICommandService dependency from the base layout controller
  (and its now-dead test-harness stub)
- update SINGLE_PANE_SCENARIOS.md + desktopSessionLayoutController.md for the
  merged controller, transition-triggered R1, and the Toggle Details command

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Sandeep Somavarapu
2026-07-05 05:12:00 +02:00
parent 28a74af283
commit ea096becc2
14 changed files with 28 additions and 34 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
screenshots/after-cmd-n.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -112,13 +112,13 @@ The **auto-managed** tabs (the pinned Changes tab and the default File tab) are
## 5. Detail panel content (driven by the active tab)
`DetailPanelController` maps the active editor tab to the detail content, **reveal-on-activate**:
The single-pane layout controller (`SinglePaneDesktopSessionLayoutController`) maps the active editor tab to the detail content. By default the detail panel is **closed** for a created session (Editor-only); it is opened via **Toggle Details** (or restored per-session), and while visible its container follows the active tab (the one exception is restoring the detail after a transient browser-tab hide):
| Active tab | Detail panel |
|-----------|--------------|
| **Changes** | Branch Changes file list + Checks — revealed when the Changes tab activates |
| **File** (Explorer) | Files/Explorer tree — revealed when a File tab activates |
| **Browser** | **Hidden** (transiently) while the Browser tab is active |
| **Changes** | Branch Changes file list + Checks — shown (Changes container) while the detail is visible |
| **File** (Explorer) | Files/Explorer tree — shown (Files container) while the detail is visible |
| **Browser** | **Hidden** (transiently) while the Browser tab is active; restored when switching back |
Rules:
- **Reveal on activate, respect after.** Switching to a Changes/File tab reveals the detail with the
@@ -229,9 +229,9 @@ No side pane at all — the detail panel and managed tabs are not shown; the cha
| Docked layout, hide/show editor, detail width, sash-reveal sync, grid | `browser/workbench.ts` |
| Docked panel overlay + resize sash | `browser/dockedAuxiliaryBarController.ts` |
| Editor tab bar kept visible when content hidden; sash-reveal trigger | `browser/parts/editorPart.ts` |
| Active tab → detail container mapping (reveal-on-activate, browser transient) | `contrib/changes/browser/detailPanelController.ts` |
| Managed Changes + File tabs (suppressed opens) | `contrib/changes/browser/changesTabController.ts` |
| Active tab → detail container mapping (browser transient) | `contrib/layout/browser/singlePaneDesktopSessionLayoutController.ts` |
| Managed Changes + File tabs (suppressed opens) | `contrib/layout/browser/singlePaneDesktopSessionLayoutController.ts` |
| Startup controller selection | `contrib/layout/browser/sessions.layout.contribution.ts` |
| New-session level-triggered editor hide (R1/R2) | `contrib/layout/browser/singlePaneDesktopSessionLayoutController.ts` |
| New-session transition-triggered editor hide (R1) | `contrib/layout/browser/singlePaneDesktopSessionLayoutController.ts` |
| Hide Editor chevron, Maximize, add-tab actions | `contrib/editor/browser/editor.contribution.ts`, `contrib/editor/browser/addTabActions.ts` |
| Detail (aux bar) toggle | `browser/layoutActions.ts` |
| Toggle Details command + editor-title item | `contrib/layout/browser/singlePaneDesktopSessionLayoutController.ts` |

View File

@@ -50,9 +50,7 @@ const editorTitleActionsWhen = ContextKeyExpr.and(
IsAuxiliaryWindowContext.toNegated(),
IsTopRightEditorGroupContext);
const singlePaneEditorTitleMaximizeOrder = 1000000;
const singlePaneEditorTitleAuxiliaryBarOrder = 1000001;
const singlePaneEditorTitleHideEditorOrder = 999999;
void singlePaneEditorTitleAuxiliaryBarOrder;
class SinglePaneAddTabContribution extends Disposable implements IWorkbenchContribution {

View File

@@ -18,7 +18,6 @@ import { Categories } from '../../../../platform/action/common/actionCommonCateg
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { ILifecycleService } from '../../../../workbench/services/lifecycle/common/lifecycle.js';
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
@@ -143,7 +142,6 @@ export abstract class BaseLayoutController extends Disposable {
@ISessionChangesService protected readonly _sessionChangesService: ISessionChangesService,
@IChangesViewService protected readonly _changesViewService: IChangesViewService,
@IViewDescriptorService protected readonly _viewDescriptorService: IViewDescriptorService,
@ICommandService protected readonly _commandService: ICommandService,
@IContextKeyService protected readonly _contextKeyService: IContextKeyService,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@ILifecycleService protected readonly _lifecycleService: ILifecycleService,

View File

@@ -124,15 +124,18 @@ menu item, keybinding, and command-palette entry are inert.
#### D11 — Single-pane new-session views are Files-only
In single-pane detail-panel mode only, while a new (uncreated) workspace session view is active, the
editor content is hidden by default so the editor tab bar and Files detail panel remain without showing
editor content. The hide is driven by the **session and active editor**, not by the editor's visibility:
`_registerSinglePaneNewSessionRules` hides the editor part whenever this is a new-session view whose active
editor is not real content (the managed empty tab or none), and leaves it alone once a real file/browser
editor is active. It deliberately does **not** react to the editor becoming visible, so an explicit reveal
sticks — opening a file reveals the editor before the file becomes the active editor (Task 4), and toggling
the detail panel off reveals the empty editor so the side pane does not vanish (Task 2). Spurious width-based
reveals are blocked at the source via `setSuppressDockedEditorRevealSync(true)` while the view has no real
content. The shared D3b/D9b new-session side-pane visibility state is unchanged: once the user hides the
side pane for a new session, it stays hidden for later new sessions.
editor content. The hide is **transition-triggered**: `_registerNewSessionRules` (a no-op base hook
overridden by `SinglePaneDesktopSessionLayoutController`) hides the editor part when it **just became
visible**, or when the new-session view was **just entered** with the editor already visible (an
inherited-visible editor from the previous session), and only while the active editor is not real content
(the managed empty tab or none). It leaves the editor alone once a real file/browser editor is active.
Switching to a managed tab (e.g. the Files placeholder) while the editor is **already** visible does not
hide it — only a visibility transition or entering the view does. An explicit reveal sticks — opening a
file reveals the editor before the file becomes the active editor (Task 4), and toggling the detail panel
off reveals the empty editor so the side pane does not vanish (Task 2); entering the view always resets to
editor-closed, so a stale cross-session explicit reveal cannot carry over. A momentary width-based reveal
(e.g. a sash drag) is re-hidden by the same rule. The shared D3b/D9b new-session side-pane visibility state
is unchanged: once the user hides the side pane for a new session, it stays hidden for later new sessions.
### Scenario: a cramped (small) window
On a small window there isn't room for the sessions sidebar, the editor, and the side pane all at once.
@@ -256,13 +259,14 @@ and hands control back once the user reopens the sessions sidebar manually.
collapsing an already editor-only state) just captures the resulting state, so an explicit aux-bar hide
— including one whose editor-only state is restored when the pane re-opens — is never turned into a
collapse.
- **Single-pane new-session rule [D11]** — `LayoutController` exposes
`_registerSinglePaneNewSessionRules` as a no-op hook. `SinglePaneDesktopSessionLayoutController`
overrides it with an `autorun` over the active session, multi-session state, and editor maximized
state; on entering an uncreated workspace session that is not a quick chat, it hides `EDITOR_PART` under
`suppressEditorPartAutoVisibility()` and remembers that session resource. The autorun does not read
editor visibility as an observable, so later editor reveals do not retrigger the hide. D3b continues to
open the Files container unless the shared new-session side-pane state says it is hidden.
- **Single-pane new-session rule [D11]** — `LayoutController` exposes `_registerNewSessionRules` as a
no-op hook. `SinglePaneDesktopSessionLayoutController` overrides it with an `autorun` over the active
session, multi-session state, editor maximized state, the active editor, and **the editor-part
visibility**; on an uncreated workspace session that is not a quick chat, it hides `EDITOR_PART` (under
`suppressEditorPartAutoVisibility()`) when the editor just became visible or the view was just entered
with the editor visible, tracking the previous editor-visible / in-new-session-view values across runs so
a mere active-editor change (e.g. switching to the Files placeholder) does not retrigger the hide. D3b
continues to open the Files container unless the shared new-session side-pane state says it is hidden.
- **Responsive sidebar [D7]** — `_registerResponsiveSidebar` derives `spaceConstrained = enabled && small
&& editor visible && aux-bar visible && !multipleSessionsVisible` from the experimental setting
`sessions.layout.autoCollapseSessionsSidebar` (`observableConfigValue`, default `product.quality !==

View File

@@ -10,7 +10,6 @@ import { constObservable, ISettableObservable, observableValue } from '../../../
import { URI } from '../../../../../base/common/uri.js';
import { isEqual } from '../../../../../base/common/resources.js';
import { mock } from '../../../../../base/test/common/mock.js';
import { ICommandEvent, ICommandService } from '../../../../../platform/commands/common/commands.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
@@ -161,7 +160,6 @@ export interface ITestLayoutHarness {
onDidChangeEditorMaximized: Emitter<void>;
onDidActiveEditorChange: Emitter<void>;
onWillOpenEditor: Emitter<IEditorWillOpenEvent>;
onDidExecuteCommand: Emitter<ICommandEvent>;
onDidCloseEditor: Emitter<{ editor: EditorInput }>;
onDidEditorsChange: Emitter<void>;
onDidLayoutMainContainer: Emitter<IDimension>;
@@ -238,7 +236,6 @@ export function createTestHarness(store: DisposableStore, options: ICreateOption
onDidChangeEditorMaximized: store.add(new Emitter<void>()),
onDidActiveEditorChange: store.add(new Emitter<void>()),
onWillOpenEditor: store.add(new Emitter<IEditorWillOpenEvent>()),
onDidExecuteCommand: store.add(new Emitter<ICommandEvent>()),
onDidCloseEditor: store.add(new Emitter<{ editor: EditorInput }>()),
onDidEditorsChange: store.add(new Emitter<void>()),
onDidLayoutMainContainer: store.add(new Emitter<IDimension>()),
@@ -307,9 +304,6 @@ export function createTestHarness(store: DisposableStore, options: ICreateOption
instaService.stub(IChangesViewService, new class extends mock<IChangesViewService>() {
override setChangesetId(): void { }
});
instaService.stub(ICommandService, new class extends mock<ICommandService>() {
override readonly onDidExecuteCommand = harness.onDidExecuteCommand.event;
});
instaService.stub(ILifecycleService, new class extends mock<ILifecycleService>() {
// Resolves only when a test opts in via `activateAux`, so the single-pane
// managed-tab / detail-panel behaviour is not spun up otherwise.