@alpaca-editor/core 1.0.3942 → 1.0.3944

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,259 @@
1
+ import { Splitter, SplitterPanel } from "../../editor/ui/Splitter";
2
+
3
+ import { FullItem, ItemDescriptor } from "../../editor/pageModel";
4
+ import { useEditContext } from "../../editor/client/editContext";
5
+
6
+ import { useEffect, useState } from "react";
7
+ import { StepComponentProps } from "../../config/types";
8
+ import { PageViewer } from "../../editor/page-viewer/PageViewer";
9
+
10
+ import { useDebouncedCallback } from "use-debounce";
11
+ import { getChildren } from "../../editor/services/contentService";
12
+ import { ActionButton } from "../../components/ActionButton";
13
+ import Generate from "./Generate";
14
+ import { usePageCreator } from "./usePageCreator";
15
+
16
+ export function BuildPageStep({
17
+ data,
18
+ setData,
19
+ parentItem,
20
+ wizard,
21
+ setStepCompleted,
22
+ }: StepComponentProps) {
23
+ const editContext = useEditContext();
24
+
25
+ const [createdPageDescriptor, setCreatedPageDescriptor] =
26
+ useState<ItemDescriptor>();
27
+ const [pageLoaded, setPageLoaded] = useState(false);
28
+
29
+ const [fullParentItem, setFullParentItem] = useState<FullItem>();
30
+ const [isBuilding, setIsBuilding] = useState(false);
31
+
32
+ const [validationMessage, setValidationMessage] = useState<string>();
33
+
34
+ useEffect(() => {
35
+ if (
36
+ createdPageDescriptor &&
37
+ createdPageDescriptor.id === editContext?.page?.item?.descriptor.id
38
+ )
39
+ setPageLoaded(true);
40
+ }, [editContext?.page]);
41
+
42
+ useEffect(() => {
43
+ const loadParentItem = async () => {
44
+ if (!parentItem) return;
45
+ const item = await editContext?.itemsRepository.getItem(parentItem);
46
+ setFullParentItem(item);
47
+ };
48
+ loadParentItem();
49
+ }, [parentItem]);
50
+
51
+ useEffect(() => {
52
+ const buildComponents = async () => {
53
+ try {
54
+ console.log(
55
+ "Building components",
56
+ createdPageDescriptor,
57
+ editContext?.page,
58
+ );
59
+ // Recursively create components from page model
60
+ if (pageModel.components && pageModel.components.length > 0) {
61
+ await pageCreator.createComponentsRecursively(
62
+ pageModel.components,
63
+ "root",
64
+ );
65
+ }
66
+ editContext?.requestRefresh();
67
+ console.log(
68
+ "Components built",
69
+ createdPageDescriptor,
70
+ editContext?.page,
71
+ );
72
+ } finally {
73
+ setIsBuilding(false);
74
+ }
75
+ };
76
+ if (pageLoaded) {
77
+ buildComponents();
78
+ }
79
+ }, [pageLoaded]);
80
+
81
+ // Initialize pageModel if it doesn't exist
82
+ const pageModel = data.pageModel || {
83
+ components: [],
84
+ name: "",
85
+ metaDescription: "",
86
+ };
87
+
88
+ // Handle input changes
89
+ const handleInputChange = (field: keyof typeof pageModel, value: string) => {
90
+ const updatedPageModel = {
91
+ ...pageModel,
92
+ [field]: value,
93
+ };
94
+
95
+ setData({
96
+ ...data,
97
+ pageModel: updatedPageModel,
98
+ });
99
+ };
100
+
101
+ const checkName = async () => {
102
+ if (!parentItem) return;
103
+ let valid: boolean =
104
+ !!wizard.templateId && pageModel.name.trim().length >= 3;
105
+ if (valid) {
106
+ const children = await getChildren(
107
+ parentItem.id,
108
+ editContext?.sessionId ?? "",
109
+ [],
110
+ false,
111
+ editContext?.contentEditorItem?.language || "en",
112
+ );
113
+ if (
114
+ children.find(
115
+ (x) =>
116
+ x.name.toLocaleLowerCase() ===
117
+ pageModel.name.trim().toLocaleLowerCase(),
118
+ )
119
+ ) {
120
+ valid = false;
121
+ setValidationMessage("A page with this name already exists.");
122
+ }
123
+ } else {
124
+ if (pageModel.name.trim().length > 0 && pageModel.name.trim().length < 3)
125
+ setValidationMessage("Name is too short.");
126
+ else setValidationMessage(undefined);
127
+ }
128
+
129
+ if (valid) setValidationMessage(undefined);
130
+ return valid;
131
+ };
132
+
133
+ const checkNameValidDebounced = useDebouncedCallback(
134
+ async () => checkName(),
135
+ 500,
136
+ );
137
+
138
+ useEffect(() => {
139
+ checkNameValidDebounced();
140
+ }, [pageModel.name]);
141
+
142
+ const createPage = () => {
143
+ setTimeout(async () => {
144
+ if (!editContext || !parentItem) return;
145
+ try {
146
+ if (!(await checkName())) return;
147
+ setIsBuilding(true);
148
+ const result = await editContext.operations.createItem(
149
+ parentItem,
150
+ wizard.templateId,
151
+ pageModel.name,
152
+ );
153
+ if (!result) return;
154
+ editContext?.loadItem(result, { addToBrowseHistory: true });
155
+
156
+ setCreatedPageDescriptor(result);
157
+
158
+ console.log("Page created", result, "Page model", pageModel);
159
+ setStepCompleted(true);
160
+ } catch (error) {
161
+ console.error("Error creating page", error);
162
+ setIsBuilding(false);
163
+ }
164
+ }, 1);
165
+ };
166
+
167
+ const pageCreator = usePageCreator(
168
+ createdPageDescriptor,
169
+ wizard,
170
+ (pageModel) => setData({ ...data, pageModel }),
171
+ );
172
+
173
+ const settingsPanel: SplitterPanel = {
174
+ name: "settings",
175
+ defaultSize: 450,
176
+ collapsible: false,
177
+ content: (
178
+ <div className="pr-6">
179
+ <div className="mb-4">
180
+ <div className="mb-1 text-sm font-medium">Target Parent Item</div>
181
+ <div className="mb-4 break-after-all text-xs">
182
+ {fullParentItem?.path}
183
+ </div>
184
+ </div>
185
+ <div className="mb-4">
186
+ <label htmlFor="pageName" className="mb-1 block text-sm font-medium">
187
+ Page Name
188
+ </label>
189
+ <input
190
+ id="pageName"
191
+ type="text"
192
+ className="w-full rounded border p-2 text-sm"
193
+ value={pageModel.name}
194
+ onChange={(e) => handleInputChange("name", e.target.value)}
195
+ placeholder="Enter page name"
196
+ />
197
+ {validationMessage && (
198
+ <div className="mt-2 text-sm text-red-500">{validationMessage}</div>
199
+ )}
200
+ </div>
201
+
202
+ <div className="mb-4">
203
+ <label
204
+ htmlFor="metaDescription"
205
+ className="mb-1 block text-sm font-medium"
206
+ >
207
+ Meta Description
208
+ </label>
209
+ <textarea
210
+ id="metaDescription"
211
+ className="min-h-[100px] w-full rounded border p-2 text-sm"
212
+ value={pageModel.metaDescription}
213
+ onChange={(e) =>
214
+ handleInputChange("metaDescription", e.target.value)
215
+ }
216
+ placeholder="Enter meta description"
217
+ />
218
+ </div>
219
+ <ActionButton
220
+ disabled={isBuilding || !!validationMessage}
221
+ className="w-full flex-1"
222
+ onClick={() => {
223
+ createPage();
224
+ }}
225
+ isLoading={isBuilding}
226
+ loadingText="Working"
227
+ >
228
+ Create Page
229
+ </ActionButton>
230
+ </div>
231
+ ),
232
+ };
233
+
234
+ const contentPanel: SplitterPanel = {
235
+ name: "content",
236
+ defaultSize: "auto",
237
+ collapsible: false,
238
+ content: (
239
+ <>
240
+ <div className={pageLoaded ? "h-full" : "h-0"}>
241
+ <PageViewer
242
+ name="single"
243
+ compareView={false}
244
+ showFormEditor={false}
245
+ followEditsDefault={true}
246
+ pageViewContext={editContext!.pageView}
247
+ />
248
+ </div>
249
+ {isBuilding && !pageLoaded && (
250
+ <div className="flex h-full items-center justify-center">
251
+ <Generate title="Building page..." />
252
+ </div>
253
+ )}
254
+ </>
255
+ ),
256
+ };
257
+
258
+ return <Splitter panels={[settingsPanel, contentPanel]}></Splitter>;
259
+ }
@@ -0,0 +1,296 @@
1
+ import { InputText } from "primereact/inputtext";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { WizardData } from "../PageWizard";
4
+
5
+ import { StepComponentProps } from "../../config/types";
6
+ import { ActionButton } from "../../components/ActionButton";
7
+ import { UploadIcon, DocumentIcon } from "../../editor/ui/Icons";
8
+ import { Spinner } from "../../editor/ui/Spinner";
9
+ import { Card } from "../../components/ui/card";
10
+ import { Button } from "../../components/ui/button";
11
+ import { LinkIcon } from "lucide-react";
12
+ import { InputTextarea } from "primereact/inputtextarea";
13
+
14
+ export function CollectStep({
15
+ step,
16
+ data,
17
+ setData,
18
+ setStepCompleted,
19
+ }: StepComponentProps) {
20
+ const [isUploading, setIsUploading] = useState(false);
21
+ const [isScraping, setIsScraping] = useState(false);
22
+ const [error, setError] = useState<string | null>(null);
23
+ const [scrapeError, setScrapeError] = useState<string | null>(null);
24
+ const [scrapeUrl, setScrapeUrl] = useState<string | null>(null);
25
+ const fileInputRef = useRef<HTMLInputElement>(null);
26
+
27
+ // Extract configuration from step
28
+ const enableUpload = step?.fields.enableUpload === "1";
29
+ const enableScrape = step?.fields.enableScrape === "1";
30
+ const enableText = step?.fields.enableText === "1";
31
+
32
+ const uploadTitle = step?.fields.uploadTitle || "Upload a file (optional)";
33
+ const uploadDescription =
34
+ step?.fields.uploadDescription || "Upload a file to the page";
35
+
36
+ const scrapeTitle =
37
+ step?.fields.scrapeTitle || "Scrape page from URL (optional)";
38
+ const scrapeDescription =
39
+ step?.fields.scrapeDescription ||
40
+ "Only scrape websites you have permission to access. You are responsible for complying with the website's terms of service and all applicable laws.";
41
+
42
+ const textTitle = step?.fields.textTitle || "Text";
43
+ const textDescription =
44
+ step?.fields.textDescription ||
45
+ "Provide the text you want to use for the page";
46
+
47
+ const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+
51
+ if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
52
+ uploadFile(e.dataTransfer.files[0]!);
53
+ setError(null);
54
+ }
55
+ };
56
+
57
+ useEffect(() => {
58
+ if (data.htmlContent) {
59
+ setStepCompleted(true);
60
+ }
61
+ }, [data.htmlContent]);
62
+
63
+ const uploadFile = async (file: File) => {
64
+ if (!file) {
65
+ setError("Please select a file first");
66
+ return;
67
+ }
68
+
69
+ try {
70
+ setIsUploading(true);
71
+ setError(null);
72
+
73
+ const formData = new FormData();
74
+ formData.append("uploadedFile", file);
75
+
76
+ const response = await fetch("/alpaca/editor/page-wizard/convertFile", {
77
+ method: "POST",
78
+ body: formData,
79
+ });
80
+
81
+ if (!response.ok) {
82
+ throw new Error(
83
+ `Upload failed: ${response.status} ${response.statusText}`,
84
+ );
85
+ }
86
+
87
+ const htmlContent = await response.text();
88
+ setData((prev: WizardData) => ({
89
+ ...prev,
90
+ htmlContent: htmlContent,
91
+ }));
92
+ } catch (err) {
93
+ setError(err instanceof Error ? err.message : "Unknown error occurred");
94
+ } finally {
95
+ setIsUploading(false);
96
+ }
97
+ };
98
+
99
+ const triggerFileInput = () => {
100
+ fileInputRef.current ? (fileInputRef.current.value = "") : "";
101
+
102
+ fileInputRef.current?.click();
103
+ };
104
+
105
+ const handleScrape = async (e: React.FormEvent) => {
106
+ e.preventDefault();
107
+
108
+ try {
109
+ setScrapeError(null);
110
+
111
+ if (!scrapeUrl) {
112
+ setScrapeError("Please enter a URL first");
113
+ return;
114
+ }
115
+
116
+ setIsScraping(true);
117
+ const response = await fetch("/alpaca/editor/page-wizard/scrape", {
118
+ method: "POST",
119
+ body: JSON.stringify({ url: scrapeUrl }),
120
+ headers: {
121
+ "Content-Type": "application/json",
122
+ },
123
+ });
124
+
125
+ if (!response.ok) {
126
+ throw new Error(
127
+ `Scraping failed: ${response.status} ${response.statusText}`,
128
+ );
129
+ }
130
+
131
+ const htmlContent = await response.text();
132
+
133
+ setData((prev: WizardData) => ({
134
+ ...prev,
135
+ htmlContent: htmlContent,
136
+ }));
137
+ } catch (err) {
138
+ setScrapeError(
139
+ err instanceof Error ? err.message : "Unknown error occurred",
140
+ );
141
+ } finally {
142
+ setIsScraping(false);
143
+ }
144
+ };
145
+
146
+ return (
147
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
148
+ {enableUpload && (
149
+ <Card
150
+ icon={<UploadIcon className="text-theme-secondary" />}
151
+ title={uploadTitle}
152
+ collapsible="mobileOnly"
153
+ defaultCollapsed="mobileOnly"
154
+ description={uploadDescription}
155
+ >
156
+ {error && <div className="mt-2 text-sm text-red-500">{error}</div>}
157
+ <div
158
+ className={`flex flex-col items-center justify-center border border-dashed border-gray-300 p-8 ${
159
+ isUploading ? "" : "bg-white"
160
+ }`}
161
+ onDrop={handleDrop}
162
+ onDragOver={(e) => e.preventDefault()}
163
+ >
164
+ <div className="flex flex-col items-center justify-center gap-1 text-center text-sm">
165
+ {!isUploading && (
166
+ <svg
167
+ width="49"
168
+ height="48"
169
+ viewBox="0 0 49 48"
170
+ fill="none"
171
+ xmlns="http://www.w3.org/2000/svg"
172
+ >
173
+ <rect
174
+ x="0.666992"
175
+ width="48"
176
+ height="48"
177
+ rx="24"
178
+ fill="#F6EEFF"
179
+ />
180
+ <path
181
+ d="M32.604 20.68C32.594 20.648 32.583 20.6167 32.571 20.586C32.5235 20.4775 32.4572 20.3783 32.375 20.293L26.375 14.293C26.2897 14.2108 26.1905 14.1445 26.082 14.097C26.052 14.083 26.02 14.075 25.988 14.064C25.9043 14.0356 25.8172 14.0185 25.729 14.013C25.707 14.011 25.688 14 25.667 14H18.667C17.564 14 16.667 14.897 16.667 16V32C16.667 33.103 17.564 34 18.667 34H30.667C31.77 34 32.667 33.103 32.667 32V21C32.667 20.979 32.656 20.96 32.654 20.938C32.6487 20.8502 32.6319 20.7634 32.604 20.68ZM29.253 20H26.667V17.414L29.253 20ZM18.667 32V16H24.667V21C24.667 21.2652 24.7723 21.5196 24.9599 21.7071C25.1474 21.8946 25.4018 22 25.667 22H30.667L30.669 32H18.667Z"
182
+ fill="#9650FB"
183
+ />
184
+ </svg>
185
+ )}
186
+ {isUploading && <Spinner />}
187
+ {!isUploading && (
188
+ <>
189
+ <input
190
+ ref={fileInputRef}
191
+ type="file"
192
+ multiple
193
+ accept=".pdf,.doc,.docx"
194
+ style={{ display: "none" }}
195
+ onChange={(e) => {
196
+ const file = e.target.files?.[0];
197
+ if (file) {
198
+ uploadFile(file);
199
+ }
200
+ }}
201
+ onDrop={handleDrop}
202
+ disabled={isUploading}
203
+ />
204
+ <div className="flex items-center gap-1">
205
+ <span
206
+ className={`text-sm font-medium ${
207
+ isUploading
208
+ ? "text-gray-500"
209
+ : "text-theme-secondary cursor-pointer underline"
210
+ } `}
211
+ onClick={triggerFileInput}
212
+ >
213
+ Click
214
+ </span>
215
+ or drag & drop to upload
216
+ </div>
217
+ </>
218
+ )}
219
+ </div>
220
+ </div>
221
+ </Card>
222
+ )}
223
+ {enableScrape && (
224
+ <Card
225
+ icon={<LinkIcon className="text-theme-secondary" />}
226
+ title={scrapeTitle}
227
+ description={scrapeDescription}
228
+ collapsible="mobileOnly"
229
+ defaultCollapsed="mobileOnly"
230
+ >
231
+ <div className="flex flex-col gap-2">
232
+ <InputText
233
+ type="text"
234
+ className="rounded-md border border-gray-300 px-3 py-2"
235
+ onChange={(e) => setScrapeUrl(e.target.value)}
236
+ />
237
+ {scrapeError && (
238
+ <div className="mt-2 text-sm text-red-500">{scrapeError}</div>
239
+ )}
240
+ <div className="flex gap-2">
241
+ <ActionButton
242
+ type="submit"
243
+ isLoading={isScraping}
244
+ onClick={handleScrape}
245
+ loadingText="Scraping..."
246
+ >
247
+ Scrape
248
+ </ActionButton>
249
+ </div>
250
+ </div>
251
+ </Card>
252
+ )}
253
+ {enableText && (
254
+ <Card
255
+ icon={<DocumentIcon className="text-theme-secondary" />}
256
+ title={textTitle}
257
+ description={textDescription}
258
+ className={`${!enableUpload && !enableScrape ? "md:col-span-2" : enableUpload && enableScrape ? "md:col-span-2" : ""}`}
259
+ >
260
+ <InputTextarea
261
+ className="border-theme-secondary max-h-full w-full text-sm"
262
+ value={data.htmlContent}
263
+ rows={20}
264
+ onChange={(e) =>
265
+ setData((prev: WizardData) => ({
266
+ ...prev,
267
+ htmlContent: e.target.value || "",
268
+ }))
269
+ }
270
+ />
271
+ {step.children && step.children.length > 0 && (
272
+ <div>
273
+ <div className="mt-2 mb-1 text-sm font-semibold">Examples:</div>
274
+ <div className="flex flex-wrap gap-2">
275
+ {step.children.map((child) => (
276
+ <Button
277
+ variant="outline"
278
+ key={child.id}
279
+ onClick={() => {
280
+ setData((prev: WizardData) => ({
281
+ ...prev,
282
+ htmlContent: child.fields.text,
283
+ }));
284
+ }}
285
+ >
286
+ {child.fields.title}
287
+ </Button>
288
+ ))}
289
+ </div>
290
+ </div>
291
+ )}
292
+ </Card>
293
+ )}
294
+ </div>
295
+ );
296
+ }