@jbpark/live-editor 4.4.0 → 4.6.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 (77) hide show
  1. package/README.ko.md +4 -1
  2. package/README.md +7 -3
  3. package/dist/{boundary-B17cepvm.js → boundary-BLB80tyw.js} +8 -5
  4. package/dist/boundary-BLB80tyw.js.map +1 -0
  5. package/dist/cache-B0-XcIxJ.js.map +1 -1
  6. package/dist/{compile-DDfk5P25.js → compile-Dxas-D05.js} +3 -3
  7. package/dist/compile-Dxas-D05.js.map +1 -0
  8. package/dist/{constants-h1JAgOeW.js → constants-DU17Tdwo.js} +3 -3
  9. package/dist/constants-DU17Tdwo.js.map +1 -0
  10. package/dist/{context-DB5kW3qe.js → context-B8MzP7_p.js} +11 -5
  11. package/dist/context-B8MzP7_p.js.map +1 -0
  12. package/dist/{core-CRsAGpei.js → core-Bgfek364.js} +2 -2
  13. package/dist/core-Bgfek364.js.map +1 -0
  14. package/dist/{detect-typescript-CCj9ALXc.js → detect-typescript-Dr_oLniM.js} +2 -2
  15. package/dist/detect-typescript-Dr_oLniM.js.map +1 -0
  16. package/dist/dnd/index.d.ts +1 -1
  17. package/dist/dnd/index.js +1 -1
  18. package/dist/{dnd-BE5ZizqX.js → dnd-UoJILA75.js} +973 -885
  19. package/dist/dnd-UoJILA75.js.map +1 -0
  20. package/dist/{document-BDn_f08_.js → document-CGbe6bSV.js} +7739 -7736
  21. package/dist/document-CGbe6bSV.js.map +1 -0
  22. package/dist/editor/index.js +2 -2
  23. package/dist/{editor-DQ8Q--qR.js → editor-CMElfHer.js} +2 -2
  24. package/dist/editor-CMElfHer.js.map +1 -0
  25. package/dist/editor-caches-ClMvdcQF.js.map +1 -1
  26. package/dist/error/index.d.ts +1 -1
  27. package/dist/error/index.js +2 -2
  28. package/dist/{error-DWhxQEom.js → error-CN_GCnXB.js} +13 -6
  29. package/dist/error-CN_GCnXB.js.map +1 -0
  30. package/dist/{index-Dgh1JTX3.d.ts → index-C69Lv-jR.d.ts} +72 -69
  31. package/dist/{index-C4SGQ3Ci.d.ts → index-CRwEhJ7w.d.ts} +4 -1
  32. package/dist/{index-f7ijuNTa.d.ts → index-DKYUxi-E.d.ts} +115 -155
  33. package/dist/index-DrWtSKXu.d.ts +11 -0
  34. package/dist/index.d.ts +18 -18
  35. package/dist/index.js +12 -8
  36. package/dist/index.js.map +1 -1
  37. package/dist/{ast-dYb78955.js → items-B6OXxcLG.js} +588 -718
  38. package/dist/items-B6OXxcLG.js.map +1 -0
  39. package/dist/messages-CSrMI4Rm.js +133 -0
  40. package/dist/messages-CSrMI4Rm.js.map +1 -0
  41. package/dist/messages-IBPs4TkZ.d.ts +130 -0
  42. package/dist/preview/index.js +2 -2
  43. package/dist/{preview-BnaCEFsF.js → preview-CtyG5UbH.js} +6 -4
  44. package/dist/preview-CtyG5UbH.js.map +1 -0
  45. package/dist/provider/index.d.ts +3 -8
  46. package/dist/provider/index.js +3 -2
  47. package/dist/{sections-CBVMO8Jl.js → sections-BYWOBISr.js} +9 -4
  48. package/dist/sections-BYWOBISr.js.map +1 -0
  49. package/dist/states-Ci1AvoQ9.js.map +1 -1
  50. package/dist/style.css +505 -21
  51. package/dist/{use-dynamic-tailwind-Bcipc1hL.js → use-dynamic-tailwind-BP1toJkW.js} +66 -40
  52. package/dist/use-dynamic-tailwind-BP1toJkW.js.map +1 -0
  53. package/dist/utils/ast/index.d.ts +3 -2
  54. package/dist/utils/ast/index.js +150 -3
  55. package/dist/utils/ast/index.js.map +1 -0
  56. package/dist/utils/index.d.ts +30 -13
  57. package/dist/utils/index.js +26 -6
  58. package/dist/utils/index.js.map +1 -0
  59. package/dist/utils/tailwind/index.js.map +1 -1
  60. package/dist/validate-DB59Z_Nx.d.ts +100 -0
  61. package/dist/validate-iLo8IONY.js +64 -0
  62. package/dist/validate-iLo8IONY.js.map +1 -0
  63. package/package.json +3 -3
  64. package/dist/ast-dYb78955.js.map +0 -1
  65. package/dist/boundary-B17cepvm.js.map +0 -1
  66. package/dist/compile-DDfk5P25.js.map +0 -1
  67. package/dist/constants-h1JAgOeW.js.map +0 -1
  68. package/dist/context-DB5kW3qe.js.map +0 -1
  69. package/dist/core-CRsAGpei.js.map +0 -1
  70. package/dist/detect-typescript-CCj9ALXc.js.map +0 -1
  71. package/dist/dnd-BE5ZizqX.js.map +0 -1
  72. package/dist/document-BDn_f08_.js.map +0 -1
  73. package/dist/editor-DQ8Q--qR.js.map +0 -1
  74. package/dist/error-DWhxQEom.js.map +0 -1
  75. package/dist/preview-BnaCEFsF.js.map +0 -1
  76. package/dist/sections-CBVMO8Jl.js.map +0 -1
  77. package/dist/use-dynamic-tailwind-Bcipc1hL.js.map +0 -1
@@ -1,133 +1,91 @@
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-BDn_f08_.js";
2
- import { c as RESERVED_BINDING_PROPERTIES, o as DRAGGABLE_ITEMS, r as DATA_ATTR, t as BINDING_PROP } from "./constants-h1JAgOeW.js";
1
+ import { S as require_lib, c as inspectDocument, h as replaceIds, m as fillIdsFrom, n as createSectionPreviewCache, r as fillSectionIds, w as __toESM, x as nanoid } from "./document-CGbe6bSV.js";
2
+ import { c as RESERVED_BINDING_PROPERTIES, o as PALETTE_SECTIONS, r as DATA_ATTR, t as BINDING_PROP } from "./constants-DU17Tdwo.js";
3
+ import { n as validateBindingValue } from "./validate-iLo8IONY.js";
4
+ import { i as useLiveMessages } from "./messages-CSrMI4Rm.js";
3
5
  import { i as usePreview } from "./states-Ci1AvoQ9.js";
4
- import { E as readNodeBindings, F as parseValue, M as extractObjectProperties, P as parseArrayExpression, S as getStructuredValue, _ as moveSelectedIndices, a as getChildrenSignatures, c as appendArrayItem, d as moveArrayItems, g as canStructurallyEditArray, h as updateArrayItemValue, i as updateAll, j as extractNodeValue, k as canLosslesslyEvaluateSource, l as duplicateArrayItems, m as updateArrayItemProperty, p as removeArrayItems, s as extract, t as validateBindingValue, u as moveArrayItem, v as findEditableChildren, y as getCurrentValue } from "./ast-dYb78955.js";
6
+ import { A as extractNodeValue, C as getStructuredValue, D as readNodeBindings, M as parseArrayExpression, N as parseValue, b as getCurrentValue, c as updateArrayItemValue, f as updateAll, g as moveSelectedIndices, h as getChildrenSignatures, i as moveArrayItems, j as extractObjectProperties, k as canLosslesslyEvaluateSource, l as canStructurallyEditArray, n as duplicateArrayItems, o as removeArrayItems, r as moveArrayItem, s as updateArrayItemProperty, t as appendArrayItem, v as extract, y as findEditableChildren } from "./items-B6OXxcLG.js";
5
7
  import { t as cn } from "./cn-CAnad2vL.js";
6
- import { o as preloadScripts } from "./compile-DDfk5P25.js";
7
- import { a as Frame, i as useStableModules, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-Bcipc1hL.js";
8
- import { t as Core } from "./core-CRsAGpei.js";
9
- import { a as replaceSections, n as extractSections, r as generateSection } from "./sections-CBVMO8Jl.js";
10
- import { n as Error$1, t as ErrorBoundary } from "./boundary-B17cepvm.js";
11
- import { Children, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
8
+ import { o as preloadScripts } from "./compile-Dxas-D05.js";
9
+ import { a as useStableModules, i as Frame, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-BP1toJkW.js";
10
+ import { i as generateSection, o as replaceSections, r as extractSections } from "./sections-BYWOBISr.js";
11
+ import { n as Error$1, t as ErrorBoundary } from "./boundary-BLB80tyw.js";
12
+ import { t as Core } from "./core-Bgfek364.js";
13
+ import { Children, createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
12
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
15
  import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, PointerSensor, TouchSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
14
16
  import { restrictToParentElement, restrictToVerticalAxis } from "@dnd-kit/modifiers";
15
17
  import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
16
18
  import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography } from "@jbpark/ui-kit";
17
19
  import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
18
- import { createPortal } from "react-dom";
19
- import { ArrowDown, ArrowUp, ChevronDown, ChevronRight, ChevronUp, Copy, Crosshair, GripVertical, LayoutGrid, Plus, Trash, X } from "lucide-react";
20
20
  import { CSS as CSS$1 } from "@dnd-kit/utilities";
21
- //#region src/components/dnd/droppable.tsx
21
+ import { ArrowDown, ArrowUp, ChevronDown, ChevronRight, ChevronUp, Copy, Crosshair, GripVertical, LayoutGrid, Plus, Trash, X } from "lucide-react";
22
+ //#region src/components/dnd/palette/palette-drag.ts
22
23
  var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
24
+ const PALETTE_SECTION = "palette-section";
25
+ const paletteDragData = (section) => ({
26
+ type: PALETTE_SECTION,
27
+ section
28
+ });
29
+ const paletteSectionOf = (data) => {
30
+ const current = data?.current;
31
+ return current?.type === PALETTE_SECTION ? current.section : void 0;
32
+ };
33
+ //#endregion
34
+ //#region src/components/dnd/canvas/droppable.tsx
23
35
  const Droppable = ({ children, className }) => {
24
36
  const { setNodeRef, isOver, active } = useDroppable({ id: "sortable-area" });
25
37
  const { setNodeRef: setBottomRef, isOver: isBottomOver } = useDroppable({ id: "sortable-area-bottom" });
26
- const isNewItemDragging = active?.data.current?.type === "new-item";
27
- const shouldHighlight = isOver && isNewItemDragging;
28
- const shouldHighlightBottom = isBottomOver && isNewItemDragging;
38
+ const isPaletteSectionDragging = !!paletteSectionOf(active?.data);
39
+ const shouldHighlight = isOver && isPaletteSectionDragging;
40
+ const shouldHighlightBottom = isBottomOver && isPaletteSectionDragging;
41
+ const messages = useLiveMessages();
29
42
  return /* @__PURE__ */ jsxs("div", {
30
43
  ref: setNodeRef,
31
44
  className: cn("min-h-full", "border-2 border-dashed p-1", shouldHighlight ? "border-blue-300 bg-blue-50" : "border-gray-200", className),
32
- children: [children, isNewItemDragging && /* @__PURE__ */ jsx("div", {
45
+ children: [children, isPaletteSectionDragging && /* @__PURE__ */ jsx("div", {
33
46
  ref: setBottomRef,
34
47
  className: cn("mt-2 min-h-24", "rounded-lg border-2 border-dashed", "transition-all duration-200", "flex items-center justify-center", shouldHighlightBottom ? "border-blue-400 bg-blue-100" : "border-gray-300 bg-gray-50"),
35
48
  children: /* @__PURE__ */ jsx(Typography.Text, {
36
49
  className: "text-sm text-gray-500",
37
- children: shouldHighlightBottom ? "Drop here" : "Drag here to add at bottom"
50
+ children: shouldHighlightBottom ? messages.canvas.dropHere : messages.canvas.dropAtBottom
38
51
  })
39
52
  })]
40
53
  });
41
54
  };
42
55
  //#endregion
43
- //#region src/components/dnd/edit-options.ts
44
- const toastEditError = (error) => {
45
- Toast.error(error.title, error.description ? { description: error.description } : void 0);
46
- };
47
- const DndEditOptionsContext = createContext({ reportError: toastEditError });
48
- const useDndEditOptions = () => useContext(DndEditOptionsContext);
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 findElementById = (section, id) => {
77
- const selector = `[${DATA_ATTR.ID}="${CSS.escape(id)}"]`;
78
- const roots = [section];
79
- for (const root of roots) {
80
- const found = root.querySelector(selector);
81
- if (found) return found;
82
- root.querySelectorAll("iframe").forEach((frame) => {
83
- if (frame.contentDocument) roots.push(frame.contentDocument);
84
- });
85
- root.querySelectorAll("*").forEach((element) => {
86
- if (element.shadowRoot) roots.push(element.shadowRoot);
87
- });
88
- }
89
- return null;
90
- };
91
- const viewportRect = (element) => {
92
- const rect = element.getBoundingClientRect();
93
- let x = rect.left;
94
- let y = rect.top;
95
- let frame = element.ownerDocument.defaultView?.frameElement ?? null;
96
- while (frame) {
97
- const frameRect = frame.getBoundingClientRect();
98
- x += frameRect.left;
99
- y += frameRect.top;
100
- frame = frame.ownerDocument.defaultView?.frameElement ?? null;
101
- }
102
- return new DOMRect(x, y, rect.width, rect.height);
56
+ //#region src/components/dnd/canvas/inspector-highlight.tsx
57
+ const InspectorHighlight = ({ rect }) => {
58
+ const ref = useRef(null);
59
+ useLayoutEffect(() => {
60
+ const box = ref.current;
61
+ const container = box?.closest("[data-frame-container]");
62
+ if (!box || !rect || !container) return;
63
+ const outer = container.getBoundingClientRect();
64
+ box.style.left = `${rect.left - outer.left - container.clientLeft + container.scrollLeft}px`;
65
+ box.style.top = `${rect.top - outer.top - container.clientTop + container.scrollTop}px`;
66
+ box.style.width = `${rect.width}px`;
67
+ box.style.height = `${rect.height}px`;
68
+ }, [rect]);
69
+ return rect ? /* @__PURE__ */ jsx("div", {
70
+ ref,
71
+ "aria-hidden": true,
72
+ "data-dnd-inspector-highlight": true,
73
+ style: {
74
+ position: "absolute",
75
+ pointerEvents: "none",
76
+ outline: "2px solid #3b82f6",
77
+ outlineOffset: -1,
78
+ background: "rgba(59, 130, 246, 0.12)",
79
+ zIndex: 65
80
+ }
81
+ }) : null;
103
82
  };
104
83
  //#endregion
105
- //#region src/components/dnd/inspector-highlight.tsx
106
- const InspectorHighlight = ({ rect }) => rect ? createPortal(/* @__PURE__ */ jsx("div", {
107
- "aria-hidden": true,
108
- "data-dnd-inspector-highlight": true,
109
- style: {
110
- position: "fixed",
111
- left: rect.left,
112
- top: rect.top,
113
- width: rect.width,
114
- height: rect.height,
115
- pointerEvents: "none",
116
- outline: "2px solid #3b82f6",
117
- outlineOffset: -1,
118
- background: "rgba(59, 130, 246, 0.12)",
119
- zIndex: 2147483647
120
- }
121
- }), document.body) : null;
122
- //#endregion
123
- //#region src/components/dnd/draggable.tsx
84
+ //#region src/components/dnd/palette/draggable.tsx
124
85
  const DraggableItem = ({ item, children }) => {
125
86
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
126
87
  id: item.id,
127
- data: {
128
- type: "new-item",
129
- item
130
- }
88
+ data: paletteDragData(item)
131
89
  });
132
90
  return children({
133
91
  ref: setNodeRef,
@@ -158,173 +116,475 @@ const DefaultDraggableItem = ({ item, onAdd, tapToAdd = false }) => /* @__PURE__
158
116
  })
159
117
  });
160
118
  //#endregion
161
- //#region src/components/dnd/layout-context.ts
162
- const DndRegionContext = createContext(null);
163
- const useDndRegions = (subject) => {
164
- const regions = useContext(DndRegionContext);
165
- if (!regions) throw new Error(`${subject} must be used inside <Live.Dnd>.`);
166
- return regions;
167
- };
168
- const useDndPalette = () => useDndRegions("useDndPalette()").palette;
169
- const useDndPanel = () => useDndRegions("useDndPanel()").panel;
170
- const useDndLayout = () => useDndRegions("useDndLayout()");
119
+ //#region src/components/dnd/canvas/section-fallback-context.ts
120
+ const SectionFallbackContext = createContext(void 0);
171
121
  //#endregion
172
- //#region src/components/dnd/panel-binding.ts
173
- const readAttribute = (node, name) => node.dataAttributes.find((attribute) => attribute.name === name)?.value;
174
- const canEditBindingValue = (node, binding) => {
175
- if (RESERVED_BINDING_PROPERTIES.includes(binding.property)) return false;
176
- 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;
177
- const attribute = node.attributes.find((candidate) => candidate.name === binding.property);
178
- if (!attribute) return true;
179
- return attribute.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
180
- };
181
- const toBindingFields = (binding) => ({
182
- label: binding.label,
183
- property: binding.property,
184
- type: binding.type,
185
- widget: binding.widget,
186
- options: binding.options,
187
- render: binding.render,
188
- min: binding.min,
189
- max: binding.max,
190
- pattern: binding.pattern,
191
- required: binding.required,
192
- meta: binding.meta
193
- });
194
- const resolveRenderEntry = (render, key) => {
195
- const entry = render?.[key];
196
- if (entry && "type" in entry) {
197
- const leaf = entry;
198
- return {
199
- ...leaf,
200
- label: leaf.label ?? key,
201
- property: leaf.property ?? key
202
- };
203
- }
204
- return {
205
- label: key,
206
- property: key,
207
- render: entry
208
- };
209
- };
210
- const CONTENT_PROPERTIES = /* @__PURE__ */ new Set([
211
- "innerText",
212
- "innerHTML",
213
- "children"
214
- ]);
215
- const isAttributeAbsent = (node, binding) => !CONTENT_PROPERTIES.has(binding.property) && !node.attributes.some((candidate) => candidate.name === binding.property);
216
- const toPanelBindingData = (node, id, binding) => ({
217
- ...toBindingFields(binding),
218
- id,
219
- value: getStructuredValue(node, binding.property, binding.type),
220
- rawValue: getCurrentValue(node, binding.property),
221
- ...!canEditBindingValue(node, binding) && { canEditValue: false },
222
- ...isAttributeAbsent(node, binding) && { present: false },
223
- element: {
224
- tagName: node.tagName || "element",
225
- text: node.textContent.replace(/\s+/g, " ").trim()
226
- }
227
- });
228
- const resolvePanelBindings = (node) => {
229
- const id = readAttribute(node, "data-id");
230
- if (!id) return null;
231
- const parsed = readNodeBindings(node);
232
- if (!parsed.length) return null;
233
- return {
234
- id,
235
- tagName: node.tagName || "element",
236
- bindings: parsed.map((binding) => toPanelBindingData(node, id, binding))
237
- };
122
+ //#region src/components/dnd/canvas/section-fallback.tsx
123
+ const ConsumerFallback = ({ render, args, builtin }) => {
124
+ const node = render(args);
125
+ return node === void 0 ? builtin : /* @__PURE__ */ jsx(Fragment, { children: node });
238
126
  };
239
- const withPanelCommit = (bindings, commit) => bindings.map((binding) => ({
240
- ...binding,
241
- onChange: (value) => commit?.({
242
- id: binding.id,
243
- label: binding.label,
244
- property: binding.property,
245
- value
246
- })
247
- }));
248
- //#endregion
249
- //#region src/components/dnd/panel/bulk-actions-bar.tsx
250
- const BulkActionsBar = ({ count, disabled = false, onDuplicate, onMoveUp, onMoveDown, onDelete, onClear }) => {
251
- if (count === 0) return null;
252
- return /* @__PURE__ */ jsxs("div", {
253
- className: "flex items-center justify-between rounded border\n border-blue-200 bg-blue-50 p-2",
254
- children: [/* @__PURE__ */ jsxs("div", {
255
- className: "text-xs font-medium text-blue-700",
256
- children: [count, " selected"]
257
- }), /* @__PURE__ */ jsxs("div", {
258
- className: "flex items-center space-x-1",
259
- children: [
260
- /* @__PURE__ */ jsx(Button, {
261
- size: "small",
262
- icon: /* @__PURE__ */ jsx(Copy, {}),
263
- title: "Duplicate selected",
264
- disabled,
265
- onClick: onDuplicate
266
- }),
267
- /* @__PURE__ */ jsx(Button, {
268
- size: "small",
269
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
270
- title: "Move selected up",
271
- disabled,
272
- onClick: onMoveUp
273
- }),
274
- /* @__PURE__ */ jsx(Button, {
275
- size: "small",
276
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
277
- title: "Move selected down",
278
- disabled,
279
- onClick: onMoveDown
280
- }),
281
- /* @__PURE__ */ jsx(Button, {
282
- danger: true,
283
- size: "small",
284
- icon: /* @__PURE__ */ jsx(X, {}),
285
- title: "Delete selected",
286
- disabled,
287
- onClick: onDelete
288
- }),
289
- /* @__PURE__ */ jsx(Button, {
290
- size: "small",
291
- onClick: onClear,
292
- children: "Clear"
293
- })
294
- ]
295
- })]
127
+ const SectionFallback = ({ args, builtin }) => {
128
+ const render = useContext(SectionFallbackContext);
129
+ if (!render) return builtin;
130
+ return /* @__PURE__ */ jsx(ErrorBoundary, {
131
+ fallback: () => builtin,
132
+ children: /* @__PURE__ */ jsx(ConsumerFallback, {
133
+ render,
134
+ args,
135
+ builtin
136
+ })
296
137
  });
297
138
  };
298
139
  //#endregion
299
- //#region src/components/dnd/panel/node.tsx
300
- const Node = ({ data, onChange }) => {
301
- const source = resolvePanelBindings(data);
302
- if (!source) return null;
303
- return /* @__PURE__ */ jsx("div", {
304
- className: "space-y-2 rounded",
305
- children: /* @__PURE__ */ jsx("div", {
306
- className: "space-y-1",
307
- children: withPanelCommit(source.bindings, onChange).map((binding) => /* @__PURE__ */ jsxs("div", {
308
- className: "space-y-1",
309
- children: [/* @__PURE__ */ jsxs("label", {
310
- className: "block text-xs font-semibold text-gray-700",
311
- children: [binding.label, /* @__PURE__ */ jsxs("span", {
312
- className: "ml-1 text-gray-400",
313
- children: [
314
- "(",
315
- binding.property,
316
- ")"
317
- ]
318
- })]
319
- }), /* @__PURE__ */ jsx(Field, {
320
- binding,
321
- onNodeChange: onChange
322
- })]
323
- }, `${binding.id}-${binding.property}`))
140
+ //#region src/components/dnd/canvas/renderer.tsx
141
+ const Renderer = ({ preview, sectionId = "", sectionName = "", sectionCode = preview, forceFallback = false, headers, modules, frame, dynamicTailwind = false, provider }) => {
142
+ const module = useCompiledModule(forceFallback ? "" : preview, modules);
143
+ const section = {
144
+ id: sectionId,
145
+ name: sectionName,
146
+ code: sectionCode
147
+ };
148
+ const messages = useLiveMessages();
149
+ const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind && !forceFallback);
150
+ const renderProvider = (component) => {
151
+ return provider ? provider(component) : component;
152
+ };
153
+ if (forceFallback) return /* @__PURE__ */ jsx(SectionFallback, {
154
+ args: {
155
+ section,
156
+ reason: "forced"
157
+ },
158
+ builtin: /* @__PURE__ */ jsx(Error$1, {
159
+ message: messages.errors.sectionNotRendered,
160
+ title: messages.errors.sectionUnavailable
324
161
  })
325
162
  });
326
- };
327
- //#endregion
163
+ if (module?.error) return /* @__PURE__ */ jsx(SectionFallback, {
164
+ args: {
165
+ section,
166
+ reason: "compile",
167
+ message: module.error
168
+ },
169
+ builtin: /* @__PURE__ */ jsx(Error$1, {
170
+ message: module.error,
171
+ title: messages.errors.compile
172
+ })
173
+ });
174
+ const Component = module?.exports?.default;
175
+ if (!Component) return null;
176
+ return /* @__PURE__ */ jsx(Frame, {
177
+ ...frame,
178
+ autoHeight: true,
179
+ children: (container) => /* @__PURE__ */ jsx("div", {
180
+ ref: wrapperRef,
181
+ className: "w-full overflow-x-hidden",
182
+ "data-editor-mode": true,
183
+ children: /* @__PURE__ */ jsx(ErrorBoundary, {
184
+ resetKeys: [preview],
185
+ fallback: (message, reset) => /* @__PURE__ */ jsx(SectionFallback, {
186
+ args: {
187
+ section,
188
+ reason: "runtime",
189
+ message
190
+ },
191
+ builtin: /* @__PURE__ */ jsx(Error$1, {
192
+ message,
193
+ onReset: reset,
194
+ title: messages.errors.rendering
195
+ })
196
+ }),
197
+ children: renderProvider(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Component, {
198
+ headers,
199
+ container
200
+ }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })] }))
201
+ })
202
+ })
203
+ });
204
+ };
205
+ const sameValue = (a, b) => {
206
+ if (Object.is(a, b)) return true;
207
+ if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((item, i) => Object.is(item, b[i]));
208
+ if (a && b && typeof a === "object" && typeof b === "object" && Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
209
+ const aKeys = Object.keys(a);
210
+ return aKeys.length === Object.keys(b).length && aKeys.every((key) => Object.is(a[key], b[key]));
211
+ }
212
+ return false;
213
+ };
214
+ const sameFrame = (a, b) => {
215
+ if (a === b) return true;
216
+ if (!a || !b) return false;
217
+ return [.../* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])].every((key) => sameValue(a[key], b[key]));
218
+ };
219
+ const arePropsEqual = (prev, next) => {
220
+ return [.../* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)])].every((key) => key === "frame" ? sameFrame(prev.frame, next.frame) : Object.is(prev[key], next[key]));
221
+ };
222
+ var renderer_default = memo(Renderer, arePropsEqual);
223
+ //#endregion
224
+ //#region src/components/dnd/canvas/sortable.tsx
225
+ const NAVIGATION_KEYS = {
226
+ ArrowUp: "previous",
227
+ ArrowDown: "next",
228
+ Home: "first",
229
+ End: "last"
230
+ };
231
+ const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, nodeRef, onNavigate, onDeleteKey, inspecting = false, onInspectMove, onInspectPick, onInspectLeave }) => {
232
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging, isOver, active } = useSortable({
233
+ id,
234
+ disabled: inspecting
235
+ });
236
+ const messages = useLiveMessages();
237
+ const style = {
238
+ transform: CSS$1.Transform.toString(transform),
239
+ transition,
240
+ opacity: isDragging ? .5 : 1,
241
+ cursor: inspecting ? "crosshair" : "grab"
242
+ };
243
+ const isPaletteSectionOver = isOver && !!paletteSectionOf(active?.data);
244
+ const onKeyDown = (e) => {
245
+ listeners?.onKeyDown?.(e);
246
+ if (e.target !== e.currentTarget || active || e.defaultPrevented) return;
247
+ if (e.key === "Enter") {
248
+ e.preventDefault();
249
+ onClick?.();
250
+ return;
251
+ }
252
+ const to = NAVIGATION_KEYS[e.key];
253
+ if (to && onNavigate) {
254
+ e.preventDefault();
255
+ onNavigate(to);
256
+ return;
257
+ }
258
+ if ((e.key === "Delete" || e.key === "Backspace") && onDeleteKey) {
259
+ e.preventDefault();
260
+ onDeleteKey();
261
+ }
262
+ };
263
+ const setRefs = (node) => {
264
+ setNodeRef(node);
265
+ setActivatorNodeRef(node);
266
+ nodeRef?.(node);
267
+ };
268
+ const onDelete = (e) => {
269
+ e.stopPropagation();
270
+ _onDelete?.(id);
271
+ };
272
+ const onCopy = (e) => {
273
+ e.stopPropagation();
274
+ _onCopy?.(id);
275
+ };
276
+ const press = (action) => (e) => {
277
+ e.stopPropagation();
278
+ action?.();
279
+ };
280
+ return /* @__PURE__ */ jsxs("div", {
281
+ ref: setRefs,
282
+ style,
283
+ ...attributes,
284
+ ...listeners,
285
+ onKeyDown,
286
+ onClick,
287
+ className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isPaletteSectionOver && "border-t-4 border-t-green-500"),
288
+ children: [
289
+ /* @__PURE__ */ jsx("div", {
290
+ className: cn("absolute inset-0 z-50"),
291
+ onPointerMove: inspecting ? (e) => onInspectMove?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY) : void 0,
292
+ onPointerLeave: inspecting ? onInspectLeave : void 0,
293
+ onClick: inspecting ? (e) => {
294
+ e.stopPropagation();
295
+ onInspectPick?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY);
296
+ } : void 0
297
+ }),
298
+ children,
299
+ selected && /* @__PURE__ */ jsxs(Space, {
300
+ className: cn("absolute top-1 right-1 z-60"),
301
+ children: [
302
+ onMoveUp && /* @__PURE__ */ jsx(Button, {
303
+ icon: /* @__PURE__ */ jsx(ChevronUp, {}),
304
+ disabled: !canMoveUp,
305
+ onClick: press(onMoveUp),
306
+ "aria-label": messages.section.moveUp
307
+ }),
308
+ onMoveDown && /* @__PURE__ */ jsx(Button, {
309
+ icon: /* @__PURE__ */ jsx(ChevronDown, {}),
310
+ disabled: !canMoveDown,
311
+ onClick: press(onMoveDown),
312
+ "aria-label": messages.section.moveDown
313
+ }),
314
+ /* @__PURE__ */ jsx(Button, {
315
+ icon: /* @__PURE__ */ jsx(Copy, {}),
316
+ onClick: onCopy,
317
+ "aria-label": messages.section.duplicate
318
+ }),
319
+ /* @__PURE__ */ jsx(Button, {
320
+ danger: true,
321
+ icon: /* @__PURE__ */ jsx(Trash, {}),
322
+ onClick: onDelete,
323
+ "aria-label": messages.section.delete
324
+ })
325
+ ]
326
+ })
327
+ ]
328
+ });
329
+ };
330
+ //#endregion
331
+ //#region src/components/dnd/canvas/overlay.tsx
332
+ const Overlay = ({ sections, isForced, renderProps }) => {
333
+ const { active } = useDndContext();
334
+ if (!active) return null;
335
+ const paletteSection = paletteSectionOf(active.data);
336
+ if (paletteSection) return /* @__PURE__ */ jsx(DefaultDraggableItem, { item: paletteSection });
337
+ const section = sections.find((s) => s.id === active.id);
338
+ if (section) {
339
+ const preview = generateSection(section.code, renderProps.fullCode, { containerId: renderProps.containerId });
340
+ return /* @__PURE__ */ jsx(Sortable, {
341
+ id: section.id,
342
+ name: section.name,
343
+ children: /* @__PURE__ */ jsx(renderer_default, {
344
+ preview,
345
+ sectionId: section.id,
346
+ sectionName: section.name,
347
+ sectionCode: section.code,
348
+ forceFallback: isForced?.(section) ?? false,
349
+ modules: renderProps.modules,
350
+ frame: renderProps.frame,
351
+ dynamicTailwind: renderProps.dynamicTailwind
352
+ })
353
+ });
354
+ }
355
+ return null;
356
+ };
357
+ //#endregion
358
+ //#region src/components/dnd/edit-options.ts
359
+ const toastEditError = (error) => {
360
+ Toast.error(error.title, error.description ? { description: error.description } : void 0);
361
+ };
362
+ const DndEditOptionsContext = createContext({ reportError: toastEditError });
363
+ const useDndEditOptions = () => useContext(DndEditOptionsContext);
364
+ //#endregion
365
+ //#region src/components/dnd/inspector.ts
366
+ const DndInspectorContext = createContext(null);
367
+ const useDndInspector = () => {
368
+ const inspector = useContext(DndInspectorContext);
369
+ if (!inspector) throw new Error("useDndInspector() must be used inside <Live.Dnd>.");
370
+ return inspector;
371
+ };
372
+ const outside = (root) => (element) => element.getRootNode() !== root;
373
+ const deepestAt = (root, x, y, skip) => {
374
+ const element = root.elementsFromPoint(x, y).find((candidate) => !skip(candidate));
375
+ if (!element) return null;
376
+ if (element instanceof HTMLIFrameElement) {
377
+ const document = element.contentDocument;
378
+ if (!document) return element;
379
+ const rect = element.getBoundingClientRect();
380
+ return deepestAt(document, x - rect.left, y - rect.top, outside(document)) ?? element;
381
+ }
382
+ if (element.shadowRoot && element.shadowRoot !== root) return deepestAt(element.shadowRoot, x, y, outside(element.shadowRoot)) ?? element;
383
+ return element;
384
+ };
385
+ const hasId = (element) => Boolean(element.getAttribute(DATA_ATTR.ID));
386
+ const pickElement = (section, overlay, x, y) => {
387
+ let element = deepestAt(section.ownerDocument, x, y, (candidate) => candidate === overlay || candidate === section || !section.contains(candidate));
388
+ while (element && element !== section && !hasId(element)) element = element.parentElement ?? element.parentNode?.host ?? null;
389
+ return element === section ? null : element;
390
+ };
391
+ const findElementById = (section, id) => {
392
+ const selector = `[${DATA_ATTR.ID}="${CSS.escape(id)}"]`;
393
+ const roots = [section];
394
+ for (const root of roots) {
395
+ const found = root.querySelector(selector);
396
+ if (found) return found;
397
+ root.querySelectorAll("iframe").forEach((frame) => {
398
+ if (frame.contentDocument) roots.push(frame.contentDocument);
399
+ });
400
+ root.querySelectorAll("*").forEach((element) => {
401
+ if (element.shadowRoot) roots.push(element.shadowRoot);
402
+ });
403
+ }
404
+ return null;
405
+ };
406
+ const viewportRect = (element) => {
407
+ const rect = element.getBoundingClientRect();
408
+ let x = rect.left;
409
+ let y = rect.top;
410
+ let frame = element.ownerDocument.defaultView?.frameElement ?? null;
411
+ while (frame) {
412
+ const frameRect = frame.getBoundingClientRect();
413
+ x += frameRect.left;
414
+ y += frameRect.top;
415
+ frame = frame.ownerDocument.defaultView?.frameElement ?? null;
416
+ }
417
+ return new DOMRect(x, y, rect.width, rect.height);
418
+ };
419
+ //#endregion
420
+ //#region src/components/dnd/layout-context.ts
421
+ const DndRegionContext = createContext(null);
422
+ const useDndRegions = (subject) => {
423
+ const regions = useContext(DndRegionContext);
424
+ if (!regions) throw new Error(`${subject} must be used inside <Live.Dnd>.`);
425
+ return regions;
426
+ };
427
+ const useDndPalette = () => useDndRegions("useDndPalette()").palette;
428
+ const useDndPanel = () => useDndRegions("useDndPanel()").panel;
429
+ const useDndLayout = () => useDndRegions("useDndLayout()");
430
+ //#endregion
431
+ //#region src/components/dnd/panel-binding.ts
432
+ const readAttribute = (node, name) => node.dataAttributes.find((attribute) => attribute.name === name)?.value;
433
+ const canEditBindingValue = (node, binding) => {
434
+ if (RESERVED_BINDING_PROPERTIES.includes(binding.property)) return false;
435
+ 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;
436
+ const attribute = node.attributes.find((candidate) => candidate.name === binding.property);
437
+ if (!attribute) return true;
438
+ return attribute.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
439
+ };
440
+ const toBindingFields = (binding) => ({
441
+ label: binding.label,
442
+ property: binding.property,
443
+ type: binding.type,
444
+ widget: binding.widget,
445
+ options: binding.options,
446
+ render: binding.render,
447
+ min: binding.min,
448
+ max: binding.max,
449
+ pattern: binding.pattern,
450
+ required: binding.required,
451
+ meta: binding.meta
452
+ });
453
+ const resolveRenderEntry = (render, key) => {
454
+ const entry = render?.[key];
455
+ if (entry && "type" in entry) {
456
+ const leaf = entry;
457
+ return {
458
+ ...leaf,
459
+ label: leaf.label ?? key,
460
+ property: leaf.property ?? key
461
+ };
462
+ }
463
+ return {
464
+ label: key,
465
+ property: key,
466
+ render: entry
467
+ };
468
+ };
469
+ const CONTENT_PROPERTIES = /* @__PURE__ */ new Set([
470
+ "innerText",
471
+ "innerHTML",
472
+ "children"
473
+ ]);
474
+ const isAttributeAbsent = (node, binding) => !CONTENT_PROPERTIES.has(binding.property) && !node.attributes.some((candidate) => candidate.name === binding.property);
475
+ const toPanelBindingData = (node, id, binding) => ({
476
+ ...toBindingFields(binding),
477
+ id,
478
+ value: getStructuredValue(node, binding.property, binding.type),
479
+ rawValue: getCurrentValue(node, binding.property),
480
+ ...!canEditBindingValue(node, binding) && { canEditValue: false },
481
+ ...isAttributeAbsent(node, binding) && { present: false },
482
+ element: {
483
+ tagName: node.tagName || "element",
484
+ text: node.textContent.replace(/\s+/g, " ").trim()
485
+ }
486
+ });
487
+ const resolvePanelBindings = (node) => {
488
+ const id = readAttribute(node, "data-id");
489
+ if (!id) return null;
490
+ const parsed = readNodeBindings(node);
491
+ if (!parsed.length) return null;
492
+ return {
493
+ id,
494
+ tagName: node.tagName || "element",
495
+ bindings: parsed.map((binding) => toPanelBindingData(node, id, binding))
496
+ };
497
+ };
498
+ const withPanelCommit = (bindings, commit) => bindings.map((binding) => ({
499
+ ...binding,
500
+ onChange: (value) => commit?.({
501
+ id: binding.id,
502
+ label: binding.label,
503
+ property: binding.property,
504
+ value
505
+ })
506
+ }));
507
+ //#endregion
508
+ //#region src/components/dnd/panel/bulk-actions-bar.tsx
509
+ const BulkActionsBar = ({ count, disabled = false, onDuplicate, onMoveUp, onMoveDown, onDelete, onClear }) => {
510
+ const messages = useLiveMessages();
511
+ if (count === 0) return null;
512
+ return /* @__PURE__ */ jsxs("div", {
513
+ className: "flex items-center justify-between rounded border\n border-blue-200 bg-blue-50 p-2",
514
+ children: [/* @__PURE__ */ jsx("div", {
515
+ className: "text-xs font-medium text-blue-700",
516
+ children: messages.selection.count(count)
517
+ }), /* @__PURE__ */ jsxs("div", {
518
+ className: "flex items-center space-x-1",
519
+ children: [
520
+ /* @__PURE__ */ jsx(Button, {
521
+ size: "small",
522
+ icon: /* @__PURE__ */ jsx(Copy, {}),
523
+ title: messages.selection.duplicate,
524
+ disabled,
525
+ onClick: onDuplicate
526
+ }),
527
+ /* @__PURE__ */ jsx(Button, {
528
+ size: "small",
529
+ icon: /* @__PURE__ */ jsx(ArrowUp, {}),
530
+ title: messages.selection.moveUp,
531
+ disabled,
532
+ onClick: onMoveUp
533
+ }),
534
+ /* @__PURE__ */ jsx(Button, {
535
+ size: "small",
536
+ icon: /* @__PURE__ */ jsx(ArrowDown, {}),
537
+ title: messages.selection.moveDown,
538
+ disabled,
539
+ onClick: onMoveDown
540
+ }),
541
+ /* @__PURE__ */ jsx(Button, {
542
+ danger: true,
543
+ size: "small",
544
+ icon: /* @__PURE__ */ jsx(X, {}),
545
+ title: messages.selection.delete,
546
+ disabled,
547
+ onClick: onDelete
548
+ }),
549
+ /* @__PURE__ */ jsx(Button, {
550
+ size: "small",
551
+ onClick: onClear,
552
+ children: messages.selection.clear
553
+ })
554
+ ]
555
+ })]
556
+ });
557
+ };
558
+ //#endregion
559
+ //#region src/components/dnd/panel/node.tsx
560
+ const Node = ({ data, onChange }) => {
561
+ const source = resolvePanelBindings(data);
562
+ if (!source) return null;
563
+ return /* @__PURE__ */ jsx("div", {
564
+ className: "space-y-2 rounded",
565
+ children: /* @__PURE__ */ jsx("div", {
566
+ className: "space-y-1",
567
+ children: withPanelCommit(source.bindings, onChange).map((binding) => /* @__PURE__ */ jsxs("div", {
568
+ className: "space-y-1",
569
+ children: [/* @__PURE__ */ jsxs("label", {
570
+ className: "block text-xs font-semibold text-gray-700",
571
+ children: [binding.label, /* @__PURE__ */ jsxs("span", {
572
+ className: "ml-1 text-gray-400",
573
+ children: [
574
+ "(",
575
+ binding.property,
576
+ ")"
577
+ ]
578
+ })]
579
+ }), /* @__PURE__ */ jsx(Field, {
580
+ binding,
581
+ onNodeChange: onChange
582
+ })]
583
+ }, `${binding.id}-${binding.property}`))
584
+ })
585
+ });
586
+ };
587
+ //#endregion
328
588
  //#region src/components/dnd/panel/use-structural-selection.ts
329
589
  const selectionAfter = (command, selected, count) => {
330
590
  switch (command.type) {
@@ -420,6 +680,7 @@ const useDndChildren = (items, { onChange } = {}) => {
420
680
  const Children$1 = ({ value, onChange, onNodeChange }) => {
421
681
  const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
422
682
  const { selection, actions } = useDndChildren(items, { onChange });
683
+ const messages = useLiveMessages();
423
684
  const editableChildrenMap = useMemo(() => {
424
685
  const map = /* @__PURE__ */ new Map();
425
686
  items.forEach((item, index) => {
@@ -433,19 +694,15 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
433
694
  children: [
434
695
  /* @__PURE__ */ jsxs("div", {
435
696
  className: "flex items-center justify-between",
436
- children: [/* @__PURE__ */ jsxs("div", {
697
+ children: [/* @__PURE__ */ jsx("div", {
437
698
  className: "text-sm font-semibold text-green-700",
438
- children: [
439
- "Children Items (",
440
- items.length,
441
- ")"
442
- ]
699
+ children: messages.children.heading(items.length)
443
700
  }), /* @__PURE__ */ jsx(Button, {
444
701
  size: "small",
445
702
  color: "green",
446
703
  icon: /* @__PURE__ */ jsx(Plus, {}),
447
704
  onClick: actions.add,
448
- children: "Add Child"
705
+ children: messages.children.add
449
706
  })]
450
707
  }),
451
708
  /* @__PURE__ */ jsx(BulkActionsBar, {
@@ -470,15 +727,9 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
470
727
  checked: selection.isSelected(itemIndex),
471
728
  onChange: () => {}
472
729
  })
473
- }), /* @__PURE__ */ jsxs("div", {
730
+ }), /* @__PURE__ */ jsx("div", {
474
731
  className: "text-xs font-medium text-green-800",
475
- children: [
476
- "Child ",
477
- itemIndex + 1,
478
- " (",
479
- item.tagName || "fragment",
480
- ")"
481
- ]
732
+ children: messages.children.child(itemIndex + 1, item.tagName || "fragment")
482
733
  })]
483
734
  }), /* @__PURE__ */ jsxs("div", {
484
735
  className: "flex space-x-1",
@@ -486,17 +737,20 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
486
737
  /* @__PURE__ */ jsx(Button, {
487
738
  size: "small",
488
739
  icon: /* @__PURE__ */ jsx(ArrowUp, {}),
740
+ "aria-label": messages.children.moveUp,
489
741
  disabled: itemIndex === 0,
490
742
  onClick: () => actions.move(itemIndex, itemIndex - 1)
491
743
  }),
492
744
  /* @__PURE__ */ jsx(Button, {
493
745
  size: "small",
494
746
  icon: /* @__PURE__ */ jsx(ArrowDown, {}),
747
+ "aria-label": messages.children.moveDown,
495
748
  disabled: itemIndex === items.length - 1,
496
749
  onClick: () => actions.move(itemIndex, itemIndex + 1)
497
750
  }),
498
751
  /* @__PURE__ */ jsx(Button, {
499
- title: "Delete item",
752
+ "aria-label": messages.children.delete,
753
+ title: messages.children.delete,
500
754
  danger: true,
501
755
  size: "small",
502
756
  icon: /* @__PURE__ */ jsx(X, {}),
@@ -509,7 +763,7 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
509
763
  className: "space-y-2",
510
764
  children: [/* @__PURE__ */ jsx("div", {
511
765
  className: "text-xs font-medium text-green-700",
512
- children: "Editable Bindings:"
766
+ children: messages.children.editableBindings
513
767
  }), editableChildrenMap.get(itemIndex).map((editableNode, idx) => {
514
768
  const nodeId = editableNode.dataAttributes.find((a) => a.name === "data-id")?.value;
515
769
  return /* @__PURE__ */ jsxs("div", {
@@ -529,7 +783,7 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
529
783
  className: "space-y-1",
530
784
  children: [/* @__PURE__ */ jsx("div", {
531
785
  className: "text-xs font-medium text-green-700",
532
- children: "Child Nodes:"
786
+ children: messages.children.childNodes
533
787
  }), item.children.map((node, nodeIndex) => /* @__PURE__ */ jsx("div", {
534
788
  className: "ml-2 rounded border border-green-100 bg-white p-2",
535
789
  children: /* @__PURE__ */ jsx(Node, {
@@ -566,9 +820,9 @@ const getFieldKind = (binding) => {
566
820
  const isStructuralFieldKind = (kind) => kind === "items" || kind === "children";
567
821
  //#endregion
568
822
  //#region src/components/dnd/panel/sortable-items.tsx
569
- const label = (items, id) => {
823
+ const label = (messages, items, id) => {
570
824
  const index = items.findIndex((item) => item.id === id);
571
- return index >= 0 ? `Item ${index + 1}` : "Item";
825
+ return index >= 0 ? messages.items.item(index + 1) : String(id);
572
826
  };
573
827
  const SortableItems = ({ items, disabled, onMove, children }) => {
574
828
  const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 4 } }), useSensor(TouchSensor, { activationConstraint: {
@@ -580,15 +834,18 @@ const SortableItems = ({ items, disabled, onMove, children }) => {
580
834
  const to = items.find((item) => item.id === over?.id);
581
835
  if (from && to && from !== to) onMove(from, to.index);
582
836
  };
837
+ const messages = useLiveMessages();
838
+ const { announcements: say } = messages;
839
+ const name = (id) => label(messages, items, id);
583
840
  return /* @__PURE__ */ jsx(DndContext, {
584
841
  sensors,
585
842
  collisionDetection: closestCenter,
586
843
  modifiers: [restrictToVerticalAxis, restrictToParentElement],
587
844
  accessibility: { announcements: {
588
- onDragStart: ({ active }) => `Picked up ${label(items, active.id)}.`,
589
- onDragOver: ({ active, over }) => over ? `${label(items, active.id)} is over ${label(items, over.id)}.` : void 0,
590
- onDragEnd: ({ active, over }) => over ? `${label(items, active.id)} was moved to the place of ${label(items, over.id)}.` : `${label(items, active.id)} was dropped.`,
591
- onDragCancel: ({ active }) => `Moving ${label(items, active.id)} was cancelled.`
845
+ onDragStart: ({ active }) => say.pickedUp(name(active.id)),
846
+ onDragOver: ({ active, over }) => over ? say.itemOver(name(active.id), name(over.id)) : void 0,
847
+ onDragEnd: ({ active, over }) => over ? say.itemMoved(name(active.id), name(over.id)) : say.dropped(name(active.id)),
848
+ onDragCancel: ({ active }) => say.cancelled(name(active.id))
592
849
  } },
593
850
  onDragEnd,
594
851
  children: /* @__PURE__ */ jsx(SortableContext, {
@@ -603,6 +860,7 @@ const SortableItems = ({ items, disabled, onMove, children }) => {
603
860
  });
604
861
  };
605
862
  const ItemCard = ({ id, title, disabled, expanded, onToggle, checkbox, controls, className, children }) => {
863
+ const messages = useLiveMessages();
606
864
  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
607
865
  id,
608
866
  disabled
@@ -624,7 +882,7 @@ const ItemCard = ({ id, title, disabled, expanded, onToggle, checkbox, controls,
624
882
  ref: setActivatorNodeRef,
625
883
  ...attributes,
626
884
  ...listeners,
627
- "aria-label": `Reorder ${title}`,
885
+ "aria-label": messages.items.reorder(title),
628
886
  disabled,
629
887
  className: "cursor-grab rounded p-0.5 text-gray-400\n hover:text-gray-700 disabled:cursor-not-allowed\n disabled:opacity-40",
630
888
  children: /* @__PURE__ */ jsx(GripVertical, { size: 14 })
@@ -755,18 +1013,21 @@ const removeIds = (ids, indices) => {
755
1013
  };
756
1014
  const useDndItems = (value, { render, onChange, onNodeChange } = {}) => {
757
1015
  const { reportError, bindingOptions } = useDndEditOptions();
1016
+ const messages = useLiveMessages();
758
1017
  const { objectItems, primitiveItems, parseError } = useMemo(() => parseSource(value, bindingOptions), [value, bindingOptions]);
759
1018
  const canEditStructure = useMemo(() => canStructurallyEditArray(value), [value]);
760
1019
  const reportErrorRef = useRef(reportError);
1020
+ const messagesRef = useRef(messages);
761
1021
  useEffect(() => {
762
1022
  reportErrorRef.current = reportError;
1023
+ messagesRef.current = messages;
763
1024
  });
764
1025
  useEffect(() => {
765
1026
  if (parseError) reportErrorRef.current({
766
1027
  type: "parse",
767
1028
  target: "items",
768
- title: "Failed to parse items",
769
- description: "Check the console for details."
1029
+ title: messagesRef.current.editErrors.itemsParseFailed,
1030
+ description: messagesRef.current.editErrors.checkConsole
770
1031
  });
771
1032
  }, [parseError]);
772
1033
  const kind = primitiveItems.length > 0 && objectItems.length === 0 ? "primitive" : "object";
@@ -811,8 +1072,8 @@ const useDndItems = (value, { render, onChange, onNodeChange } = {}) => {
811
1072
  if (next === null) {
812
1073
  reportError({
813
1074
  type: "items",
814
- title: "Failed to update this item",
815
- description: "The source was preserved. Structural edits require a dense array without spreads; use the code editor for unsupported syntax."
1075
+ title: messages.editErrors.itemUpdateFailed,
1076
+ description: messages.editErrors.itemUpdateFailedDetail
816
1077
  });
817
1078
  return false;
818
1079
  }
@@ -1036,6 +1297,7 @@ const NestedGroup = ({ group, onNodeChange }) => {
1036
1297
  });
1037
1298
  };
1038
1299
  const Items = ({ value, render, onChange, onChildChange }) => {
1300
+ const messages = useLiveMessages();
1039
1301
  const { kind, items, selection, expansion, actions, canEditStructure, parseError } = useDndItems(value, {
1040
1302
  render,
1041
1303
  onChange,
@@ -1043,34 +1305,30 @@ const Items = ({ value, render, onChange, onChildChange }) => {
1043
1305
  });
1044
1306
  const header = /* @__PURE__ */ jsxs("div", {
1045
1307
  className: "flex items-center justify-between",
1046
- children: [/* @__PURE__ */ jsxs("div", {
1308
+ children: [/* @__PURE__ */ jsx("div", {
1047
1309
  className: "text-sm font-semibold",
1048
- children: [
1049
- "Items (",
1050
- items.length,
1051
- ")"
1052
- ]
1310
+ children: messages.items.heading(items.length)
1053
1311
  }), items.length > 0 && canEditStructure && /* @__PURE__ */ jsx(Button, {
1054
1312
  size: "small",
1055
1313
  icon: /* @__PURE__ */ jsx(Plus, {}),
1056
1314
  variant: "solid",
1057
1315
  color: "green",
1058
1316
  onClick: actions.add,
1059
- children: "Add Item"
1317
+ children: messages.items.add
1060
1318
  })]
1061
1319
  });
1062
1320
  if (parseError) return /* @__PURE__ */ jsxs("div", {
1063
1321
  className: "space-y-4",
1064
1322
  children: [header, /* @__PURE__ */ jsx("div", {
1065
1323
  className: "rounded border border-dashed border-red-200 p-3 text-xs\n text-red-600",
1066
- children: "This value could not be read as a list. Edit it in the code editor."
1324
+ children: messages.items.unreadable
1067
1325
  })]
1068
1326
  });
1069
1327
  if (items.length === 0) return /* @__PURE__ */ jsxs("div", {
1070
1328
  className: "space-y-4",
1071
1329
  children: [header, /* @__PURE__ */ jsx("div", {
1072
1330
  className: "rounded border border-dashed border-gray-200 p-3 text-xs\n text-gray-500",
1073
- children: "No editable items. The panel copies an existing item rather than guessing the shape of a new one — add the first item in the code editor."
1331
+ children: messages.items.empty
1074
1332
  })]
1075
1333
  });
1076
1334
  const bulkBar = /* @__PURE__ */ jsx(BulkActionsBar, {
@@ -1088,12 +1346,14 @@ const Items = ({ value, render, onChange, onChildChange }) => {
1088
1346
  /* @__PURE__ */ jsx(Button, {
1089
1347
  size: "small",
1090
1348
  icon: /* @__PURE__ */ jsx(ArrowUp, {}),
1349
+ "aria-label": messages.items.moveUp,
1091
1350
  disabled: !canEditStructure || item.index === 0,
1092
1351
  onClick: () => actions.move(item.elementIndex, item.index - 1)
1093
1352
  }),
1094
1353
  /* @__PURE__ */ jsx(Button, {
1095
1354
  size: "small",
1096
1355
  icon: /* @__PURE__ */ jsx(ArrowDown, {}),
1356
+ "aria-label": messages.items.moveDown,
1097
1357
  disabled: !canEditStructure || item.index === items.length - 1,
1098
1358
  onClick: () => actions.move(item.elementIndex, item.index + 1)
1099
1359
  }),
@@ -1101,6 +1361,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
1101
1361
  danger: true,
1102
1362
  size: "small",
1103
1363
  icon: /* @__PURE__ */ jsx(X, {}),
1364
+ "aria-label": messages.items.delete,
1104
1365
  disabled: !canEditStructure || items.length <= 1,
1105
1366
  onClick: () => actions.remove(item.elementIndex)
1106
1367
  })
@@ -1139,9 +1400,16 @@ const Items = ({ value, render, onChange, onChildChange }) => {
1139
1400
  children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
1140
1401
  group,
1141
1402
  onNodeChange: onChildChange
1142
- }, group.property)) : /* @__PURE__ */ jsx("div", {
1403
+ }, group.property)) : /* @__PURE__ */ jsxs("div", {
1143
1404
  className: "text-xs text-gray-500",
1144
- children: "✓ No JSX bindings found"
1405
+ children: [
1406
+ /* @__PURE__ */ jsx("span", {
1407
+ "aria-hidden": "true",
1408
+ children: "✓"
1409
+ }),
1410
+ " ",
1411
+ messages.items.noNestedBindings
1412
+ ]
1145
1413
  })
1146
1414
  })] });
1147
1415
  const allExpanded = expansion.expandedIds.length === items.length;
@@ -1155,13 +1423,13 @@ const Items = ({ value, render, onChange, onChildChange }) => {
1155
1423
  size: "small",
1156
1424
  variant: "text",
1157
1425
  onClick: () => expansion.setExpanded(allExpanded ? [] : items.map((item) => item.id)),
1158
- children: allExpanded ? "Collapse all" : "Expand all"
1426
+ children: allExpanded ? messages.items.collapseAll : messages.items.expandAll
1159
1427
  })
1160
1428
  }),
1161
1429
  bulkBar,
1162
1430
  !canEditStructure && /* @__PURE__ */ jsx("div", {
1163
1431
  className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
1164
- 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."
1432
+ children: messages.items.structureLocked
1165
1433
  }),
1166
1434
  /* @__PURE__ */ jsx(SortableItems, {
1167
1435
  items,
@@ -1169,7 +1437,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
1169
1437
  onMove: (item, toIndex) => actions.move(item.elementIndex, toIndex),
1170
1438
  children: (item) => /* @__PURE__ */ jsx(ItemCard, {
1171
1439
  id: item.id,
1172
- title: `Item ${item.index + 1}`,
1440
+ title: messages.items.item(item.index + 1),
1173
1441
  disabled: !canEditStructure,
1174
1442
  expanded: expansion.isExpanded(item.id),
1175
1443
  onToggle: () => expansion.toggle(item.id),
@@ -1278,6 +1546,8 @@ const FieldError = ({ message }) => message ? /* @__PURE__ */ jsx("p", {
1278
1546
  children: message
1279
1547
  }) : null;
1280
1548
  const BuiltinField = ({ binding, onNodeChange }) => {
1549
+ const messages = useLiveMessages();
1550
+ const validationOptions = { messages: messages.validation };
1281
1551
  const { id, value, rawValue, onChange } = binding;
1282
1552
  const [validationError, setValidationError] = useState(null);
1283
1553
  const [text, setText] = useState(rawValue);
@@ -1290,12 +1560,12 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1290
1560
  if (kind === "readonly") return /* @__PURE__ */ jsxs("div", {
1291
1561
  className: "space-y-1 rounded border border-dashed border-amber-200 p-3\n text-xs text-amber-700",
1292
1562
  children: [
1293
- /* @__PURE__ */ jsx("div", { children: "This expression is preserved but is not editable here." }),
1563
+ /* @__PURE__ */ jsx("div", { children: messages.panel.expressionPreserved }),
1294
1564
  /* @__PURE__ */ jsx("code", {
1295
1565
  className: "block overflow-x-auto text-amber-800",
1296
1566
  children: rawValue
1297
1567
  }),
1298
- /* @__PURE__ */ jsx("div", { children: "Use the code editor to change it." })
1568
+ /* @__PURE__ */ jsx("div", { children: messages.panel.useCodeEditor })
1299
1569
  ]
1300
1570
  });
1301
1571
  const commitIfChanged = (next) => {
@@ -1372,9 +1642,9 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1372
1642
  value: parseDateValue(stringValue),
1373
1643
  onChange: (date) => {
1374
1644
  const next = date ? formatDateValue(date) : "";
1375
- const result = validateBindingValue(binding, next);
1645
+ const result = validateBindingValue(binding, next, validationOptions);
1376
1646
  if (!result.valid) {
1377
- setValidationError(result.message ?? "Invalid value.");
1647
+ setValidationError(result.message ?? messages.panel.invalidValue);
1378
1648
  return;
1379
1649
  }
1380
1650
  setValidationError(null);
@@ -1385,12 +1655,12 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1385
1655
  type: "url",
1386
1656
  value: text,
1387
1657
  onChange: (e) => setText(e.target.value),
1388
- placeholder: "https://example.com",
1658
+ placeholder: messages.panel.urlPlaceholder,
1389
1659
  onBlur: (e) => {
1390
1660
  const next = e.target.value.trim();
1391
- const result = validateBindingValue(binding, next);
1661
+ const result = validateBindingValue(binding, next, validationOptions);
1392
1662
  if (!result.valid) {
1393
- setValidationError(result.message ?? "Invalid value.");
1663
+ setValidationError(result.message ?? messages.panel.invalidValue);
1394
1664
  return;
1395
1665
  }
1396
1666
  setValidationError(null);
@@ -1401,13 +1671,13 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1401
1671
  type: "number",
1402
1672
  value: text,
1403
1673
  onChange: (e) => setText(e.target.value),
1404
- placeholder: "Enter a numeric value",
1674
+ placeholder: messages.panel.numberPlaceholder,
1405
1675
  onBlur: (e) => {
1406
1676
  const raw = e.target.value.trim();
1407
1677
  const next = raw !== "" && !Number.isNaN(Number(raw)) ? Number(raw) : raw;
1408
- const result = validateBindingValue(binding, next);
1678
+ const result = validateBindingValue(binding, next, validationOptions);
1409
1679
  if (!result.valid) {
1410
- setValidationError(result.message ?? "Invalid value.");
1680
+ setValidationError(result.message ?? messages.panel.invalidValue);
1411
1681
  return;
1412
1682
  }
1413
1683
  setValidationError(null);
@@ -1417,13 +1687,13 @@ const BuiltinField = ({ binding, onNodeChange }) => {
1417
1687
  return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input.TextArea, {
1418
1688
  value: text,
1419
1689
  onChange: (e) => setText(e.target.value),
1420
- placeholder: "Enter a value",
1690
+ placeholder: messages.panel.textPlaceholder,
1421
1691
  onBlur: (e) => {
1422
1692
  const raw = e.target.value.trim();
1423
1693
  const next = binding.type ? raw : parseValue(raw);
1424
- const result = validateBindingValue(binding, next);
1694
+ const result = validateBindingValue(binding, next, validationOptions);
1425
1695
  if (!result.valid) {
1426
- setValidationError(result.message ?? "Invalid value.");
1696
+ setValidationError(result.message ?? messages.panel.invalidValue);
1427
1697
  return;
1428
1698
  }
1429
1699
  setValidationError(null);
@@ -1484,6 +1754,7 @@ const describeElement = (binding) => {
1484
1754
  };
1485
1755
  const nodeIdOf = (target) => target instanceof Element && target.closest("[data-node-id]")?.getAttribute("data-node-id") || null;
1486
1756
  const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange, readOnly = false }) => {
1757
+ const messages = useLiveMessages();
1487
1758
  const groups = groupBindingsById(bindings);
1488
1759
  const rootRef = useRef(null);
1489
1760
  const inspector = useContext(DndInspectorContext);
@@ -1519,7 +1790,7 @@ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canM
1519
1790
  }, [pickedId, groups.length]);
1520
1791
  if (!item) return /* @__PURE__ */ jsx(Typography.Paragraph, {
1521
1792
  className: cn("p-4 text-sm text-gray-500"),
1522
- children: "Please select a section."
1793
+ children: messages.panel.selectSection
1523
1794
  });
1524
1795
  return /* @__PURE__ */ jsxs("div", {
1525
1796
  ref: rootRef,
@@ -1543,19 +1814,19 @@ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canM
1543
1814
  icon: /* @__PURE__ */ jsx(ChevronUp, {}),
1544
1815
  disabled: !canMoveUp,
1545
1816
  onClick: onMoveUp,
1546
- "aria-label": "Move section up"
1817
+ "aria-label": messages.section.moveUp
1547
1818
  }),
1548
1819
  onMoveDown && /* @__PURE__ */ jsx(Button, {
1549
1820
  icon: /* @__PURE__ */ jsx(ChevronDown, {}),
1550
1821
  disabled: !canMoveDown,
1551
1822
  onClick: onMoveDown,
1552
- "aria-label": "Move section down"
1823
+ "aria-label": messages.section.moveDown
1553
1824
  }),
1554
1825
  onDelete && /* @__PURE__ */ jsx(Button, {
1555
1826
  danger: true,
1556
1827
  icon: /* @__PURE__ */ jsx(Trash, {}),
1557
1828
  onClick: () => onDelete(item.id),
1558
- "aria-label": "Delete section"
1829
+ "aria-label": messages.section.delete
1559
1830
  })
1560
1831
  ]
1561
1832
  })]
@@ -1563,11 +1834,11 @@ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canM
1563
1834
  readOnly && /* @__PURE__ */ jsx(Typography.Text, {
1564
1835
  role: "status",
1565
1836
  className: "block text-xs text-amber-700",
1566
- 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."
1837
+ children: messages.panel.readOnly
1567
1838
  }),
1568
1839
  !groups.length && /* @__PURE__ */ jsx(Typography.Text, {
1569
1840
  className: "text-xs text-gray-400",
1570
- children: "No editable elements."
1841
+ children: messages.panel.noEditableElements
1571
1842
  }),
1572
1843
  groups.map((group) => {
1573
1844
  const element = describeElement(group[0]);
@@ -1630,6 +1901,7 @@ const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1630
1901
  const Canvas = ({ className, style, ...restProps }) => {
1631
1902
  const { canvas, documentError } = useDndRegions("<Live.Dnd.Canvas>");
1632
1903
  const inspector = useDndInspector();
1904
+ const messages = useLiveMessages();
1633
1905
  return /* @__PURE__ */ jsxs("div", {
1634
1906
  className: cn("relative h-full w-full overflow-y-auto", className),
1635
1907
  "data-frame-container": true,
@@ -1649,8 +1921,8 @@ const Canvas = ({ className, style, ...restProps }) => {
1649
1921
  color: "primary",
1650
1922
  variant: inspector.active ? "solid" : "outlined",
1651
1923
  "aria-pressed": inspector.active,
1652
- "aria-label": "Pick an element",
1653
- title: inspector.active ? "Click an element to edit it. Esc to cancel." : "Pick an element",
1924
+ "aria-label": messages.canvas.pickElement,
1925
+ title: inspector.active ? messages.canvas.pickElementActive : messages.canvas.pickElement,
1654
1926
  onClick: inspector.toggle
1655
1927
  })
1656
1928
  })
@@ -1664,298 +1936,259 @@ const Panel = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1664
1936
  });
1665
1937
  const Layout = ({ palette, panel }) => {
1666
1938
  const { isMobile, selectedId, clearSelection, paletteOpen, setPaletteOpen } = useDndRegions("<Live.Dnd.Layout>");
1939
+ const messages = useLiveMessages();
1667
1940
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1668
1941
  isMobile ? /* @__PURE__ */ jsx(Canvas, {}) : /* @__PURE__ */ jsxs(Splitter, {
1669
1942
  withHandle: true,
1670
1943
  orientation: "horizontal",
1671
- children: [
1672
- /* @__PURE__ */ jsx(Splitter.Panel, {
1673
- defaultSize: "20%",
1674
- minSize: "15%",
1675
- maxSize: "35%",
1676
- collapsible: true,
1677
- children: palette ?? /* @__PURE__ */ jsx(Palette, {})
1678
- }),
1679
- /* @__PURE__ */ jsx(Splitter.Panel, {
1680
- defaultSize: "60%",
1681
- children: /* @__PURE__ */ jsx(Canvas, {})
1682
- }),
1683
- /* @__PURE__ */ jsx(Splitter.Panel, {
1684
- defaultSize: "20%",
1685
- minSize: "15%",
1686
- maxSize: "35%",
1687
- collapsible: true,
1688
- children: panel ?? /* @__PURE__ */ jsx(Panel, {})
1689
- })
1690
- ]
1691
- }),
1692
- /* @__PURE__ */ jsx(Button, {
1693
- type: "primary",
1694
- shape: "circle",
1695
- icon: /* @__PURE__ */ jsx(LayoutGrid, {}),
1696
- "aria-label": "Components",
1697
- className: "fixed right-4 bottom-4 z-20 md:hidden",
1698
- onClick: () => setPaletteOpen(true)
1699
- }),
1700
- /* @__PURE__ */ jsx(Drawer, {
1701
- open: isMobile && paletteOpen,
1702
- onClose: () => setPaletteOpen(false),
1703
- direction: "bottom",
1704
- size: "large",
1705
- title: "Components",
1706
- children: palette ?? /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Layout>" })
1707
- }),
1708
- /* @__PURE__ */ jsx(Drawer, {
1709
- open: isMobile && Boolean(selectedId),
1710
- onClose: clearSelection,
1711
- direction: "bottom",
1712
- size: "large",
1713
- title: "Properties",
1714
- children: panel ?? /* @__PURE__ */ jsx(PanelFields, { subject: "<Live.Dnd.Layout>" })
1715
- })
1716
- ] });
1717
- };
1718
- //#endregion
1719
- //#region src/components/dnd/section-fallback-context.ts
1720
- const SectionFallbackContext = createContext(void 0);
1721
- //#endregion
1722
- //#region src/components/dnd/section-fallback.tsx
1723
- const ConsumerFallback = ({ render, args, builtin }) => {
1724
- const node = render(args);
1725
- return node === void 0 ? builtin : /* @__PURE__ */ jsx(Fragment, { children: node });
1726
- };
1727
- const SectionFallback = ({ args, builtin }) => {
1728
- const render = useContext(SectionFallbackContext);
1729
- if (!render) return builtin;
1730
- return /* @__PURE__ */ jsx(ErrorBoundary, {
1731
- fallback: () => builtin,
1732
- children: /* @__PURE__ */ jsx(ConsumerFallback, {
1733
- render,
1734
- args,
1735
- builtin
1736
- })
1737
- });
1738
- };
1739
- //#endregion
1740
- //#region src/components/dnd/renderer.tsx
1741
- const Renderer = ({ preview, sectionId = "", sectionName = "", sectionCode = preview, forceFallback = false, headers, modules, frame, dynamicTailwind = false, provider }) => {
1742
- const module = useCompiledModule(forceFallback ? "" : preview, modules);
1743
- const section = {
1744
- id: sectionId,
1745
- name: sectionName,
1746
- code: sectionCode
1747
- };
1748
- const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind && !forceFallback);
1749
- const renderProvider = (component) => {
1750
- return provider ? provider(component) : component;
1751
- };
1752
- if (forceFallback) return /* @__PURE__ */ jsx(SectionFallback, {
1753
- args: {
1754
- section,
1755
- reason: "forced"
1756
- },
1757
- builtin: /* @__PURE__ */ jsx(Error$1, {
1758
- message: "This section is not rendered in this editor.",
1759
- title: "Section Unavailable"
1760
- })
1761
- });
1762
- if (module?.error) return /* @__PURE__ */ jsx(SectionFallback, {
1763
- args: {
1764
- section,
1765
- reason: "compile",
1766
- message: module.error
1767
- },
1768
- builtin: /* @__PURE__ */ jsx(Error$1, {
1769
- message: module.error,
1770
- title: "Compile Error"
1771
- })
1772
- });
1773
- const Component = module?.exports?.default;
1774
- if (!Component) return null;
1775
- return /* @__PURE__ */ jsx(Frame, {
1776
- ...frame,
1777
- autoHeight: true,
1778
- children: (container) => /* @__PURE__ */ jsx("div", {
1779
- ref: wrapperRef,
1780
- className: "w-full overflow-x-hidden",
1781
- "data-editor-mode": true,
1782
- children: /* @__PURE__ */ jsx(ErrorBoundary, {
1783
- resetKeys: [preview],
1784
- fallback: (message, reset) => /* @__PURE__ */ jsx(SectionFallback, {
1785
- args: {
1786
- section,
1787
- reason: "runtime",
1788
- message
1789
- },
1790
- builtin: /* @__PURE__ */ jsx(Error$1, {
1791
- message,
1792
- onReset: reset,
1793
- title: "Rendering Error"
1794
- })
1944
+ children: [
1945
+ /* @__PURE__ */ jsx(Splitter.Panel, {
1946
+ defaultSize: "20%",
1947
+ minSize: "15%",
1948
+ maxSize: "35%",
1949
+ collapsible: true,
1950
+ children: palette ?? /* @__PURE__ */ jsx(Palette, {})
1795
1951
  }),
1796
- children: renderProvider(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Component, {
1797
- headers,
1798
- container
1799
- }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })] }))
1800
- })
1952
+ /* @__PURE__ */ jsx(Splitter.Panel, {
1953
+ defaultSize: "60%",
1954
+ children: /* @__PURE__ */ jsx(Canvas, {})
1955
+ }),
1956
+ /* @__PURE__ */ jsx(Splitter.Panel, {
1957
+ defaultSize: "20%",
1958
+ minSize: "15%",
1959
+ maxSize: "35%",
1960
+ collapsible: true,
1961
+ children: panel ?? /* @__PURE__ */ jsx(Panel, {})
1962
+ })
1963
+ ]
1964
+ }),
1965
+ /* @__PURE__ */ jsx(Button, {
1966
+ type: "primary",
1967
+ shape: "circle",
1968
+ icon: /* @__PURE__ */ jsx(LayoutGrid, {}),
1969
+ "aria-label": messages.canvas.palette,
1970
+ className: "fixed right-4 bottom-4 z-20 md:hidden",
1971
+ onClick: () => setPaletteOpen(true)
1972
+ }),
1973
+ /* @__PURE__ */ jsx(Drawer, {
1974
+ open: isMobile && paletteOpen,
1975
+ onClose: () => setPaletteOpen(false),
1976
+ direction: "bottom",
1977
+ size: "large",
1978
+ title: messages.canvas.palette,
1979
+ children: palette ?? /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Layout>" })
1980
+ }),
1981
+ /* @__PURE__ */ jsx(Drawer, {
1982
+ open: isMobile && Boolean(selectedId),
1983
+ onClose: clearSelection,
1984
+ direction: "bottom",
1985
+ size: "large",
1986
+ title: messages.canvas.properties,
1987
+ children: panel ?? /* @__PURE__ */ jsx(PanelFields, { subject: "<Live.Dnd.Layout>" })
1801
1988
  })
1802
- });
1803
- };
1804
- const sameValue = (a, b) => {
1805
- if (Object.is(a, b)) return true;
1806
- if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((item, i) => Object.is(item, b[i]));
1807
- if (a && b && typeof a === "object" && typeof b === "object" && Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
1808
- const aKeys = Object.keys(a);
1809
- return aKeys.length === Object.keys(b).length && aKeys.every((key) => Object.is(a[key], b[key]));
1810
- }
1811
- return false;
1812
- };
1813
- const sameFrame = (a, b) => {
1814
- if (a === b) return true;
1815
- if (!a || !b) return false;
1816
- return [.../* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])].every((key) => sameValue(a[key], b[key]));
1817
- };
1818
- const arePropsEqual = (prev, next) => {
1819
- return [.../* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)])].every((key) => key === "frame" ? sameFrame(prev.frame, next.frame) : Object.is(prev[key], next[key]));
1989
+ ] });
1820
1990
  };
1821
- var renderer_default = memo(Renderer, arePropsEqual);
1822
1991
  //#endregion
1823
- //#region src/components/dnd/sortable.tsx
1824
- const NAVIGATION_KEYS = {
1825
- ArrowUp: "previous",
1826
- ArrowDown: "next",
1827
- Home: "first",
1828
- End: "last"
1829
- };
1830
- const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, nodeRef, onNavigate, onDeleteKey, inspecting = false, onInspectMove, onInspectPick, onInspectLeave }) => {
1831
- const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging, isOver, active } = useSortable({
1832
- id,
1833
- disabled: inspecting
1992
+ //#region src/components/dnd/state/use-delete-flow.ts
1993
+ const useDeleteFlow = ({ sections, remove, onBeforeDelete }) => {
1994
+ const removeRef = useRef(remove);
1995
+ useEffect(() => {
1996
+ removeRef.current = remove;
1834
1997
  });
1835
- const style = {
1836
- transform: CSS$1.Transform.toString(transform),
1837
- transition,
1838
- opacity: isDragging ? .5 : 1,
1839
- cursor: inspecting ? "crosshair" : "grab"
1840
- };
1841
- const isNewItemOver = isOver && active?.data.current?.type === "new-item";
1842
- const onKeyDown = (e) => {
1843
- listeners?.onKeyDown?.(e);
1844
- if (e.target !== e.currentTarget || active || e.defaultPrevented) return;
1845
- if (e.key === "Enter") {
1846
- e.preventDefault();
1847
- onClick?.();
1998
+ return (id, onDeleted) => {
1999
+ const section = sections.find((candidate) => candidate.id === id);
2000
+ if (!onBeforeDelete || !section) {
2001
+ remove(id);
2002
+ onDeleted?.();
1848
2003
  return;
1849
2004
  }
1850
- const to = NAVIGATION_KEYS[e.key];
1851
- if (to && onNavigate) {
1852
- e.preventDefault();
1853
- onNavigate(to);
1854
- return;
2005
+ const decide = (allowed) => {
2006
+ if (allowed) {
2007
+ removeRef.current(id);
2008
+ onDeleted?.();
2009
+ }
2010
+ };
2011
+ try {
2012
+ const answer = onBeforeDelete(section);
2013
+ if (typeof answer === "boolean") {
2014
+ decide(answer);
2015
+ return;
2016
+ }
2017
+ answer.then(decide, (error) => {
2018
+ console.error("onBeforeDelete rejected; the section was kept", error);
2019
+ });
2020
+ } catch (error) {
2021
+ console.error("onBeforeDelete threw; the section was kept", error);
1855
2022
  }
1856
- if ((e.key === "Delete" || e.key === "Backspace") && onDeleteKey) {
1857
- e.preventDefault();
1858
- onDeleteKey();
2023
+ };
2024
+ };
2025
+ //#endregion
2026
+ //#region src/components/dnd/state/use-dnd-keyboard.ts
2027
+ const KEYBOARD_CODES = {
2028
+ start: ["Space"],
2029
+ cancel: ["Escape"],
2030
+ end: ["Space", "Enter"]
2031
+ };
2032
+ const createNodeRegistry = () => {
2033
+ const nodes = /* @__PURE__ */ new Map();
2034
+ const callbacks = /* @__PURE__ */ new Map();
2035
+ return {
2036
+ get: (id) => nodes.get(id),
2037
+ register: (id) => {
2038
+ let callback = callbacks.get(id);
2039
+ if (!callback) {
2040
+ callback = (node) => {
2041
+ if (node) nodes.set(id, node);
2042
+ else {
2043
+ nodes.delete(id);
2044
+ callbacks.delete(id);
2045
+ }
2046
+ };
2047
+ callbacks.set(id, callback);
2048
+ }
2049
+ return callback;
1859
2050
  }
1860
2051
  };
1861
- const setRefs = (node) => {
1862
- setNodeRef(node);
1863
- setActivatorNodeRef(node);
1864
- nodeRef?.(node);
2052
+ };
2053
+ const useDndKeyboard = ({ sections, selectedId, selectedIndex, selectOnly, move, requestDelete }) => {
2054
+ const { announcements: say } = useLiveMessages();
2055
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }), useSensor(KeyboardSensor, {
2056
+ coordinateGetter: sortableKeyboardCoordinates,
2057
+ keyboardCodes: KEYBOARD_CODES
2058
+ }));
2059
+ const nameOf = (id, data) => {
2060
+ return paletteSectionOf(data)?.name ?? sections.find((section) => section.id === id)?.name ?? say.section;
1865
2061
  };
1866
- const onDelete = (e) => {
1867
- e.stopPropagation();
1868
- _onDelete?.(id);
2062
+ const positionOf = (id) => sections.findIndex((section) => section.id === id) + 1;
2063
+ const announcements = {
2064
+ onDragStart: ({ active }) => say.pickedUp(nameOf(active.id, active.data)),
2065
+ onDragOver: ({ active, over }) => over && positionOf(over.id) > 0 ? say.movedTo(nameOf(active.id, active.data), positionOf(over.id), sections.length) : say.notOver(nameOf(active.id, active.data)),
2066
+ onDragEnd: ({ active, over }) => over && positionOf(over.id) > 0 ? say.droppedAt(nameOf(active.id, active.data), positionOf(over.id), sections.length) : say.dropped(nameOf(active.id, active.data)),
2067
+ onDragCancel: ({ active }) => say.cancelled(nameOf(active.id, active.data))
1869
2068
  };
1870
- const onCopy = (e) => {
1871
- e.stopPropagation();
1872
- _onCopy?.(id);
2069
+ const screenReaderInstructions = { draggable: say.instructions };
2070
+ const [sectionNodes] = useState(createNodeRegistry);
2071
+ const focusSection = (id) => sectionNodes.get(id)?.focus();
2072
+ const onNavigate = (fromId, to) => {
2073
+ const from = sections.findIndex((section) => section.id === fromId);
2074
+ const target = sections[to === "first" ? 0 : to === "last" ? sections.length - 1 : from + (to === "next" ? 1 : -1)];
2075
+ if (from < 0 || !target || target.id === fromId) return;
2076
+ selectOnly(target.id);
2077
+ focusSection(target.id);
2078
+ };
2079
+ const onDeleteKey = (id) => {
2080
+ const index = sections.findIndex((section) => section.id === id);
2081
+ const neighbor = sections[index + 1] ?? sections[index - 1];
2082
+ requestDelete(id, () => {
2083
+ if (neighbor) focusSection(neighbor.id);
2084
+ });
2085
+ };
2086
+ const onMoveButton = (id, direction) => {
2087
+ const to = sections.findIndex((section) => section.id === id) + (direction === "up" ? -1 : 1);
2088
+ move(id, direction);
2089
+ if (to === 0 || to === sections.length - 1) focusSection(id);
2090
+ };
2091
+ useEffect(() => {
2092
+ if (selectedId === null) return;
2093
+ const frame = requestAnimationFrame(() => {
2094
+ sectionNodes.get(selectedId)?.scrollIntoView?.({ block: "nearest" });
2095
+ });
2096
+ return () => cancelAnimationFrame(frame);
2097
+ }, [
2098
+ sectionNodes,
2099
+ selectedId,
2100
+ selectedIndex
2101
+ ]);
2102
+ return {
2103
+ sensors,
2104
+ announcements,
2105
+ screenReaderInstructions,
2106
+ sectionNodes,
2107
+ onNavigate,
2108
+ onDeleteKey,
2109
+ onMoveButton
2110
+ };
2111
+ };
2112
+ //#endregion
2113
+ //#region src/components/dnd/state/use-inspector-state.ts
2114
+ const useInspectorState = ({ selectedId, selectOnly, sectionNodes, onNodePick }) => {
2115
+ const [inspecting, setInspecting] = useState(false);
2116
+ const [picked, setPicked] = useState(null);
2117
+ const [hoverRect, setHoverRect] = useState(null);
2118
+ const [fieldTarget, setFieldTarget] = useState(null);
2119
+ const [fieldRect, setFieldRect] = useState(null);
2120
+ if (picked && picked.sectionId !== selectedId) setPicked(null);
2121
+ const stopInspecting = useCallback(() => {
2122
+ setInspecting(false);
2123
+ setHoverRect(null);
2124
+ }, []);
2125
+ useEffect(() => {
2126
+ if (!inspecting) return;
2127
+ const onKeyDown = (e) => {
2128
+ if (e.key === "Escape") stopInspecting();
2129
+ };
2130
+ window.addEventListener("keydown", onKeyDown);
2131
+ return () => window.removeEventListener("keydown", onKeyDown);
2132
+ }, [inspecting, stopInspecting]);
2133
+ useEffect(() => {
2134
+ const section = selectedId === null ? null : sectionNodes.get(selectedId);
2135
+ if (!fieldTarget || !section) return;
2136
+ let frame = 0;
2137
+ const measure = () => {
2138
+ cancelAnimationFrame(frame);
2139
+ frame = requestAnimationFrame(() => {
2140
+ const element = findElementById(section, fieldTarget);
2141
+ setFieldRect(element ? viewportRect(element) : null);
2142
+ });
2143
+ };
2144
+ measure();
2145
+ window.addEventListener("scroll", measure, true);
2146
+ window.addEventListener("resize", measure);
2147
+ return () => {
2148
+ cancelAnimationFrame(frame);
2149
+ window.removeEventListener("scroll", measure, true);
2150
+ window.removeEventListener("resize", measure);
2151
+ };
2152
+ }, [
2153
+ fieldTarget,
2154
+ selectedId,
2155
+ sectionNodes
2156
+ ]);
2157
+ const inspector = {
2158
+ active: inspecting,
2159
+ activate: () => setInspecting(true),
2160
+ deactivate: stopInspecting,
2161
+ toggle: () => inspecting ? stopInspecting() : setInspecting(true),
2162
+ picked,
2163
+ highlight: setFieldTarget
2164
+ };
2165
+ const onInspectMove = (section, overlay, x, y) => {
2166
+ const element = pickElement(section, overlay, x, y);
2167
+ setHoverRect(element ? viewportRect(element) : null);
2168
+ };
2169
+ const onInspectPick = (sectionId, section, overlay, x, y) => {
2170
+ const id = pickElement(section, overlay, x, y)?.getAttribute(DATA_ATTR.ID);
2171
+ if (!id) return;
2172
+ const pick = {
2173
+ id,
2174
+ sectionId
2175
+ };
2176
+ selectOnly(sectionId);
2177
+ setPicked(pick);
2178
+ stopInspecting();
2179
+ onNodePick?.(pick);
1873
2180
  };
1874
- const press = (action) => (e) => {
1875
- e.stopPropagation();
1876
- action?.();
2181
+ return {
2182
+ inspector,
2183
+ inspecting,
2184
+ onInspectMove,
2185
+ onInspectPick,
2186
+ onInspectLeave: () => setHoverRect(null),
2187
+ highlightRect: inspecting ? hoverRect : fieldTarget ? fieldRect : null
1877
2188
  };
1878
- return /* @__PURE__ */ jsxs("div", {
1879
- ref: setRefs,
1880
- style,
1881
- ...attributes,
1882
- ...listeners,
1883
- onKeyDown,
1884
- onClick,
1885
- className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isNewItemOver && "border-t-4 border-t-green-500"),
1886
- children: [
1887
- /* @__PURE__ */ jsx("div", {
1888
- className: cn("absolute inset-0 z-50"),
1889
- onPointerMove: inspecting ? (e) => onInspectMove?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY) : void 0,
1890
- onPointerLeave: inspecting ? onInspectLeave : void 0,
1891
- onClick: inspecting ? (e) => {
1892
- e.stopPropagation();
1893
- onInspectPick?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY);
1894
- } : void 0
1895
- }),
1896
- children,
1897
- selected && /* @__PURE__ */ jsxs(Space, {
1898
- className: cn("absolute top-1 right-1 z-60"),
1899
- children: [
1900
- onMoveUp && /* @__PURE__ */ jsx(Button, {
1901
- icon: /* @__PURE__ */ jsx(ChevronUp, {}),
1902
- disabled: !canMoveUp,
1903
- onClick: press(onMoveUp),
1904
- "aria-label": "Move section up"
1905
- }),
1906
- onMoveDown && /* @__PURE__ */ jsx(Button, {
1907
- icon: /* @__PURE__ */ jsx(ChevronDown, {}),
1908
- disabled: !canMoveDown,
1909
- onClick: press(onMoveDown),
1910
- "aria-label": "Move section down"
1911
- }),
1912
- /* @__PURE__ */ jsx(Button, {
1913
- icon: /* @__PURE__ */ jsx(Copy, {}),
1914
- onClick: onCopy,
1915
- "aria-label": "Duplicate section"
1916
- }),
1917
- /* @__PURE__ */ jsx(Button, {
1918
- danger: true,
1919
- icon: /* @__PURE__ */ jsx(Trash, {}),
1920
- onClick: onDelete,
1921
- "aria-label": "Delete section"
1922
- })
1923
- ]
1924
- })
1925
- ]
1926
- });
1927
- };
1928
- //#endregion
1929
- //#region src/components/dnd/overlay.tsx
1930
- const Overlay = ({ sections, isForced, renderProps }) => {
1931
- const { active } = useDndContext();
1932
- if (!active) return null;
1933
- if (active.data.current?.type === "new-item") {
1934
- const item = active.data.current.item;
1935
- return /* @__PURE__ */ jsx(DefaultDraggableItem, { item });
1936
- }
1937
- const section = sections.find((s) => s.id === active.id);
1938
- if (section) {
1939
- const preview = generateSection(section.code, renderProps.fullCode, { containerId: renderProps.containerId });
1940
- return /* @__PURE__ */ jsx(Sortable, {
1941
- id: section.id,
1942
- name: section.name,
1943
- children: /* @__PURE__ */ jsx(renderer_default, {
1944
- preview,
1945
- sectionId: section.id,
1946
- sectionName: section.name,
1947
- sectionCode: section.code,
1948
- forceFallback: isForced?.(section) ?? false,
1949
- modules: renderProps.modules,
1950
- frame: renderProps.frame,
1951
- dynamicTailwind: renderProps.dynamicTailwind
1952
- })
1953
- });
1954
- }
1955
- return null;
1956
2189
  };
1957
2190
  //#endregion
1958
- //#region src/components/dnd/use-section-document.ts
2191
+ //#region src/components/dnd/state/use-section-document.ts
1959
2192
  const createLastParsed = () => {
1960
2193
  let last = null;
1961
2194
  return {
@@ -2008,7 +2241,7 @@ const useSectionDocument = (value, onChange, { containerId, sectionNameFallback,
2008
2241
  view.document
2009
2242
  ]);
2010
2243
  const selectedIndex = sections.findIndex((s) => s.id === selectedId);
2011
- const selectedItem = selectedIndex >= 0 ? sections[selectedIndex] : void 0;
2244
+ const selectedSection = selectedIndex >= 0 ? sections[selectedIndex] : void 0;
2012
2245
  const pendingRef = useRef(null);
2013
2246
  useEffect(() => {
2014
2247
  pendingRef.current = null;
@@ -2054,17 +2287,17 @@ const useSectionDocument = (value, onChange, { containerId, sectionNameFallback,
2054
2287
  sections,
2055
2288
  previews,
2056
2289
  selectedId,
2057
- selectedItem,
2290
+ selectedSection,
2058
2291
  selectedIndex,
2059
2292
  select: useCallback((id) => {
2060
2293
  setSelectedId((prev) => prev === id ? null : id);
2061
2294
  }, []),
2062
2295
  selectOnly: useCallback((id) => setSelectedId(id), []),
2063
2296
  clearSelection: useCallback(() => setSelectedId(null), []),
2064
- add: useCallback((item, atIndex) => {
2297
+ add: useCallback((section, atIndex) => {
2065
2298
  if (refuse()) return;
2066
2299
  const current = latestSections();
2067
- const next = { code: item.code };
2300
+ const next = { code: section.code };
2068
2301
  commit(atIndex === void 0 || atIndex < 0 ? [...current, next] : [
2069
2302
  ...current.slice(0, atIndex),
2070
2303
  next,
@@ -2148,85 +2381,69 @@ const useSectionDocument = (value, onChange, { containerId, sectionNameFallback,
2148
2381
  };
2149
2382
  //#endregion
2150
2383
  //#region src/components/dnd/dnd.tsx
2151
- const describeUpdateFailure = (failure, label) => {
2384
+ const describeUpdateFailure = (failure, label, { editErrors: m }) => {
2152
2385
  switch (failure?.reason) {
2153
2386
  case "attribute-not-found": return {
2154
- title: `Failed to update "${label}"`,
2155
- description: `This element has no "${failure.property}" attribute — check the property in its data-binding.`
2387
+ title: m.updateFailed(label),
2388
+ description: m.attributeNotFound(failure.property)
2156
2389
  };
2157
2390
  case "binding-not-declared": return {
2158
- title: `Failed to update "${label}"`,
2159
- description: `No binding for ${failure.property ? `property "${failure.property}"` : `label "${label}"`} is declared on this element's data-binding.`
2391
+ title: m.updateFailed(label),
2392
+ description: m.bindingNotDeclared({
2393
+ label,
2394
+ property: failure.property
2395
+ })
2160
2396
  };
2161
2397
  case "duplicate-binding": return {
2162
- title: `Failed to update "${label}"`,
2163
- description: `${failure.count} bindings share ${failure.property ? `property "${failure.property}"` : `label "${label}"`} on this element — remove the duplicate in its data-binding.`
2398
+ title: m.updateFailed(label),
2399
+ description: m.duplicateBinding({
2400
+ count: failure.count,
2401
+ label,
2402
+ property: failure.property
2403
+ })
2164
2404
  };
2165
2405
  case "reserved-property": return {
2166
- title: `Cannot edit "${label}" in the panel`,
2167
- description: `"${failure.property}" is managed by the editor, so a binding can't change it.`
2406
+ title: m.cannotEdit(label),
2407
+ description: m.reservedProperty(failure.property)
2168
2408
  };
2169
2409
  case "required-property": return {
2170
- title: `Cannot remove "${label}"`,
2171
- description: `"${failure.property}" is marked required in its data-binding, so it can't be removed.`
2410
+ title: m.cannotRemove(label),
2411
+ description: m.requiredProperty(failure.property)
2172
2412
  };
2173
2413
  case "no-binding": return {
2174
- title: `Failed to update "${label}"`,
2175
- description: "This element has no data-binding declaration."
2414
+ title: m.updateFailed(label),
2415
+ description: m.noBinding
2176
2416
  };
2177
2417
  case "element-not-found": return {
2178
- title: `Failed to update "${label}"`,
2179
- description: "The target element could not be found in this section."
2418
+ title: m.updateFailed(label),
2419
+ description: m.elementNotFound
2420
+ };
2421
+ case "self-closing": return {
2422
+ title: m.cannotEdit(label),
2423
+ description: m.selfClosing(failure.property)
2180
2424
  };
2181
2425
  case "unsupported-syntax": return {
2182
- title: `Cannot edit "${label}" in the panel`,
2183
- description: `The "${failure.property}" expression was preserved. Change it in the code editor instead.`
2426
+ title: m.cannotEdit(label),
2427
+ description: m.unsupportedSyntax(failure.property)
2184
2428
  };
2185
2429
  case "parse-error": return {
2186
- title: `Failed to update "${label}"`,
2187
- description: failure.error instanceof Error ? failure.error.message : "Check the console for details."
2430
+ title: m.updateFailed(label),
2431
+ description: failure.error instanceof Error ? failure.error.message : m.checkConsole
2188
2432
  };
2189
- default: return { title: `Failed to update "${label}"` };
2433
+ default: return { title: m.updateFailed(label) };
2190
2434
  }
2191
2435
  };
2192
- const KEYBOARD_CODES = {
2193
- start: ["Space"],
2194
- cancel: ["Escape"],
2195
- end: ["Space", "Enter"]
2196
- };
2197
- 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." };
2198
- const createNodeRegistry = () => {
2199
- const nodes = /* @__PURE__ */ new Map();
2200
- const callbacks = /* @__PURE__ */ new Map();
2201
- return {
2202
- get: (id) => nodes.get(id),
2203
- register: (id) => {
2204
- let callback = callbacks.get(id);
2205
- if (!callback) {
2206
- callback = (node) => {
2207
- if (node) nodes.set(id, node);
2208
- else {
2209
- nodes.delete(id);
2210
- callbacks.delete(id);
2211
- }
2212
- };
2213
- callbacks.set(id, callback);
2214
- }
2215
- return callback;
2216
- }
2217
- };
2218
- };
2219
- const blockedEditError = (error) => ({
2436
+ const blockedEditError = (error, { editErrors: m }) => ({
2220
2437
  type: "parse",
2221
2438
  target: "document",
2222
2439
  reason: "parse-error",
2223
2440
  error,
2224
- title: "The document has a syntax error",
2225
- description: "The canvas shows the last version that parsed. Fix the error in the code, then edit here again."
2441
+ title: m.syntaxError,
2442
+ description: m.syntaxErrorDetail
2226
2443
  });
2227
2444
  const conditionalModifiers = (args) => {
2228
2445
  const { active } = args;
2229
- if (active?.data.current?.type === "new-item") return args.transform;
2446
+ if (paletteSectionOf(active?.data)) return args.transform;
2230
2447
  return restrictToVerticalAxis(args);
2231
2448
  };
2232
2449
  const NO_MODULES = {};
@@ -2244,179 +2461,56 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2244
2461
  return false;
2245
2462
  }
2246
2463
  };
2247
- const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }), useSensor(KeyboardSensor, {
2248
- coordinateGetter: sortableKeyboardCoordinates,
2249
- keyboardCodes: KEYBOARD_CODES
2250
- }));
2251
- const nameOf = (id, data) => {
2252
- return (data?.current)?.item?.name ?? sections.find((section) => section.id === id)?.name ?? "section";
2253
- };
2254
- const positionOf = (id) => sections.findIndex((section) => section.id === id) + 1;
2255
- const announcements = {
2256
- onDragStart: ({ active }) => `Picked up ${nameOf(active.id, active.data)}.`,
2257
- 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.`,
2258
- 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.`,
2259
- onDragCancel: ({ active }) => `Moving ${nameOf(active.id, active.data)} was cancelled.`
2260
- };
2261
2464
  const value = _value === void 0 ? code : _value;
2465
+ const messages = useLiveMessages();
2262
2466
  const reportError = onEditError ?? toastEditError;
2263
2467
  const reportErrorRef = useRef(reportError);
2468
+ const messagesRef = useRef(messages);
2264
2469
  useEffect(() => {
2265
2470
  reportErrorRef.current = reportError;
2471
+ messagesRef.current = messages;
2266
2472
  });
2267
2473
  const onBlockedEdit = useCallback((problem) => {
2268
- if (problem.reason === "parse-error") reportErrorRef.current(blockedEditError(problem.error));
2474
+ if (problem.reason === "parse-error") reportErrorRef.current(blockedEditError(problem.error, messagesRef.current));
2269
2475
  }, []);
2270
- const { sections, previews, selectedId, selectedItem, selectedIndex, select, selectOnly, clearSelection, add: addItem, remove, copy: onCopy, move: moveSection, reorder, patch, getCommittedSection, problem, stale } = useSectionDocument(value, _onChange, {
2476
+ const { sections, previews, selectedId, selectedSection, selectedIndex, select, selectOnly, clearSelection, add: addSection, remove, copy: onCopy, move: moveSection, reorder, patch, getCommittedSection, problem, stale } = useSectionDocument(value, _onChange, {
2271
2477
  containerId,
2272
2478
  sectionNameFallback,
2273
2479
  onBlockedEdit
2274
2480
  });
2275
2481
  const missingContainer = problem?.reason === "container-not-found" ? problem.containerId : null;
2276
- const onDragStart = (_) => {};
2277
- const onDragOver = (_e) => {};
2278
2482
  const onDragEnd = (event) => {
2279
2483
  const { active, over } = event;
2280
2484
  if (!over) return;
2281
- if (active.data.current?.type === "new-item") {
2282
- const item = active.data.current.item;
2485
+ const section = paletteSectionOf(active.data);
2486
+ if (section) {
2283
2487
  const atBottom = over.id === "sortable-area" || over.id === "sortable-area-bottom";
2284
- addItem(item, atBottom ? void 0 : sections.findIndex((s) => s.id === over.id));
2488
+ addSection(section, atBottom ? void 0 : sections.findIndex((s) => s.id === over.id));
2285
2489
  return;
2286
2490
  }
2287
2491
  if (active.id !== over.id && sections.some((s) => s.id === active.id)) reorder(String(active.id), String(over.id));
2288
2492
  };
2289
2493
  const onSelect = (id) => select(id);
2290
- const [inspecting, setInspecting] = useState(false);
2291
- const [picked, setPicked] = useState(null);
2292
- const [highlight, setHighlight] = useState(null);
2293
- const [fieldTarget, setFieldTarget] = useState(null);
2294
- const [fieldRect, setFieldRect] = useState(null);
2295
- if (picked && picked.sectionId !== selectedId) setPicked(null);
2296
- const stopInspecting = useCallback(() => {
2297
- setInspecting(false);
2298
- setHighlight(null);
2299
- }, []);
2300
- useEffect(() => {
2301
- if (!inspecting) return;
2302
- const onKeyDown = (e) => {
2303
- if (e.key === "Escape") stopInspecting();
2304
- };
2305
- window.addEventListener("keydown", onKeyDown);
2306
- return () => window.removeEventListener("keydown", onKeyDown);
2307
- }, [inspecting, stopInspecting]);
2308
- const inspector = {
2309
- active: inspecting,
2310
- activate: () => setInspecting(true),
2311
- deactivate: stopInspecting,
2312
- toggle: () => inspecting ? stopInspecting() : setInspecting(true),
2313
- picked,
2314
- highlight: setFieldTarget
2315
- };
2316
- const onInspectMove = (section, overlay, x, y) => {
2317
- const element = pickElement(section, overlay, x, y);
2318
- setHighlight(element ? viewportRect(element) : null);
2319
- };
2320
- const onInspectPick = (sectionId, section, overlay, x, y) => {
2321
- const id = pickElement(section, overlay, x, y)?.getAttribute(DATA_ATTR.ID);
2322
- if (!id) return;
2323
- const pick = {
2324
- id,
2325
- sectionId
2326
- };
2327
- selectOnly(sectionId);
2328
- setPicked(pick);
2329
- stopInspecting();
2330
- onNodePick?.(pick);
2331
- };
2332
- const removeRef = useRef(remove);
2333
- useEffect(() => {
2334
- removeRef.current = remove;
2494
+ const requestDelete = useDeleteFlow({
2495
+ sections,
2496
+ remove,
2497
+ onBeforeDelete
2335
2498
  });
2336
- const requestDelete = (id, onDeleted) => {
2337
- const section = sections.find((candidate) => candidate.id === id);
2338
- if (!onBeforeDelete || !section) {
2339
- remove(id);
2340
- onDeleted?.();
2341
- return;
2342
- }
2343
- const decide = (allowed) => {
2344
- if (allowed) {
2345
- removeRef.current(id);
2346
- onDeleted?.();
2347
- }
2348
- };
2349
- try {
2350
- const answer = onBeforeDelete(section);
2351
- if (typeof answer === "boolean") {
2352
- decide(answer);
2353
- return;
2354
- }
2355
- answer.then(decide, (error) => {
2356
- console.error("onBeforeDelete rejected; the section was kept", error);
2357
- });
2358
- } catch (error) {
2359
- console.error("onBeforeDelete threw; the section was kept", error);
2360
- }
2361
- };
2362
2499
  const onDelete = (id) => requestDelete(id);
2363
- const [sectionNodes] = useState(createNodeRegistry);
2364
- const focusSection = (id) => sectionNodes.get(id)?.focus();
2365
- const onNavigate = (fromId, to) => {
2366
- const from = sections.findIndex((section) => section.id === fromId);
2367
- const index = to === "first" ? 0 : to === "last" ? sections.length - 1 : from + (to === "next" ? 1 : -1);
2368
- const target = sections[index];
2369
- if (from < 0 || !target || target.id === fromId) return;
2370
- selectOnly(target.id);
2371
- focusSection(target.id);
2372
- };
2373
- const onDeleteKey = (id) => {
2374
- const index = sections.findIndex((section) => section.id === id);
2375
- const neighbor = sections[index + 1] ?? sections[index - 1];
2376
- requestDelete(id, () => {
2377
- if (neighbor) focusSection(neighbor.id);
2378
- });
2379
- };
2380
- const onMoveButton = (id, direction) => {
2381
- const to = sections.findIndex((section) => section.id === id) + (direction === "up" ? -1 : 1);
2382
- moveSection(id, direction);
2383
- if (to === 0 || to === sections.length - 1) focusSection(id);
2384
- };
2385
- useEffect(() => {
2386
- const section = selectedId === null ? null : sectionNodes.get(selectedId);
2387
- if (!fieldTarget || !section) return;
2388
- let frame = 0;
2389
- const measure = () => {
2390
- cancelAnimationFrame(frame);
2391
- frame = requestAnimationFrame(() => {
2392
- const element = findElementById(section, fieldTarget);
2393
- setFieldRect(element ? viewportRect(element) : null);
2394
- });
2395
- };
2396
- measure();
2397
- window.addEventListener("scroll", measure, true);
2398
- window.addEventListener("resize", measure);
2399
- return () => {
2400
- cancelAnimationFrame(frame);
2401
- window.removeEventListener("scroll", measure, true);
2402
- window.removeEventListener("resize", measure);
2403
- };
2404
- }, [
2405
- fieldTarget,
2500
+ const { sensors, announcements, screenReaderInstructions, sectionNodes, onNavigate, onDeleteKey, onMoveButton } = useDndKeyboard({
2501
+ sections,
2406
2502
  selectedId,
2407
- sectionNodes
2408
- ]);
2409
- useEffect(() => {
2410
- if (selectedId === null) return;
2411
- const frame = requestAnimationFrame(() => {
2412
- sectionNodes.get(selectedId)?.scrollIntoView?.({ block: "nearest" });
2413
- });
2414
- return () => cancelAnimationFrame(frame);
2415
- }, [
2416
- sectionNodes,
2503
+ selectedIndex,
2504
+ selectOnly,
2505
+ move: moveSection,
2506
+ requestDelete
2507
+ });
2508
+ const { inspector, inspecting, onInspectMove, onInspectPick, onInspectLeave, highlightRect } = useInspectorState({
2417
2509
  selectedId,
2418
- selectedIndex
2419
- ]);
2510
+ selectOnly,
2511
+ sectionNodes,
2512
+ onNodePick
2513
+ });
2420
2514
  const onChange = (next) => {
2421
2515
  if (!next.id) return;
2422
2516
  patch(next);
@@ -2425,8 +2519,8 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2425
2519
  bindings: bindingRegistry,
2426
2520
  bindingKeys
2427
2521
  }), [bindingRegistry, bindingKeys]);
2428
- const selectedCode = selectedItem?.code;
2429
- const selectedSectionId = selectedItem?.id;
2522
+ const selectedCode = selectedSection?.code;
2523
+ const selectedSectionId = selectedSection?.id;
2430
2524
  const { fields, updatedCode, parseError } = useMemo(() => {
2431
2525
  if (!selectedCode) return {
2432
2526
  fields: [],
@@ -2459,8 +2553,8 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2459
2553
  target: "document",
2460
2554
  reason: "container-not-found",
2461
2555
  containerId: missingContainer,
2462
- title: `No #${missingContainer} element in the document`,
2463
- description: `Sections are the <section> elements inside the element with id="${missingContainer}". Start from createDocument(), or set containerId to match your document.`
2556
+ title: messagesRef.current.editErrors.missingContainer(missingContainer),
2557
+ description: messagesRef.current.editErrors.missingContainerDetail(missingContainer)
2464
2558
  });
2465
2559
  }, [missingContainer]);
2466
2560
  useEffect(() => {
@@ -2468,17 +2562,17 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2468
2562
  type: "parse",
2469
2563
  target: "section",
2470
2564
  error: parseError.error,
2471
- title: "Failed to parse this section",
2472
- description: "Check the console for details."
2565
+ title: messagesRef.current.editErrors.sectionParseFailed,
2566
+ description: messagesRef.current.editErrors.checkConsole
2473
2567
  });
2474
2568
  }, [parseError]);
2475
2569
  const commitChanges = (changes) => {
2476
2570
  if (changes.length === 0) return;
2477
2571
  if (stale && problem?.reason === "parse-error") {
2478
- reportError(blockedEditError(problem.error));
2572
+ reportError(blockedEditError(problem.error, messages));
2479
2573
  return;
2480
2574
  }
2481
- const committed = selectedItem && getCommittedSection(selectedItem.id)?.code;
2575
+ const committed = selectedSection && getCommittedSection(selectedSection.id)?.code;
2482
2576
  const result = updateAll(committed && committed !== selectedCode ? committed : updatedCode, changes.map(({ id, label, property, value: changeValue }) => ({
2483
2577
  dataId: id,
2484
2578
  label,
@@ -2493,12 +2587,12 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2493
2587
  label,
2494
2588
  property,
2495
2589
  failure: result.failure,
2496
- ...describeUpdateFailure(result.failure, label)
2590
+ ...describeUpdateFailure(result.failure, label, messages)
2497
2591
  });
2498
2592
  return;
2499
2593
  }
2500
- if (selectedItem) onChange({
2501
- ...selectedItem,
2594
+ if (selectedSection) onChange({
2595
+ ...selectedSection,
2502
2596
  code: result.code
2503
2597
  });
2504
2598
  };
@@ -2509,14 +2603,14 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2509
2603
  if (frame?.scripts?.length) preloadScripts(frame.scripts);
2510
2604
  }, [frame?.scripts]);
2511
2605
  const palette = {
2512
- items: items?.length ? items : DRAGGABLE_ITEMS,
2513
- onAdd: (item) => {
2514
- addItem(item);
2606
+ items: items?.length ? items : PALETTE_SECTIONS,
2607
+ onAdd: (section) => {
2608
+ addSection(section);
2515
2609
  setMobilePaletteOpen(false);
2516
2610
  }
2517
2611
  };
2518
2612
  const panel = {
2519
- item: selectedItem,
2613
+ item: selectedSection,
2520
2614
  onChange,
2521
2615
  onDelete,
2522
2616
  onMoveUp: () => moveSection(selectedId, "up"),
@@ -2529,71 +2623,67 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2529
2623
  readOnly: stale,
2530
2624
  bindingOptions
2531
2625
  };
2532
- const canvas = /* @__PURE__ */ jsxs(Fragment, { children: [stale && /* @__PURE__ */ jsx("div", {
2533
- role: "status",
2534
- className: cn("sticky top-0 z-70", "border-b border-amber-200 bg-amber-50 px-3 py-2", "text-sm text-amber-900"),
2535
- children: "Showing the last version that parsed. Fix the syntax error in the code to edit here again."
2536
- }), /* @__PURE__ */ jsx(Droppable, {
2537
- className: cn(!sections.length && "h-full"),
2538
- children: !sections.length ? /* @__PURE__ */ jsx("div", {
2539
- className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
2540
- children: problem?.reason === "parse-error" ? /* @__PURE__ */ jsxs(Space, {
2541
- orientation: "vertical",
2542
- align: "center",
2543
- 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" })]
2544
- }) : missingContainer !== null ? /* @__PURE__ */ jsxs(Space, {
2545
- orientation: "vertical",
2546
- align: "center",
2547
- children: [/* @__PURE__ */ jsxs(Typography.Paragraph, { children: [
2548
- "No #",
2549
- missingContainer,
2550
- " element in the document"
2551
- ] }), /* @__PURE__ */ jsxs(Typography.Text, { children: [
2552
- "Sections go inside the element with id=\"",
2553
- missingContainer,
2554
- "\""
2555
- ] })]
2556
- }) : /* @__PURE__ */ jsxs(Space, {
2557
- orientation: "vertical",
2558
- align: "center",
2559
- 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" })]
2560
- })
2561
- }) : /* @__PURE__ */ jsx(SortableContext, {
2562
- items: sections.map((s) => s.id),
2563
- strategy: verticalListSortingStrategy,
2564
- children: sections.map((section, index) => /* @__PURE__ */ jsx(Sortable, {
2565
- id: section.id,
2566
- name: section.name,
2567
- selected: selectedId === section.id,
2568
- onClick: () => onSelect(section.id),
2569
- onDelete,
2570
- onCopy,
2571
- onMoveUp: () => onMoveButton(section.id, "up"),
2572
- onMoveDown: () => onMoveButton(section.id, "down"),
2573
- canMoveUp: index > 0,
2574
- canMoveDown: index < sections.length - 1,
2575
- nodeRef: sectionNodes.register(section.id),
2576
- onNavigate: (to) => onNavigate(section.id, to),
2577
- onDeleteKey: () => onDeleteKey(section.id),
2578
- inspecting,
2579
- onInspectMove,
2580
- onInspectPick: (...args) => onInspectPick(section.id, ...args),
2581
- onInspectLeave: () => setHighlight(null),
2582
- children: /* @__PURE__ */ jsx(renderer_default, {
2583
- preview: previews[index],
2584
- sectionId: section.id,
2585
- sectionName: section.name,
2586
- sectionCode: section.code,
2587
- forceFallback: isForced(section),
2588
- modules,
2589
- frame,
2590
- dynamicTailwind,
2591
- provider,
2592
- ...props
2626
+ const canvas = /* @__PURE__ */ jsxs(Fragment, { children: [
2627
+ stale && /* @__PURE__ */ jsx("div", {
2628
+ role: "status",
2629
+ className: cn("sticky top-0 z-70", "border-b border-amber-200 bg-amber-50 px-3 py-2", "text-sm text-amber-900"),
2630
+ children: messages.canvas.stale
2631
+ }),
2632
+ /* @__PURE__ */ jsx(Droppable, {
2633
+ className: cn(!sections.length && "h-full"),
2634
+ children: !sections.length ? /* @__PURE__ */ jsx("div", {
2635
+ className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
2636
+ children: problem?.reason === "parse-error" ? /* @__PURE__ */ jsxs(Space, {
2637
+ orientation: "vertical",
2638
+ align: "center",
2639
+ children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: messages.canvas.syntaxError }), /* @__PURE__ */ jsx(Typography.Text, { children: messages.canvas.syntaxErrorDetail })]
2640
+ }) : missingContainer !== null ? /* @__PURE__ */ jsxs(Space, {
2641
+ orientation: "vertical",
2642
+ align: "center",
2643
+ children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: messages.canvas.missingContainer(missingContainer) }), /* @__PURE__ */ jsx(Typography.Text, { children: messages.canvas.missingContainerDetail(missingContainer) })]
2644
+ }) : /* @__PURE__ */ jsxs(Space, {
2645
+ orientation: "vertical",
2646
+ align: "center",
2647
+ children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: messages.canvas.empty }), /* @__PURE__ */ jsx(Typography.Text, { children: isMobile ? messages.canvas.emptyHintTouch : messages.canvas.emptyHintDrag })]
2593
2648
  })
2594
- }, section.id))
2595
- })
2596
- })] });
2649
+ }) : /* @__PURE__ */ jsx(SortableContext, {
2650
+ items: sections.map((s) => s.id),
2651
+ strategy: verticalListSortingStrategy,
2652
+ children: sections.map((section, index) => /* @__PURE__ */ jsx(Sortable, {
2653
+ id: section.id,
2654
+ name: section.name,
2655
+ selected: selectedId === section.id,
2656
+ onClick: () => onSelect(section.id),
2657
+ onDelete,
2658
+ onCopy,
2659
+ onMoveUp: () => onMoveButton(section.id, "up"),
2660
+ onMoveDown: () => onMoveButton(section.id, "down"),
2661
+ canMoveUp: index > 0,
2662
+ canMoveDown: index < sections.length - 1,
2663
+ nodeRef: sectionNodes.register(section.id),
2664
+ onNavigate: (to) => onNavigate(section.id, to),
2665
+ onDeleteKey: () => onDeleteKey(section.id),
2666
+ inspecting,
2667
+ onInspectMove,
2668
+ onInspectPick: (...args) => onInspectPick(section.id, ...args),
2669
+ onInspectLeave,
2670
+ children: /* @__PURE__ */ jsx(renderer_default, {
2671
+ preview: previews[index],
2672
+ sectionId: section.id,
2673
+ sectionName: section.name,
2674
+ sectionCode: section.code,
2675
+ forceFallback: isForced(section),
2676
+ modules,
2677
+ frame,
2678
+ dynamicTailwind,
2679
+ provider,
2680
+ ...props
2681
+ })
2682
+ }, section.id))
2683
+ })
2684
+ }),
2685
+ /* @__PURE__ */ jsx(InspectorHighlight, { rect: highlightRect })
2686
+ ] });
2597
2687
  return /* @__PURE__ */ jsx(SectionFallbackContext.Provider, {
2598
2688
  value: renderSectionFallback,
2599
2689
  children: /* @__PURE__ */ jsxs(DndContext, {
@@ -2601,22 +2691,20 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2601
2691
  collisionDetection: closestCenter,
2602
2692
  accessibility: {
2603
2693
  announcements,
2604
- screenReaderInstructions: SCREEN_READER_INSTRUCTIONS
2694
+ screenReaderInstructions
2605
2695
  },
2606
2696
  modifiers: [conditionalModifiers],
2607
- onDragStart,
2608
- onDragOver,
2609
2697
  onDragEnd,
2610
2698
  children: [/* @__PURE__ */ jsx("div", {
2611
2699
  className: cn("relative flex h-full w-full", className),
2612
2700
  ...restProps,
2613
- children: /* @__PURE__ */ jsxs(DndEditOptionsContext.Provider, {
2701
+ children: /* @__PURE__ */ jsx(DndEditOptionsContext.Provider, {
2614
2702
  value: {
2615
2703
  renderField,
2616
2704
  reportError,
2617
2705
  bindingOptions
2618
2706
  },
2619
- children: [/* @__PURE__ */ jsx(DndRegionContext.Provider, {
2707
+ children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
2620
2708
  value: {
2621
2709
  palette,
2622
2710
  panel,
@@ -2632,7 +2720,7 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
2632
2720
  value: inspector,
2633
2721
  children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
2634
2722
  })
2635
- }), /* @__PURE__ */ jsx(InspectorHighlight, { rect: inspecting ? highlight : fieldTarget ? fieldRect : null })]
2723
+ })
2636
2724
  })
2637
2725
  }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
2638
2726
  sections,
@@ -2663,6 +2751,6 @@ const useItemsEditor = useDndItems;
2663
2751
  /** @deprecated Renamed to `useDndChildren`. */
2664
2752
  const useChildrenEditor = useDndChildren;
2665
2753
  //#endregion
2666
- 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 };
2754
+ 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, useDndInspector as v, DraggableItem as y };
2667
2755
 
2668
- //# sourceMappingURL=dnd-BE5ZizqX.js.map
2756
+ //# sourceMappingURL=dnd-UoJILA75.js.map