Skip to content

Commit da1044f

Browse files
sessions: drop the composer card, give each control its own surface
Over a chat background the new-session composer sat on a single frosted card, while a started session gives every control its own surface. This makes the composer match the started session. - Delete `.new-chat-widget-content`'s card styling: the padding, blur, border, radius and shadow. - Give the bottom-row action labels and the workspace pills an opaque `--session-view-background` fill, and put a 2px gap between the agent host config controls now that they no longer share one surface. - Keep the hover tint opaque. `--vscode-toolbar-hoverBackground` is translucent, so it goes on `background-image` as a flat gradient and composites over the opaque fill instead of replacing it. - Frame the bottom-row chips when a chat background is set, the same move the in-session secondary toolbar makes in `chatView.css`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 9db4cf8 commit da1044f

5 files changed

Lines changed: 80 additions & 30 deletions

File tree

src/vs/sessions/contrib/chat/browser/media/chatView.css

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,6 @@
3838
overflow: hidden;
3939
}
4040

41-
.monaco-workbench.agent-sessions-workbench .part.sessionspart.has-chat-background .chat-view .sessions-chat-widget:not(.new-chat-in-session) .new-chat-widget-content {
42-
box-sizing: border-box;
43-
padding: var(--vscode-spacing-size120);
44-
background: color-mix(in srgb, var(--session-view-background) 86%, transparent);
45-
-webkit-backdrop-filter: blur(var(--vscode-spacing-size160)) saturate(1.08);
46-
backdrop-filter: blur(var(--vscode-spacing-size160)) saturate(1.08);
47-
border: var(--vscode-strokeThickness) solid color-mix(in srgb, var(--vscode-editorWidget-border, var(--vscode-widget-border)) 70%, transparent);
48-
border-radius: var(--vscode-cornerRadius-large);
49-
box-shadow: 0 var(--vscode-spacing-size80) var(--vscode-spacing-size240) color-mix(in srgb, var(--vscode-widget-shadow) 18%, transparent);
50-
}
51-
5241
.monaco-workbench.agent-sessions-workbench .part.sessionspart.has-chat-background .chat-view .interactive-session .chat-secondary-toolbar .action-label,
5342
.monaco-workbench.agent-sessions-workbench .part.sessionspart.has-chat-background .chat-view .interactive-session .chat-context-usage-widget {
5443
background-color: var(--vscode-chat-list-background, var(--vscode-button-secondaryBackground));

src/vs/sessions/contrib/chat/browser/media/chatWidget.css

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,30 @@
204204

205205
/* Match VS Code chat-secondary-toolbar sizing for action items in the bottom row
206206
* (e.g. session control pickers and repo config pickers) so they align with the
207-
* model picker font in the input toolbar. */
207+
* model picker font in the input toolbar. Each item carries its own opaque
208+
* surface so a chat background cannot show through it. */
208209
.new-chat-widget-container .new-chat-bottom-container .action-label {
209210
height: 16px;
210211
padding: var(--vscode-spacing-size40) var(--vscode-spacing-size80);
211212
font-size: var(--vscode-fontSize-label2, 11px);
212213
color: var(--vscode-icon-foreground);
214+
background-color: var(--session-view-background);
215+
}
216+
217+
/* Over a chat background these chips sit straight on the wallpaper with nothing
218+
* to separate their opaque surface from it, so give them a frame — the same move
219+
* the in-session secondary toolbar makes in chatView.css. */
220+
.monaco-workbench.agent-sessions-workbench .part.sessionspart.has-chat-background .new-chat-widget-container .new-chat-bottom-container .action-label {
221+
border: var(--vscode-strokeThickness) solid var(--vscode-commandCenter-inactiveBorder, var(--vscode-widget-border));
222+
border-radius: var(--vscode-cornerRadius-small);
223+
}
224+
225+
/* Outranks the shared action-bar hover in workbench style.css, which would
226+
* otherwise swap the opaque surface for a translucent one. The tint goes on the
227+
* background-image layer so it composites over that surface. */
228+
.monaco-workbench.agent-sessions-workbench .new-chat-widget-container .new-chat-bottom-container .action-label:not(.disabled):hover {
229+
background-color: var(--session-view-background);
230+
background-image: linear-gradient(var(--vscode-toolbar-hoverBackground), var(--vscode-toolbar-hoverBackground));
213231
}
214232

215233
.new-chat-widget-container .new-chat-bottom-container .action-label > .codicon {
@@ -279,6 +297,7 @@
279297
gap: var(--vscode-spacing-size80);
280298
min-height: var(--vscode-spacing-size240);
281299
padding: var(--vscode-spacing-size20) var(--vscode-spacing-size120);
300+
background-color: var(--session-view-background);
282301
border: var(--vscode-strokeThickness) solid var(--vscode-commandCenter-inactiveBorder, var(--vscode-widget-border));
283302
border-radius: var(--vscode-cornerRadius-circle);
284303
box-sizing: border-box;
@@ -287,9 +306,13 @@
287306
line-height: var(--vscode-spacing-size160);
288307
}
289308

309+
/* Re-assert the opaque surface, which the generic picker-slot hover below would
310+
* otherwise replace, and keep the tint on the background-image layer so it
311+
* composites over that surface. */
290312
.sessions-chat-picker-slot.sessions-workspace-category-picker-slot .action-label:hover,
291313
.sessions-chat-picker-slot.sessions-workspace-category-picker-slot .action-label[aria-expanded='true'] {
292-
background-color: var(--vscode-toolbar-hoverBackground);
314+
background-color: var(--session-view-background);
315+
background-image: linear-gradient(var(--vscode-toolbar-hoverBackground), var(--vscode-toolbar-hoverBackground));
293316
border-color: var(--vscode-commandCenter-activeBorder, var(--vscode-focusBorder));
294317
color: var(--vscode-foreground);
295318
}

src/vs/sessions/contrib/chat/test/browser/chatView.test.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -605,70 +605,82 @@ suite('Sessions - Chat View', () => {
605605
workbench.style.setProperty('--vscode-button-secondaryBackground', 'rgba(0, 0, 0, 0.08)');
606606
workbench.style.setProperty('--vscode-button-secondaryBorder', '#808080');
607607
workbench.style.setProperty('--vscode-button-secondaryForeground', '#202020');
608-
workbench.style.setProperty('--vscode-cornerRadius-large', '8px');
608+
workbench.style.setProperty('--vscode-commandCenter-inactiveBorder', '#606060');
609609
workbench.style.setProperty('--vscode-cornerRadius-small', '4px');
610-
workbench.style.setProperty('--vscode-spacing-size120', '12px');
611610
workbench.style.setProperty('--vscode-strokeThickness', '1px');
612611
const part = dom.append(workbench, dom.$('.part.sessionspart.has-chat-background'));
613612
const chatView = dom.append(part, dom.$('.chat-view'));
614613
const newChatWidget = dom.append(chatView, dom.$('.sessions-chat-widget'));
615614
const newChatContent = dom.append(newChatWidget, dom.$('.new-chat-widget-content'));
616-
const inSessionWidget = dom.append(chatView, dom.$('.sessions-chat-widget.new-chat-in-session'));
617-
const inSessionContent = dom.append(inSessionWidget, dom.$('.new-chat-widget-content'));
615+
const newChatContainer = dom.append(newChatWidget, dom.$('.new-chat-widget-container'));
616+
const bottomContainer = dom.append(newChatContainer, dom.$('.new-chat-bottom-container'));
617+
const bottomAction = dom.append(bottomContainer, dom.$('.action-label'));
618+
const workspacePickerSlot = dom.append(newChatContainer, dom.$('.sessions-chat-picker-slot.sessions-workspace-category-picker-slot'));
619+
const workspacePill = dom.append(workspacePickerSlot, dom.$('.action-label'));
618620
const session = dom.append(chatView, dom.$('.interactive-session'));
619621
const secondaryToolbar = dom.append(session, dom.$('.chat-secondary-toolbar'));
620622
const secondaryAction = dom.append(secondaryToolbar, dom.$('.action-label'));
621623
const contextUsage = dom.append(secondaryToolbar, dom.$('.chat-context-usage-widget'));
622624
const plainPart = dom.append(workbench, dom.$('.part.sessionspart'));
623625
const plainChatView = dom.append(plainPart, dom.$('.chat-view'));
624-
const plainNewChatWidget = dom.append(plainChatView, dom.$('.sessions-chat-widget'));
625-
const plainNewChatContent = dom.append(plainNewChatWidget, dom.$('.new-chat-widget-content'));
626626
const plainSession = dom.append(plainChatView, dom.$('.interactive-session'));
627627
const plainSecondaryToolbar = dom.append(plainSession, dom.$('.chat-secondary-toolbar'));
628628
const plainSecondaryAction = dom.append(plainSecondaryToolbar, dom.$('.action-label'));
629629
const plainContextUsage = dom.append(plainSecondaryToolbar, dom.$('.chat-context-usage-widget'));
630+
const plainNewChatWidget = dom.append(plainChatView, dom.$('.sessions-chat-widget'));
631+
const plainNewChatContainer = dom.append(plainNewChatWidget, dom.$('.new-chat-widget-container'));
632+
const plainBottomContainer = dom.append(plainNewChatContainer, dom.$('.new-chat-bottom-container'));
633+
const plainBottomAction = dom.append(plainBottomContainer, dom.$('.action-label'));
630634
dom.getWindow(workbench).document.body.appendChild(workbench);
631635
disposables.add(toDisposable(() => workbench.remove()));
632636

633637
const newChatStyle = dom.getWindow(newChatContent).getComputedStyle(newChatContent);
638+
const bottomActionStyle = dom.getWindow(bottomAction).getComputedStyle(bottomAction);
639+
const workspacePillStyle = dom.getWindow(workspacePill).getComputedStyle(workspacePill);
634640
const secondaryActionStyle = dom.getWindow(secondaryAction).getComputedStyle(secondaryAction);
635641
const contextUsageStyle = dom.getWindow(contextUsage).getComputedStyle(contextUsage);
636642
assert.deepStrictEqual({
637643
newChatBackgroundColor: newChatStyle.backgroundColor,
638-
newChatBorderRadius: newChatStyle.borderRadius,
639644
newChatPadding: newChatStyle.padding,
640-
inSessionBackgroundColor: dom.getWindow(inSessionContent).getComputedStyle(inSessionContent).backgroundColor,
645+
bottomActionBackgroundColor: bottomActionStyle.backgroundColor,
646+
bottomActionBorderColor: bottomActionStyle.borderColor,
647+
bottomActionBorderStyle: bottomActionStyle.borderStyle,
648+
bottomActionBorderRadius: bottomActionStyle.borderRadius,
649+
workspacePillBackgroundColor: workspacePillStyle.backgroundColor,
641650
secondaryActionBackgroundColor: secondaryActionStyle.backgroundColor,
642651
secondaryActionBackgroundImage: secondaryActionStyle.backgroundImage,
643652
secondaryActionBorderColor: secondaryActionStyle.borderColor,
644653
secondaryActionBorderStyle: secondaryActionStyle.borderStyle,
645654
contextUsageBackgroundColor: contextUsageStyle.backgroundColor,
646655
contextUsageBackgroundImage: contextUsageStyle.backgroundImage,
647656
contextUsageBorderRadius: contextUsageStyle.borderRadius,
648-
plainNewChatBackgroundColor: dom.getWindow(plainNewChatContent).getComputedStyle(plainNewChatContent).backgroundColor,
649-
plainNewChatPadding: dom.getWindow(plainNewChatContent).getComputedStyle(plainNewChatContent).padding,
650657
plainSecondaryActionBackgroundColor: dom.getWindow(plainSecondaryAction).getComputedStyle(plainSecondaryAction).backgroundColor,
651658
plainSecondaryActionBorderStyle: dom.getWindow(plainSecondaryAction).getComputedStyle(plainSecondaryAction).borderStyle,
652659
plainContextUsageBackgroundColor: dom.getWindow(plainContextUsage).getComputedStyle(plainContextUsage).backgroundColor,
653660
plainContextUsageBorderStyle: dom.getWindow(plainContextUsage).getComputedStyle(plainContextUsage).borderStyle,
661+
plainBottomActionBackgroundColor: dom.getWindow(plainBottomAction).getComputedStyle(plainBottomAction).backgroundColor,
662+
plainBottomActionBorderStyle: dom.getWindow(plainBottomAction).getComputedStyle(plainBottomAction).borderStyle,
654663
}, {
655-
newChatBackgroundColor: 'color(srgb 1 1 1 / 0.86)',
656-
newChatBorderRadius: '8px',
657-
newChatPadding: '12px',
658-
inSessionBackgroundColor: 'rgba(0, 0, 0, 0)',
664+
newChatBackgroundColor: 'rgba(0, 0, 0, 0)',
665+
newChatPadding: '0px',
666+
bottomActionBackgroundColor: 'rgb(255, 255, 255)',
667+
bottomActionBorderColor: 'rgb(96, 96, 96)',
668+
bottomActionBorderStyle: 'solid',
669+
bottomActionBorderRadius: '4px',
670+
workspacePillBackgroundColor: 'rgb(255, 255, 255)',
659671
secondaryActionBackgroundColor: 'rgb(255, 255, 255)',
660672
secondaryActionBackgroundImage: 'linear-gradient(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08))',
661673
secondaryActionBorderColor: 'rgb(128, 128, 128)',
662674
secondaryActionBorderStyle: 'solid',
663675
contextUsageBackgroundColor: 'rgb(255, 255, 255)',
664676
contextUsageBackgroundImage: 'linear-gradient(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08))',
665677
contextUsageBorderRadius: '4px',
666-
plainNewChatBackgroundColor: 'rgba(0, 0, 0, 0)',
667-
plainNewChatPadding: '0px',
668678
plainSecondaryActionBackgroundColor: 'rgba(0, 0, 0, 0)',
669679
plainSecondaryActionBorderStyle: 'none',
670680
plainContextUsageBackgroundColor: 'rgba(0, 0, 0, 0)',
671681
plainContextUsageBorderStyle: 'none',
682+
plainBottomActionBackgroundColor: 'rgb(255, 255, 255)',
683+
plainBottomActionBorderStyle: 'none',
672684
});
673685
});
674686

src/vs/sessions/contrib/chat/test/browser/newChatWidget.fixture.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { assert } from '../../../../../base/common/assert.js';
88
import { Codicon } from '../../../../../base/common/codicons.js';
99
import { Event } from '../../../../../base/common/event.js';
1010
import { MarkdownString } from '../../../../../base/common/htmlContent.js';
11+
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
1112
import { constObservable, observableValue } from '../../../../../base/common/observable.js';
1213
import { extUri } from '../../../../../base/common/resources.js';
1314
import { mock } from '../../../../../base/test/common/mock.js';
@@ -44,6 +45,7 @@ import { activeSessionViewBackground } from '../../../../common/theme.js';
4445
import { Menus } from '../../../../browser/menus.js';
4546
import { AgentHostFilterConnectionStatus, IAgentHostFilterService } from '../../../../services/agentHostFilter/common/agentHostFilter.js';
4647
import { ISessionsChatBackgroundService } from '../../../../services/chatBackground/browser/chatBackgroundService.js';
48+
import { SessionsChatBackgroundRenderer } from '../../../../services/chatBackground/browser/chatBackgroundRenderer.js';
4749
import { ISessionsProvidersService } from '../../../../services/sessions/browser/sessionsProvidersService.js';
4850
import { ISessionsRecentWorkspacesService } from '../../../../services/sessions/browser/sessionsRecentWorkspacesService.js';
4951
import { ISessionsService } from '../../../../services/sessions/browser/sessionsService.js';
@@ -85,6 +87,7 @@ interface INewChatWidgetFixtureOptions {
8587
readonly primaryToolbarWidth?: number;
8688
readonly phoneLayout?: boolean;
8789
readonly migrationCount?: number;
90+
readonly withChatBackground?: boolean;
8891
}
8992

9093
class AutoModelFixtureMenuService extends FixtureMenuService {
@@ -121,6 +124,21 @@ class AutoModelFixtureMenuService extends FixtureMenuService {
121124
}
122125
}
123126

127+
/**
128+
* Wraps the composer in the `.part.sessionspart` host the Agents window uses and
129+
* paints the real codicon wallpaper into it, so the fixture shows the composer
130+
* the way it reads once a chat background is set.
131+
*/
132+
function createChatBackgroundPart(container: HTMLElement, disposableStore: DisposableStore): HTMLElement {
133+
const part = dom.append(container, dom.$('.part.sessionspart'));
134+
part.style.position = 'relative';
135+
part.style.width = '100%';
136+
part.style.height = '100%';
137+
const renderer = disposableStore.add(new SessionsChatBackgroundRenderer(part));
138+
renderer.setBackground({ kind: 'codicons' });
139+
return part;
140+
}
141+
124142
/**
125143
* Renders the whole new-session composer (`NewChatView` → `NewChatWidget`) inside
126144
* a `.session-view` so the draft-comments banner sits above the input the way it
@@ -151,6 +169,7 @@ async function renderNewChatWidget(context: ComponentFixtureContext, options: IN
151169
primaryToolbarWidth,
152170
phoneLayout = false,
153171
migrationCount = 0,
172+
withChatBackground = false,
154173
} = options;
155174
const feedbackItems: readonly IAgentFeedback[] = Array.from({ length: commentCount }, (_, index) => ({
156175
id: `feedback-${index}`,
@@ -330,7 +349,7 @@ async function renderNewChatWidget(context: ComponentFixtureContext, options: IN
330349
container.classList.add('monaco-workbench', 'agent-sessions-workbench');
331350
container.classList.toggle('phone-layout', phoneLayout);
332351

333-
const sessionView = dom.append(container, dom.$('.session-view.is-active'));
352+
const sessionView = dom.append(withChatBackground ? createChatBackgroundPart(container, disposableStore) : container, dom.$('.session-view.is-active'));
334353
sessionView.style.width = '100%';
335354
sessionView.style.height = '100%';
336355
sessionView.style.backgroundColor = asCssVariable(activeSessionViewBackground);
@@ -411,6 +430,11 @@ export default defineThemedFixtureGroup({ path: 'sessions/chat/newWidget/' }, {
411430
expectedVisualDescriptions: ['The new-session composer shows Copilot, microsoft/vscode, and Issue/PR pills aligned to the left above the chat input. Customize is aligned separately to the right edge of the input, with a yellow migration indicator and no chevron.'],
412431
render: context => renderNewChatWidget(context, { withWorkspace: true, migrationCount: 3 }),
413432
}),
433+
NewSessionChatBackground: defineComponentFixture({
434+
labels: { kind: 'screenshot', blocksCi: true },
435+
expectedVisualDescriptions: ['The new-session composer sits directly on the codicon wallpaper with no card behind it. The workspace pills, the input area and the bottom-row controls each carry their own opaque surface and a thin border, and the wallpaper shows through the gaps between them.'],
436+
render: context => renderNewChatWidget(context, { withWorkspace: true, withAutoModel: true, withChatBackground: true }),
437+
}),
414438
NewSessionAutoModel: defineComponentFixture({
415439
labels: { kind: 'screenshot', blocksCi: true },
416440
expectedVisualDescriptions: ['The new-session input toolbar shows an Auto model picker whose background fits closely around the Copilot icon and Auto label without excessive empty horizontal space. The bottom row shows optically tuned compact rocket, warning, and connection status icons centered in matching controls, followed by the full Status text action without clipping.'],

src/vs/sessions/contrib/providers/agentHost/browser/media/agentHostSessionConfigPicker.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
.sessions-chat-agent-host-config {
77
display: flex;
88
align-items: center;
9+
/* Each config control carries its own surface, so they need a gap. */
10+
gap: var(--vscode-spacing-size20);
911
min-width: 0;
1012
}
1113

0 commit comments

Comments
 (0)