Skip to content

Commit 4611452

Browse files
committed
sessions: restore minimized split on activation
Expand a minimized Sessions or Editor part when it receives pointer or keyboard focus while preserving the widths of unrelated panes.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d29943e commit 4611452

4 files changed

Lines changed: 66 additions & 3 deletions

File tree

.github/skills/sessions/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Then read the relevant spec for the area you are changing (see table below). If
2626

2727
## Common Pitfalls
2828

29+
- **Minimum-size activation across the Sessions/Editor split must be symmetric**: when either part is at minimum width, pointer or keyboard activation expands it by shrinking its sibling to minimum width. Implement and test both directions together; a one-sided Sessions-only handler does not match editor-grid behavior.
30+
2931
- **Animation performance must preserve perceptual smoothness**: reducing a continuous title shimmer to 10 stepped updates per second makes the sweep visibly choppy even if paint counts improve. Use a smooth baseline such as 30 updates per second, then measure the remaining performance win; do not optimize decorative motion by callback counts alone.
3032

3133
- **Pet placement must align the visible sprite, not only its absolute-positioning box**: anchoring the button at `bottom: 100%` leaves the pet visually detached because the input stack has top padding and transient confirmation/question surfaces add their own top margin. Keep the host on the complete stack and derive the optical offset from the actual input-to-host inset, capped at the confirmation/question alignment; one fixed deeper offset makes the bare input look overlapped.

src/vs/sessions/LAYOUT.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ The **Sessions Part is the flexible ("remaining width") view** in the top-right
6464

6565
The Sessions Part-to-Editor gap and the gap above the bottom Panel share `AGENTS_FLOATING_PANEL_GAP` in TypeScript layout and its registered CSS token, `--vscode-agents-layout-floatingPanelGap`. Their grid sashes keep the split boundaries unchanged, but expand and shift their hit areas to fill those visual gaps exactly. Each shows the standard persistent three-dot gripper at rest and yields to the full sash highlight while hovered or dragged. The Auxiliary Bar's leading padding and part-internal sashes retain their independent geometry.
6666

67+
When either the Sessions Part or Editor has been resized to its minimum width, activating that part by pointer or keyboard focus restores it to the available width by resizing its sibling to minimum width. This mirrors minimized editor-group activation while targeting only the Sessions/Editor pair, so the Sidebar and Auxiliary Bar retain their established widths.
68+
6769
Editor-content overlays must use the editor pane container rather than the editor-group root. In the single-pane layout, the group spans both the editor and the docked detail panel while the pane container is inset to the editor's actual bounds; anchoring feedback controls such as the Submit toolbar to the group would place them over the detail panel.
6870

6971
### 2.3 Layout Priority Model

src/vs/sessions/browser/workbench.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import './media/workbench.css';
99
import './media/phoneLayout.css';
1010
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js';
1111
import { Emitter, Event, setGlobalLeakWarningThreshold } from '../../base/common/event.js';
12-
import { addDisposableListener, getActiveDocument, getActiveElement, getClientArea, getWindowId, getWindows, IDimension, isAncestorUsingFlowTo, isHTMLElement, size, Dimension, runWhenWindowIdle } from '../../base/browser/dom.js';
12+
import { addDisposableGenericMouseDownListener, addDisposableListener, EventType, getActiveDocument, getActiveElement, getClientArea, getWindowId, getWindows, IDimension, isAncestorUsingFlowTo, isHTMLElement, size, Dimension, runWhenWindowIdle } from '../../base/browser/dom.js';
1313
import { DeferredPromise, RunOnceScheduler } from '../../base/common/async.js';
1414
import { isFullscreen, onDidChangeFullscreen, isChrome, isFirefox, isSafari } from '../../base/browser/browser.js';
1515
import { mark } from '../../base/common/performance.js';
@@ -1089,6 +1089,8 @@ export class Workbench extends Disposable implements IAgentWorkbenchLayoutServic
10891089
editorPartContainer.classList.add('part', 'editor');
10901090
editorPartContainer.id = Parts.EDITOR_PART;
10911091
editorPartContainer.setAttribute('role', 'main');
1092+
this._register(addDisposableListener(editorPartContainer, EventType.FOCUS_IN, () => this._restoreEditorPartOnActivation()));
1093+
this._register(addDisposableGenericMouseDownListener(editorPartContainer, () => this._restoreEditorPartOnActivation()));
10921094
this._editorPartContainer = editorPartContainer;
10931095

10941096
mark('code/willCreatePart/workbench.parts.editor');
@@ -1103,6 +1105,8 @@ export class Workbench extends Disposable implements IAgentWorkbenchLayoutServic
11031105
sessionsPartContainer.classList.add('part', 'sessionspart', 'basepanel', 'right', AGENTS_PART_CARD_CLASS);
11041106
sessionsPartContainer.id = Parts.SESSIONS_PART;
11051107
sessionsPartContainer.setAttribute('role', 'main');
1108+
this._register(addDisposableListener(sessionsPartContainer, EventType.FOCUS_IN, () => this._restoreSessionsPartOnActivation()));
1109+
this._register(addDisposableGenericMouseDownListener(sessionsPartContainer, () => this._restoreSessionsPartOnActivation()));
11061110

11071111
mark(`code/willCreatePart/${Parts.SESSIONS_PART}`);
11081112
this.getPart(Parts.SESSIONS_PART).create(sessionsPartContainer);
@@ -1111,6 +1115,34 @@ export class Workbench extends Disposable implements IAgentWorkbenchLayoutServic
11111115
this.mainContainer.appendChild(sessionsPartContainer);
11121116
}
11131117

1118+
private _restoreSessionsPartOnActivation(): void {
1119+
if (!this.workbenchGrid || !this.isVisible(Parts.EDITOR_PART, mainWindow)) {
1120+
return;
1121+
}
1122+
1123+
this._restoreMinimizedPartOnActivation(this.sessionsPartView, this.editorPartView);
1124+
}
1125+
1126+
private _restoreEditorPartOnActivation(): void {
1127+
if (!this.workbenchGrid || !this.isVisible(Parts.EDITOR_PART, mainWindow) || !this.isVisible(Parts.SESSIONS_PART)) {
1128+
return;
1129+
}
1130+
1131+
this._restoreMinimizedPartOnActivation(this.editorPartView, this.sessionsPartView);
1132+
}
1133+
1134+
private _restoreMinimizedPartOnActivation(target: ISerializableView, sibling: ISerializableView): void {
1135+
const targetSize = this.workbenchGrid.getViewSize(target);
1136+
if (targetSize.width !== target.minimumWidth) {
1137+
return;
1138+
}
1139+
1140+
const siblingSize = this.workbenchGrid.getViewSize(sibling);
1141+
if (siblingSize.width > sibling.minimumWidth) {
1142+
this.workbenchGrid.resizeView(sibling, { width: sibling.minimumWidth, height: siblingSize.height });
1143+
}
1144+
}
1145+
11141146
private createCustomViewGridPart(): void {
11151147
const customViewGridPartContainer = document.createElement('div');
11161148
customViewGridPartContainer.classList.add('part', 'customviewgridpart', 'basepanel', 'right', AGENTS_PART_CARD_CLASS);

src/vs/sessions/test/browser/workbench.test.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ suite('Sessions - Workbench', () => {
6060
const updateMobileCustomViewNavigation = Reflect.get(Workbench.prototype, '_updateMobileCustomViewNavigation') as (this: ITestWorkbench) => void;
6161
const isVisible = Workbench.prototype.isVisible as (this: ITestWorkbench, part: Parts) => boolean;
6262
const toggleSecondarySideBar = Workbench.prototype.toggleSecondarySideBar as (this: ITestWorkbench) => void;
63+
const restoreSessionsPartOnActivation = Reflect.get(Workbench.prototype, '_restoreSessionsPartOnActivation') as (this: ITestWorkbench) => void;
64+
const restoreEditorPartOnActivation = Reflect.get(Workbench.prototype, '_restoreEditorPartOnActivation') as (this: ITestWorkbench) => void;
6365

6466
// --- Harness ------------------------------------------------------------
6567

@@ -147,8 +149,8 @@ suite('Sessions - Workbench', () => {
147149
}
148150

149151
function createHost(options: IHostOptions = {}): ITestWorkbench {
150-
const editorPartView = {};
151-
const sessionsPartView = {};
152+
const editorPartView = { minimumWidth: 300 };
153+
const sessionsPartView = { minimumWidth: 300 };
152154
const sideBarPartView = {};
153155
const auxiliaryBarPartView = {};
154156
const panelPartView = {};
@@ -288,6 +290,31 @@ suite('Sessions - Workbench', () => {
288290

289291
// --- Editor split / reveal ---------------------------------------------
290292

293+
test('activating a minimized Sessions or Editor Part resizes its sibling to minimum width', () => {
294+
const sessionsMinimized = createHost({ sessionsWidth: 300, editorWidth: 700, partVisibility: { editor: true } });
295+
const editorMinimized = createHost({ sessionsWidth: 700, editorWidth: 300, partVisibility: { editor: true } });
296+
const neitherMinimized = createHost({ sessionsWidth: 301, editorWidth: 301, partVisibility: { editor: true } });
297+
const editorHidden = createHost({ sessionsWidth: 300, editorWidth: 700, partVisibility: { editor: false } });
298+
299+
restoreSessionsPartOnActivation.call(sessionsMinimized);
300+
restoreEditorPartOnActivation.call(editorMinimized);
301+
restoreSessionsPartOnActivation.call(neitherMinimized);
302+
restoreEditorPartOnActivation.call(neitherMinimized);
303+
restoreSessionsPartOnActivation.call(editorHidden);
304+
305+
assert.deepStrictEqual([
306+
sessionsMinimized.resizes,
307+
editorMinimized.resizes,
308+
neitherMinimized.resizes,
309+
editorHidden.resizes,
310+
], [
311+
[{ width: 300, height: 800 }],
312+
[{ width: 300, height: 800 }],
313+
[],
314+
[],
315+
]);
316+
});
317+
291318
test('tracks editor pane visibility across editor and auxiliary bar changes', () => {
292319
const host = createHost({ partVisibility: { editor: false, auxiliaryBar: true } });
293320

0 commit comments

Comments
 (0)