From ad047e27d5989a950b5105c8024169ba0ddd6c33 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 4 Oct 2024 19:47:00 +0200 Subject: [PATCH] tweak editor inline chat visuals (#230521) ...to be more aligned with other peek experiences, like peek ref or edit breakpoint --- .../contrib/zoneWidget/browser/zoneWidget.ts | 2 +- .../contrib/chat/browser/chatWidget.ts | 4 +- .../inlineChat/browser/inlineChatWidget.ts | 21 ++++++---- .../browser/inlineChatZoneWidget.ts | 27 +++++++++--- .../inlineChat/browser/media/inlineChat.css | 41 +++++++++++++++---- 5 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index e1d387deb12..a6a07eb284d 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -356,7 +356,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this._positionMarkerId.clear(); } - private _decoratingElementsHeight(): number { + protected _decoratingElementsHeight(): number { const lineHeight = this.editor.getOption(EditorOption.lineHeight); let result = 0; diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 68af7274f0c..fd312a75898 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -984,7 +984,7 @@ export class ChatWidget extends Disposable implements IChatWidget { const inputPartHeight = this.inputPart.inputPartHeight; const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight; - const listHeight = height - inputPartHeight; + const listHeight = Math.max(0, height - inputPartHeight); this.tree.layout(listHeight, width); this.tree.getHTMLElement().style.height = `${listHeight}px`; @@ -998,7 +998,7 @@ export class ChatWidget extends Disposable implements IChatWidget { revealLastElement(this.tree); } - this.listContainer.style.height = `${height - inputPartHeight}px`; + this.listContainer.style.height = `${listHeight}px`; this._onDidChangeHeight.fire(height); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index b840b30ad79..5af1a5773f0 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -75,6 +75,8 @@ export interface IInlineChatWidgetConstructionOptions { * The options for the chat widget */ chatWidgetViewOptions?: IChatWidgetViewOptions; + + inZoneWidget?: boolean; } export interface IInlineChatMessage { @@ -123,7 +125,7 @@ export class InlineChatWidget { constructor( location: IChatWidgetLocationOptions, - options: IInlineChatWidgetConstructionOptions, + private readonly _options: IInlineChatWidgetConstructionOptions, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @@ -158,7 +160,7 @@ export class InlineChatWidget { // filter responses that // - are just text edits(prevents the "Made Edits") // - are all empty - if (item.response.value.length > 0 && item.response.value.every(item => item.kind === 'textEditGroup' && options.chatWidgetViewOptions?.rendererOptions?.renderTextEditsAsSummary?.(item.uri))) { + if (item.response.value.length > 0 && item.response.value.every(item => item.kind === 'textEditGroup' && _options.chatWidgetViewOptions?.rendererOptions?.renderTextEditsAsSummary?.(item.uri))) { return false; } if (item.response.value.length === 0) { @@ -168,7 +170,7 @@ export class InlineChatWidget { } return true; }, - ...options.chatWidgetViewOptions + ..._options.chatWidgetViewOptions }, { listForeground: inlineChatForeground, @@ -178,6 +180,7 @@ export class InlineChatWidget { resultEditorBackground: editorBackground } ); + this._elements.root.classList.toggle('in-zone-widget', !!_options.inZoneWidget); this._chatWidget.render(this._elements.chatWidget); this._elements.chatWidget.style.setProperty(asCssVariableName(chatRequestBackground), asCssVariable(inlineChatBackground)); this._chatWidget.setVisible(true); @@ -237,13 +240,13 @@ export class InlineChatWidget { this._store.add(this._chatWidget.inputEditor.onDidFocusEditorWidget(() => this._ctxInputEditorFocused.set(true))); this._store.add(this._chatWidget.inputEditor.onDidBlurEditorWidget(() => this._ctxInputEditorFocused.set(false))); - const statusMenuId = options.statusMenuId instanceof MenuId ? options.statusMenuId : options.statusMenuId.menu; + const statusMenuId = _options.statusMenuId instanceof MenuId ? _options.statusMenuId : _options.statusMenuId.menu; // BUTTON bar - const statusMenuOptions = options.statusMenuId instanceof MenuId ? undefined : options.statusMenuId.options; + const statusMenuOptions = _options.statusMenuId instanceof MenuId ? undefined : _options.statusMenuId.options; const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.toolbar1, statusMenuId, { toolbarOptions: { primaryGroup: '0_main' }, - telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, + telemetrySource: _options.chatWidgetViewOptions?.menus?.telemetrySource, menuOptions: { renderShortTitle: true }, ...statusMenuOptions, }); @@ -251,8 +254,8 @@ export class InlineChatWidget { this._store.add(statusButtonBar); // secondary toolbar - const toolbar2 = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.toolbar2, options.secondaryMenuId ?? MenuId.for(''), { - telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, + const toolbar2 = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.toolbar2, _options.secondaryMenuId ?? MenuId.for(''), { + telemetrySource: _options.chatWidgetViewOptions?.menus?.telemetrySource, menuOptions: { renderShortTitle: true, shouldForwardArgs: true }, actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action instanceof MenuItemAction && action.item.id === MarkUnhelpfulActionId) { @@ -375,7 +378,7 @@ export class InlineChatWidget { } protected _getExtraHeight(): number { - return 2 /*border*/ + 4 /*shadow*/; + return this._options.inZoneWidget ? 1 : (2 /*border*/ + 4 /*shadow*/); } get value(): string { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 5e732d8a83a..c487376229c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -13,7 +13,7 @@ import { EditorLayoutInfo, EditorOption } from '../../../../editor/common/config import { Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; import { ScrollType } from '../../../../editor/common/editorCommon.js'; -import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js'; +import { IOptions, ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; @@ -26,6 +26,19 @@ import { EditorBasedInlineChatWidget } from './inlineChatWidget.js'; export class InlineChatZoneWidget extends ZoneWidget { + private static readonly _options: IOptions = { + showFrame: true, + frameWidth: 1, + // frameColor: 'var(--vscode-inlineChat-border)', + // isResizeable: true, + showArrow: false, + isAccessible: true, + className: 'inline-chat-widget', + keepEditorSelection: true, + showInHiddenAreas: true, + ordinal: 50000, + }; + readonly widget: EditorBasedInlineChatWidget; private readonly _scrollUp = this._disposables.add(new ScrollUpState(this.editor)); @@ -40,7 +53,7 @@ export class InlineChatZoneWidget extends ZoneWidget { @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configurationService: IConfigurationService, ) { - super(editor, { showFrame: false, showArrow: false, isAccessible: true, className: 'inline-chat-widget', keepEditorSelection: true, showInHiddenAreas: true, ordinal: 50000 }); + super(editor, InlineChatZoneWidget._options); this._ctxCursorPosition = CTX_INLINE_CHAT_OUTER_CURSOR_POSITION.bindTo(contextKeyService); @@ -63,6 +76,7 @@ export class InlineChatZoneWidget extends ZoneWidget { } }, secondaryMenuId: MENU_INLINE_CHAT_WIDGET_SECONDARY, + inZoneWidget: true, chatWidgetViewOptions: { menus: { telemetrySource: 'interactiveEditorWidget-toolbar', @@ -123,6 +137,9 @@ export class InlineChatZoneWidget extends ZoneWidget { } protected override _fillContainer(container: HTMLElement): void { + + container.style.setProperty('--vscode-inlineChat-background', 'var(--vscode-editor-background)'); + container.appendChild(this.widget.domNode); } @@ -140,7 +157,7 @@ export class InlineChatZoneWidget extends ZoneWidget { const chatContentHeight = this.widget.contentHeight; const editorHeight = this.editor.getLayoutInfo().height; - const contentHeight = Math.min(chatContentHeight, Math.max(this.widget.minHeight, editorHeight * 0.42)); + const contentHeight = this._decoratingElementsHeight() + Math.min(chatContentHeight, Math.max(this.widget.minHeight, editorHeight * 0.42)); const heightInLines = contentHeight / this.editor.getOption(EditorOption.lineHeight); return { linesValue: heightInLines, pixelsValue: contentHeight }; } @@ -155,8 +172,8 @@ export class InlineChatZoneWidget extends ZoneWidget { assertType(this.container); const info = this.editor.getLayoutInfo(); - const marginWithoutIndentation = info.glyphMarginWidth + info.decorationsWidth + info.lineNumbersWidth; - this.container.style.marginLeft = `${marginWithoutIndentation}px`; + const marginWithoutIndentation = info.glyphMarginWidth + info.lineNumbersWidth; + this.container.style.paddingLeft = `${marginWithoutIndentation}px`; const revealZone = this._createZoneAndScrollRestoreFn(position); super.show(position, this._computeHeight().linesValue); diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index 734a66b9966..897697cf875 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -3,13 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .zone-widget.inline-chat-widget { - z-index: 3; -} - -.monaco-workbench .zone-widget.inline-chat-widget .interactive-session { - max-width: unset; -} .monaco-workbench .inline-chat { color: inherit; @@ -21,6 +14,40 @@ position: relative; } +.monaco-workbench .zone-widget.inline-chat-widget { + z-index: 3; +} + +.monaco-workbench .zone-widget.inline-chat-widget .interactive-session { + max-width: unset; +} + +.monaco-workbench .zone-widget.inline-chat-widget .interactive-session .chat-input-container { + border-color: var(--vscode-inlineChat-border); +} + +.monaco-workbench .zone-widget.inline-chat-widget .interactive-session .chat-input-container:focus-within { + border-color: var(--vscode-focusBorder); +} + +.monaco-workbench .zone-widget.inline-chat-widget .interactive-session .chat-input-container .interactive-input-part { + padding-top: 0px +} + +.monaco-workbench .zone-widget.inline-chat-widget > .zone-widget-container { + background: var(--vscode-inlineChat-background); +} + +.monaco-workbench .zone-widget.inline-chat-widget > .zone-widget-container > .inline-chat { + color: inherit; + border-radius: unset; + border: unset; + box-shadow: unset; + background: var(--vscode-inlineChat-background); + position: relative; + outline: none; +} + .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part { padding: 4px 8px 0 8px; }