mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Show chat agent hover in chat input editor (#214105)
* Show chat agent hover in chat input editor * Clean up * Comment
This commit is contained in:
parent
acfe0e20ce
commit
403294d92b
|
@ -43,6 +43,7 @@ import { ChatCodeBlockContextProviderService } from 'vs/workbench/contrib/chat/b
|
|||
import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib';
|
||||
import 'vs/workbench/contrib/chat/browser/contrib/chatContextAttachments';
|
||||
import 'vs/workbench/contrib/chat/browser/contrib/chatInputCompletions';
|
||||
import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover';
|
||||
import { ChatAgentLocation, ChatAgentNameService, ChatAgentService, IChatAgentNameService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
|
|
|
@ -9,6 +9,7 @@ import { IUpdatableHoverOptions } from 'vs/base/browser/ui/hover/hover';
|
|||
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
|
@ -29,6 +30,9 @@ export class ChatAgentHover extends Disposable {
|
|||
private readonly publisherName: HTMLElement;
|
||||
private readonly description: HTMLElement;
|
||||
|
||||
private readonly _onDidChangeContents = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeContents: Event<void> = this._onDidChangeContents.event;
|
||||
|
||||
constructor(
|
||||
@IChatAgentService private readonly chatAgentService: IChatAgentService,
|
||||
@IExtensionsWorkbenchService private readonly extensionService: IExtensionsWorkbenchService,
|
||||
|
@ -110,6 +114,7 @@ export class ChatAgentHover extends Disposable {
|
|||
const extension = extensions[0];
|
||||
if (extension?.publisherDomain?.verified) {
|
||||
this.domNode.classList.toggle('verifiedPublisher', true);
|
||||
this._onDidChangeContents.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -197,10 +197,7 @@ class InputEditorDecorations extends Disposable {
|
|||
|
||||
const textDecorations: IDecorationOptions[] | undefined = [];
|
||||
if (agentPart) {
|
||||
const isDupe = !!this.chatAgentService.getAgents().find(other => other.name === agentPart.agent.name && other.id !== agentPart.agent.id);
|
||||
const publisher = isDupe ? `(${agentPart.agent.publisherDisplayName}) ` : '';
|
||||
const agentHover = `${publisher}${agentPart.agent.description}`;
|
||||
textDecorations.push({ range: agentPart.editorRange, hoverMessage: new MarkdownString(agentHover) });
|
||||
textDecorations.push({ range: agentPart.editorRange });
|
||||
if (agentSubcommandPart) {
|
||||
textDecorations.push({ range: agentSubcommandPart.editorRange, hoverMessage: new MarkdownString(agentSubcommandPart.command.description) });
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IModelDecoration } from 'vs/editor/common/model';
|
||||
import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatAgentHover, getChatAgentHoverOptions } from 'vs/workbench/contrib/chat/browser/chatAgentHover';
|
||||
import { ChatEditorHoverWrapper } from 'vs/workbench/contrib/chat/browser/contrib/editorHoverWrapper';
|
||||
import { IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { extractAgentAndCommand } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
|
||||
export class ChatAgentHoverParticipant implements IEditorHoverParticipant<ChatAgentHoverPart> {
|
||||
|
||||
public readonly hoverOrdinal: number = 1;
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IChatWidgetService private readonly chatWidgetService: IChatWidgetService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
) { }
|
||||
|
||||
public computeSync(anchor: HoverAnchor, _lineDecorations: IModelDecoration[]): ChatAgentHoverPart[] {
|
||||
if (!this.editor.hasModel()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const widget = this.chatWidgetService.getWidgetByInputUri(this.editor.getModel().uri);
|
||||
if (!widget) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { agentPart } = extractAgentAndCommand(widget.parsedInput);
|
||||
if (!agentPart) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Range.containsPosition(agentPart.editorRange, anchor.range.getStartPosition())) {
|
||||
return [new ChatAgentHoverPart(this, Range.lift(agentPart.editorRange), agentPart.agent)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: ChatAgentHoverPart[]): IDisposable {
|
||||
if (!hoverParts.length) {
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
const store = new DisposableStore();
|
||||
const hover = store.add(this.instantiationService.createInstance(ChatAgentHover));
|
||||
store.add(hover.onDidChangeContents(() => context.onContentsChanged()));
|
||||
const agent = hoverParts[0].agent;
|
||||
hover.setAgent(agent.id);
|
||||
|
||||
const actions = getChatAgentHoverOptions(() => agent, this.commandService).actions;
|
||||
const wrapper = this.instantiationService.createInstance(ChatEditorHoverWrapper, hover.domNode, actions);
|
||||
context.fragment.appendChild(wrapper.domNode);
|
||||
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatAgentHoverPart implements IHoverPart {
|
||||
|
||||
constructor(
|
||||
public readonly owner: IEditorHoverParticipant<ChatAgentHoverPart>,
|
||||
public readonly range: Range,
|
||||
public readonly agent: IChatAgentData
|
||||
) { }
|
||||
|
||||
public isValidForHoverAnchor(anchor: HoverAnchor): boolean {
|
||||
return (
|
||||
anchor.type === HoverAnchorType.Range
|
||||
&& this.range.startColumn <= anchor.range.startColumn
|
||||
&& this.range.endColumn >= anchor.range.endColumn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HoverParticipantRegistry.register(ChatAgentHoverParticipant);
|
|
@ -0,0 +1,52 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/editorHoverWrapper';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IHoverAction } from 'vs/base/browser/ui/hover/hover';
|
||||
import { HoverAction } from 'vs/base/browser/ui/hover/hoverWidget';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
const $ = dom.$;
|
||||
const h = dom.h;
|
||||
|
||||
/**
|
||||
* This borrows some of HoverWidget so that a chat editor hover can be rendered in the same way as a workbench hover.
|
||||
* Maybe it can be reusable in a generic way.
|
||||
*/
|
||||
export class ChatEditorHoverWrapper {
|
||||
public readonly domNode: HTMLElement;
|
||||
|
||||
constructor(
|
||||
hoverContentElement: HTMLElement,
|
||||
actions: IHoverAction[] | undefined,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
) {
|
||||
const hoverElement = h(
|
||||
'.chat-editor-hover-wrapper@root',
|
||||
[h('.chat-editor-hover-wrapper-content@content')]);
|
||||
this.domNode = hoverElement.root;
|
||||
hoverElement.content.appendChild(hoverContentElement);
|
||||
|
||||
if (actions && actions.length > 0) {
|
||||
const statusBarElement = $('.hover-row.status-bar');
|
||||
const actionsElement = $('.actions');
|
||||
actions.forEach(action => {
|
||||
const keybinding = this.keybindingService.lookupKeybinding(action.commandId);
|
||||
const keybindingLabel = keybinding ? keybinding.getLabel() : null;
|
||||
HoverAction.render(actionsElement, {
|
||||
label: action.label,
|
||||
commandId: action.commandId,
|
||||
run: e => {
|
||||
action.run(e);
|
||||
},
|
||||
iconClass: action.iconClass
|
||||
}, keybindingLabel);
|
||||
});
|
||||
statusBarElement.appendChild(actionsElement);
|
||||
this.domNode.appendChild(statusBarElement);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.chat-editor-hover-wrapper-content {
|
||||
padding: 2px 8px;
|
||||
}
|
|
@ -22,8 +22,8 @@
|
|||
outline: 1px solid var(--vscode-chat-requestBorder);
|
||||
}
|
||||
|
||||
.monaco-hover .markdown-hover .hover-contents .chat-agent-hover-icon .codicon {
|
||||
font-size: 23px;
|
||||
.chat-agent-hover .chat-agent-hover-icon .codicon {
|
||||
font-size: 23px !important; /* Override workbench hover styles */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -34,7 +34,7 @@
|
|||
gap: 4px;
|
||||
}
|
||||
|
||||
.monaco-hover .chat-agent-hover .chat-agent-hover-publisher .codicon.codicon-extensions-verified-publisher {
|
||||
.chat-agent-hover .chat-agent-hover-publisher .codicon.codicon-extensions-verified-publisher {
|
||||
color: var(--vscode-extensionIcon-verifiedForeground);
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,10 @@
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.chat-agent-hover-header .chat-agent-hover-details {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.chat-agent-hover-extension {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
|
Loading…
Reference in a new issue