Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/sessions/contrib/chat/browser/newChatViewPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
};

const pickerOptions: IChatInputPickerOptions = {
onlyShowIconsForDefaultActions: observableValue('onlyShowIcons', false),
hideChevrons: observableValue('hideChevrons', false),
hoverPosition: { hoverPosition: HoverPosition.ABOVE },
};

Expand Down
44 changes: 28 additions & 16 deletions src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -209,6 +209,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<ChatTodoListWidget>());
private readonly _chatQuestionCarouselWidget = this._register(new MutableDisposable<ChatQuestionCarouselPart>());
private readonly _chatQuestionCarouselDisposables = this._register(new DisposableStore());
Expand All @@ -220,6 +221,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge

private _onDidLoadInputState: Emitter<void> = this._register(new Emitter());
readonly onDidLoadInputState: Event<void> = 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<void>());
readonly onDidFocus: Event<void> = this._onDidFocus.event;
Expand Down Expand Up @@ -2152,10 +2158,7 @@ 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) < 650 /* This is a magical number based on testing*/
).recomputeInitiallyAndOnChange(this._store),
hideChevrons: derived(reader => this._stableInputPartWidth.read(reader) < 400),
hoverPosition: {
forcePosition: true,
hoverPosition: location === ChatWidgetLocation.SidebarRight && !isMaximized ? HoverPosition.LEFT : HoverPosition.RIGHT
Expand All @@ -2173,7 +2176,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) {
Expand Down Expand Up @@ -2234,17 +2237,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);
Expand All @@ -2262,7 +2261,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, {
Expand All @@ -2277,7 +2276,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) {
Expand Down Expand Up @@ -2998,6 +2997,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
*/
layout(width: number) {
this.cachedWidth = width;
this._stableInputPartWidth.set(width, undefined);

return this._layout(width);
}
Expand Down Expand Up @@ -3041,10 +3041,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);
Expand Down Expand Up @@ -3136,3 +3137,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';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface IChatInputPickerOptions {

readonly actionContext?: IChatExecuteActionContext;

readonly onlyShowIconsForDefaultActions: IObservable<boolean>;
readonly hideChevrons: IObservable<boolean>;

readonly hoverPosition?: IHoverPositionOptions;
}
Expand Down Expand Up @@ -53,8 +53,9 @@ 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.element.classList.toggle('hide-chevrons', hideChevrons);
this.renderLabel(this.element);
}
}));
Comment thread
daviddossett marked this conversation as resolved.
Expand All @@ -74,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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -459,6 +460,7 @@ export class ModelPickerWidget extends Disposable {

private _selectedModel: ILanguageModelChatMetadataAndIdentifier | undefined;
private _badge: ModelPickerBadge | undefined;
private _hideChevrons: IObservable<boolean> | undefined;

private _domNode: HTMLElement | undefined;
private _badgeIcon: HTMLElement | undefined;
Expand All @@ -484,6 +486,17 @@ export class ModelPickerWidget extends Disposable {
super();
}

setHideChevrons(hideChevrons: IObservable<boolean>): void {
this._hideChevrons = hideChevrons;
this._register(autorun(reader => {
const hide = hideChevrons.read(reader);
if (this._domNode) {
this._domNode.classList.toggle('hide-chevrons', hide);
}
this._renderLabel();
}));
}
Comment thread
daviddossett marked this conversation as resolved.

setSelectedModel(model: ILanguageModelChatMetadataAndIdentifier | undefined): void {
this._selectedModel = model;
this._renderLabel();
Expand All @@ -501,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();

Expand Down Expand Up @@ -637,7 +655,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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,15 @@ 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);

const currentMode = this.delegate.currentMode.get();
const isDefault = currentMode.id === ChatMode.Agent.id;
const state = currentMode.label.get();
let icon = currentMode.icon.get();

Expand All @@ -274,13 +278,16 @@ 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));
}
labelElements.push(...renderLabelWithIcons(`$(chevron-down)`));
if (!collapsed) {
labelElements.push(...renderLabelWithIcons(`$(chevron-down)`));
}

dom.reset(element, ...labelElements);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -208,11 +213,12 @@ 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));
labelElements.push(...renderLabelWithIcons(`$(chevron-down)`));
}
labelElements.push(...renderLabelWithIcons(`$(chevron-down)`));

dom.reset(element, ...labelElements);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
36 changes: 35 additions & 1 deletion src/vs/workbench/contrib/chat/browser/widget/media/chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1394,12 +1398,42 @@ 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);
}

/* 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: 0;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;

.codicon {
justify-content: center;
}
}

/* 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;
}

/* 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,
.monaco-workbench .interactive-session .chat-input-toolbar .chat-sessionPicker-item .action-label .codicon-chevron-down {
font-size: 12px;
Expand Down
Loading