@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.
- package/dist/components/ActionButton.js +2 -2
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/SimpleLanguageSelector.js +3 -1
- package/dist/components/SimpleLanguageSelector.js.map +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -1
- package/dist/editor/ContextMenu.js +0 -1
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/Editor.js +9 -3
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldListField.js +16 -24
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditButton.js +1 -1
- package/dist/editor/ImageEditButton.js.map +1 -1
- package/dist/editor/ImageEditor.js +1 -1
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/MainLayout.js +2 -2
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/Terminal.js +1 -1
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/Titlebar.js +0 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +26 -0
- package/dist/editor/ai/AgentCostDisplay.js +65 -0
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
- package/dist/editor/ai/Agents.js +83 -29
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiPromptPopover.d.ts +7 -0
- package/dist/editor/ai/AiPromptPopover.js +111 -0
- package/dist/editor/ai/AiPromptPopover.js.map +1 -0
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -0
- package/dist/editor/ai/AiResponseMessage.js +104 -18
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +18 -2
- package/dist/editor/ai/AiTerminal.js +423 -63
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/ai/EditorAiTerminal.d.ts +2 -1
- package/dist/editor/ai/EditorAiTerminal.js +2 -2
- package/dist/editor/ai/EditorAiTerminal.js.map +1 -1
- package/dist/editor/ai/editorAiContext.d.ts +0 -1
- package/dist/editor/ai/editorAiContext.js +0 -2
- package/dist/editor/ai/editorAiContext.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +3 -2
- package/dist/editor/client/EditorClient.js +326 -68
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +6 -4
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +19 -0
- package/dist/editor/client/fieldModificationStore.js +125 -0
- package/dist/editor/client/fieldModificationStore.js.map +1 -0
- package/dist/editor/client/itemsRepository.d.ts +1 -1
- package/dist/editor/client/itemsRepository.js +38 -28
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -0
- package/dist/editor/client/operations.js +39 -31
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +5 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/component-designer/aiContext.js +0 -2
- package/dist/editor/component-designer/aiContext.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +5 -7
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +5 -7
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
- package/dist/editor/hooks/useEditorSettings.js +61 -0
- package/dist/editor/hooks/useEditorSettings.js.map +1 -0
- package/dist/editor/menubar/ItemActionsMenu.js +2 -2
- package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +4 -3
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +9 -8
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.js +40 -6
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/reviews/Comment.js +7 -6
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +84 -12
- package/dist/editor/services/agentService.js +256 -15
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +18 -3
- package/dist/editor/services/aiService.js +5 -3
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contextService.js +0 -1
- package/dist/editor/services/contextService.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +3 -0
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +1 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +2 -2
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.d.ts +1 -0
- package/dist/editor/sidebar/GraphQL.js +8 -2
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +1 -1
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/SEOInfo.js +1 -1
- package/dist/editor/sidebar/SEOInfo.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
- package/dist/editor/sidebar/ViewSelector.js +64 -48
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +2 -11
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
- package/dist/editor/ui/SimpleIconButton.js +8 -4
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +1 -1
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +1 -1
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/steps/TranslateStep.js +233 -18
- package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/RecentPages.js +1 -13
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/splash-screen/SplashScreen.js +1 -1
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +88 -3
- package/dist/types.d.ts +6 -0
- package/package.json +2 -2
- package/src/components/ActionButton.tsx +3 -2
- package/src/components/SimpleLanguageSelector.tsx +6 -1
- package/src/config/config.tsx +1 -1
- package/src/config/types.ts +1 -1
- package/src/editor/ContextMenu.tsx +0 -3
- package/src/editor/Editor.tsx +11 -3
- package/src/editor/FieldListField.tsx +22 -31
- package/src/editor/ImageEditButton.tsx +1 -0
- package/src/editor/ImageEditor.tsx +1 -0
- package/src/editor/MainLayout.tsx +2 -2
- package/src/editor/Terminal.tsx +1 -1
- package/src/editor/Titlebar.tsx +0 -2
- package/src/editor/ai/AgentCostDisplay.tsx +237 -0
- package/src/editor/ai/Agents.tsx +147 -65
- package/src/editor/ai/AiPromptPopover.tsx +209 -0
- package/src/editor/ai/AiResponseMessage.tsx +232 -45
- package/src/editor/ai/AiTerminal.tsx +559 -88
- package/src/editor/ai/EditorAiTerminal.tsx +3 -0
- package/src/editor/ai/editorAiContext.ts +0 -3
- package/src/editor/client/EditorClient.tsx +409 -117
- package/src/editor/client/editContext.ts +7 -5
- package/src/editor/client/fieldModificationStore.ts +196 -0
- package/src/editor/client/itemsRepository.ts +41 -31
- package/src/editor/client/operations.ts +95 -76
- package/src/editor/commands/componentCommands.tsx +9 -3
- package/src/editor/commands/itemCommands.tsx +0 -1
- package/src/editor/component-designer/aiContext.ts +0 -2
- package/src/editor/field-types/DropLinkEditor.tsx +1 -1
- package/src/editor/field-types/MultiLineText.tsx +9 -9
- package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
- package/src/editor/field-types/SingleLineText.tsx +9 -9
- package/src/editor/hooks/useEditorSettings.ts +68 -0
- package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
- package/src/editor/menubar/PageSelector.tsx +1 -1
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
- package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
- package/src/editor/page-viewer/EditorForm.tsx +1 -0
- package/src/editor/page-viewer/PageViewer.tsx +9 -8
- package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
- package/src/editor/page-viewer/pageViewContext.ts +40 -5
- package/src/editor/reviews/Comment.tsx +7 -7
- package/src/editor/services/agentService.ts +405 -31
- package/src/editor/services/aiService.ts +24 -5
- package/src/editor/services/contextService.ts +0 -1
- package/src/editor/services/systemService.ts +7 -1
- package/src/editor/sidebar/ComponentPalette.tsx +4 -1
- package/src/editor/sidebar/EditHistory.tsx +2 -0
- package/src/editor/sidebar/GraphQL.tsx +19 -7
- package/src/editor/sidebar/MainContentTree.tsx +1 -1
- package/src/editor/sidebar/SEOInfo.tsx +1 -1
- package/src/editor/sidebar/ViewSelector.tsx +80 -64
- package/src/editor/ui/PerfectTree.tsx +2 -18
- package/src/editor/ui/SimpleIconButton.tsx +56 -38
- package/src/index.ts +2 -0
- package/src/page-wizard/steps/CollectStep.tsx +0 -2
- package/src/page-wizard/steps/StructureStep.tsx +3 -0
- package/src/page-wizard/steps/TranslateStep.tsx +473 -62
- package/src/revision.ts +2 -2
- package/src/splash-screen/RecentPages.tsx +0 -14
- package/src/splash-screen/SplashScreen.tsx +3 -2
- package/src/types.ts +7 -0
- package/dist/editor/ai/AiPopup.d.ts +0 -10
- package/dist/editor/ai/AiPopup.js +0 -23
- package/dist/editor/ai/AiPopup.js.map +0 -1
- package/dist/editor/ai/AiToolCall.d.ts +0 -5
- package/dist/editor/ai/AiToolCall.js +0 -28
- package/dist/editor/ai/AiToolCall.js.map +0 -1
- package/src/editor/ai/AiPopup.tsx +0 -59
- 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
|
|
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
|
-
|
|
8
|
-
import {
|
|
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<{
|
|
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
|
-
|
|
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
|
|
61
|
-
<
|
|
62
|
-
{
|
|
63
|
-
|
|
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
|
-
|
|
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:
|
|
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="
|
|
79
|
-
<div className="
|
|
80
|
-
|
|
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="
|
|
92
|
-
<div className="
|
|
93
|
-
|
|
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 = ({
|
|
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="
|
|
109
|
-
<div className="mb-3
|
|
110
|
-
<div className="text-sm font-semibold text-gray-800">
|
|
111
|
-
{toolCall
|
|
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
|
|
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 =
|
|
134
|
-
|
|
135
|
-
|
|
284
|
+
const toolCalls =
|
|
285
|
+
message.tool_calls && message.tool_calls.length > 0
|
|
286
|
+
? message.tool_calls
|
|
287
|
+
: preservedToolCalls[originalIndex] || [];
|
|
136
288
|
return (
|
|
137
|
-
|
|
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-
|
|
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
|
|
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
|
-
|
|
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 ||
|
|
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
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
<
|
|
398
|
+
<X strokeWidth={1} size={14} /> Reject
|
|
212
399
|
</div>
|
|
213
400
|
)}
|
|
214
401
|
{changesRejected && (
|