@alpaca-editor/core 1.0.0

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 (239) hide show
  1. package/.prettierrc +3 -0
  2. package/eslint.config.mjs +4 -0
  3. package/images/bg-shape-black.webp +0 -0
  4. package/package.json +52 -0
  5. package/src/client-components/api.ts +6 -0
  6. package/src/client-components/index.ts +19 -0
  7. package/src/components/ActionButton.tsx +43 -0
  8. package/src/components/Error.tsx +57 -0
  9. package/src/config/config.tsx +737 -0
  10. package/src/config/types.ts +263 -0
  11. package/src/editor/ComponentInfo.tsx +77 -0
  12. package/src/editor/ConfirmationDialog.tsx +103 -0
  13. package/src/editor/ContentTree.tsx +654 -0
  14. package/src/editor/ContextMenu.tsx +155 -0
  15. package/src/editor/Editor.tsx +91 -0
  16. package/src/editor/EditorWarning.tsx +34 -0
  17. package/src/editor/EditorWarnings.tsx +33 -0
  18. package/src/editor/FieldEditorPopup.tsx +65 -0
  19. package/src/editor/FieldHistory.tsx +74 -0
  20. package/src/editor/FieldList.tsx +190 -0
  21. package/src/editor/FieldListField.tsx +387 -0
  22. package/src/editor/FieldListFieldWithFallbacks.tsx +211 -0
  23. package/src/editor/FloatingToolbar.tsx +163 -0
  24. package/src/editor/ImageEditor.tsx +129 -0
  25. package/src/editor/InsertMenu.tsx +332 -0
  26. package/src/editor/ItemInfo.tsx +90 -0
  27. package/src/editor/LinkEditorDialog.tsx +192 -0
  28. package/src/editor/MainLayout.tsx +94 -0
  29. package/src/editor/NewEditorClient.tsx +11 -0
  30. package/src/editor/PictureCropper.tsx +505 -0
  31. package/src/editor/PictureEditor.tsx +206 -0
  32. package/src/editor/PictureEditorDialog.tsx +381 -0
  33. package/src/editor/PublishDialog.ignore +74 -0
  34. package/src/editor/ScrollingContentTree.tsx +47 -0
  35. package/src/editor/Terminal.tsx +215 -0
  36. package/src/editor/Titlebar.tsx +23 -0
  37. package/src/editor/ai/AiPopup.tsx +59 -0
  38. package/src/editor/ai/AiResponseMessage.tsx +82 -0
  39. package/src/editor/ai/AiTerminal.tsx +450 -0
  40. package/src/editor/ai/AiToolCall.tsx +46 -0
  41. package/src/editor/ai/EditorAiTerminal.tsx +20 -0
  42. package/src/editor/ai/editorAiContext.ts +18 -0
  43. package/src/editor/client/DialogContext.tsx +49 -0
  44. package/src/editor/client/EditorClient.tsx +1831 -0
  45. package/src/editor/client/GenericDialog.tsx +50 -0
  46. package/src/editor/client/editContext.ts +330 -0
  47. package/src/editor/client/helpers.ts +44 -0
  48. package/src/editor/client/itemsRepository.ts +391 -0
  49. package/src/editor/client/operations.ts +610 -0
  50. package/src/editor/client/pageModelBuilder.ts +182 -0
  51. package/src/editor/commands/commands.ts +23 -0
  52. package/src/editor/commands/componentCommands.tsx +408 -0
  53. package/src/editor/commands/createVersionCommand.ts +33 -0
  54. package/src/editor/commands/deleteVersionCommand.ts +71 -0
  55. package/src/editor/commands/itemCommands.tsx +186 -0
  56. package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +201 -0
  57. package/src/editor/commands/undo.ts +39 -0
  58. package/src/editor/component-designer/ComponentDesigner.tsx +70 -0
  59. package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx +11 -0
  60. package/src/editor/component-designer/ComponentDesignerMenu.tsx +91 -0
  61. package/src/editor/component-designer/ComponentEditor.tsx +97 -0
  62. package/src/editor/component-designer/ComponentRenderingCodeEditor.tsx +31 -0
  63. package/src/editor/component-designer/ComponentRenderingEditor.tsx +104 -0
  64. package/src/editor/component-designer/ComponentsDropdown.tsx +39 -0
  65. package/src/editor/component-designer/PlaceholdersEditor.tsx +183 -0
  66. package/src/editor/component-designer/RenderingsDropdown.tsx +36 -0
  67. package/src/editor/component-designer/TemplateEditor.tsx +236 -0
  68. package/src/editor/component-designer/aiContext.ts +23 -0
  69. package/src/editor/componentTreeHelper.tsx +114 -0
  70. package/src/editor/control-center/ControlCenterMenu.tsx +71 -0
  71. package/src/editor/control-center/IndexOverview.tsx +50 -0
  72. package/src/editor/control-center/IndexSettings.tsx +266 -0
  73. package/src/editor/control-center/Status.tsx +7 -0
  74. package/src/editor/editor-warnings/ItemLocked.tsx +63 -0
  75. package/src/editor/editor-warnings/NoLanguageWriteAccess.tsx +22 -0
  76. package/src/editor/editor-warnings/NoWorkflowWriteAccess.tsx +23 -0
  77. package/src/editor/editor-warnings/NoWriteAccess.tsx +15 -0
  78. package/src/editor/editor-warnings/ValidationErrors.tsx +54 -0
  79. package/src/editor/field-types/AttachmentEditor.tsx +9 -0
  80. package/src/editor/field-types/CheckboxEditor.tsx +47 -0
  81. package/src/editor/field-types/DropLinkEditor.tsx +75 -0
  82. package/src/editor/field-types/DropListEditor.tsx +84 -0
  83. package/src/editor/field-types/ImageFieldEditor.tsx +65 -0
  84. package/src/editor/field-types/InternalLinkFieldEditor.tsx +112 -0
  85. package/src/editor/field-types/LinkFieldEditor.tsx +85 -0
  86. package/src/editor/field-types/MultiLineText.tsx +63 -0
  87. package/src/editor/field-types/PictureFieldEditor.tsx +121 -0
  88. package/src/editor/field-types/RawEditor.tsx +53 -0
  89. package/src/editor/field-types/ReactQuill.tsx +580 -0
  90. package/src/editor/field-types/RichTextEditor.tsx +22 -0
  91. package/src/editor/field-types/RichTextEditorComponent.tsx +108 -0
  92. package/src/editor/field-types/SingleLineText.tsx +150 -0
  93. package/src/editor/field-types/TreeListEditor.tsx +261 -0
  94. package/src/editor/fieldTypes.ts +140 -0
  95. package/src/editor/media-selector/AiImageSearch.tsx +186 -0
  96. package/src/editor/media-selector/AiImageSearchPrompt.tsx +95 -0
  97. package/src/editor/media-selector/MediaSelector.tsx +42 -0
  98. package/src/editor/media-selector/Preview.tsx +14 -0
  99. package/src/editor/media-selector/Thumbnails.tsx +48 -0
  100. package/src/editor/media-selector/TreeSelector.tsx +292 -0
  101. package/src/editor/media-selector/UploadZone.tsx +137 -0
  102. package/src/editor/menubar/ActionsMenu.tsx +47 -0
  103. package/src/editor/menubar/ActiveUsers.tsx +17 -0
  104. package/src/editor/menubar/ApproveAndPublish.tsx +18 -0
  105. package/src/editor/menubar/BrowseHistory.tsx +37 -0
  106. package/src/editor/menubar/ItemLanguageVersion.tsx +52 -0
  107. package/src/editor/menubar/LanguageSelector.tsx +152 -0
  108. package/src/editor/menubar/Menu.tsx +83 -0
  109. package/src/editor/menubar/NavButtons.tsx +74 -0
  110. package/src/editor/menubar/PageSelector.tsx +139 -0
  111. package/src/editor/menubar/PageViewerControls.tsx +99 -0
  112. package/src/editor/menubar/Separator.tsx +12 -0
  113. package/src/editor/menubar/SiteInfo.tsx +53 -0
  114. package/src/editor/menubar/User.tsx +27 -0
  115. package/src/editor/menubar/VersionSelector.tsx +143 -0
  116. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +287 -0
  117. package/src/editor/page-editor-chrome/CommentHighlightings.tsx +35 -0
  118. package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +44 -0
  119. package/src/editor/page-editor-chrome/FieldActionIndicators.tsx +23 -0
  120. package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +64 -0
  121. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +35 -0
  122. package/src/editor/page-editor-chrome/FrameMenu.tsx +263 -0
  123. package/src/editor/page-editor-chrome/FrameMenus.tsx +48 -0
  124. package/src/editor/page-editor-chrome/InlineEditor.tsx +147 -0
  125. package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +61 -0
  126. package/src/editor/page-editor-chrome/NoLayout.tsx +36 -0
  127. package/src/editor/page-editor-chrome/PageEditorChrome.tsx +119 -0
  128. package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +154 -0
  129. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +171 -0
  130. package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +233 -0
  131. package/src/editor/page-viewer/DeviceToolbar.tsx +70 -0
  132. package/src/editor/page-viewer/EditorForm.tsx +247 -0
  133. package/src/editor/page-viewer/MiniMap.tsx +351 -0
  134. package/src/editor/page-viewer/PageViewer.tsx +127 -0
  135. package/src/editor/page-viewer/PageViewerFrame.tsx +1030 -0
  136. package/src/editor/page-viewer/pageViewContext.ts +186 -0
  137. package/src/editor/pageModel.ts +191 -0
  138. package/src/editor/picture-shared.tsx +53 -0
  139. package/src/editor/reviews/Comment.tsx +265 -0
  140. package/src/editor/reviews/Comments.tsx +50 -0
  141. package/src/editor/reviews/PreviewInfo.tsx +35 -0
  142. package/src/editor/reviews/Reviews.tsx +280 -0
  143. package/src/editor/reviews/reviewCommands.tsx +47 -0
  144. package/src/editor/reviews/useReviews.tsx +70 -0
  145. package/src/editor/services/aiService.ts +155 -0
  146. package/src/editor/services/componentDesignerService.ts +151 -0
  147. package/src/editor/services/contentService.ts +159 -0
  148. package/src/editor/services/editService.ts +462 -0
  149. package/src/editor/services/indexService.ts +24 -0
  150. package/src/editor/services/reviewsService.ts +45 -0
  151. package/src/editor/services/serviceHelper.ts +95 -0
  152. package/src/editor/services/systemService.ts +5 -0
  153. package/src/editor/services/translationService.ts +21 -0
  154. package/src/editor/services-server/api.ts +150 -0
  155. package/src/editor/services-server/graphQL.ts +106 -0
  156. package/src/editor/sidebar/ComponentPalette.tsx +146 -0
  157. package/src/editor/sidebar/ComponentTree.tsx +512 -0
  158. package/src/editor/sidebar/ComponentTree2.tsxx +490 -0
  159. package/src/editor/sidebar/Debug.tsx +105 -0
  160. package/src/editor/sidebar/DictionaryEditor.tsx +261 -0
  161. package/src/editor/sidebar/EditHistory.tsx +134 -0
  162. package/src/editor/sidebar/GraphQL.tsx +164 -0
  163. package/src/editor/sidebar/Insert.tsx +35 -0
  164. package/src/editor/sidebar/MainContentTree.tsx +95 -0
  165. package/src/editor/sidebar/Performance.tsx +53 -0
  166. package/src/editor/sidebar/Sessions.tsx +35 -0
  167. package/src/editor/sidebar/Sidebar.tsx +20 -0
  168. package/src/editor/sidebar/SidebarView.tsx +150 -0
  169. package/src/editor/sidebar/Translations.tsx +276 -0
  170. package/src/editor/sidebar/Validation.tsx +102 -0
  171. package/src/editor/sidebar/ViewSelector.tsx +49 -0
  172. package/src/editor/sidebar/Workbox.tsx +209 -0
  173. package/src/editor/ui/CenteredMessage.tsx +7 -0
  174. package/src/editor/ui/CopyToClipboardButton.tsx +23 -0
  175. package/src/editor/ui/DialogButtons.tsx +11 -0
  176. package/src/editor/ui/Icons.tsx +585 -0
  177. package/src/editor/ui/ItemNameDialog.tsx +94 -0
  178. package/src/editor/ui/ItemNameDialogNew.tsx +118 -0
  179. package/src/editor/ui/ItemSearch.tsx +173 -0
  180. package/src/editor/ui/PerfectTree.tsx +550 -0
  181. package/src/editor/ui/Section.tsx +35 -0
  182. package/src/editor/ui/SimpleIconButton.tsx +43 -0
  183. package/src/editor/ui/SimpleMenu.tsx +48 -0
  184. package/src/editor/ui/SimpleTable.tsx +63 -0
  185. package/src/editor/ui/SimpleTabs.tsx +55 -0
  186. package/src/editor/ui/SimpleToolbar.tsx +7 -0
  187. package/src/editor/ui/Spinner.tsx +7 -0
  188. package/src/editor/ui/Splitter.tsx +247 -0
  189. package/src/editor/ui/StackedPanels.tsx +134 -0
  190. package/src/editor/ui/Toolbar.tsx +7 -0
  191. package/src/editor/utils/id-helper.ts +3 -0
  192. package/src/editor/utils/insertOptions.ts +69 -0
  193. package/src/editor/utils/itemutils.ts +29 -0
  194. package/src/editor/utils/useMemoDebug.ts +28 -0
  195. package/src/editor/utils.ts +435 -0
  196. package/src/editor/views/CompareView.tsx +256 -0
  197. package/src/editor/views/EditView.tsx +27 -0
  198. package/src/editor/views/ItemEditor.tsx +58 -0
  199. package/src/editor/views/SingleEditView.tsx +44 -0
  200. package/src/fonts/Geist-Black.woff2 +0 -0
  201. package/src/fonts/Geist-Bold.woff2 +0 -0
  202. package/src/fonts/Geist-ExtraBold.woff2 +0 -0
  203. package/src/fonts/Geist-ExtraLight.woff2 +0 -0
  204. package/src/fonts/Geist-Light.woff2 +0 -0
  205. package/src/fonts/Geist-Medium.woff2 +0 -0
  206. package/src/fonts/Geist-Regular.woff2 +0 -0
  207. package/src/fonts/Geist-SemiBold.woff2 +0 -0
  208. package/src/fonts/Geist-Thin.woff2 +0 -0
  209. package/src/fonts/Geist[wght].woff2 +0 -0
  210. package/src/index.ts +7 -0
  211. package/src/page-wizard/PageWizard.tsx +163 -0
  212. package/src/page-wizard/SelectWizard.tsx +109 -0
  213. package/src/page-wizard/WizardSteps.tsx +207 -0
  214. package/src/page-wizard/service.ts +35 -0
  215. package/src/page-wizard/startPageWizardCommand.ts +27 -0
  216. package/src/page-wizard/steps/BuildPageStep.tsx +266 -0
  217. package/src/page-wizard/steps/CollectStep.tsx +233 -0
  218. package/src/page-wizard/steps/ComponentTypesSelector.tsx +443 -0
  219. package/src/page-wizard/steps/Components.tsx +193 -0
  220. package/src/page-wizard/steps/CreatePage.tsx +285 -0
  221. package/src/page-wizard/steps/CreatePageAndLayoutStep.tsx +384 -0
  222. package/src/page-wizard/steps/EditButton.tsx +34 -0
  223. package/src/page-wizard/steps/FieldEditor.tsx +102 -0
  224. package/src/page-wizard/steps/Generate.tsx +32 -0
  225. package/src/page-wizard/steps/ImagesStep.tsx +318 -0
  226. package/src/page-wizard/steps/LayoutStep.tsx +228 -0
  227. package/src/page-wizard/steps/SelectStep.tsx +256 -0
  228. package/src/page-wizard/steps/schema.ts +180 -0
  229. package/src/page-wizard/steps/usePageCreator.ts +279 -0
  230. package/src/splash-screen/NewPage.tsx +232 -0
  231. package/src/splash-screen/SectionHeadline.tsx +21 -0
  232. package/src/splash-screen/SplashScreen.tsx +156 -0
  233. package/src/tour/Tour.tsx +558 -0
  234. package/src/tour/default-tour.tsx +300 -0
  235. package/src/tour/preview-tour.tsx +127 -0
  236. package/src/types.ts +302 -0
  237. package/styles.css +476 -0
  238. package/tsconfig.build.json +21 -0
  239. package/tsconfig.json +11 -0
@@ -0,0 +1,58 @@
1
+ import { Button } from "primereact/button";
2
+ import { EditorWarnings } from "../EditorWarnings";
3
+ import { FieldList } from "../FieldList";
4
+ import { ItemInfo } from "../ItemInfo";
5
+ import { useEditContext } from "../client/editContext";
6
+ import { PageViewContext } from "../page-viewer/pageViewContext";
7
+ import { FullItem } from "../pageModel";
8
+
9
+ export function ItemEditor({
10
+ item,
11
+ compareToItem,
12
+ }: {
13
+ item?: FullItem;
14
+ compareToItem?: FullItem;
15
+ pageViewContext?: PageViewContext;
16
+ }) {
17
+ const editContext = useEditContext();
18
+
19
+ if (!editContext) return;
20
+ if (!item) item = editContext.contentEditorItem;
21
+ if (!item) return;
22
+
23
+ const pageValidators =
24
+ editContext.validationResult?.find(
25
+ (x) =>
26
+ x.item.id === item?.id &&
27
+ x.item.language === item?.language &&
28
+ x.item.version === item?.version
29
+ )?.results || [];
30
+
31
+ return (
32
+ <div className="absolute inset-0 overflow-y-auto">
33
+ <EditorWarnings item={item} />
34
+ <ItemInfo item={item} />
35
+
36
+ {editContext.itemVersions.length > 0 && (
37
+ <FieldList
38
+ fields={[{ fields: Object.values(item.fields) }]}
39
+ validators={pageValidators.filter((x) => x.itemId === item.id)}
40
+ showStandardFieldsEnabled={true}
41
+ compareToFields={compareToItem?.fields}
42
+ />
43
+ )}
44
+
45
+ {editContext.itemVersions.length === 0 && (
46
+ <div className="flex justify-center p-4">
47
+ <Button
48
+ onClick={async () => {
49
+ await editContext.operations.createVersion(item.descriptor);
50
+ }}
51
+ >
52
+ Create First Version ({item.language})
53
+ </Button>
54
+ </div>
55
+ )}
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,44 @@
1
+ import { useState } from "react";
2
+ import { useEffect } from "react";
3
+ import { useEditContext } from "../client/editContext";
4
+ import { PageViewer } from "../page-viewer/PageViewer";
5
+ import { PageViewContext } from "../page-viewer/pageViewContext";
6
+ import { FullItem, ItemDescriptor } from "../pageModel";
7
+ import { ItemEditor } from "./ItemEditor";
8
+
9
+ export function SingleEditView({
10
+ pageViewContext,
11
+ itemDescriptor,
12
+ mode,
13
+ name,
14
+ }: {
15
+ pageViewContext: PageViewContext;
16
+ itemDescriptor?: ItemDescriptor;
17
+ mode: "edit" | "compare" | "view";
18
+ name: string;
19
+ }) {
20
+ const editContext = useEditContext();
21
+ const [item, setItem] = useState<FullItem | undefined>(undefined);
22
+
23
+ useEffect(() => {
24
+ async function loadItem() {
25
+ if (!itemDescriptor) return;
26
+ const item = await editContext?.itemsRepository.getItem(itemDescriptor);
27
+ setItem(item);
28
+ }
29
+ loadItem();
30
+ }, [itemDescriptor]);
31
+
32
+ if ((item && !item.hasLayout) || pageViewContext.device === "")
33
+ return <ItemEditor item={item} />;
34
+
35
+ return (
36
+ <PageViewer
37
+ mode={mode}
38
+ pageViewContext={pageViewContext}
39
+ showFormEditor={true}
40
+ name={name}
41
+ followEditsDefault={true}
42
+ />
43
+ );
44
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./config/types";
2
+
3
+ export { Editor } from "./editor/Editor";
4
+
5
+ export { configureForUser } from "./config/config";
6
+
7
+ export type { DialogProps } from "./editor/client/DialogContext";
@@ -0,0 +1,163 @@
1
+ import { useEditContext } from "../editor/client/editContext";
2
+ import { SelectWizard } from "./SelectWizard";
3
+ import { useState, useEffect } from "react";
4
+ import { WizardSteps } from "./WizardSteps";
5
+ import { ItemDescriptor } from "../editor/pageModel";
6
+ import { Thumbnail } from "../editor/media-selector/Thumbnails";
7
+ import { getWizards } from "./service"; // Adjust import as needed
8
+ import { useSearchParams } from "next/navigation";
9
+
10
+ export type Wizard = {
11
+ id: string;
12
+ name: string;
13
+ description: string;
14
+ steps: WizardStep[];
15
+ icon: string;
16
+ schema: PageSchema;
17
+ templateId: string;
18
+ };
19
+
20
+ export type PageSchema = SchemaPlaceholder[];
21
+
22
+ export type SchemaComponent = {
23
+ type: string;
24
+ typeId: string;
25
+ fields: SchemaField[];
26
+ placeholders: SchemaPlaceholder[];
27
+ };
28
+
29
+ export type SchemaPlaceholder = {
30
+ name: string;
31
+ components: SchemaComponent[];
32
+ };
33
+
34
+ export type SchemaField = {
35
+ id: string;
36
+ name: string;
37
+ type: string;
38
+ };
39
+
40
+ export type WizardSchemaComponent = {
41
+ type: string;
42
+ fields: WizardSchemaField[];
43
+ availableParentPlaceholders?: string[];
44
+ allowedChildrenComponentTypes: string[];
45
+ allowedOnRoot: boolean;
46
+ };
47
+
48
+ export type WizardSchemaField = {
49
+ name: string;
50
+ type: string;
51
+ };
52
+
53
+ export type WizardStep = {
54
+ id: string;
55
+ name: string;
56
+ description: string;
57
+ [property: string]: string;
58
+ };
59
+
60
+ export type WizardData = {
61
+ [key: string]: any;
62
+ };
63
+
64
+ export type WizardPageModel = {
65
+ components: WizardPageComponent[];
66
+ name: string;
67
+ metaDescription: string;
68
+ metaKeywords: string;
69
+ message?: string;
70
+ images?: Thumbnail[];
71
+ };
72
+
73
+ export type WizardPageComponent = {
74
+ id: string;
75
+ name: string;
76
+ type: string;
77
+ fields: WizardField[];
78
+ children?: WizardPageComponent[];
79
+ description: string;
80
+ placeholder?: string;
81
+ };
82
+
83
+ export type WizardField = {
84
+ name: string;
85
+ value: string;
86
+ type: string;
87
+ writtenValue?: string;
88
+ };
89
+
90
+ export function PageWizard() {
91
+ const editContext = useEditContext();
92
+ const [wizard, setWizard] = useState<Wizard | null>(null);
93
+ const [parentItem, setParentItem] = useState<ItemDescriptor>();
94
+ const searchParams = useSearchParams();
95
+
96
+ // Load all wizards and check URL param on mount
97
+ useEffect(() => {
98
+ if (!editContext?.currentItemDescriptor) return;
99
+ getWizards(editContext?.currentItemDescriptor).then((availableWizards) => {
100
+ // Check for wizard ID in URL
101
+ const params = new URLSearchParams(window.location.search);
102
+ const wizardName = params.get("wizard");
103
+
104
+ if (wizardName) {
105
+ // Find wizard by ID from the loaded wizards
106
+ const foundWizard = availableWizards.find((w) => w.name === wizardName);
107
+ if (foundWizard) {
108
+ setWizard(foundWizard);
109
+ setParentItem(editContext.currentItemDescriptor);
110
+ }
111
+ }
112
+ });
113
+ }, [searchParams]);
114
+
115
+ const handleSelect = (selectedWizard: Wizard) => {
116
+ setWizard(selectedWizard);
117
+ // Update URL with wizard ID
118
+ const url = new URL(window.location.href);
119
+ url.searchParams.set("wizard", selectedWizard.name);
120
+ window.history.pushState({}, "", url);
121
+ };
122
+
123
+ const handleCancel = () => {
124
+ setWizard(null);
125
+ // Remove wizard from URL
126
+ const url = new URL(window.location.href);
127
+ url.searchParams.delete("wizard");
128
+ window.history.pushState({}, "", url);
129
+ editContext?.switchView("content-editor");
130
+ };
131
+
132
+ if (!editContext?.currentItemDescriptor) return null;
133
+
134
+ if (!wizard) {
135
+ return (
136
+ <div className="fixed inset-0 z-1000 bg-white">
137
+ <SelectWizard
138
+ parentItem={parentItem}
139
+ setParentItem={setParentItem}
140
+ onSelect={handleSelect}
141
+ />
142
+ </div>
143
+ );
144
+ }
145
+
146
+ if (!parentItem) return null;
147
+
148
+ return (
149
+ <div className="fixed inset-0 z-1000 bg-white text-gray-950">
150
+ <WizardSteps
151
+ wizard={wizard}
152
+ onCancel={handleCancel}
153
+ parentItem={parentItem}
154
+ onLanguageSelected={(language) => {
155
+ setParentItem({
156
+ ...parentItem,
157
+ language: language.languageCode,
158
+ });
159
+ }}
160
+ />
161
+ </div>
162
+ );
163
+ }
@@ -0,0 +1,109 @@
1
+ import { useEffect, useState } from "react";
2
+ import { getWizards } from "./service";
3
+ import { ItemDescriptor } from "../editor/pageModel";
4
+ import { Wizard } from "./PageWizard";
5
+ import { ScrollingContentTree } from "../editor/ScrollingContentTree";
6
+ import { ItemTreeNodeData } from "../editor/services/contentService";
7
+ import { Splitter, SplitterPanel } from "../editor/ui/Splitter";
8
+ import { PageWizardLogo } from "../editor/ui/Icons";
9
+ import { SimpleIconButton } from "../editor/ui/SimpleIconButton";
10
+ import { useEditContext } from "../editor/client/editContext";
11
+ export function SelectWizard({
12
+ parentItem,
13
+ setParentItem,
14
+ onSelect,
15
+ }: {
16
+ parentItem?: ItemDescriptor;
17
+ setParentItem: (item: ItemDescriptor) => void;
18
+ onSelect: (wizard: Wizard) => void;
19
+ }) {
20
+ const editContext = useEditContext();
21
+ const [wizards, setWizards] = useState<Wizard[]>([]);
22
+
23
+ useEffect(() => {
24
+ const fetchWizards = async () => {
25
+ if (!parentItem) return;
26
+ const wizards = await getWizards(parentItem);
27
+ setWizards(wizards);
28
+ };
29
+ fetchWizards();
30
+ }, [parentItem]);
31
+
32
+ const treePanel: SplitterPanel = {
33
+ name: "tree",
34
+ defaultSize: 450,
35
+ content: (
36
+ <div className="flex h-full gap-2">
37
+ <ScrollingContentTree
38
+ selectedItemId={parentItem?.id}
39
+ onSelectionChange={(x: ItemTreeNodeData | ItemTreeNodeData[]) => {
40
+ setParentItem({
41
+ ...parentItem,
42
+ id: (x as ItemTreeNodeData).id,
43
+ language: (x as ItemTreeNodeData).language,
44
+ version: (x as ItemTreeNodeData).version,
45
+ });
46
+ }}
47
+ />
48
+ </div>
49
+ ),
50
+ };
51
+
52
+ const wizardPanel: SplitterPanel = {
53
+ name: "wizard",
54
+ defaultSize: "auto",
55
+ collapsible: false,
56
+ content: (
57
+ <div className="absolute inset-2 overflow-auto">
58
+ {!parentItem && (
59
+ <div className="flex h-full items-center justify-center text-center text-gray-500">
60
+ Select a parent item on the left!
61
+ </div>
62
+ )}
63
+ {parentItem && wizards?.length === 0 && (
64
+ <div className="text-center text-gray-500">No wizards found</div>
65
+ )}
66
+ {parentItem && wizards.length > 0 && (
67
+ <div className="mx-2 flex flex-wrap gap-3">
68
+ {wizards.map((wizard) => (
69
+ <div
70
+ key={wizard.id}
71
+ className="flex cursor-pointer items-center gap-2 rounded-md border border-gray-300 p-4 hover:bg-gray-100"
72
+ onClick={() => onSelect(wizard)}
73
+ >
74
+ <img
75
+ src={wizard.icon}
76
+ alt={wizard.name}
77
+ className="h-10 w-10"
78
+ />
79
+ <div>
80
+ <h2 className="text-lg">{wizard.name}</h2>
81
+ <p className="text-sm">{wizard.description}</p>
82
+ </div>
83
+ </div>
84
+ ))}
85
+ </div>
86
+ )}
87
+ </div>
88
+ ),
89
+ };
90
+
91
+ return (
92
+ <div className="flex h-full flex-col">
93
+ <h1 className="bg-shape-black flex w-full items-center gap-2 p-4 text-xl font-bold text-white">
94
+ <PageWizardLogo />
95
+ Select Wizard
96
+ <SimpleIconButton
97
+ icon="pi pi-times"
98
+ className="ml-auto"
99
+ onClick={() => editContext?.switchView("editor")}
100
+ label="Cancel"
101
+ />
102
+ </h1>
103
+ <div className="flex gap-2 bg-gray-100 px-4 py-3 text-sm text-gray-600">
104
+ First select a parent item, then select a wizard to create a new page.
105
+ </div>
106
+ <Splitter panels={[treePanel, wizardPanel]} />
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,207 @@
1
+ import { useState } from "react";
2
+ import { Wizard, WizardData, WizardPageModel } from "./PageWizard";
3
+ import { Button } from "primereact/button";
4
+ import { ItemDescriptor, Language } from "../editor/pageModel";
5
+ import { PageWizardLogo } from "../editor/ui/Icons";
6
+ import { SimpleIconButton } from "../editor/ui/SimpleIconButton";
7
+
8
+ import { flushSync } from "react-dom";
9
+ import { useEditContext } from "../editor/client/editContext";
10
+ import { classNames } from "primereact/utils";
11
+ // import { LanguageSelector } from "../editor/menubar/LanguageSelector";
12
+
13
+ export function WizardSteps({
14
+ wizard,
15
+ onCancel,
16
+ parentItem,
17
+ }: // onLanguageSelected,
18
+ {
19
+ wizard: Wizard;
20
+ onCancel: () => void;
21
+ parentItem: ItemDescriptor;
22
+ onLanguageSelected: (language: Language) => void;
23
+ }) {
24
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
25
+ const [data, setData] = useState<WizardData>({});
26
+ const [pageModel, setPageModel] = useState<WizardPageModel>({
27
+ components: [],
28
+ name: "",
29
+ metaDescription: "",
30
+ metaKeywords: "",
31
+ });
32
+
33
+ const editContext = useEditContext();
34
+ const [internalState, setInternalState] = useState<any>({});
35
+ const [stepCompleted, setStepCompleted] = useState(-1);
36
+
37
+ const getCurrentStep = () => {
38
+ const step = wizard.steps[currentStepIndex];
39
+ if (!step) return null;
40
+ const type = step.type;
41
+ const stepId = step.id;
42
+
43
+ if (!type) throw new Error("Step type is required");
44
+
45
+ // Extract component name by removing "Page Wizard" prefix and spaces
46
+ const componentName = type.replace("Page Wizard ", "").replace(/\s+/g, "");
47
+
48
+ const config = editContext?.configuration;
49
+ if (!config) return null;
50
+
51
+ // Get the appropriate component based on the extracted name
52
+ const StepComponent = config.pageWizard.stepComponents[componentName];
53
+
54
+ if (StepComponent) {
55
+ return (
56
+ <StepComponent
57
+ step={wizard.steps[currentStepIndex]}
58
+ data={data}
59
+ setData={setData}
60
+ setPageModel={setPageModel}
61
+ pageModel={pageModel}
62
+ key={stepId}
63
+ wizard={wizard}
64
+ setStepCompleted={(completed) => {
65
+ if (completed) {
66
+ setStepCompleted((prev) => Math.max(prev, currentStepIndex));
67
+ } else {
68
+ setStepCompleted(currentStepIndex - 1);
69
+ }
70
+ }}
71
+ internalState={internalState}
72
+ setInternalState={setInternalState}
73
+ parentItem={parentItem}
74
+ />
75
+ );
76
+ }
77
+
78
+ return null;
79
+ };
80
+
81
+ const switchStep = (index: number) => {
82
+ document.startViewTransition(() => {
83
+ flushSync(() => {
84
+ setCurrentStepIndex(index);
85
+ });
86
+ });
87
+ };
88
+
89
+ const currentStep = wizard.steps[currentStepIndex];
90
+
91
+ if (!currentStep) return null;
92
+
93
+ return (
94
+ <div className="flex flex-col flex-1 h-full justify-stretch ">
95
+ <div className="flex flex-col border-b border-gray-200 bg-gray-100 flex-1">
96
+ <div className="flex gap-2 items-center bg-shape-black text-white p-4 py-4">
97
+ <PageWizardLogo />{" "}
98
+ <h1 className="text-xl font-bold text-white">{wizard.name}</h1>
99
+ {wizard.description && (
100
+ <div className="text-gray-200">- {wizard.description}</div>
101
+ )}
102
+ <SimpleIconButton
103
+ icon="pi pi-times"
104
+ className="ml-auto"
105
+ onClick={onCancel}
106
+ label="Cancel"
107
+ />
108
+ </div>
109
+ <div className="flex gap-2 flex-1">
110
+ <div className="flex flex-col animate-fadeLeft transition-all duration-900 bg-white">
111
+ {/* <div className="">
112
+ <LanguageSelector
113
+ darkMode={true}
114
+ showAllLanguagesSwitch={true}
115
+ selectedLanguage={parentItem.language}
116
+ onLanguageSelected={onLanguageSelected}
117
+ />
118
+ </div> */}
119
+ <div className="flex flex-col text-sm text-gray-500 px-5 py-4">
120
+ {wizard.steps.map((step, index) => (
121
+ <div
122
+ key={step.id}
123
+ className={classNames(
124
+ "flex gap-4 items-center mb-5",
125
+ currentStepIndex > index && "cursor-pointer"
126
+ )}
127
+ onClick={() =>
128
+ index < currentStepIndex ? switchStep(index) : undefined
129
+ }
130
+ >
131
+ <span
132
+ className={classNames(
133
+ "border-2 rounded-full w-7 h-7 p-2 flex items-center justify-center text-xs font-medium text-activeStepColor",
134
+ currentStepIndex >= index
135
+ ? "border-canvas-red text-canvas-red"
136
+ : "border-gray-400"
137
+ )}
138
+ >
139
+ {index + 1}
140
+ </span>
141
+ <div
142
+ className={classNames(
143
+ "w-auto opacity-100",
144
+ currentStepIndex === index
145
+ ? "text-canvas-red font-semibold text-base"
146
+ : currentStepIndex >= index
147
+ ? "text-gray-600 text-sm"
148
+ : "text-gray-400 text-sm"
149
+ )}
150
+ >
151
+ {step.name}
152
+ </div>
153
+ </div>
154
+ ))}
155
+ </div>
156
+ </div>
157
+ <div className="flex-1 flex flex-col px-8 pt-9 bg-secondary lg:pr-20 xl:pr-32 container animate-fadeIn transition-all duration-500">
158
+ <div className="text-4xl font-semibold">
159
+ {currentStep?.name}
160
+ </div>
161
+ {currentStep?.description && (
162
+ <div className="text-base mt-2 font-light">
163
+ {currentStep.description}
164
+ </div>
165
+ )}
166
+ <div className="flex-1 pt-8">{getCurrentStep()}</div>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ <div className="flex justify-between border-t border-gray-200 p-3">
171
+ <div>
172
+ {currentStepIndex > 0 && (
173
+ <Button
174
+ icon="pi pi-arrow-left mr-2"
175
+ onClick={() => switchStep(currentStepIndex - 1)}
176
+ disabled={currentStepIndex === 0}
177
+ >
178
+ {wizard.steps[currentStepIndex - 1]?.name}
179
+ </Button>
180
+ )}
181
+ </div>
182
+ <div>
183
+ {currentStepIndex < wizard.steps.length - 1 && (
184
+ <Button
185
+ icon="pi pi-arrow-right mr-2"
186
+ disabled={stepCompleted < currentStepIndex}
187
+ onClick={() => switchStep(currentStepIndex + 1)}
188
+ >
189
+ {wizard.steps[currentStepIndex + 1]?.name}
190
+ </Button>
191
+ )}
192
+ {currentStepIndex === wizard.steps.length - 1 && (
193
+ <Button
194
+ icon="pi pi-check"
195
+ disabled={stepCompleted < currentStepIndex}
196
+ onClick={() => {
197
+ editContext?.switchView("editor");
198
+ }}
199
+ label="Finish"
200
+ className="ml-2"
201
+ />
202
+ )}
203
+ </div>
204
+ </div>
205
+ </div>
206
+ );
207
+ }
@@ -0,0 +1,35 @@
1
+ import { EditContextType } from "../editor/client/editContext";
2
+ import { ItemDescriptor } from "../editor/pageModel";
3
+ import { post } from "../editor/services/serviceHelper";
4
+
5
+ import { Wizard } from "./PageWizard";
6
+
7
+ export async function getWizards(
8
+ parentItem: ItemDescriptor
9
+ ): Promise<Wizard[]> {
10
+ const result = await post("/alpaca/editor/page-wizard/wizards", parentItem);
11
+ return result.data as Wizard[] | [];
12
+ }
13
+
14
+ export function createWizardAiContext({
15
+ editContext,
16
+ }: {
17
+ editContext: EditContextType;
18
+ }) {
19
+ const aiPromptUrl = "/alpaca/editor/page-wizard/prompt";
20
+
21
+ return {
22
+ // configuration.services.editorService.baseUrl +
23
+ endpoint: aiPromptUrl,
24
+ promptData: {
25
+ itemid: editContext.currentItemDescriptor?.id,
26
+ language: editContext.currentItemDescriptor?.language,
27
+ version: editContext.currentItemDescriptor?.version,
28
+ },
29
+ };
30
+ }
31
+
32
+ export async function wipeComponents(pageItem: ItemDescriptor) {
33
+ const result = await post("/alpaca/editor/page-wizard/wipePage", pageItem);
34
+ return result.data;
35
+ }
@@ -0,0 +1,27 @@
1
+ import { CommandContext } from "../editor/commands/commands";
2
+ import { ItemDescriptor } from "../editor/pageModel";
3
+ import { Wizard } from "./PageWizard";
4
+
5
+ export const startPageWizardCommand = {
6
+ id: "startWizard",
7
+ label: "Start Wizard",
8
+ icon: "pi pi-magic",
9
+ execute: async (
10
+ context: CommandContext<{ wizard: Wizard; item: ItemDescriptor }>
11
+ ) => {
12
+ if (!context.data?.item) return;
13
+
14
+ await context.editContext.loadItem(context.data.item);
15
+
16
+ // Add wizard name to URL
17
+ if (context.data?.wizard) {
18
+ await context.editContext.updateUrl({
19
+ wizard: context.data.wizard.name,
20
+ view: "page-wizard",
21
+ });
22
+ }
23
+ },
24
+ disabled: () => {
25
+ return false;
26
+ },
27
+ };