@alpaca-editor/core 1.0.4013 → 1.0.4015
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/ui/popover.js +6 -3
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/editor/Terminal.d.ts +6 -0
- package/dist/editor/Terminal.js +10 -4
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +133 -13
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +2 -1
- package/dist/editor/ai/AiTerminal.js +86 -139
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +2 -1
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +4 -2
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +59 -0
- package/dist/editor/services/agentService.js +26 -0
- package/dist/editor/services/agentService.js.map +1 -0
- package/dist/editor/services/aiService.d.ts +22 -4
- package/dist/editor/services/aiService.js +131 -20
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contextService.js +6 -4
- package/dist/editor/services/contextService.js.map +1 -1
- package/dist/editor/sidebar/SidebarView.js +2 -5
- package/dist/editor/sidebar/SidebarView.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +1 -1
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -2
- package/dist/editor/ui/SimpleIconButton.js +5 -3
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/utils/jsonCleaner.d.ts +8 -0
- package/dist/editor/utils/jsonCleaner.js +76 -0
- package/dist/editor/utils/jsonCleaner.js.map +1 -0
- package/dist/editor/views/CompareView.js +2 -2
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +7 -2
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/FindItemsStep.js +4 -1
- package/dist/page-wizard/steps/FindItemsStep.js.map +1 -1
- package/dist/page-wizard/steps/ImagesStep.js +13 -7
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/page-wizard/steps/LayoutStep.d.ts +1 -1
- package/dist/page-wizard/steps/LayoutStep.js +22 -20
- package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +8 -15
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +9 -4
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +3 -1
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +10 -11
- package/package.json +1 -1
- package/src/components/ui/popover.tsx +6 -3
- package/src/editor/Terminal.tsx +12 -3
- package/src/editor/ai/Agents.tsx +212 -16
- package/src/editor/ai/AiTerminal.tsx +113 -173
- package/src/editor/ai/GhostWriter.tsx_ +3 -3
- package/src/editor/media-selector/AiImageSearch.tsx +2 -2
- package/src/editor/media-selector/AiImageSearchPrompt.tsx +2 -2
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +5 -5
- package/src/editor/services/agentService.ts +83 -0
- package/src/editor/services/aiService.ts +176 -33
- package/src/editor/services/contextService.ts +5 -6
- package/src/editor/sidebar/SidebarView.tsx +10 -6
- package/src/editor/sidebar/ViewSelector.tsx +2 -2
- package/src/editor/ui/SimpleIconButton.tsx +20 -14
- package/src/editor/utils/jsonCleaner.ts +92 -0
- package/src/editor/views/CompareView.tsx +2 -2
- package/src/page-wizard/steps/ContentStep.tsx +10 -9
- package/src/page-wizard/steps/FindItemsStep.tsx +7 -5
- package/src/page-wizard/steps/ImagesStep.tsx +16 -11
- package/src/page-wizard/steps/LayoutStep.tsx +24 -28
- package/src/page-wizard/steps/MetaDataStep.tsx +11 -21
- package/src/page-wizard/steps/SelectStep.tsx +11 -8
- package/src/page-wizard/steps/StructureStep.tsx +4 -5
- package/src/revision.ts +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AiContext } from "../ai/AiTerminal";
|
|
2
2
|
import { EditContextType } from "../client/editContext";
|
|
3
3
|
import { FieldDescriptor, ItemDescriptor } from "../pageModel";
|
|
4
|
+
import { JsonCleaner } from "../utils/jsonCleaner";
|
|
4
5
|
|
|
5
6
|
import { ExecutionResult, get, post } from "./serviceHelper";
|
|
6
7
|
|
|
@@ -32,6 +33,9 @@ type Message = {
|
|
|
32
33
|
content: string;
|
|
33
34
|
name: string;
|
|
34
35
|
role: string;
|
|
36
|
+
id: string; // Changed from number to string to support UUIDs
|
|
37
|
+
tool_calls?: any[]; // Optional tool_calls for compatibility
|
|
38
|
+
tool_call_id?: string; // Optional tool_call_id for compatibility
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
export interface ExecutePromptResponse {
|
|
@@ -41,49 +45,117 @@ export interface ExecutePromptResponse {
|
|
|
41
45
|
numOutputTokens: number;
|
|
42
46
|
numCachedTokens: number;
|
|
43
47
|
state: string;
|
|
48
|
+
// New properties for incremental updates
|
|
49
|
+
isIncremental?: boolean;
|
|
50
|
+
deltaContent?: string;
|
|
51
|
+
previousContentLength?: number;
|
|
52
|
+
totalContentLength?: number;
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
// Unified options interface that supports both calling patterns
|
|
56
|
+
export interface ExecutePromptOptions {
|
|
57
|
+
// Common options
|
|
58
|
+
sessionId?: string | null;
|
|
59
|
+
model?: string | null;
|
|
60
|
+
|
|
61
|
+
// AiTerminal style options
|
|
62
|
+
profileId?: string;
|
|
63
|
+
selection?: string[];
|
|
64
|
+
selectedText?: string | null;
|
|
65
|
+
agentId?: string;
|
|
66
|
+
addSelectedComponents?: boolean;
|
|
67
|
+
|
|
68
|
+
// Other callers style options
|
|
69
|
+
allowedFunctions?: string[];
|
|
70
|
+
addContextContent?: boolean;
|
|
71
|
+
addAllContent?: boolean;
|
|
72
|
+
profile?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Unified function signature
|
|
46
76
|
export async function executePrompt(
|
|
47
77
|
messages: Message[],
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
options?: RequestInit,
|
|
58
|
-
model?: string,
|
|
78
|
+
context:
|
|
79
|
+
| AiContext
|
|
80
|
+
| {
|
|
81
|
+
editContext: EditContextType;
|
|
82
|
+
createAiContext: ({ editContext }: { editContext: any }) => AiContext;
|
|
83
|
+
},
|
|
84
|
+
options: ExecutePromptOptions = {},
|
|
85
|
+
requestOptions?: RequestInit,
|
|
59
86
|
callback?: (response: any) => void,
|
|
60
87
|
): Promise<ExecutePromptResponse | null> {
|
|
61
|
-
|
|
88
|
+
let endpoint: string;
|
|
89
|
+
let requestBody: any;
|
|
90
|
+
let finalRequestOptions: RequestInit;
|
|
62
91
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
// Handle context - either direct AiContext or need to create it
|
|
93
|
+
let aiContext: AiContext;
|
|
94
|
+
if ("endpoint" in context) {
|
|
95
|
+
// Direct AiContext (AiTerminal style)
|
|
96
|
+
aiContext = context;
|
|
97
|
+
} else {
|
|
98
|
+
// Create context (other callers style)
|
|
99
|
+
aiContext = context.createAiContext({ editContext: context.editContext });
|
|
100
|
+
// Default sessionId from editContext if not provided
|
|
101
|
+
if (options.sessionId === undefined) {
|
|
102
|
+
options.sessionId = context.editContext.sessionId;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
endpoint = aiContext.endpoint;
|
|
72
107
|
|
|
108
|
+
// Build unified request body
|
|
109
|
+
requestBody = {
|
|
110
|
+
messages,
|
|
111
|
+
...aiContext.promptData,
|
|
112
|
+
sessionId: options.sessionId,
|
|
113
|
+
model: options.model,
|
|
114
|
+
|
|
115
|
+
// Include all provided options
|
|
116
|
+
...options,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
finalRequestOptions = {
|
|
120
|
+
method: "POST",
|
|
121
|
+
body: JSON.stringify(requestBody),
|
|
73
122
|
credentials: "include",
|
|
74
123
|
headers: {
|
|
75
124
|
"Content-Type": "application/json",
|
|
76
125
|
},
|
|
77
|
-
...
|
|
78
|
-
}
|
|
126
|
+
...requestOptions,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const response = await fetch(endpoint, finalRequestOptions);
|
|
130
|
+
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
// Always return rich error format
|
|
133
|
+
const text = await response.text();
|
|
134
|
+
return {
|
|
135
|
+
messages: [
|
|
136
|
+
{
|
|
137
|
+
content: "There was an error processing your request: " + text,
|
|
138
|
+
name: "assistant",
|
|
139
|
+
role: "assistant",
|
|
140
|
+
id: crypto.randomUUID(),
|
|
141
|
+
tool_calls: [],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
editOperations: [],
|
|
145
|
+
numInputTokens: 0,
|
|
146
|
+
numOutputTokens: 0,
|
|
147
|
+
numCachedTokens: 0,
|
|
148
|
+
state: "error",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
79
151
|
|
|
80
152
|
if (!response?.body) return null;
|
|
81
153
|
|
|
82
154
|
const reader = response.body.getReader();
|
|
83
155
|
const decoder = new TextDecoder();
|
|
84
156
|
let buffer = "";
|
|
85
|
-
|
|
86
157
|
let result = null;
|
|
158
|
+
let accumulatedContent = "";
|
|
87
159
|
|
|
88
160
|
while (true) {
|
|
89
161
|
const { done, value } = await reader.read();
|
|
@@ -92,21 +164,58 @@ export async function executePrompt(
|
|
|
92
164
|
break;
|
|
93
165
|
}
|
|
94
166
|
|
|
95
|
-
buffer += decoder.decode(value, { stream: true });
|
|
96
|
-
|
|
97
|
-
// Split the buffer by newline and keep the last partial line for the next iteration.
|
|
167
|
+
buffer += decoder.decode(value, { stream: true });
|
|
98
168
|
const lines = buffer.split("\n");
|
|
99
169
|
|
|
100
170
|
if (lines.length > 0) {
|
|
101
|
-
buffer = lines.pop() || "";
|
|
171
|
+
buffer = lines.pop() || "";
|
|
102
172
|
|
|
103
173
|
for (let line of lines) {
|
|
104
|
-
if (line.trim() === "") continue;
|
|
174
|
+
if (line.trim() === "") continue;
|
|
105
175
|
|
|
106
176
|
try {
|
|
107
177
|
const jsonData = JSON.parse(line);
|
|
108
|
-
|
|
109
|
-
|
|
178
|
+
|
|
179
|
+
if (jsonData.isIncremental) {
|
|
180
|
+
if (jsonData.deltaContent) {
|
|
181
|
+
accumulatedContent += jsonData.deltaContent;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Always apply JsonCleaner for streaming responses
|
|
185
|
+
const cleanupResult = JsonCleaner.cleanupJson(accumulatedContent);
|
|
186
|
+
|
|
187
|
+
const incrementalResponse = {
|
|
188
|
+
...jsonData,
|
|
189
|
+
messages: [
|
|
190
|
+
{
|
|
191
|
+
content: cleanupResult.cleanedJson,
|
|
192
|
+
role: "assistant",
|
|
193
|
+
name: "assistant",
|
|
194
|
+
id: Date.now(),
|
|
195
|
+
tool_calls: [],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
callback?.(incrementalResponse);
|
|
201
|
+
result = incrementalResponse;
|
|
202
|
+
} else {
|
|
203
|
+
// Always ensure messages have IDs and tool_calls
|
|
204
|
+
let processedResponse = jsonData;
|
|
205
|
+
if (jsonData.messages) {
|
|
206
|
+
processedResponse = {
|
|
207
|
+
...jsonData,
|
|
208
|
+
messages: jsonData.messages.map((msg: any, index: number) => ({
|
|
209
|
+
...msg,
|
|
210
|
+
id: msg.id || Date.now() + index,
|
|
211
|
+
tool_calls: msg.tool_calls || [],
|
|
212
|
+
})),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
callback?.(processedResponse);
|
|
217
|
+
result = processedResponse;
|
|
218
|
+
}
|
|
110
219
|
} catch (e) {
|
|
111
220
|
console.error("Error parsing line:" + line, e);
|
|
112
221
|
}
|
|
@@ -114,11 +223,45 @@ export async function executePrompt(
|
|
|
114
223
|
}
|
|
115
224
|
}
|
|
116
225
|
|
|
117
|
-
//
|
|
226
|
+
// Handle final buffer
|
|
118
227
|
if (buffer.trim() !== "") {
|
|
119
228
|
try {
|
|
120
229
|
const jsonData = JSON.parse(buffer);
|
|
121
|
-
|
|
230
|
+
|
|
231
|
+
if (jsonData.isIncremental) {
|
|
232
|
+
if (jsonData.deltaContent) {
|
|
233
|
+
accumulatedContent += jsonData.deltaContent;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const cleanupResult = JsonCleaner.cleanupJson(accumulatedContent);
|
|
237
|
+
|
|
238
|
+
result = {
|
|
239
|
+
...jsonData,
|
|
240
|
+
messages: [
|
|
241
|
+
{
|
|
242
|
+
content: cleanupResult.cleanedJson,
|
|
243
|
+
role: "assistant",
|
|
244
|
+
name: "assistant",
|
|
245
|
+
id: Date.now(),
|
|
246
|
+
tool_calls: [],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
} else {
|
|
251
|
+
// Always ensure messages have IDs
|
|
252
|
+
if (jsonData.messages) {
|
|
253
|
+
result = {
|
|
254
|
+
...jsonData,
|
|
255
|
+
messages: jsonData.messages.map((msg: any, index: number) => ({
|
|
256
|
+
...msg,
|
|
257
|
+
id: msg.id || Date.now() + index,
|
|
258
|
+
tool_calls: msg.tool_calls || [],
|
|
259
|
+
})),
|
|
260
|
+
};
|
|
261
|
+
} else {
|
|
262
|
+
result = jsonData;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
122
265
|
} catch (e) {
|
|
123
266
|
console.error("Error parsing the final buffer content:", e);
|
|
124
267
|
}
|
|
@@ -51,28 +51,27 @@ export async function generatePageContext(
|
|
|
51
51
|
role: "system",
|
|
52
52
|
content:
|
|
53
53
|
"You are a content summarization tool. You have access to a get-content function that can extract text content from the current page. Use this function to get the page content, then create a concise abstract in exactly 100 tokens or less. Focus on the main topics, purpose, and key information. Be specific and informative. Your summary will be fed into a text completion prompt, so make it as concise as possible.",
|
|
54
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
54
55
|
},
|
|
55
56
|
];
|
|
56
57
|
|
|
57
58
|
const result = await executePrompt(
|
|
58
59
|
messages,
|
|
59
|
-
|
|
60
|
-
() => ({
|
|
60
|
+
{
|
|
61
61
|
endpoint: "/alpaca/editor/ai/prompt",
|
|
62
62
|
promptData: {
|
|
63
63
|
itemid: editContext.contentEditorItem!.id,
|
|
64
64
|
language: editContext.contentEditorItem!.language,
|
|
65
65
|
version: editContext.contentEditorItem!.version,
|
|
66
66
|
},
|
|
67
|
-
|
|
68
|
-
}),
|
|
67
|
+
},
|
|
69
68
|
{
|
|
70
69
|
profile: "context-generation",
|
|
71
70
|
addContextContent: false,
|
|
72
71
|
addAllContent: false,
|
|
72
|
+
model: "gpt-4o-mini", // Use a fast, efficient model for context generation
|
|
73
|
+
allowedFunctions: ["get-content"],
|
|
73
74
|
},
|
|
74
|
-
undefined,
|
|
75
|
-
"gpt-4o-mini", // Use a fast, efficient model for context generation
|
|
76
75
|
);
|
|
77
76
|
|
|
78
77
|
console.log("result", result);
|
|
@@ -52,10 +52,6 @@ export function SidebarView({
|
|
|
52
52
|
);
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
if (!active) {
|
|
56
|
-
return <div className="hidden h-full" />;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
55
|
// Single panel - no splitter needed
|
|
60
56
|
if (resolvedPanels.length === 1) {
|
|
61
57
|
const panel = resolvedPanels[0];
|
|
@@ -63,7 +59,11 @@ export function SidebarView({
|
|
|
63
59
|
|
|
64
60
|
return (
|
|
65
61
|
<div
|
|
66
|
-
className={cn(
|
|
62
|
+
className={cn(
|
|
63
|
+
"h-full",
|
|
64
|
+
detached ? "p-2" : "border-gray-3 border-r",
|
|
65
|
+
!active ? "hidden" : ""
|
|
66
|
+
)}
|
|
67
67
|
>
|
|
68
68
|
<div
|
|
69
69
|
className={cn(
|
|
@@ -111,7 +111,11 @@ export function SidebarView({
|
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
|
-
<div className={cn(
|
|
114
|
+
<div className={cn(
|
|
115
|
+
"h-full",
|
|
116
|
+
detached ? "p-2" : "",
|
|
117
|
+
!active ? "hidden" : ""
|
|
118
|
+
)}>
|
|
115
119
|
<Splitter
|
|
116
120
|
panels={splitterPanels}
|
|
117
121
|
direction="vertical"
|
|
@@ -140,8 +140,8 @@ export function ViewSelector() {
|
|
|
140
140
|
<VerticalDotsIcon />
|
|
141
141
|
</Button>
|
|
142
142
|
</PopoverTrigger>
|
|
143
|
-
</TooltipTrigger>
|
|
144
|
-
<TooltipContent>More views</TooltipContent>
|
|
143
|
+
</TooltipTrigger >
|
|
144
|
+
<TooltipContent side="right">More views</TooltipContent>
|
|
145
145
|
</Tooltip>
|
|
146
146
|
<PopoverContent
|
|
147
147
|
className="w-56 p-2"
|
|
@@ -5,9 +5,22 @@ import {
|
|
|
5
5
|
TooltipTrigger,
|
|
6
6
|
} from "../../components/ui/tooltip";
|
|
7
7
|
import { cn } from "../../lib/utils";
|
|
8
|
-
import { MouseEventHandler } from "react";
|
|
8
|
+
import { MouseEventHandler, forwardRef } from "react";
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export const SimpleIconButton = forwardRef<
|
|
11
|
+
HTMLButtonElement,
|
|
12
|
+
{
|
|
13
|
+
onClick: MouseEventHandler;
|
|
14
|
+
className?: string;
|
|
15
|
+
icon?: React.ReactNode;
|
|
16
|
+
label: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
id?: string;
|
|
19
|
+
size?: "large" | "small";
|
|
20
|
+
selected?: boolean;
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
>(({
|
|
11
24
|
onClick,
|
|
12
25
|
className,
|
|
13
26
|
icon,
|
|
@@ -17,21 +30,12 @@ export function SimpleIconButton({
|
|
|
17
30
|
id,
|
|
18
31
|
selected,
|
|
19
32
|
children,
|
|
20
|
-
}
|
|
21
|
-
onClick: MouseEventHandler;
|
|
22
|
-
className?: string;
|
|
23
|
-
icon?: React.ReactNode;
|
|
24
|
-
label: string;
|
|
25
|
-
disabled?: boolean;
|
|
26
|
-
id?: string;
|
|
27
|
-
size?: "large" | "small";
|
|
28
|
-
selected?: boolean;
|
|
29
|
-
children?: React.ReactNode;
|
|
30
|
-
}) {
|
|
33
|
+
}, ref) => {
|
|
31
34
|
return (
|
|
32
35
|
<Tooltip>
|
|
33
36
|
<TooltipTrigger asChild>
|
|
34
37
|
<button
|
|
38
|
+
ref={ref}
|
|
35
39
|
id={id}
|
|
36
40
|
disabled={disabled}
|
|
37
41
|
className={cn(
|
|
@@ -54,4 +58,6 @@ export function SimpleIconButton({
|
|
|
54
58
|
<TooltipContent>{label}</TooltipContent>
|
|
55
59
|
</Tooltip>
|
|
56
60
|
);
|
|
57
|
-
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
SimpleIconButton.displayName = "SimpleIconButton";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface JsonCleanupResult {
|
|
2
|
+
cleanedJson: string;
|
|
3
|
+
isComplete: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class JsonCleaner {
|
|
7
|
+
public static cleanupJson(json: string): JsonCleanupResult {
|
|
8
|
+
// Handle dangling commas
|
|
9
|
+
json = json.replace(/,}/g, "}");
|
|
10
|
+
json = json.replace(/,]/g, "]");
|
|
11
|
+
|
|
12
|
+
const stringResult = this.correctUnendedStrings(json);
|
|
13
|
+
json = stringResult.correctedJson;
|
|
14
|
+
let isComplete = stringResult.isComplete;
|
|
15
|
+
|
|
16
|
+
// Process the JSON string character by character
|
|
17
|
+
let isInString = false;
|
|
18
|
+
let isEscape = false;
|
|
19
|
+
const openStack: string[] = [];
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < json.length; i++) {
|
|
22
|
+
const c = json[i];
|
|
23
|
+
|
|
24
|
+
if (c === '"' && !isEscape) {
|
|
25
|
+
isInString = !isInString;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isInString) {
|
|
29
|
+
switch (c) {
|
|
30
|
+
case '{':
|
|
31
|
+
openStack.push(c);
|
|
32
|
+
break;
|
|
33
|
+
case '}':
|
|
34
|
+
if (openStack.length > 0) {
|
|
35
|
+
openStack.pop();
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case '[':
|
|
39
|
+
openStack.push(c);
|
|
40
|
+
break;
|
|
41
|
+
case ']':
|
|
42
|
+
if (openStack.length > 0) {
|
|
43
|
+
openStack.pop();
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isEscape = isInString && c === '\\' && !isEscape;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Append missing closing brackets
|
|
53
|
+
while (openStack.length > 0) {
|
|
54
|
+
const open = openStack.pop();
|
|
55
|
+
if (open === '{') {
|
|
56
|
+
json += "}";
|
|
57
|
+
} else if (open === '[') {
|
|
58
|
+
json += "]";
|
|
59
|
+
}
|
|
60
|
+
isComplete = false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
cleanedJson: json,
|
|
65
|
+
isComplete: isComplete
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private static correctUnendedStrings(json: string): { correctedJson: string; isComplete: boolean } {
|
|
70
|
+
let isComplete = true;
|
|
71
|
+
let isInString = false;
|
|
72
|
+
let isEscape = false;
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < json.length; i++) {
|
|
75
|
+
if (json[i] === '"' && !isEscape) {
|
|
76
|
+
isInString = !isInString;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
isEscape = isInString && json[i] === '\\' && !isEscape;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (isInString) {
|
|
83
|
+
json += '[...]"';
|
|
84
|
+
isComplete = false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
correctedJson: json,
|
|
89
|
+
isComplete: isComplete
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -115,7 +115,7 @@ export function CompareView() {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const originalSelector = (
|
|
118
|
-
<div className="flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
118
|
+
<div className=" gap-1 flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
119
119
|
<LanguageSelector
|
|
120
120
|
selectedLanguage={editContext.contentEditorItem?.descriptor?.language}
|
|
121
121
|
showAllLanguagesSwitch={false}
|
|
@@ -149,7 +149,7 @@ export function CompareView() {
|
|
|
149
149
|
);
|
|
150
150
|
|
|
151
151
|
const compareToSelector = (
|
|
152
|
-
<div className="flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
152
|
+
<div className="gap-1 flex flex-1 flex-shrink-0 items-center justify-center border-b bg-white p-1 text-sm text-gray-600">
|
|
153
153
|
<LanguageSelector
|
|
154
154
|
selectedLanguage={compareTo?.language}
|
|
155
155
|
showAllLanguagesSwitch={false}
|
|
@@ -333,13 +333,12 @@ export function ContentStep({
|
|
|
333
333
|
Input data: ${JSON.stringify(data)}`,
|
|
334
334
|
name: "system",
|
|
335
335
|
role: "system",
|
|
336
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
336
337
|
},
|
|
337
338
|
],
|
|
338
|
-
editContext,
|
|
339
|
-
|
|
340
|
-
{},
|
|
339
|
+
{ editContext, createAiContext: createWizardAiContext },
|
|
340
|
+
{ model: "o4-mini-high" },
|
|
341
341
|
{ signal: abortController.signal },
|
|
342
|
-
"o4-mini-high",
|
|
343
342
|
(response) => {
|
|
344
343
|
try {
|
|
345
344
|
const newLayout = JSON.parse(response.content) as WizardPageModel;
|
|
@@ -592,6 +591,7 @@ export function ContentStep({
|
|
|
592
591
|
|
|
593
592
|
name: "system",
|
|
594
593
|
role: "system",
|
|
594
|
+
id: crypto.randomUUID(), // Add required id property
|
|
595
595
|
},
|
|
596
596
|
];
|
|
597
597
|
|
|
@@ -603,12 +603,13 @@ export function ContentStep({
|
|
|
603
603
|
|
|
604
604
|
const result = await executePrompt(
|
|
605
605
|
prompt,
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
606
|
+
{
|
|
607
|
+
editContext: editContextRef.current!,
|
|
608
|
+
createAiContext: createWizardAiContext,
|
|
609
|
+
},
|
|
610
|
+
{ allowedFunctions: [], model: step.fields.aiModel || "gpt-4.1" },
|
|
609
611
|
{ signal: localAbortController.signal },
|
|
610
|
-
|
|
611
|
-
(response) => {
|
|
612
|
+
(response: any) => {
|
|
612
613
|
try {
|
|
613
614
|
// Handle streaming response - might have different structure during streaming
|
|
614
615
|
const content =
|
|
@@ -216,24 +216,26 @@ Return a JSON object with suggested item IDs in this format:
|
|
|
216
216
|
);
|
|
217
217
|
|
|
218
218
|
try {
|
|
219
|
+
const localAbortController = new AbortController();
|
|
220
|
+
|
|
219
221
|
const result = await executePrompt(
|
|
220
222
|
[
|
|
221
223
|
{
|
|
222
224
|
content: `You are an expert content curator helping to find relevant items from a content tree. You can traverse the tree using the get-children function. Return ONLY a valid JSON object with suggestions and reasoning.`,
|
|
223
225
|
name: "system",
|
|
224
226
|
role: "system",
|
|
227
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
225
228
|
},
|
|
226
229
|
{
|
|
227
230
|
content: basePrompt,
|
|
228
231
|
name: "user",
|
|
229
232
|
role: "user",
|
|
233
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
230
234
|
},
|
|
231
235
|
],
|
|
232
|
-
editContext,
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
undefined,
|
|
236
|
-
step.fields.aiModel || "gpt-4.1",
|
|
236
|
+
{ editContext, createAiContext: createWizardAiContext },
|
|
237
|
+
{ model: step.fields.aiModel || "gpt-4.1" },
|
|
238
|
+
{ signal: localAbortController.signal },
|
|
237
239
|
);
|
|
238
240
|
|
|
239
241
|
console.log("AI RESULT: ", result);
|
|
@@ -98,25 +98,30 @@ export const ImagesStep = (props: StepComponentProps) => {
|
|
|
98
98
|
}));
|
|
99
99
|
|
|
100
100
|
try {
|
|
101
|
-
|
|
101
|
+
const localAbortController = new AbortController();
|
|
102
|
+
const imageInstructions =
|
|
103
|
+
props.step.fields.instructions ||
|
|
104
|
+
"Please analyze the provided data and suggest images.";
|
|
105
|
+
|
|
102
106
|
const aiPromptResult = await executePrompt(
|
|
103
107
|
[
|
|
104
108
|
{
|
|
105
|
-
content: `You are an AI assistant for building a web page. You will later have to create images for the page.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Instructions: ${props.step.fields.instructions}`,
|
|
109
|
+
content: `You are an AI assistant for building a web page. You will later have to create images for the page.
|
|
110
|
+
${imageInstructions}
|
|
111
|
+
|
|
112
|
+
Reply with a json object of type WizardPageModel = { message: string; components: Component[]; };
|
|
110
113
|
|
|
114
|
+
Where components are of type Component = { id: string; name: string; imageFields: {name: string; value: string; alt: string; prompt: string; }[]; };
|
|
115
|
+
You should update images existing images in fields in the components array. Update the prompt field for each image, keep name, value and alt unchanged. Make sure you populate prompt field for every image.
|
|
116
|
+
`,
|
|
111
117
|
name: "system",
|
|
112
118
|
role: "system",
|
|
119
|
+
id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
|
|
113
120
|
},
|
|
114
121
|
],
|
|
115
|
-
editContext!,
|
|
116
|
-
|
|
117
|
-
{},
|
|
118
|
-
undefined,
|
|
119
|
-
"gpt-4.1",
|
|
122
|
+
{ editContext: editContext!, createAiContext: createWizardAiContext },
|
|
123
|
+
{ model: "gpt-4.1" },
|
|
124
|
+
{ signal: localAbortController.signal },
|
|
120
125
|
);
|
|
121
126
|
|
|
122
127
|
let images: Image[] = [];
|