@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.
Files changed (51) hide show
  1. package/lib/browser/ai-chat-ui-contribution.d.ts +1 -0
  2. package/lib/browser/ai-chat-ui-contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-chat-ui-contribution.js +46 -7
  4. package/lib/browser/ai-chat-ui-contribution.js.map +1 -1
  5. package/lib/browser/ai-chat-ui-frontend-module.d.ts.map +1 -1
  6. package/lib/browser/ai-chat-ui-frontend-module.js +1 -0
  7. package/lib/browser/ai-chat-ui-frontend-module.js.map +1 -1
  8. package/lib/browser/chat-response-renderer/index.d.ts +1 -0
  9. package/lib/browser/chat-response-renderer/index.d.ts.map +1 -1
  10. package/lib/browser/chat-response-renderer/index.js +1 -0
  11. package/lib/browser/chat-response-renderer/index.js.map +1 -1
  12. package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts +9 -0
  13. package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts.map +1 -0
  14. package/lib/browser/chat-response-renderer/thinking-part-renderer.js +42 -0
  15. package/lib/browser/chat-response-renderer/thinking-part-renderer.js.map +1 -0
  16. package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts +25 -18
  17. package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts.map +1 -1
  18. package/lib/browser/chat-tree-view/chat-view-tree-widget.js +19 -59
  19. package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
  20. package/lib/browser/chat-view-commands.d.ts +1 -0
  21. package/lib/browser/chat-view-commands.d.ts.map +1 -1
  22. package/lib/browser/chat-view-commands.js +5 -0
  23. package/lib/browser/chat-view-commands.js.map +1 -1
  24. package/lib/browser/chat-view-language-contribution.d.ts +1 -0
  25. package/lib/browser/chat-view-language-contribution.d.ts.map +1 -1
  26. package/lib/browser/chat-view-language-contribution.js +3 -1
  27. package/lib/browser/chat-view-language-contribution.js.map +1 -1
  28. package/lib/browser/chat-view-widget-toolbar-contribution.d.ts +8 -1
  29. package/lib/browser/chat-view-widget-toolbar-contribution.d.ts.map +1 -1
  30. package/lib/browser/chat-view-widget-toolbar-contribution.js +42 -0
  31. package/lib/browser/chat-view-widget-toolbar-contribution.js.map +1 -1
  32. package/lib/browser/chat-view-widget.d.ts +6 -0
  33. package/lib/browser/chat-view-widget.d.ts.map +1 -1
  34. package/lib/browser/chat-view-widget.js +14 -2
  35. package/lib/browser/chat-view-widget.js.map +1 -1
  36. package/lib/browser/session-settings-dialog.d.ts +35 -0
  37. package/lib/browser/session-settings-dialog.d.ts.map +1 -0
  38. package/lib/browser/session-settings-dialog.js +118 -0
  39. package/lib/browser/session-settings-dialog.js.map +1 -0
  40. package/package.json +13 -12
  41. package/src/browser/ai-chat-ui-contribution.ts +42 -8
  42. package/src/browser/ai-chat-ui-frontend-module.ts +2 -0
  43. package/src/browser/chat-response-renderer/index.ts +1 -0
  44. package/src/browser/chat-response-renderer/thinking-part-renderer.tsx +44 -0
  45. package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +33 -80
  46. package/src/browser/chat-view-commands.ts +6 -0
  47. package/src/browser/chat-view-language-contribution.ts +2 -0
  48. package/src/browser/chat-view-widget-toolbar-contribution.tsx +47 -1
  49. package/src/browser/chat-view-widget.tsx +17 -3
  50. package/src/browser/session-settings-dialog.tsx +144 -0
  51. 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
- private commandRegistry: CommandRegistry;
97
+ protected commandRegistry: CommandRegistry;
92
98
 
93
99
  @inject(OpenerService)
94
100
  protected readonly openerService: OpenerService;
95
101
 
96
102
  @inject(HoverService)
97
- private hoverService: HoverService;
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.renderDisabledMessage();
160
+ return this.renderWelcomeMessage();
149
161
  }
150
162
 
151
- private renderDisabledMessage(): React.ReactNode {
152
- return <div className={'theia-ResponseNode'}>
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 &nbsp;
166
- {this.renderLinkButton(nls.localize('theia/ai/chat-ui/chat-view-tree-widget/settingsMenu', 'the settings menu'), CommonCommands.OPEN_PREFERENCES.id)}
167
- &nbsp;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
- private renderLinkButton(title: string, openCommandId: string): React.ReactNode {
209
- return <a
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
- private mapRequestToNode(request: ChatRequestModel): RequestNode {
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
- private mapResponseToNode(response: ChatResponseModel): ResponseNode {
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
- private async recreateModelTree(chatModel: ChatModel): Promise<void> {
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
- private renderAgent(node: RequestNode | ResponseNode): React.ReactNode {
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
- private getAgentLabel(node: RequestNode | ResponseNode): string {
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
- private getAgent(node: RequestNode | ResponseNode): ChatAgent | undefined {
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
- private getAgentIconClassName(node: RequestNode | ResponseNode): string | undefined {
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
- private renderDetail(node: RequestNode | ResponseNode): React.ReactNode {
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
- private renderChatRequest(node: RequestNode): React.ReactNode {
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
- private renderChatResponse(node: ResponseNode): React.ReactNode {
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
- private getChatResponsePartRenderer(content: ChatResponseContent, node: ResponseNode): React.ReactNode {
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 = `${nls.localizeByDefault('Chat')}`;
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.onActiveSessionChanged(event => {
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
+ }