@theia/ai-chat-ui 1.55.1 → 1.57.0-next.7
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.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 +3 -2
- package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/markdown-part-renderer.js +5 -3
- 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 +4 -0
- 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 +80 -13
- 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 +5 -3
- 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 +134 -11
- 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,6 +30,7 @@ import {
|
|
|
27
30
|
CommonCommands,
|
|
28
31
|
CompositeTreeNode,
|
|
29
32
|
ContextMenuRenderer,
|
|
33
|
+
HoverService,
|
|
30
34
|
Key,
|
|
31
35
|
KeyCode,
|
|
32
36
|
NodeProps,
|
|
@@ -46,6 +50,7 @@ import * as React from '@theia/core/shared/react';
|
|
|
46
50
|
import { ChatNodeToolbarActionContribution } from '../chat-node-toolbar-action-contribution';
|
|
47
51
|
import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
|
|
48
52
|
import { useMarkdownRendering } from '../chat-response-renderer/markdown-part-renderer';
|
|
53
|
+
import { AIVariableService } from '@theia/ai-core';
|
|
49
54
|
|
|
50
55
|
// TODO Instead of directly operating on the ChatRequestModel we could use an intermediate view model
|
|
51
56
|
export interface RequestNode extends TreeNode {
|
|
@@ -77,9 +82,15 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
77
82
|
@inject(ChatAgentService)
|
|
78
83
|
protected chatAgentService: ChatAgentService;
|
|
79
84
|
|
|
85
|
+
@inject(AIVariableService)
|
|
86
|
+
protected readonly variableService: AIVariableService;
|
|
87
|
+
|
|
80
88
|
@inject(CommandRegistry)
|
|
81
89
|
private commandRegistry: CommandRegistry;
|
|
82
90
|
|
|
91
|
+
@inject(HoverService)
|
|
92
|
+
private hoverService: HoverService;
|
|
93
|
+
|
|
83
94
|
protected _shouldScrollToEnd = true;
|
|
84
95
|
|
|
85
96
|
protected isEnabled = false;
|
|
@@ -267,22 +278,39 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
267
278
|
|
|
268
279
|
private renderAgent(node: RequestNode | ResponseNode): React.ReactNode {
|
|
269
280
|
const inProgress = isResponseNode(node) && !node.response.isComplete && !node.response.isCanceled && !node.response.isError;
|
|
281
|
+
const waitingForInput = isResponseNode(node) && node.response.isWaitingForInput;
|
|
270
282
|
const toolbarContributions = !inProgress
|
|
271
283
|
? this.chatNodeToolbarActionContributions.getContributions()
|
|
272
284
|
.flatMap(c => c.getToolbarActions(node))
|
|
273
285
|
.filter(action => this.commandRegistry.isEnabled(action.commandId, node))
|
|
274
286
|
.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))
|
|
275
287
|
: [];
|
|
288
|
+
const agentLabel = React.createRef<HTMLHeadingElement>();
|
|
289
|
+
const agentDescription = this.getAgent(node)?.description;
|
|
276
290
|
return <React.Fragment>
|
|
277
291
|
<div className='theia-ChatNodeHeader'>
|
|
278
292
|
<div className={`theia-AgentAvatar ${this.getAgentIconClassName(node)}`}></div>
|
|
279
|
-
<h3
|
|
293
|
+
<h3 ref={agentLabel}
|
|
294
|
+
className='theia-AgentLabel'
|
|
295
|
+
onMouseEnter={() => {
|
|
296
|
+
if (agentDescription) {
|
|
297
|
+
this.hoverService.requestHover({
|
|
298
|
+
content: agentDescription,
|
|
299
|
+
target: agentLabel.current!,
|
|
300
|
+
position: 'right'
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}}>
|
|
304
|
+
{this.getAgentLabel(node)}
|
|
305
|
+
</h3>
|
|
280
306
|
{inProgress && <span className='theia-ChatContentInProgress'>Generating</span>}
|
|
307
|
+
{inProgress && waitingForInput && <span className='theia-ChatContentInProgress'>Waiting for input</span>}
|
|
281
308
|
<div className='theia-ChatNodeToolbar'>
|
|
282
309
|
{!inProgress &&
|
|
283
310
|
toolbarContributions.length > 0 &&
|
|
284
311
|
toolbarContributions.map(action =>
|
|
285
312
|
<span
|
|
313
|
+
key={action.commandId}
|
|
286
314
|
className={`theia-ChatNodeToolbarAction ${action.icon}`}
|
|
287
315
|
title={action.tooltip}
|
|
288
316
|
onClick={e => {
|
|
@@ -308,8 +336,14 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
308
336
|
// TODO find user name
|
|
309
337
|
return 'You';
|
|
310
338
|
}
|
|
311
|
-
|
|
312
|
-
|
|
339
|
+
return this.getAgent(node)?.name ?? 'AI';
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private getAgent(node: RequestNode | ResponseNode): ChatAgent | undefined {
|
|
343
|
+
if (isRequestNode(node)) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
return node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined;
|
|
313
347
|
}
|
|
314
348
|
|
|
315
349
|
private getAgentIconClassName(node: RequestNode | ResponseNode): string | undefined {
|
|
@@ -331,7 +365,12 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
331
365
|
}
|
|
332
366
|
|
|
333
367
|
private renderChatRequest(node: RequestNode): React.ReactNode {
|
|
334
|
-
return <ChatRequestRender
|
|
368
|
+
return <ChatRequestRender
|
|
369
|
+
node={node}
|
|
370
|
+
hoverService={this.hoverService}
|
|
371
|
+
chatAgentService={this.chatAgentService}
|
|
372
|
+
variableService={this.variableService}
|
|
373
|
+
/>;
|
|
335
374
|
}
|
|
336
375
|
|
|
337
376
|
private renderChatResponse(node: ResponseNode): React.ReactNode {
|
|
@@ -339,12 +378,28 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
339
378
|
<div className={'theia-ResponseNode'}>
|
|
340
379
|
{!node.response.isComplete
|
|
341
380
|
&& node.response.response.content.length === 0
|
|
342
|
-
&& node.response.progressMessages
|
|
343
|
-
|
|
344
|
-
|
|
381
|
+
&& node.response.progressMessages
|
|
382
|
+
.filter(c => c.show === 'untilFirstContent')
|
|
383
|
+
.map((c, i) =>
|
|
384
|
+
<ProgressMessage {...c} key={`${node.id}-progress-untilFirstContent-${i}`} />
|
|
385
|
+
)
|
|
386
|
+
}
|
|
345
387
|
{node.response.response.content.map((c, i) =>
|
|
346
388
|
<div className='theia-ResponseNode-Content' key={`${node.id}-content-${i}`}>{this.getChatResponsePartRenderer(c, node)}</div>
|
|
347
389
|
)}
|
|
390
|
+
{!node.response.isComplete
|
|
391
|
+
&& node.response.progressMessages
|
|
392
|
+
.filter(c => c.show === 'whileIncomplete')
|
|
393
|
+
.map((c, i) =>
|
|
394
|
+
<ProgressMessage {...c} key={`${node.id}-progress-whileIncomplete-${i}`} />
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
{node.response.progressMessages
|
|
398
|
+
.filter(c => c.show === 'forever')
|
|
399
|
+
.map((c, i) =>
|
|
400
|
+
<ProgressMessage {...c} key={`${node.id}-progress-afterComplete-${i}`} />
|
|
401
|
+
)
|
|
402
|
+
}
|
|
348
403
|
</div>
|
|
349
404
|
);
|
|
350
405
|
}
|
|
@@ -375,11 +430,79 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
375
430
|
}
|
|
376
431
|
}
|
|
377
432
|
|
|
378
|
-
const ChatRequestRender = (
|
|
379
|
-
|
|
380
|
-
|
|
433
|
+
const ChatRequestRender = (
|
|
434
|
+
{
|
|
435
|
+
node, hoverService, chatAgentService, variableService
|
|
436
|
+
}: {
|
|
437
|
+
node: RequestNode,
|
|
438
|
+
hoverService: HoverService,
|
|
439
|
+
chatAgentService: ChatAgentService,
|
|
440
|
+
variableService: AIVariableService
|
|
441
|
+
}) => {
|
|
442
|
+
const parts = node.request.message.parts;
|
|
443
|
+
return (
|
|
444
|
+
<div className="theia-RequestNode">
|
|
445
|
+
<p>
|
|
446
|
+
{parts.map((part, index) => {
|
|
447
|
+
if (part instanceof ParsedChatRequestAgentPart || part instanceof ParsedChatRequestVariablePart) {
|
|
448
|
+
let description = undefined;
|
|
449
|
+
let className = '';
|
|
450
|
+
if (part instanceof ParsedChatRequestAgentPart) {
|
|
451
|
+
description = chatAgentService.getAgent(part.agentId)?.description;
|
|
452
|
+
className = 'theia-RequestNode-AgentLabel';
|
|
453
|
+
} else if (part instanceof ParsedChatRequestVariablePart) {
|
|
454
|
+
description = variableService.getVariable(part.variableName)?.description;
|
|
455
|
+
className = 'theia-RequestNode-VariableLabel';
|
|
456
|
+
}
|
|
457
|
+
return (
|
|
458
|
+
<HoverableLabel
|
|
459
|
+
key={index}
|
|
460
|
+
text={part.text}
|
|
461
|
+
description={description}
|
|
462
|
+
hoverService={hoverService}
|
|
463
|
+
className={className}
|
|
464
|
+
/>
|
|
465
|
+
);
|
|
466
|
+
} else {
|
|
467
|
+
// maintain the leading and trailing spaces with explicit ` `, otherwise they would get trimmed by the markdown renderer
|
|
468
|
+
const ref = useMarkdownRendering(part.text.replace(/^\s|\s$/g, ' '), true);
|
|
469
|
+
return (
|
|
470
|
+
<span key={index} ref={ref}></span>
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
})}
|
|
474
|
+
</p>
|
|
475
|
+
</div>
|
|
476
|
+
);
|
|
477
|
+
};
|
|
381
478
|
|
|
382
|
-
|
|
479
|
+
const HoverableLabel = (
|
|
480
|
+
{
|
|
481
|
+
text, description, hoverService, className
|
|
482
|
+
}: {
|
|
483
|
+
text: string,
|
|
484
|
+
description?: string,
|
|
485
|
+
hoverService: HoverService,
|
|
486
|
+
className: string
|
|
487
|
+
}) => {
|
|
488
|
+
const spanRef = React.createRef<HTMLSpanElement>();
|
|
489
|
+
return (
|
|
490
|
+
<span
|
|
491
|
+
className={className}
|
|
492
|
+
ref={spanRef}
|
|
493
|
+
onMouseEnter={() => {
|
|
494
|
+
if (description) {
|
|
495
|
+
hoverService.requestHover({
|
|
496
|
+
content: description,
|
|
497
|
+
target: spanRef.current!,
|
|
498
|
+
position: 'right'
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}}
|
|
502
|
+
>
|
|
503
|
+
{text}
|
|
504
|
+
</span>
|
|
505
|
+
);
|
|
383
506
|
};
|
|
384
507
|
|
|
385
508
|
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);
|