@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,5 +1,5 @@
1
- import { C as require_lib, S as require_lib$1, _ as nanoid, b as unwrap, d as traverse, g as getJSXTagName, v as attrValue, w as __toESM, x as wrap, y as generateCode } from "./document-BDn_f08_.js";
2
- import { c as RESERVED_BINDING_PROPERTIES, n as CONFIG, r as DATA_ATTR, s as REGEX, t as BINDING_PROP } from "./constants-h1JAgOeW.js";
1
+ import { C as require_lib, S as require_lib$1, _ as generateCode, b as getJSXTagName, d as traverse, g as attrValue, v as unwrap, w as __toESM, x as nanoid, y as wrap } from "./document-CGbe6bSV.js";
2
+ import { c as RESERVED_BINDING_PROPERTIES, n as CONFIG, r as DATA_ATTR, s as REGEX, t as BINDING_PROP } from "./constants-DU17Tdwo.js";
3
3
  import { n as registerEditorCache } from "./editor-caches-ClMvdcQF.js";
4
4
  import { t as createBoundedCache } from "./cache-B0-XcIxJ.js";
5
5
  import { z } from "zod";
@@ -95,86 +95,6 @@ const parseValue = (value) => {
95
95
  }
96
96
  return value;
97
97
  };
98
- const MAX_FLATTEN_DEPTH = 20;
99
- const isEditablePrimitive = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
100
- const flattenEditableValue = (value) => {
101
- const parsed = parseValue(value);
102
- if (typeof parsed !== "object" || parsed === null || Object.keys(parsed).length === 0) return null;
103
- const entries = [];
104
- const walk = (node, path, depth) => {
105
- if (depth > MAX_FLATTEN_DEPTH) return;
106
- if (isEditablePrimitive(node)) {
107
- entries.push({
108
- path,
109
- value: node
110
- });
111
- return;
112
- }
113
- if (Array.isArray(node)) {
114
- node.forEach((item, index) => walk(item, [...path, index], depth + 1));
115
- return;
116
- }
117
- if (typeof node === "object" && node !== null) Object.entries(node).forEach(([key, item]) => walk(item, [...path, key], depth + 1));
118
- };
119
- walk(parsed, [], 0);
120
- return entries.length ? entries : null;
121
- };
122
- const findEditableLeaf = (root, path) => {
123
- let node = unwrapExpression(root);
124
- for (const segment of path) {
125
- if (import_lib$1.isArrayExpression(node)) {
126
- if (typeof segment !== "number") return null;
127
- const elements = node.elements.slice(0, segment + 1);
128
- const element = node.elements[segment];
129
- if (!element || elements.some((item) => import_lib$1.isSpreadElement(item))) return null;
130
- node = unwrapExpression(element);
131
- continue;
132
- }
133
- if (import_lib$1.isObjectExpression(node)) {
134
- const key = String(segment);
135
- const matches = (property) => import_lib$1.isObjectProperty(property) && !property.computed && (import_lib$1.isIdentifier(property.key) && property.key.name === key || import_lib$1.isStringLiteral(property.key) && property.key.value === key || import_lib$1.isNumericLiteral(property.key) && String(property.key.value) === key);
136
- let index = node.properties.length - 1;
137
- while (index >= 0 && !matches(node.properties[index])) index--;
138
- const property = node.properties[index];
139
- if (!property || !import_lib$1.isObjectProperty(property) || node.properties.slice(index + 1).some((item) => import_lib$1.isSpreadElement(item))) return null;
140
- node = unwrapExpression(property.value);
141
- continue;
142
- }
143
- return null;
144
- }
145
- return node;
146
- };
147
- const isEditableLeaf = (node) => import_lib$1.isStringLiteral(node) || import_lib$1.isNumericLiteral(node) || import_lib$1.isBooleanLiteral(node) || import_lib$1.isUnaryExpression(node) && node.operator === "-" && import_lib$1.isNumericLiteral(node.argument) || import_lib$1.isTemplateLiteral(node) && node.expressions.length === 0 || import_lib$1.isJSXElement(node) || import_lib$1.isJSXFragment(node);
148
- const quoteString = (text, quote) => {
149
- if (quote === "`") return `\`${text.replace(/[\\`]|\$\{/g, (match) => `\\${match}`)}\``;
150
- const escaped = JSON.stringify(text).slice(1, -1);
151
- return quote === "\"" ? `"${escaped}"` : `'${escaped.replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
152
- };
153
- const isJSXSource = (text) => {
154
- try {
155
- const expression = (0, import_lib.parseExpression)(text, { plugins: ["jsx"] });
156
- return import_lib$1.isJSXElement(expression) || import_lib$1.isJSXFragment(expression);
157
- } catch {
158
- return false;
159
- }
160
- };
161
- const leafSource = (value, leaf, next) => {
162
- if (typeof next !== "string") return String(next);
163
- if ((import_lib$1.isJSXElement(leaf) || import_lib$1.isJSXFragment(leaf)) && isJSXSource(next)) return next.trim();
164
- const quote = import_lib$1.isStringLiteral(leaf) || import_lib$1.isTemplateLiteral(leaf) ? value[leaf.start] : "'";
165
- return quoteString(next, quote);
166
- };
167
- const setEditableValue = (value, path, next) => {
168
- if (!path.length) return value;
169
- let leaf;
170
- try {
171
- leaf = findEditableLeaf((0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] }), path);
172
- } catch {
173
- return value;
174
- }
175
- if (!leaf || !isEditableLeaf(leaf) || leaf.start == null || leaf.end == null) return value;
176
- return `${value.slice(0, leaf.start)}${leafSource(value, leaf, next)}${value.slice(leaf.end)}`;
177
- };
178
98
  const extractNodeValue = (node) => {
179
99
  if (import_lib$1.isBooleanLiteral(node)) return {
180
100
  type: "boolean",
@@ -223,35 +143,6 @@ const extractNodeValue = (node) => {
223
143
  value: null
224
144
  };
225
145
  };
226
- const createNodeFromValue = (type, value) => {
227
- switch (type) {
228
- case "boolean": return import_lib$1.booleanLiteral(value === true);
229
- case "number": return import_lib$1.numericLiteral(Number(value));
230
- case "string": return import_lib$1.stringLiteral(String(value));
231
- case "null": return import_lib$1.nullLiteral();
232
- case "array":
233
- case "object":
234
- case "unknown": return null;
235
- default: return null;
236
- }
237
- };
238
- const valueToExpression = (value) => {
239
- if (typeof value === "string") return import_lib$1.stringLiteral(value);
240
- if (typeof value === "number") return value < 0 ? import_lib$1.unaryExpression("-", import_lib$1.numericLiteral(-value)) : import_lib$1.numericLiteral(value);
241
- if (typeof value === "boolean") return import_lib$1.booleanLiteral(value);
242
- if (value === null) return import_lib$1.nullLiteral();
243
- if (Array.isArray(value)) return import_lib$1.arrayExpression(value.map((item) => valueToExpression(item) ?? import_lib$1.nullLiteral()));
244
- if (typeof value === "object") {
245
- const properties = [];
246
- for (const [key, item] of Object.entries(value)) {
247
- const expr = valueToExpression(item);
248
- if (expr === null) continue;
249
- properties.push(import_lib$1.objectProperty(import_lib$1.stringLiteral(key), expr));
250
- }
251
- return import_lib$1.objectExpression(properties);
252
- }
253
- return null;
254
- };
255
146
  const parseArrayExpression = (value) => {
256
147
  try {
257
148
  const ast = unwrapExpression((0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] }));
@@ -277,10 +168,6 @@ const extractObjectProperties = (element) => {
277
168
  });
278
169
  return properties;
279
170
  };
280
- const arrayExpressionToCode = (elements) => {
281
- const nextAst = import_lib$1.arrayExpression(elements);
282
- return generateCode(nextAst);
283
- };
284
171
  //#endregion
285
172
  //#region src/utils/ast/binding.ts
286
173
  const bindingTypeSchema = z.enum(BINDING_TYPES);
@@ -496,431 +383,100 @@ const findEditableChildren = (node) => {
496
383
  return editableChildren;
497
384
  };
498
385
  //#endregion
499
- //#region src/utils/selection.ts
500
- const removeIndices = (items, indices) => {
501
- return items.filter((_, index) => !indices.has(index));
386
+ //#region src/utils/ast/extract.ts
387
+ const collectText = (children) => {
388
+ return children.filter((c) => import_lib$1.isJSXText(c)).map((c) => c.value.trim()).filter((v) => v.length).join(" ");
502
389
  };
503
- /**
504
- * Shifts every selected index up/down by one step as a block, preserving
505
- * relative order — scattered selections stop moving individually once they
506
- * hit an unselected neighbor, so the whole group slides together instead of
507
- * items passing through each other.
508
- */
509
- const moveSelectedIndices = (items, indices, direction) => {
510
- const next = [...items];
511
- const nextIndices = new Set(indices);
512
- const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
513
- for (const index of ordered) {
514
- const target = direction === "up" ? index - 1 : index + 1;
515
- if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
516
- [next[index], next[target]] = [next[target], next[index]];
517
- nextIndices.delete(index);
518
- nextIndices.add(target);
519
- }
520
- return {
521
- items: next,
522
- indices: nextIndices
523
- };
390
+ const parseJSXName = (tagName) => {
391
+ const parts = tagName.split(".");
392
+ if (parts.length === 1) return import_lib$1.jsxIdentifier(parts[0]);
393
+ let expr = import_lib$1.jsxIdentifier(parts[0]);
394
+ for (let i = 1; i < parts.length; i++) expr = import_lib$1.jsxMemberExpression(expr, import_lib$1.jsxIdentifier(parts[i]));
395
+ return expr;
524
396
  };
525
- //#endregion
526
- //#region src/utils/ast/patch.ts
527
- const hasOnlyLayoutNewlines = (content) => {
528
- let index = 0;
529
- while (index < content.length) {
530
- const char = content[index];
531
- if (char === "`" || char === "<") return false;
532
- if (char === "\"" || char === "'") {
533
- const next = skipStringLiteral(content, index);
534
- if (next === -1) return false;
535
- index = next;
536
- continue;
537
- }
538
- if (char === "/") {
539
- const following = content[index + 1];
540
- if (following === "/") {
541
- const lineEnd = content.indexOf("\n", index + 2);
542
- index = lineEnd === -1 ? content.length : lineEnd;
543
- continue;
544
- }
545
- if (following === "*") {
546
- const commentEnd = content.indexOf("*/", index + 2);
547
- if (commentEnd === -1) return false;
548
- index = commentEnd + 2;
549
- continue;
550
- }
551
- return false;
552
- }
553
- index++;
397
+ const extractCache = createBoundedCache(CONFIG.CACHE_LIMIT);
398
+ const NO_MAP = {};
399
+ let optionExtractCaches = /* @__PURE__ */ new WeakMap();
400
+ const cacheFor = ({ bindings, bindingKeys }) => {
401
+ if (!bindings && !bindingKeys) return extractCache;
402
+ let byKeys = optionExtractCaches.get(bindings ?? NO_MAP);
403
+ if (!byKeys) {
404
+ byKeys = /* @__PURE__ */ new WeakMap();
405
+ optionExtractCaches.set(bindings ?? NO_MAP, byKeys);
554
406
  }
555
- return true;
407
+ let cache = byKeys.get(bindingKeys ?? NO_MAP);
408
+ if (!cache) {
409
+ cache = createBoundedCache(CONFIG.CACHE_LIMIT);
410
+ byKeys.set(bindingKeys ?? NO_MAP, cache);
411
+ }
412
+ return cache;
556
413
  };
557
- const skipStringLiteral = (content, start) => {
558
- const quote = content[start];
559
- for (let index = start + 1; index < content.length; index++) {
560
- const char = content[index];
561
- if (char === "\\") {
562
- const escaped = content[index + 1];
563
- if (escaped === "\n" || escaped === "\r") return -1;
564
- index++;
565
- continue;
414
+ const extractAttributes = (attributes, source) => {
415
+ const allAttrs = [];
416
+ const dataAttrs = [];
417
+ for (const attr of attributes) {
418
+ if (!import_lib$1.isJSXAttribute(attr) || !import_lib$1.isJSXIdentifier(attr.name)) continue;
419
+ const name = attr.name.name;
420
+ let value = null;
421
+ let isStringLiteral = false;
422
+ if (attr.value) {
423
+ if (import_lib$1.isStringLiteral(attr.value)) {
424
+ value = attr.value.value;
425
+ isStringLiteral = true;
426
+ } else if (import_lib$1.isJSXExpressionContainer(attr.value)) try {
427
+ const expression = attr.value.expression;
428
+ const structural = import_lib$1.isArrayExpression(unwrapExpression(expression)) || import_lib$1.isObjectExpression(unwrapExpression(expression));
429
+ value = source && structural && expression.start != null && expression.end != null ? source.slice(expression.start, expression.end) : generateCode(expression);
430
+ isStringLiteral = false;
431
+ } catch {
432
+ value = null;
433
+ }
566
434
  }
567
- if (char === quote) return index + 1;
568
- if (char === "\n") return -1;
435
+ const entry = {
436
+ name,
437
+ value,
438
+ isStringLiteral
439
+ };
440
+ allAttrs.push(entry);
441
+ if (name.startsWith("data-")) dataAttrs.push(entry);
569
442
  }
570
- return -1;
571
- };
572
- const lineIndentAt = (source, offset) => {
573
- const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
574
- return /^[ \t]*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
575
- };
576
- const lineTerminatorOf = (source) => {
577
- return source.includes("\r\n") ? "\r\n" : "\n";
443
+ return {
444
+ allAttrs,
445
+ dataAttrs
446
+ };
578
447
  };
579
- const applyEdits = (source, edits) => {
580
- if (edits.length === 0) return source;
581
- const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
582
- for (const edit of ordered) if (!Number.isInteger(edit.start) || !Number.isInteger(edit.end) || edit.start < 0 || edit.end > source.length || edit.start > edit.end) throw new Error(`Invalid source edit [${edit.start}, ${edit.end}) for a source of length ${source.length}`);
583
- for (let i = 1; i < ordered.length; i++) {
584
- const previous = ordered[i - 1];
585
- const current = ordered[i];
586
- if (current.start < previous.end) throw new Error(`Overlapping source edits: [${previous.start}, ${previous.end}) and [${current.start}, ${current.end})`);
587
- }
588
- let result = "";
589
- let cursor = 0;
590
- const terminator = lineTerminatorOf(source);
591
- for (const edit of ordered) {
592
- const content = edit.indent && edit.content.includes("\n") && hasOnlyLayoutNewlines(edit.content) ? edit.content.replace(/\n/g, `${terminator}${lineIndentAt(source, edit.start)}`) : edit.content;
593
- result += source.slice(cursor, edit.start) + content;
594
- cursor = edit.end;
595
- }
596
- return result + source.slice(cursor);
448
+ const buildChildElements = (node) => {
449
+ const childElements = [];
450
+ if (node.textContent) childElements.push(import_lib$1.jsxText(node.textContent));
451
+ node.children?.forEach((child) => {
452
+ const childJSX = nodeToJSX(child);
453
+ if (childJSX) childElements.push(childJSX);
454
+ });
455
+ return childElements;
597
456
  };
598
- //#endregion
599
- //#region src/utils/ast/array-source.ts
600
- const commaIn = (source, start, end) => {
601
- let comma = null;
602
- for (let at = start; at < end; at++) {
603
- if (/\s/.test(source[at])) continue;
604
- if (source.startsWith("/*", at)) {
605
- const close = source.indexOf("*/", at + 2);
606
- if (close < 0 || close + 2 > end) return null;
607
- at = close + 1;
608
- } else if (source.startsWith("//", at)) {
609
- const newline = source.indexOf("\n", at + 2);
610
- at = newline < 0 ? end : newline;
611
- } else if (source[at] === "," && comma === null) comma = at;
612
- else return null;
613
- }
614
- return comma;
615
- };
616
- const denseArraySource = (source) => {
617
- const array = parseArrayExpression(source);
618
- if (!array || array.elements.some((node) => !import_lib$1.isExpression(node) || node.extra?.parenthesized)) return null;
619
- const elements = array.elements;
620
- const commas = elements.slice(0, -1).map((node, index) => commaIn(source, node.end, elements[index + 1].start));
621
- if (commas.some((comma) => comma === null)) return null;
622
- const last = elements.at(-1);
623
- return {
624
- array,
625
- elements,
626
- commas,
627
- trailing: last ? commaIn(source, last.end, array.end - 1) : null
628
- };
629
- };
630
- const canStructurallyEditArray = (source) => denseArraySource(source) !== null;
631
- const validArrayIndices = (indices, length) => [...indices].every((index) => Number.isInteger(index) && index >= 0 && index < length);
632
- const reorderArraySource = (source, elements, next) => applyEdits(source, elements.map((node, index) => ({
633
- start: node.start,
634
- end: node.end,
635
- content: source.slice(next[index].start, next[index].end)
636
- })));
637
- const removeArraySource = (source, data, indices) => {
638
- const edits = [];
639
- const removedCommas = /* @__PURE__ */ new Set();
640
- const { elements, commas, trailing } = data;
641
- for (let index = 0; index < elements.length; index++) {
642
- if (!indices.has(index)) continue;
643
- const start = index;
644
- while (index + 1 < elements.length && indices.has(index + 1)) index++;
645
- for (let current = start; current <= index; current++) {
646
- const node = elements[current];
647
- edits.push({
648
- start: node.start,
649
- end: node.end,
650
- content: ""
651
- });
652
- if (current < elements.length - 1) removedCommas.add(commas[current]);
653
- }
654
- if (index === elements.length - 1) {
655
- if (trailing !== null) removedCommas.add(trailing);
656
- else if (start > 0) removedCommas.add(commas[start - 1]);
657
- }
658
- }
659
- for (const comma of removedCommas) edits.push({
660
- start: comma,
661
- end: comma + 1,
662
- content: ""
663
- });
664
- return applyEdits(source, edits);
665
- };
666
- const appendArraySource = (source, data, copies) => {
667
- const last = data.elements.at(-1);
668
- if (!last || copies.length === 0) return source;
669
- return applyEdits(source, [{
670
- start: last.end,
671
- end: last.end,
672
- content: `, ${copies.join(", ")}`
673
- }]);
674
- };
675
- //#endregion
676
- //#region src/utils/ast/items.ts
677
- const elementsOf = (code) => parseArrayExpression(code)?.elements ?? null;
678
- const kindOf = (element) => import_lib$1.isObjectExpression(element) ? "object" : "primitive";
679
- const parseItems = (code) => {
680
- return elementsOf(code)?.flatMap((node, index) => import_lib$1.isExpression(node) ? [{
681
- index,
682
- kind: kindOf(node),
683
- node
684
- }] : []) ?? null;
685
- };
686
- const resolveRenderLeaf = (render, key) => {
687
- const leaf = render?.[key];
688
- return leaf && "type" in leaf ? leaf : null;
689
- };
690
- const toTemplateRaw = (value) => {
691
- return value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
692
- };
693
- const buildPropertyValue = (value, declaredType, renderLeaf, current) => {
694
- if (renderLeaf?.property === BINDING_PROP.INNER_HTML) {
695
- const raw = String(value);
696
- return import_lib$1.templateLiteral([import_lib$1.templateElement({
697
- raw: toTemplateRaw(raw),
698
- cooked: raw
699
- }, true)], []);
700
- }
701
- if (renderLeaf?.type === "jsx" || import_lib$1.isJSXElement(current) || import_lib$1.isJSXFragment(current)) {
702
- const trimmed = String(value).trim();
703
- if (!trimmed.startsWith("<")) return import_lib$1.stringLiteral(trimmed);
704
- try {
705
- return (0, import_lib.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
706
- } catch {
707
- return;
708
- }
709
- }
710
- if (declaredType === "array" || declaredType === "object") {
711
- if (typeof value === "string") try {
712
- return (0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] });
713
- } catch {
714
- return;
715
- }
716
- if (!isLosslesslyEvaluable(current)) return;
717
- return valueToExpression(value) ?? void 0;
718
- }
719
- return createNodeFromValue(declaredType, parseValue(value)) ?? void 0;
720
- };
721
- const updateArrayItemProperty = (code, index, key, value, render) => {
722
- const elements = elementsOf(code);
723
- const element = elements?.[index];
724
- if (!elements || !import_lib$1.isObjectExpression(element)) return null;
725
- const target = element.properties.find((property) => import_lib$1.isObjectProperty(property) && import_lib$1.isIdentifier(property.key) && property.key.name === key);
726
- if (!target) return null;
727
- const declaredType = extractObjectProperties(element)[key]?.type ?? "string";
728
- const nextValue = buildPropertyValue(value, declaredType, resolveRenderLeaf(render, key), target.value);
729
- if (!nextValue) return null;
730
- let serialized = typeof value === "string" && (declaredType === "array" || declaredType === "object" || import_lib$1.isJSXElement(nextValue) || import_lib$1.isJSXFragment(nextValue)) ? value.trim() : generateCode(nextValue);
731
- if (nextValue.trailingComments?.some((comment) => comment.type === "CommentLine")) serialized += "\n";
732
- const content = target.shorthand ? `${key}: ${serialized}` : serialized;
733
- return applyEdits(code, [{
734
- start: target.value.start,
735
- end: target.value.end,
736
- content
737
- }]);
738
- };
739
- const updateArrayItemValue = (code, index, value) => {
740
- const elements = elementsOf(code);
741
- const element = elements?.[index];
742
- if (!elements || !import_lib$1.isExpression(element)) return null;
743
- const nextValue = createNodeFromValue(extractNodeValue(element).type, parseValue(value));
744
- if (!nextValue) return null;
745
- return applyEdits(code, [{
746
- start: element.start,
747
- end: element.end,
748
- content: generateCode(nextValue)
749
- }]);
750
- };
751
- const moveArrayItem = (code, from, to) => {
752
- const data = denseArraySource(code);
753
- if (!data || !validArrayIndices([from], data.elements.length) || !Number.isInteger(to)) return null;
754
- const next = [...data.elements];
755
- const [moved] = next.splice(from, 1);
756
- next.splice(to, 0, moved);
757
- return reorderArraySource(code, data.elements, next);
758
- };
759
- const moveArrayItems = (code, indices, direction) => {
760
- const data = denseArraySource(code);
761
- if (!data || !validArrayIndices(indices, data.elements.length) || !["up", "down"].includes(direction)) return null;
762
- const next = moveSelectedIndices(data.elements, indices, direction);
763
- return {
764
- code: reorderArraySource(code, data.elements, next.items),
765
- indices: next.indices
766
- };
767
- };
768
- const removeArrayItems = (code, indices, kind) => {
769
- const data = denseArraySource(code);
770
- if (!data || !validArrayIndices(indices, data.elements.length)) return null;
771
- const remaining = removeIndices(data.elements, indices);
772
- if (!(kind === void 0 ? remaining : remaining.filter((node) => kindOf(node) === kind)).length) return null;
773
- return removeArraySource(code, data, indices);
774
- };
775
- const copyItem = (code, element, generateId, usedIds) => {
776
- const edits = [];
777
- let unsupported = false;
778
- const reserveId = (id) => {
779
- if (usedIds.has(id)) unsupported = true;
780
- usedIds.add(id);
781
- return JSON.stringify(id);
782
- };
783
- import_lib$1.traverseFast(element, (node) => {
784
- if (!import_lib$1.isJSXOpeningElement(node)) return;
785
- const ids = node.attributes.filter((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name, { name: "data-id" }));
786
- const attr = ids[0];
787
- if (ids.length > 1 || attr && (!import_lib$1.isStringLiteral(attr.value) || node.attributes.slice(node.attributes.indexOf(attr) + 1).some((a) => import_lib$1.isJSXSpreadAttribute(a)))) {
788
- unsupported = true;
789
- return;
790
- }
791
- if (attr && import_lib$1.isStringLiteral(attr.value)) edits.push({
792
- start: attr.value.start,
793
- end: attr.value.end,
794
- content: reserveId(generateId())
795
- });
796
- });
797
- if (import_lib$1.isObjectExpression(element)) {
798
- const keys = element.properties.filter((prop) => import_lib$1.isObjectProperty(prop) && !prop.computed && (import_lib$1.isIdentifier(prop.key, { name: "key" }) || import_lib$1.isStringLiteral(prop.key, { value: "key" })));
799
- const key = keys[0];
800
- if (keys.length > 1 || key && (!import_lib$1.isStringLiteral(key.value) || element.properties.slice(element.properties.indexOf(key) + 1).some((prop) => import_lib$1.isSpreadElement(prop) || "computed" in prop && prop.computed))) return null;
801
- if (key && import_lib$1.isStringLiteral(key.value)) edits.push({
802
- start: key.value.start,
803
- end: key.value.end,
804
- content: reserveId(`${key.value.value}-${generateId()}`)
805
- });
806
- }
807
- if (unsupported) return null;
808
- return applyEdits(code.slice(element.start, element.end), edits.map((edit) => ({
809
- ...edit,
810
- start: edit.start - element.start,
811
- end: edit.end - element.start
812
- })));
813
- };
814
- const duplicateArrayItems = (code, indices, generateId = () => nanoid(6)) => {
815
- const data = denseArraySource(code);
816
- if (!data || !indices.size || !validArrayIndices(indices, data.elements.length)) return null;
817
- const usedIds = /* @__PURE__ */ new Set();
818
- import_lib$1.traverseFast(data.array, (node) => {
819
- if (import_lib$1.isJSXAttribute(node) && import_lib$1.isJSXIdentifier(node.name, { name: "data-id" }) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
820
- else if (import_lib$1.isObjectProperty(node) && !node.computed && (import_lib$1.isIdentifier(node.key, { name: "key" }) || import_lib$1.isStringLiteral(node.key, { value: "key" })) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
821
- });
822
- const copies = [...indices].sort((a, b) => a - b).map((index) => copyItem(code, data.elements[index], generateId, usedIds));
823
- return copies.every((copy) => copy !== null) ? appendArraySource(code, data, copies) : null;
824
- };
825
- const appendArrayItem = (code, kind, generateId = () => nanoid(6)) => {
826
- const index = denseArraySource(code)?.elements.findIndex((node) => kindOf(node) === kind) ?? -1;
827
- return index < 0 ? null : duplicateArrayItems(code, /* @__PURE__ */ new Set([index]), generateId);
828
- };
829
- //#endregion
830
- //#region src/utils/ast/extract.ts
831
- const collectText = (children) => {
832
- return children.filter((c) => import_lib$1.isJSXText(c)).map((c) => c.value.trim()).filter((v) => v.length).join(" ");
833
- };
834
- const parseJSXName = (tagName) => {
835
- const parts = tagName.split(".");
836
- if (parts.length === 1) return import_lib$1.jsxIdentifier(parts[0]);
837
- let expr = import_lib$1.jsxIdentifier(parts[0]);
838
- for (let i = 1; i < parts.length; i++) expr = import_lib$1.jsxMemberExpression(expr, import_lib$1.jsxIdentifier(parts[i]));
839
- return expr;
840
- };
841
- const extractCache = createBoundedCache(CONFIG.CACHE_LIMIT);
842
- const NO_MAP = {};
843
- let optionExtractCaches = /* @__PURE__ */ new WeakMap();
844
- const cacheFor = ({ bindings, bindingKeys }) => {
845
- if (!bindings && !bindingKeys) return extractCache;
846
- let byKeys = optionExtractCaches.get(bindings ?? NO_MAP);
847
- if (!byKeys) {
848
- byKeys = /* @__PURE__ */ new WeakMap();
849
- optionExtractCaches.set(bindings ?? NO_MAP, byKeys);
850
- }
851
- let cache = byKeys.get(bindingKeys ?? NO_MAP);
852
- if (!cache) {
853
- cache = createBoundedCache(CONFIG.CACHE_LIMIT);
854
- byKeys.set(bindingKeys ?? NO_MAP, cache);
855
- }
856
- return cache;
857
- };
858
- const extractAttributes = (attributes, source) => {
859
- const allAttrs = [];
860
- const dataAttrs = [];
861
- for (const attr of attributes) {
862
- if (!import_lib$1.isJSXAttribute(attr) || !import_lib$1.isJSXIdentifier(attr.name)) continue;
863
- const name = attr.name.name;
864
- let value = null;
865
- let isStringLiteral = false;
866
- if (attr.value) {
867
- if (import_lib$1.isStringLiteral(attr.value)) {
868
- value = attr.value.value;
869
- isStringLiteral = true;
870
- } else if (import_lib$1.isJSXExpressionContainer(attr.value)) try {
871
- const expression = attr.value.expression;
872
- const structural = import_lib$1.isArrayExpression(unwrapExpression(expression)) || import_lib$1.isObjectExpression(unwrapExpression(expression));
873
- value = source && structural && expression.start != null && expression.end != null ? source.slice(expression.start, expression.end) : generateCode(expression);
874
- isStringLiteral = false;
875
- } catch {
876
- value = null;
877
- }
878
- }
879
- const entry = {
880
- name,
881
- value,
882
- isStringLiteral
883
- };
884
- allAttrs.push(entry);
885
- if (name.startsWith("data-")) dataAttrs.push(entry);
886
- }
887
- return {
888
- allAttrs,
889
- dataAttrs
890
- };
891
- };
892
- const buildChildElements = (node) => {
893
- const childElements = [];
894
- if (node.textContent) childElements.push(import_lib$1.jsxText(node.textContent));
895
- node.children?.forEach((child) => {
896
- const childJSX = nodeToJSX(child);
897
- if (childJSX) childElements.push(childJSX);
898
- });
899
- return childElements;
900
- };
901
- const nodeToJSX = (node) => {
902
- try {
903
- if (node.isFragment) {
904
- const childElements = buildChildElements(node);
905
- return import_lib$1.jsxFragment(import_lib$1.jsxOpeningFragment(), import_lib$1.jsxClosingFragment(), childElements);
906
- }
907
- if (node.tagName === "div" && node.dataAttributes.some((attr) => attr.name === "data-item")) {
908
- if (node.children) return nodeToJSX(node.children[0]);
909
- return null;
910
- }
911
- const attributes = node.attributes.map((attr) => {
912
- const attrName = import_lib$1.jsxIdentifier(attr.name);
913
- if (!attr.value) return import_lib$1.jsxAttribute(attrName, null);
914
- return import_lib$1.jsxAttribute(attrName, attrValue(attr));
915
- });
916
- const elementName = parseJSXName(node.tagName);
917
- const openingElement = import_lib$1.jsxOpeningElement(elementName, attributes);
918
- const closingElement = import_lib$1.jsxClosingElement(elementName);
919
- const children = buildChildElements(node);
920
- return import_lib$1.jsxElement(openingElement, closingElement, children, false);
921
- } catch (error) {
922
- console.error("❌ DataAttrNode to JSX conversion error:", error);
923
- return null;
457
+ const nodeToJSX = (node) => {
458
+ try {
459
+ if (node.isFragment) {
460
+ const childElements = buildChildElements(node);
461
+ return import_lib$1.jsxFragment(import_lib$1.jsxOpeningFragment(), import_lib$1.jsxClosingFragment(), childElements);
462
+ }
463
+ if (node.tagName === "div" && node.dataAttributes.some((attr) => attr.name === "data-item")) {
464
+ if (node.children) return nodeToJSX(node.children[0]);
465
+ return null;
466
+ }
467
+ const attributes = node.attributes.map((attr) => {
468
+ const attrName = import_lib$1.jsxIdentifier(attr.name);
469
+ if (!attr.value) return import_lib$1.jsxAttribute(attrName, null);
470
+ return import_lib$1.jsxAttribute(attrName, attrValue(attr));
471
+ });
472
+ const elementName = parseJSXName(node.tagName);
473
+ const openingElement = import_lib$1.jsxOpeningElement(elementName, attributes);
474
+ const closingElement = import_lib$1.jsxClosingElement(elementName);
475
+ const children = buildChildElements(node);
476
+ return import_lib$1.jsxElement(openingElement, closingElement, children, false);
477
+ } catch (error) {
478
+ console.error("❌ DataAttrNode to JSX conversion error:", error);
479
+ return null;
924
480
  }
925
481
  };
926
482
  const createWrapperNode = (textContent, children) => ({
@@ -1092,6 +648,106 @@ function clearExtractCache() {
1092
648
  }
1093
649
  registerEditorCache(clearExtractCache);
1094
650
  //#endregion
651
+ //#region src/utils/selection.ts
652
+ const removeIndices = (items, indices) => {
653
+ return items.filter((_, index) => !indices.has(index));
654
+ };
655
+ /**
656
+ * Shifts every selected index up/down by one step as a block, preserving
657
+ * relative order — scattered selections stop moving individually once they
658
+ * hit an unselected neighbor, so the whole group slides together instead of
659
+ * items passing through each other.
660
+ */
661
+ const moveSelectedIndices = (items, indices, direction) => {
662
+ const next = [...items];
663
+ const nextIndices = new Set(indices);
664
+ const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
665
+ for (const index of ordered) {
666
+ const target = direction === "up" ? index - 1 : index + 1;
667
+ if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
668
+ [next[index], next[target]] = [next[target], next[index]];
669
+ nextIndices.delete(index);
670
+ nextIndices.add(target);
671
+ }
672
+ return {
673
+ items: next,
674
+ indices: nextIndices
675
+ };
676
+ };
677
+ //#endregion
678
+ //#region src/utils/ast/patch.ts
679
+ const hasOnlyLayoutNewlines = (content) => {
680
+ let index = 0;
681
+ while (index < content.length) {
682
+ const char = content[index];
683
+ if (char === "`" || char === "<") return false;
684
+ if (char === "\"" || char === "'") {
685
+ const next = skipStringLiteral(content, index);
686
+ if (next === -1) return false;
687
+ index = next;
688
+ continue;
689
+ }
690
+ if (char === "/") {
691
+ const following = content[index + 1];
692
+ if (following === "/") {
693
+ const lineEnd = content.indexOf("\n", index + 2);
694
+ index = lineEnd === -1 ? content.length : lineEnd;
695
+ continue;
696
+ }
697
+ if (following === "*") {
698
+ const commentEnd = content.indexOf("*/", index + 2);
699
+ if (commentEnd === -1) return false;
700
+ index = commentEnd + 2;
701
+ continue;
702
+ }
703
+ return false;
704
+ }
705
+ index++;
706
+ }
707
+ return true;
708
+ };
709
+ const skipStringLiteral = (content, start) => {
710
+ const quote = content[start];
711
+ for (let index = start + 1; index < content.length; index++) {
712
+ const char = content[index];
713
+ if (char === "\\") {
714
+ const escaped = content[index + 1];
715
+ if (escaped === "\n" || escaped === "\r") return -1;
716
+ index++;
717
+ continue;
718
+ }
719
+ if (char === quote) return index + 1;
720
+ if (char === "\n") return -1;
721
+ }
722
+ return -1;
723
+ };
724
+ const lineIndentAt = (source, offset) => {
725
+ const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
726
+ return /^[ \t]*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
727
+ };
728
+ const lineTerminatorOf = (source) => {
729
+ return source.includes("\r\n") ? "\r\n" : "\n";
730
+ };
731
+ const applyEdits = (source, edits) => {
732
+ if (edits.length === 0) return source;
733
+ const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
734
+ for (const edit of ordered) if (!Number.isInteger(edit.start) || !Number.isInteger(edit.end) || edit.start < 0 || edit.end > source.length || edit.start > edit.end) throw new Error(`Invalid source edit [${edit.start}, ${edit.end}) for a source of length ${source.length}`);
735
+ for (let i = 1; i < ordered.length; i++) {
736
+ const previous = ordered[i - 1];
737
+ const current = ordered[i];
738
+ if (current.start < previous.end) throw new Error(`Overlapping source edits: [${previous.start}, ${previous.end}) and [${current.start}, ${current.end})`);
739
+ }
740
+ let result = "";
741
+ let cursor = 0;
742
+ const terminator = lineTerminatorOf(source);
743
+ for (const edit of ordered) {
744
+ const content = edit.indent && edit.content.includes("\n") && hasOnlyLayoutNewlines(edit.content) ? edit.content.replace(/\n/g, `${terminator}${lineIndentAt(source, edit.start)}`) : edit.content;
745
+ result += source.slice(cursor, edit.start) + content;
746
+ cursor = edit.end;
747
+ }
748
+ return result + source.slice(cursor);
749
+ };
750
+ //#endregion
1095
751
  //#region src/utils/ast/children.ts
1096
752
  const modeledSignatures = (nodes) => nodes.map((node) => {
1097
753
  const jsx = nodeToJSX(node);
@@ -1238,7 +894,42 @@ const editChildrenSource = (source, parent, input, options) => {
1238
894
  }
1239
895
  };
1240
896
  //#endregion
1241
- //#region src/utils/ast/update.ts
897
+ //#region src/utils/ast/value-expression.ts
898
+ const createNodeFromValue = (type, value) => {
899
+ switch (type) {
900
+ case "boolean": return import_lib$1.booleanLiteral(value === true);
901
+ case "number": return import_lib$1.numericLiteral(Number(value));
902
+ case "string": return import_lib$1.stringLiteral(String(value));
903
+ case "null": return import_lib$1.nullLiteral();
904
+ case "array":
905
+ case "object":
906
+ case "unknown": return null;
907
+ default: return null;
908
+ }
909
+ };
910
+ const valueToExpression = (value) => {
911
+ if (typeof value === "string") return import_lib$1.stringLiteral(value);
912
+ if (typeof value === "number") return value < 0 ? import_lib$1.unaryExpression("-", import_lib$1.numericLiteral(-value)) : import_lib$1.numericLiteral(value);
913
+ if (typeof value === "boolean") return import_lib$1.booleanLiteral(value);
914
+ if (value === null) return import_lib$1.nullLiteral();
915
+ if (Array.isArray(value)) return import_lib$1.arrayExpression(value.map((item) => valueToExpression(item) ?? import_lib$1.nullLiteral()));
916
+ if (typeof value === "object") {
917
+ const properties = [];
918
+ for (const [key, item] of Object.entries(value)) {
919
+ const expr = valueToExpression(item);
920
+ if (expr === null) continue;
921
+ properties.push(import_lib$1.objectProperty(import_lib$1.stringLiteral(key), expr));
922
+ }
923
+ return import_lib$1.objectExpression(properties);
924
+ }
925
+ return null;
926
+ };
927
+ const arrayExpressionToCode = (elements) => {
928
+ const nextAst = import_lib$1.arrayExpression(elements);
929
+ return generateCode(nextAst);
930
+ };
931
+ //#endregion
932
+ //#region src/utils/ast/edit-source.ts
1242
933
  const childrenRange = (element) => {
1243
934
  const { openingElement, closingElement } = element;
1244
935
  if (!closingElement || openingElement.end == null || closingElement.start == null) return null;
@@ -1413,11 +1104,143 @@ const addAttribute = (source, opening, propertyName, value, type) => {
1413
1104
  indent: true
1414
1105
  }];
1415
1106
  };
1416
- const canEditAttributeValue = (attribute, value, type) => {
1417
- if (!import_lib$1.isJSXExpressionContainer(attribute.value) || import_lib$1.isJSXEmptyExpression(attribute.value.expression) || isLosslesslyEvaluable(attribute.value.expression)) return true;
1418
- return typeof value === "string" && holdsStructuralExpression(attribute.value) && !(type && STRING_VALUED_TYPES.has(type)) && structuralSource(value) !== null;
1107
+ const canEditAttributeValue = (attribute, value, type) => {
1108
+ if (!import_lib$1.isJSXExpressionContainer(attribute.value) || import_lib$1.isJSXEmptyExpression(attribute.value.expression) || isLosslesslyEvaluable(attribute.value.expression)) return true;
1109
+ return typeof value === "string" && holdsStructuralExpression(attribute.value) && !(type && STRING_VALUED_TYPES.has(type)) && structuralSource(value) !== null;
1110
+ };
1111
+ //#endregion
1112
+ //#region src/utils/ast/update.ts
1113
+ const hasDataId = (opening, dataId) => opening.attributes.some((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.ID && attr.value && import_lib$1.isStringLiteral(attr.value) && attr.value.value === dataId);
1114
+ const findJSXAttribute = (opening, name) => opening.attributes.find((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === name);
1115
+ const findBinding = (opening, { dataId, label, property, options }) => {
1116
+ const bindingAttr = findJSXAttribute(opening, DATA_ATTR.BINDING);
1117
+ if (bindingAttr && !bindingAttr.value) return { failure: {
1118
+ reason: "no-binding",
1119
+ dataId
1120
+ } };
1121
+ let own;
1122
+ if (bindingAttr?.value) {
1123
+ let bindingValue = "";
1124
+ if (import_lib$1.isStringLiteral(bindingAttr.value)) bindingValue = bindingAttr.value.value;
1125
+ else if (import_lib$1.isJSXExpressionContainer(bindingAttr.value)) try {
1126
+ bindingValue = generateCode(bindingAttr.value.expression);
1127
+ } catch (error) {
1128
+ return { failure: {
1129
+ reason: "parse-error",
1130
+ error
1131
+ } };
1132
+ }
1133
+ own = parseBinding(bindingValue);
1134
+ }
1135
+ const keyAttr = findJSXAttribute(opening, DATA_ATTR.BINDING_KEY);
1136
+ const bindings = resolveBindings(own, keyAttr && (keyAttr.value && import_lib$1.isStringLiteral(keyAttr.value) ? keyAttr.value.value : null), getJSXTagName(opening), options);
1137
+ if (!own && bindings.length === 0) return { failure: {
1138
+ reason: "no-binding",
1139
+ dataId
1140
+ } };
1141
+ const matches = bindings.filter((binding) => property !== void 0 ? binding.property === property : binding.label === label);
1142
+ if (matches.length === 0) return { failure: {
1143
+ reason: "binding-not-declared",
1144
+ dataId,
1145
+ label,
1146
+ property
1147
+ } };
1148
+ if (matches.length > 1) return { failure: {
1149
+ reason: "duplicate-binding",
1150
+ dataId,
1151
+ label,
1152
+ property,
1153
+ count: matches.length
1154
+ } };
1155
+ return { binding: matches[0] };
1156
+ };
1157
+ const checkEdit = (binding, { dataId, value }) => {
1158
+ const property = binding.property;
1159
+ if (RESERVED_BINDING_PROPERTIES.includes(property)) return {
1160
+ reason: "reserved-property",
1161
+ dataId,
1162
+ property
1163
+ };
1164
+ if (value === void 0 && binding.required) return {
1165
+ reason: "required-property",
1166
+ dataId,
1167
+ property
1168
+ };
1169
+ };
1170
+ const toElementEdit = (result, onNull) => result ? { edits: result } : { failure: onNull() };
1171
+ const editProperty = (source, element, binding, { dataId, value, options }) => {
1172
+ const opening = element.openingElement;
1173
+ const prop = binding.property;
1174
+ const unset = value === void 0;
1175
+ const couldNotUpdate = () => ({
1176
+ reason: "parse-error",
1177
+ error: /* @__PURE__ */ new Error(`could not update "${prop}"`)
1178
+ });
1179
+ const contentWithoutSlot = !unset && String(value) !== "" && !childrenRange(element);
1180
+ switch (prop) {
1181
+ case BINDING_PROP.INNER_TEXT:
1182
+ if (contentWithoutSlot) return { failure: {
1183
+ reason: "self-closing",
1184
+ dataId,
1185
+ property: prop
1186
+ } };
1187
+ return toElementEdit(editInnerText(source, element, unset ? "" : String(value)), couldNotUpdate);
1188
+ case BINDING_PROP.INNER_HTML:
1189
+ if (contentWithoutSlot && binding.type !== "richtext") return { failure: {
1190
+ reason: "self-closing",
1191
+ dataId,
1192
+ property: prop
1193
+ } };
1194
+ return toElementEdit(binding.type === "richtext" ? editRichtext(element, unset ? "" : String(value)) : editInnerHTML(element, unset ? "" : String(value)), couldNotUpdate);
1195
+ case BINDING_PROP.CHILDREN:
1196
+ if (unset) return { failure: {
1197
+ reason: "unsupported-syntax",
1198
+ dataId,
1199
+ property: prop
1200
+ } };
1201
+ return toElementEdit(editChildrenSource(source, element, value, options), () => ({
1202
+ reason: "parse-error",
1203
+ error: /* @__PURE__ */ new Error("Unsupported or stale children edit; source was preserved")
1204
+ }));
1205
+ default: {
1206
+ const attribute = findAttribute(opening, prop);
1207
+ if (unset) return toElementEdit(attribute ? removeAttribute(source, attribute) : [], () => ({
1208
+ reason: "parse-error",
1209
+ error: /* @__PURE__ */ new Error(`could not remove "${prop}"`)
1210
+ }));
1211
+ if (!attribute) return toElementEdit(value === "" ? [] : addAttribute(source, opening, prop, value, binding.type), () => ({
1212
+ reason: "attribute-not-found",
1213
+ dataId,
1214
+ property: prop
1215
+ }));
1216
+ if (binding.type !== "jsx" && !canEditAttributeValue(attribute, value, binding.type)) return { failure: {
1217
+ reason: "unsupported-syntax",
1218
+ dataId,
1219
+ property: prop
1220
+ } };
1221
+ return toElementEdit(binding.type === "jsx" ? editJsxAttribute(opening, prop, value) : editAttribute(opening, prop, value, binding.type), () => ({
1222
+ reason: "attribute-not-found",
1223
+ dataId,
1224
+ property: prop
1225
+ }));
1226
+ }
1227
+ }
1228
+ };
1229
+ const editElement = (source, element, request) => {
1230
+ const found = findBinding(element.openingElement, request);
1231
+ if ("failure" in found) return found;
1232
+ const refused = checkEdit(found.binding, request);
1233
+ if (refused) return { failure: refused };
1234
+ return editProperty(source, element, found.binding, request);
1419
1235
  };
1420
1236
  const update = (code, dataId, label, value, property, options = {}) => {
1237
+ const request = {
1238
+ dataId,
1239
+ label,
1240
+ value,
1241
+ property,
1242
+ options
1243
+ };
1421
1244
  try {
1422
1245
  const wrapped = wrap(code);
1423
1246
  const ast = (0, import_lib.parse)(wrapped, {
@@ -1427,150 +1250,15 @@ const update = (code, dataId, label, value, property, options = {}) => {
1427
1250
  let changed = false;
1428
1251
  let failure;
1429
1252
  const edits = [];
1430
- const collect = (result, onNull) => {
1431
- if (!result) {
1432
- failure = onNull();
1433
- return;
1434
- }
1435
- edits.push(...result);
1436
- changed = true;
1437
- };
1438
1253
  traverse(ast, { JSXElement(path) {
1439
- const opening = path.node.openingElement;
1440
- if (!opening.attributes.find((attr) => {
1441
- return import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.ID && attr.value && import_lib$1.isStringLiteral(attr.value) && attr.value.value === dataId;
1442
- })) return;
1443
- const bindingAttr = opening.attributes.find((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.BINDING);
1444
- if (bindingAttr && !bindingAttr.value) {
1445
- failure = {
1446
- reason: "no-binding",
1447
- dataId
1448
- };
1449
- return;
1450
- }
1451
- let own;
1452
- if (bindingAttr?.value) {
1453
- let bindingValue = "";
1454
- if (import_lib$1.isStringLiteral(bindingAttr.value)) bindingValue = bindingAttr.value.value;
1455
- else if (import_lib$1.isJSXExpressionContainer(bindingAttr.value)) try {
1456
- bindingValue = generateCode(bindingAttr.value.expression);
1457
- } catch (error) {
1458
- failure = {
1459
- reason: "parse-error",
1460
- error
1461
- };
1462
- return;
1463
- }
1464
- own = parseBinding(bindingValue);
1465
- }
1466
- const keyAttr = opening.attributes.find((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.BINDING_KEY);
1467
- const bindings = resolveBindings(own, keyAttr && (keyAttr.value && import_lib$1.isStringLiteral(keyAttr.value) ? keyAttr.value.value : null), getJSXTagName(opening), options);
1468
- if (!own && bindings.length === 0) {
1469
- failure = {
1470
- reason: "no-binding",
1471
- dataId
1472
- };
1473
- return;
1474
- }
1475
- const matches = bindings.filter((binding) => property !== void 0 ? binding.property === property : binding.label === label);
1476
- if (matches.length === 0) {
1477
- failure = {
1478
- reason: "binding-not-declared",
1479
- dataId,
1480
- label,
1481
- property
1482
- };
1483
- return;
1484
- }
1485
- if (matches.length > 1) {
1486
- failure = {
1487
- reason: "duplicate-binding",
1488
- dataId,
1489
- label,
1490
- property,
1491
- count: matches.length
1492
- };
1254
+ if (!hasDataId(path.node.openingElement, dataId)) return;
1255
+ const result = editElement(wrapped, path.node, request);
1256
+ if ("failure" in result) {
1257
+ failure = result.failure;
1493
1258
  return;
1494
1259
  }
1495
- const propertyBinding = matches[0];
1496
- const prop = propertyBinding.property;
1497
- if (RESERVED_BINDING_PROPERTIES.includes(prop)) {
1498
- failure = {
1499
- reason: "reserved-property",
1500
- dataId,
1501
- property: prop
1502
- };
1503
- return;
1504
- }
1505
- const unset = value === void 0;
1506
- if (unset && propertyBinding.required) {
1507
- failure = {
1508
- reason: "required-property",
1509
- dataId,
1510
- property: prop
1511
- };
1512
- return;
1513
- }
1514
- switch (prop) {
1515
- case BINDING_PROP.INNER_TEXT:
1516
- collect(editInnerText(wrapped, path.node, unset ? "" : String(value)), () => ({
1517
- reason: "parse-error",
1518
- error: /* @__PURE__ */ new Error(`could not update "${prop}"`)
1519
- }));
1520
- break;
1521
- case BINDING_PROP.INNER_HTML:
1522
- collect(propertyBinding.type === "richtext" ? editRichtext(path.node, unset ? "" : String(value)) : editInnerHTML(path.node, unset ? "" : String(value)), () => ({
1523
- reason: "parse-error",
1524
- error: /* @__PURE__ */ new Error(`could not update "${prop}"`)
1525
- }));
1526
- break;
1527
- case BINDING_PROP.CHILDREN:
1528
- if (unset) {
1529
- failure = {
1530
- reason: "unsupported-syntax",
1531
- dataId,
1532
- property: prop
1533
- };
1534
- break;
1535
- }
1536
- collect(editChildrenSource(wrapped, path.node, value, options), () => ({
1537
- reason: "parse-error",
1538
- error: /* @__PURE__ */ new Error("Unsupported or stale children edit; source was preserved")
1539
- }));
1540
- break;
1541
- default: {
1542
- const attribute = findAttribute(opening, prop);
1543
- if (unset) {
1544
- collect(attribute ? removeAttribute(wrapped, attribute) : [], () => ({
1545
- reason: "parse-error",
1546
- error: /* @__PURE__ */ new Error(`could not remove "${prop}"`)
1547
- }));
1548
- break;
1549
- }
1550
- if (!attribute) {
1551
- collect(value === "" ? [] : addAttribute(wrapped, opening, prop, value, propertyBinding.type), () => ({
1552
- reason: "attribute-not-found",
1553
- dataId,
1554
- property: prop
1555
- }));
1556
- break;
1557
- }
1558
- if (attribute && propertyBinding.type !== "jsx" && !canEditAttributeValue(attribute, value, propertyBinding.type)) {
1559
- failure = {
1560
- reason: "unsupported-syntax",
1561
- dataId,
1562
- property: prop
1563
- };
1564
- break;
1565
- }
1566
- collect(propertyBinding.type === "jsx" ? editJsxAttribute(opening, prop, value) : editAttribute(opening, prop, value, propertyBinding.type), () => ({
1567
- reason: "attribute-not-found",
1568
- dataId,
1569
- property: prop
1570
- }));
1571
- break;
1572
- }
1573
- }
1260
+ edits.push(...result.edits);
1261
+ changed = true;
1574
1262
  } });
1575
1263
  if (!changed) return {
1576
1264
  code,
@@ -1639,55 +1327,237 @@ const bulkUpdate = (raw, entries) => {
1639
1327
  };
1640
1328
  };
1641
1329
  //#endregion
1642
- //#region src/utils/ast/validate.ts
1643
- const VALID = { valid: true };
1644
- const validateBindingValue = (binding, value) => {
1645
- if (value === "" || value === null || value === void 0) {
1646
- if (binding.required) return {
1647
- valid: false,
1648
- message: "This field is required."
1649
- };
1650
- return VALID;
1330
+ //#region src/utils/ast/array-source.ts
1331
+ const commaIn = (source, start, end) => {
1332
+ let comma = null;
1333
+ for (let at = start; at < end; at++) {
1334
+ if (/\s/.test(source[at])) continue;
1335
+ if (source.startsWith("/*", at)) {
1336
+ const close = source.indexOf("*/", at + 2);
1337
+ if (close < 0 || close + 2 > end) return null;
1338
+ at = close + 1;
1339
+ } else if (source.startsWith("//", at)) {
1340
+ const newline = source.indexOf("\n", at + 2);
1341
+ at = newline < 0 ? end : newline;
1342
+ } else if (source[at] === "," && comma === null) comma = at;
1343
+ else return null;
1651
1344
  }
1652
- if (typeof value === "number") {
1653
- if (binding.min !== void 0 && value < binding.min) return {
1654
- valid: false,
1655
- message: `Must be at least ${binding.min}.`
1656
- };
1657
- if (binding.max !== void 0 && value > binding.max) return {
1658
- valid: false,
1659
- message: `Must be at most ${binding.max}.`
1660
- };
1345
+ return comma;
1346
+ };
1347
+ const denseArraySource = (source) => {
1348
+ const array = parseArrayExpression(source);
1349
+ if (!array || array.elements.some((node) => !import_lib$1.isExpression(node) || node.extra?.parenthesized)) return null;
1350
+ const elements = array.elements;
1351
+ const commas = elements.slice(0, -1).map((node, index) => commaIn(source, node.end, elements[index + 1].start));
1352
+ if (commas.some((comma) => comma === null)) return null;
1353
+ const last = elements.at(-1);
1354
+ return {
1355
+ array,
1356
+ elements,
1357
+ commas,
1358
+ trailing: last ? commaIn(source, last.end, array.end - 1) : null
1359
+ };
1360
+ };
1361
+ const canStructurallyEditArray = (source) => denseArraySource(source) !== null;
1362
+ const validArrayIndices = (indices, length) => [...indices].every((index) => Number.isInteger(index) && index >= 0 && index < length);
1363
+ const reorderArraySource = (source, elements, next) => applyEdits(source, elements.map((node, index) => ({
1364
+ start: node.start,
1365
+ end: node.end,
1366
+ content: source.slice(next[index].start, next[index].end)
1367
+ })));
1368
+ const removeArraySource = (source, data, indices) => {
1369
+ const edits = [];
1370
+ const removedCommas = /* @__PURE__ */ new Set();
1371
+ const { elements, commas, trailing } = data;
1372
+ for (let index = 0; index < elements.length; index++) {
1373
+ if (!indices.has(index)) continue;
1374
+ const start = index;
1375
+ while (index + 1 < elements.length && indices.has(index + 1)) index++;
1376
+ for (let current = start; current <= index; current++) {
1377
+ const node = elements[current];
1378
+ edits.push({
1379
+ start: node.start,
1380
+ end: node.end,
1381
+ content: ""
1382
+ });
1383
+ if (current < elements.length - 1) removedCommas.add(commas[current]);
1384
+ }
1385
+ if (index === elements.length - 1) {
1386
+ if (trailing !== null) removedCommas.add(trailing);
1387
+ else if (start > 0) removedCommas.add(commas[start - 1]);
1388
+ }
1389
+ }
1390
+ for (const comma of removedCommas) edits.push({
1391
+ start: comma,
1392
+ end: comma + 1,
1393
+ content: ""
1394
+ });
1395
+ return applyEdits(source, edits);
1396
+ };
1397
+ const appendArraySource = (source, data, copies) => {
1398
+ const last = data.elements.at(-1);
1399
+ if (!last || copies.length === 0) return source;
1400
+ return applyEdits(source, [{
1401
+ start: last.end,
1402
+ end: last.end,
1403
+ content: `, ${copies.join(", ")}`
1404
+ }]);
1405
+ };
1406
+ //#endregion
1407
+ //#region src/utils/ast/items.ts
1408
+ const elementsOf = (code) => parseArrayExpression(code)?.elements ?? null;
1409
+ const kindOf = (element) => import_lib$1.isObjectExpression(element) ? "object" : "primitive";
1410
+ const parseItems = (code) => {
1411
+ return elementsOf(code)?.flatMap((node, index) => import_lib$1.isExpression(node) ? [{
1412
+ index,
1413
+ kind: kindOf(node),
1414
+ node
1415
+ }] : []) ?? null;
1416
+ };
1417
+ const resolveRenderLeaf = (render, key) => {
1418
+ const leaf = render?.[key];
1419
+ return leaf && "type" in leaf ? leaf : null;
1420
+ };
1421
+ const toTemplateRaw = (value) => {
1422
+ return value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
1423
+ };
1424
+ const buildPropertyValue = (value, declaredType, renderLeaf, current) => {
1425
+ if (renderLeaf?.property === BINDING_PROP.INNER_HTML) {
1426
+ const raw = String(value);
1427
+ return import_lib$1.templateLiteral([import_lib$1.templateElement({
1428
+ raw: toTemplateRaw(raw),
1429
+ cooked: raw
1430
+ }, true)], []);
1661
1431
  }
1662
- if (typeof value === "string" && binding.pattern) {
1663
- let regex;
1432
+ if (renderLeaf?.type === "jsx" || import_lib$1.isJSXElement(current) || import_lib$1.isJSXFragment(current)) {
1433
+ const trimmed = String(value).trim();
1434
+ if (!trimmed.startsWith("<")) return import_lib$1.stringLiteral(trimmed);
1664
1435
  try {
1665
- regex = new RegExp(binding.pattern);
1436
+ return (0, import_lib.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
1666
1437
  } catch {
1667
- return VALID;
1438
+ return;
1668
1439
  }
1669
- if (!regex.test(value)) return {
1670
- valid: false,
1671
- message: "Value does not match the required format."
1672
- };
1673
1440
  }
1674
- if (typeof value === "string" && binding.type === "url") try {
1675
- new URL(value);
1676
- } catch {
1677
- return {
1678
- valid: false,
1679
- message: "Must be a valid URL."
1680
- };
1441
+ if (declaredType === "array" || declaredType === "object") {
1442
+ if (typeof value === "string") try {
1443
+ return (0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] });
1444
+ } catch {
1445
+ return;
1446
+ }
1447
+ if (!isLosslesslyEvaluable(current)) return;
1448
+ return valueToExpression(value) ?? void 0;
1681
1449
  }
1682
- if (typeof value === "string" && binding.type === "date") {
1683
- if (Number.isNaN(Date.parse(value))) return {
1684
- valid: false,
1685
- message: "Must be a valid date."
1686
- };
1450
+ return createNodeFromValue(declaredType, parseValue(value)) ?? void 0;
1451
+ };
1452
+ const updateArrayItemProperty = (code, index, key, value, render) => {
1453
+ const elements = elementsOf(code);
1454
+ const element = elements?.[index];
1455
+ if (!elements || !import_lib$1.isObjectExpression(element)) return null;
1456
+ const target = element.properties.find((property) => import_lib$1.isObjectProperty(property) && import_lib$1.isIdentifier(property.key) && property.key.name === key);
1457
+ if (!target) return null;
1458
+ const declaredType = extractObjectProperties(element)[key]?.type ?? "string";
1459
+ const nextValue = buildPropertyValue(value, declaredType, resolveRenderLeaf(render, key), target.value);
1460
+ if (!nextValue) return null;
1461
+ let serialized = typeof value === "string" && (declaredType === "array" || declaredType === "object" || import_lib$1.isJSXElement(nextValue) || import_lib$1.isJSXFragment(nextValue)) ? value.trim() : generateCode(nextValue);
1462
+ if (nextValue.trailingComments?.some((comment) => comment.type === "CommentLine")) serialized += "\n";
1463
+ const content = target.shorthand ? `${key}: ${serialized}` : serialized;
1464
+ return applyEdits(code, [{
1465
+ start: target.value.start,
1466
+ end: target.value.end,
1467
+ content
1468
+ }]);
1469
+ };
1470
+ const updateArrayItemValue = (code, index, value) => {
1471
+ const elements = elementsOf(code);
1472
+ const element = elements?.[index];
1473
+ if (!elements || !import_lib$1.isExpression(element)) return null;
1474
+ const nextValue = createNodeFromValue(extractNodeValue(element).type, parseValue(value));
1475
+ if (!nextValue) return null;
1476
+ return applyEdits(code, [{
1477
+ start: element.start,
1478
+ end: element.end,
1479
+ content: generateCode(nextValue)
1480
+ }]);
1481
+ };
1482
+ const moveArrayItem = (code, from, to) => {
1483
+ const data = denseArraySource(code);
1484
+ if (!data || !validArrayIndices([from], data.elements.length) || !Number.isInteger(to)) return null;
1485
+ const next = [...data.elements];
1486
+ const [moved] = next.splice(from, 1);
1487
+ next.splice(to, 0, moved);
1488
+ return reorderArraySource(code, data.elements, next);
1489
+ };
1490
+ const moveArrayItems = (code, indices, direction) => {
1491
+ const data = denseArraySource(code);
1492
+ if (!data || !validArrayIndices(indices, data.elements.length) || !["up", "down"].includes(direction)) return null;
1493
+ const next = moveSelectedIndices(data.elements, indices, direction);
1494
+ return {
1495
+ code: reorderArraySource(code, data.elements, next.items),
1496
+ indices: next.indices
1497
+ };
1498
+ };
1499
+ const removeArrayItems = (code, indices, kind) => {
1500
+ const data = denseArraySource(code);
1501
+ if (!data || !validArrayIndices(indices, data.elements.length)) return null;
1502
+ const remaining = removeIndices(data.elements, indices);
1503
+ if (!(kind === void 0 ? remaining : remaining.filter((node) => kindOf(node) === kind)).length) return null;
1504
+ return removeArraySource(code, data, indices);
1505
+ };
1506
+ const copyItem = (code, element, generateId, usedIds) => {
1507
+ const edits = [];
1508
+ let unsupported = false;
1509
+ const reserveId = (id) => {
1510
+ if (usedIds.has(id)) unsupported = true;
1511
+ usedIds.add(id);
1512
+ return JSON.stringify(id);
1513
+ };
1514
+ import_lib$1.traverseFast(element, (node) => {
1515
+ if (!import_lib$1.isJSXOpeningElement(node)) return;
1516
+ const ids = node.attributes.filter((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name, { name: "data-id" }));
1517
+ const attr = ids[0];
1518
+ if (ids.length > 1 || attr && (!import_lib$1.isStringLiteral(attr.value) || node.attributes.slice(node.attributes.indexOf(attr) + 1).some((a) => import_lib$1.isJSXSpreadAttribute(a)))) {
1519
+ unsupported = true;
1520
+ return;
1521
+ }
1522
+ if (attr && import_lib$1.isStringLiteral(attr.value)) edits.push({
1523
+ start: attr.value.start,
1524
+ end: attr.value.end,
1525
+ content: reserveId(generateId())
1526
+ });
1527
+ });
1528
+ if (import_lib$1.isObjectExpression(element)) {
1529
+ const keys = element.properties.filter((prop) => import_lib$1.isObjectProperty(prop) && !prop.computed && (import_lib$1.isIdentifier(prop.key, { name: "key" }) || import_lib$1.isStringLiteral(prop.key, { value: "key" })));
1530
+ const key = keys[0];
1531
+ if (keys.length > 1 || key && (!import_lib$1.isStringLiteral(key.value) || element.properties.slice(element.properties.indexOf(key) + 1).some((prop) => import_lib$1.isSpreadElement(prop) || "computed" in prop && prop.computed))) return null;
1532
+ if (key && import_lib$1.isStringLiteral(key.value)) edits.push({
1533
+ start: key.value.start,
1534
+ end: key.value.end,
1535
+ content: reserveId(`${key.value.value}-${generateId()}`)
1536
+ });
1687
1537
  }
1688
- return VALID;
1538
+ if (unsupported) return null;
1539
+ return applyEdits(code.slice(element.start, element.end), edits.map((edit) => ({
1540
+ ...edit,
1541
+ start: edit.start - element.start,
1542
+ end: edit.end - element.start
1543
+ })));
1544
+ };
1545
+ const duplicateArrayItems = (code, indices, generateId = () => nanoid(6)) => {
1546
+ const data = denseArraySource(code);
1547
+ if (!data || !indices.size || !validArrayIndices(indices, data.elements.length)) return null;
1548
+ const usedIds = /* @__PURE__ */ new Set();
1549
+ import_lib$1.traverseFast(data.array, (node) => {
1550
+ if (import_lib$1.isJSXAttribute(node) && import_lib$1.isJSXIdentifier(node.name, { name: "data-id" }) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
1551
+ else if (import_lib$1.isObjectProperty(node) && !node.computed && (import_lib$1.isIdentifier(node.key, { name: "key" }) || import_lib$1.isStringLiteral(node.key, { value: "key" })) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
1552
+ });
1553
+ const copies = [...indices].sort((a, b) => a - b).map((index) => copyItem(code, data.elements[index], generateId, usedIds));
1554
+ return copies.every((copy) => copy !== null) ? appendArraySource(code, data, copies) : null;
1555
+ };
1556
+ const appendArrayItem = (code, kind, generateId = () => nanoid(6)) => {
1557
+ const index = denseArraySource(code)?.elements.findIndex((node) => kindOf(node) === kind) ?? -1;
1558
+ return index < 0 ? null : duplicateArrayItems(code, /* @__PURE__ */ new Set([index]), generateId);
1689
1559
  };
1690
1560
  //#endregion
1691
- export { createNodeFromValue as A, isComponentTagName as C, resolveBindings as D, readNodeBindings as E, parseValue as F, setEditableValue as I, extractObjectProperties as M, flattenEditableValue as N, arrayExpressionToCode as O, parseArrayExpression as P, getStructuredValue as S, parseBindingExpression as T, moveSelectedIndices as _, getChildrenSignatures as a, getKeyBindings as b, appendArrayItem as c, moveArrayItems as d, parseItems as f, canStructurallyEditArray as g, updateArrayItemValue as h, updateAll as i, extractNodeValue as j, canLosslesslyEvaluateSource as k, duplicateArrayItems as l, updateArrayItemProperty as m, bulkUpdate as n, clearExtractCache as o, removeArrayItems as p, update as r, extract as s, validateBindingValue as t, moveArrayItem as u, findEditableChildren as v, parseBinding as w, getRegistryBindings as x, getCurrentValue as y };
1561
+ export { extractNodeValue as A, getStructuredValue as C, readNodeBindings as D, parseBindingExpression as E, parseArrayExpression as M, parseValue as N, resolveBindings as O, unwrapExpression as P, getRegistryBindings as S, parseBinding as T, clearExtractCache as _, parseItems as a, getCurrentValue as b, updateArrayItemValue as c, update as d, updateAll as f, moveSelectedIndices as g, getChildrenSignatures as h, moveArrayItems as i, extractObjectProperties as j, canLosslesslyEvaluateSource as k, canStructurallyEditArray as l, createNodeFromValue as m, duplicateArrayItems as n, removeArrayItems as o, arrayExpressionToCode as p, moveArrayItem as r, updateArrayItemProperty as s, appendArrayItem as t, bulkUpdate as u, extract as v, isComponentTagName as w, getKeyBindings as x, findEditableChildren as y };
1692
1562
 
1693
- //# sourceMappingURL=ast-dYb78955.js.map
1563
+ //# sourceMappingURL=items-B6OXxcLG.js.map