From 97048f6bd990616785aba67360a1d3148c9ad7cf Mon Sep 17 00:00:00 2001 From: David Dossett <25163139+daviddossett@users.noreply.github.com> Date: Sat, 28 Feb 2026 09:45:48 -0800 Subject: [PATCH 1/4] Reduce chat input label hide threshold from 650 to 400 --- .../contrib/chat/browser/widget/input/chatInputPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts index 44ad592b872069..078e94b8be4dc4 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts @@ -2150,7 +2150,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge actionContext: { widget }, onlyShowIconsForDefaultActions: observableFromEvent( this._inputEditor.onDidLayoutChange, - (l?: EditorLayoutInfo) => (l?.width ?? this._inputEditor.getLayoutInfo().width) < 650 /* This is a magical number based on testing*/ + (l?: EditorLayoutInfo) => (l?.width ?? this._inputEditor.getLayoutInfo().width) < 400 /* This is a magical number based on testing*/ ).recomputeInitiallyAndOnChange(this._store), hoverPosition: { forcePosition: true, From ca7c44b705c5d3f8e8f45a160d616b9fe5f54517 Mon Sep 17 00:00:00 2001 From: David Dossett <25163139+daviddossett@users.noreply.github.com> Date: Sat, 28 Feb 2026 13:53:21 -0800 Subject: [PATCH 2/4] Collapse chat input picker buttons to 22x22 icons at narrow widths When the chat input is narrow (<250px), hide chevrons on mode, session target, model, and workspace pickers. Mode and session target pickers collapse to centered 22x22 icon-only buttons matching the add-context button size. Update actionMinWidth to 22 and toolbar gap to 4px. --- .../contrib/chat/browser/newChatViewPane.ts | 1 + .../browser/widget/input/chatInputPart.ts | 45 ++++++++++++------- .../widget/input/chatInputPickerActionItem.ts | 4 ++ .../browser/widget/input/chatModelPicker.ts | 17 ++++++- .../widget/input/modePickerActionItem.ts | 9 +++- .../widget/input/modelPickerActionItem.ts | 4 +- .../widget/input/modelPickerActionItem2.ts | 1 + .../input/sessionTargetPickerActionItem.ts | 9 +++- .../widget/input/workspacePickerActionItem.ts | 4 +- .../chat/browser/widget/media/chat.css | 34 +++++++++++++- 10 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts index 9928b50f616112..8694310559afb7 100644 --- a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts +++ b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts @@ -620,6 +620,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { const pickerOptions: IChatInputPickerOptions = { onlyShowIconsForDefaultActions: observableValue('onlyShowIcons', false), + hideChevrons: observableValue('hideChevrons', false), hoverPosition: { hoverPosition: HoverPosition.ABOVE }, }; diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts index 078e94b8be4dc4..22a57a273f4858 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts @@ -35,7 +35,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { IEditorConstructionOptions } from '../../../../../../editor/browser/config/editorConfiguration.js'; import { EditorExtensionsRegistry } from '../../../../../../editor/browser/editorExtensions.js'; import { CodeEditorWidget } from '../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { EditorLayoutInfo, EditorOptions, IEditorOptions } from '../../../../../../editor/common/config/editorOptions.js'; +import { EditorOptions, IEditorOptions } from '../../../../../../editor/common/config/editorOptions.js'; import { IDimension } from '../../../../../../editor/common/core/2d/dimension.js'; import { IPosition } from '../../../../../../editor/common/core/position.js'; import { IRange, Range } from '../../../../../../editor/common/core/range.js'; @@ -207,6 +207,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private static _counter = 0; private _workingSetCollapsed = observableValue('chatInputPart.workingSetCollapsed', true); + private _stableInputPartWidth = observableValue('chatInputPart.stableInputPartWidth', 0); private readonly _chatInputTodoListWidget = this._register(new MutableDisposable()); private readonly _chatQuestionCarouselWidget = this._register(new MutableDisposable()); private readonly _chatQuestionCarouselDisposables = this._register(new DisposableStore()); @@ -218,6 +219,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private _onDidLoadInputState: Emitter = this._register(new Emitter()); readonly onDidLoadInputState: Event = this._onDidLoadInputState.event; + private readonly _toolbarRelayoutScheduler = this._register(new RunOnceScheduler(() => { + if (typeof this.cachedWidth === 'number') { + this.layout(this.cachedWidth); + } + }, 0)); private _onDidFocus = this._register(new Emitter()); readonly onDidFocus: Event = this._onDidFocus.event; @@ -2148,10 +2154,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const pickerOptions: IChatInputPickerOptions = { getOverflowAnchor: () => this.inputActionsToolbar.getElement(), actionContext: { widget }, - onlyShowIconsForDefaultActions: observableFromEvent( - this._inputEditor.onDidLayoutChange, - (l?: EditorLayoutInfo) => (l?.width ?? this._inputEditor.getLayoutInfo().width) < 400 /* This is a magical number based on testing*/ - ).recomputeInitiallyAndOnChange(this._store), + onlyShowIconsForDefaultActions: derived(reader => this._stableInputPartWidth.read(reader) < 400), + hideChevrons: derived(reader => this._stableInputPartWidth.read(reader) < 250), hoverPosition: { forcePosition: true, hoverPosition: location === ChatWidgetLocation.SidebarRight && !isMaximized ? HoverPosition.LEFT : HoverPosition.RIGHT @@ -2169,7 +2173,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge enabled: true, kind: 'last', minItems: 1, - actionMinWidth: 40 + actionMinWidth: 22 }, actionViewItemProvider: (action, options) => { if (action.id === OpenModelPickerAction.ID && action instanceof MenuItemAction) { @@ -2230,17 +2234,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY && this.options.workspacePickerDelegate) { return this.instantiationService.createInstance(WorkspacePickerActionItem, action, this.options.workspacePickerDelegate, pickerOptions); } else { - const empty = new BaseActionViewItem(undefined, action); - if (empty.element) { - empty.element.style.display = 'none'; - } - return empty; + return new HiddenActionViewItem(action); } } else if (action.id === ChatSessionPrimaryPickerAction.ID && action instanceof MenuItemAction) { // Create all pickers and return a container action view item const widgets = this.createChatSessionPickerWidgets(action); if (widgets.length === 0) { - return undefined; + return new HiddenActionViewItem(action); } // Create a container to hold all picker widgets return this.instantiationService.createInstance(ChatSessionPickersContainerActionItem, action, widgets); @@ -2258,7 +2258,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.chatSessionPickerContainer = container as HTMLElement | undefined; if (this.cachedWidth && typeof this.cachedInputToolbarWidth === 'number' && this.cachedInputToolbarWidth !== this.inputActionsToolbar.getItemsWidth()) { - this.layout(this.cachedWidth); + this._toolbarRelayoutScheduler.schedule(); } })); this.executeToolbar = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, toolbarsContainer, this.options.menus.executeToolbar, { @@ -2273,7 +2273,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.executeToolbar.context = { widget } satisfies IChatExecuteActionContext; this._register(this.executeToolbar.onDidChangeMenuItems(() => { if (this.cachedWidth && typeof this.cachedExecuteToolbarWidth === 'number' && this.cachedExecuteToolbarWidth !== this.executeToolbar.getItemsWidth()) { - this.layout(this.cachedWidth); + this._toolbarRelayoutScheduler.schedule(); } })); if (this.options.menus.inputSideToolbar) { @@ -2994,6 +2994,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge */ layout(width: number) { this.cachedWidth = width; + this._stableInputPartWidth.set(width, undefined); return this._layout(width); } @@ -3037,10 +3038,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const inputSideToolbarWidth = this.inputSideToolbarContainer ? dom.getTotalWidth(this.inputSideToolbarContainer) : 0; const getToolbarsWidthCompact = () => { + const toolbarItemGap = 4; const executeToolbarWidth = this.cachedExecuteToolbarWidth = this.executeToolbar.getItemsWidth(); const inputToolbarWidth = this.cachedInputToolbarWidth = this.inputActionsToolbar.getItemsWidth(); - const executeToolbarPadding = (this.executeToolbar.getItemsLength() - 1) * 4; - const inputToolbarPadding = this.inputActionsToolbar.getItemsLength() ? (this.inputActionsToolbar.getItemsLength() - 1) * 4 : 0; + const executeToolbarPadding = (this.executeToolbar.getItemsLength() - 1) * toolbarItemGap; + const inputToolbarPadding = this.inputActionsToolbar.getItemsLength() ? (this.inputActionsToolbar.getItemsLength() - 1) * toolbarItemGap : 0; const contextUsageWidth = dom.getTotalWidth(this.contextUsageWidgetContainer); const inputToolbarsPadding = 12; // pdading between input toolbar/execute toolbar/contextUsage. return executeToolbarWidth + executeToolbarPadding + contextUsageWidth + (this.options.renderInputToolbarBelowInput ? 0 : inputToolbarWidth + inputToolbarPadding + inputToolbarsPadding); @@ -3132,3 +3134,14 @@ class ChatSessionPickersContainerActionItem extends ActionViewItem { super.dispose(); } } + +class HiddenActionViewItem extends BaseActionViewItem { + constructor(action: IAction) { + super(undefined, action); + } + + override render(container: HTMLElement): void { + super.render(container); + container.style.display = 'none'; + } +} diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts index 1377aa5260793d..5bfdb3e16e5466 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts @@ -26,6 +26,8 @@ export interface IChatInputPickerOptions { readonly onlyShowIconsForDefaultActions: IObservable; + readonly hideChevrons: IObservable; + readonly hoverPosition?: IHoverPositionOptions; } @@ -54,8 +56,10 @@ export abstract class ChatInputPickerActionViewItem extends ActionWidgetDropdown this._register(autorun(reader => { this.pickerOptions.onlyShowIconsForDefaultActions.read(reader); + const hideChevrons = this.pickerOptions.hideChevrons.read(reader); if (this.element) { this.renderLabel(this.element); + this.element.classList.toggle('hide-chevrons', hideChevrons); } })); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts index e5c907aed38d14..891d4df72a42c5 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts @@ -12,6 +12,7 @@ import { Emitter, Event } from '../../../../../../base/common/event.js'; import { MarkdownString } from '../../../../../../base/common/htmlContent.js'; import { KeyCode } from '../../../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { autorun, IObservable } from '../../../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { localize } from '../../../../../../nls.js'; import { ActionListItemKind, IActionListItem } from '../../../../../../platform/actionWidget/browser/actionList.js'; @@ -459,6 +460,7 @@ export class ModelPickerWidget extends Disposable { private _selectedModel: ILanguageModelChatMetadataAndIdentifier | undefined; private _badge: ModelPickerBadge | undefined; + private _hideChevrons: IObservable | undefined; private _domNode: HTMLElement | undefined; private _badgeIcon: HTMLElement | undefined; @@ -484,6 +486,17 @@ export class ModelPickerWidget extends Disposable { super(); } + setHideChevrons(hideChevrons: IObservable): void { + this._hideChevrons = hideChevrons; + this._register(autorun(reader => { + const hide = hideChevrons.read(reader); + this._renderLabel(); + if (this._domNode) { + this._domNode.classList.toggle('hide-chevrons', hide); + } + })); + } + setSelectedModel(model: ILanguageModelChatMetadataAndIdentifier | undefined): void { this._selectedModel = model; this._renderLabel(); @@ -637,7 +650,9 @@ export class ModelPickerWidget extends Disposable { domChildren.push(this._badgeIcon); } - domChildren.push(...renderLabelWithIcons(`$(chevron-down)`)); + if (!this._hideChevrons?.get()) { + domChildren.push(...renderLabelWithIcons(`$(chevron-down)`)); + } dom.reset(this._domNode, ...domChildren); diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts index 3b552379ad1e86..1d06d8a8185a3b 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts @@ -260,6 +260,11 @@ export class ModePickerActionItem extends ChatInputPickerActionViewItem { return menuContributions; } + override render(container: HTMLElement): void { + super.render(container); + container.classList.add('chat-mode-picker-item'); + } + protected override renderLabel(element: HTMLElement): IDisposable | null { this.setAriaLabelAttributes(element); @@ -280,7 +285,9 @@ export class ModePickerActionItem extends ChatInputPickerActionViewItem { if (!isDefault || !icon || !this.pickerOptions.onlyShowIconsForDefaultActions.get()) { labelElements.push(dom.$('span.chat-input-picker-label', undefined, state)); } - labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); + if (!this.pickerOptions.hideChevrons.get()) { + labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); + } dom.reset(element, ...labelElements); return null; diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts index af61812b3a91ff..04bfc652890fc1 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts @@ -209,7 +209,9 @@ export class ModelPickerActionItem extends ChatInputPickerActionViewItem { } domChildren.push(dom.$('span.chat-input-picker-label', undefined, name ?? localize('chat.modelPicker.auto', "Auto"))); - domChildren.push(...renderLabelWithIcons(`$(chevron-down)`)); + if (!this.pickerOptions.hideChevrons.get()) { + domChildren.push(...renderLabelWithIcons(`$(chevron-down)`)); + } dom.reset(element, ...domChildren); this.setAriaLabelAttributes(element); diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem2.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem2.ts index 6d302d2e00366f..5a4d402e790742 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem2.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem2.ts @@ -41,6 +41,7 @@ export class EnhancedModelPickerActionItem extends BaseActionViewItem { this._pickerWidget = this._register(instantiationService.createInstance(ModelPickerWidget, delegate)); this._pickerWidget.setSelectedModel(delegate.currentModel.get()); + this._pickerWidget.setHideChevrons(pickerOptions.hideChevrons); // Sync delegate → widget when model list or selection changes externally this._register(autorun(t => { diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts index 5a31e7f187cb8c..1ea9a684817b03 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts @@ -200,6 +200,11 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem { return undefined; } + override render(container: HTMLElement): void { + super.render(container); + container.classList.add('chat-session-target-picker-item'); + } + protected override renderLabel(element: HTMLElement): IDisposable | null { this.setAriaLabelAttributes(element); const currentType = this._getSelectedSessionType(); @@ -212,7 +217,9 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem { if (!this.pickerOptions.onlyShowIconsForDefaultActions.get()) { labelElements.push(dom.$('span.chat-input-picker-label', undefined, label)); } - labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); + if (!this.pickerOptions.hideChevrons.get()) { + labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); + } dom.reset(element, ...labelElements); diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/workspacePickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/workspacePickerActionItem.ts index 989c3bc64e8edf..828664f9a8853a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/workspacePickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/workspacePickerActionItem.ts @@ -118,7 +118,9 @@ export class WorkspacePickerActionItem extends ChatInputPickerActionViewItem { labelElements.push(dom.$('span.chat-input-picker-label', undefined, localize('selectWorkspace', "Workspace"))); } - labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); + if (!this.pickerOptions.hideChevrons.get()) { + labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); + } dom.reset(element, ...labelElements); diff --git a/src/vs/workbench/contrib/chat/browser/widget/media/chat.css b/src/vs/workbench/contrib/chat/browser/widget/media/chat.css index c3ec962151a34f..b9e1003c81c190 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/widget/media/chat.css @@ -1317,6 +1317,10 @@ have to be updated for changes to the rules above, or to support more deeply nes margin-right: auto; } +.interactive-session .chat-input-toolbars > .chat-input-toolbar .actions-container:first-child { + margin-right: 0; +} + .interactive-session .chat-input-toolbars .tool-warning-indicator { position: absolute; bottom: 0; @@ -1394,12 +1398,40 @@ have to be updated for changes to the rules above, or to support more deeply nes .interactive-session .chat-input-toolbar .chat-input-picker-item .action-label, .interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label { height: 16px; - padding: 3px 0px 3px 6px; + padding: 3px 1px 3px 7px; display: flex; align-items: center; color: var(--vscode-icon-foreground); } +.interactive-session .chat-input-toolbar .chat-mode-picker-item.hide-chevrons .action-label, +.interactive-session .chat-input-toolbar .chat-session-target-picker-item.hide-chevrons .action-label, +.interactive-session .chat-input-toolbar .chat-mode-picker-item .action-label.hide-chevrons, +.interactive-session .chat-input-toolbar .chat-session-target-picker-item .action-label.hide-chevrons { + width: 22px; + min-width: 22px; + height: 22px; + padding: 3px; + box-sizing: border-box; + margin: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.interactive-session .chat-input-toolbar .chat-mode-picker-item .action-label.hide-chevrons .chat-input-picker-label, +.interactive-session .chat-input-toolbar .chat-session-target-picker-item .action-label.hide-chevrons .chat-input-picker-label { + display: none; +} + +.interactive-session .chat-input-toolbar .chat-mode-picker-item .action-label.hide-chevrons .codicon, +.interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label.hide-chevrons .codicon, +.interactive-session .chat-input-toolbar .chat-session-target-picker-item .action-label.hide-chevrons .codicon { + display: flex; + align-items: center; + justify-content: center; +} + .monaco-workbench .interactive-session .chat-input-toolbar .chat-input-picker-item .action-label .codicon-chevron-down, .monaco-workbench .interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label .codicon-chevron-down { font-size: 12px; From 0a7029a89cf7c193fe6392c785b9eafe368d39d4 Mon Sep 17 00:00:00 2001 From: David Dossett <25163139+daviddossett@users.noreply.github.com> Date: Sat, 28 Feb 2026 14:38:09 -0800 Subject: [PATCH 3/4] Simplify chat input toolbar responsive behavior --- .../contrib/chat/browser/newChatViewPane.ts | 1 - .../browser/widget/input/chatInputPart.ts | 3 +- .../widget/input/chatInputPickerActionItem.ts | 5 +-- .../browser/widget/input/chatModelPicker.ts | 2 +- .../widget/input/modePickerActionItem.ts | 6 ++-- .../input/sessionTargetPickerActionItem.ts | 5 ++- .../chat/browser/widget/media/chat.css | 32 ++++++++++--------- 7 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts index 8694310559afb7..247501dfc64e9c 100644 --- a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts +++ b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts @@ -619,7 +619,6 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { }; const pickerOptions: IChatInputPickerOptions = { - onlyShowIconsForDefaultActions: observableValue('onlyShowIcons', false), hideChevrons: observableValue('hideChevrons', false), hoverPosition: { hoverPosition: HoverPosition.ABOVE }, }; diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts index 22a57a273f4858..e852c54ce8d138 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts @@ -2154,8 +2154,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const pickerOptions: IChatInputPickerOptions = { getOverflowAnchor: () => this.inputActionsToolbar.getElement(), actionContext: { widget }, - onlyShowIconsForDefaultActions: derived(reader => this._stableInputPartWidth.read(reader) < 400), - hideChevrons: derived(reader => this._stableInputPartWidth.read(reader) < 250), + hideChevrons: derived(reader => this._stableInputPartWidth.read(reader) < 400), hoverPosition: { forcePosition: true, hoverPosition: location === ChatWidgetLocation.SidebarRight && !isMaximized ? HoverPosition.LEFT : HoverPosition.RIGHT diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts index 5bfdb3e16e5466..6c26933d03bdb9 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts @@ -24,8 +24,6 @@ export interface IChatInputPickerOptions { readonly actionContext?: IChatExecuteActionContext; - readonly onlyShowIconsForDefaultActions: IObservable; - readonly hideChevrons: IObservable; readonly hoverPosition?: IHoverPositionOptions; @@ -55,11 +53,10 @@ export abstract class ChatInputPickerActionViewItem extends ActionWidgetDropdown super(action, optionsWithAnchor, actionWidgetService, keybindingService, contextKeyService, telemetryService); this._register(autorun(reader => { - this.pickerOptions.onlyShowIconsForDefaultActions.read(reader); const hideChevrons = this.pickerOptions.hideChevrons.read(reader); if (this.element) { - this.renderLabel(this.element); this.element.classList.toggle('hide-chevrons', hideChevrons); + this.renderLabel(this.element); } })); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts index 891d4df72a42c5..54b8efd5c4a3fe 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts @@ -490,10 +490,10 @@ export class ModelPickerWidget extends Disposable { this._hideChevrons = hideChevrons; this._register(autorun(reader => { const hide = hideChevrons.read(reader); - this._renderLabel(); if (this._domNode) { this._domNode.classList.toggle('hide-chevrons', hide); } + this._renderLabel(); })); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts index 1d06d8a8185a3b..773f896f6e846b 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts @@ -269,7 +269,6 @@ export class ModePickerActionItem extends ChatInputPickerActionViewItem { this.setAriaLabelAttributes(element); const currentMode = this.delegate.currentMode.get(); - const isDefault = currentMode.id === ChatMode.Agent.id; const state = currentMode.label.get(); let icon = currentMode.icon.get(); @@ -279,13 +278,14 @@ export class ModePickerActionItem extends ChatInputPickerActionViewItem { } const labelElements = []; + const collapsed = this.pickerOptions.hideChevrons.get(); if (icon) { labelElements.push(...renderLabelWithIcons(`$(${icon.id})`)); } - if (!isDefault || !icon || !this.pickerOptions.onlyShowIconsForDefaultActions.get()) { + if (!collapsed || !icon) { labelElements.push(dom.$('span.chat-input-picker-label', undefined, state)); } - if (!this.pickerOptions.hideChevrons.get()) { + if (!collapsed) { labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts index 1ea9a684817b03..897c927f25fd86 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts @@ -213,11 +213,10 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem { const icon = getAgentSessionProviderIcon(currentType ?? AgentSessionProviders.Local); const labelElements = []; + const collapsed = this.pickerOptions.hideChevrons.get(); labelElements.push(...renderLabelWithIcons(`$(${icon.id})`)); - if (!this.pickerOptions.onlyShowIconsForDefaultActions.get()) { + if (!collapsed) { labelElements.push(dom.$('span.chat-input-picker-label', undefined, label)); - } - if (!this.pickerOptions.hideChevrons.get()) { labelElements.push(...renderLabelWithIcons(`$(chevron-down)`)); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/media/chat.css b/src/vs/workbench/contrib/chat/browser/widget/media/chat.css index b9e1003c81c190..f1b00e2dfb71ea 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/widget/media/chat.css @@ -1404,32 +1404,34 @@ have to be updated for changes to the rules above, or to support more deeply nes color: var(--vscode-icon-foreground); } -.interactive-session .chat-input-toolbar .chat-mode-picker-item.hide-chevrons .action-label, -.interactive-session .chat-input-toolbar .chat-session-target-picker-item.hide-chevrons .action-label, -.interactive-session .chat-input-toolbar .chat-mode-picker-item .action-label.hide-chevrons, -.interactive-session .chat-input-toolbar .chat-session-target-picker-item .action-label.hide-chevrons { +/* When chevrons are hidden and only showing an icon (no label), size to 22x22 with centered icon */ +.interactive-session .chat-input-toolbar .chat-input-picker-item .action-label.hide-chevrons:not(:has(.chat-input-picker-label)), +.interactive-session .chat-input-toolbar .chat-input-picker-item.hide-chevrons .action-label:not(:has(.chat-input-picker-label)), +.interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label.hide-chevrons:not(:has(.chat-input-picker-label)) { width: 22px; min-width: 22px; height: 22px; - padding: 3px; + padding: 0; box-sizing: border-box; - margin: 0; display: flex; align-items: center; justify-content: center; + + .codicon { + justify-content: center; + } } -.interactive-session .chat-input-toolbar .chat-mode-picker-item .action-label.hide-chevrons .chat-input-picker-label, -.interactive-session .chat-input-toolbar .chat-session-target-picker-item .action-label.hide-chevrons .chat-input-picker-label { - display: none; +/* When chevrons are hidden but label is still shown (e.g. model picker), use equal padding */ +.interactive-session .chat-input-toolbar .chat-input-picker-item .action-label.hide-chevrons:has(.chat-input-picker-label), +.interactive-session .chat-input-toolbar .chat-input-picker-item.hide-chevrons .action-label:has(.chat-input-picker-label), +.interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label.hide-chevrons:has(.chat-input-picker-label) { + padding: 3px 7px; } -.interactive-session .chat-input-toolbar .chat-mode-picker-item .action-label.hide-chevrons .codicon, -.interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label.hide-chevrons .codicon, -.interactive-session .chat-input-toolbar .chat-session-target-picker-item .action-label.hide-chevrons .codicon { - display: flex; - align-items: center; - justify-content: center; +/* Hide the tools button when the toolbar is in collapsed state */ +.interactive-session .chat-input-toolbar:has(.hide-chevrons) .action-item:has(.codicon-tools) { + display: none; } .monaco-workbench .interactive-session .chat-input-toolbar .chat-input-picker-item .action-label .codicon-chevron-down, From a7abb3b93c0976d9e86169af1fcfd609725817a0 Mon Sep 17 00:00:00 2001 From: David Dossett <25163139+daviddossett@users.noreply.github.com> Date: Sat, 28 Feb 2026 15:01:58 -0800 Subject: [PATCH 4/4] Apply initial hideChevrons state in render() --- .../chat/browser/widget/input/chatInputPickerActionItem.ts | 7 +++++++ .../contrib/chat/browser/widget/input/chatModelPicker.ts | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts index 6c26933d03bdb9..13682b907d93c1 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.ts @@ -75,5 +75,12 @@ export abstract class ChatInputPickerActionViewItem extends ActionWidgetDropdown override render(container: HTMLElement): void { super.render(container); container.classList.add('chat-input-picker-item'); + + // Apply initial collapsed state now that this.element exists + const hideChevrons = this.pickerOptions.hideChevrons.get(); + if (this.element) { + this.element.classList.toggle('hide-chevrons', hideChevrons); + this.renderLabel(this.element); + } } } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts index 54b8efd5c4a3fe..0257f20252dd0d 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatModelPicker.ts @@ -514,6 +514,11 @@ export class ModelPickerWidget extends Disposable { this._domNode.setAttribute('aria-haspopup', 'true'); this._domNode.setAttribute('aria-expanded', 'false'); + // Apply initial collapsed state now that _domNode exists + if (this._hideChevrons?.get()) { + this._domNode.classList.toggle('hide-chevrons', true); + } + this._badgeIcon = dom.append(this._domNode, dom.$('span.model-picker-badge')); this._updateBadge();