diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index c3d2db6d982..9b720b3d0cb 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -285,6 +285,7 @@ export class MenuId { static readonly ChatEditorInlineExecute = new MenuId('ChatEditorInputExecute'); static readonly ChatEditorInlineInputSide = new MenuId('ChatEditorInputSide'); static readonly InlineChatEditorAffordance = new MenuId('InlineChatEditorAffordance'); + static readonly InlineChatInput = new MenuId('InlineChatInput'); static readonly AccessibleView = new MenuId('AccessibleView'); static readonly MultiDiffEditorContent = new MenuId('MultiDiffEditorContent'); static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index c3bb43bc90b..8db06f95597 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -103,6 +103,7 @@ MenuRegistry.appendMenuItem(MenuId.InlineChatEditorAffordance, { registerAction2(InlineChatActions.StartSessionAction); registerAction2(InlineChatActions.FocusInlineChat); +registerAction2(InlineChatActions.SubmitInlineChatInputAction); const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index f7dc57db292..69d05897266 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diff import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { InlineChatController, InlineChatRunOptions } from './inlineChatController.js'; -import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_POSSIBLE, ACTION_START, CTX_INLINE_CHAT_V2_ENABLED, CTX_INLINE_CHAT_V1_ENABLED, CTX_HOVER_MODE } from '../common/inlineChat.js'; +import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_POSSIBLE, ACTION_START, CTX_INLINE_CHAT_V2_ENABLED, CTX_INLINE_CHAT_V1_ENABLED, CTX_HOVER_MODE, CTX_INLINE_CHAT_INPUT_HAS_TEXT } from '../common/inlineChat.js'; import { ctxHasEditorModification, ctxHasRequestInProgress } from '../../chat/browser/chatEditing/chatEditingEditorContextKeys.js'; import { localize, localize2 } from '../../../../nls.js'; import { Action2, IAction2Options, MenuId } from '../../../../platform/actions/common/actions.js'; @@ -330,3 +330,28 @@ export class UndoAndCloseSessionAction2 extends KeepOrUndoSessionAction { }); } } + +export class SubmitInlineChatInputAction extends AbstractInlineChatAction { + + constructor() { + super({ + id: 'inlineChat.submitInput', + title: localize2('submitInput', "Send"), + icon: Codicon.send, + precondition: CTX_INLINE_CHAT_INPUT_HAS_TEXT, + menu: [{ + id: MenuId.InlineChatInput, + group: '0_main', + order: 1, + }] + }); + } + + override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: unknown[]): void { + const value = ctrl.inputWidget.value; + if (value) { + ctrl.inputWidget.hide(); + ctrl.run({ message: value, autoSend: true }); + } + } +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 1790933cbb8..79335bf93cd 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -118,6 +118,7 @@ export class InlineChatController implements IEditorContribution { private readonly _renderMode: IObservable<'zone' | 'hover'>; private readonly _zone: Lazy; private readonly _gutterIndicator: InlineChatAffordance; + private readonly _inputWidget: InlineChatInputWidget; private readonly _currentSession: IObservable; @@ -129,6 +130,10 @@ export class InlineChatController implements IEditorContribution { return Boolean(this._currentSession.get()); } + get inputWidget(): InlineChatInputWidget { + return this._inputWidget; + } + constructor( private readonly _editor: ICodeEditor, @IInstantiationService private readonly _instaService: IInstantiationService, @@ -152,7 +157,7 @@ export class InlineChatController implements IEditorContribution { const notebookAgentConfig = observableConfigValue(InlineChatConfigKeys.notebookAgent, false, this._configurationService); this._renderMode = observableConfigValue(InlineChatConfigKeys.RenderMode, 'zone', this._configurationService); - const overlayWidget = this._store.add(this._instaService.createInstance(InlineChatInputWidget, editorObs)); + const overlayWidget = this._inputWidget = this._store.add(this._instaService.createInstance(InlineChatInputWidget, editorObs)); const sessionOverlayWidget = this._store.add(this._instaService.createInstance(InlineChatSessionOverlayWidget, editorObs)); this._gutterIndicator = this._store.add(this._instaService.createInstance(InlineChatAffordance, this._editor, overlayWidget)); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatOverlayWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatOverlayWidget.ts index 0284206c2bf..1789bec7a6e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatOverlayWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatOverlayWidget.ts @@ -5,10 +5,8 @@ import './media/inlineChatOverlayWidget.css'; import * as dom from '../../../../base/browser/dom.js'; +import { DEFAULT_FONT_FAMILY } from '../../../../base/browser/fonts.js'; import { renderAsPlaintext } from '../../../../base/browser/markdownRenderer.js'; -import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { IAction, Separator } from '../../../../base/common/actions.js'; -import { ActionBar, ActionsOrientation } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; @@ -23,20 +21,18 @@ import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../editor/b import { IModelService } from '../../../../editor/common/services/model.js'; import { localize } from '../../../../nls.js'; import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; -import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { ChatEditingAcceptRejectActionViewItem } from '../../chat/browser/chatEditing/chatEditingEditorOverlay.js'; -import { ACTION_START } from '../common/inlineChat.js'; +import { ACTION_START, CTX_INLINE_CHAT_INPUT_HAS_TEXT } from '../common/inlineChat.js'; import { StickyScrollController } from '../../../../editor/contrib/stickyScroll/browser/stickyScrollController.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { getSimpleEditorOptions } from '../../codeEditor/browser/simpleEditorOptions.js'; import { PlaceholderTextContribution } from '../../../../editor/contrib/placeholderText/browser/placeholderTextContribution.js'; -import { InlineChatRunOptions } from './inlineChatController.js'; import { IInlineChatSession2 } from './inlineChatSessionService.js'; -import { Position } from '../../../../editor/common/core/position.js'; import { CancelChatActionId } from '../../chat/browser/actions/chatExecuteActions.js'; import { assertType } from '../../../../base/common/types.js'; @@ -46,8 +42,9 @@ import { assertType } from '../../../../base/common/types.js'; export class InlineChatInputWidget extends Disposable { private readonly _domNode: HTMLElement; + private readonly _container: HTMLElement; private readonly _inputContainer: HTMLElement; - private readonly _actionBar: ActionBar; + private readonly _toolbarContainer: HTMLElement; private readonly _input: IActiveCodeEditor; private readonly _position = observableValue(this, null); readonly position: IObservable = this._position; @@ -55,7 +52,7 @@ export class InlineChatInputWidget extends Disposable { private readonly _showStore = this._store.add(new DisposableStore()); private readonly _stickyScrollHeight: IObservable; - private _inlineStartAction: IAction | undefined; + private readonly _layoutData: IObservable<{ totalWidth: number; toolbarWidth: number; height: number }>; private _anchorLineNumber: number = 0; private _anchorLeft: number = 0; private _anchorAbove: boolean = false; @@ -64,8 +61,8 @@ export class InlineChatInputWidget extends Disposable { constructor( private readonly _editorObs: ObservableCodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IMenuService private readonly _menuService: IMenuService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @ICommandService private readonly _commandService: ICommandService, @IInstantiationService instantiationService: IInstantiationService, @IModelService modelService: IModelService, @IConfigurationService configurationService: IConfigurationService, @@ -75,17 +72,19 @@ export class InlineChatInputWidget extends Disposable { // Create container this._domNode = dom.$('.inline-chat-gutter-menu'); + // Create inner container (background + focus border) + this._container = dom.append(this._domNode, dom.$('.inline-chat-gutter-container')); + // Create input editor container - this._inputContainer = dom.append(this._domNode, dom.$('.input')); - this._inputContainer.style.width = '200px'; - this._inputContainer.style.height = '26px'; - this._inputContainer.style.display = 'flex'; - this._inputContainer.style.alignItems = 'center'; - this._inputContainer.style.justifyContent = 'center'; + this._inputContainer = dom.append(this._container, dom.$('.input')); + + // Create toolbar container + this._toolbarContainer = dom.append(this._container, dom.$('.toolbar')); // Create editor options const options = getSimpleEditorOptions(configurationService); - options.wordWrap = 'on'; + options.wordWrap = 'off'; + options.wrappingStrategy = 'advanced'; options.lineNumbers = 'off'; options.glyphMargin = false; options.lineDecorationsWidth = 0; @@ -94,6 +93,10 @@ export class InlineChatInputWidget extends Disposable { options.minimap = { enabled: false }; options.scrollbar = { vertical: 'auto', horizontal: 'hidden', alwaysConsumeMouseWheel: true, verticalSliderSize: 6 }; options.renderLineHighlight = 'none'; + options.fontFamily = DEFAULT_FONT_FAMILY; + options.fontSize = 12; + options.lineHeight = 18; + options.cursorWidth = 1; const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { isSimpleWidget: true, @@ -107,10 +110,79 @@ export class InlineChatInputWidget extends Disposable { const model = this._store.add(modelService.createModel('', null, URI.parse(`gutter-input:${Date.now()}`), true)); this._input.setModel(model); + // Create toolbar + const toolbar = this._store.add(instantiationService.createInstance(MenuWorkbenchToolBar, this._toolbarContainer, MenuId.InlineChatInput, { + telemetrySource: 'inlineChatInput.toolbar', + hiddenItemStrategy: HiddenItemStrategy.NoHide, + toolbarOptions: { + primaryGroup: () => true, + }, + menuOptions: { shouldForwardArgs: true }, + })); + // Initialize sticky scroll height observable const stickyScrollController = StickyScrollController.get(this._editorObs.editor); this._stickyScrollHeight = stickyScrollController ? observableFromEvent(stickyScrollController.onDidChangeStickyScrollHeight, () => stickyScrollController.stickyScrollWidgetHeight) : constObservable(0); + // Track toolbar width changes + const toolbarWidth = observableValue(this, 0); + const resizeObserver = new dom.DisposableResizeObserver(() => { + toolbarWidth.set(dom.getTotalWidth(toolbar.getElement()), undefined); + }); + this._store.add(resizeObserver); + this._store.add(resizeObserver.observe(toolbar.getElement())); + + // Compute min and max widget width based on editor content width + const maxWidgetWidth = derived(r => { + const layoutInfo = this._editorObs.layoutInfo.read(r); + return Math.max(0, Math.round(layoutInfo.contentWidth * 0.70)); + }); + const minWidgetWidth = derived(r => { + const layoutInfo = this._editorObs.layoutInfo.read(r); + return Math.max(0, Math.round(layoutInfo.contentWidth * 0.33)); + }); + + const contentWidth = observableFromEvent(this, this._input.onDidChangeModelContent, () => this._input.getContentWidth()); + const contentHeight = observableFromEvent(this, this._input.onDidContentSizeChange, () => this._input.getContentHeight()); + + this._layoutData = derived(r => { + + const totalWidth = contentWidth.read(r) + toolbarWidth.read(r); + const minWidth = minWidgetWidth.read(r); + const maxWidth = maxWidgetWidth.read(r); + const clampedWidth = this._input.getOption(EditorOption.wordWrap) === 'on' + ? maxWidth + : Math.max(minWidth, Math.min(totalWidth, maxWidth)); + + const lineHeight = this._input.getOption(EditorOption.lineHeight); + const clampedHeight = Math.min(contentHeight.read(r), (3 * lineHeight)); + + if (totalWidth > clampedWidth) { + // enable word wrap + this._input.updateOptions({ wordWrap: 'on', }); + } + + return { + toolbarWidth: toolbarWidth.read(r), + totalWidth: clampedWidth, + height: clampedHeight + }; + }); + + // Update container width and editor layout when width changes + this._store.add(autorun(r => { + const { toolbarWidth, totalWidth, height } = this._layoutData.read(r); + + const inputWidth = totalWidth - toolbarWidth; + this._container.style.width = `${totalWidth}px`; + this._inputContainer.style.width = `${inputWidth}px`; + this._input.layout({ width: inputWidth, height }); + })); + + // Toggle focus class on the container + this._store.add(this._input.onDidFocusEditorText(() => this._container.classList.add('focused'))); + this._store.add(this._input.onDidBlurEditorText(() => this._container.classList.remove('focused'))); + // Update placeholder based on selection state this._store.add(autorun(r => { const selection = this._editorObs.cursorSelection.read(r); @@ -122,71 +194,38 @@ export class InlineChatInputWidget extends Disposable { this._input.updateOptions({ placeholder: this._keybindingService.appendKeybinding(placeholderText, ACTION_START) }); })); - // Listen to content size changes and resize the input editor (max 3 lines) - this._store.add(this._input.onDidContentSizeChange(e => { - if (e.contentHeightChanged) { - this._updateInputHeight(e.contentHeight); - } - })); - // Handle Enter key to submit and ArrowDown to focus action bar + // Track input text for context key and adjust width based on content + const inputHasText = CTX_INLINE_CHAT_INPUT_HAS_TEXT.bindTo(this._contextKeyService); + this._store.add(this._input.onDidChangeModelContent(() => { + inputHasText.set(this._input.getModel().getValue().trim().length > 0); + })); + this._store.add(toDisposable(() => inputHasText.reset())); + + // Handle Enter key to submit this._store.add(this._input.onKeyDown(e => { if (e.keyCode === KeyCode.Enter && !e.shiftKey) { - const value = this._input.getModel().getValue() ?? ''; - // TODO@jrieken this isn't nice - if (this._inlineStartAction && value) { - e.preventDefault(); - e.stopPropagation(); - this._actionBar.actionRunner.run( - this._inlineStartAction, - { message: value, autoSend: true } satisfies InlineChatRunOptions - ); - } + e.preventDefault(); + e.stopPropagation(); + this._commandService.executeCommand('inlineChat.submitInput'); } else if (e.keyCode === KeyCode.Escape) { // Hide overlay if input is empty const value = this._input.getModel().getValue() ?? ''; if (!value) { e.preventDefault(); e.stopPropagation(); - this._hide(); - } - } else if (e.keyCode === KeyCode.DownArrow) { - // Focus first action bar item when at the end of the input - const inputModel = this._input.getModel(); - const position = this._input.getPosition(); - const lastLineNumber = inputModel.getLineCount(); - const lastLineMaxColumn = inputModel.getLineMaxColumn(lastLineNumber); - if (Position.equals(position, new Position(lastLineNumber, lastLineMaxColumn))) { - e.preventDefault(); - e.stopPropagation(); - this._actionBar.focus(); + this.hide(); } } })); - // Create vertical action bar - this._actionBar = this._store.add(new ActionBar(this._domNode, { - orientation: ActionsOrientation.VERTICAL, - preventLoopNavigation: true, - })); - - // Handle ArrowUp on first action bar item to focus input editor - this._store.add(dom.addDisposableListener(this._actionBar.domNode, 'keydown', e => { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.UpArrow) && this._actionBar.isFocused(this._actionBar.viewItems.findIndex(item => item.action.id !== Separator.ID))) { - event.preventDefault(); - event.stopPropagation(); - this._input.focus(); - } - }, true)); - // Track focus - hide when focus leaves const focusTracker = this._store.add(dom.trackFocus(this._domNode)); - this._store.add(focusTracker.onDidBlur(() => this._hide())); + this._store.add(focusTracker.onDidBlur(() => this.hide())); + } - // Handle action bar cancel (Escape key) - this._store.add(this._actionBar.onDidCancel(() => this._hide())); - this._store.add(this._actionBar.onWillRun(() => this._hide())); + get value(): string { + return this._input.getModel().getValue(); } /** @@ -199,11 +238,8 @@ export class InlineChatInputWidget extends Disposable { this._showStore.clear(); // Clear input state + this._input.updateOptions({ wordWrap: 'off' }); this._input.getModel().setValue(''); - this._updateInputHeight(this._input.getContentHeight()); - - // Refresh actions from menu - this._refreshActions(); // Store anchor info for scroll updates this._anchorLineNumber = lineNumber; @@ -234,7 +270,7 @@ export class InlineChatInputWidget extends Disposable { ); const hasContent = !!this._input.getModel().getValue(); if (!isLineVisible && !hasContent) { - this._hide(); + this.hide(); } else { this._updatePosition(); } @@ -277,7 +313,7 @@ export class InlineChatInputWidget extends Disposable { /** * Hide the widget (removes from editor but does not dispose). */ - private _hide(): void { + hide(): void { // Focus editor if focus is still within the editor's DOM const editorDomNode = this._editorObs.editor.getDomNode(); if (editorDomNode && dom.isAncestorOfActiveElement(editorDomNode)) { @@ -286,35 +322,6 @@ export class InlineChatInputWidget extends Disposable { this._position.set(null, undefined); this._showStore.clear(); } - - private _refreshActions(): void { - // Clear existing actions - this._actionBar.clear(); - this._inlineStartAction = undefined; - - // Get fresh actions from menu - const actions = getFlatActionBarActions(this._menuService.getMenuActions(MenuId.ChatEditorInlineGutter, this._contextKeyService, { shouldForwardArgs: true })); - - // Set actions with keybindings (skip ACTION_START since we have the input editor) - for (const action of actions) { - if (action.id === ACTION_START) { - this._inlineStartAction = action; - continue; - } - const keybinding = this._keybindingService.lookupKeybinding(action.id)?.getLabel(); - this._actionBar.push(action, { icon: false, label: true, keybinding }); - } - } - - private _updateInputHeight(contentHeight: number): void { - const lineHeight = this._input.getOption(EditorOption.lineHeight); - const maxHeight = 3 * lineHeight; - const clampedHeight = Math.min(contentHeight, maxHeight); - const containerPadding = 8; - - this._inputContainer.style.height = `${clampedHeight + containerPadding}px`; - this._input.layout({ width: 200, height: clampedHeight }); - } } /** diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index 2215f0f84dd..73344bbf55f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -324,17 +324,15 @@ /* Gutter menu overlay widget */ .inline-chat-gutter-menu { - background: var(--vscode-menu-background); + background: var(--vscode-panel-background); border: 1px solid var(--vscode-menu-border, var(--vscode-widget-border)); - border-radius: 4px; + border-radius: 5px; box-shadow: 0 2px 8px var(--vscode-widget-shadow); - padding: 4px 0; - min-width: 160px; z-index: 10000; } .inline-chat-gutter-menu .input { - padding: 0 8px; + padding: 0 6px; } .inline-chat-gutter-menu .monaco-action-bar.vertical .action-item { diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatEditorAffordance.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatEditorAffordance.css index 1cd7c1d50cb..b13f086fd95 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatEditorAffordance.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatEditorAffordance.css @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ .inline-chat-content-widget { - background-color: var(--vscode-editor-background); - padding: 2px; - border-radius: 8px; + background-color: var(--vscode-panel-background); + padding: 0 1px; + border-radius: 5px; display: flex; align-items: center; box-shadow: 0 4px 8px var(--vscode-widget-shadow); @@ -14,6 +14,7 @@ min-width: var(--vscode-inline-chat-affordance-height); min-height: var(--vscode-inline-chat-affordance-height); line-height: var(--vscode-inline-chat-affordance-height); + border: 1px solid var(--vscode-input-border, transparent); } .inline-chat-content-widget .action-label.codicon.codicon-light-bulb, diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatOverlayWidget.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatOverlayWidget.css index 1af3ff339a1..f88c3626fad 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatOverlayWidget.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatOverlayWidget.css @@ -7,8 +7,39 @@ transition: top 100ms; } -.inline-chat-gutter-menu .input .monaco-editor-background { - background-color: var(--vscode-menu-background); +.inline-chat-gutter-container { + box-sizing: border-box; + display: flex; + align-items: center; + margin: 2px; + background-color: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border, transparent); + border-radius: 5px; + box-shadow: 0 2px 8px var(--vscode-widget-shadow); + overflow: hidden; +} + +.inline-chat-gutter-container.focused { + border-color: var(--vscode-focusBorder); +} + +.inline-chat-gutter-container > .input { + flex: 1; + min-width: 0; +} + +.inline-chat-gutter-container > .input .monaco-editor-background { + background-color: var(--vscode-input-background); +} + +.inline-chat-gutter-container > .toolbar { + display: flex; + align-items: center; + padding: 0 4px; +} + +.inline-chat-gutter-container > .toolbar .monaco-action-bar .actions-container { + gap: 2px; } .inline-chat-session-overlay-widget { diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index e8866c447f3..99591004f82 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -108,6 +108,7 @@ export const CTX_INLINE_CHAT_FOCUSED = new RawContextKey('inlineChatFoc export const CTX_INLINE_CHAT_EDITING = new RawContextKey('inlineChatEditing', true, localize('inlineChatEditing', "Whether the user is currently editing or generating code in the inline chat")); export const CTX_INLINE_CHAT_RESPONSE_FOCUSED = new RawContextKey('inlineChatResponseFocused', false, localize('inlineChatResponseFocused', "Whether the interactive widget's response is focused")); export const CTX_INLINE_CHAT_EMPTY = new RawContextKey('inlineChatEmpty', false, localize('inlineChatEmpty', "Whether the interactive editor input is empty")); +export const CTX_INLINE_CHAT_INPUT_HAS_TEXT = new RawContextKey('inlineChatInputHasText', false, localize('inlineChatInputHasText', "Whether the inline chat input widget has text")); export const CTX_INLINE_CHAT_INNER_CURSOR_FIRST = new RawContextKey('inlineChatInnerCursorFirst', false, localize('inlineChatInnerCursorFirst', "Whether the cursor of the iteractive editor input is on the first line")); export const CTX_INLINE_CHAT_INNER_CURSOR_LAST = new RawContextKey('inlineChatInnerCursorLast', false, localize('inlineChatInnerCursorLast', "Whether the cursor of the iteractive editor input is on the last line")); export const CTX_INLINE_CHAT_OUTER_CURSOR_POSITION = new RawContextKey<'above' | 'below' | ''>('inlineChatOuterCursorPosition', '', localize('inlineChatOuterCursorPosition', "Whether the cursor of the outer editor is above or below the interactive editor input"));