@jbpark/live-editor 4.1.1 → 4.3.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 (58) hide show
  1. package/README.ko.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/{ast-jab6hQrT.js → ast-BFbDWO-l.js} +288 -229
  4. package/dist/ast-BFbDWO-l.js.map +1 -0
  5. package/dist/{boundary-D2IFja-3.js → boundary-B17cepvm.js} +12 -9
  6. package/dist/boundary-B17cepvm.js.map +1 -0
  7. package/dist/{compile-CBOAtQMz.js → compile-0Dvp8zp5.js} +37 -5
  8. package/dist/compile-0Dvp8zp5.js.map +1 -0
  9. package/dist/{constants-BD3FZKWE.js → constants-CKvdqZtO.js} +11 -4
  10. package/dist/constants-CKvdqZtO.js.map +1 -0
  11. package/dist/{context-DQ-UtoyS.js → context-93X3gyfJ.js} +2 -2
  12. package/dist/{context-DQ-UtoyS.js.map → context-93X3gyfJ.js.map} +1 -1
  13. package/dist/{core-DaXVtvH5.js → core-B3ruccKb.js} +2 -2
  14. package/dist/{core-DaXVtvH5.js.map → core-B3ruccKb.js.map} +1 -1
  15. package/dist/{detect-typescript-b0A751v2.js → detect-typescript-5MV4mCM7.js} +2 -2
  16. package/dist/{detect-typescript-b0A751v2.js.map → detect-typescript-5MV4mCM7.js.map} +1 -1
  17. package/dist/dnd/index.d.ts +2 -2
  18. package/dist/dnd/index.js +2 -2
  19. package/dist/{dnd-CMrXfD8O.js → dnd-Z1KS16Fy.js} +969 -322
  20. package/dist/dnd-Z1KS16Fy.js.map +1 -0
  21. package/dist/{document-J9Lo9p81.d.ts → document-Ct4jW78J.d.ts} +42 -22
  22. package/dist/{document-C5Dypv4_.js → document-cEEdFERz.js} +138 -46
  23. package/dist/document-cEEdFERz.js.map +1 -0
  24. package/dist/editor/index.js +2 -2
  25. package/dist/{editor-C3tQaMlU.js → editor-DvMruljw.js} +2 -2
  26. package/dist/{editor-C3tQaMlU.js.map → editor-DvMruljw.js.map} +1 -1
  27. package/dist/error/index.d.ts +1 -1
  28. package/dist/error/index.js +2 -2
  29. package/dist/{error-KW8XCcjH.js → error-DWhxQEom.js} +2 -2
  30. package/dist/{error-KW8XCcjH.js.map → error-DWhxQEom.js.map} +1 -1
  31. package/dist/{index-DNNj_0ba.d.ts → index-C4SGQ3Ci.d.ts} +4 -3
  32. package/dist/{index-C4t0OawE.d.ts → index-CtlUYnWq.d.ts} +131 -34
  33. package/dist/{index-B_p74BRM.d.ts → index-w3kLcCXl.d.ts} +29 -5
  34. package/dist/index.d.ts +5 -5
  35. package/dist/index.js +5 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/preview/index.js +2 -2
  38. package/dist/{preview-BwEfltgi.js → preview-C_UzpGWh.js} +3 -3
  39. package/dist/{preview-BwEfltgi.js.map → preview-C_UzpGWh.js.map} +1 -1
  40. package/dist/provider/index.js +1 -1
  41. package/dist/sections-GJQ63uEB.js +24 -0
  42. package/dist/sections-GJQ63uEB.js.map +1 -0
  43. package/dist/{use-dynamic-tailwind-DuGLeDR3.js → use-dynamic-tailwind-DpbjmATF.js} +20 -11
  44. package/dist/use-dynamic-tailwind-DpbjmATF.js.map +1 -0
  45. package/dist/utils/ast/index.d.ts +3 -3
  46. package/dist/utils/ast/index.js +3 -3
  47. package/dist/utils/index.d.ts +7 -6
  48. package/dist/utils/index.js +5 -5
  49. package/package.json +1 -1
  50. package/dist/ast-jab6hQrT.js.map +0 -1
  51. package/dist/boundary-D2IFja-3.js.map +0 -1
  52. package/dist/compile-CBOAtQMz.js.map +0 -1
  53. package/dist/constants-BD3FZKWE.js.map +0 -1
  54. package/dist/dnd-CMrXfD8O.js.map +0 -1
  55. package/dist/document-C5Dypv4_.js.map +0 -1
  56. package/dist/sections-B7dWoNVJ.js +0 -19
  57. package/dist/sections-B7dWoNVJ.js.map +0 -1
  58. package/dist/use-dynamic-tailwind-DuGLeDR3.js.map +0 -1
@@ -1,22 +1,23 @@
1
- import { d as nanoid, h as __toESM, n as createSectionPreviewCache, p as require_lib, r as fillSectionIds } from "./document-C5Dypv4_.js";
2
- import { a as DRAGGABLE_ITEMS, t as BINDING_PROP } from "./constants-BD3FZKWE.js";
1
+ import { S as require_lib, _ as nanoid, c as inspectDocument, h as replaceIds, m as fillIdsFrom, n as createSectionPreviewCache, r as fillSectionIds, w as __toESM } from "./document-cEEdFERz.js";
2
+ import { c as RESERVED_BINDING_PROPERTIES, o as DRAGGABLE_ITEMS, r as DATA_ATTR, t as BINDING_PROP } from "./constants-CKvdqZtO.js";
3
3
  import { i as usePreview } from "./states-Ci1AvoQ9.js";
4
- import { C as parseBinding, E as canLosslesslyEvaluateSource, M as parseValue, O as extractNodeValue, S as getStructuredValue, _ as updateArrayItemValue, b as findEditableChildren, d as duplicateArrayItems, f as moveArrayItem, g as updateArrayItemProperty, h as removeArrayItems, i as replaceIds, j as parseArrayExpression, k as extractObjectProperties, l as extract, o as update, p as moveArrayItems, r as fillIds, s as getChildrenSignatures, t as validateBindingValue, u as appendArrayItem, v as canStructurallyEditArray, x as getCurrentValue, y as moveSelectedIndices } from "./ast-jab6hQrT.js";
4
+ import { A as parseValue, D as extractObjectProperties, E as extractNodeValue, _ as moveSelectedIndices, a as getChildrenSignatures, b as getStructuredValue, c as appendArrayItem, d as moveArrayItems, g as canStructurallyEditArray, h as updateArrayItemValue, i as updateAll, k as parseArrayExpression, l as duplicateArrayItems, m as updateArrayItemProperty, p as removeArrayItems, s as extract, t as validateBindingValue, u as moveArrayItem, v as findEditableChildren, w as canLosslesslyEvaluateSource, x as parseBinding, y as getCurrentValue } from "./ast-BFbDWO-l.js";
5
5
  import { t as cn } from "./cn-CAnad2vL.js";
6
- import { o as preloadScripts } from "./compile-CBOAtQMz.js";
7
- import { a as Frame, i as useStableModules, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-DuGLeDR3.js";
8
- import { t as Core } from "./core-DaXVtvH5.js";
9
- import { i as replaceSections, n as generateSection, t as extractSections } from "./sections-B7dWoNVJ.js";
10
- import { n as Error$1, t as ErrorBoundary } from "./boundary-D2IFja-3.js";
6
+ import { o as preloadScripts } from "./compile-0Dvp8zp5.js";
7
+ import { a as Frame, i as useStableModules, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-DpbjmATF.js";
8
+ import { t as Core } from "./core-B3ruccKb.js";
9
+ import { a as replaceSections, n as extractSections, r as generateSection } from "./sections-GJQ63uEB.js";
10
+ import { n as Error$1, t as ErrorBoundary } from "./boundary-B17cepvm.js";
11
11
  import { Children, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
12
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
- import { DndContext, DragOverlay, PointerSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
14
- import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
15
- import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
13
+ import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, PointerSensor, TouchSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
14
+ import { restrictToParentElement, restrictToVerticalAxis } from "@dnd-kit/modifiers";
15
+ import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
16
16
  import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography } from "@jbpark/ui-kit";
17
17
  import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
18
- import { ArrowDown, ArrowUp, ChevronDown, ChevronUp, Copy, LayoutGrid, Plus, Trash, X } from "lucide-react";
19
- import { CSS } from "@dnd-kit/utilities";
18
+ import { createPortal } from "react-dom";
19
+ import { ArrowDown, ArrowUp, ChevronDown, ChevronRight, ChevronUp, Copy, Crosshair, GripVertical, LayoutGrid, Plus, Trash, X } from "lucide-react";
20
+ import { CSS as CSS$1 } from "@dnd-kit/utilities";
20
21
  //#region src/components/dnd/droppable.tsx
21
22
  var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
22
23
  const Droppable = ({ children, className }) => {
@@ -46,6 +47,64 @@ const toastEditError = (error) => {
46
47
  const DndEditOptionsContext = createContext({ reportError: toastEditError });
47
48
  const useDndEditOptions = () => useContext(DndEditOptionsContext);
48
49
  //#endregion
50
+ //#region src/components/dnd/inspector.ts
51
+ const DndInspectorContext = createContext(null);
52
+ const useDndInspector = () => {
53
+ const inspector = useContext(DndInspectorContext);
54
+ if (!inspector) throw new Error("useDndInspector() must be used inside <Live.Dnd>.");
55
+ return inspector;
56
+ };
57
+ const outside = (root) => (element) => element.getRootNode() !== root;
58
+ const deepestAt = (root, x, y, skip) => {
59
+ const element = root.elementsFromPoint(x, y).find((candidate) => !skip(candidate));
60
+ if (!element) return null;
61
+ if (element instanceof HTMLIFrameElement) {
62
+ const document = element.contentDocument;
63
+ if (!document) return element;
64
+ const rect = element.getBoundingClientRect();
65
+ return deepestAt(document, x - rect.left, y - rect.top, outside(document)) ?? element;
66
+ }
67
+ if (element.shadowRoot && element.shadowRoot !== root) return deepestAt(element.shadowRoot, x, y, outside(element.shadowRoot)) ?? element;
68
+ return element;
69
+ };
70
+ const hasId = (element) => Boolean(element.getAttribute(DATA_ATTR.ID));
71
+ const pickElement = (section, overlay, x, y) => {
72
+ let element = deepestAt(section.ownerDocument, x, y, (candidate) => candidate === overlay || candidate === section || !section.contains(candidate));
73
+ while (element && element !== section && !hasId(element)) element = element.parentElement ?? element.parentNode?.host ?? null;
74
+ return element === section ? null : element;
75
+ };
76
+ const viewportRect = (element) => {
77
+ const rect = element.getBoundingClientRect();
78
+ let x = rect.left;
79
+ let y = rect.top;
80
+ let frame = element.ownerDocument.defaultView?.frameElement ?? null;
81
+ while (frame) {
82
+ const frameRect = frame.getBoundingClientRect();
83
+ x += frameRect.left;
84
+ y += frameRect.top;
85
+ frame = frame.ownerDocument.defaultView?.frameElement ?? null;
86
+ }
87
+ return new DOMRect(x, y, rect.width, rect.height);
88
+ };
89
+ //#endregion
90
+ //#region src/components/dnd/inspector-highlight.tsx
91
+ const InspectorHighlight = ({ rect }) => rect ? createPortal(/* @__PURE__ */ jsx("div", {
92
+ "aria-hidden": true,
93
+ "data-dnd-inspector-highlight": true,
94
+ style: {
95
+ position: "fixed",
96
+ left: rect.left,
97
+ top: rect.top,
98
+ width: rect.width,
99
+ height: rect.height,
100
+ pointerEvents: "none",
101
+ outline: "2px solid #3b82f6",
102
+ outlineOffset: -1,
103
+ background: "rgba(59, 130, 246, 0.12)",
104
+ zIndex: 2147483647
105
+ }
106
+ }), document.body) : null;
107
+ //#endregion
49
108
  //#region src/components/dnd/draggable.tsx
50
109
  const DraggableItem = ({ item, children }) => {
51
110
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
@@ -70,9 +129,16 @@ const DefaultDraggableItem = ({ item, onAdd, tapToAdd = false }) => /* @__PURE__
70
129
  ref,
71
130
  style: { opacity: isDragging ? .5 : 1 },
72
131
  ...dragProps,
73
- className: cn("cursor-grab", "outline-none", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
132
+ className: cn("cursor-grab", "outline-none focus-visible:ring-2 focus-visible:ring-blue-500", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
74
133
  onClick: tapToAdd && onAdd ? () => onAdd(item) : void 0,
75
134
  onDoubleClick: onAdd ? () => onAdd(item) : void 0,
135
+ onKeyDown: (event) => {
136
+ dragProps.onKeyDown?.(event);
137
+ if (onAdd && event.key === "Enter" && event.target === event.currentTarget && !event.defaultPrevented) {
138
+ event.preventDefault();
139
+ onAdd(item);
140
+ }
141
+ },
76
142
  children: item.name
77
143
  })
78
144
  });
@@ -91,9 +157,11 @@ const useDndLayout = () => useDndRegions("useDndLayout()");
91
157
  //#region src/components/dnd/panel-binding.ts
92
158
  const readAttribute = (node, name) => node.dataAttributes.find((attribute) => attribute.name === name)?.value;
93
159
  const canEditBindingValue = (node, binding) => {
160
+ if (RESERVED_BINDING_PROPERTIES.includes(binding.property)) return false;
94
161
  if (binding.property === "children" || binding.property === "innerText" || binding.property === "innerHTML" || binding.property === "items" || binding.property === "data" || binding.type === "array" || binding.type === "jsx" || binding.type === "richtext") return true;
95
162
  const attribute = node.attributes.find((candidate) => candidate.name === binding.property);
96
- return attribute?.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
163
+ if (!attribute) return true;
164
+ return attribute.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
97
165
  };
98
166
  const toBindingFields = (binding) => ({
99
167
  label: binding.label,
@@ -124,12 +192,19 @@ const resolveRenderEntry = (render, key) => {
124
192
  render: entry
125
193
  };
126
194
  };
195
+ const CONTENT_PROPERTIES = /* @__PURE__ */ new Set([
196
+ "innerText",
197
+ "innerHTML",
198
+ "children"
199
+ ]);
200
+ const isAttributeAbsent = (node, binding) => !CONTENT_PROPERTIES.has(binding.property) && !node.attributes.some((candidate) => candidate.name === binding.property);
127
201
  const toPanelBindingData = (node, id, binding) => ({
128
202
  ...toBindingFields(binding),
129
203
  id,
130
204
  value: getStructuredValue(node, binding.property, binding.type),
131
205
  rawValue: getCurrentValue(node, binding.property),
132
- ...!canEditBindingValue(node, binding) && { canEditValue: false }
206
+ ...!canEditBindingValue(node, binding) && { canEditValue: false },
207
+ ...isAttributeAbsent(node, binding) && { present: false }
133
208
  });
134
209
  const resolvePanelBindings = (node) => {
135
210
  const id = readAttribute(node, "data-id");
@@ -266,7 +341,7 @@ const useStructuralSelection = (count, revision) => {
266
341
  };
267
342
  };
268
343
  //#endregion
269
- //#region src/components/dnd/panel/use-children-editor.ts
344
+ //#region src/components/dnd/panel/use-dnd-children.ts
270
345
  const producedBy = (expected, action) => {
271
346
  if (action.type === "move-selected") {
272
347
  const moved = JSON.stringify(moveSelectedIndices(expected, new Set(action.indices), action.direction).items);
@@ -278,7 +353,7 @@ const producedBy = (expected, action) => {
278
353
  return next.length === expected.length + added && expected.every((signature, index) => next[index] === signature);
279
354
  };
280
355
  };
281
- const useChildrenEditor = (items, { onChange } = {}) => {
356
+ const useDndChildren = (items, { onChange } = {}) => {
282
357
  const expected = useMemo(() => getChildrenSignatures(items), [items]);
283
358
  const revision = JSON.stringify(expected);
284
359
  const { selection, record } = useStructuralSelection(items.length, revision);
@@ -326,7 +401,7 @@ const useChildrenEditor = (items, { onChange } = {}) => {
326
401
  //#region src/components/dnd/panel/children.tsx
327
402
  const Children$1 = ({ value, onChange, onNodeChange }) => {
328
403
  const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
329
- const { selection, actions } = useChildrenEditor(items, { onChange });
404
+ const { selection, actions } = useDndChildren(items, { onChange });
330
405
  const editableChildrenMap = useMemo(() => {
331
406
  const map = /* @__PURE__ */ new Map();
332
407
  items.forEach((item, index) => {
@@ -420,7 +495,8 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
420
495
  }), editableChildrenMap.get(itemIndex).map((editableNode, idx) => {
421
496
  const nodeId = editableNode.dataAttributes.find((a) => a.name === "data-id")?.value;
422
497
  return /* @__PURE__ */ jsxs("div", {
423
- className: "rounded border border-green-100 bg-white p-2",
498
+ "data-node-id": nodeId,
499
+ className: "rounded border border-green-100 bg-white p-2\n data-[picked]:ring-2 data-[picked]:ring-blue-400",
424
500
  children: [/* @__PURE__ */ jsx("div", {
425
501
  className: "mb-1 text-xs text-green-600",
426
502
  children: editableNode.tagName
@@ -450,7 +526,118 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
450
526
  });
451
527
  };
452
528
  //#endregion
453
- //#region src/components/dnd/panel/use-items-editor.ts
529
+ //#region src/components/dnd/panel/field-kind.ts
530
+ const isColorProperty = (property) => property.toLowerCase().includes("color");
531
+ const getFieldKind = (binding) => {
532
+ const { property, type, value } = binding;
533
+ if (binding.canEditValue === false) return "readonly";
534
+ if (property === "items" || property === "data" || type === "array") return "items";
535
+ if (type === "richtext") return "richtext";
536
+ if (property === BINDING_PROP.INNER_HTML) return "html";
537
+ if (type === "jsx") return "jsx";
538
+ if (property === "children" && Array.isArray(value)) return "children";
539
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return "object";
540
+ if (type === "boolean" || typeof value === "boolean") return "boolean";
541
+ if (binding.options && Array.isArray(binding.options)) return "select";
542
+ if (type === "color" || isColorProperty(property)) return "color";
543
+ if (type === "date") return "date";
544
+ if (type === "url") return "url";
545
+ if (type === "number" || typeof value === "number") return "number";
546
+ return "text";
547
+ };
548
+ const isStructuralFieldKind = (kind) => kind === "items" || kind === "children";
549
+ //#endregion
550
+ //#region src/components/dnd/panel/sortable-items.tsx
551
+ const label = (items, id) => {
552
+ const index = items.findIndex((item) => item.id === id);
553
+ return index >= 0 ? `Item ${index + 1}` : "Item";
554
+ };
555
+ const SortableItems = ({ items, disabled, onMove, children }) => {
556
+ const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 4 } }), useSensor(TouchSensor, { activationConstraint: {
557
+ delay: 250,
558
+ tolerance: 5
559
+ } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
560
+ const onDragEnd = ({ active, over }) => {
561
+ const from = items.find((item) => item.id === active.id);
562
+ const to = items.find((item) => item.id === over?.id);
563
+ if (from && to && from !== to) onMove(from, to.index);
564
+ };
565
+ return /* @__PURE__ */ jsx(DndContext, {
566
+ sensors,
567
+ collisionDetection: closestCenter,
568
+ modifiers: [restrictToVerticalAxis, restrictToParentElement],
569
+ accessibility: { announcements: {
570
+ onDragStart: ({ active }) => `Picked up ${label(items, active.id)}.`,
571
+ onDragOver: ({ active, over }) => over ? `${label(items, active.id)} is over ${label(items, over.id)}.` : void 0,
572
+ onDragEnd: ({ active, over }) => over ? `${label(items, active.id)} was moved to the place of ${label(items, over.id)}.` : `${label(items, active.id)} was dropped.`,
573
+ onDragCancel: ({ active }) => `Moving ${label(items, active.id)} was cancelled.`
574
+ } },
575
+ onDragEnd,
576
+ children: /* @__PURE__ */ jsx(SortableContext, {
577
+ items: items.map((item) => item.id),
578
+ strategy: verticalListSortingStrategy,
579
+ disabled,
580
+ children: /* @__PURE__ */ jsx("div", {
581
+ className: "space-y-4",
582
+ children: items.map(children)
583
+ })
584
+ })
585
+ });
586
+ };
587
+ const ItemCard = ({ id, title, disabled, expanded, onToggle, checkbox, controls, className, children }) => {
588
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
589
+ id,
590
+ disabled
591
+ });
592
+ return /* @__PURE__ */ jsxs("div", {
593
+ ref: setNodeRef,
594
+ style: {
595
+ transform: CSS$1.Translate.toString(transform),
596
+ transition
597
+ },
598
+ className: cn(className, isDragging && "relative z-10 shadow-md"),
599
+ children: [/* @__PURE__ */ jsxs("div", {
600
+ className: "flex items-center justify-between",
601
+ children: [/* @__PURE__ */ jsxs("div", {
602
+ className: "flex items-center space-x-1",
603
+ children: [
604
+ /* @__PURE__ */ jsx("button", {
605
+ type: "button",
606
+ ref: setActivatorNodeRef,
607
+ ...attributes,
608
+ ...listeners,
609
+ "aria-label": `Reorder ${title}`,
610
+ disabled,
611
+ className: "cursor-grab rounded p-0.5 text-gray-400\n hover:text-gray-700 disabled:cursor-not-allowed\n disabled:opacity-40",
612
+ children: /* @__PURE__ */ jsx(GripVertical, { size: 14 })
613
+ }),
614
+ checkbox,
615
+ /* @__PURE__ */ jsxs("button", {
616
+ type: "button",
617
+ "aria-expanded": expanded,
618
+ onClick: onToggle,
619
+ className: "inline-flex items-center space-x-1 rounded px-1 text-xs\n font-medium",
620
+ children: [expanded ? /* @__PURE__ */ jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(ChevronRight, { size: 14 }), /* @__PURE__ */ jsx("span", { children: title })]
621
+ })
622
+ ]
623
+ }), controls]
624
+ }), expanded && children]
625
+ });
626
+ };
627
+ //#endregion
628
+ //#region src/components/dnd/panel/use-dnd-items.ts
629
+ const createPendingCommit = () => {
630
+ let current = null;
631
+ return {
632
+ read: () => current,
633
+ write: (next) => {
634
+ current = next;
635
+ },
636
+ clear: () => {
637
+ current = null;
638
+ }
639
+ };
640
+ };
454
641
  const bindingsInJSX = (source) => {
455
642
  const nodes = extract(source);
456
643
  const container = nodes.find((n) => n.bindings?.some((b) => b.property === "children"));
@@ -548,7 +735,7 @@ const moveId = (ids, from, to) => {
548
735
  const removeIds = (ids, indices) => {
549
736
  return ids.filter((_, index) => !indices.has(index));
550
737
  };
551
- const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
738
+ const useDndItems = (value, { render, onChange, onNodeChange } = {}) => {
552
739
  const { objectItems, primitiveItems, parseError } = useMemo(() => parseSource(value), [value]);
553
740
  const canEditStructure = useMemo(() => canStructurallyEditArray(value), [value]);
554
741
  const { reportError } = useDndEditOptions();
@@ -574,8 +761,35 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
574
761
  rawItems,
575
762
  value
576
763
  ]);
764
+ const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
577
765
  const { selection, record } = useStructuralSelection(isPrimitive ? primitiveItems.length : objectItems.length, value);
578
- const commit = (next, nextIds, nextSelection) => {
766
+ const [pending] = useState(createPendingCommit);
767
+ useEffect(() => {
768
+ pending.clear();
769
+ });
770
+ const latest = () => {
771
+ const last = pending.read();
772
+ if (last?.from !== value) return {
773
+ value,
774
+ items: rawItems,
775
+ ids: identity.ids,
776
+ selected: selection.selected
777
+ };
778
+ const parsed = parseSource(last.value);
779
+ return {
780
+ value: last.value,
781
+ items: isPrimitive ? parsed.primitiveItems : parsed.objectItems,
782
+ ids: last.ids,
783
+ selected: last.selected
784
+ };
785
+ };
786
+ const locate = (snapshot, elementIndex) => {
787
+ const shown = rawItems.find((item) => item.elementIndex === elementIndex);
788
+ if (!shown) return;
789
+ const index = snapshot.ids.indexOf(identity.ids[shown.index]);
790
+ return index >= 0 ? snapshot.items[index] : null;
791
+ };
792
+ const commit = (snapshot, next, nextIds, nextSelection) => {
579
793
  if (next === null) {
580
794
  reportError({
581
795
  type: "items",
@@ -586,126 +800,160 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
586
800
  }
587
801
  const parsed = parseSource(next);
588
802
  const nextItems = kind === "primitive" ? parsed.primitiveItems : parsed.objectItems;
589
- if (nextIds) setIdentityState(createIdentityState(next, kind, nextItems, nextIds));
590
- else setIdentityState(createIdentityState(next, kind, nextItems, identity.ids));
803
+ const ids = nextIds ?? snapshot.ids;
804
+ pending.write({
805
+ from: value,
806
+ value: next,
807
+ ids,
808
+ selected: nextSelection
809
+ });
810
+ setIdentityState(createIdentityState(next, kind, nextItems, ids));
591
811
  record(nextSelection, (revision) => revision === next, { now: true });
592
812
  onChange?.(next);
593
813
  return true;
594
814
  };
595
- const elementIndicesOf = (indices) => {
596
- return new Set((isPrimitive ? primitiveItems : objectItems).filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
597
- };
815
+ const elementIndicesOf = (snapshot, indices) => new Set(snapshot.items.filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
598
816
  const updateProperty = (elementIndex, propertyKey, next) => {
599
- commit(updateArrayItemProperty(value, elementIndex, propertyKey, next, render), void 0, new Set(selection.selected));
817
+ const snapshot = latest();
818
+ const item = locate(snapshot, elementIndex);
819
+ if (item === null) return;
820
+ commit(snapshot, updateArrayItemProperty(snapshot.value, item?.elementIndex ?? elementIndex, propertyKey, next, render), void 0, new Set(snapshot.selected));
600
821
  };
601
- return {
602
- kind,
603
- items: isPrimitive ? primitiveItems.map((item) => ({
604
- id: identity.ids[item.index] ?? item.id,
605
- index: item.index,
606
- elementIndex: item.elementIndex,
607
- properties: [],
608
- nested: [],
609
- value: {
610
- id: `primitive-${item.id}`,
611
- label: `item-${item.index}`,
612
- property: item.type,
613
- value: item.value ?? "",
614
- rawValue: item.type === "unknown" ? item.source : String(item.value ?? ""),
615
- canEditValue: canLosslesslyEvaluateSource(item.source),
616
- onChange: (next) => commit(updateArrayItemValue(value, item.elementIndex, next), void 0, new Set(selection.selected))
822
+ const updateValue = (elementIndex, next) => {
823
+ const snapshot = latest();
824
+ const item = locate(snapshot, elementIndex);
825
+ if (item === null) return;
826
+ commit(snapshot, updateArrayItemValue(snapshot.value, item?.elementIndex ?? elementIndex, next), void 0, new Set(snapshot.selected));
827
+ };
828
+ const actions = {
829
+ add: () => {
830
+ const snapshot = latest();
831
+ const next = appendArrayItem(snapshot.value, kind);
832
+ commit(snapshot, next, next ? [...snapshot.ids, nanoid(6)] : void 0, selectionAfter({ type: "append" }, snapshot.selected, snapshot.items.length));
833
+ },
834
+ move: (elementIndex, toIndex) => {
835
+ const snapshot = latest();
836
+ const from = locate(snapshot, elementIndex);
837
+ const target = snapshot.items.find((item) => item.index === toIndex);
838
+ if (!from || !target) return;
839
+ commit(snapshot, moveArrayItem(snapshot.value, from.elementIndex, target.elementIndex), moveId(snapshot.ids, from.index, target.index), /* @__PURE__ */ new Set());
840
+ },
841
+ remove: (elementIndex) => {
842
+ const snapshot = latest();
843
+ const removed = locate(snapshot, elementIndex);
844
+ if (removed === null) return;
845
+ commit(snapshot, removeArrayItems(snapshot.value, /* @__PURE__ */ new Set([removed?.elementIndex ?? elementIndex]), kind), removed ? removeIds(snapshot.ids, /* @__PURE__ */ new Set([removed.index])) : void 0, /* @__PURE__ */ new Set());
846
+ },
847
+ duplicateSelected: () => {
848
+ const snapshot = latest();
849
+ const selected = elementIndicesOf(snapshot, snapshot.selected);
850
+ const next = duplicateArrayItems(snapshot.value, selected);
851
+ const copied = snapshot.items.filter((item) => selected.has(item.elementIndex)).sort((a, b) => a.elementIndex - b.elementIndex).map(() => nanoid(6));
852
+ commit(snapshot, next, next ? [...snapshot.ids, ...copied] : void 0, selectionAfter({
853
+ type: "duplicate",
854
+ indices: [...snapshot.selected]
855
+ }, snapshot.selected, snapshot.items.length));
856
+ },
857
+ moveSelected: (direction) => {
858
+ const snapshot = latest();
859
+ const nextIdentity = moveSelectedIndices(snapshot.ids, snapshot.selected, direction);
860
+ const result = moveArrayItems(snapshot.value, elementIndicesOf(snapshot, snapshot.selected), direction);
861
+ if (!result) {
862
+ commit(snapshot, null, void 0, snapshot.selected);
863
+ return;
617
864
  }
618
- })) : objectItems.map((item) => {
619
- const id = identity.ids[item.index] ?? item.id;
620
- const properties = Object.entries(item.editableProperties).map(([key, prop]) => {
621
- const binding = resolveRenderEntry(render, key);
622
- const propertySource = value.slice(prop.astNode.start, prop.astNode.end);
623
- return {
624
- ...toBindingFields(binding),
625
- id: `item-${id}-${key}`,
626
- meta: {
627
- ...binding.meta,
628
- valueType: prop.type
629
- },
630
- value: parseValue(String(prop.value)),
631
- rawValue: prop.type === "unknown" ? propertySource : String(prop.value),
632
- canEditValue: canLosslesslyEvaluateSource(propertySource),
633
- onChange: (next) => updateProperty(item.elementIndex, key, next)
634
- };
635
- });
636
- const nested = [...Object.entries(item.jsxBindings).map(([property, nodes]) => ({
637
- property,
638
- elements: nodes.flatMap((node) => {
639
- const source = resolvePanelBindings(node);
640
- if (!source) return [];
641
- return [{
642
- id: source.id,
643
- tagName: source.tagName,
644
- bindings: withPanelCommit(source.bindings, onNodeChange)
645
- }];
646
- })
647
- })), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
648
- property,
649
- elements: [],
650
- fallback: {
651
- id: `item-${id}-${property}-jsx`,
652
- label: property,
653
- property,
654
- type: "jsx",
655
- value: code,
656
- rawValue: code,
657
- canEditValue: true,
658
- onChange: (next) => updateProperty(item.elementIndex, property, next)
659
- }
660
- }))];
865
+ const visible = parseArrayExpression(result.code)?.elements.flatMap((node, index) => import_lib.isExpression(node) && import_lib.isObjectExpression(node) === !isPrimitive ? [index] : []) ?? [];
866
+ commit(snapshot, result.code, nextIdentity.items, new Set(visible.flatMap((position, index) => result.indices.has(position) ? [index] : [])));
867
+ },
868
+ removeSelected: () => {
869
+ const snapshot = latest();
870
+ commit(snapshot, removeArrayItems(snapshot.value, elementIndicesOf(snapshot, snapshot.selected), kind), removeIds(snapshot.ids, snapshot.selected), /* @__PURE__ */ new Set());
871
+ }
872
+ };
873
+ const items = isPrimitive ? primitiveItems.map((item) => ({
874
+ id: identity.ids[item.index] ?? item.id,
875
+ index: item.index,
876
+ elementIndex: item.elementIndex,
877
+ properties: [],
878
+ nested: [],
879
+ value: {
880
+ id: `primitive-${item.id}`,
881
+ label: `item-${item.index}`,
882
+ property: item.type,
883
+ value: item.value ?? "",
884
+ rawValue: item.type === "unknown" ? item.source : String(item.value ?? ""),
885
+ canEditValue: canLosslesslyEvaluateSource(item.source),
886
+ onChange: (next) => updateValue(item.elementIndex, next)
887
+ }
888
+ })) : objectItems.map((item) => {
889
+ const id = identity.ids[item.index] ?? item.id;
890
+ const properties = Object.entries(item.editableProperties).map(([key, prop]) => {
891
+ const binding = resolveRenderEntry(render, key);
892
+ const propertySource = value.slice(prop.astNode.start, prop.astNode.end);
661
893
  return {
662
- id,
663
- index: item.index,
664
- elementIndex: item.elementIndex,
665
- properties,
666
- nested
894
+ ...toBindingFields(binding),
895
+ id: `item-${id}-${key}`,
896
+ meta: {
897
+ ...binding.meta,
898
+ valueType: prop.type
899
+ },
900
+ value: parseValue(String(prop.value)),
901
+ rawValue: prop.type === "unknown" ? propertySource : String(prop.value),
902
+ canEditValue: canLosslesslyEvaluateSource(propertySource),
903
+ onChange: (next) => updateProperty(item.elementIndex, key, next)
667
904
  };
668
- }),
905
+ });
906
+ const nested = [...Object.entries(item.jsxBindings).map(([property, nodes]) => ({
907
+ property,
908
+ elements: nodes.flatMap((node) => {
909
+ const source = resolvePanelBindings(node);
910
+ if (!source) return [];
911
+ return [{
912
+ id: source.id,
913
+ tagName: source.tagName,
914
+ bindings: withPanelCommit(source.bindings, onNodeChange)
915
+ }];
916
+ })
917
+ })), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
918
+ property,
919
+ elements: [],
920
+ fallback: {
921
+ id: `item-${id}-${property}-jsx`,
922
+ label: property,
923
+ property,
924
+ type: "jsx",
925
+ value: code,
926
+ rawValue: code,
927
+ canEditValue: true,
928
+ onChange: (next) => updateProperty(item.elementIndex, property, next)
929
+ }
930
+ }))];
931
+ return {
932
+ id,
933
+ index: item.index,
934
+ elementIndex: item.elementIndex,
935
+ properties,
936
+ nested
937
+ };
938
+ });
939
+ return {
940
+ kind,
941
+ items,
669
942
  selection,
670
- actions: {
671
- add: () => {
672
- const next = appendArrayItem(value, kind);
673
- commit(next, next ? [...identity.ids, nanoid(6)] : void 0, selectionAfter({ type: "append" }, selection.selected, rawItems.length));
674
- },
675
- move: (elementIndex, toIndex) => {
676
- const items = isPrimitive ? primitiveItems : objectItems;
677
- const from = items.find((item) => item.elementIndex === elementIndex);
678
- const target = items.find((item) => item.index === toIndex);
679
- if (!from || !target) return;
680
- commit(moveArrayItem(value, elementIndex, target.elementIndex), moveId(identity.ids, from.index, target.index), /* @__PURE__ */ new Set());
681
- },
682
- remove: (elementIndex) => {
683
- const removed = (isPrimitive ? primitiveItems : objectItems).find((item) => item.elementIndex === elementIndex);
684
- commit(removeArrayItems(value, /* @__PURE__ */ new Set([elementIndex]), kind), removed ? removeIds(identity.ids, /* @__PURE__ */ new Set([removed.index])) : void 0, /* @__PURE__ */ new Set());
685
- },
686
- duplicateSelected: () => {
687
- const selected = elementIndicesOf(selection.selected);
688
- const next = duplicateArrayItems(value, selected);
689
- const copied = rawItems.filter((item) => selected.has(item.elementIndex)).sort((a, b) => a.elementIndex - b.elementIndex).map(() => nanoid(6));
690
- commit(next, next ? [...identity.ids, ...copied] : void 0, selectionAfter({
691
- type: "duplicate",
692
- indices: [...selection.selected]
693
- }, selection.selected, rawItems.length));
694
- },
695
- moveSelected: (direction) => {
696
- const nextIdentity = moveSelectedIndices(identity.ids, selection.selected, direction);
697
- const result = moveArrayItems(value, elementIndicesOf(selection.selected), direction);
698
- if (!result) {
699
- commit(null, void 0, selection.selected);
700
- return;
701
- }
702
- const visible = parseArrayExpression(result.code)?.elements.flatMap((node, index) => import_lib.isExpression(node) && import_lib.isObjectExpression(node) === !isPrimitive ? [index] : []) ?? [];
703
- commit(result.code, nextIdentity.items, new Set(visible.flatMap((position, index) => result.indices.has(position) ? [index] : [])));
704
- },
705
- removeSelected: () => {
706
- commit(removeArrayItems(value, elementIndicesOf(selection.selected), kind), removeIds(identity.ids, selection.selected), /* @__PURE__ */ new Set());
943
+ expansion: {
944
+ expandedIds: items.flatMap((item) => collapsed.has(item.id) ? [] : [item.id]),
945
+ isExpanded: (id) => !collapsed.has(id),
946
+ toggle: (id) => setCollapsed((previous) => {
947
+ const next = new Set(previous);
948
+ if (!next.delete(id)) next.add(id);
949
+ return next;
950
+ }),
951
+ setExpanded: (ids) => {
952
+ const expanded = new Set(ids);
953
+ setCollapsed(new Set(items.flatMap((item) => expanded.has(item.id) ? [] : [item.id])));
707
954
  }
708
955
  },
956
+ actions,
709
957
  canEditStructure,
710
958
  parseError
711
959
  };
@@ -734,7 +982,8 @@ const NestedGroup = ({ group, onNodeChange }) => {
734
982
  "):"
735
983
  ]
736
984
  }), group.elements.map((element) => /* @__PURE__ */ jsxs("div", {
737
- className: "rounded border border-blue-100 bg-blue-50 p-2",
985
+ "data-node-id": element.id,
986
+ className: "rounded border border-blue-100 bg-blue-50 p-2\n data-[picked]:ring-2 data-[picked]:ring-blue-400",
738
987
  children: [/* @__PURE__ */ jsxs("div", {
739
988
  className: "mb-1 text-xs text-blue-600",
740
989
  children: [
@@ -769,7 +1018,7 @@ const NestedGroup = ({ group, onNodeChange }) => {
769
1018
  });
770
1019
  };
771
1020
  const Items = ({ value, render, onChange, onChildChange }) => {
772
- const { kind, items, selection, actions, canEditStructure, parseError } = useItemsEditor(value, {
1021
+ const { kind, items, selection, expansion, actions, canEditStructure, parseError } = useDndItems(value, {
773
1022
  render,
774
1023
  onChange,
775
1024
  onNodeChange: onChildChange
@@ -839,105 +1088,87 @@ const Items = ({ value, render, onChange, onChildChange }) => {
839
1088
  })
840
1089
  ]
841
1090
  });
842
- if (kind === "primitive") return /* @__PURE__ */ jsxs("div", {
843
- className: "space-y-4",
844
- children: [
845
- header,
846
- bulkBar,
847
- !canEditStructure && /* @__PURE__ */ jsx("div", {
848
- className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
849
- children: "Values remain editable, but moving, copying, adding, and deleting require a dense array without spreads or parenthesized top-level items. Use the code editor for those structural changes."
850
- }),
851
- items.map((item) => /* @__PURE__ */ jsxs("div", {
852
- className: "space-y-2 rounded border border-gray-100 bg-gray-50 p-2",
853
- children: [/* @__PURE__ */ jsxs("div", {
854
- className: "flex items-center justify-between space-x-1",
855
- children: [/* @__PURE__ */ jsx("div", {
856
- onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
857
- className: "inline-flex",
858
- children: /* @__PURE__ */ jsx(Checkbox, {
859
- checked: selection.isSelected(item.index),
860
- disabled: !canEditStructure,
861
- onChange: () => {}
862
- })
863
- }), itemControls(item)]
864
- }), /* @__PURE__ */ jsx(Field, {
865
- binding: item.value,
866
- onNodeChange: onChildChange
867
- })]
868
- }, item.id))
869
- ]
1091
+ const itemCheckbox = (item) => /* @__PURE__ */ jsx("div", {
1092
+ onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
1093
+ className: "inline-flex",
1094
+ children: /* @__PURE__ */ jsx(Checkbox, {
1095
+ checked: selection.isSelected(item.index),
1096
+ disabled: !canEditStructure,
1097
+ onChange: () => {}
1098
+ })
870
1099
  });
1100
+ const objectBody = (item) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1101
+ className: "space-y-2",
1102
+ children: item.properties.map((binding) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
1103
+ className: "flex flex-col space-y-2",
1104
+ children: [/* @__PURE__ */ jsx("label", {
1105
+ className: "w-20 shrink-0 text-xs font-medium",
1106
+ children: binding.label
1107
+ }), /* @__PURE__ */ jsx(Field, {
1108
+ binding,
1109
+ onNodeChange: onChildChange
1110
+ })]
1111
+ }), /* @__PURE__ */ jsxs("span", {
1112
+ className: "text-right text-xs text-gray-500",
1113
+ children: [
1114
+ "(",
1115
+ String(binding.meta?.valueType),
1116
+ ")"
1117
+ ]
1118
+ })] }, `${item.id}-${binding.property}-${binding.label}`))
1119
+ }), /* @__PURE__ */ jsx("div", {
1120
+ className: "space-y-3 border-t pt-2",
1121
+ children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
1122
+ group,
1123
+ onNodeChange: onChildChange
1124
+ }, group.property)) : /* @__PURE__ */ jsx("div", {
1125
+ className: "text-xs text-gray-500",
1126
+ children: "✓ No JSX bindings found"
1127
+ })
1128
+ })] });
1129
+ const allExpanded = expansion.expandedIds.length === items.length;
871
1130
  return /* @__PURE__ */ jsxs("div", {
872
1131
  className: "space-y-4",
873
1132
  children: [
874
1133
  header,
1134
+ items.length > 1 && /* @__PURE__ */ jsx("div", {
1135
+ className: "flex justify-end",
1136
+ children: /* @__PURE__ */ jsx(Button, {
1137
+ size: "small",
1138
+ variant: "text",
1139
+ onClick: () => expansion.setExpanded(allExpanded ? [] : items.map((item) => item.id)),
1140
+ children: allExpanded ? "Collapse all" : "Expand all"
1141
+ })
1142
+ }),
875
1143
  bulkBar,
876
1144
  !canEditStructure && /* @__PURE__ */ jsx("div", {
877
1145
  className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
878
1146
  children: "Values remain editable, but moving, copying, adding, and deleting require a dense array without spreads or parenthesized top-level items. Use the code editor for those structural changes."
879
1147
  }),
880
- items.map((item) => /* @__PURE__ */ jsxs("div", {
881
- className: "space-y-3 rounded border bg-gray-50 p-3",
882
- children: [
883
- /* @__PURE__ */ jsxs("div", {
884
- className: "flex items-center justify-between",
885
- children: [/* @__PURE__ */ jsxs("div", {
886
- className: "flex items-center space-x-2",
887
- children: [/* @__PURE__ */ jsx("div", {
888
- onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
889
- className: "inline-flex",
890
- children: /* @__PURE__ */ jsx(Checkbox, {
891
- checked: selection.isSelected(item.index),
892
- disabled: !canEditStructure,
893
- onChange: () => {}
894
- })
895
- }), /* @__PURE__ */ jsxs("div", {
896
- className: "text-xs font-medium",
897
- children: ["Item ", item.index + 1]
898
- })]
899
- }), itemControls(item)]
900
- }),
901
- /* @__PURE__ */ jsx("div", {
902
- className: "space-y-2",
903
- children: item.properties.map((binding) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
904
- className: "flex flex-col space-y-2",
905
- children: [/* @__PURE__ */ jsx("label", {
906
- className: "w-20 shrink-0 text-xs font-medium",
907
- children: binding.label
908
- }), /* @__PURE__ */ jsx(Field, {
909
- binding,
910
- onNodeChange: onChildChange
911
- })]
912
- }), /* @__PURE__ */ jsxs("span", {
913
- className: "text-right text-xs text-gray-500",
914
- children: [
915
- "(",
916
- String(binding.meta?.valueType),
917
- ")"
918
- ]
919
- })] }, `${item.id}-${binding.property}-${binding.label}`))
920
- }),
921
- /* @__PURE__ */ jsx("div", {
922
- className: "space-y-3 border-t pt-2",
923
- children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
924
- group,
925
- onNodeChange: onChildChange
926
- }, group.property)) : /* @__PURE__ */ jsx("div", {
927
- className: "text-xs text-gray-500",
928
- children: "✓ No JSX bindings found"
929
- })
930
- })
931
- ]
932
- }, item.id))
1148
+ /* @__PURE__ */ jsx(SortableItems, {
1149
+ items,
1150
+ disabled: !canEditStructure,
1151
+ onMove: (item, toIndex) => actions.move(item.elementIndex, toIndex),
1152
+ children: (item) => /* @__PURE__ */ jsx(ItemCard, {
1153
+ id: item.id,
1154
+ title: `Item ${item.index + 1}`,
1155
+ disabled: !canEditStructure,
1156
+ expanded: expansion.isExpanded(item.id),
1157
+ onToggle: () => expansion.toggle(item.id),
1158
+ checkbox: itemCheckbox(item),
1159
+ controls: itemControls(item),
1160
+ className: kind === "primitive" ? "space-y-2 rounded border border-gray-100 bg-gray-50 p-2" : "space-y-3 rounded border bg-gray-50 p-3",
1161
+ children: kind === "primitive" ? /* @__PURE__ */ jsx(Field, {
1162
+ binding: item.value,
1163
+ onNodeChange: onChildChange
1164
+ }) : objectBody(item)
1165
+ }, item.id)
1166
+ })
933
1167
  ]
934
1168
  });
935
1169
  };
936
1170
  //#endregion
937
1171
  //#region src/components/dnd/panel/field.tsx
938
- const isColorProperty = (propertyName) => {
939
- return propertyName.toLowerCase().includes("color");
940
- };
941
1172
  const normalizeToHex = (value) => {
942
1173
  const trimmed = value.trim();
943
1174
  if (/^#([0-9A-Fa-f]{3}){1,2}$/.test(trimmed)) return trimmed;
@@ -1024,6 +1255,10 @@ const ColorPickerField = ({ value, onChange }) => {
1024
1255
  }
1025
1256
  });
1026
1257
  };
1258
+ const FieldError = ({ message }) => message ? /* @__PURE__ */ jsx("p", {
1259
+ className: "mt-1 mb-0 text-xs text-red-500",
1260
+ children: message
1261
+ }) : null;
1027
1262
  const BuiltinField = ({ binding, onNodeChange }) => {
1028
1263
  const { id, value, rawValue, onChange } = binding;
1029
1264
  const [validationError, setValidationError] = useState(null);
@@ -1033,7 +1268,8 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1033
1268
  setPrevRawValue(rawValue);
1034
1269
  setText(rawValue);
1035
1270
  }
1036
- if (binding.canEditValue === false) return /* @__PURE__ */ jsxs("div", {
1271
+ const kind = getFieldKind(binding);
1272
+ if (kind === "readonly") return /* @__PURE__ */ jsxs("div", {
1037
1273
  className: "space-y-1 rounded border border-dashed border-amber-200 p-3\n text-xs text-amber-700",
1038
1274
  children: [
1039
1275
  /* @__PURE__ */ jsx("div", { children: "This expression is preserved but is not editable here." }),
@@ -1047,31 +1283,28 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1047
1283
  const commitIfChanged = (next) => {
1048
1284
  if (next !== (typeof next === "string" ? rawValue : value)) onChange(next);
1049
1285
  };
1050
- if (binding.property === "items" || binding.property === "data" || binding.type === "array") return /* @__PURE__ */ jsx(Items, {
1286
+ if (kind === "items") return /* @__PURE__ */ jsx(Items, {
1051
1287
  value: rawValue,
1052
1288
  render: binding.render,
1053
1289
  onChange,
1054
1290
  onChildChange: onNodeChange
1055
1291
  });
1056
- if (binding.type === "richtext") return /* @__PURE__ */ jsx(RichTextEditor, {
1292
+ if (kind === "richtext") return /* @__PURE__ */ jsx(RichTextEditor, {
1057
1293
  value: rawValue,
1058
1294
  toolbar: RICHTEXT_TOOLBAR,
1059
1295
  onChange: commitIfChanged
1060
1296
  });
1061
- if (binding.property === BINDING_PROP.INNER_HTML || binding.type === "jsx") {
1062
- const isHTML = binding.property === BINDING_PROP.INNER_HTML;
1063
- return /* @__PURE__ */ jsx(JSXEditorField, {
1064
- value: rawValue,
1065
- isHTML,
1066
- onSave: commitIfChanged
1067
- });
1068
- }
1069
- if (binding.property === "children" && Array.isArray(value)) return /* @__PURE__ */ jsx(Children$1, {
1297
+ if (kind === "html" || kind === "jsx") return /* @__PURE__ */ jsx(JSXEditorField, {
1298
+ value: rawValue,
1299
+ isHTML: kind === "html",
1300
+ onSave: commitIfChanged
1301
+ });
1302
+ if (kind === "children") return /* @__PURE__ */ jsx(Children$1, {
1070
1303
  value,
1071
1304
  onChange,
1072
1305
  onNodeChange
1073
1306
  });
1074
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1307
+ if (kind === "object") {
1075
1308
  const objectValue = value;
1076
1309
  return /* @__PURE__ */ jsx("div", {
1077
1310
  className: "space-y-2 rounded border border-gray-200 bg-gray-50 p-2",
@@ -1101,23 +1334,23 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1101
1334
  })
1102
1335
  });
1103
1336
  }
1104
- if (binding.type === "boolean" || typeof value === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
1337
+ if (kind === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
1105
1338
  checked: value === true || value === "true",
1106
1339
  onChange: (checked) => {
1107
1340
  onChange(checked);
1108
1341
  }
1109
1342
  });
1110
1343
  const stringValue = rawValue;
1111
- if (binding.options && Array.isArray(binding.options)) return /* @__PURE__ */ jsx(Select, {
1344
+ if (kind === "select") return /* @__PURE__ */ jsx(Select, {
1112
1345
  value: stringValue,
1113
- options: binding.options,
1346
+ options: binding.options ?? [],
1114
1347
  onChange: commitIfChanged
1115
1348
  });
1116
- if (binding.type === "color" || isColorProperty(binding.property)) return /* @__PURE__ */ jsx(ColorPickerField, {
1349
+ if (kind === "color") return /* @__PURE__ */ jsx(ColorPickerField, {
1117
1350
  value: normalizeToHex(stringValue),
1118
1351
  onChange: commitIfChanged
1119
1352
  });
1120
- if (binding.type === "date") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DatePicker, {
1353
+ if (kind === "date") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DatePicker, {
1121
1354
  value: parseDateValue(stringValue),
1122
1355
  onChange: (date) => {
1123
1356
  const next = date ? formatDateValue(date) : "";
@@ -1129,11 +1362,8 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1129
1362
  setValidationError(null);
1130
1363
  commitIfChanged(next);
1131
1364
  }
1132
- }), validationError && /* @__PURE__ */ jsx("p", {
1133
- className: "mt-1 text-xs text-red-500",
1134
- children: validationError
1135
- })] });
1136
- if (binding.type === "url") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1365
+ }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1366
+ if (kind === "url") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1137
1367
  type: "url",
1138
1368
  value: text,
1139
1369
  onChange: (e) => setText(e.target.value),
@@ -1148,11 +1378,8 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1148
1378
  setValidationError(null);
1149
1379
  commitIfChanged(next);
1150
1380
  }
1151
- }), validationError && /* @__PURE__ */ jsx("p", {
1152
- className: "mt-1 text-xs text-red-500",
1153
- children: validationError
1154
- })] });
1155
- if (binding.type === "number" || typeof value === "number") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1381
+ }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1382
+ if (kind === "number") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1156
1383
  type: "number",
1157
1384
  value: text,
1158
1385
  onChange: (e) => setText(e.target.value),
@@ -1168,10 +1395,7 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1168
1395
  setValidationError(null);
1169
1396
  commitIfChanged(next);
1170
1397
  }
1171
- }), validationError && /* @__PURE__ */ jsx("p", {
1172
- className: "mt-1 text-xs text-red-500",
1173
- children: validationError
1174
- })] });
1398
+ }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1175
1399
  return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input.TextArea, {
1176
1400
  value: text,
1177
1401
  onChange: (e) => setText(e.target.value),
@@ -1187,10 +1411,7 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1187
1411
  setValidationError(null);
1188
1412
  commitIfChanged(next);
1189
1413
  }
1190
- }), validationError && /* @__PURE__ */ jsx("p", {
1191
- className: "mt-1 text-xs text-red-500",
1192
- children: validationError
1193
- })] });
1414
+ }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1194
1415
  };
1195
1416
  const Field = (props) => {
1196
1417
  const { renderField } = useDndEditOptions();
@@ -1235,13 +1456,36 @@ const groupBindingsById = (bindings) => {
1235
1456
  }
1236
1457
  return [...groups.values()];
1237
1458
  };
1238
- const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange }) => {
1459
+ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange, readOnly = false }) => {
1239
1460
  const groups = groupBindingsById(bindings);
1461
+ const rootRef = useRef(null);
1462
+ const pickedId = (useContext(DndInspectorContext)?.picked ?? null)?.id;
1463
+ useEffect(() => {
1464
+ const root = rootRef.current;
1465
+ if (!root || !pickedId) return;
1466
+ const target = root.querySelector(`[data-node-id="${CSS.escape(pickedId)}"]`);
1467
+ if (!target) return;
1468
+ target.setAttribute("data-picked", "");
1469
+ const until = performance.now() + 600;
1470
+ let frame = 0;
1471
+ const keepInView = () => {
1472
+ const box = target.getBoundingClientRect();
1473
+ const view = root.getBoundingClientRect();
1474
+ if (box.top < view.top || box.bottom > view.bottom) target.scrollIntoView?.({ block: "nearest" });
1475
+ if (performance.now() < until) frame = requestAnimationFrame(keepInView);
1476
+ };
1477
+ keepInView();
1478
+ return () => {
1479
+ cancelAnimationFrame(frame);
1480
+ target.removeAttribute("data-picked");
1481
+ };
1482
+ }, [pickedId, groups.length]);
1240
1483
  if (!item) return /* @__PURE__ */ jsx(Typography.Paragraph, {
1241
1484
  className: cn("p-4 text-sm text-gray-500"),
1242
1485
  children: "Please select a section."
1243
1486
  });
1244
1487
  return /* @__PURE__ */ jsxs("div", {
1488
+ ref: rootRef,
1245
1489
  className: cn("h-full space-y-4 p-4", "overflow-x-hidden overflow-y-auto"),
1246
1490
  children: [
1247
1491
  /* @__PURE__ */ jsxs("div", {
@@ -1273,13 +1517,22 @@ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canM
1273
1517
  ]
1274
1518
  })]
1275
1519
  }),
1520
+ readOnly && /* @__PURE__ */ jsx(Typography.Text, {
1521
+ role: "status",
1522
+ className: "block text-xs text-amber-700",
1523
+ children: "The document has a syntax error. These fields are from the last version that parsed, and can't be edited until it parses again."
1524
+ }),
1276
1525
  !groups.length && /* @__PURE__ */ jsx(Typography.Text, {
1277
1526
  className: "text-xs text-gray-400",
1278
1527
  children: "No editable elements."
1279
1528
  }),
1280
- groups.map((group) => /* @__PURE__ */ jsx(FieldGroup, {
1281
- bindings: group,
1282
- onNodeChange
1529
+ groups.map((group) => /* @__PURE__ */ jsx("div", {
1530
+ "data-node-id": group[0].id,
1531
+ className: "rounded data-[picked]:ring-2 data-[picked]:ring-blue-400\n data-[picked]:ring-offset-2",
1532
+ children: /* @__PURE__ */ jsx(FieldGroup, {
1533
+ bindings: group,
1534
+ onNodeChange
1535
+ })
1283
1536
  }, group[0].id))
1284
1537
  ]
1285
1538
  });
@@ -1299,7 +1552,7 @@ const PaletteItems = ({ subject }) => {
1299
1552
  });
1300
1553
  };
1301
1554
  const PanelFields = ({ subject }) => {
1302
- const { panel: { item, onDelete, onMoveUp, onMoveDown, canMoveUp, canMoveDown, bindings, onNodeChange } } = useDndRegions(subject);
1555
+ const { panel: { item, onDelete, onMoveUp, onMoveDown, canMoveUp, canMoveDown, bindings, onNodeChange, readOnly } } = useDndRegions(subject);
1303
1556
  return /* @__PURE__ */ jsx(Panel$1, {
1304
1557
  item,
1305
1558
  onDelete,
@@ -1308,7 +1561,8 @@ const PanelFields = ({ subject }) => {
1308
1561
  canMoveUp,
1309
1562
  canMoveDown,
1310
1563
  bindings,
1311
- onNodeChange
1564
+ onNodeChange,
1565
+ readOnly
1312
1566
  });
1313
1567
  };
1314
1568
  const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
@@ -1317,8 +1571,9 @@ const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1317
1571
  children: /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Palette>" })
1318
1572
  });
1319
1573
  const Canvas = ({ className, style, ...restProps }) => {
1320
- const { canvas } = useDndRegions("<Live.Dnd.Canvas>");
1321
- return /* @__PURE__ */ jsx("div", {
1574
+ const { canvas, documentError } = useDndRegions("<Live.Dnd.Canvas>");
1575
+ const inspector = useDndInspector();
1576
+ return /* @__PURE__ */ jsxs("div", {
1322
1577
  className: cn("relative h-full w-full overflow-y-auto", className),
1323
1578
  "data-frame-container": true,
1324
1579
  style: {
@@ -1328,7 +1583,21 @@ const Canvas = ({ className, style, ...restProps }) => {
1328
1583
  ...style
1329
1584
  },
1330
1585
  ...restProps,
1331
- children: canvas
1586
+ children: [canvas, !documentError && /* @__PURE__ */ jsx("div", {
1587
+ className: "pointer-events-none sticky bottom-0 z-70 h-0",
1588
+ children: /* @__PURE__ */ jsx("div", {
1589
+ className: "pointer-events-auto absolute bottom-3 left-3",
1590
+ children: /* @__PURE__ */ jsx(Button, {
1591
+ icon: /* @__PURE__ */ jsx(Crosshair, {}),
1592
+ color: "primary",
1593
+ variant: inspector.active ? "solid" : "outlined",
1594
+ "aria-pressed": inspector.active,
1595
+ "aria-label": "Pick an element",
1596
+ title: inspector.active ? "Click an element to edit it. Esc to cancel." : "Pick an element",
1597
+ onClick: inspector.toggle
1598
+ })
1599
+ })
1600
+ })]
1332
1601
  });
1333
1602
  };
1334
1603
  const Panel = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
@@ -1495,15 +1764,48 @@ const arePropsEqual = (prev, next) => {
1495
1764
  var renderer_default = memo(Renderer, arePropsEqual);
1496
1765
  //#endregion
1497
1766
  //#region src/components/dnd/sortable.tsx
1498
- const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy }) => {
1499
- const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver, active } = useSortable({ id });
1767
+ const NAVIGATION_KEYS = {
1768
+ ArrowUp: "previous",
1769
+ ArrowDown: "next",
1770
+ Home: "first",
1771
+ End: "last"
1772
+ };
1773
+ const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy, nodeRef, onNavigate, onDeleteKey, inspecting = false, onInspectMove, onInspectPick, onInspectLeave }) => {
1774
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging, isOver, active } = useSortable({
1775
+ id,
1776
+ disabled: inspecting
1777
+ });
1500
1778
  const style = {
1501
- transform: CSS.Transform.toString(transform),
1779
+ transform: CSS$1.Transform.toString(transform),
1502
1780
  transition,
1503
1781
  opacity: isDragging ? .5 : 1,
1504
- cursor: "grab"
1782
+ cursor: inspecting ? "crosshair" : "grab"
1505
1783
  };
1506
1784
  const isNewItemOver = isOver && active?.data.current?.type === "new-item";
1785
+ const onKeyDown = (e) => {
1786
+ listeners?.onKeyDown?.(e);
1787
+ if (e.target !== e.currentTarget || active || e.defaultPrevented) return;
1788
+ if (e.key === "Enter") {
1789
+ e.preventDefault();
1790
+ onClick?.();
1791
+ return;
1792
+ }
1793
+ const to = NAVIGATION_KEYS[e.key];
1794
+ if (to && onNavigate) {
1795
+ e.preventDefault();
1796
+ onNavigate(to);
1797
+ return;
1798
+ }
1799
+ if ((e.key === "Delete" || e.key === "Backspace") && onDeleteKey) {
1800
+ e.preventDefault();
1801
+ onDeleteKey();
1802
+ }
1803
+ };
1804
+ const setRefs = (node) => {
1805
+ setNodeRef(node);
1806
+ setActivatorNodeRef(node);
1807
+ nodeRef?.(node);
1808
+ };
1507
1809
  const onDelete = (e) => {
1508
1810
  e.stopPropagation();
1509
1811
  _onDelete?.(id);
@@ -1513,14 +1815,23 @@ const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy
1513
1815
  _onCopy?.(id);
1514
1816
  };
1515
1817
  return /* @__PURE__ */ jsxs("div", {
1516
- ref: setNodeRef,
1818
+ ref: setRefs,
1517
1819
  style,
1518
1820
  ...attributes,
1519
1821
  ...listeners,
1822
+ onKeyDown,
1520
1823
  onClick,
1521
1824
  className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isNewItemOver && "border-t-4 border-t-green-500"),
1522
1825
  children: [
1523
- /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0 z-50") }),
1826
+ /* @__PURE__ */ jsx("div", {
1827
+ className: cn("absolute inset-0 z-50"),
1828
+ onPointerMove: inspecting ? (e) => onInspectMove?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY) : void 0,
1829
+ onPointerLeave: inspecting ? onInspectLeave : void 0,
1830
+ onClick: inspecting ? (e) => {
1831
+ e.stopPropagation();
1832
+ onInspectPick?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY);
1833
+ } : void 0
1834
+ }),
1524
1835
  children,
1525
1836
  selected && /* @__PURE__ */ jsxs(Space, {
1526
1837
  className: cn("absolute top-1 right-1 z-60"),
@@ -1547,7 +1858,7 @@ const Overlay = ({ sections, isForced, renderProps }) => {
1547
1858
  }
1548
1859
  const section = sections.find((s) => s.id === active.id);
1549
1860
  if (section) {
1550
- const preview = generateSection(section.code, renderProps.fullCode);
1861
+ const preview = generateSection(section.code, renderProps.fullCode, { containerId: renderProps.containerId });
1551
1862
  return /* @__PURE__ */ jsx(Sortable, {
1552
1863
  id: section.id,
1553
1864
  name: section.name,
@@ -1567,29 +1878,100 @@ const Overlay = ({ sections, isForced, renderProps }) => {
1567
1878
  };
1568
1879
  //#endregion
1569
1880
  //#region src/components/dnd/use-section-document.ts
1570
- const useSectionDocument = (value, onChange) => {
1881
+ const createLastParsed = () => {
1882
+ let last = null;
1883
+ return {
1884
+ read: (containerId) => last && last.containerId === containerId ? last.document : void 0,
1885
+ write: (containerId, document) => {
1886
+ last = {
1887
+ containerId,
1888
+ document
1889
+ };
1890
+ }
1891
+ };
1892
+ };
1893
+ const useSectionDocument = (value, onChange, { containerId, sectionNameFallback, onBlockedEdit } = {}) => {
1571
1894
  const { setCode } = usePreview();
1572
1895
  const [selectedId, setSelectedId] = useState(null);
1573
- const document = useMemo(() => fillSectionIds(value), [value]);
1574
- const sections = useMemo(() => extractSections(document), [document]);
1896
+ const readSections = useCallback((code) => extractSections(code, {
1897
+ containerId,
1898
+ sectionNameFallback
1899
+ }), [containerId, sectionNameFallback]);
1900
+ const document = useMemo(() => fillSectionIds(value, void 0, { containerId }), [containerId, value]);
1901
+ const [lastParsed] = useState(createLastParsed);
1902
+ const view = useMemo(() => {
1903
+ const inspection = inspectDocument(document, { containerId });
1904
+ if (inspection.ok) {
1905
+ lastParsed.write(containerId, document);
1906
+ return {
1907
+ document,
1908
+ problem: null,
1909
+ stale: false
1910
+ };
1911
+ }
1912
+ const { ok: _ok, ...problem } = inspection;
1913
+ const last = problem.reason === "parse-error" ? lastParsed.read(containerId) : void 0;
1914
+ return {
1915
+ document: last ?? document,
1916
+ problem,
1917
+ stale: last !== void 0
1918
+ };
1919
+ }, [
1920
+ containerId,
1921
+ document,
1922
+ lastParsed
1923
+ ]);
1924
+ const sections = useMemo(() => readSections(view.document), [readSections, view.document]);
1575
1925
  const [previewCache] = useState(() => createSectionPreviewCache());
1576
- const previews = useMemo(() => previewCache.compute(document, sections), [
1926
+ const previews = useMemo(() => previewCache.compute(view.document, sections, { containerId }), [
1927
+ containerId,
1577
1928
  previewCache,
1578
1929
  sections,
1579
- document
1930
+ view.document
1580
1931
  ]);
1581
1932
  const selectedIndex = sections.findIndex((s) => s.id === selectedId);
1582
1933
  const selectedItem = selectedIndex >= 0 ? sections[selectedIndex] : void 0;
1934
+ const pendingRef = useRef(null);
1935
+ useEffect(() => {
1936
+ pendingRef.current = null;
1937
+ });
1938
+ const latestDocument = useCallback(() => {
1939
+ const pending = pendingRef.current;
1940
+ return pending?.from === document ? pending.code : document;
1941
+ }, [document]);
1942
+ const latestSections = useCallback(() => readSections(latestDocument()), [latestDocument, readSections]);
1583
1943
  const commit = useCallback((nextSections) => {
1584
- const nextCode = fillSectionIds(replaceSections(document, nextSections.map((s) => s.code)));
1944
+ const nextCode = fillSectionIds(replaceSections(latestDocument(), nextSections.map((s) => s.code), { containerId }), void 0, { containerId });
1945
+ pendingRef.current = {
1946
+ from: document,
1947
+ code: nextCode
1948
+ };
1585
1949
  onChange?.(nextCode);
1586
1950
  setCode(nextCode);
1587
- return extractSections(nextCode);
1951
+ return readSections(nextCode);
1588
1952
  }, [
1953
+ containerId,
1589
1954
  document,
1955
+ latestDocument,
1590
1956
  onChange,
1957
+ readSections,
1591
1958
  setCode
1592
1959
  ]);
1960
+ const getCommittedSection = useCallback((id) => {
1961
+ const pending = pendingRef.current;
1962
+ if (pending?.from !== document) return;
1963
+ return readSections(pending.code).find((s) => s.id === id);
1964
+ }, [document, readSections]);
1965
+ const { problem, stale } = view;
1966
+ const refuse = useCallback(() => {
1967
+ if (!stale || !problem) return false;
1968
+ onBlockedEdit?.(problem);
1969
+ return true;
1970
+ }, [
1971
+ onBlockedEdit,
1972
+ problem,
1973
+ stale
1974
+ ]);
1593
1975
  return {
1594
1976
  sections,
1595
1977
  previews,
@@ -1599,54 +1981,91 @@ const useSectionDocument = (value, onChange) => {
1599
1981
  select: useCallback((id) => {
1600
1982
  setSelectedId((prev) => prev === id ? null : id);
1601
1983
  }, []),
1984
+ selectOnly: useCallback((id) => setSelectedId(id), []),
1602
1985
  clearSelection: useCallback(() => setSelectedId(null), []),
1603
1986
  add: useCallback((item, atIndex) => {
1987
+ if (refuse()) return;
1988
+ const current = latestSections();
1604
1989
  const next = { code: item.code };
1605
- commit(atIndex === void 0 || atIndex < 0 ? [...sections, next] : [
1606
- ...sections.slice(0, atIndex),
1990
+ commit(atIndex === void 0 || atIndex < 0 ? [...current, next] : [
1991
+ ...current.slice(0, atIndex),
1607
1992
  next,
1608
- ...sections.slice(atIndex)
1993
+ ...current.slice(atIndex)
1609
1994
  ]);
1610
- }, [commit, sections]),
1995
+ }, [
1996
+ commit,
1997
+ refuse,
1998
+ latestSections
1999
+ ]),
1611
2000
  remove: useCallback((id) => {
1612
- const index = sections.findIndex((s) => s.id === id);
2001
+ if (refuse()) return;
2002
+ const current = latestSections();
2003
+ const index = current.findIndex((s) => s.id === id);
1613
2004
  if (index < 0) return;
1614
2005
  if (id === selectedId) setSelectedId(null);
1615
- commit(sections.filter((_, i) => i !== index));
2006
+ commit(current.filter((_, i) => i !== index));
1616
2007
  }, [
1617
2008
  commit,
1618
- sections,
2009
+ refuse,
2010
+ latestSections,
1619
2011
  selectedId
1620
2012
  ]),
1621
2013
  copy: useCallback((id) => {
1622
- const index = sections.findIndex((s) => s.id === id);
1623
- const source = sections[index];
2014
+ if (refuse()) return;
2015
+ const current = latestSections();
2016
+ const index = current.findIndex((s) => s.id === id);
2017
+ const source = current[index];
1624
2018
  if (!source) return;
1625
2019
  const committed = commit([
1626
- ...sections.slice(0, index + 1),
2020
+ ...current.slice(0, index + 1),
1627
2021
  { code: replaceIds(source.code) },
1628
- ...sections.slice(index + 1)
2022
+ ...current.slice(index + 1)
1629
2023
  ]);
1630
2024
  setSelectedId(committed[index + 1]?.id ?? null);
1631
- }, [commit, sections]),
2025
+ }, [
2026
+ commit,
2027
+ refuse,
2028
+ latestSections
2029
+ ]),
1632
2030
  move: useCallback((id, direction) => {
1633
- const index = sections.findIndex((s) => s.id === id);
2031
+ if (refuse()) return;
2032
+ const current = latestSections();
2033
+ const index = current.findIndex((s) => s.id === id);
1634
2034
  const targetIndex = direction === "up" ? index - 1 : index + 1;
1635
- if (index < 0 || targetIndex < 0 || targetIndex >= sections.length) return;
1636
- commit(arrayMove(sections, index, targetIndex));
1637
- }, [commit, sections]),
2035
+ if (index < 0 || targetIndex < 0 || targetIndex >= current.length) return;
2036
+ commit(arrayMove(current, index, targetIndex));
2037
+ }, [
2038
+ commit,
2039
+ refuse,
2040
+ latestSections
2041
+ ]),
1638
2042
  reorder: useCallback((activeId, overId) => {
1639
- const prevIndex = sections.findIndex((s) => s.id === activeId);
1640
- const nextIndex = sections.findIndex((s) => s.id === overId);
2043
+ if (refuse()) return;
2044
+ const current = latestSections();
2045
+ const prevIndex = current.findIndex((s) => s.id === activeId);
2046
+ const nextIndex = current.findIndex((s) => s.id === overId);
1641
2047
  if (prevIndex < 0 || nextIndex < 0 || prevIndex === nextIndex) return;
1642
- commit(arrayMove(sections, prevIndex, nextIndex));
1643
- }, [commit, sections]),
2048
+ commit(arrayMove(current, prevIndex, nextIndex));
2049
+ }, [
2050
+ commit,
2051
+ refuse,
2052
+ latestSections
2053
+ ]),
1644
2054
  patch: useCallback((next) => {
1645
- commit(sections.map((s) => s.id === next.id ? {
2055
+ if (refuse()) return;
2056
+ const current = latestSections();
2057
+ commit(current.map((s) => s.id === next.id ? {
1646
2058
  ...s,
1647
2059
  ...next
1648
2060
  } : s));
1649
- }, [commit, sections])
2061
+ }, [
2062
+ commit,
2063
+ refuse,
2064
+ latestSections
2065
+ ]),
2066
+ getCommittedSection,
2067
+ problem,
2068
+ stale
1650
2069
  };
1651
2070
  };
1652
2071
  //#endregion
@@ -1665,6 +2084,14 @@ const describeUpdateFailure = (failure, label) => {
1665
2084
  title: `Failed to update "${label}"`,
1666
2085
  description: `${failure.count} bindings share ${failure.property ? `property "${failure.property}"` : `label "${label}"`} on this element — remove the duplicate in its data-binding.`
1667
2086
  };
2087
+ case "reserved-property": return {
2088
+ title: `Cannot edit "${label}" in the panel`,
2089
+ description: `"${failure.property}" is managed by the editor, so a binding can't change it.`
2090
+ };
2091
+ case "required-property": return {
2092
+ title: `Cannot remove "${label}"`,
2093
+ description: `"${failure.property}" is marked required in its data-binding, so it can't be removed.`
2094
+ };
1668
2095
  case "no-binding": return {
1669
2096
  title: `Failed to update "${label}"`,
1670
2097
  description: "This element has no data-binding declaration."
@@ -1684,13 +2111,48 @@ const describeUpdateFailure = (failure, label) => {
1684
2111
  default: return { title: `Failed to update "${label}"` };
1685
2112
  }
1686
2113
  };
2114
+ const KEYBOARD_CODES = {
2115
+ start: ["Space"],
2116
+ cancel: ["Escape"],
2117
+ end: ["Space", "Enter"]
2118
+ };
2119
+ const SCREEN_READER_INSTRUCTIONS = { draggable: "On a palette item, press Enter to add it to the canvas. On a canvas section, press Enter to select it, the up and down arrow keys to go to the previous or next section, and Delete to remove it. Press Space to pick a section up, the arrow keys to move it, and Space or Enter to drop it. Press Escape to cancel." };
2120
+ const createNodeRegistry = () => {
2121
+ const nodes = /* @__PURE__ */ new Map();
2122
+ const callbacks = /* @__PURE__ */ new Map();
2123
+ return {
2124
+ get: (id) => nodes.get(id),
2125
+ register: (id) => {
2126
+ let callback = callbacks.get(id);
2127
+ if (!callback) {
2128
+ callback = (node) => {
2129
+ if (node) nodes.set(id, node);
2130
+ else {
2131
+ nodes.delete(id);
2132
+ callbacks.delete(id);
2133
+ }
2134
+ };
2135
+ callbacks.set(id, callback);
2136
+ }
2137
+ return callback;
2138
+ }
2139
+ };
2140
+ };
2141
+ const blockedEditError = (error) => ({
2142
+ type: "parse",
2143
+ target: "document",
2144
+ reason: "parse-error",
2145
+ error,
2146
+ title: "The document has a syntax error",
2147
+ description: "The canvas shows the last version that parsed. Fix the error in the code, then edit here again."
2148
+ });
1687
2149
  const conditionalModifiers = (args) => {
1688
2150
  const { active } = args;
1689
2151
  if (active?.data.current?.type === "new-item") return args.transform;
1690
2152
  return restrictToVerticalAxis(args);
1691
2153
  };
1692
2154
  const NO_MODULES = {};
1693
- const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, children, ...restProps }) => {
2155
+ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, onBeforeDelete, sectionNameFallback, containerId, onNodePick, children, ...restProps }) => {
1694
2156
  const modules = useStableModules(_modules);
1695
2157
  const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
1696
2158
  const { breakpoint } = useResponsiveSize();
@@ -1704,9 +2166,35 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1704
2166
  return false;
1705
2167
  }
1706
2168
  };
1707
- const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }));
2169
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }), useSensor(KeyboardSensor, {
2170
+ coordinateGetter: sortableKeyboardCoordinates,
2171
+ keyboardCodes: KEYBOARD_CODES
2172
+ }));
2173
+ const nameOf = (id, data) => {
2174
+ return (data?.current)?.item?.name ?? sections.find((section) => section.id === id)?.name ?? "section";
2175
+ };
2176
+ const positionOf = (id) => sections.findIndex((section) => section.id === id) + 1;
2177
+ const announcements = {
2178
+ onDragStart: ({ active }) => `Picked up ${nameOf(active.id, active.data)}.`,
2179
+ onDragOver: ({ active, over }) => over && positionOf(over.id) > 0 ? `${nameOf(active.id, active.data)} moved to position ${positionOf(over.id)} of ${sections.length}.` : `${nameOf(active.id, active.data)} is no longer over a position.`,
2180
+ onDragEnd: ({ active, over }) => over && positionOf(over.id) > 0 ? `${nameOf(active.id, active.data)} dropped at position ${positionOf(over.id)} of ${sections.length}.` : `${nameOf(active.id, active.data)} dropped.`,
2181
+ onDragCancel: ({ active }) => `Moving ${nameOf(active.id, active.data)} was cancelled.`
2182
+ };
1708
2183
  const value = _value === void 0 ? code : _value;
1709
- const { sections, previews, selectedId, selectedItem, selectedIndex, select, clearSelection, add: addItem, remove: onDelete, copy: onCopy, move: moveSection, reorder, patch } = useSectionDocument(value, _onChange);
2184
+ const reportError = onEditError ?? toastEditError;
2185
+ const reportErrorRef = useRef(reportError);
2186
+ useEffect(() => {
2187
+ reportErrorRef.current = reportError;
2188
+ });
2189
+ const onBlockedEdit = useCallback((problem) => {
2190
+ if (problem.reason === "parse-error") reportErrorRef.current(blockedEditError(problem.error));
2191
+ }, []);
2192
+ const { sections, previews, selectedId, selectedItem, selectedIndex, select, selectOnly, clearSelection, add: addItem, remove, copy: onCopy, move: moveSection, reorder, patch, getCommittedSection, problem, stale } = useSectionDocument(value, _onChange, {
2193
+ containerId,
2194
+ sectionNameFallback,
2195
+ onBlockedEdit
2196
+ });
2197
+ const missingContainer = problem?.reason === "container-not-found" ? problem.containerId : null;
1710
2198
  const onDragStart = (_) => {};
1711
2199
  const onDragOver = (_e) => {};
1712
2200
  const onDragEnd = (event) => {
@@ -1721,11 +2209,110 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1721
2209
  if (active.id !== over.id && sections.some((s) => s.id === active.id)) reorder(String(active.id), String(over.id));
1722
2210
  };
1723
2211
  const onSelect = (id) => select(id);
2212
+ const [inspecting, setInspecting] = useState(false);
2213
+ const [picked, setPicked] = useState(null);
2214
+ const [highlight, setHighlight] = useState(null);
2215
+ if (picked && picked.sectionId !== selectedId) setPicked(null);
2216
+ const stopInspecting = useCallback(() => {
2217
+ setInspecting(false);
2218
+ setHighlight(null);
2219
+ }, []);
2220
+ useEffect(() => {
2221
+ if (!inspecting) return;
2222
+ const onKeyDown = (e) => {
2223
+ if (e.key === "Escape") stopInspecting();
2224
+ };
2225
+ window.addEventListener("keydown", onKeyDown);
2226
+ return () => window.removeEventListener("keydown", onKeyDown);
2227
+ }, [inspecting, stopInspecting]);
2228
+ const inspector = {
2229
+ active: inspecting,
2230
+ activate: () => setInspecting(true),
2231
+ deactivate: stopInspecting,
2232
+ toggle: () => inspecting ? stopInspecting() : setInspecting(true),
2233
+ picked
2234
+ };
2235
+ const onInspectMove = (section, overlay, x, y) => {
2236
+ const element = pickElement(section, overlay, x, y);
2237
+ setHighlight(element ? viewportRect(element) : null);
2238
+ };
2239
+ const onInspectPick = (sectionId, section, overlay, x, y) => {
2240
+ const id = pickElement(section, overlay, x, y)?.getAttribute(DATA_ATTR.ID);
2241
+ if (!id) return;
2242
+ const pick = {
2243
+ id,
2244
+ sectionId
2245
+ };
2246
+ selectOnly(sectionId);
2247
+ setPicked(pick);
2248
+ stopInspecting();
2249
+ onNodePick?.(pick);
2250
+ };
2251
+ const removeRef = useRef(remove);
2252
+ useEffect(() => {
2253
+ removeRef.current = remove;
2254
+ });
2255
+ const requestDelete = (id, onDeleted) => {
2256
+ const section = sections.find((candidate) => candidate.id === id);
2257
+ if (!onBeforeDelete || !section) {
2258
+ remove(id);
2259
+ onDeleted?.();
2260
+ return;
2261
+ }
2262
+ const decide = (allowed) => {
2263
+ if (allowed) {
2264
+ removeRef.current(id);
2265
+ onDeleted?.();
2266
+ }
2267
+ };
2268
+ try {
2269
+ const answer = onBeforeDelete(section);
2270
+ if (typeof answer === "boolean") {
2271
+ decide(answer);
2272
+ return;
2273
+ }
2274
+ answer.then(decide, (error) => {
2275
+ console.error("onBeforeDelete rejected; the section was kept", error);
2276
+ });
2277
+ } catch (error) {
2278
+ console.error("onBeforeDelete threw; the section was kept", error);
2279
+ }
2280
+ };
2281
+ const onDelete = (id) => requestDelete(id);
2282
+ const [sectionNodes] = useState(createNodeRegistry);
2283
+ const focusSection = (id) => sectionNodes.get(id)?.focus();
2284
+ const onNavigate = (fromId, to) => {
2285
+ const from = sections.findIndex((section) => section.id === fromId);
2286
+ const index = to === "first" ? 0 : to === "last" ? sections.length - 1 : from + (to === "next" ? 1 : -1);
2287
+ const target = sections[index];
2288
+ if (from < 0 || !target || target.id === fromId) return;
2289
+ selectOnly(target.id);
2290
+ focusSection(target.id);
2291
+ };
2292
+ const onDeleteKey = (id) => {
2293
+ const index = sections.findIndex((section) => section.id === id);
2294
+ const neighbor = sections[index + 1] ?? sections[index - 1];
2295
+ requestDelete(id, () => {
2296
+ if (neighbor) focusSection(neighbor.id);
2297
+ });
2298
+ };
2299
+ useEffect(() => {
2300
+ if (selectedId === null) return;
2301
+ const frame = requestAnimationFrame(() => {
2302
+ sectionNodes.get(selectedId)?.scrollIntoView?.({ block: "nearest" });
2303
+ });
2304
+ return () => cancelAnimationFrame(frame);
2305
+ }, [
2306
+ sectionNodes,
2307
+ selectedId,
2308
+ selectedIndex
2309
+ ]);
1724
2310
  const onChange = (next) => {
1725
2311
  if (!next.id) return;
1726
2312
  patch(next);
1727
2313
  };
1728
2314
  const selectedCode = selectedItem?.code;
2315
+ const selectedSectionId = selectedItem?.id;
1729
2316
  const { fields, updatedCode, parseError } = useMemo(() => {
1730
2317
  if (!selectedCode) return {
1731
2318
  fields: [],
@@ -1733,9 +2320,9 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1733
2320
  parseError: null
1734
2321
  };
1735
2322
  try {
1736
- const updated = fillIds(selectedCode);
2323
+ const updated = fillIdsFrom(selectedCode, selectedSectionId ?? "");
1737
2324
  return {
1738
- fields: extract(updated).filter((node) => node.tagName !== "section"),
2325
+ fields: extract(updated),
1739
2326
  updatedCode: updated !== selectedCode ? updated : selectedCode,
1740
2327
  parseError: null
1741
2328
  };
@@ -1747,12 +2334,17 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1747
2334
  parseError: { error: e }
1748
2335
  };
1749
2336
  }
1750
- }, [selectedCode]);
1751
- const reportError = onEditError ?? toastEditError;
1752
- const reportErrorRef = useRef(reportError);
2337
+ }, [selectedCode, selectedSectionId]);
1753
2338
  useEffect(() => {
1754
- reportErrorRef.current = reportError;
1755
- });
2339
+ if (missingContainer !== null) reportErrorRef.current({
2340
+ type: "parse",
2341
+ target: "document",
2342
+ reason: "container-not-found",
2343
+ containerId: missingContainer,
2344
+ title: `No #${missingContainer} element in the document`,
2345
+ description: `Sections are the <section> elements inside the element with id="${missingContainer}". Start from createDocument(), or set containerId to match your document.`
2346
+ });
2347
+ }, [missingContainer]);
1756
2348
  useEffect(() => {
1757
2349
  if (parseError) reportErrorRef.current({
1758
2350
  type: "parse",
@@ -1762,9 +2354,21 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1762
2354
  description: "Check the console for details."
1763
2355
  });
1764
2356
  }, [parseError]);
1765
- const onFieldChange = ({ id, label, property, value: fieldValue }) => {
1766
- const result = update(updatedCode, id, label, fieldValue, property);
2357
+ const commitChanges = (changes) => {
2358
+ if (changes.length === 0) return;
2359
+ if (stale && problem?.reason === "parse-error") {
2360
+ reportError(blockedEditError(problem.error));
2361
+ return;
2362
+ }
2363
+ const committed = selectedItem && getCommittedSection(selectedItem.id)?.code;
2364
+ const result = updateAll(committed && committed !== selectedCode ? committed : updatedCode, changes.map(({ id, label, property, value: changeValue }) => ({
2365
+ dataId: id,
2366
+ label,
2367
+ property,
2368
+ value: changeValue
2369
+ })));
1767
2370
  if (!result.success) {
2371
+ const { id, label, property } = changes[result.index];
1768
2372
  reportError({
1769
2373
  type: "update",
1770
2374
  id,
@@ -1780,6 +2384,7 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1780
2384
  code: result.code
1781
2385
  });
1782
2386
  };
2387
+ const onFieldChange = (change) => commitChanges([change]);
1783
2388
  const bindingFields = useMemo(() => fields.flatMap((node) => resolvePanelBindings(node)?.bindings ?? []), [fields]);
1784
2389
  const bindings = withPanelCommit(bindingFields, onFieldChange);
1785
2390
  useEffect(() => {
@@ -1801,13 +2406,35 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1801
2406
  canMoveUp: selectedIndex > 0,
1802
2407
  canMoveDown: selectedIndex >= 0 && selectedIndex < sections.length - 1,
1803
2408
  bindings,
1804
- onNodeChange: onFieldChange
2409
+ onNodeChange: onFieldChange,
2410
+ onNodesChange: commitChanges,
2411
+ readOnly: stale
1805
2412
  };
1806
- const canvas = /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Droppable, {
2413
+ const canvas = /* @__PURE__ */ jsxs(Fragment, { children: [stale && /* @__PURE__ */ jsx("div", {
2414
+ role: "status",
2415
+ className: cn("sticky top-0 z-70", "border-b border-amber-200 bg-amber-50 px-3 py-2", "text-sm text-amber-900"),
2416
+ children: "Showing the last version that parsed. Fix the syntax error in the code to edit here again."
2417
+ }), /* @__PURE__ */ jsx(Droppable, {
1807
2418
  className: cn(!sections.length && "h-full"),
1808
2419
  children: !sections.length ? /* @__PURE__ */ jsx("div", {
1809
2420
  className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
1810
- children: /* @__PURE__ */ jsxs(Space, {
2421
+ children: problem?.reason === "parse-error" ? /* @__PURE__ */ jsxs(Space, {
2422
+ orientation: "vertical",
2423
+ align: "center",
2424
+ children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: "The document has a syntax error" }), /* @__PURE__ */ jsx(Typography.Text, { children: "Fix it in the code to see its sections" })]
2425
+ }) : missingContainer !== null ? /* @__PURE__ */ jsxs(Space, {
2426
+ orientation: "vertical",
2427
+ align: "center",
2428
+ children: [/* @__PURE__ */ jsxs(Typography.Paragraph, { children: [
2429
+ "No #",
2430
+ missingContainer,
2431
+ " element in the document"
2432
+ ] }), /* @__PURE__ */ jsxs(Typography.Text, { children: [
2433
+ "Sections go inside the element with id=\"",
2434
+ missingContainer,
2435
+ "\""
2436
+ ] })]
2437
+ }) : /* @__PURE__ */ jsxs(Space, {
1811
2438
  orientation: "vertical",
1812
2439
  align: "center",
1813
2440
  children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: "No sections available" }), /* @__PURE__ */ jsx(Typography.Text, { children: isMobile ? "Tap a component to add it" : "Drag a component from the left to add it" })]
@@ -1822,6 +2449,13 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1822
2449
  onClick: () => onSelect(section.id),
1823
2450
  onDelete,
1824
2451
  onCopy,
2452
+ nodeRef: sectionNodes.register(section.id),
2453
+ onNavigate: (to) => onNavigate(section.id, to),
2454
+ onDeleteKey: () => onDeleteKey(section.id),
2455
+ inspecting,
2456
+ onInspectMove,
2457
+ onInspectPick: (...args) => onInspectPick(section.id, ...args),
2458
+ onInspectLeave: () => setHighlight(null),
1825
2459
  children: /* @__PURE__ */ jsx(renderer_default, {
1826
2460
  preview: previews[index],
1827
2461
  sectionId: section.id,
@@ -1836,12 +2470,16 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1836
2470
  })
1837
2471
  }, section.id))
1838
2472
  })
1839
- }) });
2473
+ })] });
1840
2474
  return /* @__PURE__ */ jsx(SectionFallbackContext.Provider, {
1841
2475
  value: renderSectionFallback,
1842
2476
  children: /* @__PURE__ */ jsxs(DndContext, {
1843
2477
  sensors,
1844
2478
  collisionDetection: closestCenter,
2479
+ accessibility: {
2480
+ announcements,
2481
+ screenReaderInstructions: SCREEN_READER_INSTRUCTIONS
2482
+ },
1845
2483
  modifiers: [conditionalModifiers],
1846
2484
  onDragStart,
1847
2485
  onDragOver,
@@ -1849,12 +2487,12 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1849
2487
  children: [/* @__PURE__ */ jsx("div", {
1850
2488
  className: cn("relative flex h-full w-full", className),
1851
2489
  ...restProps,
1852
- children: /* @__PURE__ */ jsx(DndEditOptionsContext.Provider, {
2490
+ children: /* @__PURE__ */ jsxs(DndEditOptionsContext.Provider, {
1853
2491
  value: {
1854
2492
  renderField,
1855
2493
  reportError
1856
2494
  },
1857
- children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
2495
+ children: [/* @__PURE__ */ jsx(DndRegionContext.Provider, {
1858
2496
  value: {
1859
2497
  palette,
1860
2498
  panel,
@@ -1863,10 +2501,14 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1863
2501
  selectedId,
1864
2502
  clearSelection,
1865
2503
  paletteOpen: mobilePaletteOpen,
1866
- setPaletteOpen: setMobilePaletteOpen
2504
+ setPaletteOpen: setMobilePaletteOpen,
2505
+ documentError: problem?.reason ?? null
1867
2506
  },
1868
- children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
1869
- })
2507
+ children: /* @__PURE__ */ jsx(DndInspectorContext.Provider, {
2508
+ value: inspector,
2509
+ children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
2510
+ })
2511
+ }), /* @__PURE__ */ jsx(InspectorHighlight, { rect: inspecting ? highlight : null })]
1870
2512
  })
1871
2513
  }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
1872
2514
  sections,
@@ -1876,7 +2518,8 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
1876
2518
  modules,
1877
2519
  frame,
1878
2520
  dynamicTailwind,
1879
- ...props
2521
+ ...props,
2522
+ containerId
1880
2523
  }
1881
2524
  }) })]
1882
2525
  })
@@ -1891,7 +2534,11 @@ Dnd.Palette = Palette;
1891
2534
  Dnd.Canvas = Canvas;
1892
2535
  Dnd.Panel = Panel;
1893
2536
  Dnd.Layout = Layout;
2537
+ /** @deprecated Renamed to `useDndItems`. */
2538
+ const useItemsEditor = useDndItems;
2539
+ /** @deprecated Renamed to `useDndChildren`. */
2540
+ const useChildrenEditor = useDndChildren;
1894
2541
  //#endregion
1895
- export { Panel as a, useChildrenEditor as c, useDndPanel as d, DraggableItem as f, Palette as i, useDndLayout as l, Canvas as n, Field as o, Layout as r, useItemsEditor as s, Dnd as t, useDndPalette as u };
2542
+ export { useDndPanel as _, Layout as a, Field as c, isStructuralFieldKind as d, useDndChildren as f, useDndPalette as g, useDndLayout as h, Canvas as i, useDndItems as l, withPanelCommit as m, useChildrenEditor as n, Palette as o, resolvePanelBindings as p, useItemsEditor as r, Panel as s, Dnd as t, getFieldKind as u, DraggableItem as v, useDndInspector as y };
1896
2543
 
1897
- //# sourceMappingURL=dnd-CMrXfD8O.js.map
2544
+ //# sourceMappingURL=dnd-Z1KS16Fy.js.map