@jbpark/live-editor 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/ast-CZwpVrJv.js +851 -0
  2. package/dist/ast-CZwpVrJv.js.map +1 -0
  3. package/dist/{boundary-BTP1nfG6.js → boundary-C90q3YsT.js} +8 -7
  4. package/dist/boundary-C90q3YsT.js.map +1 -0
  5. package/dist/{context-649REFsj.js → context-Y-M9fViD.js} +4 -8
  6. package/dist/context-Y-M9fViD.js.map +1 -0
  7. package/dist/{core-D3q5rpuz.js → core-Co8Nlq16.js} +2 -2
  8. package/dist/core-Co8Nlq16.js.map +1 -0
  9. package/dist/dnd/index.d.ts +2 -2
  10. package/dist/dnd/index.js +2 -2
  11. package/dist/{dnd-DTT37EbA.js → dnd-ClnGRNaY.js} +386 -324
  12. package/dist/dnd-ClnGRNaY.js.map +1 -0
  13. package/dist/editor/index.d.ts +1 -1
  14. package/dist/editor/index.js +2 -2
  15. package/dist/{editor-LzqHe_-F.js → editor-COKGtmfE.js} +3 -3
  16. package/dist/{editor-LzqHe_-F.js.map → editor-COKGtmfE.js.map} +1 -1
  17. package/dist/error/index.d.ts +1 -1
  18. package/dist/error/index.js +2 -2
  19. package/dist/{error-CO0EbMlA.js → error-o0SBwtTv.js} +2 -2
  20. package/dist/{error-CO0EbMlA.js.map → error-o0SBwtTv.js.map} +1 -1
  21. package/dist/{document-C6bNHl7l.js → extract-abHYmNsu.js} +927 -42
  22. package/dist/extract-abHYmNsu.js.map +1 -0
  23. package/dist/{index-Ba8jGXSM.d.ts → index-BQdZ6LrR.d.ts} +5 -1
  24. package/dist/{index-DxbG789f.d.ts → index-B_p74BRM.d.ts} +29 -14
  25. package/dist/{index-CCynrS6c.d.ts → index-DNNj_0ba.d.ts} +3 -2
  26. package/dist/{index-lgHdCda5.d.ts → index-DQkL37eL.d.ts} +10 -2
  27. package/dist/{index-DswWDEXZ.d.ts → index-Doh4VTXr.d.ts} +65 -35
  28. package/dist/index.d.ts +7 -7
  29. package/dist/index.js +5 -5
  30. package/dist/index.js.map +1 -1
  31. package/dist/preview/index.d.ts +2 -2
  32. package/dist/preview/index.js +3 -2
  33. package/dist/{preview-CWT0lgwq.js → preview-DKyKKcu7.js} +4 -4
  34. package/dist/{preview-CWT0lgwq.js.map → preview-DKyKKcu7.js.map} +1 -1
  35. package/dist/provider/index.js +1 -1
  36. package/dist/{use-dynamic-tailwind-DAhKGsY4.js → use-dynamic-tailwind-BF8TQ3Yy.js} +83 -27
  37. package/dist/use-dynamic-tailwind-BF8TQ3Yy.js.map +1 -0
  38. package/dist/utils/ast/index.d.ts +2 -2
  39. package/dist/utils/ast/index.js +3 -3
  40. package/dist/utils/index.d.ts +4 -7
  41. package/dist/utils/index.js +3 -3
  42. package/dist/{utils-WyxPYZGh.js → utils-BQ7WL7Jh.js} +33 -16
  43. package/dist/{utils-WyxPYZGh.js.map → utils-BQ7WL7Jh.js.map} +1 -1
  44. package/package.json +3 -1
  45. package/dist/ast-Bx2Avo4d.js +0 -1530
  46. package/dist/ast-Bx2Avo4d.js.map +0 -1
  47. package/dist/boundary-BTP1nfG6.js.map +0 -1
  48. package/dist/context-649REFsj.js.map +0 -1
  49. package/dist/core-D3q5rpuz.js.map +0 -1
  50. package/dist/dnd-DTT37EbA.js.map +0 -1
  51. package/dist/document-C6bNHl7l.js.map +0 -1
  52. package/dist/use-dynamic-tailwind-DAhKGsY4.js.map +0 -1
@@ -1,18 +1,18 @@
1
- import { C as __toESM, b as DRAGGABLE_ITEMS, f as nanoid, g as BINDING_PROP, m as require_lib, n as createSectionPreviewCache, r as fillSectionIds } from "./document-C6bNHl7l.js";
2
- import { d as replaceSections, o as extractSections, r as cn, s as generateSection, u as preloadScripts } from "./utils-WyxPYZGh.js";
1
+ import { D as fillSectionIds, E as createSectionPreviewCache, I as nanoid, K as __toESM, L as require_lib, U as DRAGGABLE_ITEMS, _ as parseArrayExpression, a as findEditableChildren, c as parseBinding, d as canLosslesslyEvaluateSource, m as extractObjectProperties, n as extract, o as getCurrentValue, p as extractNodeValue, s as getStructuredValue, v as parseValue, z as BINDING_PROP } from "./extract-abHYmNsu.js";
2
+ import { c as generateSection, d as preloadScripts, i as cn, p as replaceSections, s as extractSections } from "./utils-BQ7WL7Jh.js";
3
3
  import { i as usePreview } from "./states-Ci1AvoQ9.js";
4
- import { A as parseValue, D as extractObjectProperties, E as extractNodeValue, S as parseBinding, _ as updateArrayItemValue, b as getCurrentValue, d as duplicateArrayItems, f as moveArrayItem, g as updateArrayItemProperty, h as removeArrayItems, i as replaceIds, k as parseArrayExpression, l as extract, o as update, p as moveArrayItems, r as fillIds, s as getChildrenSignatures, t as validateBindingValue, u as appendArrayItem, v as moveSelectedIndices, x as getStructuredValue, y as findEditableChildren } from "./ast-Bx2Avo4d.js";
5
- import { n as useCompiledModule, r as Frame, t as useDynamicTailwind } from "./use-dynamic-tailwind-DAhKGsY4.js";
6
- import { t as Core } from "./core-D3q5rpuz.js";
7
- import { n as Error$1, t as ErrorBoundary } from "./boundary-BTP1nfG6.js";
4
+ import { _ as moveSelectedIndices, c as appendArrayItem, d as moveArrayItems, g as canStructurallyEditArray, h as updateArrayItemValue, i as replaceIds, l as duplicateArrayItems, m as updateArrayItemProperty, o as update, p as removeArrayItems, r as fillIds, s as getChildrenSignatures, t as validateBindingValue, u as moveArrayItem } from "./ast-CZwpVrJv.js";
5
+ import { i as Frame, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-BF8TQ3Yy.js";
6
+ import { t as Core } from "./core-Co8Nlq16.js";
7
+ import { n as Error$1, t as ErrorBoundary } from "./boundary-C90q3YsT.js";
8
8
  import { Children, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
9
- import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography, Upload } from "@jbpark/ui-kit";
10
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
10
  import { DndContext, DragOverlay, PointerSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
12
11
  import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
13
12
  import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
13
+ import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography } from "@jbpark/ui-kit";
14
14
  import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
15
- import { AlertCircle, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bookmark, Calendar, Camera, Check, ChevronDown, ChevronRight, ChevronUp, Clock, Copy, Download, Edit, Eye, Filter, Heart, Home, Image, Info, LayoutGrid, Link, Mail, Map as Map$1, Menu, MessageCircle, Phone, Plus, Search, Settings, Share, ShoppingCart, Star, Trash, Upload as Upload$1, User, X } from "lucide-react";
15
+ import { ArrowDown, ArrowUp, ChevronDown, ChevronUp, Copy, LayoutGrid, Plus, Trash, X } from "lucide-react";
16
16
  import { CSS } from "@dnd-kit/utilities";
17
17
  //#region src/components/dnd/droppable.tsx
18
18
  var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
@@ -36,6 +36,13 @@ const Droppable = ({ children, className }) => {
36
36
  });
37
37
  };
38
38
  //#endregion
39
+ //#region src/components/dnd/edit-options.ts
40
+ const toastEditError = (error) => {
41
+ Toast.error(error.title, error.description ? { description: error.description } : void 0);
42
+ };
43
+ const DndEditOptionsContext = createContext({ reportError: toastEditError });
44
+ const useDndEditOptions = () => useContext(DndEditOptionsContext);
45
+ //#endregion
39
46
  //#region src/components/dnd/draggable.tsx
40
47
  const DraggableItem = ({ item, children }) => {
41
48
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
@@ -78,8 +85,73 @@ const useDndPalette = () => useDndRegions("useDndPalette()").palette;
78
85
  const useDndPanel = () => useDndRegions("useDndPanel()").panel;
79
86
  const useDndLayout = () => useDndRegions("useDndLayout()");
80
87
  //#endregion
88
+ //#region src/components/dnd/panel-binding.ts
89
+ const readAttribute = (node, name) => node.dataAttributes.find((attribute) => attribute.name === name)?.value;
90
+ const canEditBindingValue = (node, binding) => {
91
+ 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;
92
+ const attribute = node.attributes.find((candidate) => candidate.name === binding.property);
93
+ return attribute?.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
94
+ };
95
+ const toBindingFields = (binding) => ({
96
+ label: binding.label,
97
+ property: binding.property,
98
+ type: binding.type,
99
+ widget: binding.widget,
100
+ options: binding.options,
101
+ render: binding.render,
102
+ min: binding.min,
103
+ max: binding.max,
104
+ pattern: binding.pattern,
105
+ required: binding.required,
106
+ meta: binding.meta
107
+ });
108
+ const resolveRenderEntry = (render, key) => {
109
+ const entry = render?.[key];
110
+ if (entry && "type" in entry) {
111
+ const leaf = entry;
112
+ return {
113
+ ...leaf,
114
+ label: leaf.label ?? key,
115
+ property: leaf.property ?? key
116
+ };
117
+ }
118
+ return {
119
+ label: key,
120
+ property: key,
121
+ render: entry
122
+ };
123
+ };
124
+ const toPanelBindingData = (node, id, binding) => ({
125
+ ...toBindingFields(binding),
126
+ id,
127
+ value: getStructuredValue(node, binding.property, binding.type),
128
+ rawValue: getCurrentValue(node, binding.property),
129
+ ...!canEditBindingValue(node, binding) && { canEditValue: false }
130
+ });
131
+ const resolvePanelBindings = (node) => {
132
+ const id = readAttribute(node, "data-id");
133
+ const bindingAttr = readAttribute(node, "data-binding");
134
+ if (!id || !bindingAttr) return null;
135
+ const parsed = node.bindings ?? parseBinding(bindingAttr);
136
+ if (!parsed.length) return null;
137
+ return {
138
+ id,
139
+ tagName: node.tagName || "element",
140
+ bindings: parsed.map((binding) => toPanelBindingData(node, id, binding))
141
+ };
142
+ };
143
+ const withPanelCommit = (bindings, commit) => bindings.map((binding) => ({
144
+ ...binding,
145
+ onChange: (value) => commit?.({
146
+ id: binding.id,
147
+ label: binding.label,
148
+ property: binding.property,
149
+ value
150
+ })
151
+ }));
152
+ //#endregion
81
153
  //#region src/components/dnd/panel/bulk-actions-bar.tsx
82
- const BulkActionsBar = ({ count, onDuplicate, onMoveUp, onMoveDown, onDelete, onClear }) => {
154
+ const BulkActionsBar = ({ count, disabled = false, onDuplicate, onMoveUp, onMoveDown, onDelete, onClear }) => {
83
155
  if (count === 0) return null;
84
156
  return /* @__PURE__ */ jsxs("div", {
85
157
  className: "flex items-center justify-between rounded border\n border-blue-200 bg-blue-50 p-2",
@@ -93,18 +165,21 @@ const BulkActionsBar = ({ count, onDuplicate, onMoveUp, onMoveDown, onDelete, on
93
165
  size: "small",
94
166
  icon: /* @__PURE__ */ jsx(Copy, {}),
95
167
  title: "Duplicate selected",
168
+ disabled,
96
169
  onClick: onDuplicate
97
170
  }),
98
171
  /* @__PURE__ */ jsx(Button, {
99
172
  size: "small",
100
173
  icon: /* @__PURE__ */ jsx(ArrowUp, {}),
101
174
  title: "Move selected up",
175
+ disabled,
102
176
  onClick: onMoveUp
103
177
  }),
104
178
  /* @__PURE__ */ jsx(Button, {
105
179
  size: "small",
106
180
  icon: /* @__PURE__ */ jsx(ArrowDown, {}),
107
181
  title: "Move selected down",
182
+ disabled,
108
183
  onClick: onMoveDown
109
184
  }),
110
185
  /* @__PURE__ */ jsx(Button, {
@@ -112,6 +187,7 @@ const BulkActionsBar = ({ count, onDuplicate, onMoveUp, onMoveDown, onDelete, on
112
187
  size: "small",
113
188
  icon: /* @__PURE__ */ jsx(X, {}),
114
189
  title: "Delete selected",
190
+ disabled,
115
191
  onClick: onDelete
116
192
  }),
117
193
  /* @__PURE__ */ jsx(Button, {
@@ -126,58 +202,29 @@ const BulkActionsBar = ({ count, onDuplicate, onMoveUp, onMoveDown, onDelete, on
126
202
  //#endregion
127
203
  //#region src/components/dnd/panel/node.tsx
128
204
  const Node = ({ data, onChange }) => {
129
- const idAttr = data.dataAttributes.find((a) => a.name === "data-id");
130
- const bindingAttr = data.dataAttributes.find((a) => a.name === "data-binding");
131
- if (!idAttr?.value || !bindingAttr?.value) return null;
132
- const dataId = idAttr.value;
133
- const bindings = data.bindings || parseBinding(bindingAttr.value);
134
- if (!bindings.length) return null;
205
+ const source = resolvePanelBindings(data);
206
+ if (!source) return null;
135
207
  return /* @__PURE__ */ jsx("div", {
136
208
  className: "space-y-2 rounded",
137
209
  children: /* @__PURE__ */ jsx("div", {
138
210
  className: "space-y-1",
139
- children: bindings.map((binding) => {
140
- const currentValue = getCurrentValue(data, binding.property);
141
- const structuredValue = getStructuredValue(data, binding.property, binding.type);
142
- return /* @__PURE__ */ jsxs("div", {
143
- className: "space-y-1",
144
- children: [/* @__PURE__ */ jsxs("label", {
145
- className: "block text-xs font-semibold text-gray-700",
146
- children: [binding.label, /* @__PURE__ */ jsxs("span", {
147
- className: "ml-1 text-gray-400",
148
- children: [
149
- "(",
150
- binding.property,
151
- ")"
152
- ]
153
- })]
154
- }), /* @__PURE__ */ jsx(Field, {
155
- binding: {
156
- id: dataId,
157
- label: binding.label,
158
- property: binding.property,
159
- type: binding.type,
160
- widget: binding.widget,
161
- options: binding.options,
162
- render: binding.render,
163
- min: binding.min,
164
- max: binding.max,
165
- pattern: binding.pattern,
166
- required: binding.required,
167
- meta: binding.meta,
168
- value: structuredValue,
169
- rawValue: currentValue,
170
- onChange: (next) => onChange?.({
171
- id: dataId,
172
- label: binding.label,
173
- property: binding.property,
174
- value: next
175
- })
176
- },
177
- onNodeChange: onChange
211
+ children: withPanelCommit(source.bindings, onChange).map((binding) => /* @__PURE__ */ jsxs("div", {
212
+ className: "space-y-1",
213
+ children: [/* @__PURE__ */ jsxs("label", {
214
+ className: "block text-xs font-semibold text-gray-700",
215
+ children: [binding.label, /* @__PURE__ */ jsxs("span", {
216
+ className: "ml-1 text-gray-400",
217
+ children: [
218
+ "(",
219
+ binding.property,
220
+ ")"
221
+ ]
178
222
  })]
179
- }, `${binding.property}-${binding.label}`);
180
- })
223
+ }), /* @__PURE__ */ jsx(Field, {
224
+ binding,
225
+ onNodeChange: onChange
226
+ })]
227
+ }, `${binding.id}-${binding.property}`))
181
228
  })
182
229
  });
183
230
  };
@@ -356,63 +403,7 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
356
403
  });
357
404
  };
358
405
  //#endregion
359
- //#region src/components/dnd/panel/icon-map.tsx
360
- const ICON_MAP = {
361
- AlertCircle,
362
- ArrowDown,
363
- ArrowLeft,
364
- ArrowRight,
365
- ArrowUp,
366
- Bell,
367
- Bookmark,
368
- Calendar,
369
- Camera,
370
- Check,
371
- ChevronDown,
372
- ChevronRight,
373
- Clock,
374
- Download,
375
- Edit,
376
- Eye,
377
- Filter,
378
- Heart,
379
- Home,
380
- Image,
381
- Info,
382
- Link,
383
- Mail,
384
- Map: Map$1,
385
- Menu,
386
- MessageCircle,
387
- Phone,
388
- Plus,
389
- Search,
390
- Settings,
391
- Share,
392
- ShoppingCart,
393
- Star,
394
- Trash,
395
- Upload: Upload$1,
396
- User,
397
- X
398
- };
399
- const ICON_OPTIONS = Object.entries(ICON_MAP).map(([name, Icon]) => ({
400
- label: /* @__PURE__ */ jsxs("span", {
401
- className: "flex items-center gap-2",
402
- children: [/* @__PURE__ */ jsx(Icon, { size: 14 }), name]
403
- }),
404
- value: name
405
- }));
406
- //#endregion
407
406
  //#region src/components/dnd/panel/use-items-editor.ts
408
- const resolveLeaf = (render, key) => {
409
- const leaf = render?.[key];
410
- return leaf && "type" in leaf ? leaf : null;
411
- };
412
- const resolveMap = (render, key) => {
413
- const leaf = render?.[key];
414
- return leaf && !("type" in leaf) ? leaf : void 0;
415
- };
416
407
  const bindingsInJSX = (source) => {
417
408
  const nodes = extract(source);
418
409
  const container = nodes.find((n) => n.bindings?.some((b) => b.property === "children"));
@@ -512,8 +503,19 @@ const removeIds = (ids, indices) => {
512
503
  };
513
504
  const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
514
505
  const { objectItems, primitiveItems, parseError } = useMemo(() => parseSource(value), [value]);
506
+ const canEditStructure = useMemo(() => canStructurallyEditArray(value), [value]);
507
+ const { reportError } = useDndEditOptions();
508
+ const reportErrorRef = useRef(reportError);
509
+ useEffect(() => {
510
+ reportErrorRef.current = reportError;
511
+ });
515
512
  useEffect(() => {
516
- if (parseError) Toast.error("Failed to parse items", { description: "Check the console for details." });
513
+ if (parseError) reportErrorRef.current({
514
+ type: "parse",
515
+ target: "items",
516
+ title: "Failed to parse items",
517
+ description: "Check the console for details."
518
+ });
517
519
  }, [parseError]);
518
520
  const kind = primitiveItems.length > 0 && objectItems.length === 0 ? "primitive" : "object";
519
521
  const isPrimitive = kind === "primitive";
@@ -528,7 +530,11 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
528
530
  const selection = useMultiSelect(isPrimitive ? primitiveItems.length : objectItems.length);
529
531
  const commit = (next, nextIds) => {
530
532
  if (next === null) {
531
- Toast.error("Failed to update this item", { description: "The source was preserved. Structural edits require a dense array without spreads; use the code editor for unsupported syntax." });
533
+ reportError({
534
+ type: "items",
535
+ title: "Failed to update this item",
536
+ description: "The source was preserved. Structural edits require a dense array without spreads; use the code editor for unsupported syntax."
537
+ });
532
538
  return false;
533
539
  }
534
540
  const parsed = parseSource(next);
@@ -557,70 +563,50 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
557
563
  label: `item-${item.index}`,
558
564
  property: item.type,
559
565
  value: item.value ?? "",
560
- rawValue: String(item.value ?? ""),
566
+ rawValue: item.type === "unknown" ? item.source : String(item.value ?? ""),
567
+ canEditValue: canLosslesslyEvaluateSource(item.source),
561
568
  onChange: (next) => commit(updateArrayItemValue(value, item.elementIndex, next))
562
569
  }
563
570
  })) : objectItems.map((item) => {
564
571
  const id = identity.ids[item.index] ?? item.id;
565
572
  const properties = Object.entries(item.editableProperties).map(([key, prop]) => {
566
- const leaf = resolveLeaf(render, key);
573
+ const binding = resolveRenderEntry(render, key);
574
+ const propertySource = value.slice(prop.astNode.start, prop.astNode.end);
567
575
  return {
576
+ ...toBindingFields(binding),
568
577
  id: `item-${id}-${key}`,
569
- label: key,
570
- property: leaf ? leaf.property ?? leaf.type : key,
571
- type: leaf?.type,
572
- render: leaf ? leaf.render : resolveMap(render, key),
578
+ meta: {
579
+ ...binding.meta,
580
+ valueType: prop.type
581
+ },
573
582
  value: parseValue(String(prop.value)),
574
- rawValue: String(prop.value),
575
- meta: { valueType: prop.type },
583
+ rawValue: prop.type === "unknown" ? propertySource : String(prop.value),
584
+ canEditValue: canLosslesslyEvaluateSource(propertySource),
576
585
  onChange: (next) => updateProperty(item.elementIndex, key, next)
577
586
  };
578
587
  });
579
588
  const nested = [...Object.entries(item.jsxBindings).map(([property, nodes]) => ({
580
589
  property,
581
590
  elements: nodes.flatMap((node) => {
582
- const id = node.dataAttributes.find((a) => a.name === "data-id")?.value;
583
- const attr = node.dataAttributes.find((a) => a.name === "data-binding")?.value;
584
- if (!id || !attr) return [];
585
- const parsed = node.bindings ?? parseBinding(attr);
586
- if (!parsed.length) return [];
591
+ const source = resolvePanelBindings(node);
592
+ if (!source) return [];
587
593
  return [{
588
- id,
589
- tagName: node.tagName || "element",
590
- bindings: parsed.map((binding) => ({
591
- id,
592
- label: binding.label,
593
- property: binding.property,
594
- type: binding.type,
595
- widget: binding.widget,
596
- options: binding.options,
597
- render: binding.render,
598
- min: binding.min,
599
- max: binding.max,
600
- pattern: binding.pattern,
601
- required: binding.required,
602
- meta: binding.meta,
603
- value: getStructuredValue(node, binding.property, binding.type),
604
- rawValue: getCurrentValue(node, binding.property),
605
- onChange: (next) => onNodeChange?.({
606
- id,
607
- label: binding.label,
608
- property: binding.property,
609
- value: next
610
- })
611
- }))
594
+ id: source.id,
595
+ tagName: source.tagName,
596
+ bindings: withPanelCommit(source.bindings, onNodeChange)
612
597
  }];
613
598
  })
614
599
  })), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
615
600
  property,
616
601
  elements: [],
617
602
  fallback: {
618
- id: `item-${item.id}-${property}-jsx`,
603
+ id: `item-${id}-${property}-jsx`,
619
604
  label: property,
620
605
  property,
621
606
  type: "jsx",
622
607
  value: code,
623
608
  rawValue: code,
609
+ canEditValue: true,
624
610
  onChange: (next) => updateProperty(item.elementIndex, property, next)
625
611
  }
626
612
  }))];
@@ -670,6 +656,7 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
670
656
  if (commit(removeArrayItems(value, elementIndicesOf(selection.selected), kind), removeIds(identity.ids, selection.selected))) selection.clear();
671
657
  }
672
658
  },
659
+ canEditStructure,
673
660
  parseError
674
661
  };
675
662
  };
@@ -732,7 +719,7 @@ const NestedGroup = ({ group, onNodeChange }) => {
732
719
  });
733
720
  };
734
721
  const Items = ({ value, render, onChange, onChildChange }) => {
735
- const { kind, items, selection, actions, parseError } = useItemsEditor(value, {
722
+ const { kind, items, selection, actions, canEditStructure, parseError } = useItemsEditor(value, {
736
723
  render,
737
724
  onChange,
738
725
  onNodeChange: onChildChange
@@ -746,7 +733,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
746
733
  items.length,
747
734
  ")"
748
735
  ]
749
- }), items.length > 0 && /* @__PURE__ */ jsx(Button, {
736
+ }), items.length > 0 && canEditStructure && /* @__PURE__ */ jsx(Button, {
750
737
  size: "small",
751
738
  icon: /* @__PURE__ */ jsx(Plus, {}),
752
739
  variant: "solid",
@@ -771,6 +758,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
771
758
  });
772
759
  const bulkBar = /* @__PURE__ */ jsx(BulkActionsBar, {
773
760
  count: selection.selected.size,
761
+ disabled: !canEditStructure,
774
762
  onDuplicate: actions.duplicateSelected,
775
763
  onMoveUp: () => actions.moveSelected("up"),
776
764
  onMoveDown: () => actions.moveSelected("down"),
@@ -783,20 +771,20 @@ const Items = ({ value, render, onChange, onChildChange }) => {
783
771
  /* @__PURE__ */ jsx(Button, {
784
772
  size: "small",
785
773
  icon: /* @__PURE__ */ jsx(ArrowUp, {}),
786
- disabled: item.index === 0,
774
+ disabled: !canEditStructure || item.index === 0,
787
775
  onClick: () => actions.move(item.elementIndex, item.index - 1)
788
776
  }),
789
777
  /* @__PURE__ */ jsx(Button, {
790
778
  size: "small",
791
779
  icon: /* @__PURE__ */ jsx(ArrowDown, {}),
792
- disabled: item.index === items.length - 1,
780
+ disabled: !canEditStructure || item.index === items.length - 1,
793
781
  onClick: () => actions.move(item.elementIndex, item.index + 1)
794
782
  }),
795
783
  /* @__PURE__ */ jsx(Button, {
796
784
  danger: true,
797
785
  size: "small",
798
786
  icon: /* @__PURE__ */ jsx(X, {}),
799
- disabled: items.length <= 1,
787
+ disabled: !canEditStructure || items.length <= 1,
800
788
  onClick: () => actions.remove(item.elementIndex)
801
789
  })
802
790
  ]
@@ -806,15 +794,20 @@ const Items = ({ value, render, onChange, onChildChange }) => {
806
794
  children: [
807
795
  header,
808
796
  bulkBar,
797
+ !canEditStructure && /* @__PURE__ */ jsx("div", {
798
+ className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
799
+ 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."
800
+ }),
809
801
  items.map((item) => /* @__PURE__ */ jsxs("div", {
810
802
  className: "space-y-2 rounded border border-gray-100 bg-gray-50 p-2",
811
803
  children: [/* @__PURE__ */ jsxs("div", {
812
804
  className: "flex items-center justify-between space-x-1",
813
805
  children: [/* @__PURE__ */ jsx("div", {
814
- onClick: (e) => selection.toggle(item.index, e.shiftKey),
806
+ onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
815
807
  className: "inline-flex",
816
808
  children: /* @__PURE__ */ jsx(Checkbox, {
817
809
  checked: selection.isSelected(item.index),
810
+ disabled: !canEditStructure,
818
811
  onChange: () => {}
819
812
  })
820
813
  }), itemControls(item)]
@@ -830,6 +823,10 @@ const Items = ({ value, render, onChange, onChildChange }) => {
830
823
  children: [
831
824
  header,
832
825
  bulkBar,
826
+ !canEditStructure && /* @__PURE__ */ jsx("div", {
827
+ className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
828
+ 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."
829
+ }),
833
830
  items.map((item) => /* @__PURE__ */ jsxs("div", {
834
831
  className: "space-y-3 rounded border bg-gray-50 p-3",
835
832
  children: [
@@ -838,10 +835,11 @@ const Items = ({ value, render, onChange, onChildChange }) => {
838
835
  children: [/* @__PURE__ */ jsxs("div", {
839
836
  className: "flex items-center space-x-2",
840
837
  children: [/* @__PURE__ */ jsx("div", {
841
- onClick: (e) => selection.toggle(item.index, e.shiftKey),
838
+ onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
842
839
  className: "inline-flex",
843
840
  children: /* @__PURE__ */ jsx(Checkbox, {
844
841
  checked: selection.isSelected(item.index),
842
+ disabled: !canEditStructure,
845
843
  onChange: () => {}
846
844
  })
847
845
  }), /* @__PURE__ */ jsxs("div", {
@@ -914,6 +912,36 @@ const RICHTEXT_TOOLBAR = [
914
912
  "orderedList",
915
913
  "link"
916
914
  ];
915
+ const JSXEditorField = ({ value, isHTML, onSave }) => {
916
+ const selectionRef = useRef(null);
917
+ const focusedRef = useRef(false);
918
+ const handleUpdate = useCallback((update) => {
919
+ selectionRef.current = update.state.selection;
920
+ }, []);
921
+ const handleCreateEditor = useCallback((view) => {
922
+ if (selectionRef.current) view.dispatch({ selection: selectionRef.current });
923
+ if (focusedRef.current) view.focus();
924
+ }, []);
925
+ const handleFocus = useCallback(() => {
926
+ focusedRef.current = true;
927
+ }, []);
928
+ const handleBlur = useCallback((event) => {
929
+ if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) focusedRef.current = false;
930
+ }, []);
931
+ return /* @__PURE__ */ jsx("div", {
932
+ onFocusCapture: handleFocus,
933
+ onBlurCapture: handleBlur,
934
+ children: /* @__PURE__ */ jsx(Core, {
935
+ value,
936
+ height: "150px",
937
+ fragment: !isHTML,
938
+ raw: isHTML,
939
+ onSave,
940
+ onUpdate: handleUpdate,
941
+ onCreateEditor: handleCreateEditor
942
+ })
943
+ });
944
+ };
917
945
  const ColorPickerField = ({ value, onChange }) => {
918
946
  const [liveValue, setLiveValue] = useState(value);
919
947
  const [prevValue, setPrevValue] = useState(value);
@@ -946,7 +974,7 @@ const ColorPickerField = ({ value, onChange }) => {
946
974
  }
947
975
  });
948
976
  };
949
- const Field = ({ binding, onNodeChange }) => {
977
+ const BuiltinField = ({ binding, onNodeChange }) => {
950
978
  const { id, value, rawValue, onChange } = binding;
951
979
  const [validationError, setValidationError] = useState(null);
952
980
  const [text, setText] = useState(rawValue);
@@ -955,6 +983,17 @@ const Field = ({ binding, onNodeChange }) => {
955
983
  setPrevRawValue(rawValue);
956
984
  setText(rawValue);
957
985
  }
986
+ if (binding.canEditValue === false) return /* @__PURE__ */ jsxs("div", {
987
+ className: "space-y-1 rounded border border-dashed border-amber-200 p-3\n text-xs text-amber-700",
988
+ children: [
989
+ /* @__PURE__ */ jsx("div", { children: "This expression is preserved but is not editable here." }),
990
+ /* @__PURE__ */ jsx("code", {
991
+ className: "block overflow-x-auto text-amber-800",
992
+ children: rawValue
993
+ }),
994
+ /* @__PURE__ */ jsx("div", { children: "Use the code editor to change it." })
995
+ ]
996
+ });
958
997
  const commitIfChanged = (next) => {
959
998
  if (next !== (typeof next === "string" ? rawValue : value)) onChange(next);
960
999
  };
@@ -971,11 +1010,9 @@ const Field = ({ binding, onNodeChange }) => {
971
1010
  });
972
1011
  if (binding.property === BINDING_PROP.INNER_HTML || binding.type === "jsx") {
973
1012
  const isHTML = binding.property === BINDING_PROP.INNER_HTML;
974
- return /* @__PURE__ */ jsx(Core, {
1013
+ return /* @__PURE__ */ jsx(JSXEditorField, {
975
1014
  value: rawValue,
976
- height: "150px",
977
- fragment: !isHTML,
978
- raw: isHTML,
1015
+ isHTML,
979
1016
  onSave: commitIfChanged
980
1017
  });
981
1018
  }
@@ -988,30 +1025,30 @@ const Field = ({ binding, onNodeChange }) => {
988
1025
  const objectValue = value;
989
1026
  return /* @__PURE__ */ jsx("div", {
990
1027
  className: "space-y-2 rounded border border-gray-200 bg-gray-50 p-2",
991
- children: Object.entries(objectValue).map(([key, val]) => /* @__PURE__ */ jsxs("div", {
992
- className: "space-y-1",
993
- children: [/* @__PURE__ */ jsx("label", {
994
- className: "block text-xs font-medium text-gray-600",
995
- children: key
996
- }), /* @__PURE__ */ jsx(Field, {
997
- binding: {
998
- id,
999
- label: key,
1000
- property: binding.render?.[key] && "type" in binding.render[key] ? binding.render[key].type : key,
1001
- type: binding.render?.[key] && "type" in binding.render[key] ? binding.render[key].type : void 0,
1002
- render: binding.render?.[key] && !("type" in binding.render[key]) ? binding.render[key] : void 0,
1003
- value: val,
1004
- rawValue: typeof val === "object" && val !== null ? JSON.stringify(val) : String(val),
1005
- onChange: (next) => {
1006
- onChange({
1007
- ...objectValue,
1008
- [key]: next
1009
- });
1010
- }
1011
- },
1012
- onNodeChange
1013
- })]
1014
- }, key))
1028
+ children: Object.entries(objectValue).map(([key, val]) => {
1029
+ const nested = resolveRenderEntry(binding.render, key);
1030
+ return /* @__PURE__ */ jsxs("div", {
1031
+ className: "space-y-1",
1032
+ children: [/* @__PURE__ */ jsx("label", {
1033
+ className: "block text-xs font-medium text-gray-600",
1034
+ children: nested.label
1035
+ }), /* @__PURE__ */ jsx(Field, {
1036
+ binding: {
1037
+ ...toBindingFields(nested),
1038
+ id,
1039
+ value: val,
1040
+ rawValue: typeof val === "object" && val !== null ? JSON.stringify(val) : String(val),
1041
+ onChange: (next) => {
1042
+ onChange({
1043
+ ...objectValue,
1044
+ [key]: next
1045
+ });
1046
+ }
1047
+ },
1048
+ onNodeChange
1049
+ })]
1050
+ }, key);
1051
+ })
1015
1052
  });
1016
1053
  }
1017
1054
  if (binding.type === "boolean" || typeof value === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
@@ -1065,61 +1102,6 @@ const Field = ({ binding, onNodeChange }) => {
1065
1102
  className: "mt-1 text-xs text-red-500",
1066
1103
  children: validationError
1067
1104
  })] });
1068
- if (binding.widget === "icon-picker" || binding.type === "icon-picker") {
1069
- const SelectedIcon = ICON_MAP[stringValue];
1070
- return /* @__PURE__ */ jsxs("div", {
1071
- className: "flex items-center gap-2",
1072
- children: [/* @__PURE__ */ jsx(Select, {
1073
- value: stringValue,
1074
- options: ICON_OPTIONS,
1075
- onChange: commitIfChanged
1076
- }), SelectedIcon && /* @__PURE__ */ jsx(SelectedIcon, {
1077
- size: 18,
1078
- className: "shrink-0"
1079
- })]
1080
- });
1081
- }
1082
- if (binding.widget === "asset-picker" || binding.type === "asset-picker") {
1083
- const defaultUploadValue = stringValue ? [{
1084
- uid: "current",
1085
- name: stringValue.split("/").pop() || "asset",
1086
- url: stringValue
1087
- }] : [];
1088
- return /* @__PURE__ */ jsxs("div", {
1089
- className: "space-y-2",
1090
- children: [
1091
- /* @__PURE__ */ jsx(Input, {
1092
- value: text,
1093
- onChange: (e) => setText(e.target.value),
1094
- placeholder: "Enter an image URL",
1095
- onBlur: (e) => {
1096
- const next = e.target.value.trim();
1097
- const result = validateBindingValue(binding, next);
1098
- if (!result.valid) {
1099
- setValidationError(result.message ?? "Invalid value.");
1100
- return;
1101
- }
1102
- setValidationError(null);
1103
- commitIfChanged(next);
1104
- }
1105
- }),
1106
- /* @__PURE__ */ jsx(Upload, {
1107
- multiple: false,
1108
- maxCount: 1,
1109
- accept: "image/*",
1110
- defaultValue: defaultUploadValue,
1111
- onChange: (files) => {
1112
- const next = files[0]?.url ?? "";
1113
- commitIfChanged(next);
1114
- }
1115
- }),
1116
- validationError && /* @__PURE__ */ jsx("p", {
1117
- className: "mt-1 text-xs text-red-500",
1118
- children: validationError
1119
- })
1120
- ]
1121
- });
1122
- }
1123
1105
  if (binding.type === "number" || typeof value === "number") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1124
1106
  type: "number",
1125
1107
  value: text,
@@ -1160,6 +1142,13 @@ const Field = ({ binding, onNodeChange }) => {
1160
1142
  children: validationError
1161
1143
  })] });
1162
1144
  };
1145
+ const Field = (props) => {
1146
+ const { renderField } = useDndEditOptions();
1147
+ const builtin = /* @__PURE__ */ jsx(BuiltinField, { ...props });
1148
+ if (!renderField) return builtin;
1149
+ const custom = renderField(props, builtin);
1150
+ return custom === void 0 ? builtin : /* @__PURE__ */ jsx(Fragment, { children: custom });
1151
+ };
1163
1152
  //#endregion
1164
1153
  //#region src/components/dnd/panel/field-group.tsx
1165
1154
  const FieldGroup = ({ bindings, onNodeChange }) => /* @__PURE__ */ jsx("div", {
@@ -1351,16 +1340,59 @@ const Layout = ({ palette, panel }) => {
1351
1340
  ] });
1352
1341
  };
1353
1342
  //#endregion
1343
+ //#region src/components/dnd/section-fallback-context.ts
1344
+ const SectionFallbackContext = createContext(void 0);
1345
+ //#endregion
1346
+ //#region src/components/dnd/section-fallback.tsx
1347
+ const ConsumerFallback = ({ render, args, builtin }) => {
1348
+ const node = render(args);
1349
+ return node === void 0 ? builtin : /* @__PURE__ */ jsx(Fragment, { children: node });
1350
+ };
1351
+ const SectionFallback = ({ args, builtin }) => {
1352
+ const render = useContext(SectionFallbackContext);
1353
+ if (!render) return builtin;
1354
+ return /* @__PURE__ */ jsx(ErrorBoundary, {
1355
+ fallback: () => builtin,
1356
+ children: /* @__PURE__ */ jsx(ConsumerFallback, {
1357
+ render,
1358
+ args,
1359
+ builtin
1360
+ })
1361
+ });
1362
+ };
1363
+ //#endregion
1354
1364
  //#region src/components/dnd/renderer.tsx
1355
- const Renderer = ({ preview, headers, modules, frame, dynamicTailwind = false, provider }) => {
1356
- const module = useCompiledModule(preview, modules);
1357
- const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind);
1365
+ const Renderer = ({ preview, sectionId = "", sectionName = "", sectionCode = preview, forceFallback = false, headers, modules, frame, dynamicTailwind = false, provider }) => {
1366
+ const module = useCompiledModule(forceFallback ? "" : preview, modules);
1367
+ const section = {
1368
+ id: sectionId,
1369
+ name: sectionName,
1370
+ code: sectionCode
1371
+ };
1372
+ const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind && !forceFallback);
1358
1373
  const renderProvider = (component) => {
1359
1374
  return provider ? provider(component) : component;
1360
1375
  };
1361
- if (module?.error) return /* @__PURE__ */ jsx(Error$1, {
1362
- message: module.error,
1363
- title: "Compile Error"
1376
+ if (forceFallback) return /* @__PURE__ */ jsx(SectionFallback, {
1377
+ args: {
1378
+ section,
1379
+ reason: "forced"
1380
+ },
1381
+ builtin: /* @__PURE__ */ jsx(Error$1, {
1382
+ message: "This section is not rendered in this editor.",
1383
+ title: "Section Unavailable"
1384
+ })
1385
+ });
1386
+ if (module?.error) return /* @__PURE__ */ jsx(SectionFallback, {
1387
+ args: {
1388
+ section,
1389
+ reason: "compile",
1390
+ message: module.error
1391
+ },
1392
+ builtin: /* @__PURE__ */ jsx(Error$1, {
1393
+ message: module.error,
1394
+ title: "Compile Error"
1395
+ })
1364
1396
  });
1365
1397
  const Component = module?.exports?.default;
1366
1398
  if (!Component) return null;
@@ -1373,6 +1405,18 @@ const Renderer = ({ preview, headers, modules, frame, dynamicTailwind = false, p
1373
1405
  "data-editor-mode": true,
1374
1406
  children: /* @__PURE__ */ jsx(ErrorBoundary, {
1375
1407
  resetKeys: [preview],
1408
+ fallback: (message, reset) => /* @__PURE__ */ jsx(SectionFallback, {
1409
+ args: {
1410
+ section,
1411
+ reason: "runtime",
1412
+ message
1413
+ },
1414
+ builtin: /* @__PURE__ */ jsx(Error$1, {
1415
+ message,
1416
+ onReset: reset,
1417
+ title: "Rendering Error"
1418
+ })
1419
+ }),
1376
1420
  children: renderProvider(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Component, {
1377
1421
  headers,
1378
1422
  container
@@ -1427,7 +1471,7 @@ const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy
1427
1471
  };
1428
1472
  //#endregion
1429
1473
  //#region src/components/dnd/overlay.tsx
1430
- const Overlay = ({ sections, renderProps }) => {
1474
+ const Overlay = ({ sections, isForced, renderProps }) => {
1431
1475
  const { active } = useDndContext();
1432
1476
  if (!active) return null;
1433
1477
  if (active.data.current?.type === "new-item") {
@@ -1442,6 +1486,10 @@ const Overlay = ({ sections, renderProps }) => {
1442
1486
  name: section.name,
1443
1487
  children: /* @__PURE__ */ jsx(renderer_default, {
1444
1488
  preview,
1489
+ sectionId: section.id,
1490
+ sectionName: section.name,
1491
+ sectionCode: section.code,
1492
+ forceFallback: isForced?.(section) ?? false,
1445
1493
  modules: renderProps.modules,
1446
1494
  frame: renderProps.frame,
1447
1495
  dynamicTailwind: renderProps.dynamicTailwind
@@ -1558,6 +1606,10 @@ const describeUpdateFailure = (failure, label) => {
1558
1606
  title: `Failed to update "${label}"`,
1559
1607
  description: "The target element could not be found in this section."
1560
1608
  };
1609
+ case "unsupported-syntax": return {
1610
+ title: `Cannot edit "${label}" in the panel`,
1611
+ description: `The "${failure.property}" expression was preserved. Change it in the code editor instead.`
1612
+ };
1561
1613
  case "parse-error": return {
1562
1614
  title: `Failed to update "${label}"`,
1563
1615
  description: failure.error instanceof Error ? failure.error.message : "Check the console for details."
@@ -1570,11 +1622,19 @@ const conditionalModifiers = (args) => {
1570
1622
  if (active?.data.current?.type === "new-item") return args.transform;
1571
1623
  return restrictToVerticalAxis(args);
1572
1624
  };
1573
- const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, children, ...restProps }) => {
1625
+ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, children, ...restProps }) => {
1574
1626
  const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
1575
1627
  const { breakpoint } = useResponsiveSize();
1576
1628
  const isMobile = breakpoint.current === "xs" || breakpoint.current === "sm";
1577
1629
  const { code } = usePreview();
1630
+ const isForced = (section) => {
1631
+ try {
1632
+ return shouldForceSectionFallback?.(section) === true;
1633
+ } catch (error) {
1634
+ console.error("shouldForceSectionFallback threw", error);
1635
+ return false;
1636
+ }
1637
+ };
1578
1638
  const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }));
1579
1639
  const value = _value === void 0 ? code : _value;
1580
1640
  const { sections, previews, selectedId, selectedItem, selectedIndex, select, clearSelection, add: addItem, remove: onDelete, copy: onCopy, move: moveSection, reorder, patch } = useSectionDocument(value, _onChange);
@@ -1601,32 +1661,49 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1601
1661
  if (!selectedCode) return {
1602
1662
  fields: [],
1603
1663
  updatedCode: "",
1604
- parseError: false
1664
+ parseError: null
1605
1665
  };
1606
1666
  try {
1607
1667
  const updated = fillIds(selectedCode);
1608
1668
  return {
1609
1669
  fields: extract(updated).filter((node) => node.tagName !== "section"),
1610
1670
  updatedCode: updated !== selectedCode ? updated : selectedCode,
1611
- parseError: false
1671
+ parseError: null
1612
1672
  };
1613
1673
  } catch (e) {
1614
1674
  console.warn("⚠️ Parsing error", e);
1615
1675
  return {
1616
1676
  fields: [],
1617
1677
  updatedCode: "",
1618
- parseError: true
1678
+ parseError: { error: e }
1619
1679
  };
1620
1680
  }
1621
1681
  }, [selectedCode]);
1682
+ const reportError = onEditError ?? toastEditError;
1683
+ const reportErrorRef = useRef(reportError);
1684
+ useEffect(() => {
1685
+ reportErrorRef.current = reportError;
1686
+ });
1622
1687
  useEffect(() => {
1623
- if (parseError) Toast.error("Failed to parse this section", { description: "Check the console for details." });
1688
+ if (parseError) reportErrorRef.current({
1689
+ type: "parse",
1690
+ target: "section",
1691
+ error: parseError.error,
1692
+ title: "Failed to parse this section",
1693
+ description: "Check the console for details."
1694
+ });
1624
1695
  }, [parseError]);
1625
1696
  const onFieldChange = ({ id, label, property, value: fieldValue }) => {
1626
1697
  const result = update(updatedCode, id, label, fieldValue, property);
1627
1698
  if (!result.success) {
1628
- const { title, description } = describeUpdateFailure(result.failure, label);
1629
- Toast.error(title, description ? { description } : void 0);
1699
+ reportError({
1700
+ type: "update",
1701
+ id,
1702
+ label,
1703
+ property,
1704
+ failure: result.failure,
1705
+ ...describeUpdateFailure(result.failure, label)
1706
+ });
1630
1707
  return;
1631
1708
  }
1632
1709
  if (selectedItem) onChange({
@@ -1634,37 +1711,8 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1634
1711
  code: result.code
1635
1712
  });
1636
1713
  };
1637
- const bindings = useMemo(() => {
1638
- return fields.flatMap((node) => {
1639
- const dataId = node.dataAttributes.find((a) => a.name === "data-id")?.value;
1640
- const bindingAttr = node.dataAttributes.find((a) => a.name === "data-binding")?.value;
1641
- if (!dataId || !bindingAttr) return [];
1642
- return (node.bindings ?? parseBinding(bindingAttr)).map((binding) => ({
1643
- id: dataId,
1644
- label: binding.label,
1645
- property: binding.property,
1646
- type: binding.type,
1647
- widget: binding.widget,
1648
- options: binding.options,
1649
- render: binding.render,
1650
- min: binding.min,
1651
- max: binding.max,
1652
- pattern: binding.pattern,
1653
- required: binding.required,
1654
- meta: binding.meta,
1655
- value: getStructuredValue(node, binding.property, binding.type),
1656
- rawValue: getCurrentValue(node, binding.property)
1657
- }));
1658
- });
1659
- }, [fields]).map((field) => ({
1660
- ...field,
1661
- onChange: (value) => onFieldChange({
1662
- id: field.id,
1663
- label: field.label,
1664
- property: field.property,
1665
- value
1666
- })
1667
- }));
1714
+ const bindingFields = useMemo(() => fields.flatMap((node) => resolvePanelBindings(node)?.bindings ?? []), [fields]);
1715
+ const bindings = withPanelCommit(bindingFields, onFieldChange);
1668
1716
  useEffect(() => {
1669
1717
  if (frame?.scripts?.length) preloadScripts(frame.scripts);
1670
1718
  }, [frame?.scripts]);
@@ -1707,6 +1755,10 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1707
1755
  onCopy,
1708
1756
  children: /* @__PURE__ */ jsx(renderer_default, {
1709
1757
  preview: previews[index],
1758
+ sectionId: section.id,
1759
+ sectionName: section.name,
1760
+ sectionCode: section.code,
1761
+ forceFallback: isForced(section),
1710
1762
  modules,
1711
1763
  frame,
1712
1764
  dynamicTailwind,
@@ -1716,40 +1768,50 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1716
1768
  }, section.id))
1717
1769
  })
1718
1770
  }) });
1719
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(DndContext, {
1720
- sensors,
1721
- collisionDetection: closestCenter,
1722
- modifiers: [conditionalModifiers],
1723
- onDragStart,
1724
- onDragOver,
1725
- onDragEnd,
1726
- children: [/* @__PURE__ */ jsx("div", {
1727
- className: cn("relative flex h-full w-full", className),
1728
- ...restProps,
1729
- children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
1730
- value: {
1731
- palette,
1732
- panel,
1733
- canvas,
1734
- isMobile,
1735
- selectedId,
1736
- clearSelection,
1737
- paletteOpen: mobilePaletteOpen,
1738
- setPaletteOpen: setMobilePaletteOpen
1739
- },
1740
- children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
1741
- })
1742
- }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
1743
- sections,
1744
- renderProps: {
1745
- fullCode: value,
1746
- modules,
1747
- frame,
1748
- dynamicTailwind,
1749
- ...props
1750
- }
1751
- }) })]
1752
- }) });
1771
+ return /* @__PURE__ */ jsx(SectionFallbackContext.Provider, {
1772
+ value: renderSectionFallback,
1773
+ children: /* @__PURE__ */ jsxs(DndContext, {
1774
+ sensors,
1775
+ collisionDetection: closestCenter,
1776
+ modifiers: [conditionalModifiers],
1777
+ onDragStart,
1778
+ onDragOver,
1779
+ onDragEnd,
1780
+ children: [/* @__PURE__ */ jsx("div", {
1781
+ className: cn("relative flex h-full w-full", className),
1782
+ ...restProps,
1783
+ children: /* @__PURE__ */ jsx(DndEditOptionsContext.Provider, {
1784
+ value: {
1785
+ renderField,
1786
+ reportError
1787
+ },
1788
+ children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
1789
+ value: {
1790
+ palette,
1791
+ panel,
1792
+ canvas,
1793
+ isMobile,
1794
+ selectedId,
1795
+ clearSelection,
1796
+ paletteOpen: mobilePaletteOpen,
1797
+ setPaletteOpen: setMobilePaletteOpen
1798
+ },
1799
+ children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
1800
+ })
1801
+ })
1802
+ }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
1803
+ sections,
1804
+ isForced,
1805
+ renderProps: {
1806
+ fullCode: value,
1807
+ modules,
1808
+ frame,
1809
+ dynamicTailwind,
1810
+ ...props
1811
+ }
1812
+ }) })]
1813
+ })
1814
+ });
1753
1815
  };
1754
1816
  //#endregion
1755
1817
  //#region src/components/dnd/index.ts
@@ -1761,6 +1823,6 @@ Dnd.Canvas = Canvas;
1761
1823
  Dnd.Panel = Panel;
1762
1824
  Dnd.Layout = Layout;
1763
1825
  //#endregion
1764
- export { Panel as a, ICON_MAP as c, useDndLayout as d, useDndPalette as f, Palette as i, ICON_OPTIONS as l, DraggableItem as m, Canvas as n, Field as o, useDndPanel as p, Layout as r, useItemsEditor as s, Dnd as t, useChildrenEditor as u };
1826
+ 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 };
1765
1827
 
1766
- //# sourceMappingURL=dnd-DTT37EbA.js.map
1828
+ //# sourceMappingURL=dnd-ClnGRNaY.js.map