@alpaca-editor/core 1.0.3969 → 1.0.3973

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 (54) hide show
  1. package/dist/editor/FieldList.d.ts +3 -0
  2. package/dist/editor/FieldList.js +3 -1
  3. package/dist/editor/FieldList.js.map +1 -1
  4. package/dist/editor/client/EditorClient.js +10 -0
  5. package/dist/editor/client/EditorClient.js.map +1 -1
  6. package/dist/editor/client/editContext.d.ts +2 -0
  7. package/dist/editor/client/editContext.js.map +1 -1
  8. package/dist/editor/client/pageModelBuilder.js +2 -1
  9. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  10. package/dist/editor/page-editor-chrome/CommentHighlighting.js +68 -48
  11. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  12. package/dist/editor/page-viewer/EditorForm.js +77 -12
  13. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  14. package/dist/editor/page-viewer/PageViewerFrame.js +13 -45
  15. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  16. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +3 -0
  17. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  18. package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
  19. package/dist/editor/page-viewer/pageViewContext.js +5 -0
  20. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  21. package/dist/editor/pageModel.d.ts +3 -0
  22. package/dist/editor/services/contentService.d.ts +1 -0
  23. package/dist/editor/services/contentService.js.map +1 -1
  24. package/dist/editor/sidebar/ComponentPalette.js +2 -2
  25. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  26. package/dist/editor/sidebar/ComponentTree.js +96 -44
  27. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  28. package/dist/editor/sidebar/Debug.js +1 -1
  29. package/dist/editor/sidebar/Debug.js.map +1 -1
  30. package/dist/editor/utils.d.ts +1 -2
  31. package/dist/editor/utils.js +37 -26
  32. package/dist/editor/utils.js.map +1 -1
  33. package/dist/revision.d.ts +2 -2
  34. package/dist/revision.js +2 -2
  35. package/dist/styles.css +6 -0
  36. package/dist/types.d.ts +2 -0
  37. package/package.json +1 -1
  38. package/src/editor/FieldList.tsx +35 -2
  39. package/src/editor/client/EditorClient.tsx +16 -0
  40. package/src/editor/client/editContext.ts +3 -0
  41. package/src/editor/client/pageModelBuilder.ts +2 -1
  42. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +81 -54
  43. package/src/editor/page-viewer/EditorForm.tsx +96 -12
  44. package/src/editor/page-viewer/PageViewerFrame.tsx +17 -59
  45. package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +3 -0
  46. package/src/editor/page-viewer/pageViewContext.ts +9 -3
  47. package/src/editor/pageModel.ts +3 -0
  48. package/src/editor/services/contentService.ts +7 -1
  49. package/src/editor/sidebar/ComponentPalette.tsx +4 -4
  50. package/src/editor/sidebar/ComponentTree.tsx +150 -67
  51. package/src/editor/sidebar/Debug.tsx +1 -1
  52. package/src/editor/utils.ts +41 -28
  53. package/src/revision.ts +2 -2
  54. package/src/types.ts +2 -0
@@ -13,7 +13,8 @@ import { PerfectTree, TreeNode } from "../ui/PerfectTree";
13
13
 
14
14
  import { isValidPlaceholder } from "../componentTreeHelper";
15
15
  import { getAbsoluteIconUrl } from "../utils";
16
- import { Plus } from "lucide-react";
16
+ import { Plus, EyeIcon, EyeOff } from "lucide-react";
17
+ import { SimpleIconButton } from "../ui/SimpleIconButton";
17
18
 
18
19
  type CustomTreeNode = TreeNode<Component | Placeholder> & {
19
20
  parent?: CustomTreeNode;
@@ -45,6 +46,25 @@ export function ComponentTree({}) {
45
46
 
46
47
  const treeRef = useRef<HTMLDivElement>(null);
47
48
 
49
+ // Helper function to check if a component has editable descendants
50
+ function hasEditableDescendants(component: Component): boolean {
51
+ if (!component.placeholders) return false;
52
+
53
+ for (const placeholder of component.placeholders) {
54
+ // If placeholder itself is editable, we have editable descendants
55
+ if (placeholder.editable) return true;
56
+
57
+ // Check if any component in this placeholder is editable or has editable descendants
58
+ for (const comp of placeholder.components) {
59
+ if (comp.editable || hasEditableDescendants(comp)) {
60
+ return true;
61
+ }
62
+ }
63
+ }
64
+
65
+ return false;
66
+ }
67
+
48
68
  useEffect(() => {
49
69
  if (treeRef.current) {
50
70
  const selectedNodeKey = Object.keys(selectedKeys || {})[0];
@@ -106,7 +126,18 @@ export function ComponentTree({}) {
106
126
  tags: [],
107
127
  };
108
128
 
109
- node.children = p.components.map((c) => mapComponentNode(c, node));
129
+ // Always map components, but filter based on editability and descendants
130
+ const mappedComponents = p.components.map((c) => mapComponentNode(c, node));
131
+
132
+ if (editContext?.hideNonEditableComponents) {
133
+ // Show components that are either editable or have editable descendants
134
+ node.children = mappedComponents.filter((componentNode) => {
135
+ const component = componentNode.data as Component;
136
+ return component.editable || hasEditableDescendants(component);
137
+ });
138
+ } else {
139
+ node.children = mappedComponents;
140
+ }
110
141
 
111
142
  return node;
112
143
  }
@@ -119,23 +150,42 @@ export function ComponentTree({}) {
119
150
  return [];
120
151
  }
121
152
 
122
- // Show all placeholders (both editable and non-editable)
123
- const allPlaceholders = c.placeholders;
153
+ // Filter placeholders based on hideNonEditableComponents setting and editable descendants
154
+ let placeholdersToShow = c.placeholders;
155
+ if (editContext?.hideNonEditableComponents) {
156
+ placeholdersToShow = c.placeholders.filter((p) => {
157
+ // Show placeholder if it's editable or has editable descendants
158
+ if (p.editable) return true;
159
+ return p.components.some(
160
+ (comp) => comp.editable || hasEditableDescendants(comp),
161
+ );
162
+ });
163
+ }
124
164
 
125
- if (allPlaceholders.length > 1) {
126
- return allPlaceholders.map((x) => mapPlaceholderNode(x, parent));
127
- } else if (allPlaceholders.length > 0) {
128
- const placeholder = allPlaceholders[0]!;
129
- // For single placeholders, always map the components directly (don't show placeholder node)
130
- return placeholder.components.map((c) => {
165
+ if (placeholdersToShow.length > 1) {
166
+ return placeholdersToShow.map((x) => mapPlaceholderNode(x, parent));
167
+ } else if (placeholdersToShow.length > 0) {
168
+ const placeholder = placeholdersToShow[0]!;
169
+ // For single placeholders, map the components directly (don't show placeholder node)
170
+ const mappedComponents = placeholder.components.map((c) => {
131
171
  const componentNode = mapComponentNode(c, parent);
132
- // Apply gray styling to components in non-editable placeholders
133
- if (!placeholder.editable) {
172
+ // Apply gray styling to components in non-editable placeholders (only when showing non-editable components)
173
+ if (!placeholder.editable && !editContext?.hideNonEditableComponents) {
134
174
  componentNode.className =
135
175
  (componentNode.className || "") + " text-gray-400";
136
176
  }
137
177
  return componentNode;
138
178
  });
179
+
180
+ if (editContext?.hideNonEditableComponents) {
181
+ // Filter components to only show editable ones or those with editable descendants
182
+ return mappedComponents.filter((componentNode) => {
183
+ const component = componentNode.data as Component;
184
+ return component.editable || hasEditableDescendants(component);
185
+ });
186
+ } else {
187
+ return mappedComponents;
188
+ }
139
189
  }
140
190
  return [];
141
191
  }
@@ -158,7 +208,8 @@ export function ComponentTree({}) {
158
208
  data: c,
159
209
  parent: parent,
160
210
  tags: [],
161
- className: c.isRemovedFromMasterLanguage ? "text-gray-400" : "",
211
+ className:
212
+ c.isRemovedFromMasterLanguage || !c.editable ? "text-gray-400" : "",
162
213
  type: "component",
163
214
  };
164
215
 
@@ -219,6 +270,7 @@ export function ComponentTree({}) {
219
270
  node.children = mapPlaceholders(c, node);
220
271
 
221
272
  if (c.items.length > 0 && c.items[0]!.id !== c.datasourceItem?.id) {
273
+ console.log("c.items", c.items, c);
222
274
  const itemsNode: CustomTreeNode = {
223
275
  key: "items" + c.id,
224
276
  label: "items",
@@ -287,6 +339,7 @@ export function ComponentTree({}) {
287
339
 
288
340
  const handleTreeSelection = useCallback(
289
341
  (key: string, event: React.MouseEvent) => {
342
+ console.log("handleTreeSelection", key, event);
290
343
  let newKeys = [key];
291
344
  if (event.ctrlKey) {
292
345
  if (selectedKeys.includes(key)) {
@@ -300,6 +353,8 @@ export function ComponentTree({}) {
300
353
  .filter((key) => nodeDictionary[key])
301
354
  .map((key) => nodeDictionary[key]!.componentId!);
302
355
 
356
+ console.log("selectedComponentIds", selectedComponentIds, nodeDictionary);
357
+
303
358
  editContextRef.current?.select(selectedComponentIds);
304
359
 
305
360
  if (selectedComponentIds.length === 1) {
@@ -489,60 +544,88 @@ export function ComponentTree({}) {
489
544
 
490
545
  // Use the PerfectTree component
491
546
  return (
492
- <div className="p-2" ref={treeRef}>
493
- <PerfectTree
494
- nodes={rootNodes}
495
- isDragging={!!editContext?.dragObject}
496
- selectedKeys={selectedItems}
497
- expandedKeys={expandedItems}
498
- onToggleExpand={handleToggle}
499
- onSelect={handleTreeSelection}
500
- onContextMenu={handleTreeContextMenu}
501
- enableDragAndDrop={true}
502
- onDragOverZone={(parent, index, event) =>
503
- handleDragOverZone(parent as CustomTreeNode | null, index, event)
504
- }
505
- isValidDropZone={(parent, index) => {
506
- const placeholder = getPlaceholder(parent as CustomTreeNode | null);
507
- if (!placeholder) return false;
508
- if (!editContext?.dragObject) return false;
509
- const result = isValidPlaceholder(
510
- placeholder,
511
- editContext.dragObject,
512
- );
513
-
514
- return result;
515
- }}
516
- onStartDrag={(data) => {
517
- const component = data.node.data as Component;
518
-
519
- // Initialize drag data to make sure dataTransfer is set
520
- data.event.dataTransfer.setData("text/plain", data.node.key);
521
-
522
- // Only create drag object for components with datasourceItem
523
- if (!component?.datasourceItem) return;
524
- setTimeout(() => {
525
- editContext!.dragStart({
526
- type: "component",
527
- typeId: component.typeId,
528
- templateId: component.datasourceItem?.templateId,
529
- name: component.name,
530
- component: {
531
- id: component.id,
532
- language: editContext!.page!.item.language,
533
- version: editContext!.page!.item.version,
534
- },
535
- });
536
- }, 1);
537
- }}
538
- onDragEnd={(event) => {
539
- editContext!.dragEnd();
540
- }}
541
- onDrop={(parent, index, event) =>
542
- handleDropZone(parent as CustomTreeNode | null, index, event)
543
- }
544
- renderNode={(node) => renderNode(node as CustomTreeNode)}
545
- />
547
+ <div className="flex h-full flex-col">
548
+ {/* Toolbar */}
549
+ <div className="flex items-center justify-between border-b border-gray-200 px-2 py-1">
550
+ <SimpleIconButton
551
+ icon={
552
+ editContext?.hideNonEditableComponents ? (
553
+ <EyeOff className="h-4 w-4" strokeWidth={1.5} />
554
+ ) : (
555
+ <EyeIcon className="h-4 w-4" strokeWidth={1.5} />
556
+ )
557
+ }
558
+ label={
559
+ editContext?.hideNonEditableComponents
560
+ ? "Show non-editable components"
561
+ : "Hide non-editable components"
562
+ }
563
+ size="small"
564
+ selected={editContext?.hideNonEditableComponents}
565
+ onClick={() =>
566
+ editContext?.setHideNonEditableComponents(
567
+ !editContext.hideNonEditableComponents,
568
+ )
569
+ }
570
+ />
571
+ </div>
572
+
573
+ {/* Tree content */}
574
+ <div className="flex-1 p-2" ref={treeRef}>
575
+ <PerfectTree
576
+ nodes={rootNodes}
577
+ isDragging={!!editContext?.dragObject}
578
+ selectedKeys={selectedItems}
579
+ expandedKeys={expandedItems}
580
+ onToggleExpand={handleToggle}
581
+ onSelect={handleTreeSelection}
582
+ onContextMenu={handleTreeContextMenu}
583
+ enableDragAndDrop={true}
584
+ onDragOverZone={(parent, index, event) =>
585
+ handleDragOverZone(parent as CustomTreeNode | null, index, event)
586
+ }
587
+ isValidDropZone={(parent, index) => {
588
+ const placeholder = getPlaceholder(parent as CustomTreeNode | null);
589
+ if (!placeholder) return false;
590
+ if (!editContext?.dragObject) return false;
591
+ const result = isValidPlaceholder(
592
+ placeholder,
593
+ editContext.dragObject,
594
+ );
595
+
596
+ return result;
597
+ }}
598
+ onStartDrag={(data) => {
599
+ const component = data.node.data as Component;
600
+
601
+ // Initialize drag data to make sure dataTransfer is set
602
+ data.event.dataTransfer.setData("text/plain", data.node.key);
603
+
604
+ // Only create drag object for components with datasourceItem
605
+ if (!component?.datasourceItem) return;
606
+ setTimeout(() => {
607
+ editContext!.dragStart({
608
+ type: "component",
609
+ typeId: component.typeId,
610
+ templateId: component.datasourceItem?.templateId,
611
+ name: component.name,
612
+ component: {
613
+ id: component.id,
614
+ language: editContext!.page!.item.language,
615
+ version: editContext!.page!.item.version,
616
+ },
617
+ });
618
+ }, 1);
619
+ }}
620
+ onDragEnd={(event) => {
621
+ editContext!.dragEnd();
622
+ }}
623
+ onDrop={(parent, index, event) =>
624
+ handleDropZone(parent as CustomTreeNode | null, index, event)
625
+ }
626
+ renderNode={(node) => renderNode(node as CustomTreeNode)}
627
+ />
628
+ </div>
546
629
  </div>
547
630
  );
548
631
  }
@@ -98,7 +98,7 @@ export function Debug({}: {}) {
98
98
  tabs={tabs}
99
99
  activeTab={activeTab}
100
100
  setActiveTab={setActiveTab}
101
- className="alpaceditor-tabs border-gray-3 border-b px-2 pt-2 text-sm"
101
+ className="alpaceditor-tabs border-gray-3 flex border-b px-2 pt-2 text-sm"
102
102
  />
103
103
 
104
104
  <div className="absolute top-2 right-1 h-4 w-4">
@@ -254,10 +254,19 @@ export function getFieldDescriptorFromElement(
254
254
  };
255
255
  }
256
256
 
257
- export function findNearestComponentId(startElement: Element) {
257
+ export function findNearestEditableComponentId(startElement: Element) {
258
258
  let element: Element | null = startElement;
259
259
  while (element) {
260
- if (element.hasAttribute("data-component-start")) {
260
+ if (element.getAttribute("sc_item") && isEditableComponent(element)) {
261
+ return extractItemIdFromItemUri(element.getAttribute("sc_item")!);
262
+ }
263
+
264
+ if (
265
+ element.hasAttribute("data-component-start") &&
266
+ !element.hasAttribute("data-skip-component") &&
267
+ isEditableComponent(element)
268
+ ) {
269
+ console.log("found editable component", element);
261
270
  return element.getAttribute("data-component-start") ?? undefined;
262
271
  }
263
272
  const prev: Element | null = element.previousElementSibling;
@@ -268,6 +277,10 @@ export function findNearestComponentId(startElement: Element) {
268
277
  return;
269
278
  }
270
279
 
280
+ function isEditableComponent(element: Element) {
281
+ return element.getAttribute("data-editable") !== "false";
282
+ }
283
+
271
284
  export function findFieldElement(
272
285
  iframe: HTMLIFrameElement,
273
286
  fieldDescriptor: FieldDescriptor,
@@ -427,35 +440,35 @@ export const getAbsolutePosition = (
427
440
  };
428
441
  };
429
442
 
430
- export function findParentComponentId(element: HTMLElement) {
431
- while (element && element !== document.documentElement) {
432
- if (element.getAttribute("sc_item")) {
433
- return extractItemIdFromItemUri(element.getAttribute("sc_item")!);
434
- }
443
+ // export function findParentComponentId(element: HTMLElement) {
444
+ // while (element && element !== document.documentElement) {
445
+ // if (element.getAttribute("sc_item")) {
446
+ // return extractItemIdFromItemUri(element.getAttribute("sc_item")!);
447
+ // }
435
448
 
436
- const startElement = findStartElement(element);
449
+ // const startElement = findStartElement(element);
437
450
 
438
- if (startElement) {
439
- return startElement.getAttribute("data-component-start");
440
- }
441
- element = element.parentElement as HTMLElement;
442
- }
443
- return null;
444
- }
451
+ // if (startElement) {
452
+ // return startElement.getAttribute("data-component-start");
453
+ // }
454
+ // element = element.parentElement as HTMLElement;
455
+ // }
456
+ // return null;
457
+ // }
445
458
 
446
- function findStartElement(element: HTMLElement) {
447
- let prev: Element | null = element;
448
- while (prev) {
449
- prev = prev.previousElementSibling;
450
- if (
451
- prev?.getAttribute("data-component-start") &&
452
- !prev.hasAttribute("data-skip-component")
453
- ) {
454
- return prev;
455
- }
456
- }
457
- return null;
458
- }
459
+ // function findStartElement(element: HTMLElement) {
460
+ // let prev: Element | null = element;
461
+ // while (prev) {
462
+ // prev = prev.previousElementSibling;
463
+ // if (
464
+ // prev?.getAttribute("data-component-start") &&
465
+ // !prev.hasAttribute("data-skip-component")
466
+ // ) {
467
+ // return prev;
468
+ // }
469
+ // }
470
+ // return null;
471
+ // }
459
472
 
460
473
  export function normalizeGuid(id: string) {
461
474
  id = id.toUpperCase();
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.3969";
2
- export const buildDate = "2025-06-27 01:46:55";
1
+ export const version = "1.0.3973";
2
+ export const buildDate = "2025-07-02 01:52:41";
package/src/types.ts CHANGED
@@ -168,6 +168,7 @@ export type UserPreferences = {
168
168
  pinnedViews?: string[];
169
169
  showViewNames?: boolean;
170
170
  showRightSidebar?: boolean;
171
+ hideNonEditableComponents?: boolean;
171
172
  };
172
173
 
173
174
  export type HistoryEntry = {
@@ -238,6 +239,7 @@ export type InsertOption = {
238
239
  typeId: string;
239
240
  name: string;
240
241
  icon: string;
242
+ thumbnail?: string;
241
243
  group?: string;
242
244
  svg?: string;
243
245
  isInvalid: boolean;