@alpaca-editor/core 1.0.3969 → 1.0.3974

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 (88) hide show
  1. package/dist/components/ui/command.js +1 -1
  2. package/dist/components/ui/command.js.map +1 -1
  3. package/dist/config/config.js +5 -1
  4. package/dist/config/config.js.map +1 -1
  5. package/dist/editor/ContentTree.js +1 -1
  6. package/dist/editor/ContentTree.js.map +1 -1
  7. package/dist/editor/FieldList.d.ts +3 -0
  8. package/dist/editor/FieldList.js +3 -1
  9. package/dist/editor/FieldList.js.map +1 -1
  10. package/dist/editor/ItemInfo.js +4 -2
  11. package/dist/editor/ItemInfo.js.map +1 -1
  12. package/dist/editor/client/EditorClient.js +28 -2
  13. package/dist/editor/client/EditorClient.js.map +1 -1
  14. package/dist/editor/client/editContext.d.ts +2 -0
  15. package/dist/editor/client/editContext.js.map +1 -1
  16. package/dist/editor/client/operations.js +36 -1
  17. package/dist/editor/client/operations.js.map +1 -1
  18. package/dist/editor/client/pageModelBuilder.js +2 -1
  19. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  20. package/dist/editor/field-types/ImageFieldEditor.js +3 -3
  21. package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
  22. package/dist/editor/field-types/PictureFieldEditor.js +2 -2
  23. package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
  24. package/dist/editor/menubar/ActiveUsers.js +18 -5
  25. package/dist/editor/menubar/ActiveUsers.js.map +1 -1
  26. package/dist/editor/menubar/LanguageSelector.js +3 -10
  27. package/dist/editor/menubar/LanguageSelector.js.map +1 -1
  28. package/dist/editor/menubar/WorkflowButton.js +63 -14
  29. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  30. package/dist/editor/page-editor-chrome/CommentHighlighting.js +68 -48
  31. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  32. package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
  33. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  34. package/dist/editor/page-viewer/EditorForm.js +77 -12
  35. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  36. package/dist/editor/page-viewer/PageViewerFrame.js +21 -48
  37. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  38. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +7 -1
  39. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  40. package/dist/editor/page-viewer/pageViewContext.d.ts +2 -0
  41. package/dist/editor/page-viewer/pageViewContext.js +10 -0
  42. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  43. package/dist/editor/pageModel.d.ts +5 -0
  44. package/dist/editor/services/contentService.d.ts +3 -0
  45. package/dist/editor/services/contentService.js +0 -1
  46. package/dist/editor/services/contentService.js.map +1 -1
  47. package/dist/editor/sidebar/ComponentPalette.js +2 -2
  48. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  49. package/dist/editor/sidebar/ComponentTree.js +193 -78
  50. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  51. package/dist/editor/sidebar/Debug.js +1 -1
  52. package/dist/editor/sidebar/Debug.js.map +1 -1
  53. package/dist/editor/utils.d.ts +1 -2
  54. package/dist/editor/utils.js +36 -30
  55. package/dist/editor/utils.js.map +1 -1
  56. package/dist/revision.d.ts +2 -2
  57. package/dist/revision.js +2 -2
  58. package/dist/styles.css +37 -10
  59. package/dist/types.d.ts +2 -0
  60. package/package.json +1 -1
  61. package/src/components/ui/command.tsx +1 -1
  62. package/src/config/config.tsx +5 -1
  63. package/src/editor/ContentTree.tsx +1 -1
  64. package/src/editor/FieldList.tsx +35 -2
  65. package/src/editor/ItemInfo.tsx +7 -6
  66. package/src/editor/client/EditorClient.tsx +46 -2
  67. package/src/editor/client/editContext.ts +3 -0
  68. package/src/editor/client/operations.ts +43 -1
  69. package/src/editor/client/pageModelBuilder.ts +2 -1
  70. package/src/editor/field-types/ImageFieldEditor.tsx +7 -7
  71. package/src/editor/field-types/PictureFieldEditor.tsx +4 -4
  72. package/src/editor/menubar/ActiveUsers.tsx +26 -11
  73. package/src/editor/menubar/LanguageSelector.tsx +11 -25
  74. package/src/editor/menubar/WorkflowButton.tsx +125 -38
  75. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +82 -54
  76. package/src/editor/page-editor-chrome/FrameMenu.tsx +1 -1
  77. package/src/editor/page-viewer/EditorForm.tsx +96 -12
  78. package/src/editor/page-viewer/PageViewerFrame.tsx +27 -62
  79. package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +8 -1
  80. package/src/editor/page-viewer/pageViewContext.ts +15 -3
  81. package/src/editor/pageModel.ts +5 -0
  82. package/src/editor/services/contentService.ts +10 -2
  83. package/src/editor/sidebar/ComponentPalette.tsx +4 -4
  84. package/src/editor/sidebar/ComponentTree.tsx +273 -114
  85. package/src/editor/sidebar/Debug.tsx +1 -1
  86. package/src/editor/utils.ts +40 -32
  87. package/src/revision.ts +2 -2
  88. 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, Pentagon } 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,59 @@ 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
+
68
+ // Helper function to get all editable descendants, flattening the tree
69
+ function getEditableDescendants(
70
+ component: Component,
71
+ parent: CustomTreeNode,
72
+ ): CustomTreeNode[] {
73
+ const editableNodes: CustomTreeNode[] = [];
74
+
75
+ if (component.editable) {
76
+ // If this component is editable, include it and map its children normally
77
+ const node = mapComponentNode(component, parent);
78
+ editableNodes.push(node);
79
+ } else {
80
+ // If this component is not editable, skip it and collect its editable descendants
81
+ if (component.placeholders) {
82
+ for (const placeholder of component.placeholders) {
83
+ // if (placeholder.editable) {
84
+ // const placeholderNode = mapPlaceholderNode(placeholder, parent);
85
+ // editableNodes.push(placeholderNode);
86
+ // } else {
87
+ for (const childComponent of placeholder.components) {
88
+ const childEditableNodes = getEditableDescendants(
89
+ childComponent,
90
+ parent,
91
+ );
92
+ editableNodes.push(...childEditableNodes);
93
+ }
94
+ // }
95
+ }
96
+ }
97
+ }
98
+
99
+ return editableNodes;
100
+ }
101
+
48
102
  useEffect(() => {
49
103
  if (treeRef.current) {
50
104
  const selectedNodeKey = Object.keys(selectedKeys || {})[0];
@@ -106,7 +160,24 @@ export function ComponentTree({}) {
106
160
  tags: [],
107
161
  };
108
162
 
109
- node.children = p.components.map((c) => mapComponentNode(c, node));
163
+ if (editContext?.hideNonEditableComponents) {
164
+ // Flatten the tree: collect all editable descendants directly
165
+ const flattenedComponents: CustomTreeNode[] = [];
166
+ for (const component of p.components) {
167
+ const editableNodes = getEditableDescendants(component, node);
168
+ flattenedComponents.push(...editableNodes);
169
+ }
170
+ node.children = flattenedComponents;
171
+ } else {
172
+ // Always map components, but filter based on editability and descendants
173
+ const mappedComponents = p.components.map((c) =>
174
+ mapComponentNode(c, node),
175
+ );
176
+ node.children = mappedComponents.filter((componentNode) => {
177
+ const component = componentNode.data as Component;
178
+ return component.editable || true; //hasEditableDescendants(component);
179
+ });
180
+ }
110
181
 
111
182
  return node;
112
183
  }
@@ -119,23 +190,66 @@ export function ComponentTree({}) {
119
190
  return [];
120
191
  }
121
192
 
122
- // Show all placeholders (both editable and non-editable)
123
- const allPlaceholders = c.placeholders;
124
-
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) => {
131
- const componentNode = mapComponentNode(c, parent);
132
- // Apply gray styling to components in non-editable placeholders
133
- if (!placeholder.editable) {
134
- componentNode.className =
135
- (componentNode.className || "") + " text-gray-400";
193
+ if (editContext?.hideNonEditableComponents) {
194
+ // Flatten the tree: collect all editable descendants from all placeholders
195
+ const flattenedNodes: CustomTreeNode[] = [];
196
+
197
+ for (const placeholder of c.placeholders) {
198
+ if (placeholder.editable) {
199
+ //If placeholder is editable, include it normally
200
+ if (c.placeholders.length > 1 || !c.editable) {
201
+ // Multiple placeholders - show placeholder node
202
+ const placeholderNode = mapPlaceholderNode(placeholder, parent);
203
+ flattenedNodes.push(placeholderNode);
204
+ } else {
205
+ // Single placeholder - map components directly
206
+ for (const component of placeholder.components) {
207
+ const editableNodes = getEditableDescendants(component, parent);
208
+ flattenedNodes.push(...editableNodes);
209
+ }
210
+ }
211
+ } else {
212
+ // Placeholder is not editable - collect editable descendants from its components
213
+ for (const component of placeholder.components) {
214
+ const editableNodes = getEditableDescendants(component, parent);
215
+ flattenedNodes.push(...editableNodes);
216
+ }
136
217
  }
137
- return componentNode;
218
+ }
219
+
220
+ return flattenedNodes;
221
+ } else {
222
+ // Original logic for when hideNonEditableComponents is false
223
+ // Filter placeholders based on editable descendants
224
+ let placeholdersToShow = c.placeholders.filter((p) => {
225
+ // Show placeholder if it's editable or has editable descendants
226
+ if (p.editable) return true;
227
+ return p.components.some(
228
+ (comp) => comp.editable || true, // || hasEditableDescendants(comp),
229
+ );
138
230
  });
231
+
232
+ if (placeholdersToShow.length > 1) {
233
+ return placeholdersToShow.map((x) => mapPlaceholderNode(x, parent));
234
+ } else if (placeholdersToShow.length > 0) {
235
+ const placeholder = placeholdersToShow[0]!;
236
+ // For single placeholders, map the components directly (don't show placeholder node)
237
+ const mappedComponents = placeholder.components.map((c) => {
238
+ const componentNode = mapComponentNode(c, parent);
239
+ // Apply gray styling to components in non-editable placeholders (only when showing non-editable components)
240
+ if (!placeholder.editable) {
241
+ componentNode.className =
242
+ (componentNode.className || "") + " text-gray-400";
243
+ }
244
+ return componentNode;
245
+ });
246
+
247
+ // Filter components to only show editable ones or those with editable descendants
248
+ return mappedComponents.filter((componentNode) => {
249
+ const component = componentNode.data as Component;
250
+ return component.editable || true; // || hasEditableDescendants(component);
251
+ });
252
+ }
139
253
  }
140
254
  return [];
141
255
  }
@@ -158,7 +272,8 @@ export function ComponentTree({}) {
158
272
  data: c,
159
273
  parent: parent,
160
274
  tags: [],
161
- className: c.isRemovedFromMasterLanguage ? "text-gray-400" : "",
275
+ className:
276
+ c.isRemovedFromMasterLanguage || !c.editable ? "text-gray-400" : "",
162
277
  type: "component",
163
278
  };
164
279
 
@@ -205,6 +320,16 @@ export function ComponentTree({}) {
205
320
  });
206
321
  }
207
322
 
323
+ if (c.isSharedLayout) {
324
+ node.tags!.push({
325
+ id: "shared-layout",
326
+ severity: "warning",
327
+ value: "",
328
+ icon: <Pentagon className="h-3 w-3 text-gray-400" strokeWidth={1} />,
329
+ tooltip: "Component is shared across all languages",
330
+ });
331
+ }
332
+
208
333
  if (c.isShared) {
209
334
  node.tags!.push({
210
335
  id: "linked",
@@ -218,7 +343,11 @@ export function ComponentTree({}) {
218
343
 
219
344
  node.children = mapPlaceholders(c, node);
220
345
 
221
- if (c.items.length > 0 && c.items[0]!.id !== c.datasourceItem?.id) {
346
+ if (
347
+ c.items.length > 0 &&
348
+ c.items[0]!.id !== c.datasourceItem?.id &&
349
+ c.items[0]!.id !== page?.item.id
350
+ ) {
222
351
  const itemsNode: CustomTreeNode = {
223
352
  key: "items" + c.id,
224
353
  label: "items",
@@ -283,7 +412,7 @@ export function ComponentTree({}) {
283
412
 
284
413
  setRootNodes(finalNodes);
285
414
  setNodeDictionary(dict);
286
- }, [page]);
415
+ }, [page, editContext?.hideNonEditableComponents]);
287
416
 
288
417
  const handleTreeSelection = useCallback(
289
418
  (key: string, event: React.MouseEvent) => {
@@ -360,45 +489,47 @@ export function ComponentTree({}) {
360
489
  },
361
490
  }));
362
491
 
363
- // Check if we have a placeholder to add insert options
364
- const placeholder = getPlaceholder(nodeDictionary[node.key] || null);
365
- if (placeholder && placeholder.insertOptions) {
366
- const insertOptions = placeholder.insertOptions
367
- .filter((x) => !x.isHidden && !x.isInvalid)
368
- .map((x) => ({
369
- id: x.typeId,
370
- icon: (
371
- <img
372
- src={getAbsoluteIconUrl(x.icon)}
373
- width="16"
374
- height="16"
375
- className="m-1"
376
- />
377
- ),
378
- label: x.name,
379
- command: () => {
380
- editContextRef.current?.operations.addComponent(
381
- x.typeId,
382
- placeholder.key,
383
- 0, // Insert at the beginning by default
384
- editContextRef.current?.currentItemDescriptor!,
385
- );
386
- },
387
- }));
388
-
389
- if (insertOptions.length > 0) {
390
- // Add separator if there are existing menu items
391
- if (menu.length > 0) {
392
- menu.push({ separator: true });
492
+ if (editContext?.mode === "edit") {
493
+ // Check if we have a placeholder to add insert options
494
+ const placeholder = getPlaceholder(nodeDictionary[node.key] || null);
495
+ if (placeholder && placeholder.insertOptions) {
496
+ const insertOptions = placeholder.insertOptions
497
+ .filter((x) => !x.isHidden && !x.isInvalid)
498
+ .map((x) => ({
499
+ id: x.typeId,
500
+ icon: (
501
+ <img
502
+ src={getAbsoluteIconUrl(x.icon)}
503
+ width="16"
504
+ height="16"
505
+ className="m-1"
506
+ />
507
+ ),
508
+ label: x.name,
509
+ command: () => {
510
+ editContextRef.current?.operations.addComponent(
511
+ x.typeId,
512
+ placeholder.key,
513
+ 0, // Insert at the beginning by default
514
+ editContextRef.current?.currentItemDescriptor!,
515
+ );
516
+ },
517
+ }));
518
+
519
+ if (insertOptions.length > 0) {
520
+ // Add separator if there are existing menu items
521
+ if (menu.length > 0) {
522
+ menu.push({ separator: true });
523
+ }
524
+
525
+ // Add insert submenu
526
+ menu.push({
527
+ id: "insert",
528
+ label: "Insert",
529
+ icon: <Plus size={16} />,
530
+ items: insertOptions,
531
+ });
393
532
  }
394
-
395
- // Add insert submenu
396
- menu.push({
397
- id: "insert",
398
- label: "Insert",
399
- icon: <Plus size={16} />,
400
- items: insertOptions,
401
- });
402
533
  }
403
534
  }
404
535
 
@@ -489,60 +620,88 @@ export function ComponentTree({}) {
489
620
 
490
621
  // Use the PerfectTree component
491
622
  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
- />
623
+ <div className="flex h-full flex-col">
624
+ {/* Toolbar */}
625
+ <div className="flex items-center justify-between border-b border-gray-200 px-2 py-1">
626
+ <SimpleIconButton
627
+ icon={
628
+ editContext?.hideNonEditableComponents ? (
629
+ <EyeOff className="h-4 w-4" strokeWidth={1.5} />
630
+ ) : (
631
+ <EyeIcon className="h-4 w-4" strokeWidth={1.5} />
632
+ )
633
+ }
634
+ label={
635
+ editContext?.hideNonEditableComponents
636
+ ? "Show non-editable components"
637
+ : "Hide non-editable components"
638
+ }
639
+ size="small"
640
+ selected={editContext?.hideNonEditableComponents}
641
+ onClick={() =>
642
+ editContext?.setHideNonEditableComponents(
643
+ !editContext.hideNonEditableComponents,
644
+ )
645
+ }
646
+ />
647
+ </div>
648
+
649
+ {/* Tree content */}
650
+ <div className="flex-1 p-2" ref={treeRef}>
651
+ <PerfectTree
652
+ nodes={rootNodes}
653
+ isDragging={!!editContext?.dragObject}
654
+ selectedKeys={selectedItems}
655
+ expandedKeys={expandedItems}
656
+ onToggleExpand={handleToggle}
657
+ onSelect={handleTreeSelection}
658
+ onContextMenu={handleTreeContextMenu}
659
+ enableDragAndDrop={true}
660
+ onDragOverZone={(parent, index, event) =>
661
+ handleDragOverZone(parent as CustomTreeNode | null, index, event)
662
+ }
663
+ isValidDropZone={(parent, index) => {
664
+ const placeholder = getPlaceholder(parent as CustomTreeNode | null);
665
+ if (!placeholder) return false;
666
+ if (!editContext?.dragObject) return false;
667
+ const result = isValidPlaceholder(
668
+ placeholder,
669
+ editContext.dragObject,
670
+ );
671
+
672
+ return result;
673
+ }}
674
+ onStartDrag={(data) => {
675
+ const component = data.node.data as Component;
676
+
677
+ // Initialize drag data to make sure dataTransfer is set
678
+ data.event.dataTransfer.setData("text/plain", data.node.key);
679
+
680
+ // Only create drag object for components with datasourceItem
681
+ if (!component?.datasourceItem) return;
682
+ setTimeout(() => {
683
+ editContext!.dragStart({
684
+ type: "component",
685
+ typeId: component.typeId,
686
+ templateId: component.datasourceItem?.templateId,
687
+ name: component.name,
688
+ component: {
689
+ id: component.id,
690
+ language: editContext!.page!.item.language,
691
+ version: editContext!.page!.item.version,
692
+ },
693
+ });
694
+ }, 1);
695
+ }}
696
+ onDragEnd={(event) => {
697
+ editContext!.dragEnd();
698
+ }}
699
+ onDrop={(parent, index, event) =>
700
+ handleDropZone(parent as CustomTreeNode | null, index, event)
701
+ }
702
+ renderNode={(node) => renderNode(node as CustomTreeNode)}
703
+ />
704
+ </div>
546
705
  </div>
547
706
  );
548
707
  }
@@ -561,7 +720,7 @@ function renderNode(node: CustomTreeNode) {
561
720
  <div key={`tag-${node.key}-${x.id}`}>
562
721
  <span
563
722
  id={`id-${node.key}-${x.id}`}
564
- className="ml-2 rounded p-0.5 px-1.5 text-xs text-white"
723
+ className="ml-2 flex rounded p-0.5 px-1.5 text-xs text-white"
565
724
  style={{
566
725
  backgroundColor: x.color,
567
726
  marginLeft: "8px",
@@ -577,7 +736,7 @@ function renderNode(node: CustomTreeNode) {
577
736
  : undefined
578
737
  }
579
738
  >
580
- {x.icon && <i className={x.icon}></i>}
739
+ {x.icon}
581
740
  {x.value}
582
741
  </span>
583
742
  </div>
@@ -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,18 @@ 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
+ ) {
261
269
  return element.getAttribute("data-component-start") ?? undefined;
262
270
  }
263
271
  const prev: Element | null = element.previousElementSibling;
@@ -268,6 +276,10 @@ export function findNearestComponentId(startElement: Element) {
268
276
  return;
269
277
  }
270
278
 
279
+ function isEditableComponent(element: Element) {
280
+ return element.getAttribute("data-editable") !== "false";
281
+ }
282
+
271
283
  export function findFieldElement(
272
284
  iframe: HTMLIFrameElement,
273
285
  fieldDescriptor: FieldDescriptor,
@@ -277,10 +289,6 @@ export function findFieldElement(
277
289
  const fieldElement = iframe.contentWindow?.document.body.querySelector(
278
290
  "[data-itemid='" +
279
291
  fieldDescriptor.item.id +
280
- "'][data-language='" +
281
- fieldDescriptor.item.language +
282
- "'][data-version='" +
283
- fieldDescriptor.item.version +
284
292
  "'][data-fieldid='" +
285
293
  fieldDescriptor.fieldId +
286
294
  "']",
@@ -427,35 +435,35 @@ export const getAbsolutePosition = (
427
435
  };
428
436
  };
429
437
 
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
- }
438
+ // export function findParentComponentId(element: HTMLElement) {
439
+ // while (element && element !== document.documentElement) {
440
+ // if (element.getAttribute("sc_item")) {
441
+ // return extractItemIdFromItemUri(element.getAttribute("sc_item")!);
442
+ // }
435
443
 
436
- const startElement = findStartElement(element);
444
+ // const startElement = findStartElement(element);
437
445
 
438
- if (startElement) {
439
- return startElement.getAttribute("data-component-start");
440
- }
441
- element = element.parentElement as HTMLElement;
442
- }
443
- return null;
444
- }
446
+ // if (startElement) {
447
+ // return startElement.getAttribute("data-component-start");
448
+ // }
449
+ // element = element.parentElement as HTMLElement;
450
+ // }
451
+ // return null;
452
+ // }
445
453
 
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
- }
454
+ // function findStartElement(element: HTMLElement) {
455
+ // let prev: Element | null = element;
456
+ // while (prev) {
457
+ // prev = prev.previousElementSibling;
458
+ // if (
459
+ // prev?.getAttribute("data-component-start") &&
460
+ // !prev.hasAttribute("data-skip-component")
461
+ // ) {
462
+ // return prev;
463
+ // }
464
+ // }
465
+ // return null;
466
+ // }
459
467
 
460
468
  export function normalizeGuid(id: string) {
461
469
  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.3974";
2
+ export const buildDate = "2025-07-02 12:45:37";
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;