Path: blob/main/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts
5241 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { $, Dimension, getActiveElement, getTotalHeight, getWindow, h, reset, trackFocus } from '../../../../base/browser/dom.js';6import { IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js';7import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js';8import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js';9import { IAction } from '../../../../base/common/actions.js';10import { Emitter, Event } from '../../../../base/common/event.js';11import { MarkdownString } from '../../../../base/common/htmlContent.js';12import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';13import { autorun, IObservable, observableValue } from '../../../../base/common/observable.js';14import { isEqual } from '../../../../base/common/resources.js';15import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';16import { Selection } from '../../../../editor/common/core/selection.js';17import { ICodeEditorViewState } from '../../../../editor/common/editorCommon.js';18import { ITextModel } from '../../../../editor/common/model.js';19import { ITextModelService } from '../../../../editor/common/services/resolverService.js';20import { localize } from '../../../../nls.js';21import { IAccessibleViewService } from '../../../../platform/accessibility/browser/accessibleView.js';22import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js';23import { IWorkbenchButtonBarOptions, MenuWorkbenchButtonBar } from '../../../../platform/actions/browser/buttonbar.js';24import { createActionViewItem, IMenuEntryActionViewItemOptions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';25import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js';26import { MenuId, MenuItemAction } from '../../../../platform/actions/common/actions.js';27import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';28import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';29import { IHoverService } from '../../../../platform/hover/browser/hover.js';30import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';31import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';32import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';33import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js';34import { IMarkdownRendererService } from '../../../../platform/markdown/browser/markdownRenderer.js';35import product from '../../../../platform/product/common/product.js';36import { asCssVariable, asCssVariableName, editorBackground, inputBackground } from '../../../../platform/theme/common/colorRegistry.js';37import { EDITOR_DRAG_AND_DROP_BACKGROUND } from '../../../common/theme.js';38import { IChatEntitlementService } from '../../../services/chat/common/chatEntitlementService.js';39import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js';40import { AccessibilityCommandId } from '../../accessibility/common/accessibilityCommands.js';41import { MarkUnhelpfulActionId } from '../../chat/browser/actions/chatTitleActions.js';42import { IChatWidgetViewOptions } from '../../chat/browser/chat.js';43import { ChatVoteDownButton } from '../../chat/browser/widget/chatListRenderer.js';44import { ChatWidget, IChatWidgetLocationOptions } from '../../chat/browser/widget/chatWidget.js';45import { chatRequestBackground } from '../../chat/common/widget/chatColors.js';46import { ChatContextKeys } from '../../chat/common/actions/chatContextKeys.js';47import { IChatModel } from '../../chat/common/model/chatModel.js';48import { ChatMode } from '../../chat/common/chatModes.js';49import { ChatAgentVoteDirection, IChatService } from '../../chat/common/chatService/chatService.js';50import { isResponseVM } from '../../chat/common/model/chatViewModel.js';51import * as marked from '../../../../base/common/marked/marked.js';52import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED, inlineChatBackground, inlineChatForeground } from '../common/inlineChat.js';53import './media/inlineChat.css';5455export interface InlineChatWidgetViewState {56editorViewState: ICodeEditorViewState;57input: string;58placeholder: string;59}6061export interface IInlineChatWidgetConstructionOptions {6263/**64* The menu that rendered as button bar, use for accept, discard etc65*/66statusMenuId: MenuId | { menu: MenuId; options: IWorkbenchButtonBarOptions };6768secondaryMenuId?: MenuId;6970/**71* The options for the chat widget72*/73chatWidgetViewOptions?: IChatWidgetViewOptions;7475inZoneWidget?: boolean;76}7778export class InlineChatWidget {7980protected readonly _elements = h(81'div.inline-chat@root',82[83h('div.chat-widget@chatWidget'),84h('div.accessibleViewer@accessibleViewer'),85h('div.status@status', [86h('div.label.info.hidden@infoLabel'),87h('div.actions.hidden@toolbar1'),88h('div.label.status.hidden@statusLabel'),89h('div.actions.secondary.hidden@toolbar2'),90h('div.label.disclaimer.hidden@disclaimerLabel'),91]),92]93);9495protected readonly _store = new DisposableStore();9697private readonly _ctxInputEditorFocused: IContextKey<boolean>;98private readonly _ctxResponseFocused: IContextKey<boolean>;99100private readonly _chatWidget: ChatWidget;101102protected readonly _onDidChangeHeight = this._store.add(new Emitter<void>());103readonly onDidChangeHeight: Event<void> = Event.filter(this._onDidChangeHeight.event, _ => !this._isLayouting);104105private readonly _requestInProgress = observableValue(this, false);106readonly requestInProgress: IObservable<boolean> = this._requestInProgress;107108private _isLayouting: boolean = false;109110readonly scopedContextKeyService: IContextKeyService;111112constructor(113location: IChatWidgetLocationOptions,114private readonly _options: IInlineChatWidgetConstructionOptions,115@IInstantiationService protected readonly _instantiationService: IInstantiationService,116@IContextKeyService private readonly _contextKeyService: IContextKeyService,117@IKeybindingService private readonly _keybindingService: IKeybindingService,118@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,119@IConfigurationService private readonly _configurationService: IConfigurationService,120@IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService,121@ITextModelService protected readonly _textModelResolverService: ITextModelService,122@IChatService private readonly _chatService: IChatService,123@IHoverService private readonly _hoverService: IHoverService,124@IChatEntitlementService private readonly _chatEntitlementService: IChatEntitlementService,125@IMarkdownRendererService private readonly _markdownRendererService: IMarkdownRendererService,126) {127this.scopedContextKeyService = this._store.add(_contextKeyService.createScoped(this._elements.chatWidget));128const scopedInstaService = _instantiationService.createChild(129new ServiceCollection([130IContextKeyService,131this.scopedContextKeyService132]),133this._store134);135136this._chatWidget = scopedInstaService.createInstance(137ChatWidget,138location,139{ isInlineChat: true },140{141autoScroll: true,142defaultElementHeight: 32,143renderStyle: 'minimal',144renderInputOnTop: false,145renderFollowups: true,146supportsFileReferences: true,147filter: item => {148if (!isResponseVM(item) || item.errorDetails) {149// show all requests and errors150return true;151}152const emptyResponse = item.response.value.length === 0;153if (emptyResponse) {154return false;155}156if (item.response.value.every(item => item.kind === 'textEditGroup' && _options.chatWidgetViewOptions?.rendererOptions?.renderTextEditsAsSummary?.(item.uri))) {157return false;158}159return true;160},161dndContainer: this._elements.root,162defaultMode: ChatMode.Ask,163..._options.chatWidgetViewOptions164},165{166listForeground: inlineChatForeground,167listBackground: inlineChatBackground,168overlayBackground: EDITOR_DRAG_AND_DROP_BACKGROUND,169inputEditorBackground: inputBackground,170resultEditorBackground: editorBackground171}172);173this._elements.root.classList.toggle('in-zone-widget', !!_options.inZoneWidget);174this._chatWidget.render(this._elements.chatWidget);175this._elements.chatWidget.style.setProperty(asCssVariableName(chatRequestBackground), asCssVariable(inlineChatBackground));176this._chatWidget.setVisible(true);177this._store.add(this._chatWidget);178179const ctxResponse = ChatContextKeys.isResponse.bindTo(this.scopedContextKeyService);180const ctxResponseVote = ChatContextKeys.responseVote.bindTo(this.scopedContextKeyService);181const ctxResponseSupportIssues = ChatContextKeys.responseSupportsIssueReporting.bindTo(this.scopedContextKeyService);182const ctxResponseError = ChatContextKeys.responseHasError.bindTo(this.scopedContextKeyService);183const ctxResponseErrorFiltered = ChatContextKeys.responseIsFiltered.bindTo(this.scopedContextKeyService);184185const viewModelStore = this._store.add(new DisposableStore());186this._store.add(this._chatWidget.onDidChangeViewModel(() => {187viewModelStore.clear();188189const viewModel = this._chatWidget.viewModel;190if (!viewModel) {191return;192}193194viewModelStore.add(toDisposable(() => {195toolbar2.context = undefined;196ctxResponse.reset();197ctxResponseVote.reset();198ctxResponseError.reset();199ctxResponseErrorFiltered.reset();200ctxResponseSupportIssues.reset();201}));202203viewModelStore.add(viewModel.onDidChange(() => {204205this._requestInProgress.set(viewModel.model.requestInProgress.get(), undefined);206207const last = viewModel.getItems().at(-1);208toolbar2.context = last;209210ctxResponse.set(isResponseVM(last));211ctxResponseVote.set(isResponseVM(last) ? last.vote === ChatAgentVoteDirection.Down ? 'down' : last.vote === ChatAgentVoteDirection.Up ? 'up' : '' : '');212ctxResponseError.set(isResponseVM(last) && last.errorDetails !== undefined);213ctxResponseErrorFiltered.set((!!(isResponseVM(last) && last.errorDetails?.responseIsFiltered)));214ctxResponseSupportIssues.set(isResponseVM(last) && (last.agent?.metadata.supportIssueReporting ?? false));215216this._onDidChangeHeight.fire();217}));218this._onDidChangeHeight.fire();219}));220221this._store.add(this.chatWidget.onDidChangeContentHeight(() => {222this._onDidChangeHeight.fire();223}));224225// context keys226this._ctxResponseFocused = CTX_INLINE_CHAT_RESPONSE_FOCUSED.bindTo(this._contextKeyService);227const tracker = this._store.add(trackFocus(this.domNode));228this._store.add(tracker.onDidBlur(() => this._ctxResponseFocused.set(false)));229this._store.add(tracker.onDidFocus(() => this._ctxResponseFocused.set(true)));230231this._ctxInputEditorFocused = CTX_INLINE_CHAT_FOCUSED.bindTo(_contextKeyService);232this._store.add(this._chatWidget.inputEditor.onDidFocusEditorWidget(() => this._ctxInputEditorFocused.set(true)));233this._store.add(this._chatWidget.inputEditor.onDidBlurEditorWidget(() => this._ctxInputEditorFocused.set(false)));234235const statusMenuId = _options.statusMenuId instanceof MenuId ? _options.statusMenuId : _options.statusMenuId.menu;236237// BUTTON bar238const statusMenuOptions = _options.statusMenuId instanceof MenuId ? undefined : _options.statusMenuId.options;239const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.toolbar1, statusMenuId, {240toolbarOptions: { primaryGroup: '0_main' },241telemetrySource: _options.chatWidgetViewOptions?.menus?.telemetrySource,242menuOptions: { renderShortTitle: true },243...statusMenuOptions,244});245this._store.add(statusButtonBar.onDidChange(() => this._onDidChangeHeight.fire()));246this._store.add(statusButtonBar);247248// secondary toolbar249const toolbar2 = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.toolbar2, _options.secondaryMenuId ?? MenuId.for(''), {250telemetrySource: _options.chatWidgetViewOptions?.menus?.telemetrySource,251menuOptions: { renderShortTitle: true, shouldForwardArgs: true },252actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => {253if (action instanceof MenuItemAction && action.item.id === MarkUnhelpfulActionId) {254return scopedInstaService.createInstance(ChatVoteDownButton, action, options as IMenuEntryActionViewItemOptions);255}256return createActionViewItem(scopedInstaService, action, options);257}258});259this._store.add(toolbar2.onDidChangeMenuItems(() => this._onDidChangeHeight.fire()));260this._store.add(toolbar2);261262263this._store.add(this._configurationService.onDidChangeConfiguration(e => {264if (e.affectsConfiguration(AccessibilityVerbositySettingId.InlineChat)) {265this._updateAriaLabel();266}267}));268269this._elements.root.tabIndex = 0;270this._elements.statusLabel.tabIndex = 0;271this._updateAriaLabel();272this._setupDisclaimer();273274this._store.add(this._hoverService.setupManagedHover(getDefaultHoverDelegate('element'), this._elements.statusLabel, () => {275return this._elements.statusLabel.dataset['title'];276}));277278this._store.add(this._chatService.onDidPerformUserAction(e => {279if (isEqual(e.sessionResource, this._chatWidget.viewModel?.model.sessionResource) && e.action.kind === 'vote') {280this.updateStatus(localize('feedbackThanks', "Thank you for your feedback!"), { resetAfter: 1250 });281}282}));283}284285private _updateAriaLabel(): void {286287this._elements.root.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat);288289if (this._accessibilityService.isScreenReaderOptimized()) {290let label = defaultAriaLabel;291if (this._configurationService.getValue<boolean>(AccessibilityVerbositySettingId.InlineChat)) {292const kbLabel = this._keybindingService.lookupKeybinding(AccessibilityCommandId.OpenAccessibilityHelp)?.getLabel();293label = kbLabel294? localize('inlineChat.accessibilityHelp', "Inline Chat Input, Use {0} for Inline Chat Accessibility Help.", kbLabel)295: localize('inlineChat.accessibilityHelpNoKb', "Inline Chat Input, Run the Inline Chat Accessibility Help command for more information.");296}297this._chatWidget.inputEditor.updateOptions({ ariaLabel: label });298}299}300301private _setupDisclaimer(): void {302const disposables = this._store.add(new DisposableStore());303304this._store.add(autorun(reader => {305disposables.clear();306reset(this._elements.disclaimerLabel);307308const sentiment = this._chatEntitlementService.sentimentObs.read(reader);309const anonymous = this._chatEntitlementService.anonymousObs.read(reader);310const requestInProgress = this._chatService.requestInProgressObs.read(reader);311312const showDisclaimer = !sentiment.installed && anonymous && !requestInProgress;313this._elements.disclaimerLabel.classList.toggle('hidden', !showDisclaimer);314315if (showDisclaimer) {316const renderedMarkdown = disposables.add(this._markdownRendererService.render(new MarkdownString(localize({ key: 'termsDisclaimer', comment: ['{Locked="]({2})"}', '{Locked="]({3})"}'] }, "By continuing with {0} Copilot, you agree to {1}'s [Terms]({2}) and [Privacy Statement]({3})", product.defaultChatAgent?.provider?.default?.name ?? '', product.defaultChatAgent?.provider?.default?.name ?? '', product.defaultChatAgent?.termsStatementUrl ?? '', product.defaultChatAgent?.privacyStatementUrl ?? ''), { isTrusted: true })));317this._elements.disclaimerLabel.appendChild(renderedMarkdown.element);318}319320this._onDidChangeHeight.fire();321}));322}323324dispose(): void {325this._store.dispose();326}327328get domNode(): HTMLElement {329return this._elements.root;330}331332get chatWidget(): ChatWidget {333return this._chatWidget;334}335336saveState() {337this._chatWidget.saveState();338}339340layout(widgetDim: Dimension) {341const contentHeight = this.contentHeight;342this._isLayouting = true;343try {344this._doLayout(widgetDim);345} finally {346this._isLayouting = false;347348if (this.contentHeight !== contentHeight) {349this._onDidChangeHeight.fire();350}351}352}353354protected _doLayout(dimension: Dimension): void {355const extraHeight = this._getExtraHeight();356const statusHeight = getTotalHeight(this._elements.status);357358// console.log('ZONE#Widget#layout', { height: dimension.height, extraHeight, progressHeight, followUpsHeight, statusHeight, LIST: dimension.height - progressHeight - followUpsHeight - statusHeight - extraHeight });359360this._elements.root.style.height = `${dimension.height - extraHeight}px`;361this._elements.root.style.width = `${dimension.width}px`;362363this._chatWidget.layout(364dimension.height - statusHeight - extraHeight,365dimension.width366);367}368369/**370* The content height of this widget is the size that would require no scrolling371*/372get contentHeight(): number {373const data = {374chatWidgetContentHeight: this._chatWidget.contentHeight,375statusHeight: getTotalHeight(this._elements.status),376extraHeight: this._getExtraHeight()377};378const result = data.chatWidgetContentHeight + data.statusHeight + data.extraHeight;379return result;380}381382get minHeight(): number {383// The chat widget is variable height and supports scrolling. It should be384// at least "maxWidgetHeight" high and at most the content height.385386let maxWidgetOutputHeight = 100;387for (const item of this._chatWidget.viewModel?.getItems() ?? []) {388if (isResponseVM(item) && item.response.value.some(r => r.kind === 'textEditGroup' && !r.state?.applied)) {389maxWidgetOutputHeight = 270;390break;391}392}393394let value = this.contentHeight;395value -= this._chatWidget.contentHeight;396value += Math.min(this._chatWidget.input.height.get() + maxWidgetOutputHeight, this._chatWidget.contentHeight);397return value;398}399400protected _getExtraHeight(): number {401return this._options.inZoneWidget ? 1 : (2 /*border*/ + 4 /*shadow*/);402}403404get value(): string {405return this._chatWidget.getInput();406}407408set value(value: string) {409this._chatWidget.setInput(value);410}411412selectAll() {413this._chatWidget.inputEditor.setSelection(new Selection(1, 1, Number.MAX_SAFE_INTEGER, 1));414}415416set placeholder(value: string) {417this._chatWidget.setInputPlaceholder(value);418}419420toggleStatus(show: boolean) {421this._elements.toolbar1.classList.toggle('hidden', !show);422this._elements.toolbar2.classList.toggle('hidden', !show);423this._elements.status.classList.toggle('hidden', !show);424this._elements.infoLabel.classList.toggle('hidden', !show);425this._onDidChangeHeight.fire();426}427428updateToolbar(show: boolean) {429this._elements.root.classList.toggle('toolbar', show);430this._elements.toolbar1.classList.toggle('hidden', !show);431this._elements.toolbar2.classList.toggle('hidden', !show);432this._elements.status.classList.toggle('actions', show);433this._elements.infoLabel.classList.toggle('hidden', show);434this._onDidChangeHeight.fire();435}436437async getCodeBlockInfo(codeBlockIndex: number): Promise<ITextModel | undefined> {438const { viewModel } = this._chatWidget;439if (!viewModel) {440return undefined;441}442const items = viewModel.getItems().filter(i => isResponseVM(i));443const item = items.at(-1);444if (!item) {445return;446}447448// Try to get the existing code block from the collection449const existingEntry = viewModel.codeBlockModelCollection.get(viewModel.sessionResource, item, codeBlockIndex);450if (existingEntry) {451return existingEntry.model;452}453454// If not found, the rendering may not have completed yet.455// Parse the markdown and create the code block model synchronously.456const markdown = item.response.getMarkdown();457let currentCodeBlockIndex = 0;458let foundCodeBlock: { text: string; lang: string } | undefined;459460marked.walkTokens(marked.lexer(markdown), token => {461if (token.type === 'code') {462if (currentCodeBlockIndex === codeBlockIndex) {463foundCodeBlock = { text: token.text, lang: token.lang || '' };464}465currentCodeBlockIndex++;466}467});468469if (!foundCodeBlock) {470return undefined;471}472473// Create the code block model synchronously474const entry = viewModel.codeBlockModelCollection.updateSync(475viewModel.sessionResource,476item,477codeBlockIndex,478{ text: foundCodeBlock.text, languageId: foundCodeBlock.lang, isComplete: true }479);480481return entry.model;482}483484get responseContent(): string | undefined {485const requests = this._chatWidget.viewModel?.model.getRequests();486return requests?.at(-1)?.response?.response.toString();487}488489490getChatModel(): IChatModel | undefined {491return this._chatWidget.viewModel?.model;492}493494setChatModel(chatModel: IChatModel) {495chatModel.inputModel.setState({ inputText: '', selections: [] });496this._chatWidget.setModel(chatModel);497}498499updateInfo(message: string): void {500this._elements.infoLabel.classList.toggle('hidden', !message);501const renderedMessage = renderLabelWithIcons(message);502reset(this._elements.infoLabel, ...renderedMessage);503this._onDidChangeHeight.fire();504}505506updateStatus(message: string, ops: { classes?: string[]; resetAfter?: number; keepMessage?: boolean; title?: string } = {}) {507const isTempMessage = typeof ops.resetAfter === 'number';508if (isTempMessage && !this._elements.statusLabel.dataset['state']) {509const statusLabel = this._elements.statusLabel.innerText;510const title = this._elements.statusLabel.dataset['title'];511const classes = Array.from(this._elements.statusLabel.classList.values());512setTimeout(() => {513this.updateStatus(statusLabel, { classes, keepMessage: true, title });514}, ops.resetAfter);515}516const renderedMessage = renderLabelWithIcons(message);517reset(this._elements.statusLabel, ...renderedMessage);518this._elements.statusLabel.className = `label status ${(ops.classes ?? []).join(' ')}`;519this._elements.statusLabel.classList.toggle('hidden', !message);520if (isTempMessage) {521this._elements.statusLabel.dataset['state'] = 'temp';522} else {523delete this._elements.statusLabel.dataset['state'];524}525526if (ops.title) {527this._elements.statusLabel.dataset['title'] = ops.title;528} else {529delete this._elements.statusLabel.dataset['title'];530}531this._onDidChangeHeight.fire();532}533534reset() {535this._chatWidget.attachmentModel.clear(true);536this._chatWidget.saveState();537538reset(this._elements.statusLabel);539this._elements.statusLabel.classList.toggle('hidden', true);540this._elements.toolbar1.classList.add('hidden');541this._elements.toolbar2.classList.add('hidden');542this.updateInfo('');543544this._elements.accessibleViewer.classList.toggle('hidden', true);545this._onDidChangeHeight.fire();546}547548focus() {549this._chatWidget.focusInput();550}551552hasFocus() {553return this.domNode.contains(getActiveElement());554}555556}557558const defaultAriaLabel = localize('aria-label', "Inline Chat Input");559560export class EditorBasedInlineChatWidget extends InlineChatWidget {561562constructor(563location: IChatWidgetLocationOptions,564parentEditor: ICodeEditor,565options: IInlineChatWidgetConstructionOptions,566@IContextKeyService contextKeyService: IContextKeyService,567@IKeybindingService keybindingService: IKeybindingService,568@IInstantiationService instantiationService: IInstantiationService,569@IAccessibilityService accessibilityService: IAccessibilityService,570@IConfigurationService configurationService: IConfigurationService,571@IAccessibleViewService accessibleViewService: IAccessibleViewService,572@ITextModelService textModelResolverService: ITextModelService,573@IChatService chatService: IChatService,574@IHoverService hoverService: IHoverService,575@ILayoutService layoutService: ILayoutService,576@IChatEntitlementService chatEntitlementService: IChatEntitlementService,577@IMarkdownRendererService markdownRendererService: IMarkdownRendererService,578) {579const overflowWidgetsNode = layoutService.getContainer(getWindow(parentEditor.getContainerDomNode())).appendChild($('.inline-chat-overflow.monaco-editor'));580super(location, {581...options,582chatWidgetViewOptions: {583...options.chatWidgetViewOptions,584editorOverflowWidgetsDomNode: overflowWidgetsNode585}586}, instantiationService, contextKeyService, keybindingService, accessibilityService, configurationService, accessibleViewService, textModelResolverService, chatService, hoverService, chatEntitlementService, markdownRendererService);587588this._store.add(toDisposable(() => {589overflowWidgetsNode.remove();590}));591}592593// --- layout594595596protected override _doLayout(dimension: Dimension): void {597598const newHeight = dimension.height;599600super._doLayout(dimension.with(undefined, newHeight));601602// update/fix the height of the zone which was set to newHeight in super._doLayout603this._elements.root.style.height = `${dimension.height - this._getExtraHeight()}px`;604}605606override reset() {607this.chatWidget.setInput();608super.reset();609}610611}612613614