@alpaca-editor/core 1.0.4008 → 1.0.4010

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 (91) hide show
  1. package/dist/components/SimpleLanguageSelector.d.ts +9 -0
  2. package/dist/components/SimpleLanguageSelector.js +50 -0
  3. package/dist/components/SimpleLanguageSelector.js.map +1 -0
  4. package/dist/config/config.js +6 -8
  5. package/dist/config/config.js.map +1 -1
  6. package/dist/editor/ContentTree.d.ts +3 -2
  7. package/dist/editor/ContentTree.js +4 -2
  8. package/dist/editor/ContentTree.js.map +1 -1
  9. package/dist/editor/Editor.d.ts +2 -1
  10. package/dist/editor/Editor.js +2 -2
  11. package/dist/editor/Editor.js.map +1 -1
  12. package/dist/editor/ScrollingContentTree.d.ts +2 -1
  13. package/dist/editor/ScrollingContentTree.js +7 -3
  14. package/dist/editor/ScrollingContentTree.js.map +1 -1
  15. package/dist/editor/client/EditorClient.d.ts +3 -1
  16. package/dist/editor/client/EditorClient.js +151 -64
  17. package/dist/editor/client/EditorClient.js.map +1 -1
  18. package/dist/editor/client/editContext.d.ts +3 -21
  19. package/dist/editor/client/editContext.js.map +1 -1
  20. package/dist/editor/client/operations.js +2 -1
  21. package/dist/editor/client/operations.js.map +1 -1
  22. package/dist/editor/componentTreeHelper.js.map +1 -1
  23. package/dist/editor/field-types/InternalLinkFieldEditor.js +18 -78
  24. package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
  25. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +53 -27
  26. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  27. package/dist/editor/page-viewer/PageViewer.js +1 -1
  28. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  29. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +19 -2
  30. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  31. package/dist/editor/services/contentService.d.ts +2 -1
  32. package/dist/editor/services/contentService.js +6 -1
  33. package/dist/editor/services/contentService.js.map +1 -1
  34. package/dist/editor/sidebar/MainContentTree.js +2 -2
  35. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  36. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +21 -19
  37. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
  38. package/dist/editor/views/SingleEditView.js +1 -1
  39. package/dist/editor/views/SingleEditView.js.map +1 -1
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.js +6 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/page-wizard/PageWizard.js +61 -41
  44. package/dist/page-wizard/PageWizard.js.map +1 -1
  45. package/dist/page-wizard/WizardSteps.js +29 -40
  46. package/dist/page-wizard/WizardSteps.js.map +1 -1
  47. package/dist/page-wizard/startPageWizardCommand.js +2 -3
  48. package/dist/page-wizard/startPageWizardCommand.js.map +1 -1
  49. package/dist/page-wizard/steps/CollectStep.js +12 -18
  50. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  51. package/dist/page-wizard/steps/ContentStep.js +10 -25
  52. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  53. package/dist/page-wizard/steps/Generate.d.ts +2 -1
  54. package/dist/page-wizard/steps/Generate.js +2 -2
  55. package/dist/page-wizard/steps/Generate.js.map +1 -1
  56. package/dist/page-wizard/steps/StructureStep.js +17 -2
  57. package/dist/page-wizard/steps/StructureStep.js.map +1 -1
  58. package/dist/page-wizard/utils/dataAccessor.d.ts +11 -0
  59. package/dist/page-wizard/utils/dataAccessor.js +19 -0
  60. package/dist/page-wizard/utils/dataAccessor.js.map +1 -1
  61. package/dist/revision.d.ts +2 -2
  62. package/dist/revision.js +2 -2
  63. package/dist/styles.css +10 -3
  64. package/package.json +1 -1
  65. package/src/components/SimpleLanguageSelector.tsx +98 -0
  66. package/src/config/config.tsx +8 -20
  67. package/src/editor/ContentTree.tsx +6 -3
  68. package/src/editor/Editor.tsx +5 -1
  69. package/src/editor/ScrollingContentTree.tsx +10 -4
  70. package/src/editor/client/EditorClient.tsx +259 -152
  71. package/src/editor/client/editContext.ts +29 -27
  72. package/src/editor/client/operations.ts +1 -1
  73. package/src/editor/componentTreeHelper.tsx +1 -0
  74. package/src/editor/field-types/InternalLinkFieldEditor.tsx +71 -144
  75. package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +66 -39
  76. package/src/editor/page-viewer/PageViewer.tsx +1 -1
  77. package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +22 -2
  78. package/src/editor/services/contentService.ts +9 -3
  79. package/src/editor/sidebar/MainContentTree.tsx +2 -0
  80. package/src/editor/ui/CopyMoveTargetSelectorDialog.tsx +52 -39
  81. package/src/editor/views/SingleEditView.tsx +1 -1
  82. package/src/index.ts +11 -0
  83. package/src/page-wizard/PageWizard.tsx +79 -47
  84. package/src/page-wizard/WizardSteps.tsx +47 -39
  85. package/src/page-wizard/startPageWizardCommand.ts +2 -3
  86. package/src/page-wizard/steps/CollectStep.tsx +26 -19
  87. package/src/page-wizard/steps/ContentStep.tsx +12 -31
  88. package/src/page-wizard/steps/Generate.tsx +3 -2
  89. package/src/page-wizard/steps/StructureStep.tsx +26 -3
  90. package/src/page-wizard/utils/dataAccessor.ts +26 -0
  91. package/src/revision.ts +2 -2
@@ -182,7 +182,7 @@ export type EditContextType = {
182
182
  mode: MediaSelectorMode;
183
183
  }) => Promise<string | null>;
184
184
 
185
- updateUrl: (params: Record<string, string>) => void;
185
+ updateUrl: (params: Record<string, string | undefined>) => void;
186
186
 
187
187
  selection: string[];
188
188
  select: (ids: string[]) => void;
@@ -337,32 +337,32 @@ export type EditContextType = {
337
337
 
338
338
  openDialog: OpenDialog;
339
339
 
340
- pageWizard: {
341
- wizard: Wizard | undefined;
342
- setWizard: React.Dispatch<React.SetStateAction<Wizard | undefined>>;
343
- parentItem: ItemDescriptor | undefined;
344
- setParentItem: React.Dispatch<
345
- React.SetStateAction<ItemDescriptor | undefined>
346
- >;
347
- // Wizard step state
348
- currentStepIndex: number;
349
- setCurrentStepIndex: React.Dispatch<React.SetStateAction<number>>;
350
- data: WizardData;
351
- setData: React.Dispatch<React.SetStateAction<WizardData>>;
352
- pageModel: WizardPageModel;
353
- setPageModel: React.Dispatch<React.SetStateAction<WizardPageModel>>;
354
- internalState: any;
355
- setInternalState: React.Dispatch<React.SetStateAction<any>>;
356
- stepCompleted: number;
357
- setStepCompleted: React.Dispatch<React.SetStateAction<number>>;
358
- pageItem: ItemDescriptor | undefined;
359
- setPageItem: React.Dispatch<
360
- React.SetStateAction<ItemDescriptor | undefined>
361
- >;
362
- beforeNextCallbackRef: React.MutableRefObject<
363
- (() => Promise<boolean>) | null
364
- >;
365
- };
340
+ // pageWizard: {
341
+ // wizard: Wizard | undefined;
342
+ // setWizard: React.Dispatch<React.SetStateAction<Wizard | undefined>>;
343
+ // parentItem: ItemDescriptor | undefined;
344
+ // setParentItem: React.Dispatch<
345
+ // React.SetStateAction<ItemDescriptor | undefined>
346
+ // >;
347
+ // // Wizard step state
348
+ // currentStepIndex: number;
349
+ // setCurrentStepIndex: React.Dispatch<React.SetStateAction<number>>;
350
+ // data: WizardData;
351
+ // setData: React.Dispatch<React.SetStateAction<WizardData>>;
352
+ // pageModel: WizardPageModel;
353
+ // setPageModel: React.Dispatch<React.SetStateAction<WizardPageModel>>;
354
+ // internalState: any;
355
+ // setInternalState: React.Dispatch<React.SetStateAction<any>>;
356
+ // stepCompleted: number;
357
+ // setStepCompleted: React.Dispatch<React.SetStateAction<number>>;
358
+ // pageItem: ItemDescriptor | undefined;
359
+ // setPageItem: React.Dispatch<
360
+ // React.SetStateAction<ItemDescriptor | undefined>
361
+ // >;
362
+ // beforeNextCallbackRef: React.MutableRefObject<
363
+ // (() => Promise<boolean>) | null
364
+ // >;
365
+ // };
366
366
 
367
367
  webSocketMessages: WebSocketMessage[];
368
368
  clearWebSocketMessages: () => void;
@@ -370,6 +370,8 @@ export type EditContextType = {
370
370
  setUserPreferences: (preferences: Partial<UserPreferences>) => void;
371
371
  favorites: any[];
372
372
  loadFavorites: () => Promise<void>;
373
+ currentWizardId: string | null;
374
+ setCurrentWizardId: (wizardId: string | null) => void;
373
375
  };
374
376
 
375
377
  const EditContext = React.createContext<EditContextType | undefined>(undefined);
@@ -208,7 +208,7 @@ export function getOperationsContext(
208
208
  if (result.type == "error") {
209
209
  console.log("error locking field", result);
210
210
  }
211
- //handleErrorResult(result, ui, state);
211
+ if (handleErrorResult(result, ui, state)) return false;
212
212
  if (result.type == "success" && result.data.success) {
213
213
  state.setLockedField(field);
214
214
  return true;
@@ -93,6 +93,7 @@ export function getAllPlaceholders(page: Page) {
93
93
  });
94
94
  };
95
95
  addPlaceholders(page.rootComponent.placeholders);
96
+
96
97
  return placeholders;
97
98
  }
98
99
 
@@ -1,17 +1,22 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useRef, useState } from "react";
4
- import { ChevronDown } from "lucide-react";
4
+ import { ChevronDown, Trash } from "lucide-react";
5
5
 
6
6
  import { Link } from "../LinkEditorDialog";
7
7
  import { useEditContext } from "../client/editContext";
8
- import ContentTree from "../ContentTree";
8
+ import { ScrollingContentTree } from "../ScrollingContentTree";
9
9
 
10
10
  import { InternalLinkField } from "../fieldTypes";
11
11
  import { getLookupSources } from "../services/editService";
12
12
  import { classNames } from "primereact/utils";
13
13
  import ItemSearch from "../ui/ItemSearch";
14
14
  import { normalizeGuid } from "../utils";
15
+ import {
16
+ Popover,
17
+ PopoverContent,
18
+ PopoverTrigger,
19
+ } from "../../components/ui/popover";
15
20
 
16
21
  export function InternalLinkFieldEditor({
17
22
  field,
@@ -38,10 +43,6 @@ export function InternalLinkFieldEditor({
38
43
  });
39
44
  }, [field]);
40
45
 
41
- const dropdownRef = useRef<HTMLDivElement>(null);
42
- const texboxRef = useRef<HTMLDivElement>(null);
43
- const containerRef = useRef<HTMLDivElement>(null);
44
-
45
46
  if (!editContext) return;
46
47
 
47
48
  // Helper function to normalize GUID formats for comparison
@@ -49,11 +50,6 @@ export function InternalLinkFieldEditor({
49
50
  return key.toLowerCase().replace(/[{}]/g, "");
50
51
  };
51
52
 
52
- const selection = [];
53
- if (link?.itemId) {
54
- selection.push(normalizeKey(link.itemId));
55
- }
56
-
57
53
  useEffect(() => {
58
54
  const loadLookupSources = async () => {
59
55
  const datasources = await getLookupSources(field, editContext.sessionId);
@@ -70,119 +66,50 @@ export function InternalLinkFieldEditor({
70
66
  field.descriptor.item.version,
71
67
  ]);
72
68
 
73
- // Close overlay when clicking outside
74
- useEffect(() => {
75
- if (!showTree) return;
76
-
77
- const handleClickOutside = (event: MouseEvent) => {
78
- if (
79
- dropdownRef.current &&
80
- !dropdownRef.current.contains(event.target as Node) &&
81
- texboxRef.current &&
82
- !texboxRef.current.contains(event.target as Node)
83
- ) {
84
- setShowTree(false);
85
- }
86
- };
87
-
88
- document.addEventListener("mousedown", handleClickOutside);
89
- return () => document.removeEventListener("mousedown", handleClickOutside);
90
- }, [showTree]);
91
-
92
- // Close overlay when parent containers scroll to prevent misalignment
93
- useEffect(() => {
94
- if (!showTree) return;
95
-
96
- const handleScroll = () => {
97
- setShowTree(false);
98
- };
99
-
100
- // Find scrollable parent elements
101
- const scrollableParents: Element[] = [];
102
- let element = texboxRef.current?.parentElement;
103
-
104
- while (element) {
105
- const computedStyle = window.getComputedStyle(element);
106
- const overflowY = computedStyle.overflowY;
107
- const overflowX = computedStyle.overflowX;
108
-
109
- if (
110
- overflowY === "auto" ||
111
- overflowY === "scroll" ||
112
- overflowX === "auto" ||
113
- overflowX === "scroll"
114
- ) {
115
- scrollableParents.push(element);
116
- }
117
- element = element.parentElement;
118
- }
119
-
120
- // Also listen to window scroll
121
- scrollableParents.push(window as any);
122
-
123
- // Add scroll listeners
124
- scrollableParents.forEach((parent) => {
125
- parent.addEventListener("scroll", handleScroll, { passive: true });
126
- });
127
-
128
- return () => {
129
- scrollableParents.forEach((parent) => {
130
- parent.removeEventListener("scroll", handleScroll);
131
- });
132
- };
133
- }, [showTree]);
134
-
135
69
  return (
136
70
  <div className="relative">
137
- <div
138
- ref={texboxRef}
139
- className={classNames(
140
- "justiy-between focus-shadow bg-gray-5 flex cursor-pointer justify-between border p-1.5 text-xs",
141
- readOnly ? "bg-gray-100" : "",
142
- )}
143
- onClick={(e) => {
144
- if (readOnly) return;
145
- setShowTree((show) => !show);
146
- e.preventDefault();
147
- }}
148
- >
149
- {link?.targetItemName ? (
150
- <span>{link.targetItemName}</span>
151
- ) : (
152
- <span>&nbsp;</span>
153
- )}
71
+ <Popover open={showTree} onOpenChange={setShowTree}>
72
+ <PopoverTrigger asChild disabled={readOnly}>
73
+ <div
74
+ className={classNames(
75
+ "justiy-between focus-shadow bg-gray-5 flex cursor-pointer justify-between border p-1.5 text-xs",
76
+ readOnly ? "cursor-default bg-gray-100" : "",
77
+ )}
78
+ >
79
+ {link?.targetItemName ? (
80
+ <span>{link.targetItemName}</span>
81
+ ) : (
82
+ <span>&nbsp;</span>
83
+ )}
84
+ {!readOnly && (
85
+ <ChevronDown
86
+ strokeWidth={1}
87
+ className={`h-4 w-4 transition-transform duration-200 ${
88
+ showTree ? "rotate-180" : "rotate-0"
89
+ }`}
90
+ />
91
+ )}
92
+ </div>
93
+ </PopoverTrigger>
94
+
154
95
  {!readOnly && (
155
- <ChevronDown
156
- strokeWidth={1}
157
- className={`h-4 w-4 transition-transform duration-200 ${
158
- showTree ? "rotate-180" : "rotate-0"
159
- }`}
160
- />
161
- )}
162
- </div>
96
+ <PopoverContent className="w-96 p-0" align="start" side="bottom">
97
+ <div className="border-b border-gray-200 bg-white p-2">
98
+ <ItemSearch
99
+ rootItemIds={rootItemIds.map((x) => normalizeGuid(x))}
100
+ autoFocus={true}
101
+ itemSelected={(item) => {
102
+ setShowTree(false);
103
+ editContext?.operations.editField({
104
+ field: field.descriptor,
105
+ rawValue: item.id,
106
+ });
107
+ }}
108
+ />
109
+ </div>
163
110
 
164
- {showTree && (
165
- <div
166
- ref={dropdownRef}
167
- className="absolute top-full left-0 z-50 mt-1 border border-gray-200 bg-white shadow-lg"
168
- style={{ width: `${texboxRef.current?.offsetWidth || 100}px` }}
169
- >
170
- <div className="border-b border-gray-200 bg-white p-2">
171
- <ItemSearch
172
- rootItemIds={rootItemIds.map((x) => normalizeGuid(x))}
173
- autoFocus={true}
174
- itemSelected={(item) => {
175
- setShowTree(false);
176
- editContext?.operations.editField({
177
- field: field.descriptor,
178
- rawValue: item.id,
179
- });
180
- }}
181
- />
182
- </div>
183
- <div className="border-b border-gray-200 bg-gray-50 p-2">
184
111
  <button
185
- className="w-full rounded px-2 py-1 text-left text-sm text-gray-600 hover:bg-gray-100"
112
+ className="w-full cursor-pointer rounded border-b bg-gray-50 p-1 px-4 text-left text-xs hover:bg-gray-100"
186
113
  onClick={() => {
187
114
  setShowTree(false);
188
115
  editContext?.operations.editField({
@@ -191,32 +118,32 @@ export function InternalLinkFieldEditor({
191
118
  });
192
119
  }}
193
120
  >
194
- No selection
121
+ <div className="flex items-center gap-2">
122
+ <Trash className="h-4 w-4" strokeWidth={1} /> Clear
123
+ </div>
195
124
  </button>
196
- </div>
197
- <div
198
- ref={containerRef}
199
- className="h-64 overflow-auto border-x border-b p-2"
200
- >
201
- <ContentTree
202
- language={editContext.contentEditorItem!.language}
203
- expandIdPath={link?.targetItemLongId}
204
- rootItemIds={rootItemIds}
205
- selectionMode="single"
206
- selectedItemIds={selection}
207
- scrollToSelected={showTree}
208
- disableAutoSelectOnExpand={true}
209
- onSelectionChange={(nodes) => {
210
- setShowTree(false);
211
- editContext?.operations.editField({
212
- field: field.descriptor,
213
- rawValue: nodes[0]?.id,
214
- });
215
- }}
216
- />
217
- </div>
218
- </div>
219
- )}
125
+
126
+ <div className="relative h-64 border-x border-b">
127
+ <ScrollingContentTree
128
+ selectedItemId={
129
+ link?.itemId ? normalizeKey(link.itemId) : undefined
130
+ }
131
+ rootItemId={rootItemIds[0]}
132
+ expandedItemId={
133
+ link?.itemId ? normalizeKey(link.itemId) : undefined
134
+ }
135
+ onSelectionChange={(nodes) => {
136
+ setShowTree(false);
137
+ editContext?.operations.editField({
138
+ field: field.descriptor,
139
+ rawValue: nodes[0]?.id,
140
+ });
141
+ }}
142
+ />
143
+ </div>
144
+ </PopoverContent>
145
+ )}
146
+ </Popover>
220
147
  </div>
221
148
  );
222
149
  }
@@ -71,63 +71,90 @@ export function PlaceholderDropZones({
71
71
  "[data-placeholder-start='" + placeholder.key + "']",
72
72
  );
73
73
 
74
+ let isUsingParentBounds = false;
75
+
74
76
  if (!placeholderElement && placeholder.components.length > 0) {
75
77
  placeholderElement = placeholder.components[0]?.firstDOMElement;
76
78
  }
77
79
 
78
- if (!placeholderElement) return;
79
-
80
- const prev = getPreviousElement(placeholderElement);
81
-
82
- const nextOrParent =
83
- getNextElement(placeholderElement) || placeholderElement.parentElement;
84
-
85
- let orientation = placeholderElement?.getAttribute("data-orientation");
80
+ // For empty placeholders, use parent component bounds as last resort
81
+ if (!placeholderElement && placeholder.parentComponent) {
82
+ placeholderElement = placeholder.parentComponent.firstDOMElement;
83
+ isUsingParentBounds = true;
84
+ }
86
85
 
87
- if (!orientation)
88
- orientation = detectOrientation(
89
- placeholderElement,
90
- placeholder.components,
91
- );
86
+ if (!placeholderElement) return;
92
87
 
93
- const rect = (prev ?? nextOrParent)!.getBoundingClientRect();
94
88
  const zoom = pageViewContext.zoom;
95
-
96
- const position =
97
- orientation === "horizontal"
98
- ? {
99
- x: (prev ? rect.x + rect.width : rect.x) * zoom,
100
- y: (rect.y + rect.height / 2) * zoom,
101
- }
102
- : {
103
- x: (rect.x + rect.width / 2) * zoom,
104
- y: (prev ? rect.y + rect.height : rect.y) * zoom,
105
- };
106
-
89
+ let position: { x: number; y: number };
90
+ let anchor: "top" | "bottom" | "left" | "right" = "top";
107
91
  let index = 0;
108
92
 
109
93
  const description = placeholderElement.getAttribute("data-description");
110
94
 
111
- let anchor: "top" | "bottom" | "left" | "right" = "top";
95
+ let referenceElement: Element;
96
+ let orientation: string | null = null;
97
+
98
+ // Special handling for empty placeholders using parent bounds
99
+ if (isUsingParentBounds) {
100
+ const rect = placeholderElement.getBoundingClientRect();
101
+ // Center the drop zone in the middle of the parent
102
+ position = {
103
+ x: (rect.x + rect.width / 2) * zoom,
104
+ y: (rect.y + rect.height / 2) * zoom,
105
+ };
106
+ anchor = "top"; // Default anchor for centered position
107
+ referenceElement = placeholderElement;
108
+ orientation = "vertical"; // Default for centered placeholders
109
+ } else {
110
+ // Original positioning logic for normal placeholders
111
+ const prev = getPreviousElement(placeholderElement);
112
+
113
+ const nextOrParent =
114
+ getNextElement(placeholderElement) || placeholderElement.parentElement;
115
+
116
+ orientation = placeholderElement?.getAttribute("data-orientation");
117
+
118
+ if (!orientation)
119
+ orientation = detectOrientation(
120
+ placeholderElement,
121
+ placeholder.components,
122
+ );
123
+
124
+ const rect = (prev ?? nextOrParent)!.getBoundingClientRect();
125
+
126
+ position =
127
+ orientation === "horizontal"
128
+ ? {
129
+ x: (prev ? rect.x + rect.width : rect.x) * zoom,
130
+ y: (rect.y + rect.height / 2) * zoom,
131
+ }
132
+ : {
133
+ x: (rect.x + rect.width / 2) * zoom,
134
+ y: (prev ? rect.y + rect.height : rect.y) * zoom,
135
+ };
136
+
137
+ if (prev && orientation === "horizontal") {
138
+ anchor = "right";
139
+ }
140
+ if (prev && orientation !== "horizontal") {
141
+ anchor = "bottom";
142
+ }
143
+ if (!prev && orientation === "horizontal") {
144
+ anchor = "left";
145
+ }
146
+ if (!prev && orientation !== "horizontal") {
147
+ anchor = "top";
148
+ }
112
149
 
113
- if (prev && orientation === "horizontal") {
114
- anchor = "right";
115
- }
116
- if (prev && orientation !== "horizontal") {
117
- anchor = "bottom";
118
- }
119
- if (!prev && orientation === "horizontal") {
120
- anchor = "left";
121
- }
122
- if (!prev && orientation !== "horizontal") {
123
- anchor = "top";
150
+ referenceElement = prev ?? nextOrParent!;
124
151
  }
125
152
 
126
153
  zones.push({
127
154
  placeholder,
128
155
  position,
129
156
  index,
130
- element: prev ?? nextOrParent!,
157
+ element: referenceElement,
131
158
  anchor,
132
159
  description,
133
160
  });
@@ -47,7 +47,7 @@ export function PageViewer({
47
47
 
48
48
  if (!lastEdit) return;
49
49
 
50
- if (!lastEdit.user.ai && lastEdit.user.name == editContext.user?.name)
50
+ if (!lastEdit.user.ai || lastEdit.user.name !== editContext.user?.name)
51
51
  return;
52
52
 
53
53
  if (editContext.selection.indexOf(lastEdit.item.id) === -1) {
@@ -180,7 +180,27 @@ export function buildPageModelSkeleton(
180
180
  currentPlaceholder.components.push(currentComponent);
181
181
  }
182
182
  }
183
- currentPlaceholder = undefined;
183
+
184
+ // Check if the new component should have an implicit placeholder
185
+ const hasImplicitPlaceholder =
186
+ element.getAttribute("data-has-implicit-placeholder") === "true";
187
+
188
+ if (hasImplicitPlaceholder && currentComponent) {
189
+ // Add an implicit placeholder to the newly created component
190
+ const implicitPlaceholder: PlaceholderSkeleton = {
191
+ key: "implicit_" + currentComponent.datasourceItem?.id,
192
+ name: "Implicit placeholder: " + currentComponent.datasourceItem?.id,
193
+ description: "",
194
+ components: [],
195
+ parentComponent: currentComponent,
196
+ editable: element.getAttribute("data-editable") !== "false",
197
+ };
198
+
199
+ currentComponent.placeholders.push(implicitPlaceholder);
200
+ currentPlaceholder = implicitPlaceholder;
201
+ } else {
202
+ currentPlaceholder = undefined;
203
+ }
184
204
  return currentComponent;
185
205
  }
186
206
  };
@@ -422,6 +442,6 @@ export function buildPageModelSkeleton(
422
442
 
423
443
  const time = performance.now() - start;
424
444
 
425
- console.log("PAGE MODEL SKELETON", page, time);
445
+ console.log("PAGE MODEL SKELETON: ", page, time);
426
446
  pageViewContextRef.current?.setPageSkeleton(page);
427
447
  }
@@ -8,7 +8,7 @@ import {
8
8
  WorkboxItem,
9
9
  ContentEditorWarning,
10
10
  } from "../../types";
11
- import { FullItem, ItemDescriptor, ItemStub } from "../pageModel";
11
+ import { FullItem, ItemDescriptor, ItemStub, Language } from "../pageModel";
12
12
 
13
13
  export type Thumbnail = {
14
14
  id: string;
@@ -236,5 +236,11 @@ export type RichTextProfileResponse = {
236
236
  };
237
237
 
238
238
  export async function getRichTextProfile(itemPath: string) {
239
- return await post<RichTextProfileResponse>(`/alpaca/editor/RichTextProfile`, {profile: itemPath});
240
- }
239
+ return await post<RichTextProfileResponse>(`/alpaca/editor/RichTextProfile`, {
240
+ profile: itemPath,
241
+ });
242
+ }
243
+
244
+ export async function getLanguages() {
245
+ return await get<Language[]>("/alpaca/editor/languages");
246
+ }
@@ -77,6 +77,7 @@ export function MainContentTree({
77
77
  <div className="relative flex-1">
78
78
  <div className="absolute inset-1 overflow-auto">
79
79
  <ContentTree
80
+ enableDragAndDrop={mode == "normal"}
80
81
  language={editContext.currentItemDescriptor?.language ?? "en"}
81
82
  rootItemId={rootItemId ?? "{11111111-1111-1111-1111-111111111111}"}
82
83
  expandIdPath={
@@ -102,6 +103,7 @@ export function MainContentTree({
102
103
  }}
103
104
  showGrabCursorForDraggableNodes={mode === "insert-component"}
104
105
  isDraggable={isDraggable}
106
+ enableContextMenu={mode === "normal"}
105
107
  renderNode={(node, defaultRenderer) => (
106
108
  <div className="group flex w-full gap-4">
107
109
  {defaultRenderer(node)}