mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-24 00:05:46 +01:00
tools: implement tool confirmation polish (#250351)
Closes https://github.com/microsoft/vscode/issues/248567
This commit is contained in:
+48
-13
@@ -5,10 +5,12 @@
|
||||
|
||||
import * as dom from '../../../../../../base/browser/dom.js';
|
||||
import { RunOnceScheduler } from '../../../../../../base/common/async.js';
|
||||
import { IMarkdownString, MarkdownString } from '../../../../../../base/common/htmlContent.js';
|
||||
import { toDisposable } from '../../../../../../base/common/lifecycle.js';
|
||||
import { count } from '../../../../../../base/common/strings.js';
|
||||
import { isEmptyObject } from '../../../../../../base/common/types.js';
|
||||
import { generateUuid } from '../../../../../../base/common/uuid.js';
|
||||
import { ElementSizeObserver } from '../../../../../../editor/browser/config/elementSizeObserver.js';
|
||||
import { MarkdownRenderer } from '../../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js';
|
||||
import { ILanguageService } from '../../../../../../editor/common/languages/language.js';
|
||||
import { IModelService } from '../../../../../../editor/common/services/model.js';
|
||||
@@ -19,7 +21,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/
|
||||
import { IKeybindingService } from '../../../../../../platform/keybinding/common/keybinding.js';
|
||||
import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../../../platform/markers/common/markers.js';
|
||||
import { ChatContextKeys } from '../../../common/chatContextKeys.js';
|
||||
import { IChatMarkdownContent, IChatToolInvocation } from '../../../common/chatService.js';
|
||||
import { IChatToolInvocation } from '../../../common/chatService.js';
|
||||
import { CodeBlockModelCollection } from '../../../common/codeBlockModelCollection.js';
|
||||
import { createToolInputUri, createToolSchemaUri, ILanguageModelToolsService } from '../../../common/languageModelToolsService.js';
|
||||
import { CancelChatActionId } from '../../actions/chatExecuteActions.js';
|
||||
@@ -33,12 +35,14 @@ import { IChatMarkdownAnchorService } from '../chatMarkdownAnchorService.js';
|
||||
import { ChatMarkdownContentPart, EditorPool } from '../chatMarkdownContentPart.js';
|
||||
import { BaseChatToolInvocationSubPart } from './chatToolInvocationSubPart.js';
|
||||
|
||||
const SHOW_MORE_MESSAGE_HEIGHT_TRIGGER = 30;
|
||||
|
||||
export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
|
||||
public readonly domNode: HTMLElement;
|
||||
|
||||
private markdownPart: ChatMarkdownContentPart | undefined;
|
||||
private markdownParts: ChatMarkdownContentPart[] = [];
|
||||
public get codeblocks(): IChatCodeBlockInfo[] {
|
||||
return this.markdownPart?.codeblocks ?? [];
|
||||
return this.markdownParts.flatMap(part => part.codeblocks);
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -65,7 +69,7 @@ export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
|
||||
if (!toolInvocation.confirmationMessages) {
|
||||
throw new Error('Confirmation messages are missing');
|
||||
}
|
||||
const { title, message, allowAutoConfirm } = toolInvocation.confirmationMessages;
|
||||
const { title, message, allowAutoConfirm, disclaimer } = toolInvocation.confirmationMessages;
|
||||
const continueLabel = localize('continue', "Continue");
|
||||
const continueKeybinding = keybindingService.lookupKeybinding(AcceptToolConfirmationActionId)?.getLabel();
|
||||
const continueTooltip = continueKeybinding ? `${continueLabel} (${continueKeybinding})` : continueLabel;
|
||||
@@ -109,10 +113,6 @@ export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
|
||||
this.context.container,
|
||||
));
|
||||
} else {
|
||||
const chatMarkdownContent: IChatMarkdownContent = {
|
||||
kind: 'markdownContent',
|
||||
content: message,
|
||||
};
|
||||
const codeBlockRenderOptions: ICodeBlockRenderOptions = {
|
||||
hideToolbar: true,
|
||||
reserveWidth: 19,
|
||||
@@ -125,12 +125,20 @@ export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
|
||||
};
|
||||
|
||||
const elements = dom.h('div', [
|
||||
dom.h('.message@message'),
|
||||
dom.h('.message@messageContainer', [
|
||||
dom.h('.message-wrapper@message'),
|
||||
dom.h('a.see-more@showMore'),
|
||||
]),
|
||||
dom.h('.editor@editor'),
|
||||
dom.h('.disclaimer@disclaimer'),
|
||||
]);
|
||||
|
||||
if (toolInvocation.toolSpecificData?.kind === 'input' && toolInvocation.toolSpecificData.rawInput && !isEmptyObject(toolInvocation.toolSpecificData.rawInput)) {
|
||||
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = localize('chat.input', "Input");
|
||||
elements.editor.appendChild(title);
|
||||
|
||||
const inputData = toolInvocation.toolSpecificData;
|
||||
|
||||
const codeBlockRenderOptions: ICodeBlockRenderOptions = {
|
||||
@@ -238,11 +246,30 @@ export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
|
||||
}
|
||||
}
|
||||
|
||||
this.markdownPart = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, chatMarkdownContent, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions }));
|
||||
renderFileWidgets(this.markdownPart.domNode, this.instantiationService, this.chatMarkdownAnchorService, this._store);
|
||||
elements.message.append(this.markdownPart.domNode);
|
||||
this._makeMarkdownPart(elements.message, message, codeBlockRenderOptions);
|
||||
elements.showMore.textContent = localize('seeMore', "See more");
|
||||
|
||||
const messageSeeMoreObserver = this._register(new ElementSizeObserver(elements.message, undefined));
|
||||
const updateSeeMoreDisplayed = () => {
|
||||
const show = messageSeeMoreObserver.getHeight() > SHOW_MORE_MESSAGE_HEIGHT_TRIGGER;
|
||||
elements.messageContainer.classList.toggle('can-see-more', show);
|
||||
};
|
||||
|
||||
this._register(dom.addDisposableListener(elements.showMore, 'click', () => {
|
||||
elements.messageContainer.classList.toggle('can-see-more', false);
|
||||
messageSeeMoreObserver.dispose();
|
||||
}));
|
||||
|
||||
|
||||
this._register(messageSeeMoreObserver.onDidChange(updateSeeMoreDisplayed));
|
||||
messageSeeMoreObserver.startObserving();
|
||||
|
||||
if (disclaimer) {
|
||||
this._makeMarkdownPart(elements.disclaimer, disclaimer, codeBlockRenderOptions);
|
||||
} else {
|
||||
elements.disclaimer.remove();
|
||||
}
|
||||
|
||||
this._register(this.markdownPart.onDidChangeHeight(() => this._onDidChangeHeight.fire()));
|
||||
confirmWidget = this._register(this.instantiationService.createInstance(
|
||||
ChatCustomConfirmationWidget,
|
||||
title,
|
||||
@@ -288,4 +315,12 @@ export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
|
||||
});
|
||||
this.domNode = confirmWidget.domNode;
|
||||
}
|
||||
|
||||
private _makeMarkdownPart(container: HTMLElement, message: string | IMarkdownString, codeBlockRenderOptions: ICodeBlockRenderOptions) {
|
||||
const part = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, { kind: 'markdownContent', content: typeof message === 'string' ? new MarkdownString().appendText(message) : message }, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions }));
|
||||
renderFileWidgets(part.domNode, this.instantiationService, this.chatMarkdownAnchorService, this._store);
|
||||
container.append(part.domNode);
|
||||
|
||||
this._register(part.onDidChangeHeight(() => this._onDidChangeHeight.fire()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,6 +389,35 @@
|
||||
.interactive-item-container .value > .chat-tool-invocation-part {
|
||||
.rendered-markdown p {
|
||||
margin: 0 0 6px 0;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
margin-top: 6px;
|
||||
margin-bottom: -6px;
|
||||
|
||||
.rendered-markdown p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message .see-more {
|
||||
display: none;
|
||||
color: var(--vscode-textLink-foreground);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message.can-see-more {
|
||||
.message-wrapper {
|
||||
mask-image: linear-gradient(#000 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
max-height: 2.5em;
|
||||
}
|
||||
|
||||
.see-more {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-container .rendered-markdown [data-code] {
|
||||
@@ -1121,11 +1150,6 @@ have to be updated for changes to the rules above, or to support more deeply nes
|
||||
.chat-attached-context .chat-prompt-attachment .monaco-icon-label-container {
|
||||
margin-top: -0.1em;
|
||||
}
|
||||
/*
|
||||
* This overly-specific CSS selector is needed to beat priority of some
|
||||
* styles applied on the `.chat-attached-context-attachment` element.
|
||||
*/
|
||||
.chat-attached-context .chat-prompt-attachment.error.implicit,
|
||||
.chat-attached-context .chat-prompt-attachment.warning.implicit {
|
||||
border: 1px solid currentColor;
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ export interface IToolResultDataPart {
|
||||
export interface IToolConfirmationMessages {
|
||||
title: string | IMarkdownString;
|
||||
message: string | IMarkdownString;
|
||||
disclaimer?: string | IMarkdownString;
|
||||
allowAutoConfirm?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,8 +251,7 @@ class McpToolImplementation implements IToolImpl {
|
||||
|
||||
const mcpToolWarning = localize(
|
||||
'mcp.tool.warning',
|
||||
"{0} Note that MCP servers or malicious conversation content may attempt to misuse '{1}' through tools.",
|
||||
'$(info)',
|
||||
"Note that MCP servers or malicious conversation content may attempt to misuse '{0}' through tools.",
|
||||
this._productService.nameShort
|
||||
);
|
||||
|
||||
@@ -263,7 +262,8 @@ class McpToolImplementation implements IToolImpl {
|
||||
return {
|
||||
confirmationMessages: needsConfirmation ? {
|
||||
title: new MarkdownString(localize('msg.title', "Run {0}", title)),
|
||||
message: new MarkdownString(localize('msg.msg', "{0}\n\n {1}", tool.definition.description, mcpToolWarning), { supportThemeIcons: true }),
|
||||
message: new MarkdownString(tool.definition.description, { supportThemeIcons: true }),
|
||||
disclaimer: mcpToolWarning,
|
||||
allowAutoConfirm: true,
|
||||
} : undefined,
|
||||
invocationMessage: new MarkdownString(localize('msg.run', "Running {0}", title)),
|
||||
|
||||
Reference in New Issue
Block a user