Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as dom from '../../../../../base/browser/dom.js';
7
import { createInstantHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js';
8
import { Emitter } from '../../../../../base/common/event.js';
9
import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js';
10
import { basename } from '../../../../../base/common/path.js';
11
import { URI } from '../../../../../base/common/uri.js';
12
import { Range } from '../../../../../editor/common/core/range.js';
13
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
14
import { ResourceLabels } from '../../../../browser/labels.js';
15
import { IChatRequestVariableEntry, isElementVariableEntry, isImageVariableEntry, isNotebookOutputVariableEntry, isPasteVariableEntry, isPromptFileVariableEntry, isPromptTextVariableEntry, isSCMHistoryItemVariableEntry, OmittedState } from '../../common/chatVariableEntries.js';
16
import { ChatResponseReferencePartStatusKind, IChatContentReference } from '../../common/chatService.js';
17
import { DefaultChatAttachmentWidget, ElementChatAttachmentWidget, FileAttachmentWidget, ImageAttachmentWidget, NotebookCellOutputChatAttachmentWidget, PasteAttachmentWidget, PromptFileAttachmentWidget, PromptTextAttachmentWidget, SCMHistoryItemAttachmentWidget, ToolSetOrToolItemAttachmentWidget } from '../chatAttachmentWidgets.js';
18
19
export class ChatAttachmentsContentPart extends Disposable {
20
private readonly attachedContextDisposables = this._register(new DisposableStore());
21
22
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
23
private readonly _contextResourceLabels: ResourceLabels;
24
25
public contextMenuHandler?: (attachment: IChatRequestVariableEntry, event: MouseEvent) => void;
26
27
constructor(
28
private readonly variables: IChatRequestVariableEntry[],
29
private readonly contentReferences: ReadonlyArray<IChatContentReference> = [],
30
public readonly domNode: HTMLElement | undefined = dom.$('.chat-attached-context'),
31
@IInstantiationService private readonly instantiationService: IInstantiationService,
32
) {
33
super();
34
this._contextResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this._onDidChangeVisibility.event }));
35
36
this.initAttachedContext(domNode);
37
if (!domNode.childElementCount) {
38
this.domNode = undefined;
39
}
40
}
41
42
private initAttachedContext(container: HTMLElement) {
43
dom.clearNode(container);
44
this.attachedContextDisposables.clear();
45
const hoverDelegate = this.attachedContextDisposables.add(createInstantHoverDelegate());
46
47
for (const attachment of this.variables) {
48
const resource = URI.isUri(attachment.value) ? attachment.value : attachment.value && typeof attachment.value === 'object' && 'uri' in attachment.value && URI.isUri(attachment.value.uri) ? attachment.value.uri : undefined;
49
const range = attachment.value && typeof attachment.value === 'object' && 'range' in attachment.value && Range.isIRange(attachment.value.range) ? attachment.value.range : undefined;
50
const correspondingContentReference = this.contentReferences.find((ref) => (typeof ref.reference === 'object' && 'variableName' in ref.reference && ref.reference.variableName === attachment.name) || (URI.isUri(ref.reference) && basename(ref.reference.path) === attachment.name));
51
const isAttachmentOmitted = correspondingContentReference?.options?.status?.kind === ChatResponseReferencePartStatusKind.Omitted;
52
const isAttachmentPartialOrOmitted = isAttachmentOmitted || correspondingContentReference?.options?.status?.kind === ChatResponseReferencePartStatusKind.Partial;
53
54
let widget;
55
if (attachment.kind === 'tool' || attachment.kind === 'toolset') {
56
widget = this.instantiationService.createInstance(ToolSetOrToolItemAttachmentWidget, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
57
} else if (isElementVariableEntry(attachment)) {
58
widget = this.instantiationService.createInstance(ElementChatAttachmentWidget, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
59
} else if (isImageVariableEntry(attachment)) {
60
attachment.omittedState = isAttachmentPartialOrOmitted ? OmittedState.Full : attachment.omittedState;
61
widget = this.instantiationService.createInstance(ImageAttachmentWidget, resource, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
62
} else if (isPromptFileVariableEntry(attachment)) {
63
if (attachment.automaticallyAdded) {
64
continue;
65
}
66
widget = this.instantiationService.createInstance(PromptFileAttachmentWidget, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
67
} else if (isPromptTextVariableEntry(attachment)) {
68
if (attachment.automaticallyAdded) {
69
continue;
70
}
71
widget = this.instantiationService.createInstance(PromptTextAttachmentWidget, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
72
} else if (resource && (attachment.kind === 'file' || attachment.kind === 'directory')) {
73
widget = this.instantiationService.createInstance(FileAttachmentWidget, resource, range, attachment, correspondingContentReference, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
74
} else if (isPasteVariableEntry(attachment)) {
75
widget = this.instantiationService.createInstance(PasteAttachmentWidget, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
76
} else if (resource && isNotebookOutputVariableEntry(attachment)) {
77
widget = this.instantiationService.createInstance(NotebookCellOutputChatAttachmentWidget, resource, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
78
} else if (isSCMHistoryItemVariableEntry(attachment)) {
79
widget = this.instantiationService.createInstance(SCMHistoryItemAttachmentWidget, attachment, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
80
} else {
81
widget = this.instantiationService.createInstance(DefaultChatAttachmentWidget, resource, range, attachment, correspondingContentReference, undefined, { shouldFocusClearButton: false, supportsDeletion: false }, container, this._contextResourceLabels, hoverDelegate);
82
}
83
84
let ariaLabel: string | null = null;
85
86
if (isAttachmentPartialOrOmitted) {
87
widget.element.classList.add('warning');
88
}
89
const description = correspondingContentReference?.options?.status?.description;
90
if (isAttachmentPartialOrOmitted) {
91
ariaLabel = `${ariaLabel}${description ? ` ${description}` : ''}`;
92
for (const selector of ['.monaco-icon-suffix-container', '.monaco-icon-name-container']) {
93
const element = widget.label.element.querySelector(selector);
94
if (element) {
95
element.classList.add('warning');
96
}
97
}
98
}
99
100
this._register(dom.addDisposableListener(widget.element, 'contextmenu', e => this.contextMenuHandler?.(attachment, e)));
101
102
if (this.attachedContextDisposables.isDisposed) {
103
widget.dispose();
104
return;
105
}
106
107
if (ariaLabel) {
108
widget.element.ariaLabel = ariaLabel;
109
}
110
111
this.attachedContextDisposables.add(widget);
112
}
113
}
114
}
115
116