From a0368214c858d53937b5dc17a647ea8f7ef8f9b1 Mon Sep 17 00:00:00 2001 From: mrleemurray Date: Thu, 20 Aug 2026 09:41:39 +0100 Subject: [PATCH 1/3] feat(chat): introduce compact codicons and update related components - Added `getCompactCodicon` function to retrieve compact variants of codicons. - Updated various components to use compact codicons, including: - Chat input notice widget - Chat input part - Chat completions - Mode picker action item - Model picker and its related styles - Permission and session target picker action items - Workspace picker action item - Various CSS updates to support compact icon sizes - Enhanced tests to verify the usage of compact icons in different scenarios. - Introduced new `chatIcons.ts` for managing chat-specific icon logic. --- src/vs/sessions/AI_CUSTOMIZATIONS.md | 2 + .../actions/chatSpeechToTextActions.ts | 2 +- .../agentHost/agentHostChatInputPicker.ts | 3 +- .../agentHostFolderPickerActionItem.ts | 2 +- .../media/agentHostChatInputPicker.css | 2 +- .../experiments/media/unifiedQuickAccess.css | 4 +- .../media/agentsessionsviewer.css | 2 +- .../aiCustomizationListWidget.ts | 2 +- .../aiCustomizationManagementEditor.ts | 2 +- .../customizationGroupHeaderRenderer.ts | 2 +- .../media/aiCustomizationManagement.css | 8 +-- .../aiCustomization/toolsListWidget.ts | 4 +- .../attachments/chatAttachmentWidgets.ts | 25 ++++--- .../chatCustomizationDiscoveryRenderer.ts | 9 +-- .../chatDebug/chatDebugCacheExplorerView.ts | 4 +- .../browser/chatDebug/media/chatDebug.css | 3 +- .../chatEditing/chatEditingEditorOverlay.ts | 2 +- .../chatEditingExplanationWidget.ts | 6 +- .../media/chatEditingEditorOverlay.css | 4 ++ .../media/chatEditingExplanationWidget.css | 7 +- .../contrib/chat/browser/chatIcons.ts | 48 +++++++++++++ .../chatManagement/media/chatModelsWidget.css | 4 +- .../chatSessionPickerActionItem.ts | 3 +- .../media/chatSessionPickerActionItem.css | 4 +- .../browser/chatStatus/chatStatusDashboard.ts | 4 +- .../browser/chatStatus/media/chatStatus.css | 4 +- .../chat/browser/media/chatTurnPills.css | 6 +- .../speechToText/dictationOnboarding.ts | 2 +- .../voiceInputModeActionViewItem.ts | 6 +- .../chatChangesSummaryPart.ts | 2 +- .../chatCollapsibleContentPart.ts | 5 +- .../chatContentParts/chatEditPillElement.ts | 3 +- .../chatExtensionsContentPart.ts | 2 +- .../chatMcpAuthenticationContentPart.ts | 3 +- .../chatProgressContentPart.ts | 3 +- .../chatQuestionCarouselPart.ts | 5 +- .../widget/chatContentParts/chatRichLink.ts | 18 ++--- .../chatSubagentContentPart.ts | 12 ++-- .../chatContentParts/chatSubagentOpenChat.ts | 3 +- .../chatContentParts/chatSuggestNextWidget.ts | 2 +- .../chatThinkingContentPart.ts | 15 ++-- .../chatToolInputOutputContentPart.ts | 8 +-- .../chatContentParts/chatTurnPillsPart.ts | 2 +- .../media/chatCodeBlockPill.css | 6 +- .../media/chatConfirmationWidget.css | 2 +- .../media/chatDisabledClaudeHooksContent.css | 4 ++ .../media/chatExtensionsContent.css | 4 ++ .../chatMcpServersInteractionContent.css | 4 ++ .../media/chatThinkingContent.css | 22 +++--- .../chat/browser/widget/chatDragAndDrop.ts | 2 +- .../browser/widget/chatForkActionViewItem.ts | 7 +- .../chat/browser/widget/chatListRenderer.ts | 3 +- .../browser/widget/input/chatFollowups.ts | 2 + .../widget/input/chatInputNoticeWidget.ts | 3 +- .../browser/widget/input/chatInputPart.ts | 2 +- .../input/editor/chatInputCompletions.ts | 2 +- .../widget/input/modePickerActionItem.ts | 3 +- .../input/modelPicker/media/modelPicker.css | 8 ++- .../input/modelPicker/modelPickerHover.ts | 2 +- .../input/modelPicker/modelPickerWidget.ts | 9 ++- .../input/modelPicker/modelProviderIcons.ts | 7 ++ .../input/permissionPickerActionItem.ts | 3 +- .../input/sessionTargetPickerActionItem.ts | 3 +- .../widget/input/workspacePickerActionItem.ts | 4 +- .../chat/browser/widget/media/chat.css | 70 +++++++++++-------- .../chat/test/browser/chatIcons.test.ts | 40 +++++++++++ .../chatQuestionCarouselPart.test.ts | 2 +- .../chatContentParts/chatRichLink.test.ts | 4 +- .../chatSubagentContentPart.test.ts | 6 +- .../chatSystemNotificationContentPart.test.ts | 2 +- .../chatThinkingContentPart.test.ts | 6 +- .../chatToolProgressPart.test.ts | 4 +- .../widget/chatForkActionViewItem.test.ts | 4 +- .../modelPicker/modelProviderIcons.test.ts | 18 ++++- .../contrib/mcp/browser/mcpCommands.ts | 7 +- 75 files changed, 351 insertions(+), 173 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/browser/chatIcons.ts create mode 100644 src/vs/workbench/contrib/chat/test/browser/chatIcons.test.ts diff --git a/src/vs/sessions/AI_CUSTOMIZATIONS.md b/src/vs/sessions/AI_CUSTOMIZATIONS.md index 626cf7ce2dad29..3298233dd41172 100644 --- a/src/vs/sessions/AI_CUSTOMIZATIONS.md +++ b/src/vs/sessions/AI_CUSTOMIZATIONS.md @@ -69,6 +69,8 @@ The management editor opens as a compact modal editor. The modal title and welco The first sidebar entry is a static `Overview` navigation item. It is styled like the other sidebar labels and does not mirror the active harness label; harness identity is represented by the modal title and welcome heading instead. +Dense management-editor affordances, including group and tool-list chevrons, use compact codicon glyphs and the compact codicon font-size token. + The Tools section can browse the Marketplace in the core workbench, where extension gallery browsing and installation are available. The Sessions window hides Tools Marketplace browsing and only shows the tool enablement list. The Plugins section keeps plugin maintenance close to plugin creation: its compact toolbar includes an accessible Update Plugins button beside Create Plugin. This invokes the shared `workbench.agentPlugins.checkForUpdates` command, matching the Update Plugins action in the installed Agent Plugins view title; holding Alt/Shift on that view-title action invokes the existing force-update command. Update actions are disabled while the shared operation is running. Progress is shown while checking, followed by a notification listing updated or failed plugins, or confirming that plugins are already up to date. diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatSpeechToTextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatSpeechToTextActions.ts index b6aa51e9b5837f..76054356b1f494 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatSpeechToTextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatSpeechToTextActions.ts @@ -141,7 +141,7 @@ export class ToggleChatSpeechToTextAction extends Action2 { id: ToggleChatSpeechToTextAction.ID, title: localize2('chat.speechToText.start', "Dictate (Speech to Text)"), category: CHAT_CATEGORY, - icon: Codicon.mic, + icon: Codicon.micCompact, f1: false, toggled: { condition: ChatContextKeys.speechToTextRecording, diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatInputPicker.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatInputPicker.ts index 9a8ebda1d25639..1f1df01504cfdc 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatInputPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatInputPicker.ts @@ -48,6 +48,7 @@ import { IAgentHostSessionWorkingDirectoryResolver } from './agentHostSessionWor import { IAgentHostNewSessionFolderService } from './agentHostNewSessionFolderService.js'; import { IAgentHostUntitledProvisionalSessionService } from './agentHostUntitledProvisionalSessionService.js'; import { toAgentHostBackendSessionUri } from './agentHostSessionUri.js'; +import { getCompactCodicon } from '../../chatIcons.js'; const FILTER_THRESHOLD = 10; @@ -544,7 +545,7 @@ export class AgentHostChatInputPicker extends Disposable { const icon = getConfigIcon(this._property, value); if (icon) { - dom.append(trigger, renderIcon(icon)); + dom.append(trigger, renderIcon(getCompactCodicon(icon))); } // Mirror the sessions-side picker: elevated approval levels get themed colors. if (this._property === SessionConfigKey.AutoApprove) { diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostFolderPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostFolderPickerActionItem.ts index b42041b78d7646..20aa5937d8485d 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostFolderPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostFolderPickerActionItem.ts @@ -152,7 +152,7 @@ export class AgentHostFolderPickerActionItem extends ChatInputPickerActionViewIt const label = folder ? folder.name : (selected ? basename(selected) : localize('agentHost.selectFolder', "Folder")); dom.reset( element, - ...renderLabelWithIcons(`$(folder)`), + ...renderLabelWithIcons(`$(folder-compact)`), dom.$('span.chat-input-picker-label', undefined, label), ); // Set the aria label after the visible text is in place: the base class diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/media/agentHostChatInputPicker.css b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/media/agentHostChatInputPicker.css index 109a6aff6c4684..509d6325322241 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/media/agentHostChatInputPicker.css +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/media/agentHostChatInputPicker.css @@ -84,7 +84,7 @@ } .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } } diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/experiments/media/unifiedQuickAccess.css b/src/vs/workbench/contrib/chat/browser/agentSessions/experiments/media/unifiedQuickAccess.css index f1d5cb69f3e571..75ac3713b3aff1 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/experiments/media/unifiedQuickAccess.css +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/experiments/media/unifiedQuickAccess.css @@ -79,7 +79,7 @@ } .unified-send-button .codicon { - font-size: 14px !important; + font-size: var(--vscode-codiconFontSize-compact) !important; color: var(--vscode-button-foreground) !important; } @@ -149,7 +149,7 @@ } .unified-quick-access-pr-badge .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } /* Action text styling (e.g., "Run 'zsh' command?") */ diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css b/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css index 2727e614834b5c..ffd88b09535e1e 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css @@ -95,7 +95,7 @@ align-items: center; margin-left: 4px; color: var(--vscode-charts-blue); - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); pointer-events: none; &.visible { diff --git a/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts b/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts index dc248427bb1a17..e631223a7e29bd 100644 --- a/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts @@ -172,7 +172,7 @@ class GroupHeaderRenderer implements IListRenderer { groupItems.style.display = collapsed ? 'none' : ''; chevron.className = 'group-chevron'; - chevron.classList.add(...ThemeIcon.asClassNameArray(collapsed ? Codicon.chevronRight : Codicon.chevronDown)); + chevron.classList.add(...ThemeIcon.asClassNameArray(collapsed ? Codicon.chevronRightCompact : Codicon.chevronDownCompact)); groupToggle.setAttribute('aria-expanded', String(!collapsed)); this.migrationListScrollable?.scanDomNode(); }; diff --git a/src/vs/workbench/contrib/chat/browser/aiCustomization/customizationGroupHeaderRenderer.ts b/src/vs/workbench/contrib/chat/browser/aiCustomization/customizationGroupHeaderRenderer.ts index 31b5dca325dd2e..015e4924d6cdba 100644 --- a/src/vs/workbench/contrib/chat/browser/aiCustomization/customizationGroupHeaderRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/aiCustomization/customizationGroupHeaderRenderer.ts @@ -72,7 +72,7 @@ export class CustomizationGroupHeaderRenderer { const expanded = vm.forceExpanded || this._expanded.read(reader).has(ts.id); group.style.display = expanded ? '' : 'none'; - chevron.classList.toggle('codicon-chevron-down', expanded); - chevron.classList.toggle('codicon-chevron-right', !expanded); + chevron.classList.toggle('codicon-chevron-down-compact', expanded); + chevron.classList.toggle('codicon-chevron-right-compact', !expanded); row.setAttribute('aria-expanded', String(expanded)); this._treeScrollable.scanDomNode(); })); diff --git a/src/vs/workbench/contrib/chat/browser/attachments/chatAttachmentWidgets.ts b/src/vs/workbench/contrib/chat/browser/attachments/chatAttachmentWidgets.ts index 30d39f2b5d7ec3..5e3308b1ee50dc 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/chatAttachmentWidgets.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/chatAttachmentWidgets.ts @@ -343,7 +343,7 @@ export class FileAttachmentWidget extends AbstractChatAttachmentWidget { } private renderOmittedWarning(friendlyName: string, ariaLabel: string) { - const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$('span.codicon.codicon-warning')); + const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$('span.codicon.codicon-warning-compact')); const textLabel = dom.$('span.chat-attached-context-custom-text', {}, friendlyName); this.element.appendChild(pillIcon); this.element.appendChild(textLabel); @@ -547,7 +547,7 @@ export class ImageAttachmentWidget extends AbstractChatAttachmentWidget { const imageElements = this._register(new MutableDisposable()); const renderImageElements = (buffer: Uint8Array) => { - imageElements.value = createImageElements(resource, attachment.name, fullName, this.element, buffer, attachment.id, this.hoverService, ariaLabel, currentLanguageModelName, clickHandler, this.currentLanguageModel, omittedState); + imageElements.value = createImageElements(resource, attachment.name, fullName, this.element, buffer, attachment.id, this.hoverService, ariaLabel, currentLanguageModelName, clickHandler, this.currentLanguageModel, omittedState, options.isCurrentInput === true); // createImageElements resets the label; restore the deletion hint after each render. this.element.ariaLabel = this.appendDeletionHint(ariaLabel); }; @@ -692,7 +692,8 @@ function createImageElements(resource: URI | undefined, name: string, fullName: currentLanguageModelName: string | undefined, clickHandler: () => void, currentLanguageModel?: ILanguageModelChatMetadataAndIdentifier, - omittedState?: OmittedState): IDisposable { + omittedState?: OmittedState, + useCompactWarningIcon = false): IDisposable { const disposable = new DisposableStore(); if (omittedState === OmittedState.Partial) { @@ -705,8 +706,14 @@ function createImageElements(resource: URI | undefined, name: string, fullName: if (resource) { element.style.cursor = 'pointer'; } + const createPillIcon = (icon: ThemeIcon) => { + const iconElement = dom.$('span'); + iconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + return dom.$('div.chat-attached-context-pill', {}, iconElement); + }; const supportsVision = modelSupportsVision(currentLanguageModel); - const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$(supportsVision ? 'span.codicon.codicon-file-media' : 'span.codicon.codicon-warning')); + const warningIcon = useCompactWarningIcon ? Codicon.warningCompact : Codicon.warning; + const pillIcon = createPillIcon(supportsVision ? Codicon.fileMediaCompact : warningIcon); const textLabel = dom.$('span.chat-attached-context-custom-text', {}, name); element.appendChild(pillIcon); element.appendChild(textLabel); @@ -741,7 +748,7 @@ function createImageElements(resource: URI | undefined, name: string, fullName: } else { const onImageFailed = () => { // reset to original icon on error or invalid image - const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$('span.codicon.codicon-file-media')); + const pillIcon = createPillIcon(Codicon.fileMediaCompact); replacePill(pillIcon); }; const hoverFullName = omittedState === OmittedState.Partial ? localize('chat.imageAttachmentWarning', "This GIF was partially omitted - current frame will be sent.") : fullName; @@ -1241,7 +1248,7 @@ export class NotebookCellOutputChatAttachmentWidget extends AbstractChatAttachme resource: URI, attachment: INotebookOutputVariableEntry, currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, - options: { shouldFocusClearButton: boolean; supportsDeletion: boolean }, + options: { shouldFocusClearButton: boolean; supportsDeletion: boolean; isCurrentInput?: boolean }, container: HTMLElement, contextResourceLabels: ResourceLabels, @ICommandService commandService: ICommandService, @@ -1262,7 +1269,7 @@ export class NotebookCellOutputChatAttachmentWidget extends AbstractChatAttachme case 'image/png': case 'image/jpeg': case 'image/svg': { - this.renderImageOutput(resource, attachment); + this.renderImageOutput(resource, attachment, options.isCurrentInput === true); break; } default: { @@ -1298,7 +1305,7 @@ export class NotebookCellOutputChatAttachmentWidget extends AbstractChatAttachme this.element.ariaLabel = this.appendDeletionHint(this.getAriaLabel(attachment)); this.label.setFile(resource, { hidePath: true, icon: ThemeIcon.fromId('output') }); } - private renderImageOutput(resource: URI, attachment: INotebookOutputVariableEntry) { + private renderImageOutput(resource: URI, attachment: INotebookOutputVariableEntry, useCompactWarningIcon: boolean) { let ariaLabel: string; if (attachment.omittedState === OmittedState.Full) { ariaLabel = localize('chat.omittedNotebookImageAttachment', "Omitted this Notebook ouput: {0}", attachment.name); @@ -1311,7 +1318,7 @@ export class NotebookCellOutputChatAttachmentWidget extends AbstractChatAttachme const clickHandler = async () => await this.openResource(resource, { editorOptions: { preserveFocus: true } }, false, undefined); const currentLanguageModelName = this.currentLanguageModel ? this.languageModelsService.lookupLanguageModel(this.currentLanguageModel.identifier)?.name ?? this.currentLanguageModel.identifier : undefined; const buffer = this.getOutputItem(resource, attachment)?.data.buffer ?? new Uint8Array(); - this._register(createImageElements(resource, attachment.name, attachment.name, this.element, buffer, attachment.id, this.hoverService, ariaLabel, currentLanguageModelName, clickHandler, this.currentLanguageModel, attachment.omittedState)); + this._register(createImageElements(resource, attachment.name, attachment.name, this.element, buffer, attachment.id, this.hoverService, ariaLabel, currentLanguageModelName, clickHandler, this.currentLanguageModel, attachment.omittedState, useCompactWarningIcon)); this.element.ariaLabel = this.appendDeletionHint(ariaLabel); } diff --git a/src/vs/workbench/contrib/chat/browser/chatDebug/chatCustomizationDiscoveryRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatDebug/chatCustomizationDiscoveryRenderer.ts index dc4060614c2c4e..c080c59d7ae002 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDebug/chatCustomizationDiscoveryRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDebug/chatCustomizationDiscoveryRenderer.ts @@ -21,6 +21,7 @@ import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { IChatDebugCustomizationLogEntry, IChatDebugEventCustomizationSummaryContent, IChatDebugEventFileListContent } from '../../common/chatDebugService.js'; +import { getCompactCodicon } from '../chatIcons.js'; import { InlineAnchorWidget } from '../widget/chatContentParts/chatInlineAnchorWidget.js'; import { setupCollapsibleToggle } from './chatDebugCollapsible.js'; @@ -189,7 +190,7 @@ export function renderCustomizationDiscoveryContent(content: IChatDebugEventFile for (const file of files) { const row = DOM.append(listEl, $('div.chat-debug-file-list-row')); - DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(Codicon.check)}`)); + DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(Codicon.checkCompact)}`)); row.appendChild(createInlineFileLink(file.uri, file.name ?? file.uri.path, FileKind.FILE, openerService, modelService, languageService, hoverService, labelService, disposables)); const relativeLabel = labelService.getUriLabel(file.uri, { relative: true }); row.setAttribute('aria-label', relativeLabel); @@ -231,7 +232,7 @@ export function renderCustomizationDiscoveryContent(content: IChatDebugEventFile for (const file of files) { const row = DOM.append(listEl, $('div.chat-debug-file-list-row')); - DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(Codicon.close)}`)); + DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(Codicon.closeCompact)}`)); // Build per-file detail (error message / duplicate info) let detail = ''; @@ -477,7 +478,7 @@ export function renderCustomizationSummaryContent(content: IChatDebugEventCustom } for (const entry of groupEntries) { const row = DOM.append(listEl, $('div.chat-debug-file-list-row')); - DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(icon)}`)); + DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(getCompactCodicon(icon))}`)); if (entry.uri) { row.appendChild(createInlineFileLink( @@ -495,7 +496,7 @@ export function renderCustomizationSummaryContent(content: IChatDebugEventCustom } else { for (const entry of entries) { const row = DOM.append(listEl, $('div.chat-debug-file-list-row')); - DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(icon)}`)); + DOM.append(row, $(`span.chat-debug-file-list-icon${ThemeIcon.asCSSSelector(getCompactCodicon(icon))}`)); // Hide the reason for skills (e.g. "local") and custom-agents — it's noise in the UI. const showReason = entry.category !== 'skill' && entry.category !== 'custom-agent'; diff --git a/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugCacheExplorerView.ts b/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugCacheExplorerView.ts index db2a6350bb12c9..ccd3d8cc99654d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugCacheExplorerView.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugCacheExplorerView.ts @@ -1873,11 +1873,11 @@ function currentDeltaComponent(side: ISideData): IComponentDrift { /** Codicon name for a break-cause category (rail chips, health card). */ function categoryIcon(category: CacheBreakCategory): string { switch (category) { - case CacheBreakCategory.Healthy: return 'check'; + case CacheBreakCategory.Healthy: return 'check-compact'; case CacheBreakCategory.Expiration: return 'clock'; case CacheBreakCategory.Model: return 'hubot'; case CacheBreakCategory.Tools: return 'tools'; - case CacheBreakCategory.System: return 'gear'; + case CacheBreakCategory.System: return 'gear-compact'; case CacheBreakCategory.Options: return 'symbol-parameter'; case CacheBreakCategory.History: return 'history'; case CacheBreakCategory.Unknown: return 'question'; diff --git a/src/vs/workbench/contrib/chat/browser/chatDebug/media/chatDebug.css b/src/vs/workbench/contrib/chat/browser/chatDebug/media/chatDebug.css index 7211b505021620..dc78aacda42669 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDebug/media/chatDebug.css +++ b/src/vs/workbench/contrib/chat/browser/chatDebug/media/chatDebug.css @@ -573,6 +573,7 @@ flex-shrink: 0; width: 16px; text-align: center; + font-size: var(--vscode-codiconFontSize-compact); } .chat-debug-file-link { color: var(--vscode-textLink-foreground); @@ -1197,7 +1198,7 @@ button.chat-debug-cache-finding.is-clickable:focus-visible { color: var(--vscode-descriptionForeground); } .chat-debug-cache-session-health-chip .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } .chat-debug-cache-session-health-chip.cause-tools, .chat-debug-cache-session-health-chip.cause-system, diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts index 35ddb2477e1722..b0978627fdd895 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts @@ -131,7 +131,7 @@ class ChatEditorOverlayWidget extends Disposable { const progressNode = document.createElement('div'); progressNode.classList.add('chat-editor-overlay-progress'); - append(progressNode, renderIcon(ThemeIcon.modify(Codicon.loading, 'spin'))); + append(progressNode, renderIcon(ThemeIcon.modify(Codicon.loadingCompact, 'spin'))); const textProgress = append(progressNode, $('span.progress-message')); this._domNode.appendChild(progressNode); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingExplanationWidget.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingExplanationWidget.ts index c0f59605795815..2edef286585f8a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingExplanationWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingExplanationWidget.ts @@ -274,12 +274,12 @@ export class ChatEditingExplanationWidget extends Disposable implements IOverlay this._readIndicator.classList.remove('partial', 'unread'); this._readIndicator.title = nls.localize('markAsUnread', "Mark as unread"); } else if (someRead) { - this._readIndicator.appendChild(renderIcon(Codicon.circleFilled)); + this._readIndicator.appendChild(renderIcon(Codicon.circleFilledCompact)); this._readIndicator.classList.remove('read', 'unread'); this._readIndicator.classList.add('partial'); this._readIndicator.title = nls.localize('markAllAsRead', "Mark all as read"); } else { - this._readIndicator.appendChild(renderIcon(Codicon.circleFilled)); + this._readIndicator.appendChild(renderIcon(Codicon.circleFilledCompact)); this._readIndicator.classList.remove('read', 'partial'); this._readIndicator.classList.add('unread'); this._readIndicator.title = nls.localize('markAsRead', "Mark as read"); @@ -450,7 +450,7 @@ export class ChatEditingExplanationWidget extends Disposable implements IOverlay element.classList.add('read'); element.classList.remove('unread'); } else { - element.appendChild(renderIcon(Codicon.circleFilled)); + element.appendChild(renderIcon(Codicon.circleFilledCompact)); element.classList.remove('read'); element.classList.add('unread'); } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingEditorOverlay.css b/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingEditorOverlay.css index fc44d44f42fad9..29210950e00aae 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingEditorOverlay.css +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingEditorOverlay.css @@ -43,6 +43,10 @@ gap: 6px; } +.chat-editor-overlay-widget .chat-editor-overlay-progress > .codicon { + font-size: var(--vscode-codiconFontSize-compact); +} + .chat-editor-overlay-widget.busy .chat-editor-overlay-progress { display: inline-flex; } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingExplanationWidget.css b/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingExplanationWidget.css index 71dbdf0a6d7eb6..3ff10136850062 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingExplanationWidget.css +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/media/chatEditingExplanationWidget.css @@ -89,6 +89,11 @@ background-color: var(--vscode-toolbar-hoverBackground); } +.chat-explanation-read-indicator > .codicon, +.chat-explanation-item-read > .codicon { + font-size: var(--vscode-codiconFontSize-compact); +} + .chat-explanation-read-indicator.unread { color: var(--vscode-textLink-foreground); opacity: 1; @@ -221,7 +226,7 @@ border-radius: 3px; color: var(--vscode-foreground); opacity: 0.3; - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } .chat-explanation-item:hover .chat-explanation-item-read { diff --git a/src/vs/workbench/contrib/chat/browser/chatIcons.ts b/src/vs/workbench/contrib/chat/browser/chatIcons.ts new file mode 100644 index 00000000000000..4efe79d368e189 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatIcons.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { getIconRegistry } from '../../../../platform/theme/common/iconRegistry.js'; + +/** + * Returns the compact variant of a codicon when one is registered. + */ +export function getCompactCodicon(icon: ThemeIcon): ThemeIcon { + const modifier = ThemeIcon.getModifier(icon); + const id = modifier ? icon.id.slice(0, -(modifier.length + 1)) : icon.id; + const compactId = `${id}-compact`; + + if (!getIconRegistry().getIcon(compactId)) { + return icon; + } + + return { + id: modifier ? `${compactId}~${modifier}` : compactId, + color: icon.color, + }; +} + +/** + * Replaces rendered codicon classes with their registered compact variants. + */ +export function compactCodiconsIn(element: HTMLElement): void { + const visit = (container: Element): void => { + for (const child of container.children) { + if (child.classList.contains('codicon')) { + const iconClass = [...child.classList].find(className => className.startsWith('codicon-') && !className.startsWith('codicon-modifier-')); + if (iconClass) { + const icon = ThemeIcon.fromId(iconClass.substring('codicon-'.length)); + const compactIcon = getCompactCodicon(icon); + if (compactIcon.id !== icon.id) { + child.classList.replace(iconClass, `codicon-${compactIcon.id}`); + } + } + } + visit(child); + } + }; + + visit(element); +} diff --git a/src/vs/workbench/contrib/chat/browser/chatManagement/media/chatModelsWidget.css b/src/vs/workbench/contrib/chat/browser/chatManagement/media/chatModelsWidget.css index 4ecf81f26c3090..31adbda4067cb9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatManagement/media/chatModelsWidget.css +++ b/src/vs/workbench/contrib/chat/browser/chatManagement/media/chatModelsWidget.css @@ -174,7 +174,7 @@ } .models-widget .models-table-container .monaco-table-td .model-name-container .model-deprecation-link .monaco-link .model-deprecation-link-label .codicon { - font-size: var(--vscode-codiconFontSize-compact, 12px); + font-size: var(--vscode-codiconFontSize-compact); } .models-widget .models-table-container .monaco-list-row:not(.selected) .monaco-table-tr.models-status-row .monaco-table-td .model-name-container .model-name .monaco-highlighted-label.error-status { @@ -236,7 +236,7 @@ } .models-widget .models-table-container .monaco-table-td .model-token-limits .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } /** Capabilities column styling **/ diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions/chatSessionPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/chatSessions/chatSessionPickerActionItem.ts index b0d490727d24b4..dae44597c96efd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions/chatSessionPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions/chatSessionPickerActionItem.ts @@ -26,6 +26,7 @@ import { autorun } from '../../../../../base/common/observable.js'; import { IChatEntitlementService } from '../../../../services/chat/common/chatEntitlementService.js'; import { IActionListItemHover } from '../../../../../platform/actionWidget/browser/actionList.js'; import { getModelHoverContent } from '../widget/input/modelPicker/modelPickerHover.js'; +import { getCompactCodicon } from '../chatIcons.js'; import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; @@ -241,7 +242,7 @@ export class ChatSessionPickerActionItem extends ActionWidgetDropdownActionViewI const isDefaultWithIcon = this.currentOption?.default && this.currentOption?.icon; if (this.currentOption?.icon) { - domChildren.push(renderIcon(this.currentOption.icon)); + domChildren.push(renderIcon(getCompactCodicon(this.currentOption.icon))); } if (!isDefaultWithIcon) { diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions/media/chatSessionPickerActionItem.css b/src/vs/workbench/contrib/chat/browser/chatSessions/media/chatSessionPickerActionItem.css index 1f313453aa9315..467617a42d3d9c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions/media/chatSessionPickerActionItem.css +++ b/src/vs/workbench/contrib/chat/browser/chatSessions/media/chatSessionPickerActionItem.css @@ -33,12 +33,12 @@ } span.codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); margin-left: 2px; } span.codicon.codicon-chevron-down { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); margin-left: 2px; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts b/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts index c460ce93d1ba22..08ab3d70e4cfe4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts @@ -449,7 +449,7 @@ export class ChatStatusDashboard extends DomWidget { disclosureHeader.appendChild($('span.collapsible-label', undefined, localize('inlineSuggestionsTab', "Inline Suggestions"))); chevron = disclosureHeader.appendChild($('span.collapsible-chevron')); - chevron.classList.add(...ThemeIcon.asClassNameArray(collapsed ? Codicon.chevronRight : Codicon.chevronDown)); + chevron.classList.add(...ThemeIcon.asClassNameArray(collapsed ? Codicon.chevronRightCompact : Codicon.chevronDownCompact)); statusEl = disclosureHeader.appendChild($('span.collapsible-status', undefined, getStatusText())); } @@ -467,7 +467,7 @@ export class ChatStatusDashboard extends DomWidget { collapsibleInner.inert = isCollapsed; disclosureHeader!.setAttribute('aria-expanded', String(!isCollapsed)); chevron!.className = 'collapsible-chevron'; - chevron!.classList.add(...ThemeIcon.asClassNameArray(isCollapsed ? Codicon.chevronRight : Codicon.chevronDown)); + chevron!.classList.add(...ThemeIcon.asClassNameArray(isCollapsed ? Codicon.chevronRightCompact : Codicon.chevronDownCompact)); this.storageService.store(ChatStatusDashboard.QUICK_SETTINGS_COLLAPSED_KEY, isCollapsed, StorageScope.PROFILE, StorageTarget.USER); }; diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus/media/chatStatus.css b/src/vs/workbench/contrib/chat/browser/chatStatus/media/chatStatus.css index c83132965487b6..b6c724a1ad111b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus/media/chatStatus.css +++ b/src/vs/workbench/contrib/chat/browser/chatStatus/media/chatStatus.css @@ -58,7 +58,7 @@ } .chat-status-bar-entry-tooltip .collapsible-chevron { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); display: flex; align-items: center; flex-shrink: 0; @@ -67,7 +67,7 @@ } .chat-status-bar-entry-tooltip .contributed-info-icon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); display: flex; align-items: center; flex-shrink: 0; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatTurnPills.css b/src/vs/workbench/contrib/chat/browser/media/chatTurnPills.css index 2b7d0cfefd7164..095749eccffdb1 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatTurnPills.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatTurnPills.css @@ -54,10 +54,10 @@ display: inline-flex; align-items: center; justify-content: center; - width: var(--vscode-codiconFontSize-compact, 12px); - height: var(--vscode-codiconFontSize-compact, 12px); + width: var(--vscode-codiconFontSize-compact); + height: var(--vscode-codiconFontSize-compact); margin: 0; - font-size: var(--vscode-codiconFontSize-compact, 12px); + font-size: var(--vscode-codiconFontSize-compact); flex-shrink: 0; } diff --git a/src/vs/workbench/contrib/chat/browser/speechToText/dictationOnboarding.ts b/src/vs/workbench/contrib/chat/browser/speechToText/dictationOnboarding.ts index 1f41c0c476dc42..97c8f0fa21b20a 100644 --- a/src/vs/workbench/contrib/chat/browser/speechToText/dictationOnboarding.ts +++ b/src/vs/workbench/contrib/chat/browser/speechToText/dictationOnboarding.ts @@ -764,7 +764,7 @@ export class DictationOnboardingBanner extends ChatInputNoticeWidget implements return; } - dom.append(this.pickerContainer, dom.$(`span.codicon.codicon-${Codicon.mic.id}.dictation-onboarding-picker-icon`)) + dom.append(this.pickerContainer, dom.$(`span.codicon.codicon-${Codicon.micCompact.id}.dictation-onboarding-picker-icon`)) .setAttribute('aria-hidden', 'true'); const selected = indexOfMicrophone(this.options, this.currentDeviceId()); diff --git a/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts b/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts index dd052a7cccc31b..557b2b125b33ba 100644 --- a/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts +++ b/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts @@ -97,7 +97,7 @@ export class ChatVoiceInputModeAction extends Action2 { super({ id: ChatVoiceInputModeAction.ID, title: localize2('voiceInputMode', "Voice Input Mode"), - icon: Codicon.mic, + icon: Codicon.micCompact, precondition: SegmentedVoiceInputModePillActive, menu: { id: MenuId.ChatExecute, @@ -630,7 +630,7 @@ export class VoiceInputModeActionViewItem extends BaseActionViewItem { // spinner, which the `.preparing` CSS animates. const dictationIcon = dictationBusy ? dictationDownloading.read(reader) ? Codicon.micDownloadCompact : Codicon.loadingCompact - : isDictating ? Codicon.micFilled : Codicon.mic; + : isDictating ? Codicon.micFilled : Codicon.micCompact; this._dictationIcon!.className = `chat-voice-input-mode-icon ${ThemeIcon.asClassName(dictationIcon)}`; // Wrap the download glyph in a determinate progress ring during an @@ -672,7 +672,7 @@ export class VoiceInputModeActionViewItem extends BaseActionViewItem { this._muteCell!.classList.toggle('collapsed', !mutePresent); this._muteCell!.classList.toggle('active', muted); this._muteCell!.setAttribute('aria-pressed', String(muted)); - this._muteIcon!.className = `chat-voice-input-mode-icon ${ThemeIcon.asClassName(muted ? Codicon.mic : Codicon.mute)}`; + this._muteIcon!.className = `chat-voice-input-mode-icon ${ThemeIcon.asClassName(muted ? Codicon.micCompact : Codicon.mute)}`; this._updateAriaLabels(); // Audio-reactive bars only while live (and not hovering the disconnect preview). diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts index 5e002c0ec1a019..5568750a6c283c 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts @@ -207,7 +207,7 @@ export class ChatCheckpointFileChangesSummaryContentPart extends Disposable impl const disposables = new DisposableStore(); disposables.add(this.renderViewAllFileChangesButton(container)); const chevron = container.appendChild($('span.chat-file-changes-chevron.chat-collapsible-hover-chevron', { 'aria-hidden': 'true' })); - chevron.classList.add(...ThemeIcon.asClassNameArray(Codicon.chevronRight)); + chevron.classList.add(...ThemeIcon.asClassNameArray(Codicon.chevronRightCompact)); this._register(autorun(r => { const diffs = this.fileChangesDiffsObservable.read(r); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatCollapsibleContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatCollapsibleContentPart.ts index 0f6a6976a18d31..cf99158fc5306e 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatCollapsibleContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatCollapsibleContentPart.ts @@ -22,6 +22,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ import { IMarkdownRenderer } from '../../../../../../platform/markdown/browser/markdownRenderer.js'; import { IRenderedMarkdown } from '../../../../../../base/browser/markdownRenderer.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import './media/chatCollapsibleContentPart.css'; @@ -50,7 +51,7 @@ export abstract class ChatCollapsibleContentPart extends Disposable implements I } public set icon(value: ThemeIcon | undefined) { - this._overrideIcon.set(value, undefined); + this._overrideIcon.set(value ? getCompactCodicon(value) : undefined, undefined); } protected readonly element: ChatTreeItem; @@ -106,7 +107,7 @@ export abstract class ChatCollapsibleContentPart extends Disposable implements I } // Add hover chevron indicator on the right (decorative, hide from screen readers) - const hoverChevron = $('span.chat-collapsible-hover-chevron.codicon.codicon-chevron-right', { 'aria-hidden': 'true' }); + const hoverChevron = $('span.chat-collapsible-hover-chevron.codicon.codicon-chevron-right-compact', { 'aria-hidden': 'true' }); this._hoverChevron = hoverChevron; collapseButton.element.appendChild(hoverChevron); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatEditPillElement.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatEditPillElement.ts index 75948f658f2caf..7961128401d59b 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatEditPillElement.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatEditPillElement.ts @@ -19,6 +19,7 @@ import { FileKind } from '../../../../../../platform/files/common/files.js'; import { IHoverService } from '../../../../../../platform/hover/browser/hover.js'; import { ILabelService } from '../../../../../../platform/label/common/label.js'; import { IOpenEditorOptions, registerOpenEditorListeners } from '../../../../../../platform/editor/browser/editor.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import './media/chatCodeBlockPill.css'; const $ = dom.$; @@ -126,7 +127,7 @@ export class ChatEditPillElement extends Disposable { */ setStatus(icon: ThemeIcon | undefined, label: string): void { this.statusIconEl.classList.remove(...this._statusIconClasses); - this._statusIconClasses = icon ? ThemeIcon.asClassNameArray(icon) : []; + this._statusIconClasses = icon ? ThemeIcon.asClassNameArray(getCompactCodicon(icon)) : []; if (this._statusIconClasses.length > 0) { this.statusIconEl.classList.add(...this._statusIconClasses); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatExtensionsContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatExtensionsContentPart.ts index 8d101f1c2fbfef..4154f8a9740e4a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatExtensionsContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatExtensionsContentPart.ts @@ -39,7 +39,7 @@ export class ChatExtensionsContentPart extends Disposable implements IChatConten this.domNode = dom.$('.chat-extensions-content-part'); const loadingElement = dom.append(this.domNode, dom.$('.loading-extensions-element')); - dom.append(loadingElement, dom.$(ThemeIcon.asCSSSelector(ThemeIcon.modify(Codicon.loading, 'spin'))), dom.$('span.loading-message', undefined, localize('chat.extensions.loading', 'Loading extensions...'))); + dom.append(loadingElement, dom.$(ThemeIcon.asCSSSelector(ThemeIcon.modify(Codicon.loadingCompact, 'spin'))), dom.$('span.loading-message', undefined, localize('chat.extensions.loading', 'Loading extensions...'))); const extensionsList = dom.append(this.domNode, dom.$('.extensions-list')); const list = this._register(instantiationService.createInstance(ExtensionsList, extensionsList, ChatViewId, { alwaysConsumeMouseWheel: false }, { onFocus: Event.None, onBlur: Event.None, filters: {} })); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMcpAuthenticationContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMcpAuthenticationContentPart.ts index bfbdde60c06f73..e93629b7a6868e 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMcpAuthenticationContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMcpAuthenticationContentPart.ts @@ -18,6 +18,7 @@ import { IAgentHostCustomizationService } from '../../agentSessions/agentHost/ag import { IChatMcpAuthenticationRequired, IChatMcpAuthenticationRequiredServer } from '../../../common/chatService/chatService.js'; import { ChatTreeItem } from '../../chat.js'; import { IChatRendererContent } from '../../../common/model/chatViewModel.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import { IChatContentPart } from './chatContentParts.js'; import './media/chatMcpServersInteractionContent.css'; @@ -91,7 +92,7 @@ export class ChatMcpAuthenticationContentPart extends Disposable implements ICha const container = dom.$('.chat-mcp-servers-interaction-hint'); const messageContainer = dom.$('.chat-mcp-servers-message'); const iconElement = dom.$('.chat-mcp-servers-icon'); - iconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + iconElement.classList.add(...ThemeIcon.asClassNameArray(getCompactCodicon(icon))); const rendered = this.rendered.value = this.markdownRendererService.render(new MarkdownString(content, { isTrusted: true }), action ? { actionHandler: (href: string) => { diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatProgressContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatProgressContentPart.ts index 33caddfbbfa1bf..ac4f9bbba91dfa 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatProgressContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatProgressContentPart.ts @@ -28,6 +28,7 @@ import { HoverStyle } from '../../../../../../base/browser/ui/hover/hover.js'; import { ILanguageModelToolsService } from '../../../common/tools/languageModelToolsService.js'; import { isEqual } from '../../../../../../base/common/resources.js'; import { buildPhrasePool, defaultThinkingMessages, maybePickFunWorkingMessage } from './chatThinkingContentPart.js'; +import { getCompactCodicon } from '../../chatIcons.js'; export class ChatProgressContentPart extends Disposable implements IChatContentPart { public readonly domNode: HTMLElement; @@ -255,7 +256,7 @@ export class ChatProgressSubPart extends Disposable { this.domNode = $('.progress-container'); const iconElement = $('div'); - iconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + iconElement.classList.add(...ThemeIcon.asClassNameArray(getCompactCodicon(icon))); if (tooltip) { this._register(hoverService.setupDelayedHover(iconElement, { content: tooltip, diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatQuestionCarouselPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatQuestionCarouselPart.ts index 56d0546a8e1a98..50c411592e4bf8 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatQuestionCarouselPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatQuestionCarouselPart.ts @@ -42,6 +42,7 @@ import { ITerminalChatService } from '../../../../terminal/browser/terminal.js'; import { AgentHostAutoReplyAnswer } from '../../../../../../platform/agentHost/common/agentHostSchema.js'; import { ChatCollapsibleContentPart } from './chatCollapsibleContentPart.js'; import { getChatMarkdownRenderOptions } from '../chatContentMarkdownRenderer.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import './media/chatQuestionCarousel.css'; const PREVIOUS_QUESTION_ACTION_ID = 'workbench.action.chat.previousQuestion'; @@ -73,7 +74,7 @@ class ChatQuestionAnswerCollapsiblePart extends ChatCollapsibleContentPart { const labelElement = this._collapseButton.labelElement; labelElement.textContent = ''; const icon = dom.$('span.chat-question-summary-answer-icon'); - icon.classList.add(...ThemeIcon.asClassNameArray(this.answerIcon)); + icon.classList.add(...ThemeIcon.asClassNameArray(getCompactCodicon(this.answerIcon))); icon.setAttribute('aria-hidden', 'true'); const value = dom.$('span.chat-question-summary-answer-value'); value.textContent = this.value; @@ -1732,7 +1733,7 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent answerTitle, answerPrefix, answerValue, - options?.answerIcon ?? (this.carousel.autoReply ? Codicon.copilotCompact : Codicon.comment), + options?.answerIcon ?? (this.carousel.autoReply ? Codicon.copilotCompact : Codicon.commentCompact), collapsibleContext, question.options?.length ? () => this.renderConversationOptions(question, answer) : undefined, () => this._onDidChangeHeight.fire(), diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatRichLink.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatRichLink.ts index c8b845c5484ac4..488faea8ae365b 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatRichLink.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatRichLink.ts @@ -168,11 +168,11 @@ const richLinkIcons: Readonly> = { pullRequest: 'git-pull-request', commit: 'git-commit', file: 'file', - folder: 'folder', + folder: 'folder-compact', session: 'agent', chat: 'comment-discussion', - repository: 'repo', - branch: 'git-branch', + repository: 'repo-compact', + branch: 'git-branch-compact', }; function createRichLinkStatusDom(document: Document, className: string): IChatRichLinkStatusDom { @@ -294,7 +294,7 @@ function getRichLinkStatusIcon( case 'neutral': return 'comment-discussion'; case 'error': - return 'error'; + return 'error-compact'; } } switch (statusKind) { @@ -302,11 +302,11 @@ function getRichLinkStatusIcon( case 'closed': return presentationKind === 'pullRequest' ? 'git-pull-request-closed' : 'issue-closed'; case 'merged': return 'git-merge'; case 'draft': return 'git-pull-request-draft'; - case 'notPlanned': return 'circle-slash'; - case 'pending': return 'circle-filled'; - case 'success': return 'pass-filled'; - case 'warning': return 'warning'; - case 'error': return 'error'; + case 'notPlanned': return 'circle-slash-compact'; + case 'pending': return 'circle-filled-compact'; + case 'success': return 'pass-filled-compact'; + case 'warning': return 'warning-compact'; + case 'error': return 'error-compact'; case 'neutral': return 'circle-outline'; } } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentContentPart.ts index 1af15181fcdb2c..0c2f7ee122aa7a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentContentPart.ts @@ -500,16 +500,16 @@ export class ChatSubagentContentPart extends ChatCollapsibleContentPart implemen // Note: wrapper is created lazily in initContent(), so we can't set its style here if (this._collapseButton && this.isActive) { - this._collapseButton.icon = Codicon.circleFilled; + this._collapseButton.icon = Codicon.circleFilledCompact; } this._register(autorun(r => { this.expanded.read(r); if (this._collapseButton) { if (this.isActive) { - this._collapseButton.icon = Codicon.circleFilled; + this._collapseButton.icon = Codicon.circleFilledCompact; } else { - this._collapseButton.icon = Codicon.check; + this._collapseButton.icon = Codicon.checkCompact; } } })); @@ -771,7 +771,7 @@ export class ChatSubagentContentPart extends ChatCollapsibleContentPart implemen this._updateOpenChatToolbarContext(); this.domNode.classList.remove('chat-thinking-active'); if (this._collapseButton) { - this._collapseButton.icon = Codicon.check; + this._collapseButton.icon = Codicon.checkCompact; } this.removeWorkingSpinner(); @@ -794,7 +794,7 @@ export class ChatSubagentContentPart extends ChatCollapsibleContentPart implemen this.setContentAnimationEnabled(false); this.domNode.classList.add('chat-thinking-active'); if (this._collapseButton) { - this._collapseButton.icon = Codicon.circleFilled; + this._collapseButton.icon = Codicon.circleFilledCompact; } if (this.wrapper && !this.hasToolsWaitingForConfirmation) { this.showWorkingSpinner(); @@ -822,7 +822,7 @@ export class ChatSubagentContentPart extends ChatCollapsibleContentPart implemen public finalizeTitle(): void { this.updateTitle(); if (this._collapseButton) { - this._collapseButton.icon = Codicon.check; + this._collapseButton.icon = Codicon.checkCompact; } } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentOpenChat.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentOpenChat.ts index 579dd9602d10f8..b95137ae4b717c 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentOpenChat.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSubagentOpenChat.ts @@ -35,6 +35,7 @@ import { IChatWidgetService } from '../../chat.js'; import { getChatMarkdownRenderOptions } from '../chatContentMarkdownRenderer.js'; import { renderFileWidgets } from './chatInlineAnchorWidget.js'; import { IChatMarkdownAnchorService } from './chatMarkdownAnchorService.js'; +import { getCompactCodicon } from '../../chatIcons.js'; export interface IOpenSubagentChatContext { readonly chatResource: string; @@ -566,7 +567,7 @@ export class OpenSubagentChatActionViewItem extends BaseActionViewItem { } this._activeToolIconElement.className = 'chat-subagent-pill-active-tool-icon'; if (icon) { - this._activeToolIconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + this._activeToolIconElement.classList.add(...ThemeIcon.asClassNameArray(getCompactCodicon(icon))); } } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSuggestNextWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSuggestNextWidget.ts index 1a86aeab15739b..32b926cc7b9203 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSuggestNextWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSuggestNextWidget.ts @@ -165,7 +165,7 @@ export class ChatSuggestNextWidget extends Disposable { const separator = dom.append(dropdownContainer, dom.$('.chat-suggest-next-separator')); separator.setAttribute('aria-hidden', 'true'); - const chevron = dom.append(dropdownContainer, dom.$('.codicon.codicon-chevron-down.dropdown-chevron')); + const chevron = dom.append(dropdownContainer, dom.$('.codicon.codicon-chevron-down-compact.dropdown-chevron')); chevron.setAttribute('aria-hidden', 'true'); const showContextMenu = (e: MouseEvent | KeyboardEvent, anchor?: HTMLElement) => { diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts index 63ffb968ae187e..931f7bcb21704a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts @@ -38,6 +38,7 @@ import { IChatMarkdownAnchorService } from './chatMarkdownAnchorService.js'; import { ChatMessageRole, ILanguageModelsService } from '../../../common/languageModels.js'; import './media/chatThinkingContent.css'; import { IHoverService } from '../../../../../../platform/hover/browser/hover.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js'; import { IEditorService } from '../../../../../services/editor/common/editorService.js'; import { extractImagesFromToolInvocationOutputDetails } from '../../../common/chatImageExtraction.js'; @@ -160,13 +161,13 @@ export function getToolInvocationIcon(toolId: string, registeredIcon?: ThemeIcon export function createThinkingIcon(icon: ThemeIcon): HTMLElement { const iconElement = $('span.chat-thinking-icon'); - iconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + iconElement.classList.add(...ThemeIcon.asClassNameArray(getCompactCodicon(icon))); return iconElement; } function setThinkingIcon(iconElement: HTMLElement, icon: ThemeIcon): void { iconElement.className = 'chat-thinking-icon'; - iconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + iconElement.classList.add(...ThemeIcon.asClassNameArray(getCompactCodicon(icon))); } function extractTitleFromThinkingContent(content: string): string | undefined { @@ -495,12 +496,12 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen const isExpanded = this.expanded.read(r); if (this._collapseButton) { if (this.streamingCompleted || this.element.isComplete) { - this._collapseButton.icon = Codicon.check; + this._collapseButton.icon = Codicon.checkCompact; } else if (!this.fixedScrollingMode) { if (isExpanded) { - this._collapseButton.icon = Codicon.chevronDown; + this._collapseButton.icon = Codicon.chevronDownCompact; } else { - this._collapseButton.icon = Codicon.circleFilled; + this._collapseButton.icon = Codicon.circleFilledCompact; } } } @@ -1282,7 +1283,7 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen } if (this._collapseButton) { - this._collapseButton.icon = Codicon.check; + this._collapseButton.icon = Codicon.checkCompact; } // Update scroll dimensions now that streaming is complete @@ -1699,7 +1700,7 @@ ${this.hookCount > 0 ? `EXAMPLES WITH BLOCKED CONTENT (from hooks): this.flushPendingExternalResources(); if (this._collapseButton) { - this._collapseButton.icon = Codicon.check; + this._collapseButton.icon = Codicon.checkCompact; this.setFinalizedTitle(finalLabel); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatToolInputOutputContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatToolInputOutputContentPart.ts index 2f7d77354602ae..a6f249e9b04519 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatToolInputOutputContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatToolInputOutputContentPart.ts @@ -128,7 +128,7 @@ export class ChatCollapsibleInputOutputContentPart extends Disposable { elements.root.appendChild(contentAnimation.root); // Add hover chevron indicator on the right (decorative, hide from screen readers) - const hoverChevron = dom.$('span.chat-collapsible-hover-chevron.codicon.codicon-chevron-right'); + const hoverChevron = dom.$('span.chat-collapsible-hover-chevron.codicon.codicon-chevron-right-compact'); hoverChevron.setAttribute('aria-hidden', 'true'); btn.element.appendChild(hoverChevron); @@ -142,11 +142,11 @@ export class ChatCollapsibleInputOutputContentPart extends Disposable { const isInProgress = !output && !isError; if (isError) { - btn.icon = Codicon.error; + btn.icon = Codicon.errorCompact; } else { btn.icon = output - ? Codicon.check - : ThemeIcon.modify(Codicon.loading, 'spin'); + ? Codicon.checkCompact + : ThemeIcon.modify(Codicon.loadingCompact, 'spin'); } elements.root.classList.toggle('shimmer-progress', shimmer && isInProgress); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTurnPillsPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTurnPillsPart.ts index 3097355a5743cd..9e5614fcf0e0ac 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTurnPillsPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTurnPillsPart.ts @@ -222,7 +222,7 @@ export class ChatTurnPillsContentPart extends Disposable implements IChatContent private _renderChevron(header: HTMLElement, details: HTMLDetailsElement, showChanges: IObservable): IDisposable { const chevron = header.appendChild($('span.chat-file-changes-chevron.chat-collapsible-hover-chevron', { 'aria-hidden': 'true' })); - chevron.classList.add(...ThemeIcon.asClassNameArray(Codicon.chevronRight)); + chevron.classList.add(...ThemeIcon.asClassNameArray(Codicon.chevronRightCompact)); const setExpansionState = () => { header.setAttribute('aria-expanded', String(details.open)); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatCodeBlockPill.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatCodeBlockPill.css index 064c9184d911ab..05ffffdfe1a7de 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatCodeBlockPill.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatCodeBlockPill.css @@ -33,15 +33,15 @@ color: var(--vscode-icon-foreground) !important; &::before { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } } - .codicon.codicon-check { + .codicon.codicon-check-compact { display: none; } - .show-checkmarks & .codicon.codicon-check { + .show-checkmarks & .codicon.codicon-check-compact { display: inline-flex; padding-left: 2px; margin-left: 0.2em; diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatConfirmationWidget.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatConfirmationWidget.css index ff2c204ebae8f1..7fd7ca4933806a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatConfirmationWidget.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatConfirmationWidget.css @@ -78,7 +78,7 @@ margin: 0 !important; } -.chat-confirmation-widget .chat-confirmation-widget-title .codicon-error { +.chat-confirmation-widget .chat-confirmation-widget-title .codicon-error-compact { color: var(--vscode-errorForeground) !important; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatDisabledClaudeHooksContent.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatDisabledClaudeHooksContent.css index 5874eda712ca95..7f8394ec2ad33d 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatDisabledClaudeHooksContent.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatDisabledClaudeHooksContent.css @@ -12,3 +12,7 @@ color: var(--vscode-descriptionForeground); opacity: 0.8; } + +.chat-disabled-claude-hooks-icon { + font-size: var(--vscode-codiconFontSize-compact); +} diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatExtensionsContent.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatExtensionsContent.css index f9be266abd32b3..a83adf4e767443 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatExtensionsContent.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatExtensionsContent.css @@ -25,3 +25,7 @@ .chat-extensions-content-part .loading-extensions-element .loading-message { padding-left: 4px; } + +.chat-extensions-content-part .loading-extensions-element > .codicon { + font-size: var(--vscode-codiconFontSize-compact); +} diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatMcpServersInteractionContent.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatMcpServersInteractionContent.css index 4f21ba946e7bc0..7b38f962b3f6b7 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatMcpServersInteractionContent.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatMcpServersInteractionContent.css @@ -12,3 +12,7 @@ color: var(--vscode-descriptionForeground); opacity: 0.8; } + +.chat-mcp-servers-icon { + font-size: var(--vscode-codiconFontSize-compact); +} diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css index 6a3028fdfd3ebe..878f491482eeb4 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css @@ -43,7 +43,7 @@ } .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } .rendered-markdown.collapsible-title-content { @@ -112,7 +112,7 @@ &.chat-thinking-active > .chat-used-context-label .monaco-button.monaco-icon-button { - .codicon.codicon-circle-filled { + .codicon.codicon-circle-filled-compact { display: none; } } @@ -223,8 +223,8 @@ } /* todo: ideally not !important, but the competing css has 14 specificity */ - .codicon.codicon-check, - .codicon.codicon-loading { + .codicon.codicon-check-compact, + .codicon.codicon-loading-compact { display: none !important; } @@ -236,7 +236,7 @@ display: inline-block; } - .show-checkmarks .progress-container > .codicon.codicon-check { + .show-checkmarks .progress-container > .codicon.codicon-check-compact { display: inline-flex; } @@ -269,11 +269,11 @@ .chat-thinking-tool-wrapper .chat-markdown-part.rendered-markdown { padding: 5px 12px 4px 24px; - .status-icon.codicon-check { + .status-icon.codicon-check-compact { display: none; } - .show-checkmarks .status-icon.codicon-check { + .show-checkmarks .status-icon.codicon-check-compact { display: inline-flex; } @@ -295,11 +295,11 @@ .chat-thinking-tool-wrapper > .chat-codeblock-pill-container { padding: 5px 12px 4px 24px; - .status-icon.codicon-check { + .status-icon.codicon-check-compact { display: none; } - .show-checkmarks .status-icon.codicon-check { + .show-checkmarks .status-icon.codicon-check-compact { display: inline-flex; } } @@ -349,7 +349,7 @@ top: 9px; width: 12px; height: 12px; - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); line-height: 12px; text-align: center; color: var(--vscode-descriptionForeground); @@ -412,7 +412,7 @@ margin-bottom: 2px; overflow: hidden; - .codicon:not(.codicon-check, .codicon-loading) { + .codicon:not(.codicon-check-compact, .codicon-loading-compact) { display: inline-flex; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatDragAndDrop.ts b/src/vs/workbench/contrib/chat/browser/widget/chatDragAndDrop.ts index 8f5b8bc7b2816a..6b27da6988ef74 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatDragAndDrop.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatDragAndDrop.ts @@ -467,7 +467,7 @@ export class ChatDragAndDrop extends Themable { if (type !== undefined) { // Render the overlay text - const iconAndtextElements = renderLabelWithIcons(`$(${Codicon.attach.id}) ${this.getOverlayText(type)}`); + const iconAndtextElements = renderLabelWithIcons(`$(${Codicon.attachCompact.id}) ${this.getOverlayText(type)}`); const htmlElements = iconAndtextElements.map(element => { if (typeof element === 'string') { return $('span.overlay-text', undefined, element); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts b/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts index d1ed50e89bd9ca..d8f436eb6bbadf 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts @@ -11,9 +11,10 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { localize } from '../../../../../nls.js'; import { CodiconActionViewItem } from '../../../notebook/browser/view/cellParts/cellActionView.js'; -const forkIconClasses = ThemeIcon.asClassNameArray(Codicon.repoForked); -const spinnerIconClasses = ThemeIcon.asClassNameArray(ThemeIcon.modify(Codicon.loading, 'spin')); -const labelIconClasses = [...new Set([...forkIconClasses, ...spinnerIconClasses])].filter(className => className !== 'codicon'); +const actionIconClasses = ThemeIcon.asClassNameArray(Codicon.repoForked); +const forkIconClasses = ThemeIcon.asClassNameArray(Codicon.repoForkedCompact); +const spinnerIconClasses = ThemeIcon.asClassNameArray(ThemeIcon.modify(Codicon.loadingCompact, 'spin')); +const labelIconClasses = [...new Set([...actionIconClasses, ...forkIconClasses, ...spinnerIconClasses])].filter(className => className !== 'codicon'); export class ChatForkActionViewItem extends CodiconActionViewItem { diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts index 2725a3f0a12319..87d12ff3a00df0 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts @@ -68,6 +68,7 @@ import { ClickAnimation } from '../../../../../base/browser/ui/animations/animat import { ForkConversationActionId } from '../actions/chatForkActions.js'; import { MarkHelpfulActionId } from '../actions/chatTitleActions.js'; import { ChatTreeItem, IChatCodeBlockInfo, IChatFileTreeInfo, IChatListItemRendererOptions, IChatWidgetService } from '../chat.js'; +import { getCompactCodicon } from '../chatIcons.js'; import { AgentHostSnapshotController } from '../agentSessions/agentHost/agentHostSnapshotController.js'; import { RestoreCheckpointActionId, StartOverActionId } from '../chatEditing/chatEditingActions.js'; import { ChatForkActionViewItem } from './chatForkActionViewItem.js'; @@ -1594,7 +1595,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer extends Disposable { } button.element.ariaLabel = localize('followUpAriaLabel', "Follow up question: {0}", baseTitle); button.label = new MarkdownString(baseTitle); + compactCodiconsIn(button.element); this._register(button.onDidClick(() => this.clickHandler(followup))); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputNoticeWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputNoticeWidget.ts index 960690385a1121..22bee623e636a2 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputNoticeWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputNoticeWidget.ts @@ -14,6 +14,7 @@ import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../.. import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { localize } from '../../../../../../nls.js'; import { IChatInputNoticeFocusTarget } from './chatInputNoticeHost.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import './media/chatInputNotice.css'; /** @@ -220,7 +221,7 @@ export class ChatInputNoticeWidget extends Disposable implements IChatInputNotic // The producer's class goes on the action itself rather than the housing, so // it names the thing that is actually clicked, focused and styled. - const cssClass = [ThemeIcon.asClassName(options.icon), options.className].filter(Boolean).join(' '); + const cssClass = [ThemeIcon.asClassName(getCompactCodicon(options.icon)), options.className].filter(Boolean).join(' '); const actionBar = register(new ActionBar(container)); actionBar.push(register(new Action('chatInputNotice.action', options.ariaLabel, cssClass, true, async () => options.onActivate())), { icon: true, label: false }); 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 3b9cb08d3e1ee3..57bb0e14441422 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts @@ -3484,7 +3484,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge // model — matching the same icon set the CSS keys off — rather than // querying the DOM. const voiceInputActionIconClasses = new Set([ - Codicon.mic, Codicon.micFilled, Codicon.micDownloadCompact, + Codicon.micCompact, Codicon.micFilled, Codicon.micDownloadCompact, Codicon.voiceModeCompact, Codicon.loadingCompact, Codicon.debugDisconnectCompact, ].map(icon => ThemeIcon.asClassName(icon))); const updateVoiceInputActionBorder = () => { diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts index b546c6599a2f22..702f69c59e51f1 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts @@ -735,7 +735,7 @@ class StartParameterizedPromptAction extends Action2 { after: { content: ' ', inlineClassNameAffectsLetterSpacing: true, - inlineClassName: ThemeIcon.asClassName(ThemeIcon.modify(Codicon.loading, 'spin')) + ' chat-prompt-spinner', + inlineClassName: ThemeIcon.asClassName(ThemeIcon.modify(Codicon.loadingCompact, 'spin')) + ' chat-prompt-spinner', } }); store.add(toDisposable(() => { 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 a20bd6748bb53e..7b800cc5ba5e1c 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modePickerActionItem.ts @@ -26,6 +26,7 @@ import { IProductService } from '../../../../../../platform/product/common/produ import { ITelemetryService } from '../../../../../../platform/telemetry/common/telemetry.js'; import { IChatAgentService } from '../../../common/participants/chatAgents.js'; import { ChatMode, IChatMode, IChatModes } from '../../../common/chatModes.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import { isOrganizationPromptFile } from '../../../common/promptSyntax/utils/promptsServiceUtils.js'; import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../../../common/constants.js'; import { PromptsStorage } from '../../../common/promptSyntax/service/promptsService.js'; @@ -307,7 +308,7 @@ export class ModePickerActionItem extends ChatInputPickerActionViewItem { const labelElements = []; const collapsed = this.pickerOptions.compact.get(); if (icon) { - labelElements.push(...renderLabelWithIcons(`$(${icon.id})`)); + labelElements.push(...renderLabelWithIcons(`$(${getCompactCodicon(icon).id})`)); } if (!collapsed || !icon) { labelElements.push(dom.$('span.chat-input-picker-label', undefined, state)); diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/media/modelPicker.css b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/media/modelPicker.css index 703060386aeee6..3694daa745f491 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/media/modelPicker.css +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/media/modelPicker.css @@ -59,7 +59,7 @@ } .chat-input-picker-item .action-label.model-picker-split .model-picker-section .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } .chat-input-picker-item .action-label.model-picker-split .model-picker-section .codicon + .chat-input-picker-label { @@ -150,6 +150,12 @@ padding: 6px 8px; } +.chat-model-hover-warning-text > .codicon, +.chat-model-hover-info-text > .codicon, +.chat-model-hover-promo-text > .codicon { + font-size: var(--vscode-codiconFontSize-compact); +} + .chat-model-hover-warning-text > .codicon { flex-shrink: 0; color: var(--vscode-notificationsWarningIcon-foreground); diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerHover.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerHover.ts index 21a1543265f5a2..9ff3cf7c2fba7a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerHover.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerHover.ts @@ -65,7 +65,7 @@ export function getModelHoverContent( if (!isAuto && model.metadata.warningText) { for (const message of Object.values(model.metadata.warningText)) { - container.appendChild(createMessageBanner(message, 'chat-model-hover-warning-text', Codicon.warning, disposables, openerService)); + container.appendChild(createMessageBanner(message, 'chat-model-hover-warning-text', Codicon.warningCompact, disposables, openerService)); } } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerWidget.ts index ac63192dee58f9..9c12fb6f590837 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelPickerWidget.ts @@ -39,10 +39,11 @@ import { GitHubPaths, IDefaultAccountService } from '../../../../../../../platfo import { IUpdateService } from '../../../../../../../platform/update/common/update.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from '../../../../../../../platform/workspace/common/workspaceTrust.js'; +import { getCompactCodicon } from '../../../chatIcons.js'; import { withChatInputPickerMotion } from '../chatInputPickerActionItem.js'; import { buildModelPickerItems, createManageModelsAction, getModelPickerAccessibilityProvider, getModelPickerControlModels, ModelPickerSection, shouldShowManageModelsAction } from './modelPickerItems.js'; import { ModelPickerConfiguration } from './modelPickerConfiguration.js'; -import { getModelPickerIcon } from './modelProviderIcons.js'; +import { getCompactModelPickerIcon } from './modelProviderIcons.js'; import { getModelPickerUnavailableReason, isAutoModel, ModelPickerUnavailableReason, modelPickerRequiresSetup, shouldShowCacheBreakHint as computeShouldShowCacheBreakHint } from './modelPickerPresentation.js'; const CACHE_BREAK_HINT_DISMISSED_STORAGE_KEY = 'chat.cacheBreakHintDismissed'; @@ -560,7 +561,7 @@ export class ModelPickerWidget extends Disposable { private _updateBadge(): void { if (this._badgeIcon) { if (this._badge) { - const icon = this._badge === 'info' ? Codicon.info : Codicon.warning; + const icon = this._badge === 'info' ? Codicon.info : Codicon.warningCompact; dom.reset(this._badgeIcon, renderIcon(icon)); this._badgeIcon.style.display = ''; this._badgeIcon.classList.toggle('info', this._badge === 'info'); @@ -586,7 +587,9 @@ export class ModelPickerWidget extends Disposable { // --- Name section --- const nameChildren: (HTMLElement | string)[] = []; const modelIcon = this._selectedModel - ? (this._selectedModel.metadata.statusIcon ?? (this._delegate.getPresentationOptions().showModelIcon ? getModelPickerIcon(this._selectedModel) : undefined)) + ? (this._delegate.getPresentationOptions().showModelIcon + ? getCompactModelPickerIcon(this._selectedModel) + : this._selectedModel.metadata.statusIcon ? getCompactCodicon(this._selectedModel.metadata.statusIcon) : undefined) : undefined; const compact = this._compact?.get() ?? false; if (modelIcon && !noModelsAvailable) { diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelProviderIcons.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelProviderIcons.ts index 4a8624bcc30b6f..b252cbfd854db6 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelProviderIcons.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPicker/modelProviderIcons.ts @@ -8,6 +8,7 @@ import { ThemeIcon } from '../../../../../../../base/common/themables.js'; import { localize } from '../../../../../../../nls.js'; import { registerIcon } from '../../../../../../../platform/theme/common/iconRegistry.js'; import { ILanguageModelChatMetadataAndIdentifier, isAutoLanguageModel } from '../../../../common/languageModels.js'; +import { getCompactCodicon } from '../../../chatIcons.js'; const copilotModelProviderIcon = registerIcon('chat-model-provider-copilot', Codicon.copilotCompact, localize('chatModelProviderCopilotIcon', "Icon for Copilot models.")); const openAIModelProviderIcon = registerIcon('chat-model-provider-openai', Codicon.openai, localize('chatModelProviderOpenAIIcon', "Icon for OpenAI models.")); @@ -17,6 +18,7 @@ const kimiModelProviderIcon = registerIcon('chat-model-provider-kimi', Codicon.k const microsoftModelProviderIcon = registerIcon('chat-model-provider-microsoft', Codicon.microsoft, localize('chatModelProviderMicrosoftIcon', "Icon for Microsoft models.")); const xAIModelProviderIcon = registerIcon('chat-model-provider-xai', Codicon.xai, localize('chatModelProviderXAIIcon', "Icon for xAI models.")); const genericModelProviderIcon = registerIcon('chat-model-provider-generic', Codicon.sparkle, localize('chatModelProviderGenericIcon', "Icon for other model providers.")); +const genericModelProviderCompactIcon = registerIcon('chat-model-provider-generic-compact', Codicon.sparkleCompact, localize('chatModelProviderGenericCompactIcon', "Compact icon for other model providers.")); export function getModelProviderIcon(model: ILanguageModelChatMetadataAndIdentifier): ThemeIcon { const identity = `${model.metadata.vendor} ${model.metadata.family} ${model.metadata.id} ${model.metadata.name}`.toLowerCase(); @@ -54,3 +56,8 @@ export function getModelProviderIcon(model: ILanguageModelChatMetadataAndIdentif export function getModelPickerIcon(model: ILanguageModelChatMetadataAndIdentifier): ThemeIcon { return model.metadata.statusIcon ?? getModelProviderIcon(model); } + +export function getCompactModelPickerIcon(model: ILanguageModelChatMetadataAndIdentifier): ThemeIcon { + const icon = getModelPickerIcon(model); + return icon.id === genericModelProviderIcon.id ? genericModelProviderCompactIcon : getCompactCodicon(icon); +} diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts index 66b4387ac7a6b5..0972e388f3af25 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts @@ -29,6 +29,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { IStorageService } from '../../../../../../platform/storage/common/storage.js'; import { maybeConfirmElevatedPermissionLevel } from '../../../common/chatPermissionWarnings.js'; import { AgentSandboxEnabledSettingValue, AgentSandboxEnabledValue, AgentSandboxSettingId, isAgentSandboxEnabledValue } from '../../../../../../platform/sandbox/common/settings.js'; +import { getCompactCodicon } from '../../chatIcons.js'; export interface IExtensionPermissionState { /** Stable identifier for the contributing chat session type, used to namespace action ids. */ @@ -362,7 +363,7 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { } const labelElements = []; - labelElements.push(...renderLabelWithIcons(`$(${icon.id})`)); + labelElements.push(...renderLabelWithIcons(`$(${getCompactCodicon(icon).id})`)); labelElements.push(dom.$('span.chat-input-picker-label', undefined, label)); dom.reset(element, ...labelElements); 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 d97be275daa357..a0e02c981fcc26 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/sessionTargetPickerActionItem.ts @@ -23,6 +23,7 @@ import { IKeybindingService } from '../../../../../../platform/keybinding/common import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; import { IStorageService } from '../../../../../../platform/storage/common/storage.js'; import { ITelemetryService } from '../../../../../../platform/telemetry/common/telemetry.js'; +import { getCompactCodicon } from '../../chatIcons.js'; import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js'; import { IAgentHostEnablementService } from '../../../../../../platform/agentHost/common/agentHostEnablementService.js'; import { AgentHostAllowSignedOutWhenUsableSettingId } from '../../../../../../platform/agentHost/common/agentService.js'; @@ -369,7 +370,7 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem { const icon = this._getSessionIcon({ type: currentType, label, hoverDescription: '', commandId: '' }); const labelElements = []; - labelElements.push(...renderLabelWithIcons(`$(${icon.id})`)); + labelElements.push(...renderLabelWithIcons(`$(${getCompactCodicon(icon).id})`)); labelElements.push(dom.$('span.chat-input-picker-label', undefined, label)); 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 c17af8ce736293..812b0bd282c609 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/workspacePickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/workspacePickerActionItem.ts @@ -111,10 +111,10 @@ export class WorkspacePickerActionItem extends ChatInputPickerActionViewItem { if (currentWorkspace) { // Show the workspace label or folder name const label = currentWorkspace.label || basename(currentWorkspace.uri); - labelElements.push(...renderLabelWithIcons(`$(folder)`)); + labelElements.push(...renderLabelWithIcons(`$(folder-compact)`)); labelElements.push(dom.$('span.chat-input-picker-label', undefined, label)); } else { - labelElements.push(...renderLabelWithIcons(`$(folder)`)); + labelElements.push(...renderLabelWithIcons(`$(folder-compact)`)); labelElements.push(dom.$('span.chat-input-picker-label', undefined, localize('selectWorkspace', "Workspace"))); } 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 7a7b0635da72c6..ef10856040784e 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/widget/media/chat.css @@ -979,7 +979,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-item-container.interactive-item-compact .header .codicon-avatar .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } .interactive-item-container.interactive-item-compact .header .avatar + .avatar { @@ -1074,7 +1074,7 @@ have to be updated for changes to the rules above, or to support more deeply nes .chat-dnd-overlay .attach-context-overlay-text .codicon { height: 12px; - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); margin-right: 3px; } @@ -1853,7 +1853,7 @@ have to be updated for changes to the rules above, or to support more deeply nes .interactive-session .chat-editor-container .monaco-editor .chat-prompt-spinner { transform-origin: 6px 6px; - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } .interactive-session .interactive-input-part .chat-editor-container .interactive-input-editor .monaco-editor, @@ -1935,7 +1935,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } } @@ -1988,7 +1988,7 @@ have to be updated for changes to the rules above, or to support more deeply nes position: absolute; bottom: 0; right: 0; - font-size: 12px !important; + font-size: var(--vscode-codiconFontSize-compact) !important; color: var(--vscode-problemsWarningIcon-foreground); background: var(--vscode-input-background); width: fit-content; @@ -2026,13 +2026,13 @@ have to be updated for changes to the rules above, or to support more deeply nes align-items: center; margin-left: 4px; flex-shrink: 0; - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); .codicon.codicon-info { color: var(--vscode-problemsInfoIcon-foreground) !important; } - .codicon.codicon-warning { + .codicon.codicon-warning-compact { color: var(--vscode-problemsWarningIcon-foreground) !important; } } @@ -2042,7 +2042,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } } @@ -2371,7 +2371,7 @@ have to be updated for changes to the rules above, or to support more deeply nes position: absolute; bottom: 0; left: 12px; - font-size: 12px !important; + font-size: var(--vscode-codiconFontSize-compact) !important; background: var(--vscode-input-background); width: fit-content; height: fit-content; @@ -2574,7 +2574,11 @@ have to be updated for changes to the rules above, or to support more deeply nes font-size: 11px; } -.interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups .monaco-button .codicon-sparkle { +.interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups .monaco-button .codicon { + font-size: var(--vscode-codiconFontSize-compact); +} + +.interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups .monaco-button .codicon-sparkle-compact { float: left; } @@ -3387,12 +3391,12 @@ have to be updated for changes to the rules above, or to support more deeply nes .interactive-session .chat-file-changes-label .monaco-button .codicon, .interactive-session .chat-used-context-label .monaco-button .codicon { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); color: var(--vscode-icon-foreground) !important; } /* Hide the leading icon on collapsible parts by default; show-checkmarks restores it */ -.interactive-session .chat-used-context-label .monaco-icon-button > .codicon:first-child:not(.chat-collapsible-hover-chevron):not(.codicon-error):not(.codicon-warning) { +.interactive-session .chat-used-context-label .monaco-icon-button > .codicon:first-child:not(.chat-collapsible-hover-chevron):not(.codicon-error-compact):not(.codicon-warning-compact) { display: none; } @@ -3402,7 +3406,7 @@ have to be updated for changes to the rules above, or to support more deeply nes /* Hover chevron indicator for collapsible parts */ .chat-collapsible-hover-chevron { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); opacity: 0; transform: rotate(0deg); transform-origin: center; @@ -3444,14 +3448,14 @@ have to be updated for changes to the rules above, or to support more deeply nes padding-top: 2px; > .codicon[class*='codicon-'] { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); &::before { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); } } - > .codicon.codicon-check { + > .codicon.codicon-check-compact { display: none; } @@ -3461,7 +3465,7 @@ have to be updated for changes to the rules above, or to support more deeply nes /* Matching the margin on all .monaco-text-button .codicon */ margin: 0 .2em; - &.codicon-error { + &.codicon-error-compact { color: var(--vscode-editorError-foreground) !important; } } @@ -3519,8 +3523,8 @@ have to be updated for changes to the rules above, or to support more deeply nes color: var(--vscode-descriptionForeground); } -.show-checkmarks .progress-container > .codicon.codicon-check, -.progress-container.show-checkmarks > .codicon.codicon-check { +.show-checkmarks .progress-container > .codicon.codicon-check-compact, +.progress-container.show-checkmarks > .codicon.codicon-check-compact { display: inline-flex; margin-left: 4px; } @@ -3765,7 +3769,7 @@ have to be updated for changes to the rules above, or to support more deeply nes .chat-attached-context-attachment .chat-attached-context-pill { - font-size: 12px; + font-size: var(--vscode-codiconFontSize-compact); display: inline-flex; align-items: center; padding: 2px 0 2px 0px; @@ -3775,9 +3779,10 @@ have to be updated for changes to the rules above, or to support more deeply nes outline: none; border: none; - .codicon.codicon-file-media, - .codicon.codicon-warning { - font-size: 12px; + .codicon.codicon-file-media-compact, + .codicon.codicon-warning, + .codicon.codicon-warning-compact { + font-size: var(--vscode-codiconFontSize-compact); margin-right: 2px; } } @@ -4349,7 +4354,12 @@ have to be updated for changes to the rules above, or to support more deeply nes margin-right: 5px; } - .monaco-toolbar .action-label.codicon.codicon-repo-forked { + .monaco-toolbar .action-label.codicon, + .monaco-toolbar .action-label .codicon { + font-size: var(--vscode-codiconFontSize-compact); + } + + .monaco-toolbar .action-label:has(> .chat-fork-action-icon) { width: fit-content; padding: 2px 5px; } @@ -4358,13 +4368,13 @@ have to be updated for changes to the rules above, or to support more deeply nes margin-right: 0; } - .monaco-toolbar .actions-container > .action-item:not(:first-child):has(.action-label.codicon.codicon-repo-forked) { + .monaco-toolbar .actions-container > .action-item:not(:first-child):has(.chat-fork-action-icon) { display: flex; align-items: center; gap: 4px; } - .monaco-toolbar .actions-container > .action-item:not(:first-child):has(.action-label.codicon.codicon-repo-forked)::before { + .monaco-toolbar .actions-container > .action-item:not(:first-child):has(.chat-fork-action-icon)::before { content: '\00B7'; font-size: 12px; line-height: 18px; @@ -4650,8 +4660,8 @@ have to be updated for changes to the rules above, or to support more deeply nes } /* Chevron icon in dropdown container */ -.chat-welcome-view-suggested-prompt .codicon-chevron-down.dropdown-chevron { - font-size: 12px; +.chat-welcome-view-suggested-prompt .codicon-chevron-down-compact.dropdown-chevron { + font-size: var(--vscode-codiconFontSize-compact); opacity: 0.7; flex-shrink: 0; } @@ -4779,7 +4789,7 @@ have to be updated for changes to the rules above, or to support more deeply nes When both modes are available, their outlines identify them as a paired control. A lone action is left borderless to match the send button. */ -.interactive-session .chat-input-toolbars > .chat-execute-toolbar .monaco-action-bar .action-item > .action-label.codicon-mic, +.interactive-session .chat-input-toolbars > .chat-execute-toolbar .monaco-action-bar .action-item > .action-label.codicon-mic-compact, .interactive-session .chat-input-toolbars > .chat-execute-toolbar .monaco-action-bar .action-item > .action-label.codicon-mic-filled, .interactive-session .chat-input-toolbars > .chat-execute-toolbar .monaco-action-bar .action-item > .action-label.codicon-mic-download-compact, .interactive-session .chat-input-toolbars > .chat-execute-toolbar .monaco-action-bar .action-item > .action-label.codicon-voice-mode-compact, @@ -4793,7 +4803,7 @@ have to be updated for changes to the rules above, or to support more deeply nes font-size: var(--vscode-codiconFontSize-compact); } -.chat-execute-toolbar.chat-voice-input-actions-multiple .monaco-action-bar .action-item > .action-label.codicon-mic, +.chat-execute-toolbar.chat-voice-input-actions-multiple .monaco-action-bar .action-item > .action-label.codicon-mic-compact, .chat-execute-toolbar.chat-voice-input-actions-multiple .monaco-action-bar .action-item > .action-label.codicon-mic-filled, .chat-execute-toolbar.chat-voice-input-actions-multiple .monaco-action-bar .action-item > .action-label.codicon-mic-download-compact, .chat-execute-toolbar.chat-voice-input-actions-multiple .monaco-action-bar .action-item > .action-label.codicon-voice-mode-compact, diff --git a/src/vs/workbench/contrib/chat/test/browser/chatIcons.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatIcons.test.ts new file mode 100644 index 00000000000000..c5ea08de3606c5 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/chatIcons.test.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { mainWindow } from '../../../../../base/browser/window.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; +import { ThemeIcon } from '../../../../../base/common/themables.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { compactCodiconsIn, getCompactCodicon } from '../../browser/chatIcons.js'; + +suite('ChatIcons', () => { + + ensureNoDisposablesAreLeakedInTestSuite(); + + test('uses compact variants when registered', () => { + assert.deepStrictEqual([ + getCompactCodicon(Codicon.warning).id, + getCompactCodicon(ThemeIcon.modify(Codicon.loading, 'spin')).id, + getCompactCodicon(Codicon.info).id, + ], [ + Codicon.warningCompact.id, + ThemeIcon.modify(Codicon.loadingCompact, 'spin').id, + Codicon.info.id, + ]); + }); + + test('updates rendered codicons', () => { + const element = mainWindow.document.createElement('div'); + element.innerHTML = ''; + + compactCodiconsIn(element); + + assert.deepStrictEqual([...element.children].map(child => child.className), [ + 'codicon codicon-check-compact', + 'codicon codicon-info', + ]); + }); +}); diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatQuestionCarouselPart.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatQuestionCarouselPart.test.ts index 9120660316182a..63b1b2e730febc 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatQuestionCarouselPart.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatQuestionCarouselPart.test.ts @@ -973,7 +973,7 @@ suite('ChatQuestionCarouselPart', () => { questionExpandable: question.hasAttribute('aria-expanded'), answer: answerButton.textContent, answerExpanded: answerButton.getAttribute('aria-expanded'), - answerIcon: answerButton.querySelector('.chat-question-summary-answer-icon')?.classList.contains('codicon-comment'), + answerIcon: answerButton.querySelector('.chat-question-summary-answer-icon')?.classList.contains('codicon-comment-compact'), hasChevron: !!answerButton.querySelector('.chat-collapsible-hover-chevron'), optionsTitle: widget.domNode.querySelector('.chat-question-summary-options-title')?.textContent, options: Array.from(widget.domNode.querySelectorAll('.chat-question-summary-option')).map(option => ({ diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatRichLink.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatRichLink.test.ts index 62d93cf121fd6b..66e3596c5c6e30 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatRichLink.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatRichLink.test.ts @@ -73,8 +73,8 @@ suite('ChatRichLink', () => { }, { pending: { status: 'pending', spinner: true }, warning: { status: 'warning', spinner: true }, - error: { status: 'error', icon: 'chat-rich-link-status-icon codicon codicon-error' }, - completed: { status: 'success', icon: 'chat-rich-link-status-icon codicon codicon-pass-filled' }, + error: { status: 'error', icon: 'chat-rich-link-status-icon codicon codicon-error-compact' }, + completed: { status: 'success', icon: 'chat-rich-link-status-icon codicon codicon-pass-filled-compact' }, }); }); }); diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSubagentContentPart.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSubagentContentPart.test.ts index e0c31fb741d0f9..2516ec5ba50dfe 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSubagentContentPart.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSubagentContentPart.test.ts @@ -567,7 +567,7 @@ suite('ChatSubagentContentPart', () => { assert.deepStrictEqual({ hidden: activity?.classList.contains('hidden'), label: activity?.querySelector('.chat-subagent-pill-active-tool-label')?.textContent, - hasWorkingIcon: activity?.querySelector('.chat-subagent-pill-active-tool-icon')?.classList.contains('codicon-comment'), + hasWorkingIcon: activity?.querySelector('.chat-subagent-pill-active-tool-icon')?.classList.contains('codicon-comment-compact'), ariaLabel: container.getAttribute('aria-label'), }, { hidden: false, @@ -1593,7 +1593,7 @@ suite('ChatSubagentContentPart', () => { const button = getCollapseButton(part); assert.ok(button, 'Should have collapse button'); const iconElement = getCollapseButtonIcon(button); - assert.ok(iconElement?.classList.contains('codicon-check'), 'Should have check icon after finalization'); + assert.ok(iconElement?.classList.contains('codicon-check-compact'), 'Should have check icon after finalization'); }); }); @@ -1696,7 +1696,7 @@ suite('ChatSubagentContentPart', () => { const button = getCollapseButton(part); assert.ok(button, 'Should have collapse button'); const loadingIcon = getCollapseButtonIcon(button); - assert.ok(loadingIcon?.classList.contains('codicon-circle-filled'), 'Should have circle-filled icon while streaming'); + assert.ok(loadingIcon?.classList.contains('codicon-circle-filled-compact'), 'Should have circle-filled icon while streaming'); }); }); diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSystemNotificationContentPart.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSystemNotificationContentPart.test.ts index 54d20bc00bed23..7e3a625e750e29 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSystemNotificationContentPart.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatSystemNotificationContentPart.test.ts @@ -34,7 +34,7 @@ suite('ChatSystemNotificationContentPart', () => { assert.deepStrictEqual({ text: part.domNode.textContent, - hasCheck: !!part.domNode.querySelector('.codicon-check'), + hasCheck: !!part.domNode.querySelector('.codicon-check-compact'), sameContent: part.hasSameContent({ kind: 'systemNotification', content: new MarkdownString('Background command completed') }), differentContent: part.hasSameContent({ kind: 'systemNotification', content: new MarkdownString('Different') }), }, { diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatThinkingContentPart.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatThinkingContentPart.test.ts index 7cdba09a702e4c..29aa69215e777c 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatThinkingContentPart.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatThinkingContentPart.test.ts @@ -1378,7 +1378,7 @@ suite('ChatThinkingContentPart', () => { part.finalizeTitleIfDefault(); // The button should now show a check icon - const iconElement = part.domNode.querySelector('.codicon-check'); + const iconElement = part.domNode.querySelector('.codicon-check-compact'); assert.ok(iconElement, 'Should have check icon after finalization'); assert.ok(part.domNode.classList.contains('chat-collapsible-content-animated'), 'Should enable content animation after finalization'); }); @@ -1662,7 +1662,7 @@ suite('ChatThinkingContentPart', () => { disposables.add(toDisposable(() => part.domNode.remove())); // Should have circle-filled icon (not loading spinner) while streaming - const circleIcon = part.domNode.querySelector('.codicon-circle-filled'); + const circleIcon = part.domNode.querySelector('.codicon-circle-filled-compact'); assert.ok(circleIcon, 'Should have circle-filled icon while streaming'); }); @@ -2207,7 +2207,7 @@ suite('ChatThinkingContentPart', () => { childClasses: [ 'monaco-button monaco-icon-button monaco-text-button chat-thinking-title-with-diff', 'monaco-button chat-thinking-title-diff', - 'chat-collapsible-hover-chevron codicon codicon-chevron-right expanded', + 'chat-collapsible-hover-chevron codicon codicon-chevron-right-compact expanded', ], initialExpanded: 'false', expandedAfterChevronClick: 'true', diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatToolProgressPart.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatToolProgressPart.test.ts index 0975bb3541d030..16a11dea71f973 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatToolProgressPart.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatToolProgressPart.test.ts @@ -457,7 +457,7 @@ suite('ChatToolProgressSubPart', () => { const shimmerText = part.domNode.querySelector('.chat-progress-shimmer-text'); return { shimmer: !!part.domNode.querySelector('.shimmer-progress'), - spinner: !!part.domNode.querySelector('.codicon-loading'), + spinner: !!part.domNode.querySelector('.codicon-loading, .codicon-loading-compact'), shimmerText: shimmerText?.textContent, // A negative animation-delay keeps the sweep continuous across streaming rerenders. shimmerPhaseSynced: (shimmerText?.style.animationDelay ?? '').endsWith('ms'), @@ -545,7 +545,7 @@ suite('ChatToolProgressSubPart', () => { new Set() )); - assert.strictEqual(part.domNode.querySelector('.codicon-loading'), null); + assert.strictEqual(part.domNode.querySelector('.codicon-loading, .codicon-loading-compact'), null); }); test('does not add shimmer styling for non-MCP tool progress', () => { diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts index 04b0d61c578899..422128cfc7a9dc 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts @@ -39,8 +39,8 @@ suite('ChatForkActionViewItem', () => { }); viewItem.actionRunner = actionRunner; - const forkIconClass = `codicon-${Codicon.repoForked.id}`; - const loadingIconClass = `codicon-${Codicon.loading.id}`; + const forkIconClass = `codicon-${Codicon.repoForkedCompact.id}`; + const loadingIconClass = `codicon-${Codicon.loadingCompact.id}`; const runPromise = actionRunner.run(action); const label = container.querySelector('.action-label'); const icon = label?.querySelector('.chat-fork-action-icon'); diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/input/modelPicker/modelProviderIcons.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/input/modelPicker/modelProviderIcons.test.ts index a27c31150069b8..158bcb1911e67e 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/input/modelPicker/modelProviderIcons.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/input/modelPicker/modelProviderIcons.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { Codicon } from '../../../../../../../../base/common/codicons.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../../base/test/common/utils.js'; -import { getModelPickerIcon, getModelProviderIcon } from '../../../../../browser/widget/input/modelPicker/modelProviderIcons.js'; +import { getCompactModelPickerIcon, getModelPickerIcon, getModelProviderIcon } from '../../../../../browser/widget/input/modelPicker/modelProviderIcons.js'; import { ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier } from '../../../../../common/languageModels.js'; function createModel(id: string, name: string, vendor = 'copilot', metadata?: Partial): ILanguageModelChatMetadataAndIdentifier { @@ -82,4 +82,20 @@ suite('ModelProviderIcons', () => { getModelProviderIcon(model).id, ]); }); + + test('uses compact variants in the picker trigger', () => { + const genericModel = createModel('custom', 'Custom Model', 'third-party'); + const modelWithWarning = createModel('custom', 'Custom Model', 'third-party', { statusIcon: Codicon.warning }); + const modelWithInfo = createModel('custom', 'Custom Model', 'third-party', { statusIcon: Codicon.info }); + + assert.deepStrictEqual([ + getCompactModelPickerIcon(genericModel).id, + getCompactModelPickerIcon(modelWithWarning).id, + getCompactModelPickerIcon(modelWithInfo).id, + ], [ + 'chat-model-provider-generic-compact', + Codicon.warningCompact.id, + Codicon.info.id, + ]); + }); }); diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts index 8cd082603e5f66..8fc21fa734ea33 100644 --- a/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts @@ -41,7 +41,6 @@ import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSepara import { ISecretStorageService } from '../../../../platform/secrets/common/secrets.js'; import { StorageScope } from '../../../../platform/storage/common/storage.js'; import { defaultCheckboxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { spinningLoading } from '../../../../platform/theme/common/iconRegistry.js'; import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from '../../../browser/actions/workspaceCommands.js'; import { ActiveEditorContext, RemoteNameContext, ResourceContextKey, WorkbenchStateContext, WorkspaceFolderCountContext } from '../../../common/contextkeys.js'; @@ -955,13 +954,13 @@ export class MCPServerActionRendering extends Disposable implements IWorkbenchCo stateIndicator.className = 'chat-mcp-state-indicator'; if (state === DisplayedState.NewTools) { stateIndicator.style.display = 'block'; - stateIndicator.classList.add('chat-mcp-state-new', ...ThemeIcon.asClassNameArray(Codicon.refresh)); + stateIndicator.classList.add('chat-mcp-state-new', ...ThemeIcon.asClassNameArray(Codicon.refreshCompact)); } else if (state === DisplayedState.Error) { stateIndicator.style.display = 'block'; - stateIndicator.classList.add('chat-mcp-state-error', ...ThemeIcon.asClassNameArray(Codicon.warning)); + stateIndicator.classList.add('chat-mcp-state-error', ...ThemeIcon.asClassNameArray(Codicon.warningCompact)); } else if (state === DisplayedState.Refreshing) { stateIndicator.style.display = 'block'; - stateIndicator.classList.add('chat-mcp-state-refreshing', ...ThemeIcon.asClassNameArray(spinningLoading)); + stateIndicator.classList.add('chat-mcp-state-refreshing', ...ThemeIcon.asClassNameArray(ThemeIcon.modify(Codicon.loadingCompact, 'spin'))); } else { stateIndicator.style.display = 'none'; } From 2d8595a3d6330c46d860697b679ab545b1e23e77 Mon Sep 17 00:00:00 2001 From: mrleemurray Date: Thu, 20 Aug 2026 11:24:01 +0100 Subject: [PATCH 2/3] fix(chat): address PR review feedback on compact codicons Replace the :has(.chat-fork-action-icon) selectors with stable chat-fork-action-item/chat-fork-action-label classes applied during render, avoiding descendant-dependent style invalidation in a workbench-wide stylesheet (microsoft/vscode#324985). Render the restore-confirmation cancel affordance with the close-compact glyph so it is optically correct at the compact tier instead of being a regular glyph scaled down by the toolbar sizing rule. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../contrib/chat/browser/widget/chatForkActionViewItem.ts | 5 +++++ .../browser/widget/chatRestoreCheckpointActionViewItem.ts | 2 +- .../workbench/contrib/chat/browser/widget/media/chat.css | 6 +++--- .../test/browser/widget/chatForkActionViewItem.test.ts | 8 ++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts b/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts index d8f436eb6bbadf..f4bb37500eb262 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatForkActionViewItem.ts @@ -33,7 +33,12 @@ export class ChatForkActionViewItem extends CodiconActionViewItem { override render(container: HTMLElement): void { super.render(container); + // Stable classes so the stylesheet can target this item directly instead of + // with :has(), which would add descendant-dependent style invalidation to a + // workbench-wide stylesheet (microsoft/vscode#324985). + container.classList.add('chat-fork-action-item'); if (this.label) { + this.label.classList.add('chat-fork-action-label'); this.label.textContent = ''; this.icon = document.createElement('span'); this.icon.classList.add('chat-fork-action-icon'); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatRestoreCheckpointActionViewItem.ts b/src/vs/workbench/contrib/chat/browser/widget/chatRestoreCheckpointActionViewItem.ts index f36f0546f84c6c..9571c21364f54c 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatRestoreCheckpointActionViewItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatRestoreCheckpointActionViewItem.ts @@ -57,7 +57,7 @@ export class ChatRestoreCheckpointActionViewItem extends MenuEntryActionViewItem // the ActionBar roving-tabindex pattern; keyboard users can use Escape to // cancel the inline confirmation. cancelButton.tabIndex = -1; - dom.reset(cancelButton, ...renderLabelWithIcons(`$(close)`)); + dom.reset(cancelButton, ...renderLabelWithIcons(`$(close-compact)`)); cancelButton.title = this._cancelLabel; cancelButton.setAttribute('aria-label', this._cancelLabel); this._register(dom.addDisposableListener(cancelButton, dom.EventType.CLICK, e => { 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 472e29b9c83121..341ebf5e6b58ea 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/widget/media/chat.css @@ -4386,7 +4386,7 @@ have to be updated for changes to the rules above, or to support more deeply nes font-size: var(--vscode-codiconFontSize-compact); } - .monaco-toolbar .action-label:has(> .chat-fork-action-icon) { + .monaco-toolbar .action-label.chat-fork-action-label { width: fit-content; padding: 2px 5px; } @@ -4395,13 +4395,13 @@ have to be updated for changes to the rules above, or to support more deeply nes margin-right: 0; } - .monaco-toolbar .actions-container > .action-item:not(:first-child):has(.chat-fork-action-icon) { + .monaco-toolbar .actions-container > .action-item.chat-fork-action-item:not(:first-child) { display: flex; align-items: center; gap: 4px; } - .monaco-toolbar .actions-container > .action-item:not(:first-child):has(.chat-fork-action-icon)::before { + .monaco-toolbar .actions-container > .action-item.chat-fork-action-item:not(:first-child)::before { content: '\00B7'; font-size: 12px; line-height: 18px; diff --git a/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts b/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts index 422128cfc7a9dc..06ce3bcdf075d4 100644 --- a/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/widget/chatForkActionViewItem.test.ts @@ -56,6 +56,8 @@ suite('ChatForkActionViewItem', () => { iconSpinning: icon.classList.contains('codicon-modifier-spin'), busy: label.getAttribute('aria-busy'), label: label.getAttribute('aria-label'), + itemClass: container.classList.contains('chat-fork-action-item'), + labelClass: label.classList.contains('chat-fork-action-label'), }, }, { during: { @@ -66,6 +68,8 @@ suite('ChatForkActionViewItem', () => { iconSpinning: true, busy: 'true', label: 'Forking conversation', + itemClass: true, + labelClass: true, }, }); @@ -80,6 +84,8 @@ suite('ChatForkActionViewItem', () => { iconSpinning: icon.classList.contains('codicon-modifier-spin'), busy: label.getAttribute('aria-busy'), label: label.getAttribute('aria-label'), + itemClass: container.classList.contains('chat-fork-action-item'), + labelClass: label.classList.contains('chat-fork-action-label'), }, { buttonCodicon: true, buttonSpinning: false, @@ -88,6 +94,8 @@ suite('ChatForkActionViewItem', () => { iconSpinning: false, busy: 'false', label: 'Fork conversation from this point', + itemClass: true, + labelClass: true, }); }); }); From c8a4f45fe8130290b8fbadc4211522f939ccae16 Mon Sep 17 00:00:00 2001 From: mrleemurray Date: Thu, 20 Aug 2026 11:38:51 +0100 Subject: [PATCH 3/3] fix(screenshots): update user data migration screenshots for accuracy --- test/componentFixtures/blocks-ci-screenshots.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/componentFixtures/blocks-ci-screenshots.md b/test/componentFixtures/blocks-ci-screenshots.md index c1ad1d7ae4d9e1..38a0b845ad0954 100644 --- a/test/componentFixtures/blocks-ci-screenshots.md +++ b/test/componentFixtures/blocks-ci-screenshots.md @@ -7,10 +7,10 @@ ![screenshot](https://hediet-screenshots.azurewebsites.net/images/5902ba6014c618379aa053bb3893b9763e673bea94aa13af222b07e3ee0807d0) #### chat/aiCustomizations/aiCustomizationManagementEditor/UserDataMigration/Dark -![screenshot](https://hediet-screenshots.azurewebsites.net/images/9d088c26a5039a8d33db61aee6d2f4df8c62000a7d48fd9dd6a3e4d05146add6) +![screenshot](https://hediet-screenshots.azurewebsites.net/images/c0f6964579d22dc38e9701a837f8d45fe06692d563cdd44818e257c5f98cf38a) #### chat/aiCustomizations/aiCustomizationManagementEditor/UserDataMigration/Light -![screenshot](https://hediet-screenshots.azurewebsites.net/images/b2c09ec89048a30ced6b193bea98d2e88455c46c89383853405936abbafe8ee0) +![screenshot](https://hediet-screenshots.azurewebsites.net/images/17f7907ede552371d2164be2b4f346496890646e966eac2f7eef50c9c81b5f9f) #### editor/codeEditor/CodeEditor/Dark ![screenshot](https://hediet-screenshots.azurewebsites.net/images/09075b2f4715fa8a8ad426165bb85ba96a15b7174259c7da7ef0c2d5e74f7f79)