Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/test/browser/componentFixtures/chat/chatArtifacts.fixture.ts
13405 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 { Event } from '../../../../../base/common/event.js';
7
import { observableValue } from '../../../../../base/common/observable.js';
8
import { URI } from '../../../../../base/common/uri.js';
9
import { mock } from '../../../../../base/test/common/mock.js';
10
import { IFileDialogService } from '../../../../../platform/dialogs/common/dialogs.js';
11
import { IFileService } from '../../../../../platform/files/common/files.js';
12
import { IListService, ListService } from '../../../../../platform/list/browser/listService.js';
13
import { IContextViewService } from '../../../../../platform/contextview/browser/contextView.js';
14
import { ChatArtifactsWidget } from '../../../../contrib/chat/browser/widget/chatArtifactsWidget.js';
15
import { IChatImageCarouselService } from '../../../../contrib/chat/browser/chatImageCarouselService.js';
16
import { IChatArtifact, IChatArtifacts, IChatArtifactsService, IArtifactSourceGroup } from '../../../../contrib/chat/common/tools/chatArtifactsService.js';
17
import { ComponentFixtureContext, createEditorServices, defineComponentFixture, defineThemedFixtureGroup } from '../fixtureUtils.js';
18
19
import '../../../../contrib/chat/browser/widget/media/chat.css';
20
21
function createMockArtifactsFromGroups(groups: IArtifactSourceGroup[]): IChatArtifacts {
22
const artifactGroups = observableValue<readonly IArtifactSourceGroup[]>('artifactGroups', groups);
23
return new class extends mock<IChatArtifacts>() {
24
override readonly artifactGroups = artifactGroups;
25
override setAgentArtifacts(a: IChatArtifact[]) { artifactGroups.set(a.length > 0 ? [{ source: { kind: 'agent' }, artifacts: a }] : [], undefined); }
26
override clearAgentArtifacts() { artifactGroups.set([], undefined); }
27
override clearSubagentArtifacts() { }
28
override migrate() { }
29
}();
30
}
31
32
function createMockArtifacts(artifacts: IChatArtifact[]): IChatArtifacts {
33
return createMockArtifactsFromGroups(artifacts.length > 0 ? [{ source: { kind: 'agent' }, artifacts }] : []);
34
}
35
36
function createMockArtifactsService(artifacts: IChatArtifact[]): IChatArtifactsService {
37
const instance = createMockArtifacts(artifacts);
38
return new class extends mock<IChatArtifactsService>() {
39
override getArtifacts() { return instance; }
40
}();
41
}
42
43
function createMockArtifactsServiceFromGroups(groups: IArtifactSourceGroup[]): IChatArtifactsService {
44
const instance = createMockArtifactsFromGroups(groups);
45
return new class extends mock<IChatArtifactsService>() {
46
override getArtifacts() { return instance; }
47
}();
48
}
49
50
function renderArtifactsWidget(context: ComponentFixtureContext, artifacts: IChatArtifact[]): void {
51
const { container, disposableStore } = context;
52
53
const instantiationService = createEditorServices(disposableStore, {
54
colorTheme: context.theme,
55
additionalServices: (reg) => {
56
reg.define(IListService, ListService);
57
reg.defineInstance(IContextViewService, new class extends mock<IContextViewService>() { }());
58
reg.defineInstance(IChatArtifactsService, createMockArtifactsService(artifacts));
59
reg.defineInstance(IChatImageCarouselService, new class extends mock<IChatImageCarouselService>() { }());
60
reg.defineInstance(IFileService, new class extends mock<IFileService>() { override onDidFilesChange = Event.None; override onDidRunOperation = Event.None; }());
61
reg.defineInstance(IFileDialogService, new class extends mock<IFileDialogService>() { }());
62
},
63
});
64
65
const widget = disposableStore.add(instantiationService.createInstance(ChatArtifactsWidget));
66
widget.setSessionResource(URI.parse('chat-session:test-session'));
67
68
container.style.width = '400px';
69
container.style.padding = '8px';
70
container.appendChild(widget.domNode);
71
}
72
73
function renderArtifactsWidgetFromGroups(context: ComponentFixtureContext, groups: IArtifactSourceGroup[]): void {
74
const { container, disposableStore } = context;
75
76
const instantiationService = createEditorServices(disposableStore, {
77
colorTheme: context.theme,
78
additionalServices: (reg) => {
79
reg.define(IListService, ListService);
80
reg.defineInstance(IContextViewService, new class extends mock<IContextViewService>() { }());
81
reg.defineInstance(IChatArtifactsService, createMockArtifactsServiceFromGroups(groups));
82
reg.defineInstance(IChatImageCarouselService, new class extends mock<IChatImageCarouselService>() { }());
83
reg.defineInstance(IFileService, new class extends mock<IFileService>() { override onDidFilesChange = Event.None; override onDidRunOperation = Event.None; }());
84
reg.defineInstance(IFileDialogService, new class extends mock<IFileDialogService>() { }());
85
},
86
});
87
88
const widget = disposableStore.add(instantiationService.createInstance(ChatArtifactsWidget));
89
widget.setSessionResource(URI.parse('chat-session:test-session'));
90
91
container.style.width = '400px';
92
container.style.padding = '8px';
93
container.appendChild(widget.domNode);
94
}
95
96
// ============================================================================
97
// Sample artifacts
98
// ============================================================================
99
100
const singleArtifact: IChatArtifact[] = [
101
{ label: 'Dev Server', uri: 'http://localhost:3000', type: 'devServer' },
102
];
103
104
const multipleArtifacts: IChatArtifact[] = [
105
{ label: 'Dev Server', uri: 'http://localhost:3000', type: 'devServer' },
106
{ label: 'Screenshot of login page', uri: 'file:///tmp/screenshot.png', type: 'screenshot' },
107
{ label: 'Implementation Plan', uri: 'file:///tmp/plan.md', type: 'plan' },
108
];
109
110
const multiSourceGroups: IArtifactSourceGroup[] = [
111
{
112
source: { kind: 'rules' },
113
artifacts: [
114
{ label: 'Implementation Plan', uri: 'file:///tmp/plan.md', type: 'plan', groupName: 'Plans' },
115
{ label: 'Verification Plan', uri: 'file:///tmp/verify-plan.md', type: 'plan', groupName: 'Plans' },
116
{ label: 'Screenshot 1', uri: 'file:///tmp/s1.png', type: 'screenshot', groupName: 'Screenshots', onlyShowGroup: true },
117
{ label: 'Screenshot 2', uri: 'file:///tmp/s2.png', type: 'screenshot', groupName: 'Screenshots', onlyShowGroup: true },
118
{ label: 'Screenshot 3', uri: 'file:///tmp/s3.png', type: 'screenshot', groupName: 'Screenshots', onlyShowGroup: true },
119
],
120
},
121
{
122
source: { kind: 'agent' },
123
artifacts: [
124
{ label: 'Specification (v2 - reviewed)', uri: 'file:///tmp/spec.md', type: 'plan' },
125
{ label: 'Dev Server', uri: 'http://localhost:5173', type: 'devServer' },
126
],
127
},
128
{
129
source: { kind: 'subagent', invocationId: 'sub-1', name: 'Explore' },
130
artifacts: [
131
{ label: 'Architecture Notes', uri: 'file:///tmp/arch.md', type: 'plan' },
132
],
133
},
134
];
135
136
// ============================================================================
137
// Fixtures
138
// ============================================================================
139
140
export default defineThemedFixtureGroup({ path: 'chat/artifacts/' }, {
141
SingleArtifact: defineComponentFixture({
142
render: context => renderArtifactsWidget(context, singleArtifact),
143
}),
144
145
MultipleArtifacts: defineComponentFixture({
146
render: context => renderArtifactsWidget(context, multipleArtifacts),
147
}),
148
149
MultipleArtifactsCollapsed: defineComponentFixture({
150
render: context => {
151
renderArtifactsWidget(context, multipleArtifacts);
152
const expandButton = context.container.querySelector<HTMLElement>('.chat-artifacts-expand .monaco-button');
153
expandButton?.click();
154
},
155
}),
156
157
MultiSourceExpanded: defineComponentFixture({
158
render: context => renderArtifactsWidgetFromGroups(context, multiSourceGroups),
159
}),
160
161
MultiSourceHoveredRow: defineComponentFixture({
162
render: context => {
163
renderArtifactsWidgetFromGroups(context, multiSourceGroups);
164
// Force hover on a rules-sourced leaf (save only, no clear)
165
const rows = context.container.querySelectorAll<HTMLElement>('.chat-artifacts-list-row');
166
for (const row of rows) {
167
const label = row.querySelector('.chat-artifacts-list-label');
168
if (label?.textContent === 'Implementation Plan') {
169
row.classList.add('force-hover');
170
break;
171
}
172
}
173
},
174
}),
175
});
176
177