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>
BIN
screenshots/after-cmd-n-again.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
screenshots/after-cmd-n.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
screenshots/after-reload.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
screenshots/after-session-select.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
screenshots/after-toggle-closed.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
screenshots/after-workspace-click.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
screenshots/before-reload-open.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
screenshots/before-session-select.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
screenshots/initial-state.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
@@ -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` |
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 !==
|
||||
|
||||
@@ -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.
|
||||
|
||||