@alpaca-editor/core 1.0.4049 → 1.0.4053

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.
Files changed (69) hide show
  1. package/dist/components/ui/textarea.js +1 -1
  2. package/dist/components/ui/textarea.js.map +1 -1
  3. package/dist/editor/Terminal.js +3 -3
  4. package/dist/editor/Terminal.js.map +1 -1
  5. package/dist/editor/ai/AgentCostDisplay.js +2 -2
  6. package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
  7. package/dist/editor/ai/AgentHistory.d.ts +4 -4
  8. package/dist/editor/ai/AgentHistory.js +1 -1
  9. package/dist/editor/ai/AgentHistory.js.map +1 -1
  10. package/dist/editor/ai/AgentTerminal.d.ts +4 -0
  11. package/dist/editor/ai/AgentTerminal.js +753 -0
  12. package/dist/editor/ai/AgentTerminal.js.map +1 -0
  13. package/dist/editor/ai/Agents.d.ts +1 -3
  14. package/dist/editor/ai/Agents.js +213 -353
  15. package/dist/editor/ai/Agents.js.map +1 -1
  16. package/dist/editor/ai/AiPromptPopover.js +2 -2
  17. package/dist/editor/ai/AiPromptPopover.js.map +1 -1
  18. package/dist/editor/ai/AiResponseMessage.d.ts +0 -1
  19. package/dist/editor/ai/AiResponseMessage.js +23 -143
  20. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  21. package/dist/editor/ai/AiTerminal.d.ts +5 -23
  22. package/dist/editor/ai/AiTerminal.js +81 -824
  23. package/dist/editor/ai/AiTerminal.js.map +1 -1
  24. package/dist/editor/ai/DancingDots.d.ts +1 -0
  25. package/dist/editor/ai/DancingDots.js +6 -0
  26. package/dist/editor/ai/DancingDots.js.map +1 -0
  27. package/dist/editor/ai/ToolCallDisplay.d.ts +37 -0
  28. package/dist/editor/ai/ToolCallDisplay.js +154 -0
  29. package/dist/editor/ai/ToolCallDisplay.js.map +1 -0
  30. package/dist/editor/client/EditorClient.js +5 -1
  31. package/dist/editor/client/EditorClient.js.map +1 -1
  32. package/dist/editor/services/agentService.d.ts +23 -30
  33. package/dist/editor/services/agentService.js +62 -124
  34. package/dist/editor/services/agentService.js.map +1 -1
  35. package/dist/editor/sidebar/GraphQL.js +1 -0
  36. package/dist/editor/sidebar/GraphQL.js.map +1 -1
  37. package/dist/editor/sidebar/ViewSelector.js +8 -6
  38. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  39. package/dist/editor/ui/Section.js +4 -3
  40. package/dist/editor/ui/Section.js.map +1 -1
  41. package/dist/editor/utils.d.ts +4 -0
  42. package/dist/editor/utils.js +23 -0
  43. package/dist/editor/utils.js.map +1 -1
  44. package/dist/revision.d.ts +2 -2
  45. package/dist/revision.js +2 -2
  46. package/dist/styles.css +18 -33
  47. package/package.json +1 -1
  48. package/src/components/ui/textarea.tsx +1 -1
  49. package/src/editor/Terminal.tsx +4 -4
  50. package/src/editor/ai/AgentCostDisplay.tsx +7 -11
  51. package/src/editor/ai/AgentHistory.tsx +7 -9
  52. package/src/editor/ai/AgentTerminal.tsx +1094 -0
  53. package/src/editor/ai/Agents.tsx +340 -477
  54. package/src/editor/ai/AiPromptPopover.tsx +2 -2
  55. package/src/editor/ai/AiResponseMessage.tsx +85 -366
  56. package/src/editor/ai/AiTerminal.tsx +142 -1213
  57. package/src/editor/ai/DancingDots.tsx +14 -0
  58. package/src/editor/ai/ToolCallDisplay.tsx +363 -0
  59. package/src/editor/client/EditorClient.tsx +6 -1
  60. package/src/editor/services/agentService.ts +89 -162
  61. package/src/editor/sidebar/GraphQL.tsx +1 -0
  62. package/src/editor/sidebar/ViewSelector.tsx +82 -57
  63. package/src/editor/ui/Section.tsx +4 -3
  64. package/src/editor/utils.ts +29 -0
  65. package/src/revision.ts +2 -2
  66. package/dist/editor/ai/EditorAiTerminal.d.ts +0 -6
  67. package/dist/editor/ai/EditorAiTerminal.js +0 -7
  68. package/dist/editor/ai/EditorAiTerminal.js.map +0 -1
  69. package/src/editor/ai/EditorAiTerminal.tsx +0 -23
@@ -4,27 +4,13 @@ import { useEffect, useRef, useState } from "react";
4
4
  import { TerminalService } from "primereact/terminalservice";
5
5
  import { Terminal } from "../Terminal";
6
6
  import { useEditContext } from "../client/editContext";
7
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../../components/ui/dropdown-menu";
8
- import { ChevronDown } from "lucide-react";
7
+ import { Dropdown } from "primereact/dropdown";
9
8
  import { WizardIcon } from "../ui/Icons";
10
9
  import { AiResponseMessage } from "./AiResponseMessage";
11
- import { loadAiProfiles } from "../services/aiService";
12
- import { startAgent, connectToAgentStream, cancelAgent, checkAgentState, convertAgentMessagesToTerminalFormat, AgentStreamMessageType, } from "../services/agentService";
10
+ import { loadAiProfiles, executePrompt, } from "../services/aiService";
13
11
  import { SimpleIconButton } from "../ui/SimpleIconButton";
14
- import { Settings, Square } from "lucide-react";
15
- import { AgentCostDisplay } from "./AgentCostDisplay";
16
- import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
17
- import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
18
- // Helper function to format message content
19
- function formatMessageContent(content) {
20
- return (content
21
- ?.trim()
22
- ?.replaceAll("\n", "<br>")
23
- ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") // Bold markdown
24
- ?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" class="text-blue-500" rel="noopener noreferrer">$1</a>') || "" // Links
25
- );
26
- }
27
- export function AiTerminal({ closeButton, createAiContext, defaultProfile, options, onAgentNameUpdate, }) {
12
+ import { Settings } from "lucide-react";
13
+ export function AiTerminal({ closeButton, createAiContext, defaultProfile, options, }) {
28
14
  const editContext = useEditContext();
29
15
  const [showPredefined, setShowPredefined] = useState(false);
30
16
  if (!editContext)
@@ -36,25 +22,12 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
36
22
  const [profiles, setProfiles] = useState([]);
37
23
  const [activeProfile, setActiveProfile] = useState();
38
24
  const [initialPromptExecuted, setInitialPromptExecuted] = useState(false);
39
- const [agentId, setAgentId] = useState(() => options?.agentId || crypto.randomUUID());
25
+ const [agentId] = useState(() => crypto.randomUUID());
40
26
  const selection = editContext.selection;
41
27
  const terminalRef = useRef(null);
42
28
  const [responseMessages, setResponseMessages] = useState(options?.initialMessages || []);
43
29
  const [showSettings, setShowSettings] = useState(false);
44
30
  const settingsRef = useRef(null);
45
- const [isRunning, setIsRunning] = useState(false);
46
- const abortControllerRef = useRef(null);
47
- const [terminalError, setTerminalError] = useState(null);
48
- // Debug function - expose globally for testing
49
- useEffect(() => {
50
- window.testAiTerminalError = (errorMsg = "Test error message") => {
51
- console.log("Manually triggering error:", errorMsg);
52
- setTerminalError(errorMsg);
53
- };
54
- return () => {
55
- delete window.testAiTerminalError;
56
- };
57
- }, []);
58
31
  useEffect(() => {
59
32
  if (options?.initialPrompt && !initialPromptExecuted && model) {
60
33
  // Set the initial prompt text into the terminal's state.
@@ -78,293 +51,6 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
78
51
  }
79
52
  fetchProfiles();
80
53
  }, [editContext?.currentItemDescriptor]);
81
- // Agent reconnection logic
82
- useEffect(() => {
83
- async function checkAndReconnectAgent() {
84
- if (!editContext || !editContext.sessionId || !agentId) {
85
- return;
86
- }
87
- const context = createAiContext({ editContext });
88
- try {
89
- const agentState = await checkAgentState(agentId, context);
90
- if (agentState.exists && agentState.agent) {
91
- const agent = agentState.agent;
92
- // Convert and set persisted messages if not already set via options
93
- if (agent.messages &&
94
- (!options?.initialMessages || options.initialMessages.length === 0)) {
95
- const terminalMessages = convertAgentMessagesToTerminalFormat(agent.messages);
96
- setMessages(terminalMessages);
97
- setResponseMessages(terminalMessages);
98
- }
99
- else {
100
- }
101
- // Set cost information if available
102
- if (agent.totalCost !== undefined) {
103
- setResponse((prev) => ({
104
- ...prev,
105
- totalCost: agent.totalCost,
106
- totalInputTokenCost: agent.totalInputTokenCost,
107
- totalOutputTokenCost: agent.totalOutputTokenCost,
108
- totalCachedTokenCost: agent.totalCachedInputTokenCost,
109
- totalInputTokens: agent.totalInputTokens,
110
- totalOutputTokens: agent.totalOutputTokens,
111
- totalCachedTokens: agent.totalCachedInputTokens,
112
- }));
113
- }
114
- // If agent is still running, reconnect to the stream
115
- if (agentState.isRunning) {
116
- setIsRunning(true);
117
- await reconnectToRunningAgent(agentId, context);
118
- }
119
- else {
120
- }
121
- }
122
- else {
123
- }
124
- }
125
- catch (error) {
126
- setTerminalError(`Reconnection failed: ${error instanceof Error ? error.message : "Unknown error"}`);
127
- }
128
- }
129
- // Only try to reconnect if we have an agentId and edit context
130
- if (agentId && editContext && editContext.sessionId) {
131
- checkAndReconnectAgent();
132
- }
133
- else {
134
- }
135
- }, [agentId, editContext?.sessionId]); // Run when agentId or session becomes available
136
- // Function to reconnect to a running agent stream
137
- async function reconnectToRunningAgent(agentId, context) {
138
- try {
139
- const abortController = new AbortController();
140
- abortControllerRef.current = abortController;
141
- setIsRunning(true);
142
- let accumulatedResponse = {
143
- messages: [...messages],
144
- editOperations: [],
145
- numInputTokens: 0,
146
- numOutputTokens: 0,
147
- numCachedTokens: 0,
148
- state: "running",
149
- };
150
- let completionProcessed = false;
151
- await connectToAgentStream(agentId, context, (streamMessage) => {
152
- if (!streamMessage || !streamMessage.type) {
153
- return;
154
- }
155
- // Reuse stream message handling, but pipe updates into Terminal via TerminalService
156
- try {
157
- const terminalCallback = (text, finished) => {
158
- TerminalService.emit("response", { text, finished });
159
- };
160
- const result = handleStreamMessage(streamMessage, accumulatedResponse, completionProcessed, context, terminalCallback);
161
- if (result) {
162
- accumulatedResponse = result.accumulatedResponse;
163
- completionProcessed = result.completionProcessed;
164
- }
165
- }
166
- catch (error) { }
167
- }, abortController.signal);
168
- setIsRunning(false);
169
- }
170
- catch (error) {
171
- setIsRunning(false);
172
- // Check if it's an abort error (user cancelled)
173
- const isAbortError = error instanceof Error &&
174
- (error.name === "AbortError" ||
175
- error.message.toLowerCase().includes("abort"));
176
- if (!isAbortError) {
177
- const errorMessage = `Failed to reconnect to agent: ${error instanceof Error ? error.message : "Unknown error"}`;
178
- setTerminalError(errorMessage);
179
- }
180
- else {
181
- }
182
- }
183
- }
184
- // Extract stream message handling logic into a separate function for reuse
185
- function handleStreamMessage(streamMessage, accumulatedResponse, completionProcessed, context, terminalCallback) {
186
- // Safety check for valid stream message
187
- if (!streamMessage || !streamMessage.type) {
188
- console.error("Invalid stream message received:", streamMessage);
189
- return;
190
- }
191
- console.log("Processing stream message:", streamMessage.type, streamMessage);
192
- // Handle different message types (this logic was already in the component)
193
- switch (streamMessage.type) {
194
- case AgentStreamMessageType.StatusUpdate:
195
- break;
196
- case AgentStreamMessageType.ContentChunk:
197
- if (streamMessage.data && typeof streamMessage.data === "object") {
198
- if (streamMessage.data.isIncremental &&
199
- streamMessage.data.deltaContent) {
200
- // Handle incremental content updates
201
- const updatedMessages = [...(accumulatedResponse.messages || [])];
202
- let lastAssistantMessage = null;
203
- let lastAssistantIndex = -1;
204
- // Find the last assistant message
205
- for (let i = updatedMessages.length - 1; i >= 0; i--) {
206
- if (updatedMessages[i].role === "assistant") {
207
- lastAssistantMessage = updatedMessages[i];
208
- lastAssistantIndex = i;
209
- break;
210
- }
211
- }
212
- // If no assistant message exists, or the last one has tool calls, create a new one
213
- if (!lastAssistantMessage ||
214
- (lastAssistantMessage.tool_calls &&
215
- lastAssistantMessage.tool_calls.length > 0)) {
216
- lastAssistantMessage = {
217
- id: crypto.randomUUID(),
218
- role: "assistant",
219
- name: "assistant",
220
- content: "",
221
- tool_calls: [],
222
- };
223
- updatedMessages.push(lastAssistantMessage);
224
- lastAssistantIndex = updatedMessages.length - 1;
225
- }
226
- // Append the delta content to build up the full message
227
- const currentContent = lastAssistantMessage.content || "";
228
- const newContent = currentContent + streamMessage.data.deltaContent;
229
- // Update the message with accumulated content
230
- updatedMessages[lastAssistantIndex] = {
231
- ...lastAssistantMessage,
232
- content: newContent,
233
- };
234
- // Update accumulated response with incremental content
235
- const newAccumulated = {
236
- ...accumulatedResponse,
237
- ...streamMessage.data,
238
- messages: updatedMessages,
239
- editOperations: streamMessage.data.editOperations ||
240
- accumulatedResponse.editOperations,
241
- };
242
- accumulatedResponse = newAccumulated;
243
- }
244
- else {
245
- // Non-incremental update - use as provided
246
- const newAccumulated = {
247
- ...accumulatedResponse,
248
- ...streamMessage.data,
249
- editOperations: streamMessage.data.editOperations ||
250
- accumulatedResponse.editOperations,
251
- };
252
- accumulatedResponse = newAccumulated;
253
- }
254
- // Always trigger UI update for content chunks
255
- handleResponse(accumulatedResponse, terminalCallback, false);
256
- }
257
- return { accumulatedResponse, completionProcessed };
258
- case AgentStreamMessageType.ToolCall:
259
- if (streamMessage.data && typeof streamMessage.data === "object") {
260
- // Find or create the assistant message that contains this tool call
261
- const updatedMessages = [...(accumulatedResponse.messages || [])];
262
- let lastAssistantMessage = null;
263
- let lastAssistantIndex = -1;
264
- for (let i = updatedMessages.length - 1; i >= 0; i--) {
265
- if (updatedMessages[i].role === "assistant") {
266
- lastAssistantMessage = updatedMessages[i];
267
- lastAssistantIndex = i;
268
- break;
269
- }
270
- }
271
- // If no assistant message exists, create one
272
- if (!lastAssistantMessage) {
273
- lastAssistantMessage = {
274
- id: crypto.randomUUID(),
275
- role: "assistant",
276
- name: "assistant",
277
- content: "",
278
- tool_calls: [],
279
- };
280
- updatedMessages.push(lastAssistantMessage);
281
- lastAssistantIndex = updatedMessages.length - 1;
282
- }
283
- const toolCall = {
284
- id: streamMessage.data.id,
285
- displayName: streamMessage.data.displayName,
286
- function: {
287
- name: streamMessage.data.function?.name || streamMessage.data.name,
288
- arguments: streamMessage.data.function?.arguments ||
289
- streamMessage.data.arguments,
290
- },
291
- };
292
- const existingToolCalls = lastAssistantMessage.tool_calls || [];
293
- const existingIndex = existingToolCalls.findIndex((tc) => tc.id === toolCall.id);
294
- let updatedToolCalls;
295
- if (existingIndex >= 0) {
296
- updatedToolCalls = [...existingToolCalls];
297
- updatedToolCalls[existingIndex] = toolCall;
298
- }
299
- else {
300
- updatedToolCalls = [...existingToolCalls, toolCall];
301
- }
302
- updatedMessages[lastAssistantIndex] = {
303
- ...lastAssistantMessage,
304
- tool_calls: updatedToolCalls,
305
- };
306
- const newAccumulated = {
307
- ...accumulatedResponse,
308
- messages: updatedMessages,
309
- };
310
- accumulatedResponse = newAccumulated;
311
- handleResponse(accumulatedResponse, terminalCallback, false);
312
- }
313
- return { accumulatedResponse, completionProcessed };
314
- case AgentStreamMessageType.ToolResult:
315
- if (streamMessage.data && typeof streamMessage.data === "object") {
316
- const updatedMessages = [...(accumulatedResponse.messages || [])];
317
- // Find the assistant message with the matching tool call ID
318
- for (let i = updatedMessages.length - 1; i >= 0; i--) {
319
- if (updatedMessages[i].role === "assistant" &&
320
- updatedMessages[i].tool_calls) {
321
- const toolCalls = updatedMessages[i].tool_calls || [];
322
- const toolCallIndex = toolCalls.findIndex((tc) => tc.id === streamMessage.data.toolCallId);
323
- if (toolCallIndex >= 0) {
324
- const updatedToolCalls = [...toolCalls];
325
- updatedToolCalls[toolCallIndex] = {
326
- ...updatedToolCalls[toolCallIndex],
327
- function: {
328
- ...updatedToolCalls[toolCallIndex].function,
329
- result: streamMessage.data.result,
330
- error: streamMessage.data.error,
331
- },
332
- };
333
- updatedMessages[i] = {
334
- ...updatedMessages[i],
335
- tool_calls: updatedToolCalls,
336
- };
337
- break;
338
- }
339
- }
340
- }
341
- const newAccumulated = {
342
- ...accumulatedResponse,
343
- messages: updatedMessages,
344
- };
345
- accumulatedResponse = newAccumulated;
346
- handleResponse(accumulatedResponse, terminalCallback, false);
347
- }
348
- return { accumulatedResponse, completionProcessed };
349
- case AgentStreamMessageType.Completed:
350
- if (!completionProcessed) {
351
- completionProcessed = true;
352
- console.log("Agent execution completed");
353
- setIsRunning(false);
354
- if (streamMessage.data) {
355
- handleResponse(streamMessage.data, terminalCallback, true);
356
- }
357
- }
358
- return { accumulatedResponse, completionProcessed };
359
- case AgentStreamMessageType.Error:
360
- console.error("Agent execution error:", streamMessage);
361
- setIsRunning(false);
362
- setTerminalError(streamMessage.error || "Agent execution failed");
363
- return { accumulatedResponse, completionProcessed };
364
- default:
365
- console.warn("Unknown stream message type:", streamMessage.type);
366
- }
367
- }
368
54
  useEffect(() => {
369
55
  if (activeProfile?.defaultModelId)
370
56
  setModel(activeProfile.defaultModelId);
@@ -380,7 +66,10 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
380
66
  console.log("AiTerminal: Loading initial messages", options.initialMessages);
381
67
  // Format the initial messages for display
382
68
  const formattedMessages = options.initialMessages.map((message) => {
383
- const formattedContent = formatMessageContent(message.content || "");
69
+ const formattedContent = message.content
70
+ ?.trim()
71
+ ?.replaceAll("\n", "<br>")
72
+ ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
384
73
  return {
385
74
  ...message,
386
75
  content: message.content || "",
@@ -388,6 +77,7 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
388
77
  tool_calls: message.tool_calls || [],
389
78
  };
390
79
  });
80
+ console.log("AiTerminal: Formatted messages", formattedMessages);
391
81
  // Update the internal message states
392
82
  setMessages(formattedMessages);
393
83
  setResponseMessages(formattedMessages);
@@ -395,101 +85,57 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
395
85
  const initialResponse = {
396
86
  messages: formattedMessages,
397
87
  editOperations: [],
398
- numInputTokens: options?.totalInputTokens || 0,
399
- numOutputTokens: options?.totalOutputTokens || 0,
400
- numCachedTokens: options?.totalCachedTokens || 0,
88
+ numInputTokens: 0,
89
+ numOutputTokens: 0,
90
+ numCachedTokens: 0,
401
91
  state: "loaded",
402
- // Include cost information from loaded agent session
403
- totalCost: options?.totalCost,
404
- totalInputTokenCost: options?.totalInputTokenCost,
405
- totalOutputTokenCost: options?.totalOutputTokenCost,
406
- totalCachedTokenCost: options?.totalCachedTokenCost,
407
- totalInputTokens: options?.totalInputTokens,
408
- totalOutputTokens: options?.totalOutputTokens,
409
- totalCachedTokens: options?.totalCachedTokens,
410
92
  };
93
+ console.log("AiTerminal: Setting response", initialResponse);
411
94
  setResponse(initialResponse);
412
95
  // Create individual Terminal messages for each conversation message
413
96
  const terminalMessages = [];
414
- // Group messages by conversation turns (user + assistant pairs)
415
- let i = 0;
416
- while (i < formattedMessages.length) {
417
- const message = formattedMessages[i];
418
- if (!message) {
419
- i++;
420
- continue;
421
- }
97
+ formattedMessages.forEach((message) => {
422
98
  if (message.role === "user") {
423
99
  // User messages appear as commands
424
100
  terminalMessages.push({
425
101
  type: "command",
426
102
  text: message.content,
427
103
  });
428
- i++;
429
- // Look for following assistant messages to group together
430
- const assistantMessages = [];
431
- while (i < formattedMessages.length) {
432
- const assistantMessage = formattedMessages[i];
433
- if (assistantMessage && assistantMessage.role === "assistant") {
434
- assistantMessages.push(assistantMessage);
435
- i++;
436
- }
437
- else {
438
- break;
439
- }
440
- }
441
- // If we have assistant messages, render them with AiResponseMessage
442
- if (assistantMessages.length > 0) {
443
- terminalMessages.push({
444
- type: "response",
445
- text: (_jsx(AiResponseMessage, { messages: assistantMessages, editOperations: [], finished: true })),
446
- });
447
- }
448
104
  }
449
105
  else if (message.role === "assistant") {
450
- // Handle standalone assistant messages
106
+ // Assistant messages appear as responses
451
107
  terminalMessages.push({
452
108
  type: "response",
453
- text: (_jsx(AiResponseMessage, { messages: [message], editOperations: [], finished: true })),
109
+ text: (_jsx("div", { dangerouslySetInnerHTML: {
110
+ __html: message.formattedContent || message.content || "",
111
+ } })),
454
112
  });
455
- i++;
456
113
  }
457
- else {
458
- // Skip other message types (like tool messages)
459
- i++;
114
+ // Add tool calls if present
115
+ if (message.tool_calls && message.tool_calls.length > 0) {
116
+ message.tool_calls.forEach((toolCall) => {
117
+ terminalMessages.push({
118
+ type: "response",
119
+ text: (_jsxs("div", { className: "text-gray-1 text-xs", children: ["\uD83D\uDD27 ", toolCall.displayName] })),
120
+ });
121
+ });
460
122
  }
461
- }
123
+ });
462
124
  setInitialTerminalMessages(terminalMessages);
463
125
  }
464
126
  }, [options?.initialMessages]);
465
127
  // Handle click outside for settings popover
466
128
  useEffect(() => {
467
129
  function handleClickOutside(event) {
468
- const target = event.target;
469
- // Check if the click is inside the settings popover
470
- if (settingsRef.current && settingsRef.current.contains(target)) {
471
- return;
472
- }
473
- // Check if the click is inside any dropdown menu content
474
- // Radix UI dropdown menus can have various selectors
475
- const isInDropdown = target.closest([
476
- "[data-radix-popper-content-wrapper]",
477
- '[data-slot="dropdown-menu-content"]',
478
- "[data-radix-dropdown-menu-content]",
479
- '[role="menu"]',
480
- ".dropdown-menu", // fallback for any custom dropdown classes
481
- ].join(", "));
482
- if (isInDropdown) {
483
- return;
130
+ if (settingsRef.current &&
131
+ !settingsRef.current.contains(event.target)) {
132
+ setShowSettings(false);
484
133
  }
485
- // If we get here, it's a click outside both the settings and any dropdown
486
- setShowSettings(false);
487
134
  }
488
135
  if (showSettings) {
489
- // Use mousedown instead of click for better timing
490
- document.addEventListener("mousedown", handleClickOutside);
136
+ document.addEventListener("click", handleClickOutside);
491
137
  return () => {
492
- document.removeEventListener("mousedown", handleClickOutside);
138
+ document.removeEventListener("click", handleClickOutside);
493
139
  };
494
140
  }
495
141
  }, [showSettings]);
@@ -498,7 +144,10 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
498
144
  // Replace the conversation history with the authoritative response from AI
499
145
  if (updatedMessages && Array.isArray(updatedMessages)) {
500
146
  const formattedMessages = updatedMessages.map((message) => {
501
- const formattedContent = formatMessageContent(message.content || "");
147
+ const formattedContent = message.content
148
+ ?.trim()
149
+ ?.replaceAll("\n", "<br>")
150
+ ?.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>") || "";
502
151
  return {
503
152
  ...message,
504
153
  content: message.content || "",
@@ -509,11 +158,11 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
509
158
  // Update the messages state with the complete conversation from AI
510
159
  setMessages([...formattedMessages]);
511
160
  setResponseMessages([...formattedMessages]);
512
- terminalCallback(_jsx(AiResponseMessage, { messages: formattedMessages, editOperations: response.editOperations, finished: isFinished, error: response.error }), isFinished);
161
+ terminalCallback(_jsx(AiResponseMessage, { messages: formattedMessages, editOperations: response.editOperations, finished: isFinished }), isFinished);
513
162
  }
514
163
  else {
515
164
  // Fallback: if no messages in response, keep current state
516
- terminalCallback(_jsx(AiResponseMessage, { messages: messagesRef.current, editOperations: response.editOperations, finished: isFinished, error: response.error }), isFinished);
165
+ terminalCallback(_jsx(AiResponseMessage, { messages: messagesRef.current, editOperations: response.editOperations, finished: isFinished }), isFinished);
517
166
  }
518
167
  }
519
168
  async function commandHandler(text, callback) {
@@ -524,11 +173,13 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
524
173
  name: "user",
525
174
  tool_calls: [], // Add empty toolCalls array
526
175
  };
527
- const context = createAiContext({ editContext });
176
+ const context = createAiContext({ editContext: editContext });
177
+ let lastOpIndex = 0;
528
178
  const selectedText = editContext?.selectedRange?.text || null;
529
179
  if (!activeProfile || !model)
530
180
  return;
531
- // Only send the new user message - message history will be loaded from database
181
+ // Build complete message history for API call
182
+ const conversationHistory = [...messagesRef.current, userMessage];
532
183
  const messages = [
533
184
  ...(options?.hiddenSystemPrompt
534
185
  ? [
@@ -541,408 +192,35 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
541
192
  },
542
193
  ]
543
194
  : []),
544
- userMessage, // Send only the new user message
195
+ ...conversationHistory,
545
196
  ];
546
- // Update local state to include the new user message for UI display
547
- setMessages((prevMessages) => [...prevMessages, userMessage]);
548
- setResponseMessages((prevMessages) => [...prevMessages, userMessage]);
549
- try {
550
- // Step 1: Start the agent execution
551
- const startRequest = {
552
- agentId,
553
- messages,
554
- sessionId: editContext.sessionId,
555
- profileId: activeProfile.id,
556
- model,
557
- itemid: context.promptData.itemid,
558
- language: context.promptData.language,
559
- version: context.promptData.version,
560
- selection,
561
- selectedText: selectedText || undefined,
562
- allowedFunctions: context.promptData.allowedFunctions,
563
- addContextContent: context.promptData.addContextContent,
564
- addAllContent: context.promptData.addAllContent,
565
- addSelectedComponents: true,
566
- profile: activeProfile.name,
567
- };
568
- const startResponse = await startAgent(startRequest, context);
569
- // Step 2: Connect to the agent stream for real-time updates
570
- const abortController = new AbortController();
571
- abortControllerRef.current = abortController;
572
- setIsRunning(true);
573
- let accumulatedResponse = {
574
- messages: [...messages],
575
- editOperations: [],
576
- numInputTokens: 0,
577
- numOutputTokens: 0,
578
- numCachedTokens: 0,
579
- state: "running",
580
- };
581
- let completionProcessed = false;
582
- try {
583
- console.log("Connecting to agent stream");
584
- await connectToAgentStream(agentId, context, (streamMessage) => {
585
- // console.log(
586
- // "Stream message:",
587
- // streamMessage,
588
- // new Date().toISOString(),
589
- // );
590
- // Safety check for valid stream message
591
- if (!streamMessage || !streamMessage.type) {
592
- console.error("Invalid stream message received:", streamMessage);
593
- return;
594
- }
595
- console.log("Processing stream message:", streamMessage.type, streamMessage);
596
- // Message types are now normalized in agentService.ts
597
- switch (streamMessage.type) {
598
- case AgentStreamMessageType.StatusUpdate:
599
- //console.log("Status update:", streamMessage.data);
600
- break;
601
- case AgentStreamMessageType.ContentChunk:
602
- if (streamMessage.data &&
603
- typeof streamMessage.data === "object") {
604
- // Handle incremental content updates
605
- if (streamMessage.data.isIncremental &&
606
- streamMessage.data.deltaContent) {
607
- // Find the last assistant message to append delta content to
608
- const updatedMessages = [
609
- ...(accumulatedResponse.messages || []),
610
- ];
611
- let lastAssistantMessage = null;
612
- let lastAssistantIndex = -1;
613
- // Find the last assistant message
614
- for (let i = updatedMessages.length - 1; i >= 0; i--) {
615
- if (updatedMessages[i].role === "assistant") {
616
- lastAssistantMessage = updatedMessages[i];
617
- lastAssistantIndex = i;
618
- break;
619
- }
620
- }
621
- // If no assistant message exists, or the last one has tool calls, create a new one
622
- if (!lastAssistantMessage ||
623
- (lastAssistantMessage.tool_calls &&
624
- lastAssistantMessage.tool_calls.length > 0)) {
625
- lastAssistantMessage = {
626
- id: crypto.randomUUID(),
627
- role: "assistant",
628
- name: "assistant",
629
- content: "",
630
- tool_calls: [],
631
- };
632
- updatedMessages.push(lastAssistantMessage);
633
- lastAssistantIndex = updatedMessages.length - 1;
634
- }
635
- // Append the delta content to build up the full message
636
- const currentContent = lastAssistantMessage.content || "";
637
- const newContent = currentContent + streamMessage.data.deltaContent;
638
- // Update the message with accumulated content
639
- updatedMessages[lastAssistantIndex] = {
640
- ...lastAssistantMessage,
641
- content: newContent,
642
- };
643
- // Update accumulated response with incremental content
644
- accumulatedResponse = {
645
- ...accumulatedResponse,
646
- ...streamMessage.data,
647
- messages: updatedMessages,
648
- editOperations: streamMessage.data.editOperations ||
649
- accumulatedResponse.editOperations,
650
- };
651
- }
652
- else {
653
- // Non-incremental update - use as provided
654
- accumulatedResponse = {
655
- ...accumulatedResponse,
656
- ...streamMessage.data,
657
- editOperations: streamMessage.data.editOperations ||
658
- accumulatedResponse.editOperations,
659
- };
660
- }
661
- // Always trigger UI update for content chunks
662
- handleResponse(accumulatedResponse, callback, false);
663
- }
664
- break;
665
- case AgentStreamMessageType.ToolCall:
666
- // console.log("Tool call received:", streamMessage.data);
667
- if (streamMessage.data &&
668
- typeof streamMessage.data === "object") {
669
- // Find or create the assistant message that contains this tool call
670
- const updatedMessages = [
671
- ...(accumulatedResponse.messages || []),
672
- ];
673
- let lastAssistantMessage = null;
674
- let lastAssistantIndex = -1;
675
- // Find the last assistant message
676
- for (let i = updatedMessages.length - 1; i >= 0; i--) {
677
- if (updatedMessages[i].role === "assistant") {
678
- lastAssistantMessage = updatedMessages[i];
679
- lastAssistantIndex = i;
680
- break;
681
- }
682
- }
683
- // If no assistant message exists, create one
684
- if (!lastAssistantMessage) {
685
- lastAssistantMessage = {
686
- id: crypto.randomUUID(),
687
- role: "assistant",
688
- name: "assistant",
689
- content: "",
690
- tool_calls: [],
691
- };
692
- updatedMessages.push(lastAssistantMessage);
693
- lastAssistantIndex = updatedMessages.length - 1;
694
- }
695
- // Add or update the tool call
696
- const toolCall = {
697
- id: streamMessage.data.id,
698
- displayName: streamMessage.data.displayName,
699
- function: {
700
- name: streamMessage.data.function?.name ||
701
- streamMessage.data.name,
702
- arguments: streamMessage.data.function?.arguments ||
703
- streamMessage.data.arguments,
704
- },
705
- };
706
- // Update or add the tool call to the assistant message
707
- const existingToolCalls = lastAssistantMessage.tool_calls || [];
708
- const existingIndex = existingToolCalls.findIndex((tc) => tc.id === toolCall.id);
709
- let updatedToolCalls;
710
- if (existingIndex >= 0) {
711
- // Update existing tool call
712
- updatedToolCalls = [...existingToolCalls];
713
- updatedToolCalls[existingIndex] = toolCall;
714
- }
715
- else {
716
- // Add new tool call
717
- updatedToolCalls = [...existingToolCalls, toolCall];
718
- }
719
- // Update the assistant message with the tool call
720
- updatedMessages[lastAssistantIndex] = {
721
- ...lastAssistantMessage,
722
- tool_calls: updatedToolCalls,
723
- };
724
- // Update accumulated response
725
- accumulatedResponse = {
726
- ...accumulatedResponse,
727
- messages: updatedMessages,
728
- };
729
- // Trigger UI update to show the tool call
730
- handleResponse(accumulatedResponse, callback, false);
731
- }
732
- break;
733
- case AgentStreamMessageType.ToolResult:
734
- console.log("Tool result received:", streamMessage.data);
735
- if (streamMessage.data &&
736
- typeof streamMessage.data === "object") {
737
- // Find the assistant message and tool call to update with the result
738
- const updatedMessages = [
739
- ...(accumulatedResponse.messages || []),
740
- ];
741
- let messageUpdated = false;
742
- // Find the assistant message with the matching tool call ID
743
- for (let i = updatedMessages.length - 1; i >= 0; i--) {
744
- if (updatedMessages[i].role === "assistant" &&
745
- updatedMessages[i].tool_calls) {
746
- const toolCalls = updatedMessages[i].tool_calls || [];
747
- const toolCallIndex = toolCalls.findIndex((tc) => tc.id === streamMessage.data.toolCallId);
748
- if (toolCallIndex >= 0) {
749
- // Update the tool call with the result or error
750
- const updatedToolCalls = [...toolCalls];
751
- updatedToolCalls[toolCallIndex] = {
752
- ...updatedToolCalls[toolCallIndex],
753
- function: {
754
- ...updatedToolCalls[toolCallIndex].function,
755
- result: streamMessage.data.result,
756
- error: streamMessage.data.error,
757
- },
758
- };
759
- // Update the message
760
- updatedMessages[i] = {
761
- ...updatedMessages[i],
762
- tool_calls: updatedToolCalls,
763
- };
764
- messageUpdated = true;
765
- break;
766
- }
767
- }
768
- }
769
- // Update accumulated response
770
- accumulatedResponse = {
771
- ...accumulatedResponse,
772
- messages: updatedMessages,
773
- };
774
- // Trigger UI update to show the tool result
775
- handleResponse(accumulatedResponse, callback, false);
776
- }
777
- break;
778
- case AgentStreamMessageType.Completed:
779
- if (streamMessage.data &&
780
- typeof streamMessage.data === "object") {
781
- // Final completion data
782
- const finalResponse = {
783
- messages: accumulatedResponse.messages ||
784
- streamMessage.data.messages,
785
- editOperations: streamMessage.data.editOperations ||
786
- accumulatedResponse.editOperations,
787
- numInputTokens: streamMessage.data.tokenUsage?.inputTokens ||
788
- accumulatedResponse.numInputTokens,
789
- numOutputTokens: streamMessage.data.tokenUsage?.outputTokens ||
790
- accumulatedResponse.numOutputTokens,
791
- numCachedTokens: streamMessage.data.tokenUsage?.cachedTokens ||
792
- accumulatedResponse.numCachedTokens,
793
- state: "completed",
794
- agentName: streamMessage.data.agentName,
795
- error: streamMessage.data.error || accumulatedResponse.error,
796
- // Add cost fields from backend (try both tokenUsage object and direct properties)
797
- totalCost: streamMessage.data.totalCost ||
798
- streamMessage.data.tokenUsage?.totalCost,
799
- totalInputTokenCost: streamMessage.data.totalInputTokenCost ||
800
- streamMessage.data.tokenUsage?.inputTokenCost,
801
- totalOutputTokenCost: streamMessage.data.totalOutputTokenCost ||
802
- streamMessage.data.tokenUsage?.outputTokenCost,
803
- totalCachedTokenCost: streamMessage.data.totalCachedTokenCost ||
804
- streamMessage.data.tokenUsage?.cachedTokenCost,
805
- totalInputTokens: streamMessage.data.totalInputTokens ||
806
- streamMessage.data.tokenUsage?.inputTokens,
807
- totalOutputTokens: streamMessage.data.totalOutputTokens ||
808
- streamMessage.data.tokenUsage?.outputTokens,
809
- totalCachedTokens: streamMessage.data.totalCachedTokens ||
810
- streamMessage.data.tokenUsage?.cachedTokens,
811
- };
812
- completionProcessed = true;
813
- setResponse(finalResponse);
814
- // On completion, just update the internal state and call the final callback
815
- // Don't call handleResponse again as it would duplicate the message
816
- // The Terminal component will clear the streaming response and add it to the message history
817
- const formattedMessages = (finalResponse.messages || []).map((message) => {
818
- const formattedContent = formatMessageContent(message.content || "");
819
- return {
820
- ...message,
821
- content: message.content || "",
822
- formattedContent: formattedContent,
823
- tool_calls: message.tool_calls || [],
824
- };
825
- });
826
- // Update internal state without triggering a new render via handleResponse
827
- setMessages([...formattedMessages]);
828
- setResponseMessages([...formattedMessages]);
829
- // Only call the terminal callback to mark completion (this will add to history)
830
- callback(_jsx(AiResponseMessage, { messages: formattedMessages, editOperations: finalResponse.editOperations, finished: true, error: finalResponse.error }), true);
831
- // Handle agent name update
832
- if (finalResponse.agentName && onAgentNameUpdate) {
833
- onAgentNameUpdate(finalResponse.agentName);
834
- }
835
- if (context.callback)
836
- context.callback(finalResponse);
837
- //editContext?.requestRefresh("immediate");
838
- }
839
- break;
840
- case AgentStreamMessageType.Error:
841
- const errorMessage = streamMessage.error || "Unknown agent error";
842
- console.error("Agent execution error:", errorMessage);
843
- console.log("Displaying error in terminal:", errorMessage);
844
- setTerminalError(errorMessage);
845
- const errorResponse = {
846
- ...accumulatedResponse,
847
- state: "error",
848
- error: errorMessage,
849
- };
850
- setResponse(errorResponse);
851
- // Display the error in the terminal
852
- console.log("Calling handleResponse with error");
853
- handleResponse(errorResponse, callback, true);
854
- // Also try using TerminalService directly as fallback
855
- console.log("Also trying TerminalService.emit");
856
- TerminalService.emit("response", {
857
- text: (_jsx(AiResponseMessage, { messages: [], editOperations: [], finished: true, error: errorMessage })),
858
- type: "response",
859
- });
860
- console.log("Error callback completed");
861
- setIsRunning(false);
862
- abortControllerRef.current = null;
863
- return; // Exit the stream processing
864
- default:
865
- console.log("Unhandled stream message type:", streamMessage.type);
866
- break;
867
- }
868
- }, abortController.signal);
869
- // If we reach here, the stream ended without a Completed message
870
- if (!completionProcessed) {
871
- // console.log("Stream ended without Completed message, cleaning up");
872
- const finalResponse = {
873
- ...accumulatedResponse,
874
- state: "completed",
875
- };
876
- setResponse(finalResponse);
877
- handleResponse(finalResponse, callback, true);
878
- }
879
- setIsRunning(false);
880
- abortControllerRef.current = null;
881
- }
882
- catch (streamError) {
883
- console.error("Stream connection error:", streamError);
884
- const isAbort = streamError?.name === "AbortError" ||
885
- streamError?.message?.toLowerCase?.().includes("abort");
886
- if (isAbort) {
887
- const cancelledResponse = {
888
- ...accumulatedResponse,
889
- state: "cancelled",
890
- };
891
- setResponse(cancelledResponse);
892
- handleResponse(cancelledResponse, callback, true);
893
- }
894
- else {
895
- const errorMessage = streamError instanceof Error
896
- ? streamError.message
897
- : "Unknown stream error";
898
- const errorResponse = {
899
- ...accumulatedResponse,
900
- state: "error",
901
- error: errorMessage,
902
- };
903
- setResponse(errorResponse);
904
- // Display the error in the terminal
905
- callback(_jsx(AiResponseMessage, { messages: accumulatedResponse.messages || [], editOperations: accumulatedResponse.editOperations || [], finished: true, error: errorMessage }), true);
906
- }
907
- setIsRunning(false);
908
- abortControllerRef.current = null;
909
- }
910
- }
911
- catch (error) {
912
- console.error("Error starting agent:", error);
913
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
914
- // Display error in the terminal
915
- const errorResponse = {
916
- messages: [...messages],
917
- editOperations: [],
918
- numInputTokens: 0,
919
- numOutputTokens: 0,
920
- numCachedTokens: 0,
921
- state: "error",
922
- error: errorMessage,
923
- };
924
- setResponse(errorResponse);
925
- // Show the error in the terminal
926
- callback(_jsx(AiResponseMessage, { messages: [], editOperations: [], finished: true, error: errorMessage }), true);
927
- setIsRunning(false);
928
- abortControllerRef.current = null;
929
- }
197
+ const response = await executePrompt(messages, context, {
198
+ profileId: activeProfile.id,
199
+ selection,
200
+ selectedText,
201
+ model,
202
+ sessionId: editContext.sessionId,
203
+ agentId,
204
+ addSelectedComponents: true,
205
+ }, undefined, (response) => {
206
+ setResponse(response);
207
+ handleResponse(response, callback, false);
208
+ });
209
+ if (response) {
210
+ handleResponse(response, callback, true);
211
+ if (context.callback)
212
+ context.callback(response);
213
+ editContext?.requestRefresh("immediate");
214
+ }
215
+ // if (response?.responseText)
216
+ // newMessages.push({
217
+ // content: response.responseText,
218
+ // role: "assistant",
219
+ // name: "assistant",
220
+ // });
221
+ setResponse(response ? response : undefined);
222
+ // setMessages(newMessages);
930
223
  }
931
- // Stop button handler to cancel running agent and abort stream
932
- const handleStop = async () => {
933
- try {
934
- if (!isRunning)
935
- return;
936
- const context = createAiContext({ editContext });
937
- await cancelAgent(agentId, context);
938
- }
939
- catch (e) {
940
- // Ignore cancel errors; still abort client-side stream
941
- }
942
- finally {
943
- abortControllerRef.current?.abort();
944
- }
945
- };
946
224
  useEffect(() => {
947
225
  TerminalService.on("command", commandHandler);
948
226
  return () => {
@@ -954,39 +232,18 @@ export function AiTerminal({ closeButton, createAiContext, defaultProfile, optio
954
232
  setResponseMessages([]);
955
233
  setResponse(undefined);
956
234
  setInitialTerminalMessages(undefined);
957
- setTerminalError(null); // Clear any terminal errors
958
- // Generate a new agentId when clearing the terminal to start fresh
959
- setAgentId(crypto.randomUUID());
960
- }, initialMessages: initialTerminalMessages, infobar: _jsx(AgentCostDisplay, { response: response
961
- ? {
962
- numInputTokens: response.numInputTokens,
963
- numOutputTokens: response.numOutputTokens,
964
- numCachedTokens: response.numCachedTokens,
965
- totalCost: response.totalCost,
966
- inputCost: response.totalInputTokenCost,
967
- outputCost: response.totalOutputTokenCost,
968
- cachedCost: response.totalCachedTokenCost,
969
- }
970
- : null, totalTokens: response
971
- ? {
972
- input: response.totalInputTokens || response.numInputTokens,
973
- output: response.totalOutputTokens ||
974
- response.numOutputTokens,
975
- cached: response.totalCachedTokens ||
976
- response.numCachedTokens,
977
- totalCost: response.totalCost || 0,
978
- inputCost: response.totalInputTokenCost || 0,
979
- outputCost: response.totalOutputTokenCost || 0,
980
- cachedCost: response.totalCachedTokenCost || 0,
981
- }
982
- : undefined }), prompt: prompt, setPrompt: setPrompt, submitOverride: isRunning ? (_jsx(SimpleIconButton, { icon: _jsx(Square, { size: 16, strokeWidth: 1 }), label: "Stop", onClick: handleStop, disabled: !isRunning })) : undefined, statusbar: _jsxs("div", { className: "flex flex-1 items-center justify-between gap-1", children: [_jsxs(Popover, { open: showPredefined, onOpenChange: setShowPredefined, children: [_jsxs(Tooltip, { delayDuration: 500, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx("a", { className: "ml-1 flex cursor-pointer items-center gap-1 text-xs", children: _jsx(WizardIcon, { className: "text-theme-secondary h-5 w-5" }) }) }) }), _jsx(TooltipContent, { children: "Predefined prompts" })] }), _jsx(PopoverContent, { className: "w-96 p-2", align: "start", side: "top", children: activeProfile?.prompts?.length ? (_jsx("div", { className: "flex max-h-64 flex-col gap-1 overflow-y-auto", children: activeProfile.prompts.map((p, index) => (_jsx("div", { className: "mb-1 cursor-pointer rounded-lg border border-gray-200 p-1.5 text-xs text-gray-700 hover:bg-gray-50", onClick: () => {
983
- setPrompt(p.prompt);
984
- setShowPredefined(false);
985
- }, children: p.title }, index))) })) : (_jsx("div", { className: "text-xs text-gray-500", children: "No predefined prompts available" })) })] }), editContext.selection?.length > 0 && (_jsxs("div", { className: "mr-2 flex items-center text-xs text-red-400", children: [editContext.selection.length, " items selected", _jsx(SimpleIconButton, { icon: "pi pi-times", label: "Clear selection", onClick: () => {
235
+ }, initialMessages: initialTerminalMessages, infobar: response?.numInputTokens && response?.numInputTokens > 0 ? (_jsxs("div", { className: "text-right text-gray-400", style: { fontSize: "10px" }, children: ["Tokens in: ", response?.numInputTokens?.toLocaleString(), " out:", " ", response?.numOutputTokens?.toLocaleString(), " ", response?.numCachedTokens
236
+ ? `cached: ${response?.numCachedTokens?.toLocaleString()} `
237
+ : "", response?.state] })) : null, prompt: prompt, setPrompt: setPrompt, statusbar: _jsxs("div", { className: "flex flex-1 items-center justify-between gap-1", children: [_jsxs("a", { className: "ml-1 flex cursor-pointer items-center gap-1 text-xs", onClick: () => {
238
+ setShowPredefined(!showPredefined);
239
+ }, children: [_jsx(WizardIcon, { className: "h-5 w-5" }), "Predefined prompts"] }), editContext.selection?.length > 0 && (_jsxs("div", { className: "mr-2 flex items-center text-xs text-red-400", children: [editContext.selection.length, " items selected", _jsx(SimpleIconButton, { icon: "pi pi-times", label: "Clear selection", onClick: () => {
986
240
  editContext.select([]);
987
- } })] }))] }), toolbar: _jsxs("div", { className: "flex items-stretch gap-1", children: [_jsxs("div", { className: "relative", ref: settingsRef, children: [_jsx(SimpleIconButton, { icon: _jsx(Settings, { size: 16, strokeWidth: 1 }), label: "Settings", onClick: () => setShowSettings(!showSettings) }), showSettings && (_jsxs("div", { className: "absolute top-8 right-0 z-50 flex flex-col gap-2 rounded-lg border border-gray-200 bg-white p-3 shadow-lg", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-gray-700", children: "Profile" }), _jsxs(DropdownMenu, { children: [_jsxs(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", children: [_jsx("span", { children: activeProfile?.name || "Select profile" }), _jsx(ChevronDown, { size: 16, strokeWidth: 1 })] }), _jsx(DropdownMenuContent, { className: "min-w-[200px]", children: profiles.map((profile) => (_jsx(DropdownMenuItem, { onClick: () => setActiveProfile(profile), className: "cursor-pointer", children: profile.name }, profile.id))) })] })] }), activeProfile && (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-gray-700", children: "Model" }), _jsxs(DropdownMenu, { children: [_jsxs(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", children: [_jsx("span", { children: activeProfile.models.find((m) => m.id === model)
988
- ?.name || "Select model" }), _jsx(ChevronDown, { size: 16, strokeWidth: 1 })] }), _jsx(DropdownMenuContent, { className: "min-w-[200px]", children: activeProfile.models.map((modelOption) => (_jsx(DropdownMenuItem, { onClick: () => setModel(modelOption.id), className: "cursor-pointer", children: modelOption.name }, modelOption.id))) })] })] }))] }))] }), closeButton] }), commandHandler: (v, callback) => {
241
+ } })] })), showPredefined && (_jsx("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", children: activeProfile &&
242
+ activeProfile.prompts.map((p, index) => (_jsx("div", { className: "mb-1 cursor-pointer rounded-lg border border-gray-200 p-1.5 text-xs text-gray-700", onClick: () => {
243
+ setPrompt(p.prompt);
244
+ setShowPredefined(false);
245
+ }, children: p.title }, index))) }))] }), toolbar: _jsxs("div", { className: "flex items-stretch gap-1", children: [_jsxs("div", { className: "relative", ref: settingsRef, children: [_jsx(SimpleIconButton, { icon: _jsx(Settings, { size: 16, strokeWidth: 1 }), label: "Settings", onClick: () => setShowSettings(!showSettings) }), showSettings && (_jsxs("div", { className: "absolute top-8 right-0 z-50 flex flex-col gap-2 rounded-lg border border-gray-200 bg-white p-3 shadow-lg", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-gray-700", children: "Profile" }), _jsx(Dropdown, { className: "text-sm", value: activeProfile, onChange: (e) => setActiveProfile(e.value), optionLabel: "name", options: profiles })] }), activeProfile && (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-gray-700", children: "Model" }), _jsx(Dropdown, { className: "text-sm", value: model, onChange: (e) => setModel(e.value), options: activeProfile.models })] }))] }))] }), closeButton] }), commandHandler: (v, callback) => {
989
246
  commandHandler(v, callback);
990
- } }) }), terminalError && (_jsx("div", { className: "bg-opacity-95 absolute inset-0 z-10 flex items-center justify-center bg-white", children: _jsx("div", { className: "mx-4 max-w-md rounded-lg border-l-4 border-red-500 bg-red-50 p-4 shadow-md", children: _jsxs("div", { className: "flex items-start", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx("div", { className: "h-5 w-5 text-red-400", children: "\u26A0\uFE0F" }) }), _jsxs("div", { className: "ml-3 flex-1", children: [_jsx("h3", { className: "text-sm font-medium text-red-800", children: "Agent Error" }), _jsx("p", { className: "mt-1 text-sm text-red-700", children: terminalError }), _jsx("div", { className: "mt-3", children: _jsx("button", { onClick: () => setTerminalError(null), className: "rounded bg-red-100 px-3 py-1 text-xs font-medium text-red-800 hover:bg-red-200", children: "Dismiss" }) })] })] }) }) })), activeProfile?.errorMessage && (_jsx("div", { className: "absolute inset-0 grid items-center justify-center p-2 text-sm text-red-500", children: activeProfile?.errorMessage })), !model && (_jsx("div", { className: "absolute inset-0 grid items-center justify-center text-sm text-gray-400", children: !activeProfile ? "No profile selected" : "No model selected" }))] }) }));
247
+ } }) }), activeProfile?.errorMessage && (_jsx("div", { className: "absolute inset-0 grid items-center justify-center p-2 text-sm text-red-500", children: activeProfile?.errorMessage })), !model && (_jsx("div", { className: "absolute inset-0 grid items-center justify-center text-sm text-gray-400", children: !activeProfile ? "No profile selected" : "No model selected" }))] }) }));
991
248
  }
992
249
  //# sourceMappingURL=AiTerminal.js.map