From 6eba7918bb7bfe5c9f32d349311ceb8e7303e827 Mon Sep 17 00:00:00 2001 From: Ben Villalobos <4691428+benvillalobos@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:47:30 -0700 Subject: [PATCH 01/17] Hide the 'New Automation' button when there are no automations --- src/vs/sessions/common/contextkeys.ts | 1 + .../contrib/sessions/browser/views/automationsView.ts | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/sessions/common/contextkeys.ts b/src/vs/sessions/common/contextkeys.ts index b9a666dfb75e7..f39217b64182a 100644 --- a/src/vs/sessions/common/contextkeys.ts +++ b/src/vs/sessions/common/contextkeys.ts @@ -58,6 +58,7 @@ export const MultipleSessionsVisibleContext = new RawContextKey('multip export const CustomViewVisibleContext = new RawContextKey('customViewVisible', false, localize('customViewVisible', "Whether a custom view is shown in place of the sessions grid. The side panel and the panel are hidden while it is.")); export const AutomationsCustomViewFocusContext = new RawContextKey('automationsCustomViewFocus', false, localize('automationsCustomViewFocus', "Whether the Automations custom view has keyboard focus")); +export const AutomationsHasItemsContext = new RawContextKey('automationsHasItems', false, localize('automationsHasItems', "Whether there is at least one automation")); //#endregion diff --git a/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts b/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts index 66387a53f8428..a5a4ad92db8e7 100644 --- a/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts +++ b/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts @@ -28,7 +28,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; -import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { status } from '../../../../../base/browser/ui/aria/aria.js'; import { createPixelSpinner } from '../../../../../base/browser/ui/pixelSpinner/pixelSpinner.js'; import { Gesture, GestureEvent, EventType as TouchEventType } from '../../../../../base/browser/touch.js'; @@ -46,7 +46,7 @@ import { Action2, MenuItemAction, registerAction2 } from '../../../../../platfor import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; import { BaseActionViewItem, IActionViewItemOptions } from '../../../../../base/browser/ui/actionbar/actionViewItems.js'; import { IAction } from '../../../../../base/common/actions.js'; -import { AutomationsCustomViewFocusContext } from '../../../../common/contextkeys.js'; +import { AutomationsCustomViewFocusContext, AutomationsHasItemsContext } from '../../../../common/contextkeys.js'; const $ = DOM.$; @@ -74,6 +74,7 @@ export class AutomationsCardsWidget extends Disposable { this.element = $('.automations-cards-widget'); this.element.tabIndex = -1; const focusContext = AutomationsCustomViewFocusContext.bindTo(contextKeyService); + const hasItemsContext = AutomationsHasItemsContext.bindTo(contextKeyService); const focusTracker = this._register(DOM.trackFocus(this.element)); this._register(focusTracker.onDidFocus(() => focusContext.set(true))); this._register(focusTracker.onDidBlur(() => focusContext.set(false))); @@ -85,6 +86,7 @@ export class AutomationsCardsWidget extends Disposable { this._register(autorun(reader => { const items = this.automationService.automations.read(reader); + hasItemsContext.set(items.length > 0); this.cardsSection.render(items); })); @@ -822,7 +824,7 @@ registerAction2(class NewAutomationAction extends Action2 { id: 'sessionsView.newAutomation', title: localize2('newAutomation', "New Automation"), precondition: ChatAutomationsEnabledContext, - menu: [{ id: Menus.CustomViewAutomations, group: 'navigation', order: 1, when: ChatAutomationsEnabledContext }], + menu: [{ id: Menus.CustomViewAutomations, group: 'navigation', order: 1, when: ContextKeyExpr.and(ChatAutomationsEnabledContext, AutomationsHasItemsContext) }], }); } override async run(accessor: ServicesAccessor): Promise { From e44a9aa4f9645c0d72a686f9fa2536962893c76b Mon Sep 17 00:00:00 2001 From: Ben Villalobos <4691428+benvillalobos@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:59:13 -0700 Subject: [PATCH 02/17] Fix caps --- .../contrib/sessions/browser/views/automationsView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts b/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts index a5a4ad92db8e7..fa2ca017ddce3 100644 --- a/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts +++ b/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts @@ -280,9 +280,9 @@ class AutomationCardsSection extends Disposable { const createButton = this.disposables.add(new Button(this.emptyContainer, { ...defaultButtonStyles, - title: localize('createAutomation', "Create automation"), + title: localize('createAutomation', "Create Automation"), })); - createButton.label = localize('createAutomation', "Create automation"); + createButton.label = localize('createAutomation', "Create Automation"); createButton.element.classList.add('automations-cards-create-button'); this.disposables.add(createButton.onDidClick(() => this.openCreateDialog())); } From 3df61e759807a78a0d09e87ed2b51807ba75760f Mon Sep 17 00:00:00 2001 From: Ben Villalobos <4691428+benvillalobos@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:04:46 -0700 Subject: [PATCH 03/17] Fix context key setting for btn --- .../contrib/sessions/browser/views/automationsView.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts b/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts index fa2ca017ddce3..ef3ba27bdc583 100644 --- a/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts +++ b/src/vs/sessions/contrib/sessions/browser/views/automationsView.ts @@ -74,7 +74,6 @@ export class AutomationsCardsWidget extends Disposable { this.element = $('.automations-cards-widget'); this.element.tabIndex = -1; const focusContext = AutomationsCustomViewFocusContext.bindTo(contextKeyService); - const hasItemsContext = AutomationsHasItemsContext.bindTo(contextKeyService); const focusTracker = this._register(DOM.trackFocus(this.element)); this._register(focusTracker.onDidFocus(() => focusContext.set(true))); this._register(focusTracker.onDidBlur(() => focusContext.set(false))); @@ -86,7 +85,6 @@ export class AutomationsCardsWidget extends Disposable { this._register(autorun(reader => { const items = this.automationService.automations.read(reader); - hasItemsContext.set(items.length > 0); this.cardsSection.render(items); })); @@ -752,9 +750,15 @@ class AutomationsCustomViewContribution extends Disposable { @ICustomViewService customViewService: ICustomViewService, @IActionViewItemService actionViewItemService: IActionViewItemService, @IContextKeyService contextKeyService: IContextKeyService, + @IAutomationService automationService: IAutomationService, ) { super(); + const hasItemsContext = AutomationsHasItemsContext.bindTo(contextKeyService); + this._register(autorun(reader => { + hasItemsContext.set(automationService.automations.read(reader).length > 0); + })); + this._register(customViewService.registerCustomView({ id: AUTOMATIONS_CUSTOM_VIEW_ID, ctor: new SyncDescriptor(AutomationsCustomView), From 0e363b8e3b713753ea995995177e48c706317bde Mon Sep 17 00:00:00 2001 From: Ben Villalobos <4691428+benvillalobos@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:15:31 -0700 Subject: [PATCH 04/17] Always show custom view header divider Previously the bottom border on the custom view header band was transparent by default and only became visible when the user scrolled down (via the .scrolled class toggled in customViewNode.ts). This meant the divider between the header and content was invisible at the top scroll position. Change the border color to always be visible so the header is consistently separated from the content regardless of scroll position. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/vs/sessions/browser/parts/media/customViewGridPart.css | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/sessions/browser/parts/media/customViewGridPart.css b/src/vs/sessions/browser/parts/media/customViewGridPart.css index 556516e140a02..d98b946449104 100644 --- a/src/vs/sessions/browser/parts/media/customViewGridPart.css +++ b/src/vs/sessions/browser/parts/media/customViewGridPart.css @@ -41,11 +41,7 @@ box-sizing: border-box; margin: 0 auto; padding: 6px 10px; - border-bottom: 1px solid transparent; -} - -.custom-view-header.scrolled .custom-view-header-band { - border-bottom-color: color-mix(in srgb, var(--session-view-foreground) 12%, transparent); + border-bottom: 1px solid color-mix(in srgb, var(--session-view-foreground) 12%, transparent); } .custom-view-header-title-row { From 2de012701d3be45ed14e99af439c6b1b990ae2cb Mon Sep 17 00:00:00 2001 From: Ben Villalobos <4691428+benvillalobos@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:22:56 -0700 Subject: [PATCH 05/17] Move automation dialog CSS to dedicated file and fix button padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The automation dialog CSS was still living in aiCustomizationManagement.css after the automations list widget was removed in #328654. Move it to a dedicated automationDialog.css file next to the dialog service and add the import there. Also fix padding issues where Create/Cancel buttons and the close button were too close to the dialog border (padding: 0 was inherited from the base dialog override): - dialog-toolbar-row (close button): 4px/6px → 8px/10px from top/right - dialog-buttons-row: add 8px top, 10px side, 10px bottom padding Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../browser/automationDialogService.ts | 1 + .../browser/media/automationDialog.css | 711 ++++++++++++++++++ .../media/aiCustomizationManagement.css | 704 ----------------- 3 files changed, 712 insertions(+), 704 deletions(-) create mode 100644 src/vs/sessions/contrib/automations/browser/media/automationDialog.css diff --git a/src/vs/sessions/contrib/automations/browser/automationDialogService.ts b/src/vs/sessions/contrib/automations/browser/automationDialogService.ts index fb1623d0b284a..4c6e30e733a16 100644 --- a/src/vs/sessions/contrib/automations/browser/automationDialogService.ts +++ b/src/vs/sessions/contrib/automations/browser/automationDialogService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import './media/automationDialog.css'; import * as DOM from '../../../../base/browser/dom.js'; import { IButton } from '../../../../base/browser/ui/button/button.js'; import { Dialog } from '../../../../base/browser/ui/dialog/dialog.js'; diff --git a/src/vs/sessions/contrib/automations/browser/media/automationDialog.css b/src/vs/sessions/contrib/automations/browser/media/automationDialog.css new file mode 100644 index 0000000000000..edc8930944a7a --- /dev/null +++ b/src/vs/sessions/contrib/automations/browser/media/automationDialog.css @@ -0,0 +1,711 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Dialog */ +.monaco-dialog-box.automation-dialog .dialog-message-container > .dialog-message-detail { + display: none; +} + +/* + * The dialog's built-in title (rendered into `.dialog-message-text`) + * stays in the DOM so `aria-labelledby="monaco-dialog-message-text"` + * still binds for screen readers, but is visually hidden. Our own + * `.automation-titlebar` provides the visible chrome. Visually-hidden + * pattern (not `display: none`) because some AT/screen-reader combos + * skip aria-labelledby targets when `display: none` is in effect. + * Same treatment for the unused dialog icon slot. + */ +.monaco-dialog-box.automation-dialog .dialog-message-container > .dialog-message { + margin: 0; + padding: 0; +} + +.monaco-dialog-box.automation-dialog .dialog-message-container > .dialog-message .dialog-message-text { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.monaco-dialog-box.automation-dialog .dialog-message-row > .dialog-icon.codicon { + display: none; +} + +/* + * Lock the dialog box to a stable width. Without this rule the dialog + * inherits `width: min-content` from `dialog.css` and re-flows as the + * embedded chat input's chips change size (e.g. when the model picker + * resolves a shorter label). Use min(640px, 92vw) so the dialog still + * shrinks gracefully on narrow viewports. + * + * Padding is stripped (vs. the base 8px) so our `.automation-titlebar` + * stripe can paint edge-to-edge inside the dialog body. The form pane + * re-adds its own padding. + * + * Note: `extraClasses` on Dialog adds the class to `.monaco-dialog-box` + * itself (not an ancestor), so we attach the rule directly here. + */ +.monaco-dialog-box.automation-dialog { + width: min(640px, 92vw); + min-width: min(520px, 92vw); + max-width: 92vw; + padding: 0; + background-color: var(--vscode-editorWidget-background); + color: var(--vscode-editorWidget-foreground); + /* Anchor for the absolutely-positioned close-X (see below). */ + position: relative; +} + +.monaco-workbench .monaco-dialog-box.automation-dialog:focus:not(:focus-visible) { + outline: none; +} + +/* + * Float the close-X over the titlebar so the title text sits flush + * with the top edge of the modal (QuickInput-style). Without this, + * `.dialog-toolbar-row` reserves ~24px of dead space above the title. + * `z-index` keeps the X above the sticky titlebar. + */ +.monaco-dialog-box.automation-dialog .dialog-toolbar-row { + position: absolute; + top: 0; + right: 0; + height: auto; + padding: 8px 10px 0 0; + z-index: 2; +} + +.automation-dialog-body { + display: flex; + flex-direction: column; + min-height: 0; +} + +.monaco-dialog-box.automation-dialog > .dialog-buttons-row { + padding: 8px 10px 10px; +} + +/* + * Strip the base dialog padding on the message row and container so + * our titlebar can full-bleed. The form pane below re-adds horizontal + * padding so the form fields don't crash into the dialog edge. + * + * Container still owns the scrolling (per base/.../dialog.css), so + * the titlebar uses `position: sticky; top: 0` to stay visible as the + * form scrolls underneath it. + */ +.monaco-dialog-box.automation-dialog .dialog-message-row { + padding: 0; +} + +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container { + padding: 0; + /* + * The base dialog leaves the message container at content size + * (`align-self: stretch` only stretches height in a horizontal flex + * row). Our form fields are `width: 100%` of this container, so they + * track the container's intrinsic min-content width. It shrinks + * over the first second as the embedded chat input's chip labels + * (model name, mode name, etc.) resolve and the toolbar's + * min-content drops. Forcing the container to fill the row width + * keeps every field at the dialog's locked width from first paint + * onwards. `min-width: 0` lets the container shrink inside the row + * (which has `overflow: hidden`) instead of being pushed by long + * chip labels. + */ + flex: 1 1 auto; + min-width: 0; +} + +/* + * Titlebar background matches the dialog body so the header reads as + * part of the modal, with no contrasting stripe. Both are anchored to + * `editorWidget.background`, the dialog widget's own native background + * (see defaultDialogStyles), so the header and body are always the same + * color in every theme. Set explicitly (not transparent) because the + * titlebar is sticky. A transparent background would let scrolled form + * content show through. No `border-bottom`. Sticky so the title stays + * in view as the form scrolls (the dialog's message-container is the + * scroll host). + */ +.automation-titlebar { + position: sticky; + top: 0; + z-index: 1; + /* Right padding reserves room for the floating close-X chip. */ + padding: 8px 36px 8px 14px; + text-align: left; + font-weight: var(--vscode-agents-fontWeight-semiBold); + font-size: var(--vscode-agents-fontSize-heading2); + color: var(--vscode-editorWidget-foreground); + background-color: var(--vscode-editorWidget-background); +} + +.automation-description { + padding: 8px 14px; + font-size: var(--vscode-agents-fontSize-body2, 12px); + color: var(--vscode-descriptionForeground); + line-height: 1.4; +} + +.automation-form-pane { + flex: 1 1 auto; + min-width: 0; + min-height: 0; + padding: 4px 14px 6px; +} + +/* + * Theme the native overflow scrollbar on the dialog body so it matches + * the rest of VS Code. .dialog-message-container is the scroll host + * (see base/browser/ui/dialog/dialog.css). Without this the dialog + * shows the platform's default scrollbar which clashes visually with + * the chat input embedded above. + */ +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar { + width: 10px; +} + +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-track { + background-color: transparent; +} + +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-thumb { + min-height: 20px; + background-color: var(--vscode-scrollbarSlider-background); +} + +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-thumb:hover { + background-color: var(--vscode-scrollbarSlider-hoverBackground); +} + +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-thumb:active { + background-color: var(--vscode-scrollbarSlider-activeBackground); +} + +.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-corner { + display: none; +} + +/* + * Z-index bridge for popups while the automation dialog is open. + * The dialog modal block is z-index 2575 (see base/.../dialog.css) + * and shares that value with .context-view (set inline in + * contextview.ts as `2575 + layer`). DOM order means the dialog + * shows on top, hiding any popup the chat input chips, the quick + * input, or a context menu try to open. We can't change the inline + * z-index from outside, so use `!important` while the dialog service + * marks the active container as automation-dialog-open. + */ +.automation-dialog-open .context-view.monaco-component { + z-index: 2600 !important; +} + +.automation-dialog-open .quick-input-widget { + z-index: 2600 !important; +} + +.automation-dialog-open .monaco-menu-container { + z-index: 2600 !important; +} + +.automation-form { + display: flex; + flex-direction: column; + gap: 10px; + padding: 2px 2px 4px; +} + +/* + * Host for the embedded ChatInputPart. The composer brings its own + * background, border, and rounded corners (see chat.css + * `.interactive-input-part`), so we just give it room to breathe and let + * it fill the dialog's column width. `min-height` is sized for ~5 lines + * of body text + the toolbar row so the editor doesn't feel cramped on + * first open; the editor itself grows as the user types. + */ +.automation-form-prompt-host { + display: flex; + flex-direction: column; + min-height: 140px; + /* + * The host carries the `.interactive-session` class so the shared + * chat input CSS (chip colors, focus borders, etc.) matches what + * users see in a real chat session. That class also sets + * `max-width: 950px; margin: auto; height: 100%` (chat.css), which + * is meant for the full chat panel. In our flex column it makes + * the host take its content's min-content width and center, + * collapsing the chat input to ~chip-bar width. Re-assert + * full-width sizing so the host fills the dialog's form column. + */ + width: auto; + max-width: 100%; + margin: 0; + height: auto; + align-self: stretch; +} + +.automation-form-prompt-host .interactive-input-part { + /* + * `.interactive-input-part` in chat.css has `margin: 0 12px`, which + * (combined with the default `width: auto`) is designed to inset the + * composer inside a wider chat panel. In our dialog the prompt host + * is already the form column, so the margin pushes the composer 12px + * past the host on each side. Its right border ends up sitting + * outside the dialog. Zero out the margin so the composer aligns + * with the other form fields. + */ + margin: 0; + /* Don't let long custom-mode names or model identifiers push the + * modal wider than its declared max-width. */ + max-width: 100%; + min-width: 0; +} + +.automation-form-prompt-host .interactive-input-editor, +.automation-form-prompt-host .monaco-editor, +.automation-form-prompt-host .monaco-editor-background, +.automation-form-prompt-host .monaco-editor .margin { + background-color: var(--vscode-settings-textInputBackground, var(--vscode-input-background)); +} + +.automation-form-prompt-host .interactive-input-and-side-toolbar { + /* Allow the input column to shrink below its content's preferred + * width so flex children (chips, custom-mode names) don't push the + * modal wider than its declared max-width. */ + min-width: 0; +} + +/* + * Hide chips from the embedded chat input's primary toolbar that don't fit + * the automation dialog's reduced surface: + * - "Configure Tools" (`workbench.action.chat.configureTools`, + * `.codicon-settings-compact`) — tool selection is not relevant for a + * scheduled prompt. + * - "Add Context" (`workbench.action.chat.attachContext`, + * `.codicon-add-compact`, the `+`) — the dialog doesn't support + * attachments; the prompt is the only payload sent at run time. + * - "List MCP Servers" (`workbench.mcp.listServer`, `.codicon-server`) — + * MCP server management belongs in the live chat surface, not a one-off + * prompt definition. + * + * We hide the `.action-label` (which carries the codicon class) rather than + * the parent `.action-item`. Targeting the `.action-item` would require a + * `:has()` selector, whose invalidation bookkeeping is paid across every + * `.action-item` in the workbench (see microsoft/vscode#324985). Since + * `.action-item` has no intrinsic padding/margin (actionbar.css), hiding the + * label collapses the slot to zero width, so there is no visual gap. + * + * Known limitation: the hidden chips remain in the toolbar's arrow-key focus + * rotation (the action bar skips only disabled/separator items, not + * `display:none` ones). This is an accepted temporary trade-off — the dialog + * will migrate to `newChatInput.ts`, which builds its own toolbars and never + * renders these `MenuId.ChatInput` chips at all. + */ +.automation-form-prompt-host .chat-input-toolbar .action-label.codicon-settings-compact, +.automation-form-prompt-host .chat-input-toolbar .action-label.codicon-add-compact, +.automation-form-prompt-host .chat-input-toolbar .action-item.chat-mcp { + display: none; +} + +/* + * Suppress every inter-chip divider drawn by chat.css inside the dialog, + * in both the primary (`.chat-input-toolbar`) and secondary + * (`.chat-secondary-input-toolbar`) toolbars. CSS `+` matches by DOM + * order regardless of `display:none`, so the divider would otherwise + * appear as an orphan vertical line on whichever visible chip follows + * our hidden `+`/settings items. For example the bar drawn to the left + * of the Folder/Worktree isolation chip because it follows the Copilot + * CLI chip in the secondary toolbar (microsoft/vscode-internalbacklog#8304). The remaining visible + * chips (Agent, model, mode, isolation) already have enough intrinsic + * padding to read as separate without the divider. Dropping dividers + * entirely is simpler and more resilient than per-pair suppression rules + * that depend on DOM order. + */ +.automation-form-prompt-host .chat-input-toolbar .monaco-action-bar .actions-container > .action-item + .action-item::before, +.automation-form-prompt-host .chat-secondary-input-toolbar .monaco-action-bar .actions-container > .action-item + .action-item::before { + display: none; +} + +/* + * Cancel the dialog's `
    ` indent for the chat input toolbars. + * + * `dialog.css:120` applies `padding-inline-start: 20px` to every `
      ` + * inside `.dialog-message-container` (meant to reduce the excessive + * default indent on bulleted lists in dialog body text). The + * `monaco-action-bar`'s `.actions-container` is rendered as a `
        `, so + * the chat input's primary and secondary toolbars inherit that 20px + * left indent. This pushes the Agent / Copilot CLI chips ~20px right of + * the chat input's bottom-left corner. + * + * `actionbar.css` sets `.actions-container { padding: 0 }`, but the + * dialog rule uses a logical property (`padding-inline-start`), which + * cascades-after the physical shorthand and wins regardless of selector + * specificity. Override the logical property explicitly here. + * + * Scoped to `.automation-form-prompt-host` so the regular chat panel + * (which isn't inside a dialog) is unaffected. Any `
          ` + * elements in the dialog's actual message body text retain the + * intended 20px indent. + */ +.automation-form-prompt-host .chat-input-toolbar .monaco-action-bar .actions-container, +.automation-form-prompt-host .chat-secondary-toolbar .monaco-action-bar .actions-container { + padding-inline-start: 0; + margin-left: 0; +} + +/* + * Sessions-layer workspace picker visual override. + * + * Two cascading sources need to be defeated to match Mode/Model: + * + * 1. `.sessions-chat-picker-slot.sessions-chat-workspace-picker + * .action-label` (sessions-layer `chatWidget.css:244-269`) sets a + * welcome-flow visual: label 18px, inner span 18px, icons 16px, + * chevron, generous padding, `color: var(--vscode-foreground)`. + * + * 2. `.monaco-action-bar .action-label` (actionbar.css:48) sets the + * base toolbar font-size to **11px**. This is what Mode/Model + * inherit. They don't set font-size themselves. The action bar + * base rule wins for their `.action-label`. The workspace picker's + * own sessions-layer rule (source 1) overrides that base, so we + * need to put the 11px back explicitly. `font-size: inherit` + * does NOT work because none of `.sessions-chat-picker-slot`'s + * ancestors carry an 11px font-size. The 11px lives on sibling + * `.action-label` elements via `.monaco-action-bar .action-label`, + * not on an ancestor we can inherit from. + * + * Result: match the 11px base, neutralize the welcome-flow oversizing, + * keep the Mode/Model color (`var(--vscode-icon-foreground)`), match + * the chip metrics from `.chat-secondary-toolbar .chat-input-picker-item + * .action-label` (chat.css:1728), and hide the chevron (the neighboring + * secondary-toolbar pickers don't render one). + * + * Scoped to `.automation-form-prompt-host` so the welcome view's + * standalone workspace picker keeps its larger sizing. + */ +.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label { + height: 16px; + padding: 3px 6px; + font-size: 11px; + line-height: normal; + color: var(--vscode-icon-foreground); +} + +.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label .sessions-chat-dropdown-label { + font-size: inherit; + line-height: inherit; +} + +.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label > .codicon:not(.sessions-chat-dropdown-chevron) { + font-size: 12px; +} + +/* + * The neighboring secondary-toolbar pickers don't render a dropdown + * chevron. They are recognized as chips by their padding + hover. Hide + * the chevron that `_renderTriggerLabel` always appends so the + * workspace chip reads the same way. + */ +.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label .sessions-chat-dropdown-chevron { + display: none; +} + +.automation-form-row { + display: flex; + flex-direction: column; + gap: 4px; +} + +.automation-form-row.automation-form-checkbox-row { + flex-direction: row; + align-items: center; + gap: 8px; + margin-top: 2px; + padding-top: 10px; + border-top: 1px solid var(--vscode-widget-border, transparent); +} + +.automation-form-row.automation-form-checkbox-row > .monaco-checkbox { + margin-right: 0; +} + +/* + * Lay the Schedule / Time / Day controls along a single horizontal axis. + * Each control lives in its own `.automation-form-schedule-group` + * (label-above-control flex column). Time and Day join the row only + * when the interval is daily or weekly; flex-wrap keeps the layout + * stable if a translation pushes the row past the dialog width. + */ +.automation-form-row.automation-form-schedule-row { + flex-direction: row; + align-items: flex-end; + flex-wrap: wrap; + gap: 12px; +} + +.automation-form-schedule-group { + display: flex; + flex-direction: column; + gap: 4px; + /* + * Each visible group splits the schedule row equally. Hidden + * groups (`display: none` from `applyIntervalVisibility`) drop out + * of flex distribution, so: + * - manual / hourly → Schedule fills 100% of the row + * - daily → Schedule + Time split 50/50 + * - weekly → Schedule + Time + Day split 33/33/33 + * `min-width: 0` lets the contained SelectBox shrink past its + * intrinsic content width when the row is narrow, before the + * row's `flex-wrap: wrap` kicks in as a final safety net. + */ + flex: 1 1 0; + min-width: 0; +} + +/* SelectBox container sizing. Give Schedule / Time / Day the full + * width of their parent group (which itself flex-shares the row). + * + * `.monaco-select-box` ships with no intrinsic padding or height (the + * only baked-in metrics live in the `.monaco-action-bar .action-item` + * selector, which we're not inside of). Mirror the standalone-form + * pattern used by the settings editor (`settingsEditor2.css:689-694`). + * It uses `height: 26px` and `padding: 2px 6px`. This makes the dropdowns match the + * Name InputBox above them instead of reading as toolbar widgets + * crammed into a form. */ +.automation-form-schedule-select-container { + display: flex; + min-width: 0; + width: 100%; +} + +.automation-form-schedule-select-container .monaco-select-box { + height: 26px; + padding: 2px 8px; +} + +/* + * Form-row labels read as section titles ("Name", "Schedule", "Time", + * "Day of week", "Prompt"). Mirror the established agents + * design-system section-label pattern (see `.overview-section + * .section-label` at line ~734: label1 / fontWeight-medium / + * foreground) so the labels carry visual weight as section headings + * and clearly out-rank their controls. + * + * `margin-bottom` adds breathing room between the title text and + * its control so each form row reads as a discrete section. + */ +.automation-form-label { + font-size: var(--vscode-agents-fontSize-label1); + font-weight: var(--vscode-agents-fontWeight-semiBold); + color: var(--vscode-foreground); + line-height: 1.4; + margin-bottom: 2px; + cursor: default; +} + +.automation-form-hint { + font-size: var(--vscode-agents-fontSize-body2, 11px); + color: var(--vscode-descriptionForeground); + line-height: 1.4; + min-height: 1em; +} + +.automation-form-checkbox-label { + font-size: var(--vscode-agents-fontSize-body1, 13px); + color: var(--vscode-foreground); + cursor: pointer; +} + +/* + * Host for the native `InputBox` widget. The widget owns its own + * `` and styling (border, padding, focus ring); the host just + * lets it grow to the row width. + */ +.automation-form-input-host { + display: flex; + width: 100%; +} + +.automation-form-input-host > .monaco-inputbox { + flex: 1 1 auto; + min-width: 0; +} + +.automation-form-input, +.automation-form-select, +.automation-form-textarea { + font-family: inherit; + font-size: var(--vscode-agents-fontSize-body1, 13px); + color: var(--vscode-settings-textInputForeground, var(--vscode-input-foreground)); + background-color: var(--vscode-settings-textInputBackground, var(--vscode-input-background)); + border: 1px solid var(--vscode-settings-textInputBorder, var(--vscode-input-border, var(--vscode-contrastBorder, transparent))); + border-radius: 2px; + padding: 4px 6px; + box-sizing: border-box; + width: 100%; + min-height: 26px; +} + +.automation-form-select { + color: var(--vscode-settings-dropdownForeground, var(--vscode-dropdown-foreground)); + background-color: var(--vscode-settings-dropdownBackground, var(--vscode-dropdown-background)); + border-color: var(--vscode-settings-dropdownBorder, var(--vscode-dropdown-border, var(--vscode-contrastBorder, transparent))); +} + +.automation-form-textarea { + resize: vertical; + min-height: 60px; + line-height: 1.4; +} + +.automation-form-input:focus, +.automation-form-select:focus, +.automation-form-textarea:focus { + outline: 1px solid var(--vscode-focusBorder); + outline-offset: -1px; + border-color: var(--vscode-focusBorder); +} + +.automation-form-empty { + font-size: 12px; + color: var(--vscode-errorForeground); + padding: 6px 0; +} + +/* + * Folder picker. This uses the shared `WorkspacePicker` from the sessions + * layer (see `sessionWorkspacePicker.ts`). The picker renders an + * `` chip trigger inside + * `.sessions-chat-picker-slot.sessions-chat-workspace-picker`. We restyle + * it to fit the dialog's compact, 13px form scale. The upstream styling + * lives in `sessions/contrib/chat/browser/media/chatWidget.css` and is + * sized for the new-session "hero" UI (18px font, larger chip) which is + * too large for our modal. + * + * The dropdown content itself renders through `IActionWidgetService`, + * which has its own global CSS. No overrides needed here. + */ +/* + * Isolation + branch group: parented into the chat input's + * `.chat-secondary-toolbar` so it sits at the bottom-right of the chat + * input, sharing a row with `Copilot CLI | Default Permissions`. + * `margin-left: auto` pushes the group to the right edge within that + * flex row. + * + * Visual styling mirrors the new-session view's + * `.new-chat-bottom-container` chip vocabulary (see + * `sessions/contrib/chat/browser/media/chatWidget.css` lines 178-209): + * compact label text and codicons, icon-foreground color, no border. + */ +.automation-form-prompt-host .chat-secondary-toolbar .automation-form-isolation-group { + display: inline-flex; + align-items: center; + margin-left: auto; + min-width: 0; + gap: var(--vscode-spacing-size60); +} + +.automation-form-prompt-host .automation-form-branch-picker-slot { + display: inline-flex; + min-width: 0; +} + +.automation-form-prompt-host .automation-form-branch-slot { + display: inline-flex; + align-items: center; + gap: var(--vscode-spacing-size40); + height: 16px; + padding: var(--vscode-spacing-size20) var(--vscode-spacing-size60); + font-size: var(--vscode-agents-fontSize-label2); + color: var(--vscode-icon-foreground); + background: transparent; + border: none; + min-width: 0; + border-radius: var(--vscode-cornerRadius-small); + touch-action: manipulation; +} + +.automation-form-prompt-host .automation-form-branch-slot > .codicon { + font-size: var(--vscode-codiconFontSize-compact); + color: var(--vscode-icon-foreground); + flex-shrink: 0; +} + +.automation-form-prompt-host .automation-form-branch-slot:not(.branch-picker-disabled) { + cursor: pointer; +} + +.automation-form-prompt-host .automation-form-branch-slot:not(.branch-picker-disabled):hover { + background-color: var(--vscode-toolbar-hoverBackground); + color: var(--vscode-foreground); +} + +.automation-form-prompt-host .automation-form-branch-slot:focus-visible { + outline: var(--vscode-strokeThickness) solid var(--vscode-focusBorder); + outline-offset: calc(-1 * var(--vscode-strokeThickness)); +} + +.automation-form-prompt-host .automation-form-branch-slot.branch-picker-disabled, +.automation-form-prompt-host .automation-form-branch-slot.branch-picker-disabled > .codicon { + cursor: default; + color: var(--vscode-descriptionForeground); +} + +.automation-form-prompt-host .automation-form-branch-name { + font-size: var(--vscode-agents-fontSize-label2); +} + +.automation-form-prompt-host .automation-form-branch-name { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.automation-form-prompt-host .chat-secondary-toolbar .automation-form-harness-chip { + display: inline-flex; + align-items: center; + gap: 4px; + height: 16px; + padding: 3px 6px; + font-size: 11px; + color: var(--vscode-icon-foreground); + background: transparent; + border: none; + opacity: 0.6; + cursor: default; +} + +.automation-form-prompt-host .automation-form-harness-chip > .codicon { + font-size: 12px; + flex-shrink: 0; +} + +.automation-form-prompt-host .automation-form-harness-label { + font-size: 11px; +} + +/* Missing-branch state (no folder / no git repo / detached / empty): use + * description-foreground + italic so it visually reads as "informational + * placeholder, not a real ref name". */ +.automation-form-prompt-host .automation-form-branch-slot.branch-picker-missing, +.automation-form-prompt-host .automation-form-branch-slot.branch-picker-missing > .codicon { + color: var(--vscode-descriptionForeground); +} + +.automation-form-prompt-host .automation-form-branch-slot.branch-picker-missing .automation-form-branch-name { + font-style: italic; +} + +.hc-black .automation-form-prompt-host .automation-form-branch-slot:focus-visible, +.hc-light .automation-form-prompt-host .automation-form-branch-slot:focus-visible { + outline-color: var(--vscode-contrastActiveBorder); +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/browser/aiCustomization/media/aiCustomizationManagement.css b/src/vs/workbench/contrib/chat/browser/aiCustomization/media/aiCustomizationManagement.css index e50452d6230ae..99158cfc058e5 100644 --- a/src/vs/workbench/contrib/chat/browser/aiCustomization/media/aiCustomizationManagement.css +++ b/src/vs/workbench/contrib/chat/browser/aiCustomization/media/aiCustomizationManagement.css @@ -2021,707 +2021,3 @@ pane is first mounted. View switches inside the modal are not animated. */ } /* Welcome page styles live in the welcome page variant stylesheets. */ - - -/* Dialog */ -.monaco-dialog-box.automation-dialog .dialog-message-container > .dialog-message-detail { - display: none; -} - -/* - * The dialog's built-in title (rendered into `.dialog-message-text`) - * stays in the DOM so `aria-labelledby="monaco-dialog-message-text"` - * still binds for screen readers, but is visually hidden. Our own - * `.automation-titlebar` provides the visible chrome. Visually-hidden - * pattern (not `display: none`) because some AT/screen-reader combos - * skip aria-labelledby targets when `display: none` is in effect. - * Same treatment for the unused dialog icon slot. - */ -.monaco-dialog-box.automation-dialog .dialog-message-container > .dialog-message { - margin: 0; - padding: 0; -} - -.monaco-dialog-box.automation-dialog .dialog-message-container > .dialog-message .dialog-message-text { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.monaco-dialog-box.automation-dialog .dialog-message-row > .dialog-icon.codicon { - display: none; -} - -/* - * Lock the dialog box to a stable width. Without this rule the dialog - * inherits `width: min-content` from `dialog.css` and re-flows as the - * embedded chat input's chips change size (e.g. when the model picker - * resolves a shorter label). Use min(640px, 92vw) so the dialog still - * shrinks gracefully on narrow viewports. - * - * Padding is stripped (vs. the base 8px) so our `.automation-titlebar` - * stripe can paint edge-to-edge inside the dialog body. The form pane - * re-adds its own padding. - * - * Note: `extraClasses` on Dialog adds the class to `.monaco-dialog-box` - * itself (not an ancestor), so we attach the rule directly here. - */ -.monaco-dialog-box.automation-dialog { - width: min(640px, 92vw); - min-width: min(520px, 92vw); - max-width: 92vw; - padding: 0; - background-color: var(--vscode-editorWidget-background); - color: var(--vscode-editorWidget-foreground); - /* Anchor for the absolutely-positioned close-X (see below). */ - position: relative; -} - -.monaco-workbench .monaco-dialog-box.automation-dialog:focus:not(:focus-visible) { - outline: none; -} - -/* - * Float the close-X over the titlebar so the title text sits flush - * with the top edge of the modal (QuickInput-style). Without this, - * `.dialog-toolbar-row` reserves ~24px of dead space above the title. - * `z-index` keeps the X above the sticky titlebar. - */ -.monaco-dialog-box.automation-dialog .dialog-toolbar-row { - position: absolute; - top: 0; - right: 0; - height: auto; - padding: 4px 6px 0 0; - z-index: 2; -} - -.automation-dialog-body { - display: flex; - flex-direction: column; - min-height: 0; -} - -/* - * Strip the base dialog padding on the message row and container so - * our titlebar can full-bleed. The form pane below re-adds horizontal - * padding so the form fields don't crash into the dialog edge. - * - * Container still owns the scrolling (per base/.../dialog.css), so - * the titlebar uses `position: sticky; top: 0` to stay visible as the - * form scrolls underneath it. - */ -.monaco-dialog-box.automation-dialog .dialog-message-row { - padding: 0; -} - -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container { - padding: 0; - /* - * The base dialog leaves the message container at content size - * (`align-self: stretch` only stretches height in a horizontal flex - * row). Our form fields are `width: 100%` of this container, so they - * track the container's intrinsic min-content width. It shrinks - * over the first second as the embedded chat input's chip labels - * (model name, mode name, etc.) resolve and the toolbar's - * min-content drops. Forcing the container to fill the row width - * keeps every field at the dialog's locked width from first paint - * onwards. `min-width: 0` lets the container shrink inside the row - * (which has `overflow: hidden`) instead of being pushed by long - * chip labels. - */ - flex: 1 1 auto; - min-width: 0; -} - -/* - * Titlebar background matches the dialog body so the header reads as - * part of the modal, with no contrasting stripe. Both are anchored to - * `editorWidget.background`, the dialog widget's own native background - * (see defaultDialogStyles), so the header and body are always the same - * color in every theme. Set explicitly (not transparent) because the - * titlebar is sticky. A transparent background would let scrolled form - * content show through. No `border-bottom`. Sticky so the title stays - * in view as the form scrolls (the dialog's message-container is the - * scroll host). - */ -.automation-titlebar { - position: sticky; - top: 0; - z-index: 1; - /* Right padding reserves room for the floating close-X chip. */ - padding: 8px 36px 8px 14px; - text-align: left; - font-weight: var(--vscode-agents-fontWeight-semiBold); - font-size: var(--vscode-agents-fontSize-heading2); - color: var(--vscode-editorWidget-foreground); - background-color: var(--vscode-editorWidget-background); -} - -.automation-description { - padding: 8px 14px; - font-size: var(--vscode-agents-fontSize-body2, 12px); - color: var(--vscode-descriptionForeground); - line-height: 1.4; -} - -.automation-form-pane { - flex: 1 1 auto; - min-width: 0; - min-height: 0; - padding: 4px 14px 6px; -} - -/* - * Theme the native overflow scrollbar on the dialog body so it matches - * the rest of VS Code. .dialog-message-container is the scroll host - * (see base/browser/ui/dialog/dialog.css). Without this the dialog - * shows the platform's default scrollbar which clashes visually with - * the chat input embedded above. - */ -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar { - width: 10px; -} - -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-track { - background-color: transparent; -} - -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-thumb { - min-height: 20px; - background-color: var(--vscode-scrollbarSlider-background); -} - -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-thumb:hover { - background-color: var(--vscode-scrollbarSlider-hoverBackground); -} - -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-thumb:active { - background-color: var(--vscode-scrollbarSlider-activeBackground); -} - -.monaco-dialog-box.automation-dialog .dialog-message-row .dialog-message-container::-webkit-scrollbar-corner { - display: none; -} - -/* - * Z-index bridge for popups while the automation dialog is open. - * The dialog modal block is z-index 2575 (see base/.../dialog.css) - * and shares that value with .context-view (set inline in - * contextview.ts as `2575 + layer`). DOM order means the dialog - * shows on top, hiding any popup the chat input chips, the quick - * input, or a context menu try to open. We can't change the inline - * z-index from outside, so use `!important` while the dialog service - * marks the active container as automation-dialog-open. - */ -.automation-dialog-open .context-view.monaco-component { - z-index: 2600 !important; -} - -.automation-dialog-open .quick-input-widget { - z-index: 2600 !important; -} - -.automation-dialog-open .monaco-menu-container { - z-index: 2600 !important; -} - -.automation-form { - display: flex; - flex-direction: column; - gap: 10px; - padding: 2px 2px 4px; -} - -/* - * Host for the embedded ChatInputPart. The composer brings its own - * background, border, and rounded corners (see chat.css - * `.interactive-input-part`), so we just give it room to breathe and let - * it fill the dialog's column width. `min-height` is sized for ~5 lines - * of body text + the toolbar row so the editor doesn't feel cramped on - * first open; the editor itself grows as the user types. - */ -.automation-form-prompt-host { - display: flex; - flex-direction: column; - min-height: 140px; - /* - * The host carries the `.interactive-session` class so the shared - * chat input CSS (chip colors, focus borders, etc.) matches what - * users see in a real chat session. That class also sets - * `max-width: 950px; margin: auto; height: 100%` (chat.css), which - * is meant for the full chat panel. In our flex column it makes - * the host take its content's min-content width and center, - * collapsing the chat input to ~chip-bar width. Re-assert - * full-width sizing so the host fills the dialog's form column. - */ - width: auto; - max-width: 100%; - margin: 0; - height: auto; - align-self: stretch; -} - -.automation-form-prompt-host .interactive-input-part { - /* - * `.interactive-input-part` in chat.css has `margin: 0 12px`, which - * (combined with the default `width: auto`) is designed to inset the - * composer inside a wider chat panel. In our dialog the prompt host - * is already the form column, so the margin pushes the composer 12px - * past the host on each side. Its right border ends up sitting - * outside the dialog. Zero out the margin so the composer aligns - * with the other form fields. - */ - margin: 0; - /* Don't let long custom-mode names or model identifiers push the - * modal wider than its declared max-width. */ - max-width: 100%; - min-width: 0; -} - -.automation-form-prompt-host .interactive-input-editor, -.automation-form-prompt-host .monaco-editor, -.automation-form-prompt-host .monaco-editor-background, -.automation-form-prompt-host .monaco-editor .margin { - background-color: var(--vscode-settings-textInputBackground, var(--vscode-input-background)); -} - -.automation-form-prompt-host .interactive-input-and-side-toolbar { - /* Allow the input column to shrink below its content's preferred - * width so flex children (chips, custom-mode names) don't push the - * modal wider than its declared max-width. */ - min-width: 0; -} - -/* - * Hide chips from the embedded chat input's primary toolbar that don't fit - * the automation dialog's reduced surface: - * - "Configure Tools" (`workbench.action.chat.configureTools`, - * `.codicon-settings-compact`) — tool selection is not relevant for a - * scheduled prompt. - * - "Add Context" (`workbench.action.chat.attachContext`, - * `.codicon-add-compact`, the `+`) — the dialog doesn't support - * attachments; the prompt is the only payload sent at run time. - * - "List MCP Servers" (`workbench.mcp.listServer`, `.codicon-server`) — - * MCP server management belongs in the live chat surface, not a one-off - * prompt definition. - * - * We hide the `.action-label` (which carries the codicon class) rather than - * the parent `.action-item`. Targeting the `.action-item` would require a - * `:has()` selector, whose invalidation bookkeeping is paid across every - * `.action-item` in the workbench (see microsoft/vscode#324985). Since - * `.action-item` has no intrinsic padding/margin (actionbar.css), hiding the - * label collapses the slot to zero width, so there is no visual gap. - * - * Known limitation: the hidden chips remain in the toolbar's arrow-key focus - * rotation (the action bar skips only disabled/separator items, not - * `display:none` ones). This is an accepted temporary trade-off — the dialog - * will migrate to `newChatInput.ts`, which builds its own toolbars and never - * renders these `MenuId.ChatInput` chips at all. - */ -.automation-form-prompt-host .chat-input-toolbar .action-label.codicon-settings-compact, -.automation-form-prompt-host .chat-input-toolbar .action-label.codicon-add-compact, -.automation-form-prompt-host .chat-input-toolbar .action-item.chat-mcp { - display: none; -} - -/* - * Suppress every inter-chip divider drawn by chat.css inside the dialog, - * in both the primary (`.chat-input-toolbar`) and secondary - * (`.chat-secondary-input-toolbar`) toolbars. CSS `+` matches by DOM - * order regardless of `display:none`, so the divider would otherwise - * appear as an orphan vertical line on whichever visible chip follows - * our hidden `+`/settings items. For example the bar drawn to the left - * of the Folder/Worktree isolation chip because it follows the Copilot - * CLI chip in the secondary toolbar (microsoft/vscode-internalbacklog#8304). The remaining visible - * chips (Agent, model, mode, isolation) already have enough intrinsic - * padding to read as separate without the divider. Dropping dividers - * entirely is simpler and more resilient than per-pair suppression rules - * that depend on DOM order. - */ -.automation-form-prompt-host .chat-input-toolbar .monaco-action-bar .actions-container > .action-item + .action-item::before, -.automation-form-prompt-host .chat-secondary-input-toolbar .monaco-action-bar .actions-container > .action-item + .action-item::before { - display: none; -} - -/* - * Cancel the dialog's `
            ` indent for the chat input toolbars. - * - * `dialog.css:120` applies `padding-inline-start: 20px` to every `
              ` - * inside `.dialog-message-container` (meant to reduce the excessive - * default indent on bulleted lists in dialog body text). The - * `monaco-action-bar`'s `.actions-container` is rendered as a `
                `, so - * the chat input's primary and secondary toolbars inherit that 20px - * left indent. This pushes the Agent / Copilot CLI chips ~20px right of - * the chat input's bottom-left corner. - * - * `actionbar.css` sets `.actions-container { padding: 0 }`, but the - * dialog rule uses a logical property (`padding-inline-start`), which - * cascades-after the physical shorthand and wins regardless of selector - * specificity. Override the logical property explicitly here. - * - * Scoped to `.automation-form-prompt-host` so the regular chat panel - * (which isn't inside a dialog) is unaffected. Any `
                  ` - * elements in the dialog's actual message body text retain the - * intended 20px indent. - */ -.automation-form-prompt-host .chat-input-toolbar .monaco-action-bar .actions-container, -.automation-form-prompt-host .chat-secondary-toolbar .monaco-action-bar .actions-container { - padding-inline-start: 0; - margin-left: 0; -} - -/* - * Sessions-layer workspace picker visual override. - * - * Two cascading sources need to be defeated to match Mode/Model: - * - * 1. `.sessions-chat-picker-slot.sessions-chat-workspace-picker - * .action-label` (sessions-layer `chatWidget.css:244-269`) sets a - * welcome-flow visual: label 18px, inner span 18px, icons 16px, - * chevron, generous padding, `color: var(--vscode-foreground)`. - * - * 2. `.monaco-action-bar .action-label` (actionbar.css:48) sets the - * base toolbar font-size to **11px**. This is what Mode/Model - * inherit. They don't set font-size themselves. The action bar - * base rule wins for their `.action-label`. The workspace picker's - * own sessions-layer rule (source 1) overrides that base, so we - * need to put the 11px back explicitly. `font-size: inherit` - * does NOT work because none of `.sessions-chat-picker-slot`'s - * ancestors carry an 11px font-size. The 11px lives on sibling - * `.action-label` elements via `.monaco-action-bar .action-label`, - * not on an ancestor we can inherit from. - * - * Result: match the 11px base, neutralize the welcome-flow oversizing, - * keep the Mode/Model color (`var(--vscode-icon-foreground)`), match - * the chip metrics from `.chat-secondary-toolbar .chat-input-picker-item - * .action-label` (chat.css:1728), and hide the chevron (the neighboring - * secondary-toolbar pickers don't render one). - * - * Scoped to `.automation-form-prompt-host` so the welcome view's - * standalone workspace picker keeps its larger sizing. - */ -.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label { - height: 16px; - padding: 3px 6px; - font-size: 11px; - line-height: normal; - color: var(--vscode-icon-foreground); -} - -.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label .sessions-chat-dropdown-label { - font-size: inherit; - line-height: inherit; -} - -.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label > .codicon:not(.sessions-chat-dropdown-chevron) { - font-size: 12px; -} - -/* - * The neighboring secondary-toolbar pickers don't render a dropdown - * chevron. They are recognized as chips by their padding + hover. Hide - * the chevron that `_renderTriggerLabel` always appends so the - * workspace chip reads the same way. - */ -.automation-form-prompt-host .chat-secondary-toolbar .sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label .sessions-chat-dropdown-chevron { - display: none; -} - -.automation-form-row { - display: flex; - flex-direction: column; - gap: 4px; -} - -.automation-form-row.automation-form-checkbox-row { - flex-direction: row; - align-items: center; - gap: 8px; - margin-top: 2px; - padding-top: 10px; - border-top: 1px solid var(--vscode-widget-border, transparent); -} - -.automation-form-row.automation-form-checkbox-row > .monaco-checkbox { - margin-right: 0; -} - -/* - * Lay the Schedule / Time / Day controls along a single horizontal axis. - * Each control lives in its own `.automation-form-schedule-group` - * (label-above-control flex column). Time and Day join the row only - * when the interval is daily or weekly; flex-wrap keeps the layout - * stable if a translation pushes the row past the dialog width. - */ -.automation-form-row.automation-form-schedule-row { - flex-direction: row; - align-items: flex-end; - flex-wrap: wrap; - gap: 12px; -} - -.automation-form-schedule-group { - display: flex; - flex-direction: column; - gap: 4px; - /* - * Each visible group splits the schedule row equally. Hidden - * groups (`display: none` from `applyIntervalVisibility`) drop out - * of flex distribution, so: - * - manual / hourly → Schedule fills 100% of the row - * - daily → Schedule + Time split 50/50 - * - weekly → Schedule + Time + Day split 33/33/33 - * `min-width: 0` lets the contained SelectBox shrink past its - * intrinsic content width when the row is narrow, before the - * row's `flex-wrap: wrap` kicks in as a final safety net. - */ - flex: 1 1 0; - min-width: 0; -} - -/* SelectBox container sizing. Give Schedule / Time / Day the full - * width of their parent group (which itself flex-shares the row). - * - * `.monaco-select-box` ships with no intrinsic padding or height (the - * only baked-in metrics live in the `.monaco-action-bar .action-item` - * selector, which we're not inside of). Mirror the standalone-form - * pattern used by the settings editor (`settingsEditor2.css:689-694`). - * It uses `height: 26px` and `padding: 2px 6px`. This makes the dropdowns match the - * Name InputBox above them instead of reading as toolbar widgets - * crammed into a form. */ -.automation-form-schedule-select-container { - display: flex; - min-width: 0; - width: 100%; -} - -.automation-form-schedule-select-container .monaco-select-box { - height: 26px; - padding: 2px 8px; -} - -/* - * Form-row labels read as section titles ("Name", "Schedule", "Time", - * "Day of week", "Prompt"). Mirror the established agents - * design-system section-label pattern (see `.overview-section - * .section-label` at line ~734: label1 / fontWeight-medium / - * foreground) so the labels carry visual weight as section headings - * and clearly out-rank their controls. - * - * `margin-bottom` adds breathing room between the title text and - * its control so each form row reads as a discrete section. - */ -.automation-form-label { - font-size: var(--vscode-agents-fontSize-label1); - font-weight: var(--vscode-agents-fontWeight-semiBold); - color: var(--vscode-foreground); - line-height: 1.4; - margin-bottom: 2px; - cursor: default; -} - -.automation-form-hint { - font-size: var(--vscode-agents-fontSize-body2, 11px); - color: var(--vscode-descriptionForeground); - line-height: 1.4; - min-height: 1em; -} - -.automation-form-checkbox-label { - font-size: var(--vscode-agents-fontSize-body1, 13px); - color: var(--vscode-foreground); - cursor: pointer; -} - -/* - * Host for the native `InputBox` widget. The widget owns its own - * `` and styling (border, padding, focus ring); the host just - * lets it grow to the row width. - */ -.automation-form-input-host { - display: flex; - width: 100%; -} - -.automation-form-input-host > .monaco-inputbox { - flex: 1 1 auto; - min-width: 0; -} - -.automation-form-input, -.automation-form-select, -.automation-form-textarea { - font-family: inherit; - font-size: var(--vscode-agents-fontSize-body1, 13px); - color: var(--vscode-settings-textInputForeground, var(--vscode-input-foreground)); - background-color: var(--vscode-settings-textInputBackground, var(--vscode-input-background)); - border: 1px solid var(--vscode-settings-textInputBorder, var(--vscode-input-border, var(--vscode-contrastBorder, transparent))); - border-radius: 2px; - padding: 4px 6px; - box-sizing: border-box; - width: 100%; - min-height: 26px; -} - -.automation-form-select { - color: var(--vscode-settings-dropdownForeground, var(--vscode-dropdown-foreground)); - background-color: var(--vscode-settings-dropdownBackground, var(--vscode-dropdown-background)); - border-color: var(--vscode-settings-dropdownBorder, var(--vscode-dropdown-border, var(--vscode-contrastBorder, transparent))); -} - -.automation-form-textarea { - resize: vertical; - min-height: 60px; - line-height: 1.4; -} - -.automation-form-input:focus, -.automation-form-select:focus, -.automation-form-textarea:focus { - outline: 1px solid var(--vscode-focusBorder); - outline-offset: -1px; - border-color: var(--vscode-focusBorder); -} - -.automation-form-empty { - font-size: 12px; - color: var(--vscode-errorForeground); - padding: 6px 0; -} - -/* - * Folder picker. This uses the shared `WorkspacePicker` from the sessions - * layer (see `sessionWorkspacePicker.ts`). The picker renders an - * `` chip trigger inside - * `.sessions-chat-picker-slot.sessions-chat-workspace-picker`. We restyle - * it to fit the dialog's compact, 13px form scale. The upstream styling - * lives in `sessions/contrib/chat/browser/media/chatWidget.css` and is - * sized for the new-session "hero" UI (18px font, larger chip) which is - * too large for our modal. - * - * The dropdown content itself renders through `IActionWidgetService`, - * which has its own global CSS. No overrides needed here. - */ -/* - * Isolation + branch group: parented into the chat input's - * `.chat-secondary-toolbar` so it sits at the bottom-right of the chat - * input, sharing a row with `Copilot CLI | Default Permissions`. - * `margin-left: auto` pushes the group to the right edge within that - * flex row. - * - * Visual styling mirrors the new-session view's - * `.new-chat-bottom-container` chip vocabulary (see - * `sessions/contrib/chat/browser/media/chatWidget.css` lines 178-209): - * compact label text and codicons, icon-foreground color, no border. - */ -.automation-form-prompt-host .chat-secondary-toolbar .automation-form-isolation-group { - display: inline-flex; - align-items: center; - margin-left: auto; - min-width: 0; - gap: var(--vscode-spacing-size60); -} - -.automation-form-prompt-host .automation-form-branch-picker-slot { - display: inline-flex; - min-width: 0; -} - -.automation-form-prompt-host .automation-form-branch-slot { - display: inline-flex; - align-items: center; - gap: var(--vscode-spacing-size40); - height: 16px; - padding: var(--vscode-spacing-size20) var(--vscode-spacing-size60); - font-size: var(--vscode-agents-fontSize-label2); - color: var(--vscode-icon-foreground); - background: transparent; - border: none; - min-width: 0; - border-radius: var(--vscode-cornerRadius-small); - touch-action: manipulation; -} - -.automation-form-prompt-host .automation-form-branch-slot > .codicon { - font-size: var(--vscode-codiconFontSize-compact); - color: var(--vscode-icon-foreground); - flex-shrink: 0; -} - -.automation-form-prompt-host .automation-form-branch-slot:not(.branch-picker-disabled) { - cursor: pointer; -} - -.automation-form-prompt-host .automation-form-branch-slot:not(.branch-picker-disabled):hover { - background-color: var(--vscode-toolbar-hoverBackground); - color: var(--vscode-foreground); -} - -.automation-form-prompt-host .automation-form-branch-slot:focus-visible { - outline: var(--vscode-strokeThickness) solid var(--vscode-focusBorder); - outline-offset: calc(-1 * var(--vscode-strokeThickness)); -} - -.automation-form-prompt-host .automation-form-branch-slot.branch-picker-disabled, -.automation-form-prompt-host .automation-form-branch-slot.branch-picker-disabled > .codicon { - cursor: default; - color: var(--vscode-descriptionForeground); -} - -.automation-form-prompt-host .automation-form-branch-name { - font-size: var(--vscode-agents-fontSize-label2); -} - -.automation-form-prompt-host .automation-form-branch-name { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.automation-form-prompt-host .chat-secondary-toolbar .automation-form-harness-chip { - display: inline-flex; - align-items: center; - gap: 4px; - height: 16px; - padding: 3px 6px; - font-size: 11px; - color: var(--vscode-icon-foreground); - background: transparent; - border: none; - opacity: 0.6; - cursor: default; -} - -.automation-form-prompt-host .automation-form-harness-chip > .codicon { - font-size: 12px; - flex-shrink: 0; -} - -.automation-form-prompt-host .automation-form-harness-label { - font-size: 11px; -} - -/* Missing-branch state (no folder / no git repo / detached / empty): use - * description-foreground + italic so it visually reads as "informational - * placeholder, not a real ref name". */ -.automation-form-prompt-host .automation-form-branch-slot.branch-picker-missing, -.automation-form-prompt-host .automation-form-branch-slot.branch-picker-missing > .codicon { - color: var(--vscode-descriptionForeground); -} - -.automation-form-prompt-host .automation-form-branch-slot.branch-picker-missing .automation-form-branch-name { - font-style: italic; -} - -.hc-black .automation-form-prompt-host .automation-form-branch-slot:focus-visible, -.hc-light .automation-form-prompt-host .automation-form-branch-slot:focus-visible { - outline-color: var(--vscode-contrastActiveBorder); -} From 9dc57dcf0f6d9a947f06d30e1f6564c93ebf74f1 Mon Sep 17 00:00:00 2001 From: Ben Villalobos <4691428+benvillalobos@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:27:37 -0700 Subject: [PATCH 06/17] Fix pointer cursor on automation dialog form labels Schedule, Time, Day of week, and Prompt labels were rendered as