@theia/ai-chat-ui 1.59.0-next.72 → 1.60.0-next.43
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 +0 -1
- package/lib/browser/ai-chat-ui-contribution.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-contribution.js +20 -5
- package/lib/browser/ai-chat-ui-contribution.js.map +1 -1
- package/lib/browser/chat-input-widget.d.ts.map +1 -1
- package/lib/browser/chat-input-widget.js +3 -1
- package/lib/browser/chat-input-widget.js.map +1 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.js +3 -2
- package/lib/browser/chat-response-renderer/code-part-renderer.js.map +1 -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 +2 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
- package/lib/browser/chat-view-contribution.d.ts.map +1 -1
- package/lib/browser/chat-view-contribution.js +17 -2
- package/lib/browser/chat-view-contribution.js.map +1 -1
- package/lib/browser/chat-view-language-contribution.d.ts +14 -4
- package/lib/browser/chat-view-language-contribution.d.ts.map +1 -1
- package/lib/browser/chat-view-language-contribution.js +96 -38
- package/lib/browser/chat-view-language-contribution.js.map +1 -1
- package/package.json +12 -11
- package/src/browser/ai-chat-ui-contribution.ts +20 -6
- package/src/browser/chat-input-widget.tsx +3 -1
- package/src/browser/chat-response-renderer/code-part-renderer.tsx +3 -2
- package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +2 -1
- package/src/browser/chat-view-contribution.ts +18 -2
- package/src/browser/chat-view-language-contribution.ts +134 -77
- package/src/browser/style/index.css +11 -3
|
@@ -31,6 +31,16 @@ const VARIABLE_RESOLUTION_CONTEXT = { context: 'chat-input-autocomplete' };
|
|
|
31
31
|
const VARIABLE_ARGUMENT_PICKER_COMMAND = 'trigger-variable-argument-picker';
|
|
32
32
|
const VARIABLE_ADD_CONTEXT_COMMAND = 'add-context-variable';
|
|
33
33
|
|
|
34
|
+
interface CompletionSource<T> {
|
|
35
|
+
triggerCharacter: string;
|
|
36
|
+
getItems: () => T[];
|
|
37
|
+
kind: monaco.languages.CompletionItemKind;
|
|
38
|
+
getId: (item: T) => string;
|
|
39
|
+
getName: (item: T) => string;
|
|
40
|
+
getDescription: (item: T) => string;
|
|
41
|
+
command?: monaco.languages.Command;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
@injectable()
|
|
35
45
|
export class ChatViewLanguageContribution implements FrontendApplicationContribution {
|
|
36
46
|
|
|
@@ -49,39 +59,84 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
49
59
|
onStart(_app: FrontendApplication): MaybePromise<void> {
|
|
50
60
|
monaco.languages.register({ id: CHAT_VIEW_LANGUAGE_ID, extensions: [CHAT_VIEW_LANGUAGE_EXTENSION] });
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
this.registerCompletionProviders();
|
|
63
|
+
|
|
64
|
+
monaco.editor.registerCommand(VARIABLE_ARGUMENT_PICKER_COMMAND, this.triggerVariableArgumentPicker.bind(this));
|
|
65
|
+
monaco.editor.registerCommand(VARIABLE_ADD_CONTEXT_COMMAND, (_, ...args) => args.length > 1 ? this.addContextVariable(args[0], args[1]) : undefined);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected registerCompletionProviders(): void {
|
|
69
|
+
this.registerStandardCompletionProvider({
|
|
70
|
+
triggerCharacter: PromptText.AGENT_CHAR,
|
|
71
|
+
getItems: () => this.agentService.getAgents(),
|
|
72
|
+
kind: monaco.languages.CompletionItemKind.Value,
|
|
73
|
+
getId: agent => `${agent.id} `,
|
|
74
|
+
getName: agent => agent.name,
|
|
75
|
+
getDescription: agent => agent.description
|
|
55
76
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
|
|
78
|
+
this.registerStandardCompletionProvider({
|
|
79
|
+
triggerCharacter: PromptText.VARIABLE_CHAR,
|
|
80
|
+
getItems: () => this.variableService.getVariables(),
|
|
81
|
+
kind: monaco.languages.CompletionItemKind.Variable,
|
|
82
|
+
getId: variable => variable.args?.some(arg => !arg.isOptional) ? variable.name + PromptText.VARIABLE_SEPARATOR_CHAR : `${variable.name} `,
|
|
83
|
+
getName: variable => variable.name,
|
|
84
|
+
getDescription: variable => variable.description,
|
|
85
|
+
command: {
|
|
86
|
+
title: nls.localize('theia/ai/chat-ui/selectVariableArguments', 'Select variable arguments'),
|
|
87
|
+
id: VARIABLE_ARGUMENT_PICKER_COMMAND,
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
this.registerStandardCompletionProvider({
|
|
92
|
+
triggerCharacter: PromptText.FUNCTION_CHAR,
|
|
93
|
+
getItems: () => this.toolInvocationRegistry.getAllFunctions(),
|
|
94
|
+
kind: monaco.languages.CompletionItemKind.Function,
|
|
95
|
+
getId: tool => `${tool.id} `,
|
|
96
|
+
getName: tool => tool.name,
|
|
97
|
+
getDescription: tool => tool.description ?? ''
|
|
59
98
|
});
|
|
99
|
+
|
|
100
|
+
// Register the variable argument completion provider (special case)
|
|
60
101
|
monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
|
|
61
102
|
triggerCharacters: [PromptText.VARIABLE_CHAR, PromptText.VARIABLE_SEPARATOR_CHAR],
|
|
62
|
-
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> =>
|
|
103
|
+
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> =>
|
|
104
|
+
this.provideVariableWithArgCompletions(model, position),
|
|
63
105
|
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
protected registerStandardCompletionProvider<T>(source: CompletionSource<T>): void {
|
|
64
109
|
monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
|
|
65
|
-
triggerCharacters: [
|
|
66
|
-
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> =>
|
|
110
|
+
triggerCharacters: [source.triggerCharacter],
|
|
111
|
+
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> =>
|
|
112
|
+
this.provideCompletions(model, position, source),
|
|
67
113
|
});
|
|
68
|
-
|
|
69
|
-
monaco.editor.registerCommand(VARIABLE_ARGUMENT_PICKER_COMMAND, this.triggerVariableArgumentPicker.bind(this));
|
|
70
|
-
monaco.editor.registerCommand(VARIABLE_ADD_CONTEXT_COMMAND, (_, ...args) => args.length > 1 ? this.addContextVariable(args[0], args[1]) : undefined);
|
|
71
114
|
}
|
|
72
115
|
|
|
73
116
|
getCompletionRange(model: monaco.editor.ITextModel, position: monaco.Position, triggerCharacter: string): monaco.Range | undefined {
|
|
74
|
-
|
|
117
|
+
const wordInfo = model.getWordUntilPosition(position);
|
|
75
118
|
const lineContent = model.getLineContent(position.lineNumber);
|
|
76
|
-
|
|
119
|
+
// one to the left, and -1 for 0-based index
|
|
120
|
+
const characterBeforeCurrentWord = lineContent[wordInfo.startColumn - 1 - 1];
|
|
77
121
|
|
|
78
|
-
if (
|
|
79
|
-
// Do not return agent suggestions if the user didn't just type the trigger character
|
|
122
|
+
if (characterBeforeCurrentWord !== triggerCharacter) {
|
|
80
123
|
return undefined;
|
|
81
124
|
}
|
|
82
125
|
|
|
83
|
-
//
|
|
84
|
-
|
|
126
|
+
// we are not at the beginning of the line
|
|
127
|
+
if (wordInfo.startColumn > 2) {
|
|
128
|
+
const charBeforeTrigger = model.getValueInRange({
|
|
129
|
+
startLineNumber: position.lineNumber,
|
|
130
|
+
startColumn: wordInfo.startColumn - 2,
|
|
131
|
+
endLineNumber: position.lineNumber,
|
|
132
|
+
endColumn: wordInfo.startColumn - 1
|
|
133
|
+
});
|
|
134
|
+
// If the character before the trigger is not whitespace, don't provide completions
|
|
135
|
+
if (!/\s/.test(charBeforeTrigger)) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
85
140
|
return new monaco.Range(
|
|
86
141
|
position.lineNumber,
|
|
87
142
|
wordInfo.startColumn,
|
|
@@ -90,73 +145,65 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
90
145
|
);
|
|
91
146
|
}
|
|
92
147
|
|
|
93
|
-
|
|
148
|
+
protected provideCompletions<T>(
|
|
94
149
|
model: monaco.editor.ITextModel,
|
|
95
150
|
position: monaco.Position,
|
|
96
|
-
|
|
97
|
-
items: T[],
|
|
98
|
-
kind: monaco.languages.CompletionItemKind,
|
|
99
|
-
getId: (item: T) => string,
|
|
100
|
-
getName: (item: T) => string,
|
|
101
|
-
getDescription: (item: T) => string,
|
|
102
|
-
command?: monaco.languages.Command
|
|
151
|
+
source: CompletionSource<T>
|
|
103
152
|
): ProviderResult<monaco.languages.CompletionList> {
|
|
104
|
-
const completionRange = this.getCompletionRange(model, position,
|
|
153
|
+
const completionRange = this.getCompletionRange(model, position, source.triggerCharacter);
|
|
105
154
|
if (completionRange === undefined) {
|
|
106
155
|
return { suggestions: [] };
|
|
107
156
|
}
|
|
157
|
+
|
|
158
|
+
const items = source.getItems();
|
|
108
159
|
const suggestions = items.map(item => ({
|
|
109
|
-
insertText: getId(item),
|
|
110
|
-
kind: kind,
|
|
111
|
-
label: getName(item),
|
|
160
|
+
insertText: source.getId(item),
|
|
161
|
+
kind: source.kind,
|
|
162
|
+
label: source.getName(item),
|
|
112
163
|
range: completionRange,
|
|
113
|
-
detail: getDescription(item),
|
|
114
|
-
command
|
|
164
|
+
detail: source.getDescription(item),
|
|
165
|
+
command: source.command
|
|
115
166
|
}));
|
|
167
|
+
|
|
116
168
|
return { suggestions };
|
|
117
169
|
}
|
|
118
170
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
position,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
agent => agent.name,
|
|
128
|
-
agent => agent.description
|
|
129
|
-
);
|
|
130
|
-
}
|
|
171
|
+
async provideVariableWithArgCompletions(model: monaco.editor.ITextModel, position: monaco.Position): Promise<monaco.languages.CompletionList> {
|
|
172
|
+
// Get the text of the current line up to the cursor position
|
|
173
|
+
const textUntilPosition = model.getValueInRange({
|
|
174
|
+
startLineNumber: position.lineNumber,
|
|
175
|
+
startColumn: 1,
|
|
176
|
+
endLineNumber: position.lineNumber,
|
|
177
|
+
endColumn: position.column,
|
|
178
|
+
});
|
|
131
179
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
title: nls.localize('theia/ai/chat-ui/selectVariableArguments', 'Select variable arguments'),
|
|
144
|
-
id: VARIABLE_ARGUMENT_PICKER_COMMAND,
|
|
145
|
-
}
|
|
146
|
-
);
|
|
147
|
-
}
|
|
180
|
+
// Regex that captures the variable name in contexts like "#varname" or "#var-name:args"
|
|
181
|
+
// Matches only when # is at the beginning of the string or after whitespace
|
|
182
|
+
const variableRegex = /(?:^|\s)#([\w-]*)/;
|
|
183
|
+
const match = textUntilPosition.match(variableRegex);
|
|
184
|
+
|
|
185
|
+
if (!match) {
|
|
186
|
+
return { suggestions: [] };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const currentVariableName = match[1];
|
|
190
|
+
const hasColonSeparator = textUntilPosition.includes(`${currentVariableName}:`);
|
|
148
191
|
|
|
149
|
-
async provideVariableWithArgCompletions(model: monaco.editor.ITextModel, position: monaco.Position): Promise<monaco.languages.CompletionList> {
|
|
150
192
|
const variables = this.variableService.getVariables();
|
|
151
193
|
const suggestions: monaco.languages.CompletionItem[] = [];
|
|
194
|
+
|
|
152
195
|
for (const variable of variables) {
|
|
196
|
+
// If we have a variable:arg pattern, only process the matching variable
|
|
197
|
+
if (hasColonSeparator && variable.name !== currentVariableName) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
153
201
|
const provider = await this.variableService.getArgumentCompletionProvider(variable.name);
|
|
154
202
|
if (provider) {
|
|
155
203
|
const items = await provider(model, position);
|
|
156
204
|
if (items) {
|
|
157
205
|
suggestions.push(...items.map(item => ({
|
|
158
206
|
...item,
|
|
159
|
-
// trigger command to check if we should add a context variable
|
|
160
207
|
command: {
|
|
161
208
|
title: nls.localize('theia/ai/chat-ui/addContextVariable', 'Add context variable'),
|
|
162
209
|
id: VARIABLE_ADD_CONTEXT_COMMAND,
|
|
@@ -166,20 +213,8 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
166
213
|
}
|
|
167
214
|
}
|
|
168
215
|
}
|
|
169
|
-
return { suggestions };
|
|
170
|
-
}
|
|
171
216
|
|
|
172
|
-
|
|
173
|
-
return this.getSuggestions(
|
|
174
|
-
model,
|
|
175
|
-
position,
|
|
176
|
-
PromptText.FUNCTION_CHAR,
|
|
177
|
-
this.toolInvocationRegistry.getAllFunctions(),
|
|
178
|
-
monaco.languages.CompletionItemKind.Function,
|
|
179
|
-
tool => tool.id,
|
|
180
|
-
tool => tool.name,
|
|
181
|
-
tool => tool.description ?? ''
|
|
182
|
-
);
|
|
217
|
+
return { suggestions };
|
|
183
218
|
}
|
|
184
219
|
|
|
185
220
|
protected async triggerVariableArgumentPicker(): Promise<void> {
|
|
@@ -187,35 +222,57 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
187
222
|
if (!inputEditor) {
|
|
188
223
|
return;
|
|
189
224
|
}
|
|
225
|
+
|
|
190
226
|
const model = inputEditor.getModel();
|
|
191
227
|
const position = inputEditor.getPosition();
|
|
192
228
|
if (!model || !position) {
|
|
193
229
|
return;
|
|
194
230
|
}
|
|
195
|
-
|
|
231
|
+
|
|
232
|
+
// // Get the word at cursor
|
|
233
|
+
const wordInfo = model.getWordUntilPosition(position);
|
|
234
|
+
|
|
235
|
+
// account for the variable separator character if present
|
|
236
|
+
let endOfWordPosition = position.column;
|
|
237
|
+
if (wordInfo.word === '' && this.getCharacterBeforePosition(model, position) === PromptText.VARIABLE_SEPARATOR_CHAR) {
|
|
238
|
+
endOfWordPosition = position.column - 1;
|
|
239
|
+
} else {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const variableName = model.getWordAtPosition({ ...position, column: endOfWordPosition })?.word;
|
|
196
244
|
if (!variableName) {
|
|
197
245
|
return;
|
|
198
246
|
}
|
|
247
|
+
|
|
199
248
|
const provider = await this.variableService.getArgumentPicker(variableName, VARIABLE_RESOLUTION_CONTEXT);
|
|
200
249
|
if (!provider) {
|
|
201
250
|
return;
|
|
202
251
|
}
|
|
252
|
+
|
|
203
253
|
const arg = await provider(VARIABLE_RESOLUTION_CONTEXT);
|
|
204
254
|
if (!arg) {
|
|
205
255
|
return;
|
|
206
256
|
}
|
|
257
|
+
|
|
207
258
|
inputEditor.executeEdits('variable-argument-picker', [{
|
|
208
259
|
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
|
|
209
|
-
text:
|
|
260
|
+
text: arg
|
|
210
261
|
}]);
|
|
262
|
+
|
|
211
263
|
await this.addContextVariable(variableName, arg);
|
|
212
264
|
}
|
|
213
265
|
|
|
266
|
+
protected getCharacterBeforePosition(model: monaco.editor.ITextModel, position: monaco.Position): string {
|
|
267
|
+
return model.getLineContent(position.lineNumber)[position.column - 1 - 1];
|
|
268
|
+
}
|
|
269
|
+
|
|
214
270
|
protected async addContextVariable(variableName: string, arg: string | undefined): Promise<void> {
|
|
215
271
|
const variable = this.variableService.getVariable(variableName);
|
|
216
272
|
if (!variable || !AIContextVariable.is(variable)) {
|
|
217
273
|
return;
|
|
218
274
|
}
|
|
275
|
+
|
|
219
276
|
const widget = this.shell.getWidgetById(ChatViewWidget.ID);
|
|
220
277
|
if (widget instanceof ChatViewWidget) {
|
|
221
278
|
widget.addContext({ variable, arg });
|
|
@@ -405,6 +405,10 @@ div:last-child > .theia-ChatNode {
|
|
|
405
405
|
border-color: var(--theia-focusBorder);
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
.theia-ChatInput-Editor-Box .monaco-editor {
|
|
409
|
+
outline-color: var(--theia-editor-background);
|
|
410
|
+
}
|
|
411
|
+
|
|
408
412
|
.theia-ChatInput-Editor {
|
|
409
413
|
width: 100%;
|
|
410
414
|
height: auto;
|
|
@@ -449,7 +453,7 @@ div:last-child > .theia-ChatNode {
|
|
|
449
453
|
.theia-ChatInputOptions {
|
|
450
454
|
width: 100%;
|
|
451
455
|
height: 25px;
|
|
452
|
-
padding-left:
|
|
456
|
+
padding-left: 3px;
|
|
453
457
|
padding-right: 6px;
|
|
454
458
|
display: flex;
|
|
455
459
|
justify-content: space-between;
|
|
@@ -461,7 +465,7 @@ div:last-child > .theia-ChatNode {
|
|
|
461
465
|
}
|
|
462
466
|
|
|
463
467
|
.theia-ChatInputOptions .theia-ChatInputOptions-right {
|
|
464
|
-
margin-right:
|
|
468
|
+
margin-right: 8px;
|
|
465
469
|
}
|
|
466
470
|
|
|
467
471
|
.theia-ChatInputOptions .option {
|
|
@@ -493,7 +497,7 @@ div:last-child > .theia-ChatNode {
|
|
|
493
497
|
}
|
|
494
498
|
|
|
495
499
|
.theia-ChatInputOptions .reverse {
|
|
496
|
-
|
|
500
|
+
flex-direction: row-reverse;
|
|
497
501
|
}
|
|
498
502
|
|
|
499
503
|
.theia-CodePartRenderer-root {
|
|
@@ -504,6 +508,10 @@ div:last-child > .theia-ChatNode {
|
|
|
504
508
|
border-radius: 4px;
|
|
505
509
|
}
|
|
506
510
|
|
|
511
|
+
.theia-CodePartRenderer-root .monaco-editor {
|
|
512
|
+
outline-color: var(--theia-editor-background);
|
|
513
|
+
}
|
|
514
|
+
|
|
507
515
|
.theia-CodePartRenderer-left {
|
|
508
516
|
flex-grow: 1;
|
|
509
517
|
}
|