@theia/ai-chat-ui 1.60.0-next.43 → 1.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/ai-chat-ui-contribution.d.ts +1 -0
- package/lib/browser/ai-chat-ui-contribution.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-contribution.js +46 -7
- package/lib/browser/ai-chat-ui-contribution.js.map +1 -1
- package/lib/browser/ai-chat-ui-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-frontend-module.js +1 -0
- package/lib/browser/ai-chat-ui-frontend-module.js.map +1 -1
- package/lib/browser/chat-response-renderer/index.d.ts +1 -0
- package/lib/browser/chat-response-renderer/index.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/index.js +1 -0
- package/lib/browser/chat-response-renderer/index.js.map +1 -1
- package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts +9 -0
- package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts.map +1 -0
- package/lib/browser/chat-response-renderer/thinking-part-renderer.js +42 -0
- package/lib/browser/chat-response-renderer/thinking-part-renderer.js.map +1 -0
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts +25 -18
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js +19 -59
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
- package/lib/browser/chat-view-commands.d.ts +1 -0
- package/lib/browser/chat-view-commands.d.ts.map +1 -1
- package/lib/browser/chat-view-commands.js +5 -0
- package/lib/browser/chat-view-commands.js.map +1 -1
- package/lib/browser/chat-view-language-contribution.d.ts +1 -0
- package/lib/browser/chat-view-language-contribution.d.ts.map +1 -1
- package/lib/browser/chat-view-language-contribution.js +3 -1
- package/lib/browser/chat-view-language-contribution.js.map +1 -1
- package/lib/browser/chat-view-widget-toolbar-contribution.d.ts +8 -1
- package/lib/browser/chat-view-widget-toolbar-contribution.d.ts.map +1 -1
- package/lib/browser/chat-view-widget-toolbar-contribution.js +42 -0
- package/lib/browser/chat-view-widget-toolbar-contribution.js.map +1 -1
- package/lib/browser/chat-view-widget.d.ts +6 -0
- package/lib/browser/chat-view-widget.d.ts.map +1 -1
- package/lib/browser/chat-view-widget.js +14 -2
- package/lib/browser/chat-view-widget.js.map +1 -1
- package/lib/browser/session-settings-dialog.d.ts +35 -0
- package/lib/browser/session-settings-dialog.d.ts.map +1 -0
- package/lib/browser/session-settings-dialog.js +118 -0
- package/lib/browser/session-settings-dialog.js.map +1 -0
- package/package.json +13 -12
- package/src/browser/ai-chat-ui-contribution.ts +42 -8
- package/src/browser/ai-chat-ui-frontend-module.ts +2 -0
- package/src/browser/chat-response-renderer/index.ts +1 -0
- package/src/browser/chat-response-renderer/thinking-part-renderer.tsx +44 -0
- package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +33 -80
- package/src/browser/chat-view-commands.ts +6 -0
- package/src/browser/chat-view-language-contribution.ts +2 -0
- package/src/browser/chat-view-widget-toolbar-contribution.tsx +47 -1
- package/src/browser/chat-view-widget.tsx +17 -3
- package/src/browser/session-settings-dialog.tsx +144 -0
- package/src/browser/style/index.css +31 -16
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
import { CommandRegistry, ContributionProvider } from '@theia/core';
|
|
28
28
|
import {
|
|
29
29
|
codicon,
|
|
30
|
-
CommonCommands,
|
|
31
30
|
CompositeTreeNode,
|
|
32
31
|
ContextMenuRenderer,
|
|
33
32
|
HoverService,
|
|
@@ -44,6 +43,7 @@ import {
|
|
|
44
43
|
inject,
|
|
45
44
|
injectable,
|
|
46
45
|
named,
|
|
46
|
+
optional,
|
|
47
47
|
postConstruct
|
|
48
48
|
} from '@theia/core/shared/inversify';
|
|
49
49
|
import * as React from '@theia/core/shared/react';
|
|
@@ -66,10 +66,16 @@ export interface ResponseNode extends TreeNode {
|
|
|
66
66
|
}
|
|
67
67
|
export const isResponseNode = (node: TreeNode): node is ResponseNode => 'response' in node;
|
|
68
68
|
|
|
69
|
-
function isEnterKey(e: React.KeyboardEvent): boolean {
|
|
69
|
+
export function isEnterKey(e: React.KeyboardEvent): boolean {
|
|
70
70
|
return Key.ENTER.keyCode === KeyCode.createKeyCode(e.nativeEvent).key?.keyCode;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export const ChatWelcomeMessageProvider = Symbol('ChatWelcomeMessageProvider');
|
|
74
|
+
export interface ChatWelcomeMessageProvider {
|
|
75
|
+
renderWelcomeMessage?(): React.ReactNode;
|
|
76
|
+
renderDisabledMessage?(): React.ReactNode;
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
@injectable()
|
|
74
80
|
export class ChatViewTreeWidget extends TreeWidget {
|
|
75
81
|
static readonly ID = 'chat-tree-widget';
|
|
@@ -88,13 +94,16 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
88
94
|
protected readonly variableService: AIVariableService;
|
|
89
95
|
|
|
90
96
|
@inject(CommandRegistry)
|
|
91
|
-
|
|
97
|
+
protected commandRegistry: CommandRegistry;
|
|
92
98
|
|
|
93
99
|
@inject(OpenerService)
|
|
94
100
|
protected readonly openerService: OpenerService;
|
|
95
101
|
|
|
96
102
|
@inject(HoverService)
|
|
97
|
-
|
|
103
|
+
protected hoverService: HoverService;
|
|
104
|
+
|
|
105
|
+
@inject(ChatWelcomeMessageProvider) @optional()
|
|
106
|
+
protected welcomeMessageProvider?: ChatWelcomeMessageProvider;
|
|
98
107
|
|
|
99
108
|
protected _shouldScrollToEnd = true;
|
|
100
109
|
|
|
@@ -142,80 +151,24 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
protected override renderTree(model: TreeModel): React.ReactNode {
|
|
145
|
-
if (this.isEnabled) {
|
|
154
|
+
if (!this.isEnabled) {
|
|
155
|
+
return this.renderDisabledMessage();
|
|
156
|
+
}
|
|
157
|
+
if (CompositeTreeNode.is(model.root) && model.root.children?.length > 0) {
|
|
146
158
|
return super.renderTree(model);
|
|
147
159
|
}
|
|
148
|
-
return this.
|
|
160
|
+
return this.renderWelcomeMessage();
|
|
149
161
|
}
|
|
150
162
|
|
|
151
|
-
|
|
152
|
-
return
|
|
153
|
-
<div className='theia-ResponseNode-Content' key={'disabled-message'}>
|
|
154
|
-
<div className="disable-message">
|
|
155
|
-
<span className="section-header">{
|
|
156
|
-
nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiFeatureHeader', '🚀 AI Features Available (Alpha Version)!')}
|
|
157
|
-
</span>
|
|
158
|
-
<div className="section-title">
|
|
159
|
-
<p><code>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/featuresDisabled', 'Currently, all AI Features are disabled!')}</code></p>
|
|
160
|
-
</div>
|
|
161
|
-
<div className="section-title">
|
|
162
|
-
<p>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/howToEnable', 'How to Enable the AI Features:')}</p>
|
|
163
|
-
</div>
|
|
164
|
-
<div className="section-content">
|
|
165
|
-
<p>To enable the AI features, please go to
|
|
166
|
-
{this.renderLinkButton(nls.localize('theia/ai/chat-ui/chat-view-tree-widget/settingsMenu', 'the settings menu'), CommonCommands.OPEN_PREFERENCES.id)}
|
|
167
|
-
and locate the <strong>AI Features</strong> section.</p>
|
|
168
|
-
<ol>
|
|
169
|
-
<li>Toggle the switch for <strong>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiFeaturesEnable', 'Ai-features: Enable')}</strong>.</li>
|
|
170
|
-
<li>Provide at least one LLM provider (e.g. OpenAI), also see <a href="https://theia-ide.org/docs/user_ai/" target="_blank">the documentation</a>
|
|
171
|
-
for more information.</li>
|
|
172
|
-
</ol>
|
|
173
|
-
<p>This will activate the AI capabilities in the app. Please remember, these features are <strong>in an alpha state</strong>,
|
|
174
|
-
so they may change and we are working on improving them 🚧.<br></br>
|
|
175
|
-
Please support us by <a href="https://github.com/eclipse-theia/theia">providing feedback
|
|
176
|
-
</a>!</p>
|
|
177
|
-
</div>
|
|
178
|
-
|
|
179
|
-
<div className="section-title">
|
|
180
|
-
<p>Currently Supported Views and Features:</p>
|
|
181
|
-
</div>
|
|
182
|
-
<div className="section-content">
|
|
183
|
-
<p>Once the AI features are enabled, you can access the following views and features:</p>
|
|
184
|
-
<ul>
|
|
185
|
-
<li>Code Completion</li>
|
|
186
|
-
<li>Terminal Assistance (via CTRL+I in a terminal)</li>
|
|
187
|
-
<li>This Chat View (features the following agents):
|
|
188
|
-
<ul>
|
|
189
|
-
<li>Universal Chat Agent</li>
|
|
190
|
-
<li>Coder Chat Agent</li>
|
|
191
|
-
<li>Architect Chat Agent</li>
|
|
192
|
-
<li>Command Chat Agent</li>
|
|
193
|
-
<li>Orchestrator Chat Agent</li>
|
|
194
|
-
</ul>
|
|
195
|
-
</li>
|
|
196
|
-
<li>{this.renderLinkButton(nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiHistoryView', 'AI History View'), 'aiHistory:open')}</li>
|
|
197
|
-
<li>{this.renderLinkButton(
|
|
198
|
-
nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiConfigurationView', 'AI Configuration View'), 'aiConfiguration:open')}
|
|
199
|
-
</li>
|
|
200
|
-
</ul>
|
|
201
|
-
<p>See <a href="https://theia-ide.org/docs/user_ai/" target="_blank">the documentation</a> for more information.</p>
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
</div >;
|
|
163
|
+
protected renderDisabledMessage(): React.ReactNode {
|
|
164
|
+
return this.welcomeMessageProvider?.renderDisabledMessage?.() ?? <></>;
|
|
206
165
|
}
|
|
207
166
|
|
|
208
|
-
|
|
209
|
-
return
|
|
210
|
-
role={'button'}
|
|
211
|
-
tabIndex={0}
|
|
212
|
-
onClick={() => this.commandRegistry.executeCommand(openCommandId)}
|
|
213
|
-
onKeyDown={e => isEnterKey(e) && this.commandRegistry.executeCommand(openCommandId)}>
|
|
214
|
-
{title}
|
|
215
|
-
</a>;
|
|
167
|
+
protected renderWelcomeMessage(): React.ReactNode {
|
|
168
|
+
return this.welcomeMessageProvider?.renderWelcomeMessage?.() ?? <></>;
|
|
216
169
|
}
|
|
217
170
|
|
|
218
|
-
|
|
171
|
+
protected mapRequestToNode(request: ChatRequestModel): RequestNode {
|
|
219
172
|
return {
|
|
220
173
|
id: request.id,
|
|
221
174
|
parent: this.model.root as CompositeTreeNode,
|
|
@@ -223,7 +176,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
223
176
|
};
|
|
224
177
|
}
|
|
225
178
|
|
|
226
|
-
|
|
179
|
+
protected mapResponseToNode(response: ChatResponseModel): ResponseNode {
|
|
227
180
|
return {
|
|
228
181
|
id: response.id,
|
|
229
182
|
parent: this.model.root as CompositeTreeNode,
|
|
@@ -259,7 +212,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
259
212
|
return super.getScrollToRow();
|
|
260
213
|
}
|
|
261
214
|
|
|
262
|
-
|
|
215
|
+
protected async recreateModelTree(chatModel: ChatModel): Promise<void> {
|
|
263
216
|
if (CompositeTreeNode.is(this.model.root)) {
|
|
264
217
|
const nodes: TreeNode[] = [];
|
|
265
218
|
chatModel.getRequests().forEach(request => {
|
|
@@ -289,7 +242,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
289
242
|
</React.Fragment>;
|
|
290
243
|
}
|
|
291
244
|
|
|
292
|
-
|
|
245
|
+
protected renderAgent(node: RequestNode | ResponseNode): React.ReactNode {
|
|
293
246
|
const inProgress = isResponseNode(node) && !node.response.isComplete && !node.response.isCanceled && !node.response.isError;
|
|
294
247
|
const waitingForInput = isResponseNode(node) && node.response.isWaitingForInput;
|
|
295
248
|
const toolbarContributions = !inProgress
|
|
@@ -345,7 +298,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
345
298
|
</React.Fragment>;
|
|
346
299
|
}
|
|
347
300
|
|
|
348
|
-
|
|
301
|
+
protected getAgentLabel(node: RequestNode | ResponseNode): string {
|
|
349
302
|
if (isRequestNode(node)) {
|
|
350
303
|
// TODO find user name
|
|
351
304
|
return nls.localize('theia/ai/chat-ui/chat-view-tree-widget/you', 'You');
|
|
@@ -353,14 +306,14 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
353
306
|
return this.getAgent(node)?.name ?? nls.localize('theia/ai/chat-ui/chat-view-tree-widget/ai', 'AI');
|
|
354
307
|
}
|
|
355
308
|
|
|
356
|
-
|
|
309
|
+
protected getAgent(node: RequestNode | ResponseNode): ChatAgent | undefined {
|
|
357
310
|
if (isRequestNode(node)) {
|
|
358
311
|
return undefined;
|
|
359
312
|
}
|
|
360
313
|
return node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined;
|
|
361
314
|
}
|
|
362
315
|
|
|
363
|
-
|
|
316
|
+
protected getAgentIconClassName(node: RequestNode | ResponseNode): string | undefined {
|
|
364
317
|
if (isRequestNode(node)) {
|
|
365
318
|
return codicon('account');
|
|
366
319
|
}
|
|
@@ -369,7 +322,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
369
322
|
return agent?.iconClass ?? codicon('copilot');
|
|
370
323
|
}
|
|
371
324
|
|
|
372
|
-
|
|
325
|
+
protected renderDetail(node: RequestNode | ResponseNode): React.ReactNode {
|
|
373
326
|
if (isRequestNode(node)) {
|
|
374
327
|
return this.renderChatRequest(node);
|
|
375
328
|
}
|
|
@@ -378,7 +331,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
378
331
|
};
|
|
379
332
|
}
|
|
380
333
|
|
|
381
|
-
|
|
334
|
+
protected renderChatRequest(node: RequestNode): React.ReactNode {
|
|
382
335
|
return <ChatRequestRender
|
|
383
336
|
node={node}
|
|
384
337
|
hoverService={this.hoverService}
|
|
@@ -388,7 +341,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
388
341
|
/>;
|
|
389
342
|
}
|
|
390
343
|
|
|
391
|
-
|
|
344
|
+
protected renderChatResponse(node: ResponseNode): React.ReactNode {
|
|
392
345
|
return (
|
|
393
346
|
<div className={'theia-ResponseNode'}>
|
|
394
347
|
{!node.response.isComplete
|
|
@@ -419,7 +372,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
419
372
|
);
|
|
420
373
|
}
|
|
421
374
|
|
|
422
|
-
|
|
375
|
+
protected getChatResponsePartRenderer(content: ChatResponseContent, node: ResponseNode): React.ReactNode {
|
|
423
376
|
const renderer = this.chatResponsePartRenderers.getContributions().reduce<[number, ChatResponsePartRenderer<ChatResponseContent> | undefined]>(
|
|
424
377
|
(prev, current) => {
|
|
425
378
|
const prio = current.canHandle(content);
|
|
@@ -32,6 +32,12 @@ export namespace ChatCommands {
|
|
|
32
32
|
category: CHAT_CATEGORY,
|
|
33
33
|
iconClass: codicon('lock')
|
|
34
34
|
}, '', CHAT_CATEGORY_KEY);
|
|
35
|
+
|
|
36
|
+
export const EDIT_SESSION_SETTINGS = Command.toLocalizedCommand({
|
|
37
|
+
id: 'chat:widget:session-settings',
|
|
38
|
+
category: CHAT_CATEGORY,
|
|
39
|
+
iconClass: codicon('bracket')
|
|
40
|
+
}, 'Set Session Settings', CHAT_CATEGORY_KEY);
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
export const AI_CHAT_NEW_CHAT_WINDOW_COMMAND: Command = {
|
|
@@ -25,6 +25,7 @@ import { ProviderResult } from '@theia/monaco-editor-core/esm/vs/editor/common/l
|
|
|
25
25
|
import { ChatViewWidget } from './chat-view-widget';
|
|
26
26
|
|
|
27
27
|
export const CHAT_VIEW_LANGUAGE_ID = 'theia-ai-chat-view-language';
|
|
28
|
+
export const SETTINGS_LANGUAGE_ID = 'theia-ai-chat-settings-language';
|
|
28
29
|
export const CHAT_VIEW_LANGUAGE_EXTENSION = 'aichatviewlanguage';
|
|
29
30
|
|
|
30
31
|
const VARIABLE_RESOLUTION_CONTEXT = { context: 'chat-input-autocomplete' };
|
|
@@ -58,6 +59,7 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
58
59
|
|
|
59
60
|
onStart(_app: FrontendApplication): MaybePromise<void> {
|
|
60
61
|
monaco.languages.register({ id: CHAT_VIEW_LANGUAGE_ID, extensions: [CHAT_VIEW_LANGUAGE_EXTENSION] });
|
|
62
|
+
monaco.languages.register({ id: SETTINGS_LANGUAGE_ID, extensions: ['json'], filenames: ['editor'] });
|
|
61
63
|
|
|
62
64
|
this.registerCompletionProviders();
|
|
63
65
|
|
|
@@ -17,22 +17,45 @@
|
|
|
17
17
|
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
18
18
|
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
19
19
|
import { AIChatContribution } from './ai-chat-ui-contribution';
|
|
20
|
-
import { Emitter, nls } from '@theia/core';
|
|
20
|
+
import { Emitter, InMemoryResources, URI, nls } from '@theia/core';
|
|
21
21
|
import { ChatCommands } from './chat-view-commands';
|
|
22
|
+
import { CommandRegistry } from '@theia/core/lib/common/command';
|
|
23
|
+
import { SessionSettingsDialog } from './session-settings-dialog';
|
|
24
|
+
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
|
25
|
+
import { ChatViewWidget } from './chat-view-widget';
|
|
22
26
|
|
|
23
27
|
@injectable()
|
|
24
28
|
export class ChatViewWidgetToolbarContribution implements TabBarToolbarContribution {
|
|
25
29
|
@inject(AIChatContribution)
|
|
26
30
|
protected readonly chatContribution: AIChatContribution;
|
|
27
31
|
|
|
32
|
+
@inject(CommandRegistry)
|
|
33
|
+
protected readonly commandRegistry: CommandRegistry;
|
|
34
|
+
|
|
35
|
+
@inject(MonacoEditorProvider)
|
|
36
|
+
protected readonly editorProvider: MonacoEditorProvider;
|
|
37
|
+
|
|
38
|
+
@inject(InMemoryResources)
|
|
39
|
+
protected readonly resources: InMemoryResources;
|
|
40
|
+
|
|
28
41
|
protected readonly onChatWidgetStateChangedEmitter = new Emitter<void>();
|
|
29
42
|
protected readonly onChatWidgetStateChanged = this.onChatWidgetStateChangedEmitter.event;
|
|
30
43
|
|
|
44
|
+
private readonly sessionSettingsURI = new URI('chat-view:/settings.json');
|
|
45
|
+
|
|
31
46
|
@postConstruct()
|
|
32
47
|
protected init(): void {
|
|
48
|
+
this.resources.add(this.sessionSettingsURI, '{}');
|
|
49
|
+
|
|
33
50
|
this.chatContribution.widget.then(widget => {
|
|
34
51
|
widget.onStateChanged(() => this.onChatWidgetStateChangedEmitter.fire());
|
|
35
52
|
});
|
|
53
|
+
|
|
54
|
+
this.commandRegistry.registerCommand(ChatCommands.EDIT_SESSION_SETTINGS, {
|
|
55
|
+
execute: () => this.openJsonDataDialog(),
|
|
56
|
+
isEnabled: widget => widget instanceof ChatViewWidget,
|
|
57
|
+
isVisible: widget => widget instanceof ChatViewWidget
|
|
58
|
+
});
|
|
36
59
|
}
|
|
37
60
|
|
|
38
61
|
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
|
@@ -50,5 +73,28 @@ export class ChatViewWidgetToolbarContribution implements TabBarToolbarContribut
|
|
|
50
73
|
onDidChange: this.onChatWidgetStateChanged,
|
|
51
74
|
priority: 2
|
|
52
75
|
});
|
|
76
|
+
registry.registerItem({
|
|
77
|
+
id: ChatCommands.EDIT_SESSION_SETTINGS.id,
|
|
78
|
+
command: ChatCommands.EDIT_SESSION_SETTINGS.id,
|
|
79
|
+
tooltip: nls.localize('theia/ai/session-settings-dialog/tooltip', 'Set Session Settings'),
|
|
80
|
+
priority: 3
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected async openJsonDataDialog(): Promise<void> {
|
|
85
|
+
const widget = await this.chatContribution.widget;
|
|
86
|
+
if (!widget) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const dialog = new SessionSettingsDialog(this.editorProvider, this.resources, this.sessionSettingsURI, {
|
|
91
|
+
initialSettings: widget.getSettings()
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const result = await dialog.open();
|
|
95
|
+
if (result) {
|
|
96
|
+
widget.setSettings(result);
|
|
97
|
+
}
|
|
98
|
+
|
|
53
99
|
}
|
|
54
100
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import { CommandService, deepClone, Emitter, Event, MessageService } from '@theia/core';
|
|
17
|
-
import { ChatRequest, ChatRequestModel, ChatService, ChatSession } from '@theia/ai-chat';
|
|
17
|
+
import { ChatRequest, ChatRequestModel, ChatService, ChatSession, isActiveSessionChangedEvent, MutableChatModel } from '@theia/ai-chat';
|
|
18
18
|
import { BaseWidget, codicon, ExtractableWidget, Message, PanelLayout, PreferenceService, StatefulWidget } from '@theia/core/lib/browser';
|
|
19
19
|
import { nls } from '@theia/core/lib/common/nls';
|
|
20
20
|
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
@@ -33,7 +33,7 @@ export namespace ChatViewWidget {
|
|
|
33
33
|
export class ChatViewWidget extends BaseWidget implements ExtractableWidget, StatefulWidget {
|
|
34
34
|
|
|
35
35
|
public static ID = 'chat-view-widget';
|
|
36
|
-
static LABEL =
|
|
36
|
+
static LABEL = nls.localize('theia/ai/chat/view/label', 'AI Chat');
|
|
37
37
|
|
|
38
38
|
@inject(ChatService)
|
|
39
39
|
protected chatService: ChatService;
|
|
@@ -114,7 +114,10 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
114
114
|
|
|
115
115
|
protected initListeners(): void {
|
|
116
116
|
this.toDispose.push(
|
|
117
|
-
this.chatService.
|
|
117
|
+
this.chatService.onSessionEvent(event => {
|
|
118
|
+
if (!isActiveSessionChangedEvent(event)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
118
121
|
const session = event.sessionId ? this.chatService.getSession(event.sessionId) : this.chatService.createSession();
|
|
119
122
|
if (session) {
|
|
120
123
|
this.chatSession = session;
|
|
@@ -217,4 +220,15 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
217
220
|
addContext(variable: AIVariableResolutionRequest): void {
|
|
218
221
|
this.inputWidget.addContext(variable);
|
|
219
222
|
}
|
|
223
|
+
|
|
224
|
+
setSettings(settings: { [key: string]: unknown }): void {
|
|
225
|
+
if (this.chatSession && this.chatSession.model) {
|
|
226
|
+
const model = this.chatSession.model as MutableChatModel;
|
|
227
|
+
model.setSettings(settings);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
getSettings(): { [key: string]: unknown } | undefined {
|
|
232
|
+
return this.chatSession.model.settings;
|
|
233
|
+
}
|
|
220
234
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { InMemoryResources, URI, nls } from '@theia/core';
|
|
18
|
+
import { AbstractDialog } from '@theia/core/lib/browser/dialogs';
|
|
19
|
+
|
|
20
|
+
import { Message } from '@theia/core/lib/browser';
|
|
21
|
+
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
|
22
|
+
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
|
23
|
+
|
|
24
|
+
export interface SessionSettingsDialogProps {
|
|
25
|
+
initialSettings: { [key: string]: unknown } | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class SessionSettingsDialog extends AbstractDialog<{ [key: string]: unknown }> {
|
|
29
|
+
|
|
30
|
+
protected jsonEditor: MonacoEditor | undefined;
|
|
31
|
+
protected dialogContent: HTMLDivElement;
|
|
32
|
+
protected errorMessageDiv: HTMLDivElement;
|
|
33
|
+
protected settings: { [key: string]: unknown } = {};
|
|
34
|
+
protected initialSettingsString: string;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
protected readonly editorProvider: MonacoEditorProvider,
|
|
38
|
+
protected readonly resources: InMemoryResources,
|
|
39
|
+
protected readonly uri: URI,
|
|
40
|
+
protected readonly options: SessionSettingsDialogProps
|
|
41
|
+
) {
|
|
42
|
+
super({
|
|
43
|
+
title: nls.localize('theia/ai/session-settings-dialog/title', 'Set Session Settings')
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const initialSettings = options.initialSettings;
|
|
47
|
+
this.initialSettingsString = JSON.stringify(initialSettings, undefined, 2) || '{}';
|
|
48
|
+
|
|
49
|
+
this.contentNode.classList.add('monaco-session-settings-dialog');
|
|
50
|
+
|
|
51
|
+
this.dialogContent = document.createElement('div');
|
|
52
|
+
this.dialogContent.className = 'session-settings-container';
|
|
53
|
+
this.contentNode.appendChild(this.dialogContent);
|
|
54
|
+
|
|
55
|
+
this.errorMessageDiv = document.createElement('div');
|
|
56
|
+
this.errorMessageDiv.className = 'session-settings-error';
|
|
57
|
+
this.contentNode.appendChild(this.errorMessageDiv);
|
|
58
|
+
|
|
59
|
+
this.appendCloseButton(nls.localizeByDefault('Cancel'));
|
|
60
|
+
this.appendAcceptButton(nls.localizeByDefault('Apply'));
|
|
61
|
+
|
|
62
|
+
this.createJsonEditor();
|
|
63
|
+
|
|
64
|
+
this.validateJson();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected override onAfterAttach(msg: Message): void {
|
|
68
|
+
super.onAfterAttach(msg);
|
|
69
|
+
this.update();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected override onActivateRequest(msg: Message): void {
|
|
73
|
+
super.onActivateRequest(msg);
|
|
74
|
+
if (this.jsonEditor) {
|
|
75
|
+
this.jsonEditor.focus();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
protected async createJsonEditor(): Promise<void> {
|
|
79
|
+
|
|
80
|
+
this.resources.update(this.uri, this.initialSettingsString);
|
|
81
|
+
try {
|
|
82
|
+
const editor = await this.editorProvider.createInline(this.uri, this.dialogContent, {
|
|
83
|
+
language: 'json',
|
|
84
|
+
automaticLayout: true,
|
|
85
|
+
minimap: {
|
|
86
|
+
enabled: false
|
|
87
|
+
},
|
|
88
|
+
scrollBeyondLastLine: false,
|
|
89
|
+
folding: true,
|
|
90
|
+
lineNumbers: 'on',
|
|
91
|
+
fontSize: 13,
|
|
92
|
+
wordWrap: 'on',
|
|
93
|
+
renderValidationDecorations: 'on',
|
|
94
|
+
scrollbar: {
|
|
95
|
+
vertical: 'auto',
|
|
96
|
+
horizontal: 'auto'
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
editor.getControl().onDidChangeModelContent(() => {
|
|
101
|
+
this.validateJson();
|
|
102
|
+
});
|
|
103
|
+
editor.document.textEditorModel.setValue(this.initialSettingsString);
|
|
104
|
+
|
|
105
|
+
this.jsonEditor = editor;
|
|
106
|
+
this.validateJson();
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('Failed to create JSON editor:', error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected validateJson(): void {
|
|
113
|
+
if (!this.jsonEditor) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const jsonContent = this.jsonEditor.getControl().getValue();
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
this.settings = JSON.parse(jsonContent);
|
|
121
|
+
this.errorMessageDiv.textContent = '';
|
|
122
|
+
this.setErrorButtonState(false);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
this.errorMessageDiv.textContent = `${error}`;
|
|
125
|
+
this.setErrorButtonState(true);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected setErrorButtonState(isError: boolean): void {
|
|
130
|
+
const acceptButton = this.acceptButton;
|
|
131
|
+
if (acceptButton) {
|
|
132
|
+
acceptButton.disabled = isError;
|
|
133
|
+
if (isError) {
|
|
134
|
+
acceptButton.classList.add('disabled');
|
|
135
|
+
} else {
|
|
136
|
+
acceptButton.classList.remove('disabled');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get value(): { [key: string]: unknown } {
|
|
142
|
+
return this.settings;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -504,7 +504,6 @@ div:last-child > .theia-ChatNode {
|
|
|
504
504
|
display: flex;
|
|
505
505
|
flex-direction: column;
|
|
506
506
|
gap: 2px;
|
|
507
|
-
border: var(--theia-border-width) solid var(--theia-input-border);
|
|
508
507
|
border-radius: 4px;
|
|
509
508
|
}
|
|
510
509
|
|
|
@@ -537,12 +536,6 @@ div:last-child > .theia-ChatNode {
|
|
|
537
536
|
background-color: var(--theia-toolbar-hoverBackground);
|
|
538
537
|
}
|
|
539
538
|
|
|
540
|
-
.theia-CodePartRenderer-separator {
|
|
541
|
-
width: 100%;
|
|
542
|
-
height: 1px;
|
|
543
|
-
background-color: var(--theia-input-border);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
539
|
.theia-QuestionPartRenderer-root {
|
|
547
540
|
display: flex;
|
|
548
541
|
flex-direction: column;
|
|
@@ -574,7 +567,8 @@ div:last-child > .theia-ChatNode {
|
|
|
574
567
|
background-color: var(--theia-button-secondaryBackground);
|
|
575
568
|
}
|
|
576
569
|
|
|
577
|
-
.theia-toolCall
|
|
570
|
+
.theia-toolCall,
|
|
571
|
+
.theia-thinking {
|
|
578
572
|
font-weight: normal;
|
|
579
573
|
color: var(--theia-descriptionForeground);
|
|
580
574
|
line-height: 20px;
|
|
@@ -584,11 +578,14 @@ div:last-child > .theia-ChatNode {
|
|
|
584
578
|
}
|
|
585
579
|
|
|
586
580
|
.theia-toolCall .fa,
|
|
587
|
-
.theia-toolCall details summary::marker
|
|
581
|
+
.theia-toolCall details summary::marker,
|
|
582
|
+
.theia-thinking .fa,
|
|
583
|
+
.theia-thinking details summary::marker {
|
|
588
584
|
color: var(--theia-button-background);
|
|
589
585
|
}
|
|
590
586
|
|
|
591
|
-
.theia-toolCall details pre
|
|
587
|
+
.theia-toolCall details pre,
|
|
588
|
+
.theia-thinking details pre {
|
|
592
589
|
cursor: text;
|
|
593
590
|
line-height: 1rem;
|
|
594
591
|
margin-top: 0;
|
|
@@ -596,6 +593,7 @@ div:last-child > .theia-ChatNode {
|
|
|
596
593
|
padding: 6px;
|
|
597
594
|
background-color: var(--theia-editor-background);
|
|
598
595
|
overflow: auto;
|
|
596
|
+
text-wrap: auto;
|
|
599
597
|
}
|
|
600
598
|
|
|
601
599
|
.collapsible-arguments {
|
|
@@ -666,12 +664,6 @@ details[open].collapsible-arguments .collapsible-arguments-summary {
|
|
|
666
664
|
margin: 20px 0px;
|
|
667
665
|
}
|
|
668
666
|
|
|
669
|
-
.disable-message {
|
|
670
|
-
font-size: 12px;
|
|
671
|
-
line-height: 1.6;
|
|
672
|
-
padding: 15px;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
667
|
.section-content p {
|
|
676
668
|
margin: 10px 0;
|
|
677
669
|
}
|
|
@@ -683,3 +675,26 @@ details[open].collapsible-arguments .collapsible-arguments-summary {
|
|
|
683
675
|
.section-content strong {
|
|
684
676
|
font-weight: bold;
|
|
685
677
|
}
|
|
678
|
+
|
|
679
|
+
.session-settings-container {
|
|
680
|
+
margin-bottom: 10px;
|
|
681
|
+
height: calc(100% - 50px);
|
|
682
|
+
}
|
|
683
|
+
.monaco-session-settings-dialog {
|
|
684
|
+
flex: 1;
|
|
685
|
+
min-height: 350px;
|
|
686
|
+
min-width: 500px;
|
|
687
|
+
height: 25vh;
|
|
688
|
+
width: 30vh;
|
|
689
|
+
border: 1px solid var(--theia-editorWidget-border);
|
|
690
|
+
margin-bottom: 10px;
|
|
691
|
+
}
|
|
692
|
+
.session-settings-error {
|
|
693
|
+
color: var(--theia-errorForeground);
|
|
694
|
+
min-height: 1em;
|
|
695
|
+
display: block;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.session-settings-container .monaco-editor {
|
|
699
|
+
outline-color: var(--theia-editor-background);
|
|
700
|
+
}
|