@theia/ai-chat-ui 1.62.1 → 1.63.0-next.24
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 +2 -2
- package/lib/browser/ai-chat-ui-contribution.js.map +1 -1
- package/lib/browser/chat-input-widget.d.ts +3 -1
- package/lib/browser/chat-input-widget.d.ts.map +1 -1
- package/lib/browser/chat-input-widget.js +101 -90
- package/lib/browser/chat-input-widget.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/tool-confirmation.d.ts +17 -0
- package/lib/browser/chat-response-renderer/tool-confirmation.d.ts.map +1 -0
- package/lib/browser/chat-response-renderer/tool-confirmation.js +120 -0
- package/lib/browser/chat-response-renderer/tool-confirmation.js.map +1 -0
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts +5 -1
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js +83 -19
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-input-widget.d.ts +6 -1
- package/lib/browser/chat-tree-view/chat-view-tree-input-widget.d.ts.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-input-widget.js +9 -0
- package/lib/browser/chat-tree-view/chat-view-tree-input-widget.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts +6 -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 +30 -3
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
- package/lib/browser/chat-view-widget.d.ts +5 -2
- package/lib/browser/chat-view-widget.d.ts.map +1 -1
- package/lib/browser/chat-view-widget.js +19 -6
- package/lib/browser/chat-view-widget.js.map +1 -1
- package/package.json +11 -11
- package/src/browser/ai-chat-ui-contribution.ts +2 -2
- package/src/browser/chat-input-widget.tsx +171 -137
- package/src/browser/chat-response-renderer/index.ts +1 -0
- package/src/browser/chat-response-renderer/tool-confirmation.tsx +173 -0
- package/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +115 -19
- package/src/browser/chat-tree-view/chat-view-tree-input-widget.tsx +16 -1
- package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +39 -5
- package/src/browser/chat-view-widget.tsx +23 -7
- package/src/browser/style/index.css +173 -0
|
@@ -15,15 +15,22 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
|
|
18
|
-
import { injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
19
19
|
import { ChatResponseContent, ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
|
|
20
20
|
import { ReactNode } from '@theia/core/shared/react';
|
|
21
21
|
import { nls } from '@theia/core/lib/common/nls';
|
|
22
|
+
import { codicon } from '@theia/core/lib/browser';
|
|
22
23
|
import * as React from '@theia/core/shared/react';
|
|
24
|
+
import { ToolConfirmation, ToolConfirmationState } from './tool-confirmation';
|
|
25
|
+
import { ToolConfirmationManager, ToolConfirmationMode } from '@theia/ai-chat/lib/browser/chat-tool-preferences';
|
|
26
|
+
import { ResponseNode } from '../chat-tree-view';
|
|
23
27
|
|
|
24
28
|
@injectable()
|
|
25
29
|
export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallChatResponseContent> {
|
|
26
30
|
|
|
31
|
+
@inject(ToolConfirmationManager)
|
|
32
|
+
protected toolConfirmationManager: ToolConfirmationManager;
|
|
33
|
+
|
|
27
34
|
canHandle(response: ChatResponseContent): number {
|
|
28
35
|
if (ToolCallChatResponseContent.is(response)) {
|
|
29
36
|
return 10;
|
|
@@ -31,23 +38,20 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
|
|
|
31
38
|
return -1;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
render(response: ToolCallChatResponseContent): ReactNode {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)}
|
|
49
|
-
</h4>
|
|
50
|
-
);
|
|
41
|
+
render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
42
|
+
const chatId = parentNode.sessionId;
|
|
43
|
+
const confirmationMode = response.name ? this.getToolConfirmationSettings(response.name, chatId) : ToolConfirmationMode.DISABLED;
|
|
44
|
+
return <ToolCallContent
|
|
45
|
+
response={response}
|
|
46
|
+
confirmationMode={confirmationMode}
|
|
47
|
+
toolConfirmationManager={this.toolConfirmationManager}
|
|
48
|
+
chatId={chatId}
|
|
49
|
+
renderCollapsibleArguments={this.renderCollapsibleArguments.bind(this)}
|
|
50
|
+
tryPrettyPrintJson={this.tryPrettyPrintJson.bind(this)} />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected getToolConfirmationSettings(responseId: string, chatId: string): ToolConfirmationMode {
|
|
54
|
+
return this.toolConfirmationManager.getConfirmationMode(responseId, chatId);
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
protected renderCollapsibleArguments(args: string | undefined): ReactNode {
|
|
@@ -97,5 +101,97 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
|
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
const Spinner = () => (
|
|
100
|
-
<
|
|
104
|
+
<span className={codicon('loading')}></span>
|
|
101
105
|
);
|
|
106
|
+
|
|
107
|
+
interface ToolCallContentProps {
|
|
108
|
+
response: ToolCallChatResponseContent;
|
|
109
|
+
confirmationMode: ToolConfirmationMode;
|
|
110
|
+
toolConfirmationManager: ToolConfirmationManager;
|
|
111
|
+
chatId: string;
|
|
112
|
+
renderCollapsibleArguments: (args: string | undefined) => ReactNode;
|
|
113
|
+
tryPrettyPrintJson: (response: ToolCallChatResponseContent) => string | undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A function component to handle tool call rendering and confirmation
|
|
118
|
+
*/
|
|
119
|
+
const ToolCallContent: React.FC<ToolCallContentProps> = ({ response, confirmationMode, toolConfirmationManager, chatId, tryPrettyPrintJson, renderCollapsibleArguments }) => {
|
|
120
|
+
const [confirmationState, setConfirmationState] = React.useState<ToolConfirmationState>('waiting');
|
|
121
|
+
|
|
122
|
+
React.useEffect(() => {
|
|
123
|
+
if (confirmationMode === ToolConfirmationMode.YOLO) {
|
|
124
|
+
response.confirm();
|
|
125
|
+
setConfirmationState('approved');
|
|
126
|
+
return;
|
|
127
|
+
} else if (confirmationMode === ToolConfirmationMode.DISABLED) {
|
|
128
|
+
response.deny();
|
|
129
|
+
setConfirmationState('denied');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
response.confirmed.then(
|
|
133
|
+
confirmed => {
|
|
134
|
+
if (confirmed === true) {
|
|
135
|
+
setConfirmationState('approved');
|
|
136
|
+
} else {
|
|
137
|
+
setConfirmationState('denied');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
.catch(() => {
|
|
142
|
+
setConfirmationState('denied');
|
|
143
|
+
});
|
|
144
|
+
}, [response, confirmationMode]);
|
|
145
|
+
|
|
146
|
+
const handleApprove = React.useCallback((mode: 'once' | 'session' | 'forever' = 'once') => {
|
|
147
|
+
if (mode === 'forever' && response.name) {
|
|
148
|
+
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationMode.YOLO);
|
|
149
|
+
} else if (mode === 'session' && response.name) {
|
|
150
|
+
toolConfirmationManager.setSessionConfirmationMode(response.name, ToolConfirmationMode.YOLO, chatId);
|
|
151
|
+
}
|
|
152
|
+
response.confirm();
|
|
153
|
+
}, [response, toolConfirmationManager, chatId]);
|
|
154
|
+
|
|
155
|
+
const handleDeny = React.useCallback((mode: 'once' | 'session' | 'forever' = 'once') => {
|
|
156
|
+
if (mode === 'forever' && response.name) {
|
|
157
|
+
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationMode.DISABLED);
|
|
158
|
+
} else if (mode === 'session' && response.name) {
|
|
159
|
+
toolConfirmationManager.setSessionConfirmationMode(response.name, ToolConfirmationMode.DISABLED, chatId);
|
|
160
|
+
}
|
|
161
|
+
response.deny();
|
|
162
|
+
}, [response, toolConfirmationManager, chatId]);
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div className='theia-toolCall'>
|
|
166
|
+
<h4>
|
|
167
|
+
{confirmationState === 'denied' ? (
|
|
168
|
+
<span className="theia-tool-denied">
|
|
169
|
+
<span className={codicon('error')}></span> {nls.localize('theia/ai/chat-ui/toolcall-part-renderer/denied', 'Execution denied')}: {response.name}
|
|
170
|
+
</span>
|
|
171
|
+
) : response.finished ? (
|
|
172
|
+
<details>
|
|
173
|
+
<summary>{nls.localize('theia/ai/chat-ui/toolcall-part-renderer/finished', 'Ran')} {response.name}
|
|
174
|
+
({renderCollapsibleArguments(response.arguments)})
|
|
175
|
+
</summary>
|
|
176
|
+
<pre>{tryPrettyPrintJson(response)}</pre>
|
|
177
|
+
</details>
|
|
178
|
+
) : (
|
|
179
|
+
confirmationState === 'approved' && (
|
|
180
|
+
<span>
|
|
181
|
+
<Spinner /> {nls.localizeByDefault('Running')} {response.name}
|
|
182
|
+
</span>
|
|
183
|
+
)
|
|
184
|
+
)}
|
|
185
|
+
</h4>
|
|
186
|
+
|
|
187
|
+
{/* Show confirmation UI when waiting for approval */}
|
|
188
|
+
{confirmationState === 'waiting' && (
|
|
189
|
+
<ToolConfirmation
|
|
190
|
+
response={response}
|
|
191
|
+
onApprove={handleApprove}
|
|
192
|
+
onDeny={handleDeny}
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
@@ -19,7 +19,7 @@ import { AIChatInputWidget, type AIChatInputConfiguration } from '../chat-input-
|
|
|
19
19
|
import type { EditableRequestNode } from './chat-view-tree-widget';
|
|
20
20
|
import { URI } from '@theia/core';
|
|
21
21
|
import { CHAT_VIEW_LANGUAGE_EXTENSION } from '../chat-view-language-contribution';
|
|
22
|
-
import type { ChatRequestModel, EditableChatRequestModel } from '@theia/ai-chat';
|
|
22
|
+
import type { ChatRequestModel, EditableChatRequestModel, ChatHierarchyBranch } from '@theia/ai-chat';
|
|
23
23
|
import type { AIVariableResolutionRequest } from '@theia/ai-core';
|
|
24
24
|
import { Key } from '@theia/core/lib/browser';
|
|
25
25
|
|
|
@@ -29,6 +29,10 @@ export interface AIChatTreeInputConfiguration extends AIChatInputConfiguration {
|
|
|
29
29
|
export const AIChatTreeInputArgs = Symbol('AIChatTreeInputArgs');
|
|
30
30
|
export interface AIChatTreeInputArgs {
|
|
31
31
|
node: EditableRequestNode;
|
|
32
|
+
/**
|
|
33
|
+
* The branch of the chat tree for this request node (used by the input widget for state tracking).
|
|
34
|
+
*/
|
|
35
|
+
branch?: ChatHierarchyBranch;
|
|
32
36
|
initialValue?: string;
|
|
33
37
|
onQuery: (query: string) => Promise<void>;
|
|
34
38
|
onUnpin?: () => void;
|
|
@@ -60,6 +64,13 @@ export class AIChatTreeInputWidget extends AIChatInputWidget {
|
|
|
60
64
|
@postConstruct()
|
|
61
65
|
protected override init(): void {
|
|
62
66
|
super.init();
|
|
67
|
+
this.updateBranch();
|
|
68
|
+
|
|
69
|
+
const request = this.requestNode.request;
|
|
70
|
+
this.toDispose.push(request.session.onDidChange(() => {
|
|
71
|
+
this.updateBranch();
|
|
72
|
+
}));
|
|
73
|
+
|
|
63
74
|
this.addKeyListener(this.node, Key.ESCAPE, () => {
|
|
64
75
|
this.request.cancelEdit();
|
|
65
76
|
});
|
|
@@ -71,6 +82,10 @@ export class AIChatTreeInputWidget extends AIChatInputWidget {
|
|
|
71
82
|
});
|
|
72
83
|
}
|
|
73
84
|
|
|
85
|
+
protected updateBranch(): void {
|
|
86
|
+
this.branch = this.args.branch ?? this.requestNode.branch;
|
|
87
|
+
}
|
|
88
|
+
|
|
74
89
|
protected override getResourceUri(): URI {
|
|
75
90
|
return new URI(`ai-chat:/${this.requestNode.id}-input.${CHAT_VIEW_LANGUAGE_EXTENSION}`);
|
|
76
91
|
}
|
|
@@ -64,7 +64,8 @@ import { AIChatTreeInputFactory, type AIChatTreeInputWidget } from './chat-view-
|
|
|
64
64
|
// TODO Instead of directly operating on the ChatRequestModel we could use an intermediate view model
|
|
65
65
|
export interface RequestNode extends TreeNode {
|
|
66
66
|
request: ChatRequestModel,
|
|
67
|
-
branch: ChatHierarchyBranch
|
|
67
|
+
branch: ChatHierarchyBranch,
|
|
68
|
+
sessionId: string
|
|
68
69
|
}
|
|
69
70
|
export const isRequestNode = (node: TreeNode): node is RequestNode => 'request' in node;
|
|
70
71
|
|
|
@@ -75,7 +76,8 @@ export const isEditableRequestNode = (node: TreeNode): node is EditableRequestNo
|
|
|
75
76
|
|
|
76
77
|
// TODO Instead of directly operating on the ChatResponseModel we could use an intermediate view model
|
|
77
78
|
export interface ResponseNode extends TreeNode {
|
|
78
|
-
response: ChatResponseModel
|
|
79
|
+
response: ChatResponseModel,
|
|
80
|
+
sessionId: string
|
|
79
81
|
}
|
|
80
82
|
export const isResponseNode = (node: TreeNode): node is ResponseNode => 'response' in node;
|
|
81
83
|
|
|
@@ -136,6 +138,10 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
136
138
|
|
|
137
139
|
protected isEnabled = false;
|
|
138
140
|
|
|
141
|
+
protected chatModelId: string;
|
|
142
|
+
|
|
143
|
+
onScrollLockChange?: (temporaryLocked: boolean) => void;
|
|
144
|
+
|
|
139
145
|
set shouldScrollToEnd(shouldScrollToEnd: boolean) {
|
|
140
146
|
this._shouldScrollToEnd = shouldScrollToEnd;
|
|
141
147
|
this.shouldScrollToRow = this._shouldScrollToEnd;
|
|
@@ -178,6 +184,9 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
178
184
|
widget.setEnabled(change);
|
|
179
185
|
});
|
|
180
186
|
this.update();
|
|
187
|
+
}),
|
|
188
|
+
this.onScroll(scrollEvent => {
|
|
189
|
+
this.handleScrollEvent(scrollEvent);
|
|
181
190
|
})
|
|
182
191
|
]);
|
|
183
192
|
}
|
|
@@ -187,6 +196,27 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
187
196
|
this.update();
|
|
188
197
|
}
|
|
189
198
|
|
|
199
|
+
protected handleScrollEvent(_scrollEvent: unknown): void {
|
|
200
|
+
// Check if we're at the bottom of the view
|
|
201
|
+
const isAtBottom = this.isScrolledToBottom();
|
|
202
|
+
|
|
203
|
+
// Only handle temporary scroll lock if auto-scroll is currently enabled
|
|
204
|
+
if (this.shouldScrollToEnd) {
|
|
205
|
+
if (!isAtBottom) {
|
|
206
|
+
// User scrolled away from bottom, enable temporary lock
|
|
207
|
+
this.setTemporaryScrollLock(true);
|
|
208
|
+
}
|
|
209
|
+
} else if (isAtBottom) {
|
|
210
|
+
// User scrolled back to bottom, disable temporary lock
|
|
211
|
+
this.setTemporaryScrollLock(false);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
protected setTemporaryScrollLock(enabled: boolean): void {
|
|
216
|
+
// Immediately apply scroll lock changes without delay
|
|
217
|
+
this.onScrollLockChange?.(enabled);
|
|
218
|
+
}
|
|
219
|
+
|
|
190
220
|
protected override renderTree(model: TreeModel): React.ReactNode {
|
|
191
221
|
if (!this.isEnabled) {
|
|
192
222
|
return this.renderDisabledMessage();
|
|
@@ -214,7 +244,8 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
214
244
|
get request(): ChatRequestModel {
|
|
215
245
|
return branch.get();
|
|
216
246
|
},
|
|
217
|
-
branch
|
|
247
|
+
branch,
|
|
248
|
+
sessionId: this.chatModelId
|
|
218
249
|
};
|
|
219
250
|
}
|
|
220
251
|
|
|
@@ -222,7 +253,8 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
222
253
|
return {
|
|
223
254
|
id: response.id,
|
|
224
255
|
parent: this.model.root as CompositeTreeNode,
|
|
225
|
-
response
|
|
256
|
+
response,
|
|
257
|
+
sessionId: this.chatModelId
|
|
226
258
|
};
|
|
227
259
|
}
|
|
228
260
|
|
|
@@ -292,6 +324,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
292
324
|
protected async recreateModelTree(chatModel: ChatModel): Promise<void> {
|
|
293
325
|
if (CompositeTreeNode.is(this.model.root)) {
|
|
294
326
|
const nodes: TreeNode[] = [];
|
|
327
|
+
this.chatModelId = chatModel.id;
|
|
295
328
|
chatModel.getBranches().forEach(branch => {
|
|
296
329
|
const request = branch.get();
|
|
297
330
|
nodes.push(this.mapRequestToNode(branch));
|
|
@@ -426,7 +459,8 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
426
459
|
initialValue: editableNode.request.message.request.text,
|
|
427
460
|
onQuery: async query => {
|
|
428
461
|
editableNode.request.submitEdit({ text: query });
|
|
429
|
-
}
|
|
462
|
+
},
|
|
463
|
+
branch: editableNode.branch
|
|
430
464
|
});
|
|
431
465
|
|
|
432
466
|
this.chatInputs.set(editableNode.id, widget);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
|
-
import { CommandService, deepClone, Emitter, Event, MessageService } from '@theia/core';
|
|
16
|
+
import { CommandService, deepClone, Emitter, Event, MessageService, URI } from '@theia/core';
|
|
17
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';
|
|
@@ -28,6 +28,7 @@ import { FrontendVariableService } from '@theia/ai-core/lib/browser';
|
|
|
28
28
|
export namespace ChatViewWidget {
|
|
29
29
|
export interface State {
|
|
30
30
|
locked?: boolean;
|
|
31
|
+
temporaryLocked?: boolean;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -60,7 +61,7 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
60
61
|
|
|
61
62
|
protected chatSession: ChatSession;
|
|
62
63
|
|
|
63
|
-
protected _state: ChatViewWidget.State = { locked: false };
|
|
64
|
+
protected _state: ChatViewWidget.State = { locked: false, temporaryLocked: false };
|
|
64
65
|
protected readonly onStateChangedEmitter = new Emitter<ChatViewWidget.State>();
|
|
65
66
|
|
|
66
67
|
secondaryWindow: Window | undefined;
|
|
@@ -87,7 +88,8 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
87
88
|
this.treeWidget,
|
|
88
89
|
this.inputWidget,
|
|
89
90
|
this.onStateChanged(newState => {
|
|
90
|
-
|
|
91
|
+
const shouldScrollToEnd = !newState.locked && !newState.temporaryLocked;
|
|
92
|
+
this.treeWidget.shouldScrollToEnd = shouldScrollToEnd;
|
|
91
93
|
this.update();
|
|
92
94
|
})
|
|
93
95
|
]);
|
|
@@ -107,6 +109,7 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
107
109
|
this.inputWidget.onDeleteChangeSet = this.onDeleteChangeSet.bind(this);
|
|
108
110
|
this.inputWidget.onDeleteChangeSetElement = this.onDeleteChangeSetElement.bind(this);
|
|
109
111
|
this.treeWidget.trackChatModel(this.chatSession.model);
|
|
112
|
+
this.treeWidget.onScrollLockChange = this.onScrollLockChange.bind(this);
|
|
110
113
|
|
|
111
114
|
this.initListeners();
|
|
112
115
|
|
|
@@ -161,6 +164,8 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
161
164
|
if (oldState.locked) {
|
|
162
165
|
copy.locked = oldState.locked;
|
|
163
166
|
}
|
|
167
|
+
// Don't restore temporary lock state as it should reset on restart
|
|
168
|
+
copy.temporaryLocked = false;
|
|
164
169
|
this.state = copy;
|
|
165
170
|
}
|
|
166
171
|
|
|
@@ -210,16 +215,27 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
210
215
|
this.chatService.deleteChangeSet(sessionId);
|
|
211
216
|
}
|
|
212
217
|
|
|
213
|
-
protected onDeleteChangeSetElement(sessionId: string,
|
|
214
|
-
this.chatService.deleteChangeSetElement(sessionId,
|
|
218
|
+
protected onDeleteChangeSetElement(sessionId: string, uri: URI): void {
|
|
219
|
+
this.chatService.deleteChangeSetElement(sessionId, uri);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
protected onScrollLockChange(temporaryLocked: boolean): void {
|
|
223
|
+
this.setTemporaryLock(temporaryLocked);
|
|
215
224
|
}
|
|
216
225
|
|
|
217
226
|
lock(): void {
|
|
218
|
-
this.state = { ...deepClone(this.state), locked: true };
|
|
227
|
+
this.state = { ...deepClone(this.state), locked: true, temporaryLocked: false };
|
|
219
228
|
}
|
|
220
229
|
|
|
221
230
|
unlock(): void {
|
|
222
|
-
this.state = { ...deepClone(this.state), locked: false };
|
|
231
|
+
this.state = { ...deepClone(this.state), locked: false, temporaryLocked: false };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
setTemporaryLock(locked: boolean): void {
|
|
235
|
+
// Only set temporary lock if not permanently locked
|
|
236
|
+
if (!this.state.locked) {
|
|
237
|
+
this.state = { ...deepClone(this.state), temporaryLocked: locked };
|
|
238
|
+
}
|
|
223
239
|
}
|
|
224
240
|
|
|
225
241
|
get isLocked(): boolean {
|
|
@@ -624,6 +624,179 @@ div:last-child > .theia-ChatNode {
|
|
|
624
624
|
cursor: pointer;
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
+
/* Tool confirmation styles */
|
|
628
|
+
.theia-tool-confirmation {
|
|
629
|
+
margin: 10px 0;
|
|
630
|
+
padding: 12px;
|
|
631
|
+
border: 1px solid var(--theia-dropdown-border);
|
|
632
|
+
border-radius: 4px;
|
|
633
|
+
background-color: var(--theia-editorWidget-background);
|
|
634
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.14);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.theia-tool-confirmation-header {
|
|
638
|
+
font-weight: bold;
|
|
639
|
+
margin-bottom: 8px;
|
|
640
|
+
color: var(--theia-foreground);
|
|
641
|
+
display: flex;
|
|
642
|
+
align-items: center;
|
|
643
|
+
gap: 6px;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.theia-tool-confirmation-info {
|
|
647
|
+
margin-bottom: 12px;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.theia-tool-confirmation-name,
|
|
651
|
+
.theia-tool-confirmation-args {
|
|
652
|
+
margin-bottom: 4px;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.theia-tool-confirmation-name .label,
|
|
656
|
+
.theia-tool-confirmation-args .label {
|
|
657
|
+
font-weight: bold;
|
|
658
|
+
margin-right: 6px;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.theia-tool-confirmation-args pre.value {
|
|
662
|
+
margin: 6px 0;
|
|
663
|
+
padding: 8px;
|
|
664
|
+
background-color: var(--theia-editor-background);
|
|
665
|
+
border-radius: 3px;
|
|
666
|
+
max-height: 150px;
|
|
667
|
+
overflow: auto;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.theia-tool-confirmation-actions {
|
|
671
|
+
display: flex;
|
|
672
|
+
justify-content: flex-end;
|
|
673
|
+
gap: 0;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.theia-tool-confirmation-split-button {
|
|
677
|
+
display: inline-flex;
|
|
678
|
+
position: relative;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.theia-tool-confirmation-main-btn {
|
|
682
|
+
border-top-right-radius: 0 !important;
|
|
683
|
+
border-bottom-right-radius: 0 !important;
|
|
684
|
+
margin-right: 0 !important;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.theia-tool-confirmation-chevron-btn {
|
|
688
|
+
border-top-left-radius: 0 !important;
|
|
689
|
+
border-bottom-left-radius: 0 !important;
|
|
690
|
+
width: 24px !important;
|
|
691
|
+
min-width: 24px !important;
|
|
692
|
+
max-width: 24px !important;
|
|
693
|
+
padding: 0 !important;
|
|
694
|
+
margin-left: 0 !important;
|
|
695
|
+
display: flex !important;
|
|
696
|
+
align-items: center !important;
|
|
697
|
+
justify-content: center !important;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.theia-tool-confirmation-dropdown-menu {
|
|
701
|
+
position: absolute;
|
|
702
|
+
top: 100%;
|
|
703
|
+
left: 0;
|
|
704
|
+
z-index: 10;
|
|
705
|
+
min-width: 180px;
|
|
706
|
+
background: var(--theia-menu-background);
|
|
707
|
+
border: 1px solid var(--theia-menu-border);
|
|
708
|
+
border-radius: 4px;
|
|
709
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
710
|
+
margin: 0;
|
|
711
|
+
padding: 0;
|
|
712
|
+
list-style: none;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.theia-tool-confirmation-dropdown-item {
|
|
716
|
+
padding: 6px 16px;
|
|
717
|
+
cursor: pointer;
|
|
718
|
+
white-space: nowrap;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.theia-tool-confirmation-dropdown-item:hover {
|
|
722
|
+
background: var(--theia-list-hoverBackground, #e5e5e5);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.theia-tool-confirmation-status {
|
|
726
|
+
margin: 10px 0;
|
|
727
|
+
padding: 12px;
|
|
728
|
+
border: 1px solid var(--theia-dropdown-border);
|
|
729
|
+
border-radius: 4px;
|
|
730
|
+
background-color: var(--theia-editorWidget-background);
|
|
731
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.14);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.theia-tool-confirmation-header {
|
|
735
|
+
font-weight: bold;
|
|
736
|
+
margin-bottom: 8px;
|
|
737
|
+
color: var(--theia-foreground);
|
|
738
|
+
display: flex;
|
|
739
|
+
align-items: center;
|
|
740
|
+
gap: 6px;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.theia-tool-confirmation-info {
|
|
744
|
+
margin-bottom: 12px;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.theia-tool-confirmation-name,
|
|
748
|
+
.theia-tool-confirmation-args {
|
|
749
|
+
margin-bottom: 4px;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.theia-tool-confirmation-name .label,
|
|
753
|
+
.theia-tool-confirmation-args .label {
|
|
754
|
+
font-weight: bold;
|
|
755
|
+
margin-right: 6px;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.theia-tool-confirmation-args pre.value {
|
|
759
|
+
margin: 6px 0;
|
|
760
|
+
padding: 8px;
|
|
761
|
+
background-color: var(--theia-editor-background);
|
|
762
|
+
border-radius: 3px;
|
|
763
|
+
max-height: 150px;
|
|
764
|
+
overflow: auto;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.theia-tool-confirmation-actions {
|
|
768
|
+
display: flex;
|
|
769
|
+
justify-content: flex-end;
|
|
770
|
+
gap: 8px;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.theia-tool-confirmation-status {
|
|
774
|
+
padding: 8px;
|
|
775
|
+
margin: 10px 0;
|
|
776
|
+
border-radius: 4px;
|
|
777
|
+
display: flex;
|
|
778
|
+
align-items: center;
|
|
779
|
+
gap: 6px;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.theia-tool-confirmation-status.approved {
|
|
783
|
+
background-color: var(--theia-successBackground);
|
|
784
|
+
color: var(--theia-successForeground);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.theia-tool-confirmation-status.denied {
|
|
788
|
+
background-color: var(--theia-errorBackground);
|
|
789
|
+
color: var(--theia-errorForeground);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.theia-tool-pending {
|
|
793
|
+
color: var(--theia-descriptionForeground);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.theia-tool-denied {
|
|
797
|
+
color: var(--theia-errorForeground);
|
|
798
|
+
}
|
|
799
|
+
|
|
627
800
|
.theia-toolCall .fa,
|
|
628
801
|
.theia-toolCall details summary::marker,
|
|
629
802
|
.theia-thinking .fa,
|