@alpaca-editor/core 1.0.4018 → 1.0.4026

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 (173) hide show
  1. package/dist/components/SimpleLanguageSelector.d.ts +2 -1
  2. package/dist/components/SimpleLanguageSelector.js +9 -2
  3. package/dist/components/SimpleLanguageSelector.js.map +1 -1
  4. package/dist/components/ui/input.js +1 -1
  5. package/dist/components/ui/input.js.map +1 -1
  6. package/dist/components/ui/tooltip.d.ts +3 -1
  7. package/dist/components/ui/tooltip.js +2 -2
  8. package/dist/components/ui/tooltip.js.map +1 -1
  9. package/dist/config/config.js +4 -0
  10. package/dist/config/config.js.map +1 -1
  11. package/dist/config/types.d.ts +2 -0
  12. package/dist/editor/ContentTree.js +1 -1
  13. package/dist/editor/ContentTree.js.map +1 -1
  14. package/dist/editor/ContextMenu.js +26 -0
  15. package/dist/editor/ContextMenu.js.map +1 -1
  16. package/dist/editor/FieldHistory.js +1 -1
  17. package/dist/editor/FieldHistory.js.map +1 -1
  18. package/dist/editor/FieldListField.js +2 -2
  19. package/dist/editor/FieldListField.js.map +1 -1
  20. package/dist/editor/Terminal.js +3 -1
  21. package/dist/editor/Terminal.js.map +1 -1
  22. package/dist/editor/ai/Agents.js +19 -10
  23. package/dist/editor/ai/Agents.js.map +1 -1
  24. package/dist/editor/ai/AiResponseMessage.js +63 -5
  25. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  26. package/dist/editor/ai/AiTerminal.js +7 -7
  27. package/dist/editor/ai/AiTerminal.js.map +1 -1
  28. package/dist/editor/ai/AiToolCall.js +3 -3
  29. package/dist/editor/ai/AiToolCall.js.map +1 -1
  30. package/dist/editor/client/EditorClient.js +5 -1
  31. package/dist/editor/client/EditorClient.js.map +1 -1
  32. package/dist/editor/client/editContext.d.ts +2 -0
  33. package/dist/editor/client/editContext.js.map +1 -1
  34. package/dist/editor/client/operations.d.ts +1 -0
  35. package/dist/editor/client/operations.js +7 -0
  36. package/dist/editor/client/operations.js.map +1 -1
  37. package/dist/editor/commands/componentCommands.js +1 -1
  38. package/dist/editor/commands/componentCommands.js.map +1 -1
  39. package/dist/editor/field-types/ImageFieldEditor.js +1 -1
  40. package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
  41. package/dist/editor/field-types/MultiLineText.js +1 -1
  42. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  43. package/dist/editor/field-types/PictureFieldEditor.js +1 -1
  44. package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
  45. package/dist/editor/field-types/RawEditor.js +1 -1
  46. package/dist/editor/field-types/RawEditor.js.map +1 -1
  47. package/dist/editor/field-types/RichTextEditorComponent.js +16 -17
  48. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  49. package/dist/editor/field-types/SingleLineText.js +1 -1
  50. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  51. package/dist/editor/field-types/richtext/components/SimpleDropdown.d.ts +18 -0
  52. package/dist/editor/field-types/richtext/components/SimpleDropdown.js +71 -0
  53. package/dist/editor/field-types/richtext/components/SimpleDropdown.js.map +1 -0
  54. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.d.ts +5 -0
  55. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +359 -0
  56. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js.map +1 -0
  57. package/dist/editor/field-types/richtext/components/SimpleToolbar.d.ts +16 -0
  58. package/dist/editor/field-types/richtext/components/SimpleToolbar.js +181 -0
  59. package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -0
  60. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.d.ts +9 -0
  61. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js +14 -0
  62. package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js.map +1 -0
  63. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +4 -0
  64. package/dist/editor/field-types/richtext/contextMenuFactory.js +193 -0
  65. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -0
  66. package/dist/editor/field-types/richtext/index.d.ts +6 -5
  67. package/dist/editor/field-types/richtext/index.js +6 -5
  68. package/dist/editor/field-types/richtext/index.js.map +1 -1
  69. package/dist/editor/field-types/richtext/types.d.ts +16 -16
  70. package/dist/editor/field-types/richtext/types.js +84 -84
  71. package/dist/editor/field-types/richtext/types.js.map +1 -1
  72. package/dist/editor/page-editor-chrome/CommentHighlighting.js +1 -1
  73. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  74. package/dist/editor/page-editor-chrome/FrameMenu.js +5 -5
  75. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  76. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +1 -1
  77. package/dist/editor/page-viewer/PageViewerFrame.js +3 -2
  78. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  79. package/dist/editor/services/agentService.d.ts +14 -4
  80. package/dist/editor/services/agentService.js.map +1 -1
  81. package/dist/editor/services/aiService.js +1 -0
  82. package/dist/editor/services/aiService.js.map +1 -1
  83. package/dist/page-wizard/PageWizard.d.ts +2 -0
  84. package/dist/page-wizard/PageWizard.js +6 -13
  85. package/dist/page-wizard/PageWizard.js.map +1 -1
  86. package/dist/page-wizard/WizardSteps.js +3 -1
  87. package/dist/page-wizard/WizardSteps.js.map +1 -1
  88. package/dist/page-wizard/service.d.ts +1 -0
  89. package/dist/page-wizard/service.js +7 -0
  90. package/dist/page-wizard/service.js.map +1 -1
  91. package/dist/page-wizard/steps/ContentStep.js +210 -33
  92. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  93. package/dist/page-wizard/steps/FindItemsStep.js +11 -3
  94. package/dist/page-wizard/steps/FindItemsStep.js.map +1 -1
  95. package/dist/page-wizard/steps/LayoutStep.js +1 -1
  96. package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
  97. package/dist/page-wizard/steps/MetaDataStep.js +1 -1
  98. package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
  99. package/dist/page-wizard/steps/SchottSelectImagesStep.d.ts +2 -0
  100. package/dist/page-wizard/steps/SchottSelectImagesStep.js +55 -0
  101. package/dist/page-wizard/steps/SchottSelectImagesStep.js.map +1 -0
  102. package/dist/page-wizard/steps/StructureStep.js +20 -5
  103. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  104. package/dist/page-wizard/steps/TranslateStep.d.ts +2 -0
  105. package/dist/page-wizard/steps/TranslateStep.js +413 -0
  106. package/dist/page-wizard/steps/TranslateStep.js.map +1 -0
  107. package/dist/page-wizard/utils/dataAccessor.d.ts +7 -0
  108. package/dist/page-wizard/utils/dataAccessor.js +76 -0
  109. package/dist/page-wizard/utils/dataAccessor.js.map +1 -1
  110. package/dist/revision.d.ts +2 -2
  111. package/dist/revision.js +2 -2
  112. package/dist/splash-screen/NewPage.js +5 -4
  113. package/dist/splash-screen/NewPage.js.map +1 -1
  114. package/dist/splash-screen/OpenPage.js +2 -1
  115. package/dist/splash-screen/OpenPage.js.map +1 -1
  116. package/dist/splash-screen/RecentPages.js +3 -1
  117. package/dist/splash-screen/RecentPages.js.map +1 -1
  118. package/dist/styles.css +57 -0
  119. package/package.json +5 -4
  120. package/src/components/SimpleLanguageSelector.tsx +11 -1
  121. package/src/components/ui/input.tsx +1 -1
  122. package/src/components/ui/tooltip.tsx +3 -2
  123. package/src/config/config.tsx +4 -0
  124. package/src/config/types.ts +6 -0
  125. package/src/editor/ContentTree.tsx +1 -1
  126. package/src/editor/ContextMenu.tsx +39 -0
  127. package/src/editor/FieldHistory.tsx +1 -1
  128. package/src/editor/FieldListField.tsx +2 -6
  129. package/src/editor/Terminal.tsx +5 -1
  130. package/src/editor/ai/Agents.tsx +27 -16
  131. package/src/editor/ai/AiResponseMessage.tsx +138 -23
  132. package/src/editor/ai/AiTerminal.tsx +43 -29
  133. package/src/editor/ai/AiToolCall.tsx +14 -4
  134. package/src/editor/client/EditorClient.tsx +9 -1
  135. package/src/editor/client/editContext.ts +2 -0
  136. package/src/editor/client/operations.ts +9 -0
  137. package/src/editor/commands/componentCommands.tsx +1 -1
  138. package/src/editor/field-types/ImageFieldEditor.tsx +1 -0
  139. package/src/editor/field-types/MultiLineText.tsx +1 -0
  140. package/src/editor/field-types/PictureFieldEditor.tsx +1 -0
  141. package/src/editor/field-types/RawEditor.tsx +1 -0
  142. package/src/editor/field-types/RichTextEditorComponent.tsx +27 -25
  143. package/src/editor/field-types/SingleLineText.tsx +1 -0
  144. package/src/editor/field-types/richtext/components/SimpleDropdown.tsx +165 -0
  145. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.css +261 -0
  146. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +505 -0
  147. package/src/editor/field-types/richtext/components/SimpleToolbar.tsx +362 -0
  148. package/src/editor/field-types/richtext/components/SimpleToolbarButton.tsx +36 -0
  149. package/src/editor/field-types/richtext/contextMenuFactory.tsx +264 -0
  150. package/src/editor/field-types/richtext/index.ts +6 -5
  151. package/src/editor/field-types/richtext/types.ts +168 -148
  152. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +1 -1
  153. package/src/editor/page-editor-chrome/FrameMenu.tsx +16 -11
  154. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +1 -1
  155. package/src/editor/page-viewer/PageViewerFrame.tsx +4 -3
  156. package/src/editor/services/agentService.ts +16 -5
  157. package/src/editor/services/aiService.ts +2 -0
  158. package/src/page-wizard/PageWizard.tsx +10 -13
  159. package/src/page-wizard/WizardSteps.tsx +3 -1
  160. package/src/page-wizard/service.ts +11 -0
  161. package/src/page-wizard/steps/ContentStep.tsx +376 -43
  162. package/src/page-wizard/steps/FindItemsStep.tsx +23 -3
  163. package/src/page-wizard/steps/LayoutStep.tsx +1 -1
  164. package/src/page-wizard/steps/MetaDataStep.tsx +1 -1
  165. package/src/page-wizard/steps/SchottSelectImagesStep.tsx +141 -0
  166. package/src/page-wizard/steps/StructureStep.tsx +40 -5
  167. package/src/page-wizard/steps/TranslateStep.tsx +772 -0
  168. package/src/page-wizard/utils/dataAccessor.ts +85 -0
  169. package/src/revision.ts +2 -2
  170. package/src/splash-screen/NewPage.tsx +18 -3
  171. package/src/splash-screen/OpenPage.tsx +14 -1
  172. package/src/splash-screen/RecentPages.tsx +4 -2
  173. package/tsconfig.build.json +1 -0
@@ -25,10 +25,13 @@ import { executePrompt } from "../../editor/services/aiService";
25
25
  import { usePageCreator } from "./usePageCreator";
26
26
  import { useThrottledCallback } from "use-debounce";
27
27
  import { InputTextarea } from "primereact/inputtextarea";
28
- import { InputText } from "primereact/inputtext";
28
+ import { Input } from "../../components/ui/input";
29
29
  import { LanguageSelector } from "../../editor/menubar/LanguageSelector";
30
30
  import { EditControls } from "../../editor/menubar/toolbar-sections/EditControls";
31
- import { getFilteredInputData } from "../utils/dataAccessor";
31
+ import {
32
+ getFilteredInputData,
33
+ processPromptTemplate,
34
+ } from "../utils/dataAccessor";
32
35
 
33
36
  import { WizardBox } from "../WizardBox";
34
37
  import { Wand2, PanelsTopLeft, Settings, Blocks } from "lucide-react";
@@ -36,6 +39,16 @@ import { cn } from "../../lib/utils";
36
39
  import { WizardBoxConnector } from "../WizardBoxConnector";
37
40
  import { ItemDescriptor, FullItem } from "../../editor/pageModel";
38
41
  import Generate from "./Generate";
42
+ import {
43
+ Popover,
44
+ PopoverContent,
45
+ PopoverTrigger,
46
+ } from "../../components/ui/popover";
47
+ import { ChevronDown, Folder, Trash } from "lucide-react";
48
+ import { ScrollingContentTree } from "../../editor/ScrollingContentTree";
49
+ import ItemSearch from "../../editor/ui/ItemSearch";
50
+ import { normalizeGuid } from "../../editor/utils";
51
+ import { classNames } from "primereact/utils";
39
52
 
40
53
  export function ContentStep({
41
54
  wizard,
@@ -69,20 +82,170 @@ export function ContentStep({
69
82
  parentItem?.language || "en",
70
83
  );
71
84
  const [fullParentItem, setFullParentItem] = useState<FullItem>();
85
+
72
86
  const [nameValidation, setNameValidation] = useState<{
73
87
  isValid: boolean;
74
88
  message?: string;
75
89
  }>({ isValid: false });
76
90
 
91
+ // Add target folder selection state
92
+ const [targetFolder, setTargetFolder] = useState<ItemDescriptor | null>(
93
+ parentItem || null,
94
+ );
95
+
96
+ const [isSelectingFolder, setIsSelectingFolder] = useState(false);
97
+ const [folderSelectionResult, setFolderSelectionResult] = useState<{
98
+ id: string;
99
+ path: string;
100
+ } | null>(null);
101
+ const [showFolderTree, setShowFolderTree] = useState(false);
102
+
77
103
  const modifiedFieldsContext = useModifiedFieldsContext();
78
104
 
79
105
  const pageCreator = usePageCreator(pageItem, wizard, setPageModel);
80
106
 
81
107
  if (!parentItem) return "No parent item";
82
108
 
109
+ // Add target folder selection function
110
+ const selectTargetItem = useCallback(async () => {
111
+ if (!editContext || !step.fields.targetItemPrompt) return;
112
+
113
+ setIsSelectingFolder(true);
114
+
115
+ try {
116
+ const abortController = new AbortController();
117
+
118
+ const prompt = step.fields.targetItemPrompt;
119
+
120
+ // Process template expressions in the folder selection prompt
121
+ const templateResult = processPromptTemplate(prompt, data);
122
+
123
+ console.log("ContentStep: templateResult", prompt, templateResult.value);
124
+
125
+ if (templateResult.error) {
126
+ console.error(
127
+ "Template processing error in target item prompt:",
128
+ templateResult.error,
129
+ );
130
+ setMessage(`Template Error: ${templateResult.error}`);
131
+ setIsSelectingFolder(false);
132
+ return;
133
+ }
134
+
135
+ const processedPrompt = templateResult.value;
136
+
137
+ const result = await executePrompt(
138
+ [
139
+ {
140
+ content: `${processedPrompt?.trim()}
141
+ You can use get-children to explore existing items and create-item to create new items if needed.
142
+ Reply with a json object of type { id: string; path: string; } containing the ID and path of the target item.
143
+ The language of the content is ${language}.
144
+ Current parent item: ${fullParentItem?.path || parentItem?.id}
145
+ `,
146
+ name: "system",
147
+ role: "system",
148
+ id: crypto.randomUUID(),
149
+ },
150
+ ],
151
+ { editContext, createAiContext: createWizardAiContext },
152
+ {
153
+ allowedFunctions: ["get-children", "create-item"],
154
+ model: step.fields.aiModel || "gpt-4.1",
155
+ },
156
+ { signal: abortController.signal },
157
+ (response) => {
158
+ try {
159
+ const folderResult = JSON.parse(response.content) as {
160
+ id: string;
161
+ path: string;
162
+ };
163
+
164
+ if (folderResult.id && folderResult.path) {
165
+ setFolderSelectionResult(folderResult);
166
+ // Update target folder with the selected folder
167
+ setTargetFolder({
168
+ id: folderResult.id,
169
+ language: language,
170
+ } as ItemDescriptor);
171
+ }
172
+ } catch (parseError: unknown) {}
173
+ },
174
+ );
175
+
176
+ if (result && result.messages && result.messages.length > 0) {
177
+ const lastMessage = result.messages[result.messages.length - 1];
178
+ if (lastMessage && lastMessage.content) {
179
+ const folderResult = JSON.parse(lastMessage.content) as {
180
+ id: string;
181
+ path: string;
182
+ };
183
+
184
+ if (folderResult.id && folderResult.path) {
185
+ setFolderSelectionResult(folderResult);
186
+ // Update target folder with the selected folder
187
+ setTargetFolder({
188
+ id: folderResult.id,
189
+ language: language,
190
+ } as ItemDescriptor);
191
+ }
192
+ }
193
+ }
194
+ } catch (error) {
195
+ console.error("Error selecting target folder", error);
196
+ } finally {
197
+ setIsSelectingFolder(false);
198
+ }
199
+ }, [
200
+ editContext,
201
+ step.fields.selectTargetFolderPrompt,
202
+ language,
203
+ parentItem,
204
+ data,
205
+ ]);
206
+
207
+ const handleFolderSelection = useCallback(
208
+ async (folderId: string) => {
209
+ if (!editContext) return;
210
+
211
+ try {
212
+ const folderItem = await editContext.itemsRepository.getItem({
213
+ id: folderId,
214
+ language: language,
215
+ version: 0,
216
+ });
217
+
218
+ if (folderItem) {
219
+ setTargetFolder({
220
+ id: folderId,
221
+ language: language,
222
+ } as ItemDescriptor);
223
+
224
+ setFolderSelectionResult({
225
+ id: folderId,
226
+ path: folderItem.path,
227
+ });
228
+
229
+ setFullParentItem(folderItem);
230
+ setShowFolderTree(false);
231
+ }
232
+ } catch (error) {
233
+ console.error("Error loading selected folder:", error);
234
+ }
235
+ },
236
+ [editContext, language],
237
+ );
238
+
239
+ const clearFolderSelection = useCallback(() => {
240
+ setTargetFolder(parentItem);
241
+ setFolderSelectionResult(null);
242
+ setFullParentItem(undefined);
243
+ setShowFolderTree(false);
244
+ }, [parentItem]);
245
+
83
246
  const checkPageName = useCallback(async () => {
84
- if (!parentItem) {
85
- setNameValidation({ isValid: false, message: "No parent item" });
247
+ if (!targetFolder) {
248
+ setNameValidation({ isValid: false, message: "No target folder" });
86
249
  return false;
87
250
  }
88
251
 
@@ -98,7 +261,7 @@ export function ContentStep({
98
261
  try {
99
262
  // Check if page with same name already exists
100
263
  const children = await getChildren(
101
- parentItem.id,
264
+ targetFolder.id,
102
265
  editContext?.sessionId ?? "",
103
266
  [],
104
267
  false,
@@ -129,7 +292,7 @@ export function ContentStep({
129
292
  return false;
130
293
  }
131
294
  }, [
132
- parentItem,
295
+ targetFolder,
133
296
  pageModel.name,
134
297
  editContext?.sessionId,
135
298
  editContext?.contentEditorItem?.language,
@@ -145,7 +308,7 @@ export function ContentStep({
145
308
  };
146
309
 
147
310
  const createPageIfNeeded = useCallback(async (): Promise<boolean> => {
148
- if (!editContext || !parentItem || pageItem) return true; // Page already exists
311
+ if (!editContext || !targetFolder || pageItem) return true; // Page already exists
149
312
 
150
313
  try {
151
314
  if (!(await checkPageName())) {
@@ -155,8 +318,8 @@ export function ContentStep({
155
318
 
156
319
  const result = await editContext.operations.createItem(
157
320
  {
158
- ...getItemDescriptor(parentItem),
159
- language: parentItem.language || "en",
321
+ ...getItemDescriptor(targetFolder),
322
+ language: targetFolder.language || "en",
160
323
  },
161
324
  wizard.templateId,
162
325
  pageModel.name,
@@ -180,7 +343,7 @@ export function ContentStep({
180
343
  }
181
344
  }, [
182
345
  editContext,
183
- parentItem,
346
+ targetFolder,
184
347
  pageItem,
185
348
  checkPageName,
186
349
  wizard.templateId,
@@ -302,17 +465,19 @@ export function ContentStep({
302
465
  }
303
466
  }, [pageModel.name, checkPageName]);
304
467
 
305
- // Load parent item details
468
+ // Load target folder details
306
469
  useEffect(() => {
307
- const loadParentItem = async () => {
308
- if (!parentItem) return;
309
- const item = await editContext?.itemsRepository.getItem(parentItem);
470
+ const loadTargetItem = async () => {
471
+ if (!targetFolder) return;
472
+ const item = await editContext?.itemsRepository.getItem(targetFolder);
473
+ setFullParentItem(item);
474
+ // Also set fullParentItem for backward compatibility
310
475
  setFullParentItem(item);
311
476
  };
312
- loadParentItem();
313
- }, [parentItem]);
477
+ loadTargetItem();
478
+ }, [parentItem, editContext]);
314
479
 
315
- // Auto-generate page name and meta description if not set
480
+ // Auto-generate page name and select target folder if not set
316
481
  useEffect(() => {
317
482
  if (!editContext) return;
318
483
 
@@ -326,19 +491,37 @@ export function ContentStep({
326
491
  try {
327
492
  const abortController = new AbortController();
328
493
 
494
+ // Process template expressions in the prompt
495
+ const templateResult = processPromptTemplate(
496
+ pageNameInstructions,
497
+ data,
498
+ );
499
+
500
+ if (templateResult.error) {
501
+ console.error(
502
+ "Template processing error in page name prompt:",
503
+ templateResult.error,
504
+ );
505
+ setMessage(`Template Error: ${templateResult.error}`);
506
+ setIsGenerating(false);
507
+ return;
508
+ }
509
+
510
+ const processedInstructions = templateResult.value;
511
+
329
512
  const result = await executePrompt(
330
513
  [
331
514
  {
332
- content: `${pageNameInstructions?.trim()} Reply with a json object of type PageModel = { name: string; };
515
+ content: `${processedInstructions?.trim()} Reply with a json object of type PageModel = { name: string; };
333
516
  The language of the page is ${language}.
334
- Input data: ${JSON.stringify(data)}`,
517
+ `,
335
518
  name: "system",
336
519
  role: "system",
337
520
  id: crypto.randomUUID(), // Use proper UUID instead of Date.now()
338
521
  },
339
522
  ],
340
523
  { editContext, createAiContext: createWizardAiContext },
341
- { model: "o4-mini-high" },
524
+ { model: step.fields.aiModel || "gpt-4.1" },
342
525
  { signal: abortController.signal },
343
526
  (response) => {
344
527
  try {
@@ -371,8 +554,33 @@ export function ContentStep({
371
554
  }
372
555
  };
373
556
 
374
- if (!pageModel.name) generatePageName();
375
- }, []);
557
+ // Run page name generation and folder selection in parallel
558
+ const runInitialGeneration = async () => {
559
+ const promises = [];
560
+
561
+ // Generate page name if not set
562
+ if (!pageModel.name) {
563
+ promises.push(generatePageName());
564
+ }
565
+
566
+ // Select target folder if prompt is configured and folder not yet selected
567
+ if (step.fields.targetItemPrompt && !folderSelectionResult) {
568
+ promises.push(selectTargetItem());
569
+ }
570
+
571
+ // Run both in parallel
572
+ if (promises.length > 0) {
573
+ await Promise.all(promises);
574
+ }
575
+ };
576
+
577
+ runInitialGeneration();
578
+ }, [
579
+ pageModel.name,
580
+ folderSelectionResult,
581
+ step.fields.targetItemPrompt,
582
+ selectTargetItem,
583
+ ]);
376
584
 
377
585
  const createComponents = async () => {
378
586
  if (!pageLoaded) return;
@@ -562,9 +770,27 @@ export function ContentStep({
562
770
  // Filter input data based on "Input Properties" field if specified
563
771
  const inputData = getFilteredInputData(step, data);
564
772
 
773
+ // Process template expressions in the layout instructions
774
+ const templateResult = processPromptTemplate(
775
+ internalState.layoutInstructions || "",
776
+ data,
777
+ );
778
+
779
+ if (templateResult.error) {
780
+ console.error(
781
+ "Template processing error in layout instructions:",
782
+ templateResult.error,
783
+ );
784
+ setMessage(`Template Error: ${templateResult.error}`);
785
+ setIsLoading(false);
786
+ return;
787
+ }
788
+
789
+ const processedInstructions = templateResult.value;
790
+
565
791
  const prompt = [
566
792
  {
567
- content: `${internalState.layoutInstructions ? internalState.layoutInstructions?.trim() : ""} Reply with a json object of type PageModel = { fields: Field[]; components: Component[]; message: string; };
793
+ content: `${processedInstructions ? processedInstructions?.trim() : ""} Reply with a json object of type PageModel = { fields: Field[]; components: Component[]; message: string; };
568
794
  Component = { id: string | undefined, name: string, type: string; fields: Field[]; placeholder?: string; children?: Component[]; };
569
795
  Field = { name: string; value: string; type: string; };
570
796
  Generate a descriptive name for each component including the topic.
@@ -588,7 +814,7 @@ export function ContentStep({
588
814
  Tell the user your reasoning in the message field.
589
815
 
590
816
  The language of the page is ${editContextRef.current!.page!.item!.language}.
591
- Input data: ${JSON.stringify(inputData)}`,
817
+ `,
592
818
 
593
819
  name: "system",
594
820
  role: "system",
@@ -596,12 +822,6 @@ export function ContentStep({
596
822
  },
597
823
  ];
598
824
 
599
- console.log("PAGE WIZARD DATA: ", data);
600
- console.log("PAGE WIZARD INPUTDATA: ", inputData);
601
- console.log("PAGE WIZARD PROMPT: ", prompt?.[0]?.content);
602
- console.log("PAGE WIZARD page fields: ", schema.pageFields);
603
- console.log("PAGE WIZARD schema: ", filteredSchema);
604
-
605
825
  const result = await executePrompt(
606
826
  prompt,
607
827
  {
@@ -672,7 +892,9 @@ export function ContentStep({
672
892
  collapsible="yes"
673
893
  summary={
674
894
  <div className="flex flex-col gap-2 text-xs break-all text-gray-500">
675
- <div>Folder: {fullParentItem?.path}</div>
895
+ <div>
896
+ Folder: {folderSelectionResult?.path || fullParentItem?.path}
897
+ </div>
676
898
  <div>Language: {language}</div>
677
899
  <div>Name: {pageModel.name || "Not set"}</div>
678
900
  </div>
@@ -681,24 +903,33 @@ export function ContentStep({
681
903
  >
682
904
  <div className="flex flex-col items-stretch">
683
905
  <div className="relative flex-1">
684
- {isGenerating ? (
906
+ {isGenerating || isSelectingFolder ? (
685
907
  <div className="mt-4 flex h-full items-center justify-center pb-4">
686
- <Generate title="Generating page name..." size="medium" />
908
+ <Generate
909
+ title={
910
+ isGenerating && isSelectingFolder
911
+ ? "Generating page name and selecting folder..."
912
+ : isGenerating
913
+ ? "Generating page name..."
914
+ : "Selecting folder..."
915
+ }
916
+ size="medium"
917
+ />
687
918
  </div>
688
919
  ) : (
689
920
  <>
690
921
  <div className="mb-4">
691
922
  <label
692
923
  htmlFor="pageName"
693
- className="mb-1 block text-sm font-medium"
924
+ className="mb-1 block text-xs font-medium"
694
925
  >
695
926
  Page Name
696
927
  </label>
697
- <InputText
928
+ <Input
698
929
  id="pageName"
699
930
  type="text"
700
931
  disabled={!!pageItem}
701
- className={`w-full rounded border p-2 text-sm ${
932
+ className={`bg-gray-5 w-full rounded border md:text-xs ${
702
933
  !nameValidation.isValid
703
934
  ? "border-red-500"
704
935
  : nameValidation.isValid && pageModel.name
@@ -706,7 +937,7 @@ export function ContentStep({
706
937
  : ""
707
938
  }`}
708
939
  value={pageModel.name}
709
- onChange={(e) =>
940
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
710
941
  handleInputChange("name", e.target.value)
711
942
  }
712
943
  placeholder="Enter page name"
@@ -724,12 +955,106 @@ export function ContentStep({
724
955
  </div>
725
956
 
726
957
  <div className="mb-4">
727
- <div className="mb-1 text-sm font-medium">
728
- Target Parent Item
729
- </div>
730
- <div className="mb-4 break-after-all text-xs text-gray-600">
731
- {fullParentItem?.path || "Loading..."}
958
+ <div className="mb-1 text-xs font-medium">
959
+ Target Folder
732
960
  </div>
961
+ {isSelectingFolder ? (
962
+ <div className="mb-4 flex items-center gap-2 text-xs text-gray-600">
963
+ <Generate title="Selecting folder..." size="medium" />
964
+ Selecting folder...
965
+ </div>
966
+ ) : (
967
+ <div className="mb-4">
968
+ <div className="relative">
969
+ <Popover
970
+ open={showFolderTree}
971
+ onOpenChange={setShowFolderTree}
972
+ >
973
+ <PopoverTrigger asChild>
974
+ <div
975
+ className={classNames(
976
+ "focus-shadow bg-gray-5 flex cursor-pointer justify-between border p-1.5 text-xs hover:bg-gray-100",
977
+ )}
978
+ >
979
+ <div className="flex items-center gap-2">
980
+ <Folder className="h-4 w-4" strokeWidth={1} />
981
+ <span className="break-all">
982
+ {folderSelectionResult?.path ||
983
+ fullParentItem?.path ||
984
+ "Loading..."}
985
+ </span>
986
+ </div>
987
+ <ChevronDown
988
+ strokeWidth={1}
989
+ className={`h-4 w-4 transition-transform duration-200 ${
990
+ showFolderTree ? "rotate-180" : "rotate-0"
991
+ }`}
992
+ />
993
+ </div>
994
+ </PopoverTrigger>
995
+
996
+ <PopoverContent
997
+ className="w-96 p-0"
998
+ align="start"
999
+ side="bottom"
1000
+ >
1001
+ <div className="border-b border-gray-200 bg-white p-2">
1002
+ <ItemSearch
1003
+ rootItemIds={[
1004
+ normalizeGuid(
1005
+ parentItem?.id ||
1006
+ "11111111-1111-1111-1111-111111111111",
1007
+ ),
1008
+ ]}
1009
+ autoFocus={true}
1010
+ itemSelected={(item) => {
1011
+ handleFolderSelection(item.id);
1012
+ }}
1013
+ />
1014
+ </div>
1015
+
1016
+ {folderSelectionResult && (
1017
+ <button
1018
+ className="w-full cursor-pointer rounded border-b bg-gray-50 p-1 px-4 text-left text-xs hover:bg-gray-100"
1019
+ onClick={clearFolderSelection}
1020
+ >
1021
+ <div className="flex items-center gap-2">
1022
+ <Trash
1023
+ className="h-4 w-4"
1024
+ strokeWidth={1}
1025
+ />{" "}
1026
+ Reset to original folder
1027
+ </div>
1028
+ </button>
1029
+ )}
1030
+
1031
+ <div className="relative h-64 border-x border-b">
1032
+ <ScrollingContentTree
1033
+ key={`folder-tree-${showFolderTree ? "open" : "closed"}-${targetFolder?.id || "none"}`}
1034
+ selectedItemId={targetFolder?.id}
1035
+ rootItemIds={[
1036
+ parentItem?.id ||
1037
+ "11111111-1111-1111-1111-111111111111",
1038
+ ]}
1039
+ expandedItemId={targetFolder?.id}
1040
+ scrollToSelected={true}
1041
+ onSelectionChange={(nodes) => {
1042
+ if (nodes[0]?.id) {
1043
+ handleFolderSelection(nodes[0].id);
1044
+ }
1045
+ }}
1046
+ />
1047
+ </div>
1048
+ </PopoverContent>
1049
+ </Popover>
1050
+ </div>
1051
+ {step.fields.targetItemPrompt && parentItem && (
1052
+ <div className="mt-1 text-xs text-green-600">
1053
+ ✓ Folder selected by AI
1054
+ </div>
1055
+ )}
1056
+ </div>
1057
+ )}
733
1058
  </div>
734
1059
 
735
1060
  <div className="relative mb-4">
@@ -801,6 +1126,7 @@ export function ContentStep({
801
1126
  disabled={
802
1127
  isLoading ||
803
1128
  isCreatingComponents ||
1129
+ isSelectingFolder ||
804
1130
  !data.selectedComponentTypes ||
805
1131
  data.selectedComponentTypes.length === 0 ||
806
1132
  (!pageItem &&
@@ -811,11 +1137,18 @@ export function ContentStep({
811
1137
  isLoading={
812
1138
  isLoading ||
813
1139
  isCreatingComponents ||
1140
+ isSelectingFolder ||
814
1141
  !!editContext?.activeFieldActions.find(
815
1142
  (action) => action.state === "running",
816
1143
  )
817
1144
  }
818
- loadingText={"Working"}
1145
+ loadingText={
1146
+ isSelectingFolder
1147
+ ? "Selecting folder..."
1148
+ : isGenerating
1149
+ ? "Generating..."
1150
+ : "Working"
1151
+ }
819
1152
  className="w-full flex-1"
820
1153
  >
821
1154
  {!pageItem ? "Create page" : "Delete & Regenerate content"}
@@ -9,7 +9,11 @@ import {
9
9
  fetchItemStubs,
10
10
  } from "../../editor/services/contentService";
11
11
  import { ItemDescriptor } from "../../editor/pageModel";
12
- import { evaluateDataExpression, setDataProperty } from "../utils/dataAccessor";
12
+ import {
13
+ evaluateDataExpression,
14
+ setDataProperty,
15
+ processPromptTemplate,
16
+ } from "../utils/dataAccessor";
13
17
  import { executePrompt } from "../../editor/services/aiService";
14
18
  import { createWizardAiContext } from "../service";
15
19
  import { ActionButton } from "../../components/ActionButton";
@@ -177,12 +181,28 @@ export function FindItemsStep({
177
181
  // Evaluate only the input properties that should be included
178
182
  const evaluatedInputData = evaluateDataExpression(inputProperties, data);
179
183
 
184
+ // Process template expressions in system instructions
185
+ const systemInstructionsResult = processPromptTemplate(
186
+ step.fields.systemInstructions || "",
187
+ data,
188
+ );
189
+
190
+ if (systemInstructionsResult.error) {
191
+ console.error(
192
+ "Template processing error in system instructions:",
193
+ systemInstructionsResult.error,
194
+ );
195
+ setAiError(`Template Error: ${systemInstructionsResult.error}`);
196
+ setLoadingAI(false);
197
+ return;
198
+ }
199
+
180
200
  const basePrompt = `
181
- ${step.fields.systemInstructions}
201
+ ${systemInstructionsResult.value || ""}
182
202
 
183
203
  You need to suggest relevant content items from a content tree. Here are the details:
184
204
 
185
- Input Data: ${JSON.stringify(evaluatedInputData)}
205
+
186
206
  Source Root IDs: ${JSON.stringify(rootItemIds)}
187
207
  Multi-select: ${multiSelect}
188
208
  Max selections needed: ${multiSelect ? "multiple" : "1"}
@@ -71,7 +71,7 @@ export function LayoutStep({
71
71
  Where components are of type Component = { id: string; name: string; fields: FieldDefinition[]; styles: string; };
72
72
  And fields are of type FieldDefinition = { name: string; type: string; value: string; };
73
73
 
74
- Input data: ${JSON.stringify(data)}
74
+
75
75
 
76
76
  Types: Row: Use this to group components horizontally. Section: Use this to group components with a background color or spacing. Title: Use for titles and headings. Text: Use for general text content. Quote: Use for quotes and callouts. Image: Use for images and media. TeaserList: Use for lists of items with images and descriptions. Test: Component for testing and prototyping.
77
77
 
@@ -62,7 +62,7 @@ export function MetaDataStep({
62
62
  {
63
63
  content: `${metaInstructions?.trim()} Reply with a json object of type PageModel = { metaDescription: string; metaKeywords: string; };
64
64
  The language of the page is ${parentItem?.language || "en"}.
65
- Input data: ${JSON.stringify(data)}`,
65
+ `,
66
66
  name: "system",
67
67
  role: "system",
68
68
  id: crypto.randomUUID(), // Use proper UUID instead of Date.now()