@alpaca-editor/core 1.0.4026 → 1.0.4030

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 (224) hide show
  1. package/dist/components/ActionButton.js +2 -2
  2. package/dist/components/ActionButton.js.map +1 -1
  3. package/dist/components/SimpleLanguageSelector.js +3 -1
  4. package/dist/components/SimpleLanguageSelector.js.map +1 -1
  5. package/dist/components/ui/button.d.ts +1 -1
  6. package/dist/config/config.js +1 -1
  7. package/dist/config/config.js.map +1 -1
  8. package/dist/config/types.d.ts +1 -1
  9. package/dist/editor/ContextMenu.js +0 -1
  10. package/dist/editor/ContextMenu.js.map +1 -1
  11. package/dist/editor/Editor.js +9 -3
  12. package/dist/editor/Editor.js.map +1 -1
  13. package/dist/editor/FieldListField.js +16 -24
  14. package/dist/editor/FieldListField.js.map +1 -1
  15. package/dist/editor/ImageEditButton.js +1 -1
  16. package/dist/editor/ImageEditButton.js.map +1 -1
  17. package/dist/editor/ImageEditor.js +1 -1
  18. package/dist/editor/ImageEditor.js.map +1 -1
  19. package/dist/editor/MainLayout.js +2 -2
  20. package/dist/editor/MainLayout.js.map +1 -1
  21. package/dist/editor/Terminal.js +1 -1
  22. package/dist/editor/Terminal.js.map +1 -1
  23. package/dist/editor/Titlebar.js +0 -1
  24. package/dist/editor/Titlebar.js.map +1 -1
  25. package/dist/editor/ai/AgentCostDisplay.d.ts +26 -0
  26. package/dist/editor/ai/AgentCostDisplay.js +65 -0
  27. package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
  28. package/dist/editor/ai/Agents.js +83 -29
  29. package/dist/editor/ai/Agents.js.map +1 -1
  30. package/dist/editor/ai/AiPromptPopover.d.ts +7 -0
  31. package/dist/editor/ai/AiPromptPopover.js +111 -0
  32. package/dist/editor/ai/AiPromptPopover.js.map +1 -0
  33. package/dist/editor/ai/AiResponseMessage.d.ts +1 -0
  34. package/dist/editor/ai/AiResponseMessage.js +104 -18
  35. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  36. package/dist/editor/ai/AiTerminal.d.ts +18 -2
  37. package/dist/editor/ai/AiTerminal.js +423 -63
  38. package/dist/editor/ai/AiTerminal.js.map +1 -1
  39. package/dist/editor/ai/EditorAiTerminal.d.ts +2 -1
  40. package/dist/editor/ai/EditorAiTerminal.js +2 -2
  41. package/dist/editor/ai/EditorAiTerminal.js.map +1 -1
  42. package/dist/editor/ai/editorAiContext.d.ts +0 -1
  43. package/dist/editor/ai/editorAiContext.js +0 -2
  44. package/dist/editor/ai/editorAiContext.js.map +1 -1
  45. package/dist/editor/client/EditorClient.d.ts +3 -2
  46. package/dist/editor/client/EditorClient.js +326 -68
  47. package/dist/editor/client/EditorClient.js.map +1 -1
  48. package/dist/editor/client/editContext.d.ts +6 -4
  49. package/dist/editor/client/editContext.js.map +1 -1
  50. package/dist/editor/client/fieldModificationStore.d.ts +19 -0
  51. package/dist/editor/client/fieldModificationStore.js +125 -0
  52. package/dist/editor/client/fieldModificationStore.js.map +1 -0
  53. package/dist/editor/client/itemsRepository.d.ts +1 -1
  54. package/dist/editor/client/itemsRepository.js +38 -28
  55. package/dist/editor/client/itemsRepository.js.map +1 -1
  56. package/dist/editor/client/operations.d.ts +1 -0
  57. package/dist/editor/client/operations.js +39 -31
  58. package/dist/editor/client/operations.js.map +1 -1
  59. package/dist/editor/commands/componentCommands.js +5 -3
  60. package/dist/editor/commands/componentCommands.js.map +1 -1
  61. package/dist/editor/commands/itemCommands.js.map +1 -1
  62. package/dist/editor/component-designer/aiContext.js +0 -2
  63. package/dist/editor/component-designer/aiContext.js.map +1 -1
  64. package/dist/editor/field-types/DropLinkEditor.js +1 -1
  65. package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
  66. package/dist/editor/field-types/MultiLineText.js +5 -7
  67. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  68. package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
  69. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  70. package/dist/editor/field-types/SingleLineText.js +5 -7
  71. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  72. package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
  73. package/dist/editor/hooks/useEditorSettings.js +61 -0
  74. package/dist/editor/hooks/useEditorSettings.js.map +1 -0
  75. package/dist/editor/menubar/ItemActionsMenu.js +2 -2
  76. package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
  77. package/dist/editor/menubar/PageSelector.js +1 -1
  78. package/dist/editor/menubar/PageSelector.js.map +1 -1
  79. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  80. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  81. package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
  82. package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
  83. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
  84. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  85. package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
  86. package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
  87. package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +4 -3
  88. package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
  89. package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
  90. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  91. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
  92. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  93. package/dist/editor/page-viewer/EditorForm.js +1 -1
  94. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  95. package/dist/editor/page-viewer/PageViewer.js +9 -8
  96. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  97. package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
  98. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  99. package/dist/editor/page-viewer/pageViewContext.js +40 -6
  100. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  101. package/dist/editor/reviews/Comment.js +7 -6
  102. package/dist/editor/reviews/Comment.js.map +1 -1
  103. package/dist/editor/services/agentService.d.ts +84 -12
  104. package/dist/editor/services/agentService.js +256 -15
  105. package/dist/editor/services/agentService.js.map +1 -1
  106. package/dist/editor/services/aiService.d.ts +18 -3
  107. package/dist/editor/services/aiService.js +5 -3
  108. package/dist/editor/services/aiService.js.map +1 -1
  109. package/dist/editor/services/contextService.js +0 -1
  110. package/dist/editor/services/contextService.js.map +1 -1
  111. package/dist/editor/services/systemService.d.ts +2 -1
  112. package/dist/editor/services/systemService.js +3 -0
  113. package/dist/editor/services/systemService.js.map +1 -1
  114. package/dist/editor/sidebar/ComponentPalette.js +1 -1
  115. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  116. package/dist/editor/sidebar/EditHistory.js +2 -2
  117. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  118. package/dist/editor/sidebar/GraphQL.d.ts +1 -0
  119. package/dist/editor/sidebar/GraphQL.js +8 -2
  120. package/dist/editor/sidebar/GraphQL.js.map +1 -1
  121. package/dist/editor/sidebar/MainContentTree.js +1 -1
  122. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  123. package/dist/editor/sidebar/SEOInfo.js +1 -1
  124. package/dist/editor/sidebar/SEOInfo.js.map +1 -1
  125. package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
  126. package/dist/editor/sidebar/ViewSelector.js +64 -48
  127. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  128. package/dist/editor/ui/PerfectTree.js +2 -11
  129. package/dist/editor/ui/PerfectTree.js.map +1 -1
  130. package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
  131. package/dist/editor/ui/SimpleIconButton.js +8 -4
  132. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  133. package/dist/index.d.ts +2 -0
  134. package/dist/index.js +2 -0
  135. package/dist/index.js.map +1 -1
  136. package/dist/page-wizard/steps/CollectStep.js +1 -1
  137. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  138. package/dist/page-wizard/steps/StructureStep.js +1 -1
  139. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  140. package/dist/page-wizard/steps/TranslateStep.js +233 -18
  141. package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
  142. package/dist/revision.d.ts +2 -2
  143. package/dist/revision.js +2 -2
  144. package/dist/splash-screen/RecentPages.js +1 -13
  145. package/dist/splash-screen/RecentPages.js.map +1 -1
  146. package/dist/splash-screen/SplashScreen.js +1 -1
  147. package/dist/splash-screen/SplashScreen.js.map +1 -1
  148. package/dist/styles.css +88 -3
  149. package/dist/types.d.ts +6 -0
  150. package/package.json +2 -2
  151. package/src/components/ActionButton.tsx +3 -2
  152. package/src/components/SimpleLanguageSelector.tsx +6 -1
  153. package/src/config/config.tsx +1 -1
  154. package/src/config/types.ts +1 -1
  155. package/src/editor/ContextMenu.tsx +0 -3
  156. package/src/editor/Editor.tsx +11 -3
  157. package/src/editor/FieldListField.tsx +22 -31
  158. package/src/editor/ImageEditButton.tsx +1 -0
  159. package/src/editor/ImageEditor.tsx +1 -0
  160. package/src/editor/MainLayout.tsx +2 -2
  161. package/src/editor/Terminal.tsx +1 -1
  162. package/src/editor/Titlebar.tsx +0 -2
  163. package/src/editor/ai/AgentCostDisplay.tsx +237 -0
  164. package/src/editor/ai/Agents.tsx +147 -65
  165. package/src/editor/ai/AiPromptPopover.tsx +209 -0
  166. package/src/editor/ai/AiResponseMessage.tsx +232 -45
  167. package/src/editor/ai/AiTerminal.tsx +559 -88
  168. package/src/editor/ai/EditorAiTerminal.tsx +3 -0
  169. package/src/editor/ai/editorAiContext.ts +0 -3
  170. package/src/editor/client/EditorClient.tsx +409 -117
  171. package/src/editor/client/editContext.ts +7 -5
  172. package/src/editor/client/fieldModificationStore.ts +196 -0
  173. package/src/editor/client/itemsRepository.ts +41 -31
  174. package/src/editor/client/operations.ts +95 -76
  175. package/src/editor/commands/componentCommands.tsx +9 -3
  176. package/src/editor/commands/itemCommands.tsx +0 -1
  177. package/src/editor/component-designer/aiContext.ts +0 -2
  178. package/src/editor/field-types/DropLinkEditor.tsx +1 -1
  179. package/src/editor/field-types/MultiLineText.tsx +9 -9
  180. package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
  181. package/src/editor/field-types/SingleLineText.tsx +9 -9
  182. package/src/editor/hooks/useEditorSettings.ts +68 -0
  183. package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
  184. package/src/editor/menubar/PageSelector.tsx +1 -1
  185. package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
  186. package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
  187. package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
  188. package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
  189. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
  190. package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
  191. package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
  192. package/src/editor/page-viewer/EditorForm.tsx +1 -0
  193. package/src/editor/page-viewer/PageViewer.tsx +9 -8
  194. package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
  195. package/src/editor/page-viewer/pageViewContext.ts +40 -5
  196. package/src/editor/reviews/Comment.tsx +7 -7
  197. package/src/editor/services/agentService.ts +405 -31
  198. package/src/editor/services/aiService.ts +24 -5
  199. package/src/editor/services/contextService.ts +0 -1
  200. package/src/editor/services/systemService.ts +7 -1
  201. package/src/editor/sidebar/ComponentPalette.tsx +4 -1
  202. package/src/editor/sidebar/EditHistory.tsx +2 -0
  203. package/src/editor/sidebar/GraphQL.tsx +19 -7
  204. package/src/editor/sidebar/MainContentTree.tsx +1 -1
  205. package/src/editor/sidebar/SEOInfo.tsx +1 -1
  206. package/src/editor/sidebar/ViewSelector.tsx +80 -64
  207. package/src/editor/ui/PerfectTree.tsx +2 -18
  208. package/src/editor/ui/SimpleIconButton.tsx +56 -38
  209. package/src/index.ts +2 -0
  210. package/src/page-wizard/steps/CollectStep.tsx +0 -2
  211. package/src/page-wizard/steps/StructureStep.tsx +3 -0
  212. package/src/page-wizard/steps/TranslateStep.tsx +473 -62
  213. package/src/revision.ts +2 -2
  214. package/src/splash-screen/RecentPages.tsx +0 -14
  215. package/src/splash-screen/SplashScreen.tsx +3 -2
  216. package/src/types.ts +7 -0
  217. package/dist/editor/ai/AiPopup.d.ts +0 -10
  218. package/dist/editor/ai/AiPopup.js +0 -23
  219. package/dist/editor/ai/AiPopup.js.map +0 -1
  220. package/dist/editor/ai/AiToolCall.d.ts +0 -5
  221. package/dist/editor/ai/AiToolCall.js +0 -28
  222. package/dist/editor/ai/AiToolCall.js.map +0 -1
  223. package/src/editor/ai/AiPopup.tsx +0 -59
  224. package/src/editor/ai/AiToolCall.tsx +0 -71
@@ -0,0 +1,209 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Sparkles, Send } from "lucide-react";
5
+ import {
6
+ Popover,
7
+ PopoverContent,
8
+ PopoverTrigger,
9
+ } from "../../components/ui/popover";
10
+ import { Button } from "../../components/ui/button";
11
+ import { useEditContext } from "../client/editContext";
12
+ import { AiTerminalOptions } from "./AiTerminal";
13
+ import { Component } from "../pageModel";
14
+
15
+ interface AiPromptPopoverProps {
16
+ children: React.ReactNode;
17
+ components?: Component[];
18
+ }
19
+
20
+ export function AiPromptPopover({
21
+ children,
22
+ components = [],
23
+ }: AiPromptPopoverProps) {
24
+ const [isOpen, setIsOpen] = useState(false);
25
+ const [prompt, setPrompt] = useState("");
26
+ const editContext = useEditContext();
27
+
28
+ const handleSubmit = async () => {
29
+ if (!prompt.trim()) return;
30
+
31
+ // Close the popover
32
+ setIsOpen(false);
33
+
34
+ // Create comprehensive AI options with component context
35
+ let finalPrompt = prompt.trim();
36
+ let hiddenSystemPrompt: string | undefined;
37
+
38
+ if (components.length > 0) {
39
+ // Create detailed component context for the agent
40
+ const componentInfo = components
41
+ .map((comp) => {
42
+ const componentType = comp.type || comp.name || "Unknown";
43
+ const componentId = comp.id;
44
+ const hasContent = comp.items && comp.items.length > 0;
45
+
46
+ return `- ${componentType} (ID: ${componentId})${hasContent ? " with data" : " (empty)"}`;
47
+ })
48
+ .join("\n");
49
+
50
+ finalPrompt = `I'm working with the following ${components.length === 1 ? "component" : "components"}:
51
+ ${componentInfo}
52
+
53
+ User request: ${prompt.trim()}`;
54
+
55
+ // Add hidden system prompt with component details for agent context
56
+ const componentDetails = components.map((comp) => ({
57
+ id: comp.id,
58
+ name: comp.name,
59
+ type: comp.type,
60
+ renderingId: comp.rendering?.id,
61
+ items: comp.items?.length || 0,
62
+ placeholder: comp.parentPlaceholder?.name,
63
+ isShared: comp.isShared,
64
+ }));
65
+
66
+ hiddenSystemPrompt = `Working with components: ${JSON.stringify(componentDetails, null, 2)}`;
67
+ }
68
+
69
+ // Switch to agents view first
70
+ editContext?.switchView("agents");
71
+
72
+ try {
73
+ // Execute the prompt directly with a new agent
74
+ await executePromptDirectly(finalPrompt, hiddenSystemPrompt);
75
+ } catch (error) {
76
+ console.error("Failed to execute prompt with new agent:", error);
77
+ // Fallback to the old method if the new one fails
78
+ const aiOptions: AiTerminalOptions = {
79
+ initialPrompt: finalPrompt,
80
+ hiddenSystemPrompt,
81
+ };
82
+ }
83
+
84
+ // Reset the prompt
85
+ setPrompt("");
86
+ };
87
+
88
+ const executePromptDirectly = async (
89
+ initialPrompt: string,
90
+ hiddenSystemPrompt?: string,
91
+ ) => {
92
+ if (!editContext) return;
93
+
94
+ // Generate a new agent ID
95
+ const newAgentId = crypto.randomUUID();
96
+
97
+ // Create the user message
98
+ const userMessage = {
99
+ id: crypto.randomUUID(),
100
+ content: initialPrompt,
101
+ role: "user",
102
+ name: "user",
103
+ };
104
+
105
+ // Create messages array with optional hidden system prompt
106
+ const messages = [
107
+ ...(hiddenSystemPrompt
108
+ ? [
109
+ {
110
+ role: "system",
111
+ name: "system",
112
+ content: hiddenSystemPrompt,
113
+ id: crypto.randomUUID(),
114
+ },
115
+ ]
116
+ : []),
117
+ userMessage,
118
+ ];
119
+
120
+ // Import the required functions
121
+ const { executePrompt } = await import("../services/aiService");
122
+ const { createEditorAiContext } = await import("./editorAiContext");
123
+
124
+ // Create AI context
125
+ const aiContext = createEditorAiContext({ editContext });
126
+
127
+ // Execute the prompt with the new agent ID
128
+ await executePrompt(messages, aiContext, {
129
+ sessionId: editContext.sessionId,
130
+ agentId: newAgentId,
131
+ addSelectedComponents: true,
132
+ profileId: "Editor", // Default to Editor profile
133
+ });
134
+
135
+ // The agents list will refresh automatically via websockets or periodic refresh
136
+ };
137
+
138
+ const handleKeyDown = (e: React.KeyboardEvent) => {
139
+ if (e.key === "Enter" && !e.shiftKey) {
140
+ e.preventDefault();
141
+ handleSubmit();
142
+ }
143
+ };
144
+
145
+ return (
146
+ <Popover
147
+ open={isOpen}
148
+ onOpenChange={setIsOpen}
149
+ enableIframeClickDetection={false}
150
+ >
151
+ <PopoverTrigger asChild>{children}</PopoverTrigger>
152
+ <PopoverContent
153
+ className="w-96 p-4"
154
+ side="bottom"
155
+ align="start"
156
+ onMouseDown={(e) => e.stopPropagation()}
157
+ onClick={(e) => e.stopPropagation()}
158
+ >
159
+ <div className="space-y-3">
160
+ <div className="flex items-center gap-2">
161
+ <Sparkles size={16} className="text-blue-500" />
162
+ <h3 className="font-medium">AI Assistant</h3>
163
+ </div>
164
+
165
+ <div className="space-y-2">
166
+ <label htmlFor="ai-prompt" className="text-sm text-gray-700">
167
+ What would you like AI to help you with?
168
+ {components.length > 0 && (
169
+ <span className="mt-1 block text-xs text-gray-500">
170
+ Working with {components.length} selected component
171
+ {components.length === 1 ? "" : "s"}
172
+ </span>
173
+ )}
174
+ </label>
175
+ <textarea
176
+ id="ai-prompt"
177
+ value={prompt}
178
+ onChange={(e) => setPrompt(e.target.value)}
179
+ onKeyDown={handleKeyDown}
180
+ placeholder="Enter your prompt here..."
181
+ className="w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none"
182
+ rows={3}
183
+ autoFocus
184
+ />
185
+ </div>
186
+
187
+ <div className="flex justify-end gap-2">
188
+ <Button
189
+ variant="outline"
190
+ size="sm"
191
+ onClick={() => setIsOpen(false)}
192
+ >
193
+ Cancel
194
+ </Button>
195
+ <Button
196
+ size="sm"
197
+ onClick={handleSubmit}
198
+ disabled={!prompt.trim()}
199
+ className="flex items-center gap-1"
200
+ >
201
+ <Send size={14} />
202
+ Start Agent
203
+ </Button>
204
+ </div>
205
+ </div>
206
+ </PopoverContent>
207
+ </Popover>
208
+ );
209
+ }
@@ -1,12 +1,78 @@
1
- import { useState, useEffect } from "react";
2
- import JSONPretty from "react-json-pretty";
1
+ import React, { useState, useEffect } from "react";
2
+ import { JsonView, defaultStyles } from "react-json-view-lite";
3
+ import "react-json-view-lite/dist/index.css";
3
4
 
4
5
  import { useEditContext } from "../client/editContext";
5
6
  import { EditOperation } from "../../types";
6
7
  import { Message } from "./AiTerminal";
7
- import { AiToolCall } from "./AiToolCall";
8
- import { Popover, PopoverContent, PopoverTrigger } from "../../components/ui/popover";
8
+
9
+ import {
10
+ Popover,
11
+ PopoverContent,
12
+ PopoverTrigger,
13
+ } from "../../components/ui/popover";
9
14
  import { SimpleTabs, Tab } from "../ui/SimpleTabs";
15
+ import { Spinner } from "../ui/Spinner";
16
+ import { SimpleIconButton } from "../ui/SimpleIconButton";
17
+ import {
18
+ X,
19
+ FileText,
20
+ Search,
21
+ Code,
22
+ Terminal,
23
+ FolderOpen,
24
+ FileSearch,
25
+ Trash2,
26
+ Globe,
27
+ Brain,
28
+ CheckSquare,
29
+ Edit,
30
+ FileEdit,
31
+ Wrench,
32
+ } from "lucide-react";
33
+
34
+ // Function to get the appropriate icon for each tool
35
+ const getToolIcon = (toolName: string) => {
36
+ const iconMap: { [key: string]: React.ReactElement } = {
37
+ // Content operations
38
+ "get-content": <FileText strokeWidth={1} size={14} />,
39
+ "edit-field": <Edit strokeWidth={1} size={14} />,
40
+ "suggest-field-edit": <Edit strokeWidth={1} size={14} />,
41
+
42
+ // Item operations
43
+ "create-item": <FileEdit strokeWidth={1} size={14} />,
44
+ "load-item": <FileText strokeWidth={1} size={14} />,
45
+ "get-items": <FolderOpen strokeWidth={1} size={14} />,
46
+ "get-children": <FolderOpen strokeWidth={1} size={14} />,
47
+ "get-insertoptions": <FolderOpen strokeWidth={1} size={14} />,
48
+
49
+ // Component operations
50
+ "add-component": <Code strokeWidth={1} size={14} />,
51
+ "remove-component": <Trash2 strokeWidth={1} size={14} />,
52
+ "move-components": <Edit strokeWidth={1} size={14} />,
53
+ "get-placeholders": <Code strokeWidth={1} size={14} />,
54
+ "get-component-props-and-placeholders": <Code strokeWidth={1} size={14} />,
55
+
56
+ // Search operations
57
+ "search-content": <Search strokeWidth={1} size={14} />,
58
+ "search-images": <FileSearch strokeWidth={1} size={14} />,
59
+ "google-search": <Globe strokeWidth={1} size={14} />,
60
+
61
+ // Image operations
62
+ "edit-image": <FileEdit strokeWidth={1} size={14} />,
63
+ "get-picture-description": <FileText strokeWidth={1} size={14} />,
64
+
65
+ // Comments and suggestions
66
+ "add-comment": <CheckSquare strokeWidth={1} size={14} />,
67
+ "get-comments": <CheckSquare strokeWidth={1} size={14} />,
68
+ "get-suggestions": <Brain strokeWidth={1} size={14} />,
69
+
70
+ // Default fallback
71
+ default: <Wrench strokeWidth={1} size={14} />,
72
+ };
73
+
74
+ return iconMap[toolName] || iconMap.default;
75
+ };
10
76
 
11
77
  export function AiResponseMessage({
12
78
  messages,
@@ -26,19 +92,26 @@ export function AiResponseMessage({
26
92
  );
27
93
 
28
94
  // Store tool calls to preserve them during streaming
29
- const [preservedToolCalls, setPreservedToolCalls] = useState<{[messageIndex: number]: any[]}>({});
95
+ const [preservedToolCalls, setPreservedToolCalls] = useState<{
96
+ [messageIndex: number]: any[];
97
+ }>({});
98
+
99
+ // Track popover open state for each tool call
100
+ const [openPopovers, setOpenPopovers] = useState<{
101
+ [key: string]: boolean;
102
+ }>({});
30
103
 
31
104
  // Update preserved tool calls when messages change
32
105
  useEffect(() => {
33
- const newPreservedToolCalls = {...preservedToolCalls};
34
-
106
+ const newPreservedToolCalls = { ...preservedToolCalls };
107
+
35
108
  messages.forEach((message, index) => {
36
109
  if (message.tool_calls && message.tool_calls.length > 0) {
37
110
  // Store non-empty tool calls
38
111
  newPreservedToolCalls[index] = message.tool_calls;
39
112
  }
40
113
  });
41
-
114
+
42
115
  setPreservedToolCalls(newPreservedToolCalls);
43
116
  }, [messages]);
44
117
 
@@ -53,31 +126,92 @@ export function AiResponseMessage({
53
126
  ) === undefined;
54
127
 
55
128
  // Helper function to render JSON or text
56
- const renderJsonOrText = (json: string) => {
129
+ const renderJsonOrText = (json: string | object) => {
130
+ // If it's already an object, use it directly
131
+ if (typeof json === "object" && json !== null) {
132
+ return (
133
+ <div className="font-mono text-xs" style={{ fontSize: "12px" }}>
134
+ <JsonView
135
+ data={json}
136
+ shouldExpandNode={(level) => level < 2}
137
+ style={defaultStyles}
138
+ />
139
+ </div>
140
+ );
141
+ }
142
+
143
+ // Convert to string if not already
144
+ const jsonString = typeof json === "string" ? json : String(json);
145
+
146
+ // Try to parse as JSON
57
147
  try {
58
- const parsed = JSON.parse(json);
148
+ let parsed = JSON.parse(jsonString);
149
+
150
+ // If the result is still a string that looks like JSON, parse it again
151
+ if (
152
+ typeof parsed === "string" &&
153
+ (parsed.startsWith("{") || parsed.startsWith("["))
154
+ ) {
155
+ try {
156
+ parsed = JSON.parse(parsed);
157
+ } catch (e2) {
158
+ console.log("Second parse failed, using first result");
159
+ }
160
+ }
161
+
59
162
  return (
60
- <div className="font-mono text-xs whitespace-pre-wrap">
61
- <pre className="whitespace-pre-wrap break-words">
62
- {JSON.stringify(parsed, null, 2)}
63
- </pre>
163
+ <div className="font-mono text-xs" style={{ fontSize: "12px" }}>
164
+ <JsonView
165
+ data={parsed}
166
+ shouldExpandNode={(level) => level < 2}
167
+ style={defaultStyles}
168
+ />
64
169
  </div>
65
170
  );
66
171
  } catch (e) {
67
- return <div className="whitespace-pre-wrap font-mono text-xs break-words">{json}</div>;
172
+ console.log("JSON parse failed:", e, "Trying to handle as string...");
173
+
174
+ // Try to handle double-escaped JSON
175
+ try {
176
+ const unescaped = jsonString
177
+ .replace(/\\"/g, '"')
178
+ .replace(/\\\\/g, "\\");
179
+ const parsed = JSON.parse(unescaped);
180
+
181
+ return (
182
+ <div className="font-mono text-xs" style={{ fontSize: "12px" }}>
183
+ <JsonView
184
+ data={parsed}
185
+ shouldExpandNode={(level) => level < 2}
186
+ style={defaultStyles}
187
+ />
188
+ </div>
189
+ );
190
+ } catch (e2) {
191
+ console.log("Unescaping also failed:", e2);
192
+ // If all parsing fails, display as plain text
193
+ return (
194
+ <div className="font-mono text-xs break-words whitespace-pre-wrap">
195
+ {jsonString}
196
+ </div>
197
+ );
198
+ }
68
199
  }
69
200
  };
70
201
 
71
202
  // Helper function to create tabs for a tool call
72
- const createToolCallTabs = (toolCall: any, result: Message | undefined): Tab[] => {
203
+ const createToolCallTabs = (toolCall: any, result: string): Tab[] => {
73
204
  const tabs: Tab[] = [
74
205
  {
75
206
  id: `input-${toolCall.id}`,
76
207
  label: "Input",
77
208
  content: (
78
- <div className="max-h-96 overflow-auto p-2">
79
- <div className="text-sm font-semibold text-gray-700 mb-1">Arguments:</div>
80
- <div className="text-xs">{renderJsonOrText(toolCall.function.arguments)}</div>
209
+ <div className="relative h-full w-full p-2">
210
+ <div className="absolute inset-2 overflow-auto">
211
+ <div className="text-xs">
212
+ {renderJsonOrText(toolCall.function.arguments)}
213
+ </div>
214
+ </div>
81
215
  </div>
82
216
  ),
83
217
  },
@@ -88,9 +222,10 @@ export function AiResponseMessage({
88
222
  id: `output-${toolCall.id}`,
89
223
  label: "Output",
90
224
  content: (
91
- <div className="max-h-96 overflow-auto p-2">
92
- <div className="text-sm font-semibold text-gray-700 mb-1">Result:</div>
93
- <div className="text-xs">{renderJsonOrText(result.content || "")}</div>
225
+ <div className="relative h-full w-full p-2">
226
+ <div className="absolute inset-2 overflow-y-auto">
227
+ <div className="text-xs">{renderJsonOrText(result || "")}</div>
228
+ </div>
94
229
  </div>
95
230
  ),
96
231
  });
@@ -100,22 +235,38 @@ export function AiResponseMessage({
100
235
  };
101
236
 
102
237
  // Helper function to create tool call popover content
103
- const ToolCallPopover = ({ toolCall, result }: { toolCall: any; result: Message | undefined }) => {
238
+ const ToolCallPopover = ({
239
+ toolCall,
240
+ result,
241
+ onClose,
242
+ }: {
243
+ toolCall: any;
244
+ result?: string;
245
+ onClose: () => void;
246
+ }) => {
104
247
  const [activeTab, setActiveTab] = useState(0);
105
- const tabs = createToolCallTabs(toolCall, result);
106
-
248
+ const tabs = createToolCallTabs(toolCall, result || "");
249
+
107
250
  return (
108
- <div className="w-96">
109
- <div className="mb-3 pb-2 border-b border-gray-200">
110
- <div className="text-sm font-semibold text-gray-800">
111
- {toolCall.displayName || toolCall.function.name}
251
+ <div className="h-[400px]">
252
+ <div className="mb-3 flex items-center justify-between border-b border-gray-200 px-2 pb-1">
253
+ <div className="flex items-center gap-2 text-sm font-semibold text-gray-800">
254
+ {getToolIcon(toolCall?.function?.name || "")}
255
+ <span>{toolCall.displayName || toolCall.function.name}</span>
112
256
  </div>
257
+ <SimpleIconButton
258
+ onClick={onClose}
259
+ icon={<X strokeWidth={1} size={16} />}
260
+ label="Close"
261
+ showTooltip={false}
262
+ size="small"
263
+ />
113
264
  </div>
114
265
  <SimpleTabs
115
266
  tabs={tabs}
116
267
  activeTab={activeTab}
117
268
  setActiveTab={setActiveTab}
118
- className="border-b border-gray-200 mb-3"
269
+ className="mb-3 border-b border-gray-200"
119
270
  tabClassName="text-xs"
120
271
  />
121
272
  </div>
@@ -125,45 +276,81 @@ export function AiResponseMessage({
125
276
  return (
126
277
  <div>
127
278
  {messages
128
- .filter((x) => x.role !== "tool")
279
+ .filter((x) => x.role !== "tool" && x.role !== "user")
129
280
  .map((message, filteredIndex) => {
130
281
  // Find the original index of this message in the unfiltered array
131
- const originalIndex = messages.findIndex(m => m.id === message.id);
282
+ const originalIndex = messages.findIndex((m) => m.id === message.id);
132
283
  // Use preserved tool calls if current message has empty tool calls
133
- const toolCalls = (message.tool_calls && message.tool_calls.length > 0)
134
- ? message.tool_calls
135
- : (preservedToolCalls[originalIndex] || []);
284
+ const toolCalls =
285
+ message.tool_calls && message.tool_calls.length > 0
286
+ ? message.tool_calls
287
+ : preservedToolCalls[originalIndex] || [];
136
288
  return (
137
- <div key={filteredIndex}>
289
+ <div
290
+ key={filteredIndex}
291
+ className={toolCalls.length > 0 ? "mb-2" : ""}
292
+ >
138
293
  <div
139
294
  dangerouslySetInnerHTML={{
140
295
  __html: message.formattedContent || message.content || "",
141
296
  }}
142
297
  />
143
298
  {toolCalls.length > 0 && (
144
- <div className="mt-2 flex flex-wrap gap-2">
299
+ <div className="mt-2 flex flex-col gap-2">
145
300
  {toolCalls.map((toolCall, toolIndex) => {
301
+ const toolResult = toolCall.function.result;
302
+ const popoverKey = `${originalIndex}-${toolIndex}`;
303
+
146
304
  return (
147
- <Popover key={toolIndex} enableIframeClickDetection={false}>
305
+ <Popover
306
+ key={toolIndex}
307
+ enableIframeClickDetection={false}
308
+ open={openPopovers[popoverKey] || false}
309
+ onOpenChange={(open) => {
310
+ setOpenPopovers((prev) => ({
311
+ ...prev,
312
+ [popoverKey]: open,
313
+ }));
314
+ }}
315
+ >
148
316
  <PopoverTrigger asChild>
149
317
  <div className="inline-flex cursor-pointer items-center gap-1 text-xs text-gray-500 hover:text-gray-700">
150
- <i className="pi pi-wrench text-xs" />
318
+ {toolResult ? (
319
+ <div className="flex items-center">
320
+ {getToolIcon(toolCall?.function?.name || "")}
321
+ </div>
322
+ ) : finished ? (
323
+ <div className="flex items-center opacity-50">
324
+ {getToolIcon(toolCall?.function?.name || "")}
325
+ </div>
326
+ ) : (
327
+ <Spinner size="xs" />
328
+ )}
151
329
  <span>
152
- {toolCall?.displayName || toolCall?.function?.name || 'tool call'}
330
+ {toolCall?.displayName ||
331
+ toolCall?.function?.name ||
332
+ "tool call"}
153
333
  </span>
154
334
  </div>
155
335
  </PopoverTrigger>
156
336
  <PopoverContent
157
- className="p-3"
337
+ className="w-[400px] p-3"
158
338
  align="start"
159
339
  side="bottom"
160
340
  sideOffset={8}
161
341
  onMouseDown={(e) => e.stopPropagation()}
162
342
  onClick={(e) => e.stopPropagation()}
163
343
  >
164
- <div className="max-h-[500px] overflow-hidden">
165
- <ToolCallPopover toolCall={toolCall} result={toolCall.result} />
166
- </div>
344
+ <ToolCallPopover
345
+ toolCall={toolCall}
346
+ result={toolResult}
347
+ onClose={() => {
348
+ setOpenPopovers((prev) => ({
349
+ ...prev,
350
+ [popoverKey]: false,
351
+ }));
352
+ }}
353
+ />
167
354
  </PopoverContent>
168
355
  </Popover>
169
356
  );
@@ -208,7 +395,7 @@ export function AiResponseMessage({
208
395
  setChangesRejected(true);
209
396
  }}
210
397
  >
211
- <i className="pi pi-times" /> Reject
398
+ <X strokeWidth={1} size={14} /> Reject
212
399
  </div>
213
400
  )}
214
401
  {changesRejected && (