@alpaca-editor/core 1.0.4033 → 1.0.4037
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/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/{editor/menubar → components/ui}/LanguageSelector.d.ts +1 -1
- package/dist/{editor/menubar → components/ui}/LanguageSelector.js +8 -8
- package/dist/components/ui/LanguageSelector.js.map +1 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +1 -1
- package/dist/components/ui/dropdown-menu.js +2 -2
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/sonner.js +3 -1
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/config/config.js +5 -5
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +33 -9
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/PictureEditor.js +2 -2
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/ScrollingContentTree.d.ts +2 -1
- package/dist/editor/ScrollingContentTree.js +2 -2
- package/dist/editor/ScrollingContentTree.js.map +1 -1
- package/dist/editor/Terminal.d.ts +2 -0
- package/dist/editor/Terminal.js +2 -2
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AgentHistory.d.ts +11 -0
- package/dist/editor/ai/AgentHistory.js +12 -0
- package/dist/editor/ai/AgentHistory.js.map +1 -0
- package/dist/editor/ai/Agents.js +187 -24
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +2 -1
- package/dist/editor/ai/AiResponseMessage.js +6 -6
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +1 -0
- package/dist/editor/ai/AiTerminal.js +330 -43
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +19 -7
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +48 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.js +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js +2 -2
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +2 -2
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +9 -9
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js +2 -2
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +2 -2
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +12 -12
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.js +10 -11
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +2 -2
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +4 -4
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +2 -2
- package/dist/editor/page-viewer/pageViewContext.js +11 -14
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +21 -1
- package/dist/editor/services/agentService.js +101 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -1
- package/dist/editor/services/aiService.js +1 -2
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +5 -6
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.d.ts +2 -1
- package/dist/editor/sidebar/MainContentTree.js +9 -5
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/ui/Icons.d.ts +5 -0
- package/dist/editor/ui/Icons.js +3 -0
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +26 -14
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +54 -2
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/views/CompareView.js +3 -3
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/EditView.js +1 -1
- package/dist/editor/views/EditView.js.map +1 -1
- package/dist/editor/views/ItemEditor.js +1 -1
- package/dist/editor/views/ItemEditor.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +5 -5
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/FindItemsStep.js +1 -1
- package/dist/page-wizard/steps/ImagesStep.js +1 -1
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/page-wizard/steps/LayoutStep.js +1 -1
- package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +1 -1
- package/dist/page-wizard/steps/StructureStep.js +28 -11
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/steps/TranslateStep.d.ts +1 -0
- package/dist/page-wizard/steps/TranslateStep.js +67 -73
- package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/NewPage.js +1 -1
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/OpenPage.js +1 -1
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/styles.css +71 -8
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/{editor/menubar → components/ui}/LanguageSelector.tsx +12 -12
- package/src/components/ui/dropdown-menu.tsx +3 -1
- package/src/components/ui/sonner.tsx +5 -1
- package/src/config/config.tsx +4 -3
- package/src/editor/ContentTree.tsx +41 -12
- package/src/editor/PictureEditor.tsx +2 -2
- package/src/editor/ScrollingContentTree.tsx +3 -0
- package/src/editor/Terminal.tsx +16 -7
- package/src/editor/ai/AgentHistory.tsx +85 -0
- package/src/editor/ai/Agents.tsx +256 -88
- package/src/editor/ai/AiResponseMessage.tsx +25 -11
- package/src/editor/ai/AiTerminal.tsx +571 -73
- package/src/editor/client/itemsRepository.ts +29 -12
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +52 -1
- package/src/editor/field-types/richtext/components/SimpleRichTextEditor.css +64 -0
- package/src/editor/field-types/richtext/contextMenuFactory.tsx +7 -8
- package/src/editor/menubar/ItemLanguageVersion.tsx +1 -1
- package/src/editor/menubar/PageSelector.tsx +1 -0
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +1 -1
- package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +3 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +2 -2
- package/src/editor/page-editor-chrome/InlineEditor.tsx +9 -12
- package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +3 -3
- package/src/editor/page-editor-chrome/PageEditorChrome.tsx +3 -3
- package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +1 -1
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +1 -1
- package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +2 -2
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +12 -18
- package/src/editor/page-viewer/EditorForm.tsx +1 -1
- package/src/editor/page-viewer/MiniMap.tsx +10 -11
- package/src/editor/page-viewer/PageViewer.tsx +8 -3
- package/src/editor/page-viewer/PageViewerFrame.tsx +4 -4
- package/src/editor/page-viewer/pageViewContext.ts +71 -66
- package/src/editor/services/agentService.ts +129 -1
- package/src/editor/services/aiService.ts +2 -4
- package/src/editor/sidebar/GraphQL.tsx +16 -15
- package/src/editor/sidebar/MainContentTree.tsx +12 -4
- package/src/editor/ui/Icons.tsx +35 -0
- package/src/editor/ui/ItemNameDialogNew.tsx +29 -13
- package/src/editor/ui/PerfectTree.tsx +70 -4
- package/src/editor/views/CompareView.tsx +3 -3
- package/src/editor/views/EditView.tsx +1 -1
- package/src/editor/views/ItemEditor.tsx +1 -1
- package/src/index.ts +10 -4
- package/src/page-wizard/steps/ContentStep.tsx +5 -5
- package/src/page-wizard/steps/FindItemsStep.tsx +1 -1
- package/src/page-wizard/steps/ImagesStep.tsx +1 -1
- package/src/page-wizard/steps/LayoutStep.tsx +1 -1
- package/src/page-wizard/steps/MetaDataStep.tsx +1 -1
- package/src/page-wizard/steps/SelectStep.tsx +1 -1
- package/src/page-wizard/steps/StructureStep.tsx +41 -17
- package/src/page-wizard/steps/TranslateStep.tsx +326 -222
- package/src/revision.ts +2 -2
- package/src/splash-screen/NewPage.tsx +1 -0
- package/src/splash-screen/OpenPage.tsx +1 -0
- package/dist/components/SimpleLanguageSelector.d.ts +0 -10
- package/dist/components/SimpleLanguageSelector.js +0 -59
- package/dist/components/SimpleLanguageSelector.js.map +0 -1
- package/dist/editor/menubar/LanguageSelector.js.map +0 -1
- package/src/components/SimpleLanguageSelector.tsx +0 -113
|
@@ -6,7 +6,13 @@ import { TerminalService } from "primereact/terminalservice";
|
|
|
6
6
|
|
|
7
7
|
import { Terminal } from "../Terminal";
|
|
8
8
|
import { useEditContext } from "../client/editContext";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
DropdownMenu,
|
|
11
|
+
DropdownMenuContent,
|
|
12
|
+
DropdownMenuItem,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from "../../components/ui/dropdown-menu";
|
|
15
|
+
import { ChevronDown } from "lucide-react";
|
|
10
16
|
|
|
11
17
|
import { WizardIcon } from "../ui/Icons";
|
|
12
18
|
import { AiResponseMessage } from "./AiResponseMessage";
|
|
@@ -20,14 +26,26 @@ import {
|
|
|
20
26
|
getAgents,
|
|
21
27
|
getChatHistory,
|
|
22
28
|
cancelAgent,
|
|
29
|
+
checkAgentState,
|
|
30
|
+
convertAgentMessagesToTerminalFormat,
|
|
23
31
|
AgentStreamMessageType,
|
|
24
32
|
StartAgentRequest,
|
|
25
33
|
AgentStreamMessage,
|
|
26
34
|
} from "../services/agentService";
|
|
27
35
|
import { EditOperation } from "../../types";
|
|
28
36
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
29
|
-
import { Settings } from "lucide-react";
|
|
37
|
+
import { Settings, Square } from "lucide-react";
|
|
30
38
|
import { AgentCostDisplay } from "./AgentCostDisplay";
|
|
39
|
+
import {
|
|
40
|
+
Tooltip,
|
|
41
|
+
TooltipContent,
|
|
42
|
+
TooltipTrigger,
|
|
43
|
+
} from "../../components/ui/tooltip";
|
|
44
|
+
import {
|
|
45
|
+
Popover,
|
|
46
|
+
PopoverContent,
|
|
47
|
+
PopoverTrigger,
|
|
48
|
+
} from "../../components/ui/popover";
|
|
31
49
|
|
|
32
50
|
type Response = {
|
|
33
51
|
messages: Message[];
|
|
@@ -37,6 +55,7 @@ type Response = {
|
|
|
37
55
|
numCachedTokens: number;
|
|
38
56
|
state: string;
|
|
39
57
|
agentName?: string; // Updated agent name (only sent when the agent name has been updated)
|
|
58
|
+
error?: string; // Error message when agent execution fails
|
|
40
59
|
// Cost information from backend
|
|
41
60
|
totalCost?: number;
|
|
42
61
|
totalInputTokenCost?: number;
|
|
@@ -137,6 +156,22 @@ export function AiTerminal({
|
|
|
137
156
|
);
|
|
138
157
|
const [showSettings, setShowSettings] = useState(false);
|
|
139
158
|
const settingsRef = useRef<HTMLDivElement>(null);
|
|
159
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
160
|
+
const abortControllerRef = useRef<AbortController | null>(null);
|
|
161
|
+
const [terminalError, setTerminalError] = useState<string | null>(null);
|
|
162
|
+
|
|
163
|
+
// Debug function - expose globally for testing
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
(window as any).testAiTerminalError = (
|
|
166
|
+
errorMsg: string = "Test error message",
|
|
167
|
+
) => {
|
|
168
|
+
console.log("Manually triggering error:", errorMsg);
|
|
169
|
+
setTerminalError(errorMsg);
|
|
170
|
+
};
|
|
171
|
+
return () => {
|
|
172
|
+
delete (window as any).testAiTerminalError;
|
|
173
|
+
};
|
|
174
|
+
}, []);
|
|
140
175
|
|
|
141
176
|
useEffect(() => {
|
|
142
177
|
if (options?.initialPrompt && !initialPromptExecuted && model) {
|
|
@@ -164,8 +199,271 @@ export function AiTerminal({
|
|
|
164
199
|
fetchProfiles();
|
|
165
200
|
}, [editContext?.currentItemDescriptor]);
|
|
166
201
|
|
|
202
|
+
// Agent reconnection logic
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
async function checkAndReconnectAgent() {
|
|
205
|
+
if (!editContext || !agentId) return;
|
|
206
|
+
|
|
207
|
+
const context = createAiContext({ editContext });
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
console.log("Checking agent state for reconnection:", agentId);
|
|
211
|
+
const agentState = await checkAgentState(agentId, context);
|
|
212
|
+
|
|
213
|
+
if (agentState.exists && agentState.agent) {
|
|
214
|
+
console.log(
|
|
215
|
+
"Found existing agent, restoring state:",
|
|
216
|
+
agentState.agent,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const agent = agentState.agent;
|
|
220
|
+
|
|
221
|
+
// Convert and set persisted messages if not already set via options
|
|
222
|
+
if (
|
|
223
|
+
agent.messages &&
|
|
224
|
+
(!options?.initialMessages || options.initialMessages.length === 0)
|
|
225
|
+
) {
|
|
226
|
+
const terminalMessages = convertAgentMessagesToTerminalFormat(
|
|
227
|
+
agent.messages,
|
|
228
|
+
);
|
|
229
|
+
console.log("Restoring messages:", terminalMessages);
|
|
230
|
+
setMessages(terminalMessages);
|
|
231
|
+
setResponseMessages(terminalMessages);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Set cost information if available
|
|
235
|
+
if (agent.totalCost !== undefined) {
|
|
236
|
+
setResponse(
|
|
237
|
+
(prev) =>
|
|
238
|
+
({
|
|
239
|
+
...prev,
|
|
240
|
+
totalCost: agent.totalCost,
|
|
241
|
+
totalInputTokenCost: agent.totalInputTokenCost,
|
|
242
|
+
totalOutputTokenCost: agent.totalOutputTokenCost,
|
|
243
|
+
totalCachedTokenCost: agent.totalCachedInputTokenCost,
|
|
244
|
+
totalInputTokens: agent.totalInputTokens,
|
|
245
|
+
totalOutputTokens: agent.totalOutputTokens,
|
|
246
|
+
totalCachedTokens: agent.totalCachedInputTokens,
|
|
247
|
+
}) as Response,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If agent is still running, reconnect to the stream
|
|
252
|
+
if (agentState.isRunning) {
|
|
253
|
+
console.log("Agent is still running, reconnecting to stream");
|
|
254
|
+
await reconnectToRunningAgent(agentId, context);
|
|
255
|
+
} else {
|
|
256
|
+
console.log(
|
|
257
|
+
"Agent completed while disconnected, showing final state",
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
console.log("No existing agent found or agent doesn't exist");
|
|
262
|
+
}
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error("Failed to check agent state:", error);
|
|
265
|
+
// Don't show error to user for reconnection failures - just continue as normal
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Only try to reconnect if we have an agentId and haven't already initialized with messages
|
|
270
|
+
if (agentId && editContext) {
|
|
271
|
+
checkAndReconnectAgent();
|
|
272
|
+
}
|
|
273
|
+
}, [agentId]); // Only run when agentId changes
|
|
274
|
+
|
|
275
|
+
// Function to reconnect to a running agent stream
|
|
276
|
+
async function reconnectToRunningAgent(agentId: string, context: AiContext) {
|
|
277
|
+
try {
|
|
278
|
+
const abortController = new AbortController();
|
|
279
|
+
abortControllerRef.current = abortController;
|
|
280
|
+
setIsRunning(true);
|
|
281
|
+
|
|
282
|
+
let accumulatedResponse: any = {
|
|
283
|
+
messages: [...messages],
|
|
284
|
+
editOperations: [],
|
|
285
|
+
numInputTokens: 0,
|
|
286
|
+
numOutputTokens: 0,
|
|
287
|
+
numCachedTokens: 0,
|
|
288
|
+
state: "running",
|
|
289
|
+
};
|
|
290
|
+
let completionProcessed = false;
|
|
291
|
+
|
|
292
|
+
console.log("Reconnecting to agent stream:", agentId);
|
|
293
|
+
await connectToAgentStream(
|
|
294
|
+
agentId,
|
|
295
|
+
context,
|
|
296
|
+
(streamMessage) => {
|
|
297
|
+
console.log(
|
|
298
|
+
"Reconnected stream message:",
|
|
299
|
+
streamMessage.type,
|
|
300
|
+
streamMessage,
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// Use the same stream message handling logic as the original startAgent function
|
|
304
|
+
// (This is the same logic that was already in the component)
|
|
305
|
+
handleStreamMessage(
|
|
306
|
+
streamMessage,
|
|
307
|
+
accumulatedResponse,
|
|
308
|
+
completionProcessed,
|
|
309
|
+
context,
|
|
310
|
+
);
|
|
311
|
+
},
|
|
312
|
+
abortController.signal,
|
|
313
|
+
);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error("Failed to reconnect to agent stream:", error);
|
|
316
|
+
setIsRunning(false);
|
|
317
|
+
setTerminalError(
|
|
318
|
+
`Failed to reconnect to agent: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Extract stream message handling logic into a separate function for reuse
|
|
324
|
+
function handleStreamMessage(
|
|
325
|
+
streamMessage: AgentStreamMessage,
|
|
326
|
+
accumulatedResponse: any,
|
|
327
|
+
completionProcessed: boolean,
|
|
328
|
+
context: AiContext,
|
|
329
|
+
) {
|
|
330
|
+
// Safety check for valid stream message
|
|
331
|
+
if (!streamMessage || !streamMessage.type) {
|
|
332
|
+
console.error("Invalid stream message received:", streamMessage);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
console.log(
|
|
337
|
+
"Processing stream message:",
|
|
338
|
+
streamMessage.type,
|
|
339
|
+
streamMessage,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// Handle different message types (this logic was already in the component)
|
|
343
|
+
switch (streamMessage.type) {
|
|
344
|
+
case AgentStreamMessageType.StatusUpdate:
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case AgentStreamMessageType.ContentChunk:
|
|
348
|
+
if (streamMessage.data && typeof streamMessage.data === "object") {
|
|
349
|
+
if (
|
|
350
|
+
streamMessage.data.isIncremental &&
|
|
351
|
+
streamMessage.data.deltaContent
|
|
352
|
+
) {
|
|
353
|
+
// Handle incremental content updates
|
|
354
|
+
const updatedMessages = [...(accumulatedResponse.messages || [])];
|
|
355
|
+
let lastAssistantMessage = null;
|
|
356
|
+
let lastAssistantIndex = -1;
|
|
357
|
+
|
|
358
|
+
// Find the last assistant message
|
|
359
|
+
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
360
|
+
if (updatedMessages[i].role === "assistant") {
|
|
361
|
+
lastAssistantMessage = updatedMessages[i];
|
|
362
|
+
lastAssistantIndex = i;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// If no assistant message exists, or the last one has tool calls, create a new one
|
|
368
|
+
if (
|
|
369
|
+
!lastAssistantMessage ||
|
|
370
|
+
(lastAssistantMessage.tool_calls &&
|
|
371
|
+
lastAssistantMessage.tool_calls.length > 0)
|
|
372
|
+
) {
|
|
373
|
+
lastAssistantMessage = {
|
|
374
|
+
id: crypto.randomUUID(),
|
|
375
|
+
role: "assistant",
|
|
376
|
+
name: "assistant",
|
|
377
|
+
content: "",
|
|
378
|
+
tool_calls: [],
|
|
379
|
+
};
|
|
380
|
+
updatedMessages.push(lastAssistantMessage);
|
|
381
|
+
lastAssistantIndex = updatedMessages.length - 1;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Append the delta content to build up the full message
|
|
385
|
+
const currentContent = lastAssistantMessage.content || "";
|
|
386
|
+
const newContent = currentContent + streamMessage.data.deltaContent;
|
|
387
|
+
|
|
388
|
+
// Update the message with accumulated content
|
|
389
|
+
updatedMessages[lastAssistantIndex] = {
|
|
390
|
+
...lastAssistantMessage,
|
|
391
|
+
content: newContent,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// Update accumulated response with incremental content
|
|
395
|
+
accumulatedResponse = {
|
|
396
|
+
...accumulatedResponse,
|
|
397
|
+
...streamMessage.data,
|
|
398
|
+
messages: updatedMessages,
|
|
399
|
+
editOperations:
|
|
400
|
+
streamMessage.data.editOperations ||
|
|
401
|
+
accumulatedResponse.editOperations,
|
|
402
|
+
};
|
|
403
|
+
} else {
|
|
404
|
+
// Non-incremental update - use as provided
|
|
405
|
+
accumulatedResponse = {
|
|
406
|
+
...accumulatedResponse,
|
|
407
|
+
...streamMessage.data,
|
|
408
|
+
editOperations:
|
|
409
|
+
streamMessage.data.editOperations ||
|
|
410
|
+
accumulatedResponse.editOperations,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Always trigger UI update for content chunks
|
|
415
|
+
handleResponse(accumulatedResponse, () => {}, false);
|
|
416
|
+
}
|
|
417
|
+
break;
|
|
418
|
+
|
|
419
|
+
case AgentStreamMessageType.ToolCall:
|
|
420
|
+
// Handle tool calls - simplified for reconnection
|
|
421
|
+
if (streamMessage.data && typeof streamMessage.data === "object") {
|
|
422
|
+
console.log(
|
|
423
|
+
"Tool call received during reconnection:",
|
|
424
|
+
streamMessage.data,
|
|
425
|
+
);
|
|
426
|
+
// For reconnection, we mainly need to update the UI
|
|
427
|
+
// The detailed tool call logic can be handled the same as in the original
|
|
428
|
+
}
|
|
429
|
+
break;
|
|
430
|
+
|
|
431
|
+
case AgentStreamMessageType.ToolResult:
|
|
432
|
+
// Handle tool results - simplified for reconnection
|
|
433
|
+
if (streamMessage.data && typeof streamMessage.data === "object") {
|
|
434
|
+
console.log(
|
|
435
|
+
"Tool result received during reconnection:",
|
|
436
|
+
streamMessage.data,
|
|
437
|
+
);
|
|
438
|
+
// For reconnection, we mainly need to update the UI
|
|
439
|
+
// The detailed tool result logic can be handled the same as in the original
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
|
|
443
|
+
case AgentStreamMessageType.Completed:
|
|
444
|
+
if (!completionProcessed) {
|
|
445
|
+
completionProcessed = true;
|
|
446
|
+
console.log("Agent execution completed");
|
|
447
|
+
setIsRunning(false);
|
|
448
|
+
if (streamMessage.data) {
|
|
449
|
+
handleResponse(streamMessage.data, () => {}, true);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
break;
|
|
453
|
+
|
|
454
|
+
case AgentStreamMessageType.Error:
|
|
455
|
+
console.error("Agent execution error:", streamMessage);
|
|
456
|
+
setIsRunning(false);
|
|
457
|
+
setTerminalError(streamMessage.error || "Agent execution failed");
|
|
458
|
+
break;
|
|
459
|
+
|
|
460
|
+
default:
|
|
461
|
+
console.warn("Unknown stream message type:", streamMessage.type);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
167
465
|
useEffect(() => {
|
|
168
|
-
if (activeProfile?.
|
|
466
|
+
if (activeProfile?.defaultModelId) setModel(activeProfile.defaultModelId);
|
|
169
467
|
}, [activeProfile]);
|
|
170
468
|
|
|
171
469
|
const messagesRef = useRef(messages);
|
|
@@ -297,18 +595,38 @@ export function AiTerminal({
|
|
|
297
595
|
// Handle click outside for settings popover
|
|
298
596
|
useEffect(() => {
|
|
299
597
|
function handleClickOutside(event: MouseEvent) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
) {
|
|
304
|
-
|
|
598
|
+
const target = event.target as Element;
|
|
599
|
+
|
|
600
|
+
// Check if the click is inside the settings popover
|
|
601
|
+
if (settingsRef.current && settingsRef.current.contains(target)) {
|
|
602
|
+
return;
|
|
305
603
|
}
|
|
604
|
+
|
|
605
|
+
// Check if the click is inside any dropdown menu content
|
|
606
|
+
// Radix UI dropdown menus can have various selectors
|
|
607
|
+
const isInDropdown = target.closest(
|
|
608
|
+
[
|
|
609
|
+
"[data-radix-popper-content-wrapper]",
|
|
610
|
+
'[data-slot="dropdown-menu-content"]',
|
|
611
|
+
"[data-radix-dropdown-menu-content]",
|
|
612
|
+
'[role="menu"]',
|
|
613
|
+
".dropdown-menu", // fallback for any custom dropdown classes
|
|
614
|
+
].join(", "),
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
if (isInDropdown) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// If we get here, it's a click outside both the settings and any dropdown
|
|
622
|
+
setShowSettings(false);
|
|
306
623
|
}
|
|
307
624
|
|
|
308
625
|
if (showSettings) {
|
|
309
|
-
|
|
626
|
+
// Use mousedown instead of click for better timing
|
|
627
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
310
628
|
return () => {
|
|
311
|
-
document.removeEventListener("
|
|
629
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
312
630
|
};
|
|
313
631
|
}
|
|
314
632
|
}, [showSettings]);
|
|
@@ -342,6 +660,7 @@ export function AiTerminal({
|
|
|
342
660
|
messages={formattedMessages}
|
|
343
661
|
editOperations={response.editOperations}
|
|
344
662
|
finished={isFinished}
|
|
663
|
+
error={response.error}
|
|
345
664
|
/>,
|
|
346
665
|
isFinished,
|
|
347
666
|
);
|
|
@@ -352,6 +671,7 @@ export function AiTerminal({
|
|
|
352
671
|
messages={messagesRef.current}
|
|
353
672
|
editOperations={response.editOperations}
|
|
354
673
|
finished={isFinished}
|
|
674
|
+
error={response.error}
|
|
355
675
|
/>,
|
|
356
676
|
isFinished,
|
|
357
677
|
);
|
|
@@ -416,10 +736,11 @@ export function AiTerminal({
|
|
|
416
736
|
};
|
|
417
737
|
|
|
418
738
|
const startResponse = await startAgent(startRequest, context);
|
|
419
|
-
console.log("Agent started:", startResponse);
|
|
420
739
|
|
|
421
740
|
// Step 2: Connect to the agent stream for real-time updates
|
|
422
741
|
const abortController = new AbortController();
|
|
742
|
+
abortControllerRef.current = abortController;
|
|
743
|
+
setIsRunning(true);
|
|
423
744
|
let accumulatedResponse: any = {
|
|
424
745
|
messages: [...messages],
|
|
425
746
|
editOperations: [],
|
|
@@ -442,6 +763,18 @@ export function AiTerminal({
|
|
|
442
763
|
// new Date().toISOString(),
|
|
443
764
|
// );
|
|
444
765
|
|
|
766
|
+
// Safety check for valid stream message
|
|
767
|
+
if (!streamMessage || !streamMessage.type) {
|
|
768
|
+
console.error("Invalid stream message received:", streamMessage);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
console.log(
|
|
773
|
+
"Processing stream message:",
|
|
774
|
+
streamMessage.type,
|
|
775
|
+
streamMessage,
|
|
776
|
+
);
|
|
777
|
+
|
|
445
778
|
// Message types are now normalized in agentService.ts
|
|
446
779
|
switch (streamMessage.type) {
|
|
447
780
|
case AgentStreamMessageType.StatusUpdate:
|
|
@@ -449,7 +782,10 @@ export function AiTerminal({
|
|
|
449
782
|
break;
|
|
450
783
|
|
|
451
784
|
case AgentStreamMessageType.ContentChunk:
|
|
452
|
-
if (
|
|
785
|
+
if (
|
|
786
|
+
streamMessage.data &&
|
|
787
|
+
typeof streamMessage.data === "object"
|
|
788
|
+
) {
|
|
453
789
|
// Handle incremental content updates
|
|
454
790
|
if (
|
|
455
791
|
streamMessage.data.isIncremental &&
|
|
@@ -526,7 +862,10 @@ export function AiTerminal({
|
|
|
526
862
|
|
|
527
863
|
case AgentStreamMessageType.ToolCall:
|
|
528
864
|
// console.log("Tool call received:", streamMessage.data);
|
|
529
|
-
if (
|
|
865
|
+
if (
|
|
866
|
+
streamMessage.data &&
|
|
867
|
+
typeof streamMessage.data === "object"
|
|
868
|
+
) {
|
|
530
869
|
// Find or create the assistant message that contains this tool call
|
|
531
870
|
const updatedMessages = [
|
|
532
871
|
...(accumulatedResponse.messages || []),
|
|
@@ -606,12 +945,16 @@ export function AiTerminal({
|
|
|
606
945
|
|
|
607
946
|
case AgentStreamMessageType.ToolResult:
|
|
608
947
|
console.log("Tool result received:", streamMessage.data);
|
|
609
|
-
if (
|
|
948
|
+
if (
|
|
949
|
+
streamMessage.data &&
|
|
950
|
+
typeof streamMessage.data === "object"
|
|
951
|
+
) {
|
|
610
952
|
// Find the assistant message and tool call to update with the result
|
|
611
953
|
const updatedMessages = [
|
|
612
954
|
...(accumulatedResponse.messages || []),
|
|
613
955
|
];
|
|
614
956
|
|
|
957
|
+
let messageUpdated = false;
|
|
615
958
|
// Find the assistant message with the matching tool call ID
|
|
616
959
|
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
617
960
|
if (
|
|
@@ -639,6 +982,7 @@ export function AiTerminal({
|
|
|
639
982
|
...updatedMessages[i],
|
|
640
983
|
tool_calls: updatedToolCalls,
|
|
641
984
|
};
|
|
985
|
+
messageUpdated = true;
|
|
642
986
|
break;
|
|
643
987
|
}
|
|
644
988
|
}
|
|
@@ -656,12 +1000,15 @@ export function AiTerminal({
|
|
|
656
1000
|
break;
|
|
657
1001
|
|
|
658
1002
|
case AgentStreamMessageType.Completed:
|
|
659
|
-
if (
|
|
1003
|
+
if (
|
|
1004
|
+
streamMessage.data &&
|
|
1005
|
+
typeof streamMessage.data === "object"
|
|
1006
|
+
) {
|
|
660
1007
|
// Final completion data
|
|
661
1008
|
const finalResponse = {
|
|
662
1009
|
messages:
|
|
663
|
-
|
|
664
|
-
|
|
1010
|
+
accumulatedResponse.messages ||
|
|
1011
|
+
streamMessage.data.messages,
|
|
665
1012
|
editOperations:
|
|
666
1013
|
streamMessage.data.editOperations ||
|
|
667
1014
|
accumulatedResponse.editOperations,
|
|
@@ -676,6 +1023,8 @@ export function AiTerminal({
|
|
|
676
1023
|
accumulatedResponse.numCachedTokens,
|
|
677
1024
|
state: "completed",
|
|
678
1025
|
agentName: streamMessage.data.agentName,
|
|
1026
|
+
error:
|
|
1027
|
+
streamMessage.data.error || accumulatedResponse.error,
|
|
679
1028
|
// Add cost fields from backend (try both tokenUsage object and direct properties)
|
|
680
1029
|
totalCost:
|
|
681
1030
|
streamMessage.data.totalCost ||
|
|
@@ -731,6 +1080,7 @@ export function AiTerminal({
|
|
|
731
1080
|
messages={formattedMessages}
|
|
732
1081
|
editOperations={finalResponse.editOperations}
|
|
733
1082
|
finished={true}
|
|
1083
|
+
error={finalResponse.error}
|
|
734
1084
|
/>,
|
|
735
1085
|
true,
|
|
736
1086
|
);
|
|
@@ -746,15 +1096,41 @@ export function AiTerminal({
|
|
|
746
1096
|
break;
|
|
747
1097
|
|
|
748
1098
|
case AgentStreamMessageType.Error:
|
|
749
|
-
|
|
1099
|
+
const errorMessage =
|
|
1100
|
+
streamMessage.error || "Unknown agent error";
|
|
1101
|
+
console.error("Agent execution error:", errorMessage);
|
|
1102
|
+
console.log("Displaying error in terminal:", errorMessage);
|
|
1103
|
+
setTerminalError(errorMessage);
|
|
1104
|
+
|
|
750
1105
|
const errorResponse = {
|
|
751
1106
|
...accumulatedResponse,
|
|
752
1107
|
state: "error",
|
|
753
|
-
error:
|
|
1108
|
+
error: errorMessage,
|
|
754
1109
|
};
|
|
755
1110
|
setResponse(errorResponse);
|
|
1111
|
+
|
|
1112
|
+
// Display the error in the terminal
|
|
1113
|
+
console.log("Calling handleResponse with error");
|
|
756
1114
|
handleResponse(errorResponse, callback, true);
|
|
757
|
-
|
|
1115
|
+
|
|
1116
|
+
// Also try using TerminalService directly as fallback
|
|
1117
|
+
console.log("Also trying TerminalService.emit");
|
|
1118
|
+
TerminalService.emit("response", {
|
|
1119
|
+
text: (
|
|
1120
|
+
<AiResponseMessage
|
|
1121
|
+
messages={[]}
|
|
1122
|
+
editOperations={[]}
|
|
1123
|
+
finished={true}
|
|
1124
|
+
error={errorMessage}
|
|
1125
|
+
/>
|
|
1126
|
+
),
|
|
1127
|
+
type: "response",
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
console.log("Error callback completed");
|
|
1131
|
+
setIsRunning(false);
|
|
1132
|
+
abortControllerRef.current = null;
|
|
1133
|
+
return; // Exit the stream processing
|
|
758
1134
|
|
|
759
1135
|
default:
|
|
760
1136
|
console.log(
|
|
@@ -777,24 +1153,53 @@ export function AiTerminal({
|
|
|
777
1153
|
setResponse(finalResponse);
|
|
778
1154
|
handleResponse(finalResponse, callback, true);
|
|
779
1155
|
}
|
|
1156
|
+
setIsRunning(false);
|
|
1157
|
+
abortControllerRef.current = null;
|
|
780
1158
|
} catch (streamError) {
|
|
781
1159
|
console.error("Stream connection error:", streamError);
|
|
782
|
-
const
|
|
783
|
-
streamError
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
1160
|
+
const isAbort =
|
|
1161
|
+
(streamError as any)?.name === "AbortError" ||
|
|
1162
|
+
(streamError as any)?.message?.toLowerCase?.().includes("abort");
|
|
1163
|
+
if (isAbort) {
|
|
1164
|
+
const cancelledResponse = {
|
|
1165
|
+
...accumulatedResponse,
|
|
1166
|
+
state: "cancelled",
|
|
1167
|
+
};
|
|
1168
|
+
setResponse(cancelledResponse);
|
|
1169
|
+
handleResponse(cancelledResponse, callback, true);
|
|
1170
|
+
} else {
|
|
1171
|
+
const errorMessage =
|
|
1172
|
+
streamError instanceof Error
|
|
1173
|
+
? streamError.message
|
|
1174
|
+
: "Unknown stream error";
|
|
1175
|
+
|
|
1176
|
+
const errorResponse = {
|
|
1177
|
+
...accumulatedResponse,
|
|
1178
|
+
state: "error",
|
|
1179
|
+
error: errorMessage,
|
|
1180
|
+
};
|
|
1181
|
+
setResponse(errorResponse);
|
|
1182
|
+
|
|
1183
|
+
// Display the error in the terminal
|
|
1184
|
+
callback(
|
|
1185
|
+
<AiResponseMessage
|
|
1186
|
+
messages={accumulatedResponse.messages || []}
|
|
1187
|
+
editOperations={accumulatedResponse.editOperations || []}
|
|
1188
|
+
finished={true}
|
|
1189
|
+
error={errorMessage}
|
|
1190
|
+
/>,
|
|
1191
|
+
true,
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
setIsRunning(false);
|
|
1195
|
+
abortControllerRef.current = null;
|
|
793
1196
|
}
|
|
794
1197
|
} catch (error) {
|
|
795
1198
|
console.error("Error starting agent:", error);
|
|
796
1199
|
const errorMessage =
|
|
797
1200
|
error instanceof Error ? error.message : "Unknown error";
|
|
1201
|
+
|
|
1202
|
+
// Display error in the terminal
|
|
798
1203
|
const errorResponse = {
|
|
799
1204
|
messages: [...messages],
|
|
800
1205
|
editOperations: [],
|
|
@@ -805,10 +1210,36 @@ export function AiTerminal({
|
|
|
805
1210
|
error: errorMessage,
|
|
806
1211
|
};
|
|
807
1212
|
setResponse(errorResponse);
|
|
808
|
-
|
|
1213
|
+
|
|
1214
|
+
// Show the error in the terminal
|
|
1215
|
+
callback(
|
|
1216
|
+
<AiResponseMessage
|
|
1217
|
+
messages={[]}
|
|
1218
|
+
editOperations={[]}
|
|
1219
|
+
finished={true}
|
|
1220
|
+
error={errorMessage}
|
|
1221
|
+
/>,
|
|
1222
|
+
true,
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
setIsRunning(false);
|
|
1226
|
+
abortControllerRef.current = null;
|
|
809
1227
|
}
|
|
810
1228
|
}
|
|
811
1229
|
|
|
1230
|
+
// Stop button handler to cancel running agent and abort stream
|
|
1231
|
+
const handleStop = async () => {
|
|
1232
|
+
try {
|
|
1233
|
+
if (!isRunning) return;
|
|
1234
|
+
const context = createAiContext({ editContext });
|
|
1235
|
+
await cancelAgent(agentId, context);
|
|
1236
|
+
} catch (e) {
|
|
1237
|
+
// Ignore cancel errors; still abort client-side stream
|
|
1238
|
+
} finally {
|
|
1239
|
+
abortControllerRef.current?.abort();
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
812
1243
|
useEffect(() => {
|
|
813
1244
|
TerminalService.on("command", commandHandler);
|
|
814
1245
|
|
|
@@ -832,6 +1263,7 @@ export function AiTerminal({
|
|
|
832
1263
|
setResponseMessages([]);
|
|
833
1264
|
setResponse(undefined);
|
|
834
1265
|
setInitialTerminalMessages(undefined);
|
|
1266
|
+
setTerminalError(null); // Clear any terminal errors
|
|
835
1267
|
// Generate a new agentId when clearing the terminal to start fresh
|
|
836
1268
|
setAgentId(crypto.randomUUID());
|
|
837
1269
|
}}
|
|
@@ -873,16 +1305,51 @@ export function AiTerminal({
|
|
|
873
1305
|
}
|
|
874
1306
|
prompt={prompt}
|
|
875
1307
|
setPrompt={setPrompt}
|
|
1308
|
+
submitOverride={
|
|
1309
|
+
isRunning ? (
|
|
1310
|
+
<SimpleIconButton
|
|
1311
|
+
icon={<Square size={16} strokeWidth={1} />}
|
|
1312
|
+
label="Stop"
|
|
1313
|
+
onClick={handleStop}
|
|
1314
|
+
disabled={!isRunning}
|
|
1315
|
+
/>
|
|
1316
|
+
) : undefined
|
|
1317
|
+
}
|
|
876
1318
|
statusbar=<div className="flex flex-1 items-center justify-between gap-1">
|
|
877
|
-
<
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1319
|
+
<Popover open={showPredefined} onOpenChange={setShowPredefined}>
|
|
1320
|
+
<Tooltip delayDuration={500}>
|
|
1321
|
+
<TooltipTrigger asChild>
|
|
1322
|
+
<PopoverTrigger asChild>
|
|
1323
|
+
<a className="ml-1 flex cursor-pointer items-center gap-1 text-xs">
|
|
1324
|
+
<WizardIcon className="text-theme-secondary h-5 w-5" />
|
|
1325
|
+
</a>
|
|
1326
|
+
</PopoverTrigger>
|
|
1327
|
+
</TooltipTrigger>
|
|
1328
|
+
<TooltipContent>Predefined prompts</TooltipContent>
|
|
1329
|
+
</Tooltip>
|
|
1330
|
+
<PopoverContent className="w-96 p-2" align="start" side="top">
|
|
1331
|
+
{activeProfile?.prompts?.length ? (
|
|
1332
|
+
<div className="flex max-h-64 flex-col gap-1 overflow-y-auto">
|
|
1333
|
+
{activeProfile.prompts.map((p, index) => (
|
|
1334
|
+
<div
|
|
1335
|
+
key={index}
|
|
1336
|
+
className="mb-1 cursor-pointer rounded-lg border border-gray-200 p-1.5 text-xs text-gray-700 hover:bg-gray-50"
|
|
1337
|
+
onClick={() => {
|
|
1338
|
+
setPrompt(p.prompt);
|
|
1339
|
+
setShowPredefined(false);
|
|
1340
|
+
}}
|
|
1341
|
+
>
|
|
1342
|
+
{p.title}
|
|
1343
|
+
</div>
|
|
1344
|
+
))}
|
|
1345
|
+
</div>
|
|
1346
|
+
) : (
|
|
1347
|
+
<div className="text-xs text-gray-500">
|
|
1348
|
+
No predefined prompts available
|
|
1349
|
+
</div>
|
|
1350
|
+
)}
|
|
1351
|
+
</PopoverContent>
|
|
1352
|
+
</Popover>
|
|
886
1353
|
{editContext.selection?.length > 0 && (
|
|
887
1354
|
<div className="mr-2 flex items-center text-xs text-red-400">
|
|
888
1355
|
{editContext.selection.length} items selected
|
|
@@ -895,23 +1362,6 @@ export function AiTerminal({
|
|
|
895
1362
|
/>
|
|
896
1363
|
</div>
|
|
897
1364
|
)}
|
|
898
|
-
{showPredefined && (
|
|
899
|
-
<div className="absolute right-0 bottom-8 left-0 flex flex-col gap-1 overflow-y-auto bg-white p-3 pb-1 text-sm">
|
|
900
|
-
{activeProfile &&
|
|
901
|
-
activeProfile.prompts.map((p, index) => (
|
|
902
|
-
<div
|
|
903
|
-
key={index}
|
|
904
|
-
className="mb-1 cursor-pointer rounded-lg border border-gray-200 p-1.5 text-xs text-gray-700"
|
|
905
|
-
onClick={() => {
|
|
906
|
-
setPrompt(p.prompt);
|
|
907
|
-
setShowPredefined(false);
|
|
908
|
-
}}
|
|
909
|
-
>
|
|
910
|
-
{p.title}
|
|
911
|
-
</div>
|
|
912
|
-
))}
|
|
913
|
-
</div>
|
|
914
|
-
)}
|
|
915
1365
|
</div>
|
|
916
1366
|
toolbar=<div className="flex items-stretch gap-1">
|
|
917
1367
|
<div className="relative" ref={settingsRef}>
|
|
@@ -926,27 +1376,49 @@ export function AiTerminal({
|
|
|
926
1376
|
<label className="text-xs font-medium text-gray-700">
|
|
927
1377
|
Profile
|
|
928
1378
|
</label>
|
|
929
|
-
<
|
|
930
|
-
className="text-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1379
|
+
<DropdownMenu>
|
|
1380
|
+
<DropdownMenuTrigger className="flex w-full items-center justify-between rounded-md border border-gray-300 bg-white px-3 py-2 text-xs hover:bg-gray-50 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none">
|
|
1381
|
+
<span>{activeProfile?.name || "Select profile"}</span>
|
|
1382
|
+
<ChevronDown size={16} strokeWidth={1} />
|
|
1383
|
+
</DropdownMenuTrigger>
|
|
1384
|
+
<DropdownMenuContent className="min-w-[200px]">
|
|
1385
|
+
{profiles.map((profile) => (
|
|
1386
|
+
<DropdownMenuItem
|
|
1387
|
+
key={profile.id}
|
|
1388
|
+
onClick={() => setActiveProfile(profile)}
|
|
1389
|
+
className="cursor-pointer"
|
|
1390
|
+
>
|
|
1391
|
+
{profile.name}
|
|
1392
|
+
</DropdownMenuItem>
|
|
1393
|
+
))}
|
|
1394
|
+
</DropdownMenuContent>
|
|
1395
|
+
</DropdownMenu>
|
|
936
1396
|
</div>
|
|
937
1397
|
{activeProfile && (
|
|
938
1398
|
<div className="flex flex-col gap-1">
|
|
939
1399
|
<label className="text-xs font-medium text-gray-700">
|
|
940
1400
|
Model
|
|
941
1401
|
</label>
|
|
942
|
-
<
|
|
943
|
-
className="text-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1402
|
+
<DropdownMenu>
|
|
1403
|
+
<DropdownMenuTrigger className="flex w-full items-center justify-between rounded-md border border-gray-300 bg-white px-3 py-2 text-xs hover:bg-gray-50 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none">
|
|
1404
|
+
<span>
|
|
1405
|
+
{activeProfile.models.find((m) => m.id === model)
|
|
1406
|
+
?.name || "Select model"}
|
|
1407
|
+
</span>
|
|
1408
|
+
<ChevronDown size={16} strokeWidth={1} />
|
|
1409
|
+
</DropdownMenuTrigger>
|
|
1410
|
+
<DropdownMenuContent className="min-w-[200px]">
|
|
1411
|
+
{activeProfile.models.map((modelOption) => (
|
|
1412
|
+
<DropdownMenuItem
|
|
1413
|
+
key={modelOption.id}
|
|
1414
|
+
onClick={() => setModel(modelOption.id)}
|
|
1415
|
+
className="cursor-pointer"
|
|
1416
|
+
>
|
|
1417
|
+
{modelOption.name}
|
|
1418
|
+
</DropdownMenuItem>
|
|
1419
|
+
))}
|
|
1420
|
+
</DropdownMenuContent>
|
|
1421
|
+
</DropdownMenu>
|
|
950
1422
|
</div>
|
|
951
1423
|
)}
|
|
952
1424
|
</div>
|
|
@@ -959,6 +1431,32 @@ export function AiTerminal({
|
|
|
959
1431
|
}}
|
|
960
1432
|
/>
|
|
961
1433
|
</div>
|
|
1434
|
+
{/* Display terminal errors prominently */}
|
|
1435
|
+
{terminalError && (
|
|
1436
|
+
<div className="bg-opacity-95 absolute inset-0 z-10 flex items-center justify-center bg-white">
|
|
1437
|
+
<div className="mx-4 max-w-md rounded-lg border-l-4 border-red-500 bg-red-50 p-4 shadow-md">
|
|
1438
|
+
<div className="flex items-start">
|
|
1439
|
+
<div className="flex-shrink-0">
|
|
1440
|
+
<div className="h-5 w-5 text-red-400">⚠️</div>
|
|
1441
|
+
</div>
|
|
1442
|
+
<div className="ml-3 flex-1">
|
|
1443
|
+
<h3 className="text-sm font-medium text-red-800">
|
|
1444
|
+
Agent Error
|
|
1445
|
+
</h3>
|
|
1446
|
+
<p className="mt-1 text-sm text-red-700">{terminalError}</p>
|
|
1447
|
+
<div className="mt-3">
|
|
1448
|
+
<button
|
|
1449
|
+
onClick={() => setTerminalError(null)}
|
|
1450
|
+
className="rounded bg-red-100 px-3 py-1 text-xs font-medium text-red-800 hover:bg-red-200"
|
|
1451
|
+
>
|
|
1452
|
+
Dismiss
|
|
1453
|
+
</button>
|
|
1454
|
+
</div>
|
|
1455
|
+
</div>
|
|
1456
|
+
</div>
|
|
1457
|
+
</div>
|
|
1458
|
+
</div>
|
|
1459
|
+
)}
|
|
962
1460
|
{activeProfile?.errorMessage && (
|
|
963
1461
|
<div className="absolute inset-0 grid items-center justify-center p-2 text-sm text-red-500">
|
|
964
1462
|
{activeProfile?.errorMessage}
|