@theia/ai-chat-ui 1.55.1 → 1.57.0-next.22
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/README.md +2 -1
- package/lib/browser/ai-chat-ui-contribution.js +1 -1
- 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 +2 -0
- package/lib/browser/ai-chat-ui-frontend-module.js.map +1 -1
- package/lib/browser/chat-input-widget.js +34 -22
- package/lib/browser/chat-input-widget.js.map +1 -1
- package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts +10 -4
- package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/markdown-part-renderer.js +41 -11
- package/lib/browser/chat-response-renderer/markdown-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/question-part-renderer.d.ts +10 -0
- package/lib/browser/chat-response-renderer/question-part-renderer.d.ts.map +1 -0
- package/lib/browser/chat-response-renderer/question-part-renderer.js +43 -0
- package/lib/browser/chat-response-renderer/question-part-renderer.js.map +1 -0
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts +2 -0
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js +30 -10
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts +6 -1
- 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 +85 -14
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
- package/package.json +11 -11
- package/src/browser/ai-chat-ui-contribution.ts +1 -1
- package/src/browser/ai-chat-ui-frontend-module.ts +2 -0
- package/src/browser/chat-input-widget.tsx +35 -20
- package/src/browser/chat-response-renderer/markdown-part-renderer.tsx +42 -13
- package/src/browser/chat-response-renderer/question-part-renderer.tsx +59 -0
- package/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +38 -9
- package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +141 -12
- package/src/browser/style/index.css +58 -1
|
@@ -14,12 +14,15 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import {
|
|
17
|
+
ChatAgent,
|
|
17
18
|
ChatAgentService,
|
|
18
19
|
ChatModel,
|
|
19
20
|
ChatProgressMessage,
|
|
20
21
|
ChatRequestModel,
|
|
21
22
|
ChatResponseContent,
|
|
22
23
|
ChatResponseModel,
|
|
24
|
+
ParsedChatRequestAgentPart,
|
|
25
|
+
ParsedChatRequestVariablePart,
|
|
23
26
|
} from '@theia/ai-chat';
|
|
24
27
|
import { CommandRegistry, ContributionProvider } from '@theia/core';
|
|
25
28
|
import {
|
|
@@ -27,9 +30,11 @@ import {
|
|
|
27
30
|
CommonCommands,
|
|
28
31
|
CompositeTreeNode,
|
|
29
32
|
ContextMenuRenderer,
|
|
33
|
+
HoverService,
|
|
30
34
|
Key,
|
|
31
35
|
KeyCode,
|
|
32
36
|
NodeProps,
|
|
37
|
+
OpenerService,
|
|
33
38
|
TreeModel,
|
|
34
39
|
TreeNode,
|
|
35
40
|
TreeProps,
|
|
@@ -46,6 +51,7 @@ import * as React from '@theia/core/shared/react';
|
|
|
46
51
|
import { ChatNodeToolbarActionContribution } from '../chat-node-toolbar-action-contribution';
|
|
47
52
|
import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
|
|
48
53
|
import { useMarkdownRendering } from '../chat-response-renderer/markdown-part-renderer';
|
|
54
|
+
import { AIVariableService } from '@theia/ai-core';
|
|
49
55
|
|
|
50
56
|
// TODO Instead of directly operating on the ChatRequestModel we could use an intermediate view model
|
|
51
57
|
export interface RequestNode extends TreeNode {
|
|
@@ -77,9 +83,18 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
77
83
|
@inject(ChatAgentService)
|
|
78
84
|
protected chatAgentService: ChatAgentService;
|
|
79
85
|
|
|
86
|
+
@inject(AIVariableService)
|
|
87
|
+
protected readonly variableService: AIVariableService;
|
|
88
|
+
|
|
80
89
|
@inject(CommandRegistry)
|
|
81
90
|
private commandRegistry: CommandRegistry;
|
|
82
91
|
|
|
92
|
+
@inject(OpenerService)
|
|
93
|
+
protected readonly openerService: OpenerService;
|
|
94
|
+
|
|
95
|
+
@inject(HoverService)
|
|
96
|
+
private hoverService: HoverService;
|
|
97
|
+
|
|
83
98
|
protected _shouldScrollToEnd = true;
|
|
84
99
|
|
|
85
100
|
protected isEnabled = false;
|
|
@@ -267,22 +282,39 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
267
282
|
|
|
268
283
|
private renderAgent(node: RequestNode | ResponseNode): React.ReactNode {
|
|
269
284
|
const inProgress = isResponseNode(node) && !node.response.isComplete && !node.response.isCanceled && !node.response.isError;
|
|
285
|
+
const waitingForInput = isResponseNode(node) && node.response.isWaitingForInput;
|
|
270
286
|
const toolbarContributions = !inProgress
|
|
271
287
|
? this.chatNodeToolbarActionContributions.getContributions()
|
|
272
288
|
.flatMap(c => c.getToolbarActions(node))
|
|
273
289
|
.filter(action => this.commandRegistry.isEnabled(action.commandId, node))
|
|
274
290
|
.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))
|
|
275
291
|
: [];
|
|
292
|
+
const agentLabel = React.createRef<HTMLHeadingElement>();
|
|
293
|
+
const agentDescription = this.getAgent(node)?.description;
|
|
276
294
|
return <React.Fragment>
|
|
277
295
|
<div className='theia-ChatNodeHeader'>
|
|
278
296
|
<div className={`theia-AgentAvatar ${this.getAgentIconClassName(node)}`}></div>
|
|
279
|
-
<h3
|
|
280
|
-
|
|
297
|
+
<h3 ref={agentLabel}
|
|
298
|
+
className='theia-AgentLabel'
|
|
299
|
+
onMouseEnter={() => {
|
|
300
|
+
if (agentDescription) {
|
|
301
|
+
this.hoverService.requestHover({
|
|
302
|
+
content: agentDescription,
|
|
303
|
+
target: agentLabel.current!,
|
|
304
|
+
position: 'right'
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}}>
|
|
308
|
+
{this.getAgentLabel(node)}
|
|
309
|
+
</h3>
|
|
310
|
+
{inProgress && !waitingForInput && <span className='theia-ChatContentInProgress'>Generating</span>}
|
|
311
|
+
{inProgress && waitingForInput && <span className='theia-ChatContentInProgress'>Waiting for input</span>}
|
|
281
312
|
<div className='theia-ChatNodeToolbar'>
|
|
282
313
|
{!inProgress &&
|
|
283
314
|
toolbarContributions.length > 0 &&
|
|
284
315
|
toolbarContributions.map(action =>
|
|
285
316
|
<span
|
|
317
|
+
key={action.commandId}
|
|
286
318
|
className={`theia-ChatNodeToolbarAction ${action.icon}`}
|
|
287
319
|
title={action.tooltip}
|
|
288
320
|
onClick={e => {
|
|
@@ -308,8 +340,14 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
308
340
|
// TODO find user name
|
|
309
341
|
return 'You';
|
|
310
342
|
}
|
|
311
|
-
|
|
312
|
-
|
|
343
|
+
return this.getAgent(node)?.name ?? 'AI';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private getAgent(node: RequestNode | ResponseNode): ChatAgent | undefined {
|
|
347
|
+
if (isRequestNode(node)) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
return node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined;
|
|
313
351
|
}
|
|
314
352
|
|
|
315
353
|
private getAgentIconClassName(node: RequestNode | ResponseNode): string | undefined {
|
|
@@ -331,7 +369,13 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
331
369
|
}
|
|
332
370
|
|
|
333
371
|
private renderChatRequest(node: RequestNode): React.ReactNode {
|
|
334
|
-
return <ChatRequestRender
|
|
372
|
+
return <ChatRequestRender
|
|
373
|
+
node={node}
|
|
374
|
+
hoverService={this.hoverService}
|
|
375
|
+
chatAgentService={this.chatAgentService}
|
|
376
|
+
variableService={this.variableService}
|
|
377
|
+
openerService={this.openerService}
|
|
378
|
+
/>;
|
|
335
379
|
}
|
|
336
380
|
|
|
337
381
|
private renderChatResponse(node: ResponseNode): React.ReactNode {
|
|
@@ -339,12 +383,28 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
339
383
|
<div className={'theia-ResponseNode'}>
|
|
340
384
|
{!node.response.isComplete
|
|
341
385
|
&& node.response.response.content.length === 0
|
|
342
|
-
&& node.response.progressMessages
|
|
343
|
-
|
|
344
|
-
|
|
386
|
+
&& node.response.progressMessages
|
|
387
|
+
.filter(c => c.show === 'untilFirstContent')
|
|
388
|
+
.map((c, i) =>
|
|
389
|
+
<ProgressMessage {...c} key={`${node.id}-progress-untilFirstContent-${i}`} />
|
|
390
|
+
)
|
|
391
|
+
}
|
|
345
392
|
{node.response.response.content.map((c, i) =>
|
|
346
393
|
<div className='theia-ResponseNode-Content' key={`${node.id}-content-${i}`}>{this.getChatResponsePartRenderer(c, node)}</div>
|
|
347
394
|
)}
|
|
395
|
+
{!node.response.isComplete
|
|
396
|
+
&& node.response.progressMessages
|
|
397
|
+
.filter(c => c.show === 'whileIncomplete')
|
|
398
|
+
.map((c, i) =>
|
|
399
|
+
<ProgressMessage {...c} key={`${node.id}-progress-whileIncomplete-${i}`} />
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
{node.response.progressMessages
|
|
403
|
+
.filter(c => c.show === 'forever')
|
|
404
|
+
.map((c, i) =>
|
|
405
|
+
<ProgressMessage {...c} key={`${node.id}-progress-afterComplete-${i}`} />
|
|
406
|
+
)
|
|
407
|
+
}
|
|
348
408
|
</div>
|
|
349
409
|
);
|
|
350
410
|
}
|
|
@@ -375,11 +435,80 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
375
435
|
}
|
|
376
436
|
}
|
|
377
437
|
|
|
378
|
-
const ChatRequestRender = (
|
|
379
|
-
|
|
380
|
-
|
|
438
|
+
const ChatRequestRender = (
|
|
439
|
+
{
|
|
440
|
+
node, hoverService, chatAgentService, variableService, openerService
|
|
441
|
+
}: {
|
|
442
|
+
node: RequestNode,
|
|
443
|
+
hoverService: HoverService,
|
|
444
|
+
chatAgentService: ChatAgentService,
|
|
445
|
+
variableService: AIVariableService,
|
|
446
|
+
openerService: OpenerService
|
|
447
|
+
}) => {
|
|
448
|
+
const parts = node.request.message.parts;
|
|
449
|
+
return (
|
|
450
|
+
<div className="theia-RequestNode">
|
|
451
|
+
<p>
|
|
452
|
+
{parts.map((part, index) => {
|
|
453
|
+
if (part instanceof ParsedChatRequestAgentPart || part instanceof ParsedChatRequestVariablePart) {
|
|
454
|
+
let description = undefined;
|
|
455
|
+
let className = '';
|
|
456
|
+
if (part instanceof ParsedChatRequestAgentPart) {
|
|
457
|
+
description = chatAgentService.getAgent(part.agentId)?.description;
|
|
458
|
+
className = 'theia-RequestNode-AgentLabel';
|
|
459
|
+
} else if (part instanceof ParsedChatRequestVariablePart) {
|
|
460
|
+
description = variableService.getVariable(part.variableName)?.description;
|
|
461
|
+
className = 'theia-RequestNode-VariableLabel';
|
|
462
|
+
}
|
|
463
|
+
return (
|
|
464
|
+
<HoverableLabel
|
|
465
|
+
key={index}
|
|
466
|
+
text={part.text}
|
|
467
|
+
description={description}
|
|
468
|
+
hoverService={hoverService}
|
|
469
|
+
className={className}
|
|
470
|
+
/>
|
|
471
|
+
);
|
|
472
|
+
} else {
|
|
473
|
+
// maintain the leading and trailing spaces with explicit ` `, otherwise they would get trimmed by the markdown renderer
|
|
474
|
+
const ref = useMarkdownRendering(part.text.replace(/^\s|\s$/g, ' '), openerService, true);
|
|
475
|
+
return (
|
|
476
|
+
<span key={index} ref={ref}></span>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
})}
|
|
480
|
+
</p>
|
|
481
|
+
</div>
|
|
482
|
+
);
|
|
483
|
+
};
|
|
381
484
|
|
|
382
|
-
|
|
485
|
+
const HoverableLabel = (
|
|
486
|
+
{
|
|
487
|
+
text, description, hoverService, className
|
|
488
|
+
}: {
|
|
489
|
+
text: string,
|
|
490
|
+
description?: string,
|
|
491
|
+
hoverService: HoverService,
|
|
492
|
+
className: string
|
|
493
|
+
}) => {
|
|
494
|
+
const spanRef = React.createRef<HTMLSpanElement>();
|
|
495
|
+
return (
|
|
496
|
+
<span
|
|
497
|
+
className={className}
|
|
498
|
+
ref={spanRef}
|
|
499
|
+
onMouseEnter={() => {
|
|
500
|
+
if (description) {
|
|
501
|
+
hoverService.requestHover({
|
|
502
|
+
content: description,
|
|
503
|
+
target: spanRef.current!,
|
|
504
|
+
position: 'right'
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}}
|
|
508
|
+
>
|
|
509
|
+
{text}
|
|
510
|
+
</span>
|
|
511
|
+
);
|
|
383
512
|
};
|
|
384
513
|
|
|
385
514
|
const ProgressMessage = (c: ChatProgressMessage) => (
|
|
@@ -132,6 +132,20 @@ div:last-child > .theia-ChatNode {
|
|
|
132
132
|
font-size: var(--theia-code-font-size);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
.theia-RequestNode > p div {
|
|
136
|
+
display: inline;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.theia-RequestNode .theia-RequestNode-AgentLabel,
|
|
140
|
+
.theia-RequestNode .theia-RequestNode-VariableLabel {
|
|
141
|
+
padding: calc(var(--theia-ui-padding) * 2 / 3);
|
|
142
|
+
padding-top: 0px;
|
|
143
|
+
padding-bottom: 0px;
|
|
144
|
+
border-radius: calc(var(--theia-ui-padding) * 2 / 3);
|
|
145
|
+
background: var(--theia-badge-background);
|
|
146
|
+
color: var(--theia-badge-foreground);
|
|
147
|
+
}
|
|
148
|
+
|
|
135
149
|
.chat-input-widget {
|
|
136
150
|
align-items: flex-end;
|
|
137
151
|
display: flex;
|
|
@@ -163,6 +177,7 @@ div:last-child > .theia-ChatNode {
|
|
|
163
177
|
display: flex;
|
|
164
178
|
flex-direction: column-reverse;
|
|
165
179
|
overflow: hidden;
|
|
180
|
+
transition: height 0.05s ease-in-out;
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
.theia-ChatInput-Editor:has(.monaco-editor.focused) {
|
|
@@ -230,7 +245,7 @@ div:last-child > .theia-ChatNode {
|
|
|
230
245
|
display: flex;
|
|
231
246
|
flex-direction: column;
|
|
232
247
|
gap: 2px;
|
|
233
|
-
border:
|
|
248
|
+
border: var(--theia-border-width) solid var(--theia-input-border);
|
|
234
249
|
border-radius: 4px;
|
|
235
250
|
}
|
|
236
251
|
|
|
@@ -264,6 +279,33 @@ div:last-child > .theia-ChatNode {
|
|
|
264
279
|
background-color: var(--theia-input-border);
|
|
265
280
|
}
|
|
266
281
|
|
|
282
|
+
.theia-QuestionPartRenderer-root {
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
285
|
+
gap: 8px;
|
|
286
|
+
border: var(--theia-border-width) solid
|
|
287
|
+
var(--theia-sideBarSectionHeader-border);
|
|
288
|
+
padding: 8px 12px 12px;
|
|
289
|
+
border-radius: 5px;
|
|
290
|
+
margin: 0 0 8px 0;
|
|
291
|
+
}
|
|
292
|
+
.theia-QuestionPartRenderer-options {
|
|
293
|
+
display: flex;
|
|
294
|
+
flex-wrap: wrap;
|
|
295
|
+
gap: 12px;
|
|
296
|
+
}
|
|
297
|
+
.theia-QuestionPartRenderer-option {
|
|
298
|
+
min-width: 100px;
|
|
299
|
+
flex: 1 1 auto;
|
|
300
|
+
margin: 0;
|
|
301
|
+
}
|
|
302
|
+
.theia-QuestionPartRenderer-option.selected:disabled:hover {
|
|
303
|
+
background-color: var(--theia-button-disabledBackground);
|
|
304
|
+
}
|
|
305
|
+
.theia-QuestionPartRenderer-option:disabled:not(.selected) {
|
|
306
|
+
background-color: var(--theia-button-secondaryBackground);
|
|
307
|
+
}
|
|
308
|
+
|
|
267
309
|
.theia-toolCall {
|
|
268
310
|
font-weight: normal;
|
|
269
311
|
color: var(--theia-descriptionForeground);
|
|
@@ -288,6 +330,21 @@ div:last-child > .theia-ChatNode {
|
|
|
288
330
|
overflow: auto;
|
|
289
331
|
}
|
|
290
332
|
|
|
333
|
+
.collapsible-arguments {
|
|
334
|
+
display: inline-block;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.collapsible-arguments .collapsible-arguments-summary {
|
|
338
|
+
display: inline-block;
|
|
339
|
+
white-space: nowrap;
|
|
340
|
+
text-decoration: underline;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
details[open].collapsible-arguments,
|
|
344
|
+
details[open].collapsible-arguments .collapsible-arguments-summary {
|
|
345
|
+
display: unset;
|
|
346
|
+
}
|
|
347
|
+
|
|
291
348
|
.theia-ResponseNode-ProgressMessage {
|
|
292
349
|
font-weight: normal;
|
|
293
350
|
color: var(--theia-descriptionForeground);
|