@alpaca-editor/core 1.0.3942 → 1.0.3943

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 (308) hide show
  1. package/.prettierrc +3 -0
  2. package/build.css +3 -0
  3. package/components.json +21 -0
  4. package/dist/editor/ContentTree.d.ts +2 -1
  5. package/dist/editor/ContentTree.js +23 -21
  6. package/dist/editor/ContentTree.js.map +1 -1
  7. package/dist/editor/FieldActionsOverlay.js +0 -2
  8. package/dist/editor/FieldActionsOverlay.js.map +1 -1
  9. package/dist/editor/ScrollingContentTree.js +1 -1
  10. package/dist/editor/ScrollingContentTree.js.map +1 -1
  11. package/dist/editor/Titlebar.js +1 -1
  12. package/dist/editor/Titlebar.js.map +1 -1
  13. package/dist/editor/ai/GhostWriter.js +24 -3
  14. package/dist/editor/ai/GhostWriter.js.map +1 -1
  15. package/dist/editor/client/EditorClient.js +7 -7
  16. package/dist/editor/client/EditorClient.js.map +1 -1
  17. package/dist/editor/field-types/InternalLinkFieldEditor.js +60 -10
  18. package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
  19. package/dist/editor/media-selector/MediaFolderBrowser.js +48 -1
  20. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  21. package/dist/editor/menubar/PageSelector.js +116 -65
  22. package/dist/editor/menubar/PageSelector.js.map +1 -1
  23. package/dist/editor/page-viewer/EditorForm.js +5 -2
  24. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  25. package/dist/editor/ui/ItemSearch.js +14 -8
  26. package/dist/editor/ui/ItemSearch.js.map +1 -1
  27. package/dist/editor/ui/PerfectTree.d.ts +4 -2
  28. package/dist/editor/ui/PerfectTree.js +78 -4
  29. package/dist/editor/ui/PerfectTree.js.map +1 -1
  30. package/dist/editor/ui/Splitter.js +1 -1
  31. package/dist/revision.d.ts +2 -2
  32. package/dist/revision.js +2 -2
  33. package/dist/styles.css +8 -2
  34. package/eslint.config.mjs +4 -0
  35. package/images/bg-shape-black.webp +0 -0
  36. package/images/wizard-bg.png +0 -0
  37. package/images/wizard-tour.png +0 -0
  38. package/images/wizard.png +0 -0
  39. package/package.json +2 -8
  40. package/src/client-components/api.ts +6 -0
  41. package/src/client-components/index.ts +19 -0
  42. package/src/components/ActionButton.tsx +50 -0
  43. package/src/components/Error.tsx +57 -0
  44. package/src/components/ui/CardConnector.tsx +56 -0
  45. package/src/components/ui/button.tsx +62 -0
  46. package/src/components/ui/card.tsx +372 -0
  47. package/src/components/ui/context-menu.tsx +250 -0
  48. package/src/config/config.tsx +917 -0
  49. package/src/config/types.ts +286 -0
  50. package/src/editor/ComponentInfo.tsx +90 -0
  51. package/src/editor/ConfirmationDialog.tsx +103 -0
  52. package/src/editor/ContentTree.tsx +733 -0
  53. package/src/editor/ContextMenu.tsx +230 -0
  54. package/src/editor/Editor.tsx +90 -0
  55. package/src/editor/EditorWarning.tsx +34 -0
  56. package/src/editor/EditorWarnings.tsx +33 -0
  57. package/src/editor/FieldActionsOverlay.tsx +296 -0
  58. package/src/editor/FieldEditorPopup.tsx +65 -0
  59. package/src/editor/FieldHistory.tsx +75 -0
  60. package/src/editor/FieldList.tsx +190 -0
  61. package/src/editor/FieldListField.tsx +391 -0
  62. package/src/editor/FieldListFieldWithFallbacks.tsx +217 -0
  63. package/src/editor/FloatingToolbar.tsx +163 -0
  64. package/src/editor/ImageEditor.tsx +128 -0
  65. package/src/editor/ItemInfo.tsx +90 -0
  66. package/src/editor/LinkEditorDialog.tsx +196 -0
  67. package/src/editor/MainLayout.tsx +95 -0
  68. package/src/editor/MobileLayout.tsx +68 -0
  69. package/src/editor/NewEditorClient.tsx +11 -0
  70. package/src/editor/PictureCropper.tsx +568 -0
  71. package/src/editor/PictureEditor.tsx +301 -0
  72. package/src/editor/PictureEditorDialog.tsx +381 -0
  73. package/src/editor/PublishDialog.ignore +74 -0
  74. package/src/editor/ScrollingContentTree.tsx +68 -0
  75. package/src/editor/Terminal.tsx +227 -0
  76. package/src/editor/Titlebar.tsx +104 -0
  77. package/src/editor/ai/AiPopup.tsx +59 -0
  78. package/src/editor/ai/AiResponseMessage.tsx +106 -0
  79. package/src/editor/ai/AiTerminal.tsx +503 -0
  80. package/src/editor/ai/AiToolCall.tsx +61 -0
  81. package/src/editor/ai/EditorAiTerminal.tsx +20 -0
  82. package/src/editor/ai/GhostWriter.tsx +480 -0
  83. package/src/editor/ai/aiPageModel.ts +108 -0
  84. package/src/editor/ai/editorAiContext.ts +18 -0
  85. package/src/editor/client/AboutDialog.tsx +44 -0
  86. package/src/editor/client/EditorClient.tsx +2241 -0
  87. package/src/editor/client/GenericDialog.tsx +50 -0
  88. package/src/editor/client/editContext.ts +416 -0
  89. package/src/editor/client/helpers.ts +44 -0
  90. package/src/editor/client/itemsRepository.ts +574 -0
  91. package/src/editor/client/operations.ts +768 -0
  92. package/src/editor/client/pageModelBuilder.ts +219 -0
  93. package/src/editor/commands/commands.ts +22 -0
  94. package/src/editor/commands/componentCommands.tsx +431 -0
  95. package/src/editor/commands/createVersionCommand.ts +33 -0
  96. package/src/editor/commands/deleteVersionCommand.ts +71 -0
  97. package/src/editor/commands/itemCommands.tsx +351 -0
  98. package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +201 -0
  99. package/src/editor/commands/localizeItem/LocalizeItemUtils.ts +27 -0
  100. package/src/editor/commands/undo.ts +39 -0
  101. package/src/editor/component-designer/ComponentDesigner.tsx +70 -0
  102. package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx +11 -0
  103. package/src/editor/component-designer/ComponentDesignerMenu.tsx +91 -0
  104. package/src/editor/component-designer/ComponentEditor.tsx +97 -0
  105. package/src/editor/component-designer/ComponentRenderingCodeEditor.tsx +31 -0
  106. package/src/editor/component-designer/ComponentRenderingEditor.tsx +104 -0
  107. package/src/editor/component-designer/ComponentsDropdown.tsx +39 -0
  108. package/src/editor/component-designer/PlaceholdersEditor.tsx +179 -0
  109. package/src/editor/component-designer/RenderingsDropdown.tsx +36 -0
  110. package/src/editor/component-designer/TemplateEditor.tsx +236 -0
  111. package/src/editor/component-designer/aiContext.ts +23 -0
  112. package/src/editor/componentTreeHelper.tsx +116 -0
  113. package/src/editor/context-menu/CopyMoveMenu.tsx +103 -0
  114. package/src/editor/context-menu/InsertMenu.tsx +347 -0
  115. package/src/editor/control-center/About.tsx +342 -0
  116. package/src/editor/control-center/ControlCenterMenu.tsx +76 -0
  117. package/src/editor/control-center/IndexOverview.tsx +50 -0
  118. package/src/editor/control-center/IndexSettings.tsx +266 -0
  119. package/src/editor/control-center/Info.tsx +104 -0
  120. package/src/editor/control-center/QuotaInfo.tsx +301 -0
  121. package/src/editor/control-center/Status.tsx +113 -0
  122. package/src/editor/control-center/WebSocketMessages.tsx +155 -0
  123. package/src/editor/editor-warnings/ItemLocked.tsx +63 -0
  124. package/src/editor/editor-warnings/NoLanguageWriteAccess.tsx +22 -0
  125. package/src/editor/editor-warnings/NoWorkflowWriteAccess.tsx +23 -0
  126. package/src/editor/editor-warnings/NoWriteAccess.tsx +16 -0
  127. package/src/editor/editor-warnings/ValidationErrors.tsx +54 -0
  128. package/src/editor/field-types/AttachmentEditor.tsx +9 -0
  129. package/src/editor/field-types/CheckboxEditor.tsx +47 -0
  130. package/src/editor/field-types/DropLinkEditor.tsx +80 -0
  131. package/src/editor/field-types/DropListEditor.tsx +84 -0
  132. package/src/editor/field-types/ImageFieldEditor.tsx +65 -0
  133. package/src/editor/field-types/InternalLinkFieldEditor.tsx +188 -0
  134. package/src/editor/field-types/LinkFieldEditor.tsx +85 -0
  135. package/src/editor/field-types/MultiLineText.tsx +82 -0
  136. package/src/editor/field-types/PictureFieldEditor.tsx +121 -0
  137. package/src/editor/field-types/RawEditor.tsx +53 -0
  138. package/src/editor/field-types/ReactQuill.tsx +580 -0
  139. package/src/editor/field-types/RichTextEditor.tsx +22 -0
  140. package/src/editor/field-types/RichTextEditorComponent.tsx +127 -0
  141. package/src/editor/field-types/SingleLineText.tsx +174 -0
  142. package/src/editor/field-types/TreeListEditor.tsx +261 -0
  143. package/src/editor/fieldTypes.ts +140 -0
  144. package/src/editor/media-selector/AiImageSearch.tsx +185 -0
  145. package/src/editor/media-selector/AiImageSearchPrompt.tsx +94 -0
  146. package/src/editor/media-selector/MediaFolderBrowser.tsx +321 -0
  147. package/src/editor/media-selector/MediaSelector.tsx +42 -0
  148. package/src/editor/media-selector/Preview.tsx +14 -0
  149. package/src/editor/media-selector/Thumbnails.tsx +48 -0
  150. package/src/editor/media-selector/TreeSelector.tsx +292 -0
  151. package/src/editor/media-selector/UploadZone.tsx +137 -0
  152. package/src/editor/media-selector/index.ts +8 -0
  153. package/src/editor/menubar/ActionsMenu.tsx +94 -0
  154. package/src/editor/menubar/ActiveUsers.tsx +17 -0
  155. package/src/editor/menubar/ApproveAndPublish.tsx +18 -0
  156. package/src/editor/menubar/BrowseHistory.tsx +28 -0
  157. package/src/editor/menubar/ItemLanguageVersion.tsx +76 -0
  158. package/src/editor/menubar/LanguageSelector.tsx +226 -0
  159. package/src/editor/menubar/Menu.tsx +83 -0
  160. package/src/editor/menubar/NavButtons.tsx +74 -0
  161. package/src/editor/menubar/PageSelector.tsx +278 -0
  162. package/src/editor/menubar/PageViewerControls.tsx +120 -0
  163. package/src/editor/menubar/PreviewSecondaryControls.tsx +18 -0
  164. package/src/editor/menubar/SecondaryControls.tsx +45 -0
  165. package/src/editor/menubar/Separator.tsx +12 -0
  166. package/src/editor/menubar/SiteInfo.tsx +53 -0
  167. package/src/editor/menubar/User.tsx +27 -0
  168. package/src/editor/menubar/VersionSelector.tsx +142 -0
  169. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +307 -0
  170. package/src/editor/page-editor-chrome/CommentHighlightings.tsx +35 -0
  171. package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +59 -0
  172. package/src/editor/page-editor-chrome/FieldActionIndicators.tsx +23 -0
  173. package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +64 -0
  174. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +35 -0
  175. package/src/editor/page-editor-chrome/FrameMenu.tsx +338 -0
  176. package/src/editor/page-editor-chrome/FrameMenus.tsx +48 -0
  177. package/src/editor/page-editor-chrome/InlineEditor.tsx +765 -0
  178. package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +61 -0
  179. package/src/editor/page-editor-chrome/NoLayout.tsx +36 -0
  180. package/src/editor/page-editor-chrome/PageEditorChrome.tsx +122 -0
  181. package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +161 -0
  182. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +169 -0
  183. package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +315 -0
  184. package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +300 -0
  185. package/src/editor/page-editor-chrome/SuggestionHighlightings.tsx +40 -0
  186. package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +828 -0
  187. package/src/editor/page-viewer/DeviceToolbar.tsx +70 -0
  188. package/src/editor/page-viewer/EditorForm.tsx +262 -0
  189. package/src/editor/page-viewer/MiniMap.tsx +362 -0
  190. package/src/editor/page-viewer/PageViewer.tsx +169 -0
  191. package/src/editor/page-viewer/PageViewerFrame.tsx +1022 -0
  192. package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +412 -0
  193. package/src/editor/page-viewer/pageViewContext.ts +186 -0
  194. package/src/editor/pageModel.ts +220 -0
  195. package/src/editor/picture-shared.tsx +53 -0
  196. package/src/editor/reviews/Comment.tsx +308 -0
  197. package/src/editor/reviews/Comments.tsx +125 -0
  198. package/src/editor/reviews/DiffView.tsx +109 -0
  199. package/src/editor/reviews/PreviewInfo.tsx +35 -0
  200. package/src/editor/reviews/Reviews.tsx +280 -0
  201. package/src/editor/reviews/SuggestedEdit.tsx +316 -0
  202. package/src/editor/reviews/reviewCommands.tsx +47 -0
  203. package/src/editor/reviews/useReviews.tsx +70 -0
  204. package/src/editor/services/aiService.ts +173 -0
  205. package/src/editor/services/componentDesignerService.ts +151 -0
  206. package/src/editor/services/contentService.ts +180 -0
  207. package/src/editor/services/editService.ts +488 -0
  208. package/src/editor/services/indexService.ts +24 -0
  209. package/src/editor/services/reviewsService.ts +53 -0
  210. package/src/editor/services/serviceHelper.ts +95 -0
  211. package/src/editor/services/suggestedEditsService.ts +39 -0
  212. package/src/editor/services/systemService.ts +5 -0
  213. package/src/editor/services/translationService.ts +21 -0
  214. package/src/editor/services-server/api.ts +150 -0
  215. package/src/editor/services-server/graphQL.ts +106 -0
  216. package/src/editor/sidebar/ComponentPalette.tsx +161 -0
  217. package/src/editor/sidebar/ComponentTree.tsx +549 -0
  218. package/src/editor/sidebar/Debug.tsx +111 -0
  219. package/src/editor/sidebar/DictionaryEditor.tsx +261 -0
  220. package/src/editor/sidebar/EditHistory.tsx +134 -0
  221. package/src/editor/sidebar/GraphQL.tsx +164 -0
  222. package/src/editor/sidebar/Insert.tsx +35 -0
  223. package/src/editor/sidebar/MainContentTree.tsx +102 -0
  224. package/src/editor/sidebar/Performance.tsx +53 -0
  225. package/src/editor/sidebar/Sessions.tsx +35 -0
  226. package/src/editor/sidebar/Sidebar.tsx +20 -0
  227. package/src/editor/sidebar/SidebarView.tsx +152 -0
  228. package/src/editor/sidebar/Translations.tsx +295 -0
  229. package/src/editor/sidebar/Validation.tsx +102 -0
  230. package/src/editor/sidebar/ViewSelector.tsx +60 -0
  231. package/src/editor/sidebar/Workbox.tsx +209 -0
  232. package/src/editor/ui/CenteredMessage.tsx +7 -0
  233. package/src/editor/ui/CopyMoveTargetSelectorDialog.tsx +81 -0
  234. package/src/editor/ui/CopyToClipboardButton.tsx +24 -0
  235. package/src/editor/ui/DialogButtons.tsx +11 -0
  236. package/src/editor/ui/Icons.tsx +709 -0
  237. package/src/editor/ui/ItemList.tsx +76 -0
  238. package/src/editor/ui/ItemNameDialogNew.tsx +118 -0
  239. package/src/editor/ui/ItemSearch.tsx +159 -0
  240. package/src/editor/ui/PerfectTree.tsx +676 -0
  241. package/src/editor/ui/Section.tsx +35 -0
  242. package/src/editor/ui/SimpleIconButton.tsx +54 -0
  243. package/src/editor/ui/SimpleMenu.tsx +40 -0
  244. package/src/editor/ui/SimpleTable.tsx +60 -0
  245. package/src/editor/ui/SimpleTabs.tsx +60 -0
  246. package/src/editor/ui/SimpleToolbar.tsx +7 -0
  247. package/src/editor/ui/Spinner.tsx +9 -0
  248. package/src/editor/ui/Splitter.tsx +420 -0
  249. package/src/editor/ui/StackedPanels.tsx +134 -0
  250. package/src/editor/ui/Toolbar.tsx +7 -0
  251. package/src/editor/utils/id-helper.ts +3 -0
  252. package/src/editor/utils/insertOptions.ts +69 -0
  253. package/src/editor/utils/itemutils.ts +29 -0
  254. package/src/editor/utils/useMemoDebug.ts +28 -0
  255. package/src/editor/utils.ts +486 -0
  256. package/src/editor/views/CompareView.tsx +245 -0
  257. package/src/editor/views/EditView.tsx +27 -0
  258. package/src/editor/views/ItemEditor.tsx +58 -0
  259. package/src/editor/views/MediaFolderEditView.tsx +66 -0
  260. package/src/editor/views/SingleEditView.tsx +57 -0
  261. package/src/fonts/Geist-Black.woff2 +0 -0
  262. package/src/fonts/Geist-Bold.woff2 +0 -0
  263. package/src/fonts/Geist-ExtraBold.woff2 +0 -0
  264. package/src/fonts/Geist-ExtraLight.woff2 +0 -0
  265. package/src/fonts/Geist-Light.woff2 +0 -0
  266. package/src/fonts/Geist-Medium.woff2 +0 -0
  267. package/src/fonts/Geist-Regular.woff2 +0 -0
  268. package/src/fonts/Geist-SemiBold.woff2 +0 -0
  269. package/src/fonts/Geist-Thin.woff2 +0 -0
  270. package/src/fonts/Geist[wght].woff2 +0 -0
  271. package/src/fonts/index.ts +10 -0
  272. package/src/index.ts +23 -0
  273. package/src/lib/safelist.tsx +16 -0
  274. package/src/lib/utils.ts +6 -0
  275. package/src/page-wizard/PageWizard.tsx +139 -0
  276. package/src/page-wizard/WizardBox.tsx +4 -0
  277. package/src/page-wizard/WizardBoxConnector.tsx +56 -0
  278. package/src/page-wizard/WizardSteps.tsx +458 -0
  279. package/src/page-wizard/service.ts +35 -0
  280. package/src/page-wizard/startPageWizardCommand.ts +26 -0
  281. package/src/page-wizard/steps/BuildPageStep.tsx +259 -0
  282. package/src/page-wizard/steps/CollectStep.tsx +296 -0
  283. package/src/page-wizard/steps/ComponentTypesSelector.tsx +454 -0
  284. package/src/page-wizard/steps/Components.tsx +193 -0
  285. package/src/page-wizard/steps/ContentStep.tsx +890 -0
  286. package/src/page-wizard/steps/EditButton.tsx +34 -0
  287. package/src/page-wizard/steps/FieldEditor.tsx +102 -0
  288. package/src/page-wizard/steps/Generate.tsx +60 -0
  289. package/src/page-wizard/steps/ImagesStep.tsx +382 -0
  290. package/src/page-wizard/steps/LayoutStep.tsx +227 -0
  291. package/src/page-wizard/steps/MetaDataStep.tsx +173 -0
  292. package/src/page-wizard/steps/SelectStep.tsx +281 -0
  293. package/src/page-wizard/steps/schema.ts +180 -0
  294. package/src/page-wizard/steps/usePageCreator.ts +325 -0
  295. package/src/page-wizard/usePageWizard.ts +79 -0
  296. package/src/revision.ts +2 -0
  297. package/src/splash-screen/NewPage.tsx +294 -0
  298. package/src/splash-screen/OpenPage.tsx +113 -0
  299. package/src/splash-screen/RecentPages.tsx +123 -0
  300. package/src/splash-screen/SectionHeadline.tsx +21 -0
  301. package/src/splash-screen/SplashScreen.tsx +195 -0
  302. package/src/tour/Tour.tsx +566 -0
  303. package/src/tour/default-tour.tsx +301 -0
  304. package/src/tour/preview-tour.tsx +128 -0
  305. package/src/types.ts +335 -0
  306. package/styles.css +765 -0
  307. package/tsconfig.build.json +31 -0
  308. package/tsconfig.json +14 -0
@@ -0,0 +1,227 @@
1
+ import { useEffect, useState } from "react";
2
+ import { WizardData, WizardPageModel } from "../PageWizard";
3
+
4
+ import { useEditContext } from "../../editor/client/editContext";
5
+ import { executePrompt } from "../../editor/services/aiService";
6
+ import { createWizardAiContext } from "../service";
7
+ import { Components } from "./Components";
8
+ import { Splitter, SplitterPanel } from "../../editor/ui/Splitter";
9
+ import { convertPageSchemaToWizardComponents } from "./schema";
10
+ import { StepComponentProps } from "../../config/types";
11
+ import { ActionButton } from "../../components/ActionButton";
12
+ import { ComponentTypeSelector } from "./ComponentTypesSelector";
13
+ export function LayoutStep({
14
+ wizard,
15
+ step,
16
+ data,
17
+ setData,
18
+ setStepCompleted,
19
+ internalState,
20
+ setInternalState,
21
+ }: StepComponentProps) {
22
+ const editContext = useEditContext();
23
+ const abortController = new AbortController();
24
+
25
+ const localAbortController = abortController || new AbortController();
26
+ const [isLoading, setIsLoading] = useState(false);
27
+
28
+ const [message, setMessage] = useState<string>();
29
+
30
+ const [selectedComponentTypes, setSelectedComponentTypes] = useState<
31
+ string[]
32
+ >([]);
33
+
34
+ const [customInstructions, setCustomInstructions] = useState<string>();
35
+
36
+ if (!editContext) {
37
+ return null;
38
+ }
39
+
40
+ useEffect(() => {
41
+ if (data.pageModel?.components?.length) {
42
+ setStepCompleted(!isLoading);
43
+ }
44
+ }, [data.pageModel, isLoading]);
45
+
46
+ useEffect(() => {
47
+ setCustomInstructions(step.fields.instructions);
48
+ }, []);
49
+
50
+ const createLayout = async () => {
51
+ try {
52
+ setIsLoading(true);
53
+
54
+ // Parse schema if it's a string
55
+ const schema =
56
+ typeof wizard.schema === "string"
57
+ ? JSON.parse(wizard.schema)
58
+ : wizard.schema;
59
+
60
+ // Filter the schema based on selected component types and placeholders
61
+ const filteredSchema = convertPageSchemaToWizardComponents(
62
+ schema,
63
+ selectedComponentTypes,
64
+ );
65
+
66
+ const result = await executePrompt(
67
+ [
68
+ {
69
+ content: `${customInstructions?.trim()} Reply with a json object of type PageModel = { name: string; metaDescription: string; components: Component[]; message: string; };
70
+ Component = { name: string, type: string; fields: Field[]; placeholder?: string; children?: Component[]; };
71
+ Field = { name: string; value: string; type: string; };
72
+ Generate a descriptive name for each component including the topic.
73
+ Only use component types that are in the page schema. Also suggest a page name following default sitecore item naming conventions (no underscores, no white space, no umlaute, no special characters, hyphens are allowed) and the page meta description.
74
+ Component types: ${JSON.stringify(
75
+ filteredSchema,
76
+ )} Root level component types ${filteredSchema
77
+ .filter((c) => c.allowedOnRoot)
78
+ .map((c) => c.type)
79
+ .join(", ")}
80
+ Tell the user your reasoning in the message field.
81
+ Fill image ids into picture / image fields.
82
+ Input data: ${JSON.stringify(data)}`,
83
+
84
+ name: "system",
85
+ role: "system",
86
+ },
87
+ ],
88
+ editContext,
89
+ createWizardAiContext,
90
+ {},
91
+ { signal: localAbortController.signal },
92
+ "gpt-4.1",
93
+ (response) => {
94
+ try {
95
+ const newLayout = JSON.parse(response.content) as WizardPageModel;
96
+
97
+ if (newLayout) {
98
+ setData((prev: WizardData) => ({
99
+ ...prev,
100
+ pageModel: newLayout,
101
+ }));
102
+ }
103
+
104
+ setMessage(newLayout.message);
105
+ } catch (parseError: unknown) {}
106
+ },
107
+ );
108
+
109
+ const pageModel = JSON.parse(result.content);
110
+
111
+ setData({
112
+ ...data,
113
+ pageModel: JSON.parse(result.content),
114
+ customInstructions,
115
+ });
116
+
117
+ setMessage(pageModel.message);
118
+ } catch (error) {
119
+ console.error(error);
120
+ } finally {
121
+ setIsLoading(false);
122
+ }
123
+ };
124
+
125
+ // Toggle placeholder selection
126
+
127
+ // Regenerate layout with current selected component types and placeholders
128
+ const regenerateLayout = () => {
129
+ createLayout();
130
+ };
131
+
132
+ useEffect(() => {
133
+ if (!selectedComponentTypes.length) return;
134
+
135
+ // Only set custom instructions if they're not already set or if step.instructions has changed
136
+ if (customInstructions !== step.fields.instructions) {
137
+ setCustomInstructions(step.fields.instructions);
138
+ }
139
+ }, [step.fields.instructions, selectedComponentTypes]);
140
+
141
+ // Custom instructions panel
142
+ const customInstructionsPanel = () => {
143
+ return (
144
+ <div className="mb-4">
145
+ <h3 className="text-sm font-bold">Instructions</h3>
146
+ <div className="mb-3 text-xs text-gray-500">
147
+ Provide guidance for the AI when generating your layout
148
+ </div>
149
+ <textarea
150
+ value={customInstructions}
151
+ onChange={(e) => setCustomInstructions(e.target.value)}
152
+ placeholder="Example: Make it modern and minimalist. Focus on images. Include a hero section at the top."
153
+ className="h-48 w-full rounded border border-gray-300 px-3 py-2 text-sm focus:ring-1 focus:ring-blue-500 focus:outline-none"
154
+ disabled={isLoading}
155
+ />
156
+ </div>
157
+ );
158
+ };
159
+
160
+ const settingsPanel: SplitterPanel = {
161
+ name: "Settings",
162
+ collapsible: false,
163
+ defaultSize: 450,
164
+ content: (
165
+ <div className="absolute inset-0 flex h-full flex-col overflow-auto pr-4">
166
+ {customInstructionsPanel()}
167
+ <ComponentTypeSelector
168
+ selectedComponentTypes={selectedComponentTypes}
169
+ setSelectedComponentTypes={setSelectedComponentTypes}
170
+ schema={wizard.schema}
171
+ data={data}
172
+ setData={setData}
173
+ step={step}
174
+ />
175
+ <div className="flex gap-2">
176
+ <ActionButton
177
+ onClick={regenerateLayout}
178
+ disabled={isLoading || selectedComponentTypes.length === 0}
179
+ isLoading={isLoading}
180
+ loadingText="Working"
181
+ className="flex-1"
182
+ >
183
+ {data.pageModel ? "Regenerate Layout" : "Generate Layout"}
184
+ </ActionButton>
185
+ {/* Abort button - only visible when loading */}
186
+ {isLoading && (
187
+ <button
188
+ onClick={() => {
189
+ localAbortController.abort();
190
+ setIsLoading(false);
191
+ }}
192
+ className="flex items-center gap-1 rounded bg-red-500 px-2 py-1 text-xs text-white hover:bg-red-600"
193
+ >
194
+ <span className="pi pi-times"></span>
195
+ Abort
196
+ </button>
197
+ )}
198
+ </div>
199
+ <div className="p-2 text-xs text-gray-500">{message}</div>
200
+ </div>
201
+ ),
202
+ };
203
+
204
+ const layoutPanel: SplitterPanel = {
205
+ name: "Layout",
206
+ defaultSize: "auto",
207
+ collapsible: false,
208
+ content: (
209
+ <div className="absolute inset-2 flex flex-col items-center justify-center text-gray-600">
210
+ <Components
211
+ pageModel={data.pageModel}
212
+ onComponentRemoved={() => setData({ ...data })}
213
+ onFieldEdited={() => setData({ ...data })}
214
+ thumbnails={internalState.thumbnails}
215
+ setInternalState={setInternalState}
216
+ />
217
+ </div>
218
+ ),
219
+ };
220
+
221
+ return (
222
+ <Splitter
223
+ panels={[settingsPanel, layoutPanel]}
224
+ localStorageKey="editor.page-wizard.layout"
225
+ />
226
+ );
227
+ }
@@ -0,0 +1,173 @@
1
+ import { useEffect, useState } from "react";
2
+ import { FullItem } from "../../editor/pageModel";
3
+ import { WizardPageModel } from "../PageWizard";
4
+ import { executePrompt } from "../../editor/services/aiService";
5
+ import { createWizardAiContext } from "../service";
6
+ import { useEditContext } from "../../editor/client/editContext";
7
+ import { InputTextarea } from "primereact/inputtextarea";
8
+ import { WizardBox } from "../WizardBox";
9
+ import { FileText } from "lucide-react";
10
+ import { StepComponentProps } from "../../config/types";
11
+ import Generate from "./Generate";
12
+
13
+ export function MetaDataStep({
14
+ parentItem,
15
+ pageModel,
16
+ setPageModel,
17
+ step,
18
+ data,
19
+ setStepCompleted,
20
+ }: StepComponentProps) {
21
+ const [isGenerating, setIsGenerating] = useState(false);
22
+ const [fullParentItem, setFullParentItem] = useState<FullItem>();
23
+ const editContext = useEditContext();
24
+
25
+ useEffect(() => {
26
+ if (!editContext) return;
27
+
28
+ if (editContext.mode !== "edit") {
29
+ editContext.setMode("edit");
30
+ }
31
+ }, [editContext]);
32
+
33
+ useEffect(() => {
34
+ const loadParentItem = async () => {
35
+ if (!parentItem) return;
36
+ const item = await editContext?.itemsRepository.getItem(parentItem);
37
+ setFullParentItem(item);
38
+ };
39
+ loadParentItem();
40
+ }, [parentItem]);
41
+
42
+ // Mark step as completed immediately since this is now just informational
43
+ useEffect(() => {
44
+ setStepCompleted(true);
45
+ }, [setStepCompleted]);
46
+
47
+ // Auto-generate meta description and keywords if not set
48
+ useEffect(() => {
49
+ if (!editContext) return;
50
+
51
+ const generateMetaFields = async () => {
52
+ const metaInstructions =
53
+ step.fields["Instructions for generating item name and meta fields"];
54
+
55
+ setIsGenerating(true);
56
+
57
+ try {
58
+ const abortController = new AbortController();
59
+
60
+ const result = await executePrompt(
61
+ [
62
+ {
63
+ content: `${metaInstructions?.trim()} Reply with a json object of type PageModel = { metaDescription: string; metaKeywords: string; };
64
+ Generate SEO-optimized meta description and keywords.
65
+ The language of the page is ${parentItem?.language || "en"}.
66
+ Input data: ${JSON.stringify(data)}`,
67
+ name: "system",
68
+ role: "system",
69
+ },
70
+ ],
71
+ editContext,
72
+ createWizardAiContext,
73
+ {},
74
+ { signal: abortController.signal },
75
+ "gpt-4.1",
76
+ (response) => {
77
+ try {
78
+ const newLayout = JSON.parse(response.content) as WizardPageModel;
79
+
80
+ setPageModel({
81
+ ...pageModel,
82
+ metaDescription: newLayout.metaDescription,
83
+ metaKeywords: newLayout.metaKeywords,
84
+ });
85
+ } catch (parseError: unknown) {}
86
+ },
87
+ );
88
+
89
+ const pageModelResult = JSON.parse(result.content) as WizardPageModel;
90
+ setPageModel({
91
+ ...pageModel,
92
+ metaDescription: pageModelResult.metaDescription,
93
+ metaKeywords: pageModelResult.metaKeywords,
94
+ });
95
+ } catch (error) {
96
+ console.error("Error generating meta fields", error);
97
+ } finally {
98
+ setIsGenerating(false);
99
+ }
100
+ };
101
+
102
+ if (!pageModel.metaDescription && !pageModel.metaKeywords) {
103
+ generateMetaFields();
104
+ }
105
+ }, []);
106
+
107
+ const handleInputChange = (field: keyof typeof pageModel, value: string) => {
108
+ const updatedPageModel = {
109
+ ...pageModel,
110
+ [field]: value,
111
+ };
112
+
113
+ setPageModel(updatedPageModel);
114
+ };
115
+
116
+ return (
117
+ <div className="flex h-full flex-col md:flex-row">
118
+ <WizardBox
119
+ title="SEO Settings"
120
+ icon={<FileText />}
121
+ description="Configure meta information for SEO optimization"
122
+ className="flex-1"
123
+ >
124
+ <div className="flex h-full flex-col items-stretch">
125
+ <div className="relative flex-1">
126
+ {isGenerating ? (
127
+ <div className="flex h-full items-center justify-center">
128
+ <Generate title="Generating SEO fields..." />
129
+ </div>
130
+ ) : (
131
+ <>
132
+ <div className="mb-4">
133
+ <label
134
+ htmlFor="metaDescription"
135
+ className="mb-1 block text-sm font-medium"
136
+ >
137
+ Meta Description
138
+ </label>
139
+ <InputTextarea
140
+ id="metaDescription"
141
+ className="min-h-[100px] w-full rounded border p-2 text-sm"
142
+ value={pageModel.metaDescription}
143
+ onChange={(e) =>
144
+ handleInputChange("metaDescription", e.target.value)
145
+ }
146
+ placeholder="Enter meta description"
147
+ />
148
+ </div>
149
+ <div className="mb-4">
150
+ <label
151
+ htmlFor="metaKeywords"
152
+ className="mb-1 block text-sm font-medium"
153
+ >
154
+ Meta Keywords
155
+ </label>
156
+ <InputTextarea
157
+ id="metaKeywords"
158
+ className="min-h-[100px] w-full rounded border p-2 text-sm"
159
+ value={pageModel.metaKeywords}
160
+ onChange={(e) =>
161
+ handleInputChange("metaKeywords", e.target.value)
162
+ }
163
+ placeholder="Enter meta keywords"
164
+ />
165
+ </div>
166
+ </>
167
+ )}
168
+ </div>
169
+ </div>
170
+ </WizardBox>
171
+ </div>
172
+ );
173
+ }
@@ -0,0 +1,281 @@
1
+ import { useState, useEffect } from "react";
2
+ import { WizardData } from "../PageWizard";
3
+ import { useEditContext } from "../../editor/client/editContext";
4
+ import { executePrompt } from "../../editor/services/aiService";
5
+ import { createWizardAiContext } from "../service";
6
+ import { classNames } from "primereact/utils";
7
+ import { InputTextarea } from "primereact/inputtextarea";
8
+ import { StepComponentProps } from "../../config/types";
9
+ import { ActionButton } from "../../components/ActionButton";
10
+ import Generate from "./Generate";
11
+ import { WizardBox } from "../WizardBox";
12
+ import { WizardBoxConnector } from "../WizardBoxConnector";
13
+ import { Settings, CheckSquare } from "lucide-react";
14
+
15
+ type Option = {
16
+ title: string;
17
+ description: string;
18
+ id: string;
19
+ };
20
+
21
+ type Response = {
22
+ options: Option[];
23
+ };
24
+
25
+ export function SelectStep({
26
+ step,
27
+ data,
28
+ setData,
29
+ setStepCompleted,
30
+ internalState,
31
+ setInternalState,
32
+ }: StepComponentProps) {
33
+ const editContext = useEditContext();
34
+ const [options, setOptions] = useState<Option[]>([]);
35
+ const [loading, setLoading] = useState(false);
36
+ const [error, setError] = useState<string | null>(null);
37
+ const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
38
+ const [instructions, setInstructions] = useState(step.fields.instructions);
39
+
40
+ // Get the property name from the step or use "choice" as default
41
+ const propertyName = step.fields.propertyName || "selectedOptions";
42
+
43
+ useEffect(() => {
44
+ if (data[propertyName]?.length) {
45
+ setStepCompleted(true);
46
+ }
47
+ }, [data]);
48
+
49
+ // Initialize the property in data
50
+ useEffect(() => {
51
+ if (!data[propertyName]) {
52
+ setData((prevData: WizardData) => ({
53
+ ...prevData,
54
+ [propertyName]: [],
55
+ }));
56
+ }
57
+ }, [propertyName, setData]);
58
+
59
+ const generateOptions = async () => {
60
+ if (!editContext) return;
61
+
62
+ setLoading(true);
63
+ setError(null);
64
+
65
+ try {
66
+ // Create a prompt based on step instructions and data
67
+ const prompt = `${step.fields.systemInstructions}\n${instructions}\n\nCurrent data: ${JSON.stringify(
68
+ data,
69
+ null,
70
+ 2,
71
+ )}`;
72
+
73
+ // Call the executePrompt function with the prompt
74
+ const result = await executePrompt(
75
+ [
76
+ {
77
+ content: `You are a helpful assistant that generates options for a wizard select step.
78
+ Generate a JSON array of options based on the instructions and current data.
79
+ ONLY respond with a valid JSON array without any explanation or additional text.
80
+ JSON format: {options: {title: string, description: string}[] }`,
81
+ name: "system",
82
+ role: "system",
83
+ },
84
+ {
85
+ content: prompt,
86
+ name: "user",
87
+ role: "user",
88
+ },
89
+ ],
90
+ editContext,
91
+ createWizardAiContext,
92
+ { allowedFunctions: [] },
93
+ undefined,
94
+ step.fields.aiModel || "gpt-4.1",
95
+ );
96
+
97
+ // Parse the result and set options
98
+ if (result && result.content) {
99
+ try {
100
+ const generatedOptions = JSON.parse(result.content) as Response;
101
+ for (let i = 0; i < generatedOptions.options.length; i++) {
102
+ generatedOptions.options[i]!.id = i.toString();
103
+ }
104
+
105
+ setOptions(generatedOptions.options);
106
+ setInternalState((state: any) => ({
107
+ ...state,
108
+ [step.id + "options"]: generatedOptions.options,
109
+ }));
110
+ } catch (parseError) {
111
+ console.error("Error parsing options:", parseError);
112
+ setError(
113
+ "Failed to parse options. The response was not in the expected format.",
114
+ );
115
+ }
116
+ } else {
117
+ setError("No options were generated. Please try again.");
118
+ }
119
+ } catch (err) {
120
+ setError("Failed to generate options. Please try again.");
121
+ console.error("Error generating options:", err);
122
+ } finally {
123
+ setLoading(false);
124
+ }
125
+ };
126
+
127
+ // Generate options on component mount
128
+ useEffect(() => {
129
+ if (internalState[step.id + "options"]?.length > 0) {
130
+ setOptions(internalState[step.id + "options"]);
131
+ return;
132
+ }
133
+ generateOptions();
134
+ }, [step.fields.instructions, internalState]);
135
+
136
+ // Sync data[propertyName] with selectedOptions state
137
+ useEffect(() => {
138
+ const currentSelections = (data[propertyName] as Array<Option>) || [];
139
+ setSelectedOptions(currentSelections.map((option: Option) => option.id));
140
+ }, [data, propertyName]);
141
+
142
+ // Handle option selection
143
+ const handleOptionSelect = (option: Option) => {
144
+ setData((prevData: WizardData) => {
145
+ const currentData = [
146
+ ...((prevData[propertyName] as Array<Option>) || []),
147
+ ];
148
+ const isSelected = currentData.some((o: Option) => o.id === option.id);
149
+
150
+ let updatedData;
151
+ if (isSelected) {
152
+ // Remove option if already selected
153
+ updatedData = currentData.filter((o: Option) => o.id !== option.id);
154
+ } else {
155
+ // Add option if not selected
156
+ updatedData = [...currentData, option];
157
+ }
158
+
159
+ return {
160
+ ...prevData,
161
+ [propertyName]: updatedData,
162
+ };
163
+ });
164
+ };
165
+
166
+ if (error) {
167
+ return (
168
+ <div className="p-4 text-red-500">
169
+ {error}
170
+ <button
171
+ className="ml-4 rounded bg-blue-500 px-2 py-1 text-white"
172
+ onClick={() => window.location.reload()}
173
+ >
174
+ Retry
175
+ </button>
176
+ </div>
177
+ );
178
+ }
179
+
180
+ return (
181
+ <div className="flex h-full flex-col md:flex-row">
182
+ <WizardBox
183
+ title={step.fields.configureTitle || "Configuration"}
184
+ icon={<Settings />}
185
+ description={
186
+ step.fields.configureDescription ||
187
+ "Configure instructions and regenerate options"
188
+ }
189
+ className="md:w-96"
190
+ >
191
+ <div className="flex h-full flex-col items-stretch">
192
+ <div className="relative flex-1">
193
+ <div className="absolute inset-0 overflow-y-auto">
194
+ <h3 className="text-sm font-bold">Instructions</h3>
195
+ <InputTextarea
196
+ className="h-48 w-full text-sm"
197
+ value={instructions}
198
+ onChange={(e) => setInstructions(e.target.value)}
199
+ placeholder="Enter instructions for generating the options"
200
+ />
201
+ </div>
202
+ </div>
203
+ <div className="py-2">
204
+ <ActionButton
205
+ onClick={generateOptions}
206
+ isLoading={loading}
207
+ disabled={loading}
208
+ loadingText="Thinking..."
209
+ className="w-full"
210
+ >
211
+ Regenerate Options
212
+ </ActionButton>
213
+ </div>
214
+ </div>
215
+ </WizardBox>
216
+ <WizardBoxConnector />
217
+ <WizardBox
218
+ title={step.fields.selectOptionsTitle || "Select Options"}
219
+ icon={<CheckSquare />}
220
+ description={
221
+ step.fields.selectOptionsDescription ||
222
+ "Choose from the generated options below"
223
+ }
224
+ className="flex-1"
225
+ >
226
+ <div className="h-full">
227
+ {loading && (
228
+ <div className="flex h-full items-center justify-center">
229
+ <Generate
230
+ title={
231
+ step.fields.generatingOptionsText || "Generating options..."
232
+ }
233
+ />
234
+ </div>
235
+ )}
236
+ {!loading && (
237
+ <div>
238
+ {options.length === 0 ? (
239
+ <div className="p-4 text-gray-500">
240
+ No options available. Try refreshing the page.
241
+ </div>
242
+ ) : (
243
+ options.map((option, index) => {
244
+ const isSelected = selectedOptions.includes(option.id);
245
+
246
+ return (
247
+ <div
248
+ key={option.id}
249
+ className={classNames(
250
+ "mb-3 flex items-center rounded border-2 bg-white p-3",
251
+ index % 2 === 0
252
+ ? "animate-fadeRight"
253
+ : "animate-fadeLeft",
254
+ isSelected
255
+ ? "text-theme-secondary border-theme-secondary"
256
+ : "border-gray-300 text-gray-600",
257
+ )}
258
+ onClick={() => handleOptionSelect(option)}
259
+ >
260
+ <div className="flex flex-1 cursor-pointer flex-col">
261
+ <div className="font-medium">{option.title}</div>
262
+ <div className="text-xs text-gray-600">
263
+ {option.description}
264
+ </div>
265
+ </div>
266
+ {isSelected && (
267
+ <div className="bg-theme-secondary ml-2 flex h-6 w-6 items-center justify-center rounded-full text-white">
268
+
269
+ </div>
270
+ )}
271
+ </div>
272
+ );
273
+ })
274
+ )}
275
+ </div>
276
+ )}
277
+ </div>
278
+ </WizardBox>
279
+ </div>
280
+ );
281
+ }