Skip to content

chat: circular send button, ringed standalone mic / Voice Mode buttons - #328137

Merged
eli-w-king merged 7 commits into
microsoft:mainfrom
eli-w-king:eli/agents/button-style-update-circle-outline
Jul 31, 2026
Merged

chat: circular send button, ringed standalone mic / Voice Mode buttons#328137
eli-w-king merged 7 commits into
microsoft:mainfrom
eli-w-king:eli/agents/button-style-update-circle-outline

Conversation

@eli-w-king

@eli-w-king eli-w-king commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Fixes: #328453

Makes the chat composer's execute toolbar read as one uniform set of 22px circular controls.

Before After
Send rounded square circle
Dictation / Voice Mode (standalone) bare glyph ringed circle
Configure Tools 16px compact 12px

Send button

Now a circle in its idle and enabled states and in its focus ring, so the shape never changes with state.

Standalone dictation / Voice Mode buttons

When the segmented voice pill isn't active these render on their own and lose its enclosure, so each now carries a circular outline of its own.

The ring is drawn in currentColor so it always matches the glyph it encircles — the resting icon foreground, and the same blue / purple / green the glyph takes while Voice Mode is listening, speaking, or offering to disconnect.

A border token can't track that, and the obvious ones can't even be seen here: input.border and widget.border are both null outside high contrast, so they resolve to a 20%-alpha fallback. On a 1px circle that measured contrast 9 against the background (luminance 58 vs 49) — the rings were effectively invisible, which left these buttons looking weightless next to the filled send disc. With currentColor the ring matches the glyph exactly (rgb(188,188,188) on both). High-contrast themes still take contrastBorder, so the ring stays fully opaque there.

The dictation mic glow now follows the button's radius, so the light no longer spills past its own edge.

Configure Tools icon

This was the only icon in the primary toolbar still at the base 16px, towering over the 11–12px text chips beside it. It moves to the settingsCompact glyph, per the guidance to swap to the *Compact variant when sizing down. The dependent selector in the automation dialog — which hides the chip by glyph class — is updated with it.

Icon-only chips in that toolbar are now sized and centered by a single rule. The base action-bar rule gives them a fixed 16px box with align-items: center but no justify-content, so a compact glyph sits flush left inside it; folding centering in with the size keeps the two from drifting apart. That also retires an off-ramp 14px codicon size and a :has() selector on .action-item (#324985).

Verification

Measured from rendered component fixtures rather than by eye. With 1px debug outlines on each label and glyph, every icon is centered to the pixel:

Icon Glyph vs label center (H) Vertical offset
+ 26.5 vs 26.75 0.00px
tools 145.5 vs 145.5 0.00px
mic 417.5 vs 417.5 0.00px
voice 443.5 vs 443.5 0.00px
send 469.5 vs 469.5 +0.50px (existing optical nudge)

All five share one vertical center, and the three execute-toolbar buttons are all 22px with ~10px glyphs. Checked in dark and light, including the recording-glow, download-ring, and Voice Mode listening states. hygiene and stylelint pass.

The chat input fixture was still building its toolbar with newLine / settingsGear, so it wasn't exercising the shipping icons; it now uses arrowUpCompact / settingsCompact.

Make the chat composer's execute toolbar read as one uniform set of 22px
circular controls.

- Send button is a circle rather than a rounded square, in both its idle
  and enabled states and in its focus ring, so the shape never changes
  with state.
- The standalone dictation / Voice Mode buttons lose the segmented pill's
  enclosure when they render on their own, so each now carries a circular
  outline of its own. The ring is drawn in `currentColor` so it matches
  the glyph it encircles in every state — resting icon foreground, and
  the blue / purple / green the glyph takes while Voice Mode is
  listening, speaking or offering to disconnect. A border token can't
  track that, and the obvious ones aren't even visible here: both
  `input.border` and `widget.border` are `null` outside high contrast and
  fall back to a 20%-alpha color that all but disappears on a 1px circle.
  High-contrast themes still take `contrastBorder`.
- The dictation mic glow follows the button's radius so the light no
  longer spills past its own edge.
- Configure Tools moves to the `settingsCompact` glyph. It was the only
  icon in the primary toolbar still at the base 16px, towering over the
  11-12px text chips beside it. The dependent selector in the automation
  dialog is updated with it.
- Icon-only chips in the primary toolbar are sized and centered by one
  rule. The base action-bar rule gives them a fixed 16px box with no
  inline centering, so a compact glyph would otherwise sit flush left.
  This also retires an off-ramp `14px` codicon size and a `:has()`
  selector on `.action-item` (microsoft#324985).

The chat input fixture was still constructing its toolbar with
`newLine` / `settingsGear`, so it wasn't exercising the shipping icons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 30, 2026 00:16
@eli-w-king eli-w-king self-assigned this Jul 30, 2026
@eli-w-king
eli-w-king marked this pull request as draft July 30, 2026 00:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Standardizes chat composer controls as compact circular actions.

Changes:

  • Makes send, dictation, and Voice Mode controls circular.
  • Uses compact toolbar glyphs and consistent icon centering.
  • Updates fixture and automation-dialog selectors.
Show a summary per file
File Description
renderChatInput.ts Updates fixture icons.
chat.css Adds circular controls and compact sizing.
dictationMicGlow.css Makes standalone mic glow circular.
aiCustomizationManagement.css Updates hidden configure-tools selector.
chatToolActions.ts Uses the compact settings glyph.

Review details

  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread src/vs/workbench/contrib/chat/browser/widget/media/chat.css
Comment thread src/vs/workbench/contrib/chat/browser/widget/media/chat.css Outdated
The ring was drawn in `currentColor` so it would track the glyph through
Voice Mode's state colors, but that made it far too heavy at rest — in
light themes `currentColor` is a dark icon foreground, so the circle read
as a hard dark outline next to the pill's soft one.

These buttons are single-cell stand-ins for the segmented pill, so the
enclosure should be indistinguishable when the UI swaps between them. Use
the pill's exact border declaration instead. Measured against a real
pill: dark rgb(60,60,60) vs rgb(58,58,58), light rgb(206,206,206) vs
rgb(210,210,210) — the deltas are antialiasing on the curve.

This also restores correct high-contrast behaviour through the same
token, since `input.border` resolves to `contrastBorder` there.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eli-w-king
eli-w-king marked this pull request as ready for review July 30, 2026 00:28
@eli-w-king
eli-w-king enabled auto-merge July 30, 2026 00:28
@meganrogge meganrogge added this to the 1.132.0 milestone Jul 30, 2026

@hawkticehurst hawkticehurst left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please make sure to apply circular send button styling across both editor and agents window?

Also ensure that agents window new chat and active chat are verified since they are implemented as distinct widgets

eli-w-king and others added 2 commits July 30, 2026 08:52
…voice fixtures

Apply the circular send styling across every chat composer, not just the
editor one:

- The sessions part re-declared the send button's radius as
  `cornerRadius-small`, overriding the base rule and leaving the Agents
  window's active chat a rounded square. Drop those overrides so it
  inherits from chat.css — duplicating the radius per surface is what let
  the two drift apart in the first place.
- The Agents window's new-chat composer is a distinct widget with its own
  send button, so give `.sessions-chat-send-button` the circular radius
  too, and carry the ringed-circle treatment to its voice toolbar and
  dictation mic so the row stays consistent with the editor.

Use the compact glyph for Add Context. `Codicon.addCompact` exists, and
the convention is to swap to the optically tuned glyph rather than only
scaling the base one. Updated in both composers plus the automation
dialog's selector, and the Agents attach button drops an off-ramp 14px
size in the process. The design-tokens doc listed "plus" as having no
compact variant, which is no longer true.

Add screenshot fixture variants for the standalone dictation and Voice
Mode controls across all eight states they style differently: dictation
idle / recording / preparing, and Voice Mode idle / connecting /
listening / speaking / disconnect. The glow's audio-reactive level is
pinned so the screenshots stay deterministic.

Those fixtures immediately caught a live bug: the green disconnect rule
was specificity (0,3,0) while the blanket `icon-foreground !important`
rule that colors every toolbar codicon is (0,5,0). With equal
`!important` the higher specificity wins, so the disconnect button had
been rendering grey rather than green. Scoped the rule to the full
toolbar path so it wins, rather than baselining the bug.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eli-w-king

Copy link
Copy Markdown
Contributor Author

@hawkticehurst thanks — you were right, it only covered the editor composer. Fixed across all three, and the root cause was worth calling out:

Agents window active chatsessionsPart.css re-declared the send button's radius as cornerRadius-small, which overrode the base rule from chat.css and left it a rounded square. I removed those overrides rather than updating them to circle: they were pure duplicates of the base rule, so keeping them means every future change to the send button has to be made in two files, which is exactly how the two drifted apart here.

Agents window new chat.sessions-chat-send-button is a separate widget with its own styling, so it got the circular radius directly. Its voice toolbar and dictation mic also carry the ringed-circle treatment now, since that CSS explicitly says it exists to match the send button's tier.

Editor — unchanged from before.

Verified each: the Agents new-chat send button measures a 22px disc (I had to force the enabled fill to see it — the fixture's default empty-input state renders it transparent). The active chat now inherits from chat.css.

Left the agent-feedback widget alone — its send control is a bare 16px glyph with no button surface, so it's a different tier rather than the same control styled differently. Happy to pull it in if you'd rather it match.

Also addressed both Copilot comments: Add Context moves to Codicon.addCompact, and there are now 8 fixture variants covering the standalone mic / Voice Mode states. Those fixtures caught a real bug on the way in — the green disconnect rule was losing a specificity fight with the blanket icon-foreground !important rule and had been rendering grey; details in the inline reply.

@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

@benibenj

Matched files:

  • src/vs/sessions/browser/parts/media/sessionsPart.css

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (3)

src/vs/workbench/contrib/chat/browser/widget/media/chat.css:2109

  • justify-content: center has no effect unless the element is a flex/grid container. The prior rule explicitly set display: flex (and align-items: center) but the replacement rule doesn’t, so icon-only chips may no longer be centered. Add display: flex plus vertical centering (e.g. align-items: center) to this selector, or use a centering approach that matches the element’s actual display type.
.monaco-workbench .interactive-session .chat-input-toolbar .monaco-action-bar .action-item > .action-label.codicon[class*='codicon-'] {
	justify-content: center;
	font-size: var(--vscode-codiconFontSize-compact);
}

src/vs/workbench/contrib/chat/browser/speechToText/media/dictationMicGlow.css:64

  • This rule sets --dictation-mic-glow-radius to var(--vscode-cornerRadius-circle), which is already set by the existing .dictation-mic-active { --dictation-mic-glow-radius: ... } rule earlier in the file. If there isn’t an intervening override you’re trying to out-specify, consider removing this block to avoid duplicated declarations (or adjust it to only cover the cases that actually differ).
/* Standalone dictation buttons are discs, so the glow has to be one too —
	otherwise the light spills past the button's own edge. Covers the editor
	chat toolbar and the Agents window composer's mic button. */
.chat-execute-toolbar .monaco-action-bar .action-item.dictation-mic-active,
.sessions-chat-stt-button.dictation-mic-active {
	--dictation-mic-glow-radius: var(--vscode-cornerRadius-circle);
}

src/vs/workbench/test/browser/componentFixtures/chat/renderChatInput.ts:11

  • ThemeIcon is only used as a type in this file. Consider switching this to a type-only import (import type { ThemeIcon } ...) to avoid emitting a runtime import under TS configurations that preserve value imports.
import { ThemeIcon } from '../../../../../base/common/themables.js';
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@meganrogge meganrogge left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some checks are failing

Switching Add Context to `Codicon.addCompact` changes the `+` in the
inline chat zone widget, which is a blocks-ci fixture.

Verified the change is intentional and confined before accepting the new
hashes: rendering the fixture on this branch and on its merge-base and
diffing them locally shows a single 12×12px region differing — the `+`
glyph going from 11×11 to 10×10 of ink. That is the optically tuned
compact glyph replacing the scaled-down base one, at a call site already
rendering at `codiconFontSize-compact`, which is the pairing the design
tokens guidance asks for. Nothing else in the widget moved.

Hashes are exactly the ones the failing run computed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eli-w-king
eli-w-king merged commit c9b2176 into microsoft:main Jul 31, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Standalone dictation mic button is too small when Voice Mode is not enabled

4 participants