@alpaca-editor/core 1.0.4174 → 1.0.4177
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/agents-view/AgentsView.js +0 -20
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/editor/QuickItemSwitcher.js +5 -1
- package/dist/editor/QuickItemSwitcher.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +47 -83
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +7 -5
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +1 -1
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.js +30 -64
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/useAgentStatus.js +81 -2
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/client/EditorShell.js +44 -7
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/operations.js +30 -3
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/control-center/WebSocketMessages.js +0 -1
- package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +14 -3
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +43 -32
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +8 -4
- package/dist/editor/services/agentService.js +30 -53
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +19 -1
- package/dist/editor/services/aiService.js +78 -2
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +35 -57
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +14 -23
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +12 -42
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +0 -8
- package/package.json +1 -1
- package/src/agents-view/AgentsView.tsx +1 -21
- package/src/editor/QuickItemSwitcher.tsx +23 -19
- package/src/editor/ai/AgentTerminal.tsx +83 -142
- package/src/editor/ai/Agents.tsx +9 -6
- package/src/editor/ai/AiResponseMessage.tsx +5 -1
- package/src/editor/ai/ContextInfoBar.tsx +34 -75
- package/src/editor/ai/useAgentStatus.ts +82 -4
- package/src/editor/client/EditorShell.tsx +62 -9
- package/src/editor/client/operations.ts +42 -4
- package/src/editor/control-center/WebSocketMessages.tsx +5 -1
- package/src/editor/page-viewer/PageViewerFrame.tsx +15 -2
- package/src/editor/reviews/commentAi.ts +48 -36
- package/src/editor/services/agentService.ts +38 -55
- package/src/editor/services/aiService.ts +110 -3
- package/src/page-wizard/steps/ContentStep.tsx +51 -81
- package/src/page-wizard/steps/MetaDataStep.tsx +52 -60
- package/src/page-wizard/steps/SelectStep.tsx +13 -51
- package/src/revision.ts +2 -2
|
@@ -169,12 +169,16 @@ export type AgentContextData = {
|
|
|
169
169
|
name?: string;
|
|
170
170
|
};
|
|
171
171
|
}>;
|
|
172
|
-
/** @deprecated Use components instead to get page information per component */
|
|
173
|
-
componentIds?: string[];
|
|
174
172
|
field?: {
|
|
175
173
|
fieldId: string;
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
fieldName?: string;
|
|
175
|
+
item?: {
|
|
176
|
+
id: string;
|
|
177
|
+
language: string;
|
|
178
|
+
version: number;
|
|
179
|
+
path?: string;
|
|
180
|
+
name?: string;
|
|
181
|
+
};
|
|
178
182
|
};
|
|
179
183
|
comment?: {
|
|
180
184
|
id: string;
|
|
@@ -842,71 +846,63 @@ export function canonicalizeAgentMetadata(
|
|
|
842
846
|
}
|
|
843
847
|
}
|
|
844
848
|
|
|
845
|
-
// Process
|
|
849
|
+
// Process items
|
|
846
850
|
const allPages: any[] = [];
|
|
847
|
-
if (Array.isArray(m.pages)) allPages.push(...m.pages); // legacy
|
|
848
851
|
if (Array.isArray(m.items)) allPages.push(...m.items);
|
|
849
852
|
for (const ctx of contextSources) {
|
|
850
|
-
if (Array.isArray(ctx.pages)) allPages.push(...ctx.pages); // legacy
|
|
851
853
|
if (Array.isArray(ctx.items)) allPages.push(...ctx.items);
|
|
852
854
|
}
|
|
853
855
|
if (allPages.length > 0) {
|
|
854
856
|
const dedup: any[] = [];
|
|
855
857
|
const seen = new Set<string>();
|
|
856
858
|
for (const p of allPages) {
|
|
857
|
-
const id = p?.id
|
|
859
|
+
const id = p?.id;
|
|
858
860
|
if (!id) continue;
|
|
859
|
-
const lang = (p?.language ??
|
|
860
|
-
const ver = (p?.version ??
|
|
861
|
+
const lang = (p?.language ?? "").toString();
|
|
862
|
+
const ver = (p?.version ?? "").toString();
|
|
861
863
|
const key = `${String(id)}-${lang}-${ver}`.toLowerCase();
|
|
862
864
|
if (seen.has(key)) continue;
|
|
863
865
|
seen.add(key);
|
|
864
866
|
dedup.push({
|
|
865
867
|
id: String(id),
|
|
866
|
-
language: p?.language
|
|
867
|
-
version: p?.version
|
|
868
|
-
name: p?.name
|
|
869
|
-
path: p?.path
|
|
868
|
+
language: p?.language,
|
|
869
|
+
version: p?.version,
|
|
870
|
+
name: p?.name,
|
|
871
|
+
path: p?.path,
|
|
870
872
|
});
|
|
871
873
|
}
|
|
872
874
|
m.items = dedup;
|
|
873
875
|
} else {
|
|
874
|
-
// Clear both if no items
|
|
875
876
|
delete m.items;
|
|
876
877
|
}
|
|
877
878
|
|
|
878
|
-
//
|
|
879
|
-
delete m.pages;
|
|
880
|
-
|
|
881
|
-
// Process components with page info (new structure)
|
|
879
|
+
// Process components with page info
|
|
882
880
|
const allComponents: any[] = [];
|
|
883
881
|
if (Array.isArray(m.components)) allComponents.push(...m.components);
|
|
884
|
-
if (Array.isArray(m.Components)) allComponents.push(...m.Components);
|
|
885
882
|
for (const ctx of contextSources) {
|
|
886
883
|
if (Array.isArray(ctx.components)) allComponents.push(...ctx.components);
|
|
887
|
-
if (Array.isArray(ctx.Components)) allComponents.push(...ctx.Components);
|
|
888
884
|
}
|
|
889
885
|
if (allComponents.length > 0) {
|
|
890
886
|
const dedup: any[] = [];
|
|
891
887
|
const seen = new Set<string>();
|
|
892
888
|
for (const c of allComponents) {
|
|
893
889
|
if (!c) continue;
|
|
894
|
-
const componentId = c.componentId
|
|
890
|
+
const componentId = c.componentId;
|
|
895
891
|
if (!componentId || typeof componentId !== "string") continue;
|
|
896
892
|
const key = componentId.toLowerCase();
|
|
897
893
|
if (seen.has(key)) continue;
|
|
898
894
|
seen.add(key);
|
|
899
895
|
|
|
900
|
-
const pageItem = c.pageItem
|
|
896
|
+
const pageItem = c.pageItem;
|
|
901
897
|
dedup.push({
|
|
902
898
|
componentId: String(componentId),
|
|
903
899
|
pageItem: pageItem
|
|
904
900
|
? {
|
|
905
|
-
id: String(pageItem.id ??
|
|
906
|
-
language: pageItem.language
|
|
907
|
-
version: pageItem.version
|
|
908
|
-
name: pageItem.name
|
|
909
|
-
path: pageItem.path
|
|
901
|
+
id: String(pageItem.id ?? ""),
|
|
902
|
+
language: pageItem.language,
|
|
903
|
+
version: pageItem.version,
|
|
904
|
+
name: pageItem.name,
|
|
905
|
+
path: pageItem.path,
|
|
910
906
|
}
|
|
911
907
|
: undefined,
|
|
912
908
|
});
|
|
@@ -914,35 +910,25 @@ export function canonicalizeAgentMetadata(
|
|
|
914
910
|
m.components = dedup;
|
|
915
911
|
}
|
|
916
912
|
|
|
917
|
-
// Process
|
|
918
|
-
const allComponentIds: any[] = [];
|
|
919
|
-
if (Array.isArray(m.componentIds)) allComponentIds.push(...m.componentIds);
|
|
913
|
+
// Process field - merge/complete from context sources if needed
|
|
920
914
|
for (const ctx of contextSources) {
|
|
921
|
-
if (
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
if (allComponentIds.length > 0) {
|
|
925
|
-
const ids = allComponentIds
|
|
926
|
-
.map((x) => (typeof x === "string" ? x : (x?.id ?? x?.Id)))
|
|
927
|
-
.filter((x): x is string => !!x && typeof x === "string");
|
|
928
|
-
m.componentIds = Array.from(new Set(ids));
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Process field
|
|
932
|
-
if (!m.field) {
|
|
933
|
-
for (const ctx of contextSources) {
|
|
934
|
-
if (ctx.field) {
|
|
915
|
+
if (ctx.field) {
|
|
916
|
+
// If field doesn't exist, create it from context
|
|
917
|
+
if (!m.field) {
|
|
935
918
|
m.field = {
|
|
936
|
-
fieldId:
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
ctx.field.id ??
|
|
940
|
-
ctx.field.Id,
|
|
941
|
-
itemId: ctx.field.itemId ?? ctx.field.ItemId,
|
|
942
|
-
name: ctx.field.name ?? ctx.field.Name,
|
|
919
|
+
fieldId: ctx.field.fieldId,
|
|
920
|
+
fieldName: ctx.field.fieldName,
|
|
921
|
+
item: ctx.field.item,
|
|
943
922
|
};
|
|
944
923
|
break;
|
|
945
924
|
}
|
|
925
|
+
// If field exists but is incomplete, fill in missing properties
|
|
926
|
+
if (m.field && !m.field.item && ctx.field.item) {
|
|
927
|
+
m.field.item = ctx.field.item;
|
|
928
|
+
}
|
|
929
|
+
if (m.field && !m.field.fieldName && ctx.field.fieldName) {
|
|
930
|
+
m.field.fieldName = ctx.field.fieldName;
|
|
931
|
+
}
|
|
946
932
|
}
|
|
947
933
|
}
|
|
948
934
|
|
|
@@ -967,9 +953,6 @@ export function canonicalizeAgentMetadata(
|
|
|
967
953
|
}
|
|
968
954
|
|
|
969
955
|
m.additionalData = Object.keys(additional).length ? additional : undefined;
|
|
970
|
-
if (Object.prototype.hasOwnProperty.call(m, "AdditionalData")) {
|
|
971
|
-
delete m.AdditionalData;
|
|
972
|
-
}
|
|
973
956
|
|
|
974
957
|
return m as AgentContextData;
|
|
975
958
|
}
|
|
@@ -86,7 +86,8 @@ type Message = {
|
|
|
86
86
|
|
|
87
87
|
export interface ExecutePromptResponse {
|
|
88
88
|
editOperations: any[];
|
|
89
|
-
|
|
89
|
+
content: string;
|
|
90
|
+
messages?: Message[];
|
|
90
91
|
numInputTokens: number;
|
|
91
92
|
numOutputTokens: number;
|
|
92
93
|
numCachedTokens: number;
|
|
@@ -184,23 +185,25 @@ export async function executePrompt(
|
|
|
184
185
|
};
|
|
185
186
|
|
|
186
187
|
// Use migrated endpoint under agent controller
|
|
187
|
-
const endpoint = options.endpoint || "/alpaca/editor/
|
|
188
|
+
const endpoint = options.endpoint || "/alpaca/editor/page-wizard/prompt";
|
|
188
189
|
|
|
189
190
|
const response = await fetch(endpoint, finalRequestOptions);
|
|
190
191
|
|
|
191
192
|
if (!response.ok) {
|
|
192
193
|
// Always return rich error format
|
|
193
194
|
const text = await response.text();
|
|
195
|
+
const errorContent = "There was an error processing your request: " + text;
|
|
194
196
|
return {
|
|
195
197
|
messages: [
|
|
196
198
|
{
|
|
197
|
-
content:
|
|
199
|
+
content: errorContent,
|
|
198
200
|
name: "assistant",
|
|
199
201
|
role: "assistant",
|
|
200
202
|
id: crypto.randomUUID(),
|
|
201
203
|
tool_calls: [],
|
|
202
204
|
},
|
|
203
205
|
],
|
|
206
|
+
content: errorContent,
|
|
204
207
|
editOperations: [],
|
|
205
208
|
numInputTokens: 0,
|
|
206
209
|
numOutputTokens: 0,
|
|
@@ -330,6 +333,110 @@ export async function executePrompt(
|
|
|
330
333
|
return result;
|
|
331
334
|
}
|
|
332
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Helper function to parse JSON content from AI response
|
|
338
|
+
* Strips markdown code blocks if present and parses the JSON
|
|
339
|
+
* Handles incomplete markdown blocks during streaming
|
|
340
|
+
*/
|
|
341
|
+
function parseJsonContent<T>(content: string): T {
|
|
342
|
+
let cleanedContent = content;
|
|
343
|
+
|
|
344
|
+
// Strip markdown code blocks if present
|
|
345
|
+
if (cleanedContent.startsWith("```json")) {
|
|
346
|
+
// Remove the opening ```json
|
|
347
|
+
cleanedContent = cleanedContent.substring(7).trim();
|
|
348
|
+
// Remove the closing ``` if present
|
|
349
|
+
if (cleanedContent.endsWith("```")) {
|
|
350
|
+
cleanedContent = cleanedContent
|
|
351
|
+
.substring(0, cleanedContent.length - 3)
|
|
352
|
+
.trim();
|
|
353
|
+
}
|
|
354
|
+
} else if (cleanedContent.startsWith("```")) {
|
|
355
|
+
// Remove the opening ```
|
|
356
|
+
cleanedContent = cleanedContent.substring(3).trim();
|
|
357
|
+
// Remove the closing ``` if present
|
|
358
|
+
if (cleanedContent.endsWith("```")) {
|
|
359
|
+
cleanedContent = cleanedContent
|
|
360
|
+
.substring(0, cleanedContent.length - 3)
|
|
361
|
+
.trim();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Replace JavaScript undefined with JSON null
|
|
366
|
+
// Pattern matches: undefined as a value (not inside strings)
|
|
367
|
+
cleanedContent = cleanedContent.replace(
|
|
368
|
+
/:\s*undefined\s*([,\}])/g,
|
|
369
|
+
": null$1",
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Content is already cleaned by executePrompt for streaming responses
|
|
373
|
+
// and by backend for non-streaming responses, so just parse it
|
|
374
|
+
return JSON.parse(cleanedContent) as T;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Executes a prompt and returns the parsed JSON result
|
|
379
|
+
* @param messages - The messages to send
|
|
380
|
+
* @param context - The AI context
|
|
381
|
+
* @param options - Execution options
|
|
382
|
+
* @param requestOptions - Additional request options
|
|
383
|
+
* @param callback - Optional callback for streaming responses
|
|
384
|
+
* @param stream - Whether to stream the response
|
|
385
|
+
* @returns The parsed JSON object from the response content
|
|
386
|
+
* @throws Error if the response cannot be parsed or is not in the expected format
|
|
387
|
+
*/
|
|
388
|
+
export async function executePromptWithJsonResult<T = any>(
|
|
389
|
+
messages: Message[],
|
|
390
|
+
context:
|
|
391
|
+
| AiContext
|
|
392
|
+
| {
|
|
393
|
+
editContext: EditContextType;
|
|
394
|
+
createAiContext: ({ editContext }: { editContext: any }) => AiContext;
|
|
395
|
+
},
|
|
396
|
+
options: ExecutePromptOptions = {},
|
|
397
|
+
requestOptions?: RequestInit,
|
|
398
|
+
callback?: (parsedJson: T) => void,
|
|
399
|
+
stream?: boolean,
|
|
400
|
+
): Promise<T> {
|
|
401
|
+
// Wrap the callback to parse JSON before passing to the original callback
|
|
402
|
+
const wrappedCallback = callback
|
|
403
|
+
? (response: any) => {
|
|
404
|
+
if (!response || !response.content) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
const parsed = parseJsonContent<T>(response.content);
|
|
410
|
+
callback(parsed);
|
|
411
|
+
} catch (parseError) {
|
|
412
|
+
// During streaming, JSON may still be incomplete even after cleaning
|
|
413
|
+
// Only invoke callback when we have valid parseable JSON
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
: undefined;
|
|
417
|
+
|
|
418
|
+
const result = await executePrompt(
|
|
419
|
+
messages,
|
|
420
|
+
context,
|
|
421
|
+
options,
|
|
422
|
+
requestOptions,
|
|
423
|
+
wrappedCallback,
|
|
424
|
+
stream,
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
if (!result || !result.content) {
|
|
428
|
+
throw new Error("No content in response");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
return parseJsonContent<T>(result.content);
|
|
433
|
+
} catch (parseError) {
|
|
434
|
+
throw new Error(
|
|
435
|
+
`Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
333
440
|
// moved to searchService.ts
|
|
334
441
|
|
|
335
442
|
export async function generateImage(
|
|
@@ -21,7 +21,7 @@ import { convertPageSchemaToWizardComponents } from "./schema";
|
|
|
21
21
|
import { WizardPageModel } from "../PageWizard";
|
|
22
22
|
import { createWizardAiContext, wipeComponents } from "../service";
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import { executePromptWithJsonResult } from "../../editor/services/aiService";
|
|
25
25
|
import { usePageCreator } from "./usePageCreator";
|
|
26
26
|
import { useThrottledCallback } from "use-debounce";
|
|
27
27
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -152,7 +152,10 @@ export function ContentStep({
|
|
|
152
152
|
|
|
153
153
|
console.log("PROMPT (selectTargetItem):", promptContent);
|
|
154
154
|
|
|
155
|
-
const result = await
|
|
155
|
+
const result = await executePromptWithJsonResult<{
|
|
156
|
+
id: string;
|
|
157
|
+
path: string;
|
|
158
|
+
}>(
|
|
156
159
|
[
|
|
157
160
|
{
|
|
158
161
|
content: promptContent,
|
|
@@ -169,16 +172,11 @@ export function ContentStep({
|
|
|
169
172
|
{ signal: abortController.signal },
|
|
170
173
|
(response) => {
|
|
171
174
|
try {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
path: string;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
if (folderResult.id && folderResult.path) {
|
|
178
|
-
setFolderSelectionResult(folderResult);
|
|
175
|
+
if (response.id && response.path) {
|
|
176
|
+
setFolderSelectionResult(response);
|
|
179
177
|
// Update target folder with the selected folder
|
|
180
178
|
setTargetFolder({
|
|
181
|
-
id:
|
|
179
|
+
id: response.id,
|
|
182
180
|
language: language,
|
|
183
181
|
} as ItemDescriptor);
|
|
184
182
|
}
|
|
@@ -186,23 +184,13 @@ export function ContentStep({
|
|
|
186
184
|
},
|
|
187
185
|
);
|
|
188
186
|
|
|
189
|
-
if (result && result.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (folderResult.id && folderResult.path) {
|
|
198
|
-
setFolderSelectionResult(folderResult);
|
|
199
|
-
// Update target folder with the selected folder
|
|
200
|
-
setTargetFolder({
|
|
201
|
-
id: folderResult.id,
|
|
202
|
-
language: language,
|
|
203
|
-
} as ItemDescriptor);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
187
|
+
if (result && result.id && result.path) {
|
|
188
|
+
setFolderSelectionResult(result);
|
|
189
|
+
// Update target folder with the selected folder
|
|
190
|
+
setTargetFolder({
|
|
191
|
+
id: result.id,
|
|
192
|
+
language: language,
|
|
193
|
+
} as ItemDescriptor);
|
|
206
194
|
}
|
|
207
195
|
} catch (error) {
|
|
208
196
|
console.error("Error selecting target folder", error);
|
|
@@ -288,38 +276,30 @@ export function ContentStep({
|
|
|
288
276
|
|
|
289
277
|
console.log("PROMPT (generatePageName):", promptContent);
|
|
290
278
|
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
279
|
+
const pageModelResult =
|
|
280
|
+
await executePromptWithJsonResult<WizardPageModel>(
|
|
281
|
+
[
|
|
282
|
+
{
|
|
283
|
+
content: `${processedInstructions?.trim()} Reply with a json object of type PageModel = { name: string; };
|
|
284
|
+
The language of the page is ${language}.`,
|
|
285
|
+
name: "system",
|
|
286
|
+
role: "system",
|
|
287
|
+
id: crypto.randomUUID(),
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
content: promptContent,
|
|
291
|
+
name: "user",
|
|
292
|
+
role: "user",
|
|
293
|
+
id: crypto.randomUUID(),
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
{ editContext, createAiContext: createWizardAiContext },
|
|
297
|
+
{ model: step.fields.aiModel || undefined },
|
|
298
|
+
{ signal: abortController.signal },
|
|
299
|
+
);
|
|
312
300
|
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
if (lastMessage && lastMessage.content) {
|
|
316
|
-
const pageModelResult = JSON.parse(
|
|
317
|
-
lastMessage.content,
|
|
318
|
-
) as WizardPageModel;
|
|
319
|
-
if (pageModelResult?.name) {
|
|
320
|
-
setPageModel((prev) => ({ ...prev, name: pageModelResult.name }));
|
|
321
|
-
}
|
|
322
|
-
}
|
|
301
|
+
if (pageModelResult && pageModelResult.name) {
|
|
302
|
+
setPageModel((prev) => ({ ...prev, name: pageModelResult.name }));
|
|
323
303
|
}
|
|
324
304
|
} catch (error) {
|
|
325
305
|
console.error("Error generating page name", error);
|
|
@@ -752,6 +732,13 @@ export function ContentStep({
|
|
|
752
732
|
|
|
753
733
|
pageLoadedRef.current = false;
|
|
754
734
|
|
|
735
|
+
// Reload the current page item to ensure we stay on the same page
|
|
736
|
+
if (currentPageItem) {
|
|
737
|
+
editContextRef.current?.loadItem(getItemDescriptor(currentPageItem), {
|
|
738
|
+
addToBrowseHistory: false,
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
|
|
755
742
|
editContextRef.current?.requestRefresh("immediate");
|
|
756
743
|
await waitForPageLoaded();
|
|
757
744
|
console.log("Page loaded after wipe");
|
|
@@ -838,7 +825,7 @@ export function ContentStep({
|
|
|
838
825
|
|
|
839
826
|
console.log("PROMPT: ", prompt, filteredSchema, existingPageModel);
|
|
840
827
|
|
|
841
|
-
const result = await
|
|
828
|
+
const result = await executePromptWithJsonResult<WizardPageModel>(
|
|
842
829
|
prompt,
|
|
843
830
|
{
|
|
844
831
|
editContext: editContextRef.current!,
|
|
@@ -850,33 +837,16 @@ export function ContentStep({
|
|
|
850
837
|
endpoint: "/alpaca/editor/page-wizard/prompt",
|
|
851
838
|
},
|
|
852
839
|
{ signal: localAbortController.signal },
|
|
853
|
-
(response:
|
|
840
|
+
(response: WizardPageModel) => {
|
|
854
841
|
try {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
response.content ||
|
|
858
|
-
response.messages?.[response.messages.length - 1]?.content;
|
|
859
|
-
if (content) {
|
|
860
|
-
const newLayout = JSON.parse(content) as WizardPageModel;
|
|
861
|
-
|
|
862
|
-
if (newLayout) {
|
|
863
|
-
setPageModel((prev) => mergeLayout(prev, newLayout));
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
setMessage(newLayout.message);
|
|
867
|
-
}
|
|
842
|
+
setPageModel((prev) => mergeLayout(prev, response));
|
|
843
|
+
setMessage(response.message);
|
|
868
844
|
} catch (parseError: unknown) {}
|
|
869
845
|
},
|
|
870
846
|
);
|
|
871
847
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
if (lastMessage && lastMessage.content) {
|
|
875
|
-
const finalLayout = JSON.parse(lastMessage.content);
|
|
876
|
-
console.log("RESULT LAYOUT: ", finalLayout);
|
|
877
|
-
setPageModel((prev) => mergeLayout(prev, finalLayout));
|
|
878
|
-
}
|
|
879
|
-
}
|
|
848
|
+
console.log("RESULT LAYOUT: ", result);
|
|
849
|
+
setPageModel((prev) => mergeLayout(prev, result));
|
|
880
850
|
setStepCompleted(true);
|
|
881
851
|
} catch (error) {
|
|
882
852
|
console.error(error);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import { FullItem } from "../../editor/pageModel";
|
|
3
3
|
import { WizardPageModel } from "../PageWizard";
|
|
4
|
-
import {
|
|
4
|
+
import { executePromptWithJsonResult } from "../../editor/services/aiService";
|
|
5
5
|
import { createWizardAiContext } from "../service";
|
|
6
6
|
import { useEditContext } from "../../editor/client/editContext";
|
|
7
7
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -24,7 +24,6 @@ export function MetaDataStep({
|
|
|
24
24
|
setStepCompleted,
|
|
25
25
|
}: StepComponentProps) {
|
|
26
26
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
27
|
-
const [fullParentItem, setFullParentItem] = useState<FullItem>();
|
|
28
27
|
const editContext = useEditContext();
|
|
29
28
|
|
|
30
29
|
useEffect(() => {
|
|
@@ -35,15 +34,6 @@ export function MetaDataStep({
|
|
|
35
34
|
}
|
|
36
35
|
}, [editContext]);
|
|
37
36
|
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
const loadParentItem = async () => {
|
|
40
|
-
if (!parentItem) return;
|
|
41
|
-
const item = await editContext?.itemsRepository.getItem(parentItem);
|
|
42
|
-
setFullParentItem(item);
|
|
43
|
-
};
|
|
44
|
-
loadParentItem();
|
|
45
|
-
}, [parentItem]);
|
|
46
|
-
|
|
47
37
|
// Mark step as completed immediately since this is now just informational
|
|
48
38
|
useEffect(() => {
|
|
49
39
|
setStepCompleted(true);
|
|
@@ -87,56 +77,58 @@ export function MetaDataStep({
|
|
|
87
77
|
try {
|
|
88
78
|
const abortController = new AbortController();
|
|
89
79
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
setPageModel
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
80
|
+
const pageModelResult =
|
|
81
|
+
await executePromptWithJsonResult<WizardPageModel>(
|
|
82
|
+
[
|
|
83
|
+
{
|
|
84
|
+
content: `${processedSystemInstructions}\nYou are a helpful assistant that generates SEO metadata for a page.\n\nReturn ONLY valid JSON in this exact shape: {"metaDescription": string, "metaKeywords": string}.\nLanguage: ${parentItem?.language || "en"}.`,
|
|
85
|
+
name: "system",
|
|
86
|
+
role: "system",
|
|
87
|
+
id: crypto.randomUUID(),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
content: `${processedInstructions}\n\nCurrent data: ${JSON.stringify(
|
|
91
|
+
inputData,
|
|
92
|
+
null,
|
|
93
|
+
2,
|
|
94
|
+
)}`,
|
|
95
|
+
name: "user",
|
|
96
|
+
role: "user",
|
|
97
|
+
id: crypto.randomUUID(),
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
{ editContext, createAiContext: createWizardAiContext },
|
|
101
|
+
{ model: step.fields.aiModel || undefined },
|
|
102
|
+
{ signal: abortController.signal },
|
|
103
|
+
(response: any) => {
|
|
104
|
+
try {
|
|
105
|
+
const newLayout = JSON.parse(response.content) as WizardPageModel;
|
|
106
|
+
if (newLayout && setPageModel) {
|
|
107
|
+
setPageModel((prev) => ({
|
|
108
|
+
...prev,
|
|
109
|
+
metaDescription:
|
|
110
|
+
(newLayout as any)?.metaDescription ?? prev.metaDescription,
|
|
111
|
+
metaKeywords:
|
|
112
|
+
(newLayout as any)?.metaKeywords ?? prev.metaKeywords,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
// Ignore parsing errors during streaming
|
|
123
117
|
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}));
|
|
139
|
-
}
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// Parse the final result
|
|
122
|
+
if (
|
|
123
|
+
pageModelResult &&
|
|
124
|
+
pageModelResult.metaDescription &&
|
|
125
|
+
pageModelResult.metaKeywords
|
|
126
|
+
) {
|
|
127
|
+
setPageModel((prev) => ({
|
|
128
|
+
...prev,
|
|
129
|
+
metaDescription: pageModelResult.metaDescription,
|
|
130
|
+
metaKeywords: pageModelResult.metaKeywords,
|
|
131
|
+
}));
|
|
140
132
|
}
|
|
141
133
|
} catch (error) {
|
|
142
134
|
console.error("Error generating meta fields", error);
|