@alpaca-editor/core 1.0.4018 → 1.0.4027

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 (179) hide show
  1. package/dist/components/SimpleLanguageSelector.d.ts +2 -1
  2. package/dist/components/SimpleLanguageSelector.js +9 -2
  3. package/dist/components/SimpleLanguageSelector.js.map +1 -1
  4. package/dist/components/ui/input.js +1 -1
  5. package/dist/components/ui/input.js.map +1 -1
  6. package/dist/components/ui/tooltip.d.ts +3 -1
  7. package/dist/components/ui/tooltip.js +2 -2
  8. package/dist/components/ui/tooltip.js.map +1 -1
  9. package/dist/config/config.js +4 -0
  10. package/dist/config/config.js.map +1 -1
  11. package/dist/config/types.d.ts +2 -0
  12. package/dist/editor/ContentTree.js +1 -1
  13. package/dist/editor/ContentTree.js.map +1 -1
  14. package/dist/editor/ContextMenu.js +26 -0
  15. package/dist/editor/ContextMenu.js.map +1 -1
  16. package/dist/editor/FieldHistory.js +1 -1
  17. package/dist/editor/FieldHistory.js.map +1 -1
  18. package/dist/editor/FieldListField.js +2 -2
  19. package/dist/editor/FieldListField.js.map +1 -1
  20. package/dist/editor/Terminal.js +3 -1
  21. package/dist/editor/Terminal.js.map +1 -1
  22. package/dist/editor/ai/Agents.js +37 -18
  23. package/dist/editor/ai/Agents.js.map +1 -1
  24. package/dist/editor/ai/AiResponseMessage.js +71 -5
  25. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  26. package/dist/editor/ai/AiTerminal.d.ts +3 -1
  27. package/dist/editor/ai/AiTerminal.js +53 -24
  28. package/dist/editor/ai/AiTerminal.js.map +1 -1
  29. package/dist/editor/ai/AiToolCall.js +3 -3
  30. package/dist/editor/ai/AiToolCall.js.map +1 -1
  31. package/dist/editor/ai/EditorAiTerminal.d.ts +2 -1
  32. package/dist/editor/ai/EditorAiTerminal.js +2 -2
  33. package/dist/editor/ai/EditorAiTerminal.js.map +1 -1
  34. package/dist/editor/client/EditorClient.js +5 -1
  35. package/dist/editor/client/EditorClient.js.map +1 -1
  36. package/dist/editor/client/editContext.d.ts +2 -0
  37. package/dist/editor/client/editContext.js.map +1 -1
  38. package/dist/editor/client/operations.d.ts +1 -0
  39. package/dist/editor/client/operations.js +7 -0
  40. package/dist/editor/client/operations.js.map +1 -1
  41. package/dist/editor/commands/componentCommands.js +1 -1
  42. package/dist/editor/commands/componentCommands.js.map +1 -1
  43. package/dist/editor/field-types/ImageFieldEditor.js +1 -1
  44. package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
  45. package/dist/editor/field-types/MultiLineText.js +1 -1
  46. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  47. package/dist/editor/field-types/PictureFieldEditor.js +1 -1
  48. package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
  49. package/dist/editor/field-types/RawEditor.js +1 -1
  50. package/dist/editor/field-types/RawEditor.js.map +1 -1
  51. package/dist/editor/field-types/RichTextEditorComponent.js +16 -17
  52. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  53. package/dist/editor/field-types/SingleLineText.js +1 -1
  54. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  55. package/dist/editor/field-types/richtext/components/SimpleDropdown.d.ts +18 -0
  56. package/dist/editor/field-types/richtext/components/SimpleDropdown.js +71 -0
  57. package/dist/editor/field-types/richtext/components/SimpleDropdown.js.map +1 -0
  58. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.d.ts +5 -0
  59. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +359 -0
  60. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js.map +1 -0
  61. package/dist/editor/field-types/richtext/components/SimpleToolbar.d.ts +16 -0
  62. package/dist/editor/field-types/richtext/components/SimpleToolbar.js +181 -0
  63. package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -0
  64. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.d.ts +9 -0
  65. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js +14 -0
  66. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js.map +1 -0
  67. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +4 -0
  68. package/dist/editor/field-types/richtext/contextMenuFactory.js +193 -0
  69. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -0
  70. package/dist/editor/field-types/richtext/index.d.ts +6 -5
  71. package/dist/editor/field-types/richtext/index.js +6 -5
  72. package/dist/editor/field-types/richtext/index.js.map +1 -1
  73. package/dist/editor/field-types/richtext/types.d.ts +16 -16
  74. package/dist/editor/field-types/richtext/types.js +84 -84
  75. package/dist/editor/field-types/richtext/types.js.map +1 -1
  76. package/dist/editor/page-editor-chrome/CommentHighlighting.js +1 -1
  77. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  78. package/dist/editor/page-editor-chrome/FrameMenu.js +5 -5
  79. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  80. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +1 -1
  81. package/dist/editor/page-viewer/PageViewerFrame.js +3 -2
  82. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  83. package/dist/editor/services/agentService.d.ts +14 -4
  84. package/dist/editor/services/agentService.js.map +1 -1
  85. package/dist/editor/services/aiService.d.ts +1 -0
  86. package/dist/editor/services/aiService.js +1 -0
  87. package/dist/editor/services/aiService.js.map +1 -1
  88. package/dist/page-wizard/PageWizard.d.ts +2 -0
  89. package/dist/page-wizard/PageWizard.js +6 -13
  90. package/dist/page-wizard/PageWizard.js.map +1 -1
  91. package/dist/page-wizard/WizardSteps.js +3 -1
  92. package/dist/page-wizard/WizardSteps.js.map +1 -1
  93. package/dist/page-wizard/service.d.ts +1 -0
  94. package/dist/page-wizard/service.js +7 -0
  95. package/dist/page-wizard/service.js.map +1 -1
  96. package/dist/page-wizard/steps/ContentStep.js +210 -33
  97. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  98. package/dist/page-wizard/steps/FindItemsStep.js +11 -3
  99. package/dist/page-wizard/steps/FindItemsStep.js.map +1 -1
  100. package/dist/page-wizard/steps/LayoutStep.js +1 -1
  101. package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
  102. package/dist/page-wizard/steps/MetaDataStep.js +1 -1
  103. package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
  104. package/dist/page-wizard/steps/SchottSelectImagesStep.d.ts +2 -0
  105. package/dist/page-wizard/steps/SchottSelectImagesStep.js +55 -0
  106. package/dist/page-wizard/steps/SchottSelectImagesStep.js.map +1 -0
  107. package/dist/page-wizard/steps/StructureStep.js +20 -5
  108. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  109. package/dist/page-wizard/steps/TranslateStep.d.ts +2 -0
  110. package/dist/page-wizard/steps/TranslateStep.js +413 -0
  111. package/dist/page-wizard/steps/TranslateStep.js.map +1 -0
  112. package/dist/page-wizard/utils/dataAccessor.d.ts +7 -0
  113. package/dist/page-wizard/utils/dataAccessor.js +76 -0
  114. package/dist/page-wizard/utils/dataAccessor.js.map +1 -1
  115. package/dist/revision.d.ts +2 -2
  116. package/dist/revision.js +2 -2
  117. package/dist/splash-screen/NewPage.js +5 -4
  118. package/dist/splash-screen/NewPage.js.map +1 -1
  119. package/dist/splash-screen/OpenPage.js +2 -1
  120. package/dist/splash-screen/OpenPage.js.map +1 -1
  121. package/dist/splash-screen/RecentPages.js +3 -1
  122. package/dist/splash-screen/RecentPages.js.map +1 -1
  123. package/dist/styles.css +57 -0
  124. package/package.json +5 -4
  125. package/src/components/SimpleLanguageSelector.tsx +11 -1
  126. package/src/components/ui/input.tsx +1 -1
  127. package/src/components/ui/tooltip.tsx +3 -2
  128. package/src/config/config.tsx +4 -0
  129. package/src/config/types.ts +6 -0
  130. package/src/editor/ContentTree.tsx +1 -1
  131. package/src/editor/ContextMenu.tsx +39 -0
  132. package/src/editor/FieldHistory.tsx +1 -1
  133. package/src/editor/FieldListField.tsx +2 -6
  134. package/src/editor/Terminal.tsx +5 -1
  135. package/src/editor/ai/Agents.tsx +90 -46
  136. package/src/editor/ai/AiResponseMessage.tsx +185 -24
  137. package/src/editor/ai/AiTerminal.tsx +104 -50
  138. package/src/editor/ai/AiToolCall.tsx +14 -4
  139. package/src/editor/ai/EditorAiTerminal.tsx +3 -0
  140. package/src/editor/client/EditorClient.tsx +9 -1
  141. package/src/editor/client/editContext.ts +2 -0
  142. package/src/editor/client/operations.ts +9 -0
  143. package/src/editor/commands/componentCommands.tsx +1 -1
  144. package/src/editor/field-types/ImageFieldEditor.tsx +1 -0
  145. package/src/editor/field-types/MultiLineText.tsx +1 -0
  146. package/src/editor/field-types/PictureFieldEditor.tsx +1 -0
  147. package/src/editor/field-types/RawEditor.tsx +1 -0
  148. package/src/editor/field-types/RichTextEditorComponent.tsx +27 -25
  149. package/src/editor/field-types/SingleLineText.tsx +1 -0
  150. package/src/editor/field-types/richtext/components/SimpleDropdown.tsx +165 -0
  151. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.css +261 -0
  152. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +505 -0
  153. package/src/editor/field-types/richtext/components/SimpleToolbar.tsx +362 -0
  154. package/src/editor/field-types/richtext/components/SimpleToolbarButton.tsx +36 -0
  155. package/src/editor/field-types/richtext/contextMenuFactory.tsx +264 -0
  156. package/src/editor/field-types/richtext/index.ts +6 -5
  157. package/src/editor/field-types/richtext/types.ts +168 -148
  158. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +1 -1
  159. package/src/editor/page-editor-chrome/FrameMenu.tsx +16 -11
  160. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +1 -1
  161. package/src/editor/page-viewer/PageViewerFrame.tsx +4 -3
  162. package/src/editor/services/agentService.ts +16 -5
  163. package/src/editor/services/aiService.ts +4 -0
  164. package/src/page-wizard/PageWizard.tsx +10 -13
  165. package/src/page-wizard/WizardSteps.tsx +3 -1
  166. package/src/page-wizard/service.ts +11 -0
  167. package/src/page-wizard/steps/ContentStep.tsx +376 -43
  168. package/src/page-wizard/steps/FindItemsStep.tsx +23 -3
  169. package/src/page-wizard/steps/LayoutStep.tsx +1 -1
  170. package/src/page-wizard/steps/MetaDataStep.tsx +1 -1
  171. package/src/page-wizard/steps/SchottSelectImagesStep.tsx +141 -0
  172. package/src/page-wizard/steps/StructureStep.tsx +40 -5
  173. package/src/page-wizard/steps/TranslateStep.tsx +772 -0
  174. package/src/page-wizard/utils/dataAccessor.ts +85 -0
  175. package/src/revision.ts +2 -2
  176. package/src/splash-screen/NewPage.tsx +18 -3
  177. package/src/splash-screen/OpenPage.tsx +14 -1
  178. package/src/splash-screen/RecentPages.tsx +4 -2
  179. package/tsconfig.build.json +1 -0
@@ -4,7 +4,13 @@ import { EditorAiTerminal } from "./EditorAiTerminal";
4
4
  import { SimpleIconButton } from "../ui/SimpleIconButton";
5
5
  import { Plus, X, History } from "lucide-react";
6
6
  import { cn } from "../../lib/utils";
7
- import { getAgents, getAgent, getChatHistory, AgentChat, AgentChatMessage } from "../services/agentService";
7
+ import {
8
+ getAgents,
9
+ getAgent,
10
+ getChatHistory,
11
+ AgentChat,
12
+ AgentChatMessage,
13
+ } from "../services/agentService";
8
14
  import { Message } from "./AiTerminal";
9
15
  import {
10
16
  Popover,
@@ -21,22 +27,41 @@ interface TerminalInstance {
21
27
  messages?: Message[];
22
28
  }
23
29
 
24
- function convertAgentMessagesToTerminalMessages(agentMessages: AgentChatMessage[]): Message[] {
25
- return agentMessages.map(msg => ({
26
- id: msg.id,
27
- content: msg.content,
28
- name: msg.name,
29
- role: msg.role,
30
- tool_calls: msg.functionName ? [{
31
- id: msg.toolCallId || msg.id,
32
- displayName: msg.functionName,
33
- function: {
34
- name: msg.functionName,
35
- arguments: msg.functionArguments || ""
36
- }
37
- }] : [],
38
- tool_call_id: msg.toolCallId
39
- }));
30
+ function convertAgentMessagesToTerminalMessages(
31
+ agentMessages: AgentChatMessage[],
32
+ ): Message[] {
33
+ return (
34
+ agentMessages
35
+ // Keep all messages including tool results for complete conversation context
36
+ .map((msg) => ({
37
+ id: msg.id,
38
+ content: msg.content,
39
+ name: msg.name,
40
+ role: msg.role,
41
+ tool_calls:
42
+ msg.toolCalls?.map((toolCall) => ({
43
+ id: toolCall.toolCallId,
44
+ displayName: toolCall.functionName,
45
+ function: {
46
+ name: toolCall.functionName,
47
+ arguments: toolCall.functionArguments || "",
48
+ },
49
+ })) || [],
50
+ tool_call_id: msg.toolCallId,
51
+ }))
52
+ );
53
+ }
54
+
55
+ function formatDateToLocalTime(dateString: string): string {
56
+ // Ensure the date string is treated as UTC if it doesn't have timezone info
57
+ const normalizedDate =
58
+ dateString.includes("Z") ||
59
+ dateString.includes("+") ||
60
+ dateString.includes("-", 19)
61
+ ? dateString
62
+ : dateString + "Z";
63
+
64
+ return new Date(normalizedDate).toLocaleString();
40
65
  }
41
66
 
42
67
  export function Agents({
@@ -58,39 +83,41 @@ export function Agents({
58
83
  loadAgentsFromBackend();
59
84
  }, []);
60
85
 
61
-
62
-
63
86
  const loadAgentsFromBackend = async () => {
64
87
  try {
65
88
  setLoadingAgents(true);
66
-
89
+
67
90
  // Load active agents
68
91
  const activeAgentsResult = await getAgents("active");
69
-
92
+
70
93
  // Load inactive agents for history
71
94
  const inactiveAgentsResult = await getChatHistory("completed", 20);
72
-
95
+
73
96
  if (activeAgentsResult.type === "success" && activeAgentsResult.data) {
74
97
  const activeAgents = activeAgentsResult.data;
75
-
98
+
76
99
  // Create terminals for active agents
77
100
  const activeTerminals: TerminalInstance[] = await Promise.all(
78
101
  activeAgents.map(async (agent, index) => {
79
102
  // Load messages for each active agent
80
103
  const agentWithMessages = await loadAgentMessages(agent.id);
81
-
104
+
82
105
  return {
83
106
  id: `agent-${agent.id}`,
84
107
  title: agent.name || `Agent ${index + 1}`,
85
108
  agentId: agent.id,
86
109
  options: initialOptions,
87
- messages: agentWithMessages?.messages ? convertAgentMessagesToTerminalMessages(agentWithMessages.messages) : []
110
+ messages: agentWithMessages?.messages
111
+ ? convertAgentMessagesToTerminalMessages(
112
+ agentWithMessages.messages,
113
+ )
114
+ : [],
88
115
  };
89
- })
116
+ }),
90
117
  );
91
-
118
+
92
119
  setTerminals(activeTerminals);
93
-
120
+
94
121
  // Set the first active terminal as active, or create a new one if none exist
95
122
  if (activeTerminals.length > 0) {
96
123
  setActiveTerminalId(activeTerminals[0]!.id);
@@ -106,11 +133,13 @@ export function Agents({
106
133
  nextTerminalNumber.current++;
107
134
  }
108
135
  }
109
-
110
- if (inactiveAgentsResult.type === "success" && inactiveAgentsResult.data) {
136
+
137
+ if (
138
+ inactiveAgentsResult.type === "success" &&
139
+ inactiveAgentsResult.data
140
+ ) {
111
141
  setInactiveAgents(inactiveAgentsResult.data);
112
142
  }
113
-
114
143
  } catch (error) {
115
144
  console.error("Failed to load agents:", error);
116
145
  // Create a default terminal on error
@@ -127,7 +156,9 @@ export function Agents({
127
156
  }
128
157
  };
129
158
 
130
- const loadAgentMessages = async (agentId: string): Promise<AgentChat | null> => {
159
+ const loadAgentMessages = async (
160
+ agentId: string,
161
+ ): Promise<AgentChat | null> => {
131
162
  try {
132
163
  const result = await getAgent(agentId);
133
164
  if (result.type === "success" && result.data) {
@@ -170,8 +201,8 @@ export function Agents({
170
201
 
171
202
  const openAgentFromHistory = async (agent: AgentChat) => {
172
203
  // Check if this agent is already open as a terminal
173
- const existingTerminal = terminals.find(t => t.agentId === agent.id);
174
-
204
+ const existingTerminal = terminals.find((t) => t.agentId === agent.id);
205
+
175
206
  if (existingTerminal) {
176
207
  // Switch to existing terminal
177
208
  setActiveTerminalId(existingTerminal.id);
@@ -181,14 +212,16 @@ export function Agents({
181
212
 
182
213
  // Load the agent with messages
183
214
  const agentWithMessages = await loadAgentMessages(agent.id);
184
-
215
+
185
216
  // Create new terminal for this agent
186
217
  const newTerminal: TerminalInstance = {
187
218
  id: `agent-${agent.id}`,
188
219
  title: agent.name,
189
220
  agentId: agent.id,
190
221
  options: initialOptions,
191
- messages: agentWithMessages?.messages ? convertAgentMessagesToTerminalMessages(agentWithMessages.messages) : []
222
+ messages: agentWithMessages?.messages
223
+ ? convertAgentMessagesToTerminalMessages(agentWithMessages.messages)
224
+ : [],
192
225
  };
193
226
 
194
227
  setTerminals((prev) => [...prev, newTerminal]);
@@ -238,7 +271,10 @@ export function Agents({
238
271
 
239
272
  {/* History Popover */}
240
273
  <div className="flex items-center px-1">
241
- <Popover open={historyPopoverOpen} onOpenChange={setHistoryPopoverOpen}>
274
+ <Popover
275
+ open={historyPopoverOpen}
276
+ onOpenChange={setHistoryPopoverOpen}
277
+ >
242
278
  <PopoverTrigger asChild>
243
279
  <SimpleIconButton
244
280
  onClick={() => {}}
@@ -248,7 +284,7 @@ export function Agents({
248
284
  />
249
285
  </PopoverTrigger>
250
286
  <PopoverContent className="w-64 p-0" align="end">
251
- <div className="px-3 py-2 text-xs font-medium text-gray-500 border-b border-gray-100">
287
+ <div className="border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-500">
252
288
  Agent History
253
289
  </div>
254
290
  <div className="max-h-80 overflow-y-auto">
@@ -260,17 +296,17 @@ export function Agents({
260
296
  inactiveAgents.map((agent) => (
261
297
  <div
262
298
  key={agent.id}
263
- className="cursor-pointer px-3 py-2 text-xs hover:bg-gray-50 border-b border-gray-50"
299
+ className="cursor-pointer border-b border-gray-50 px-3 py-2 text-xs hover:bg-gray-50"
264
300
  onClick={() => openAgentFromHistory(agent)}
265
301
  >
266
- <div className="font-medium text-gray-900 truncate">
302
+ <div className="truncate font-medium text-gray-900">
267
303
  {agent.name}
268
304
  </div>
269
- <div className="text-gray-500 truncate">
305
+ <div className="truncate text-gray-500">
270
306
  {agent.profileName}
271
307
  </div>
272
- <div className="text-gray-400 text-xs">
273
- {new Date(agent.updatedDate).toLocaleString()}
308
+ <div className="text-xs text-gray-400">
309
+ {formatDateToLocalTime(agent.updatedDate)}
274
310
  </div>
275
311
  </div>
276
312
  ))
@@ -306,12 +342,20 @@ export function Agents({
306
342
  activeTerminalId === terminal.id ? "block" : "hidden",
307
343
  )}
308
344
  >
309
- <EditorAiTerminal
345
+ <EditorAiTerminal
310
346
  options={{
311
347
  ...terminal.options,
312
348
  // Pass the pre-loaded messages to the terminal
313
- initialMessages: terminal.messages
314
- }}
349
+ initialMessages: terminal.messages,
350
+ }}
351
+ onAgentNameUpdate={(name) => {
352
+ // Update the terminal title when agent name is updated
353
+ setTerminals((prev) =>
354
+ prev.map((t) =>
355
+ t.id === terminal.id ? { ...t, title: name } : t,
356
+ ),
357
+ );
358
+ }}
315
359
  />
316
360
  </div>
317
361
  ))}
@@ -1,9 +1,16 @@
1
- import { useState } from "react";
1
+ import { useState, useEffect } from "react";
2
+ import JSONPretty from "react-json-pretty";
2
3
 
3
4
  import { useEditContext } from "../client/editContext";
4
5
  import { EditOperation } from "../../types";
5
6
  import { Message } from "./AiTerminal";
6
- import { AiToolCall } from "./AiToolCall";
7
+
8
+ import {
9
+ Popover,
10
+ PopoverContent,
11
+ PopoverTrigger,
12
+ } from "../../components/ui/popover";
13
+ import { SimpleTabs, Tab } from "../ui/SimpleTabs";
7
14
 
8
15
  export function AiResponseMessage({
9
16
  messages,
@@ -22,6 +29,27 @@ export function AiResponseMessage({
22
29
  undefined,
23
30
  );
24
31
 
32
+ // Store tool calls to preserve them during streaming
33
+ const [preservedToolCalls, setPreservedToolCalls] = useState<{
34
+ [messageIndex: number]: any[];
35
+ }>({});
36
+
37
+ console.log(messages);
38
+
39
+ // Update preserved tool calls when messages change
40
+ useEffect(() => {
41
+ const newPreservedToolCalls = { ...preservedToolCalls };
42
+
43
+ messages.forEach((message, index) => {
44
+ if (message.tool_calls && message.tool_calls.length > 0) {
45
+ // Store non-empty tool calls
46
+ newPreservedToolCalls[index] = message.tool_calls;
47
+ }
48
+ });
49
+
50
+ setPreservedToolCalls(newPreservedToolCalls);
51
+ }, [messages]);
52
+
25
53
  const reversedEditOperations = [...editOperations].reverse();
26
54
 
27
55
  const canReject =
@@ -32,32 +60,165 @@ export function AiResponseMessage({
32
60
  !editContext.editHistory[index]?.canUndo,
33
61
  ) === undefined;
34
62
 
63
+ // Helper function to render JSON or text
64
+ const renderJsonOrText = (json: string) => {
65
+ try {
66
+ const parsed = JSON.parse(json);
67
+ return (
68
+ <div className="font-mono text-xs whitespace-pre-wrap">
69
+ <pre className="break-words whitespace-pre-wrap">
70
+ {JSON.stringify(parsed, null, 2)}
71
+ </pre>
72
+ </div>
73
+ );
74
+ } catch (e) {
75
+ return (
76
+ <div className="font-mono text-xs break-words whitespace-pre-wrap">
77
+ {json}
78
+ </div>
79
+ );
80
+ }
81
+ };
82
+
83
+ // Helper function to create tabs for a tool call
84
+ const createToolCallTabs = (
85
+ toolCall: any,
86
+ result: Message | undefined,
87
+ ): Tab[] => {
88
+ const tabs: Tab[] = [
89
+ {
90
+ id: `input-${toolCall.id}`,
91
+ label: "Input",
92
+ content: (
93
+ <div className="max-h-96 overflow-auto p-2">
94
+ <div className="mb-1 text-sm font-semibold text-gray-700">
95
+ Arguments:
96
+ </div>
97
+ <div className="text-xs">
98
+ {renderJsonOrText(toolCall.function.arguments)}
99
+ </div>
100
+ </div>
101
+ ),
102
+ },
103
+ ];
104
+
105
+ if (result) {
106
+ tabs.push({
107
+ id: `output-${toolCall.id}`,
108
+ label: "Output",
109
+ content: (
110
+ <div className="max-h-96 overflow-auto p-2">
111
+ <div className="mb-1 text-sm font-semibold text-gray-700">
112
+ Result:
113
+ </div>
114
+ <div className="text-xs">
115
+ {renderJsonOrText(result.content || "")}
116
+ </div>
117
+ </div>
118
+ ),
119
+ });
120
+ }
121
+
122
+ return tabs;
123
+ };
124
+
125
+ // Helper function to create tool call popover content
126
+ const ToolCallPopover = ({
127
+ toolCall,
128
+ result,
129
+ }: {
130
+ toolCall: any;
131
+ result: Message | undefined;
132
+ }) => {
133
+ const [activeTab, setActiveTab] = useState(0);
134
+ const tabs = createToolCallTabs(toolCall, result);
135
+
136
+ return (
137
+ <div className="w-96">
138
+ <div className="mb-3 border-b border-gray-200 pb-2">
139
+ <div className="text-sm font-semibold text-gray-800">
140
+ {toolCall.displayName || toolCall.function.name}
141
+ </div>
142
+ </div>
143
+ <SimpleTabs
144
+ tabs={tabs}
145
+ activeTab={activeTab}
146
+ setActiveTab={setActiveTab}
147
+ className="mb-3 border-b border-gray-200"
148
+ tabClassName="text-xs"
149
+ />
150
+ </div>
151
+ );
152
+ };
153
+
154
+ // Helper function to find tool result by tool call id
155
+ const findToolResult = (toolCallId: string): Message | undefined => {
156
+ return messages.find(
157
+ (msg) => msg.role === "tool" && msg.tool_call_id === toolCallId,
158
+ );
159
+ };
160
+
35
161
  return (
36
162
  <div>
37
163
  {messages
38
164
  .filter((x) => x.role !== "tool")
39
- .map((message, index) => (
40
- <div key={index}>
41
- <div
42
- dangerouslySetInnerHTML={{
43
- __html: message.formattedContent || message.content || "",
44
- }}
45
- />
46
- {message.tool_calls && message.tool_calls.length > 0 && (
47
- <div className="text-xs text-gray-400">
48
- {message.tool_calls.map((x, index) => (
49
- <AiToolCall
50
- toolCall={x}
51
- key={index}
52
- result={messages.find(
53
- (m) => m.role === "tool" && m.tool_call_id === x.id,
54
- )}
55
- />
56
- ))}
57
- </div>
58
- )}
59
- </div>
60
- ))}
165
+ .map((message, filteredIndex) => {
166
+ // Find the original index of this message in the unfiltered array
167
+ const originalIndex = messages.findIndex((m) => m.id === message.id);
168
+ // Use preserved tool calls if current message has empty tool calls
169
+ const toolCalls =
170
+ message.tool_calls && message.tool_calls.length > 0
171
+ ? message.tool_calls
172
+ : preservedToolCalls[originalIndex] || [];
173
+ return (
174
+ <div key={filteredIndex}>
175
+ {toolCalls.length > 0 && (
176
+ <div className="mb-2 flex flex-wrap gap-2">
177
+ {toolCalls.map((toolCall, toolIndex) => {
178
+ const toolResult = findToolResult(toolCall.id);
179
+ return (
180
+ <Popover
181
+ key={toolIndex}
182
+ enableIframeClickDetection={false}
183
+ >
184
+ <PopoverTrigger asChild>
185
+ <div className="inline-flex cursor-pointer items-center gap-1 text-xs text-gray-500 hover:text-gray-700">
186
+ <i className="pi pi-wrench text-xs" />
187
+ <span>
188
+ {toolCall?.displayName ||
189
+ toolCall?.function?.name ||
190
+ "tool call"}
191
+ </span>
192
+ </div>
193
+ </PopoverTrigger>
194
+ <PopoverContent
195
+ className="p-3"
196
+ align="start"
197
+ side="bottom"
198
+ sideOffset={8}
199
+ onMouseDown={(e) => e.stopPropagation()}
200
+ onClick={(e) => e.stopPropagation()}
201
+ >
202
+ <div className="max-h-[500px] overflow-hidden">
203
+ <ToolCallPopover
204
+ toolCall={toolCall}
205
+ result={toolResult}
206
+ />
207
+ </div>
208
+ </PopoverContent>
209
+ </Popover>
210
+ );
211
+ })}
212
+ </div>
213
+ )}
214
+ <div
215
+ dangerouslySetInnerHTML={{
216
+ __html: message.formattedContent || message.content || "",
217
+ }}
218
+ />
219
+ </div>
220
+ );
221
+ })}
61
222
  {/* <div
62
223
  dangerouslySetInnerHTML={{
63
224
  __html: responseText.replaceAll("\\n", "<br>"),