@jxsuite/studio 1.0.0 → 1.1.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 (86) hide show
  1. package/dist/iframe-entry.js +622 -49
  2. package/dist/iframe-entry.js.map +15 -12
  3. package/dist/studio.css +1333 -0
  4. package/dist/studio.js +53069 -31746
  5. package/dist/studio.js.map +109 -74
  6. package/package.json +11 -9
  7. package/src/canvas/canvas-live-render.ts +21 -2
  8. package/src/canvas/canvas-render.ts +35 -16
  9. package/src/canvas/canvas-utils.ts +118 -72
  10. package/src/canvas/iframe-entry.ts +20 -0
  11. package/src/canvas/iframe-eval.ts +155 -0
  12. package/src/canvas/iframe-host.ts +131 -5
  13. package/src/canvas/iframe-inline-edit.ts +107 -1
  14. package/src/canvas/iframe-protocol.ts +43 -3
  15. package/src/canvas/iframe-render.ts +18 -0
  16. package/src/collab/collab-session.ts +23 -8
  17. package/src/collab/collab-state.ts +6 -3
  18. package/src/component-props.ts +104 -0
  19. package/src/editor/inline-edit-apply.ts +36 -1
  20. package/src/editor/inline-edit.ts +58 -1
  21. package/src/editor/shortcuts.ts +41 -12
  22. package/src/files/file-ops.ts +14 -0
  23. package/src/files/files.ts +53 -1
  24. package/src/grid/cell-editors.ts +288 -0
  25. package/src/grid/cell-popovers.ts +108 -0
  26. package/src/grid/csv-codec.ts +122 -0
  27. package/src/grid/edit-buffer.ts +490 -0
  28. package/src/grid/grid-controller.ts +417 -0
  29. package/src/grid/grid-layout.ts +83 -0
  30. package/src/grid/grid-open.ts +167 -0
  31. package/src/grid/grid-panel.ts +302 -0
  32. package/src/grid/grid-source.ts +210 -0
  33. package/src/grid/grid-view.ts +367 -0
  34. package/src/grid/schema-columns.ts +261 -0
  35. package/src/grid/sources/connector-source.ts +217 -0
  36. package/src/grid/sources/content-source.ts +542 -0
  37. package/src/grid/sources/csv-file-source.ts +247 -0
  38. package/src/grid/tabulator-tables.d.ts +120 -0
  39. package/src/panels/block-action-bar.ts +8 -3
  40. package/src/panels/chat-panel.ts +98 -0
  41. package/src/panels/data-grid.ts +9 -387
  42. package/src/panels/editors.ts +43 -17
  43. package/src/panels/events-panel.ts +137 -44
  44. package/src/panels/formula-workspace.ts +379 -0
  45. package/src/panels/frontmatter-fields.ts +231 -0
  46. package/src/panels/frontmatter-panel.ts +132 -0
  47. package/src/panels/git-panel.ts +1 -1
  48. package/src/panels/head-panel.ts +11 -175
  49. package/src/panels/properties-panel.ts +154 -144
  50. package/src/panels/right-panel.ts +9 -47
  51. package/src/panels/signals-panel.ts +115 -23
  52. package/src/panels/statement-editor.ts +710 -0
  53. package/src/panels/style-panel.ts +25 -1
  54. package/src/panels/stylebook-panel.ts +0 -2
  55. package/src/panels/tab-bar.ts +175 -6
  56. package/src/panels/tab-strip.ts +62 -6
  57. package/src/panels/toolbar.ts +37 -3
  58. package/src/services/ai-project-tools.ts +541 -0
  59. package/src/services/ai-session-store.ts +28 -0
  60. package/src/services/ai-system-prompt.ts +194 -24
  61. package/src/services/ai-tools.ts +88 -21
  62. package/src/services/automation.ts +16 -0
  63. package/src/services/document-assistant.ts +81 -11
  64. package/src/services/gated-registry.ts +72 -0
  65. package/src/services/live-preview.ts +0 -0
  66. package/src/services/preview-eval.ts +68 -0
  67. package/src/services/project-adoption.ts +31 -0
  68. package/src/site-context.ts +2 -0
  69. package/src/store.ts +4 -0
  70. package/src/studio.ts +83 -52
  71. package/src/tabs/patch-ops.ts +25 -0
  72. package/src/tabs/tab.ts +39 -1
  73. package/src/tabs/transact.ts +60 -13
  74. package/src/types.ts +12 -0
  75. package/src/ui/dynamic-slot.ts +272 -0
  76. package/src/ui/expression-editor.ts +423 -125
  77. package/src/ui/field-row.ts +5 -0
  78. package/src/ui/formula-catalog.ts +557 -0
  79. package/src/ui/formula-chips.ts +216 -0
  80. package/src/ui/formula-palette.ts +211 -0
  81. package/src/ui/layers.ts +40 -0
  82. package/src/ui/media-picker.ts +1 -1
  83. package/src/ui/panel-resize.ts +15 -1
  84. package/src/utils/preview-format.ts +26 -0
  85. package/src/view.ts +4 -4
  86. package/src/workspace/workspace.ts +14 -0
@@ -113,8 +113,9 @@ class EffectScope {
113
113
  this._isPaused = true;
114
114
  let i, l;
115
115
  if (this.scopes) {
116
- for (i = 0, l = this.scopes.length;i < l; i++) {
117
- this.scopes[i].pause();
116
+ const scopes = this.scopes.slice();
117
+ for (i = 0, l = scopes.length;i < l; i++) {
118
+ scopes[i].pause();
118
119
  }
119
120
  }
120
121
  for (i = 0, l = this.effects.length;i < l; i++) {
@@ -128,12 +129,14 @@ class EffectScope {
128
129
  this._isPaused = false;
129
130
  let i, l;
130
131
  if (this.scopes) {
131
- for (i = 0, l = this.scopes.length;i < l; i++) {
132
- this.scopes[i].resume();
132
+ const scopes = this.scopes.slice();
133
+ for (i = 0, l = scopes.length;i < l; i++) {
134
+ scopes[i].resume();
133
135
  }
134
136
  }
135
- for (i = 0, l = this.effects.length;i < l; i++) {
136
- this.effects[i].resume();
137
+ const effects = this.effects.slice();
138
+ for (i = 0, l = effects.length;i < l; i++) {
139
+ effects[i].resume();
137
140
  }
138
141
  }
139
142
  }
@@ -187,8 +190,9 @@ class EffectScope {
187
190
  }
188
191
  this.cleanups.length = 0;
189
192
  if (this.scopes) {
190
- for (i = 0, l = this.scopes.length;i < l; i++) {
191
- this.scopes[i].stop(true);
193
+ const scopes = this.scopes.slice();
194
+ for (i = 0, l = scopes.length;i < l; i++) {
195
+ scopes[i].stop(true);
192
196
  }
193
197
  this.scopes.length = 0;
194
198
  }
@@ -1444,33 +1448,142 @@ var BINARY_OPS = new Set([
1444
1448
  ">",
1445
1449
  ">=",
1446
1450
  "&&",
1447
- "||"
1451
+ "||",
1452
+ "??"
1448
1453
  ]);
1449
1454
  var ASSIGNMENT_OPS = new Set(["=", "+=", "-=", "*=", "/="]);
1450
1455
  var ARRAY_METHOD_OPS = new Set(["push", "pop", "shift", "unshift", "splice"]);
1451
1456
  var AGGREGATE_OPS = new Set(["reduce", "map", "filter"]);
1457
+ var CONDITIONAL_OPS = new Set(["?:", "switch"]);
1458
+ var PURE_METHOD_OPS = new Set([
1459
+ "includes",
1460
+ "indexOf",
1461
+ "lastIndexOf",
1462
+ "join",
1463
+ "slice",
1464
+ "concat",
1465
+ "at",
1466
+ "flat",
1467
+ "toSorted",
1468
+ "toReversed",
1469
+ "toSpliced",
1470
+ "with",
1471
+ "toUpperCase",
1472
+ "toLowerCase",
1473
+ "trim",
1474
+ "trimStart",
1475
+ "trimEnd",
1476
+ "split",
1477
+ "startsWith",
1478
+ "endsWith",
1479
+ "padStart",
1480
+ "padEnd",
1481
+ "replaceAll",
1482
+ "repeat",
1483
+ "charAt",
1484
+ "normalize",
1485
+ "toLocaleUpperCase",
1486
+ "toLocaleLowerCase",
1487
+ "toFixed",
1488
+ "toPrecision",
1489
+ "toLocaleString"
1490
+ ]);
1452
1491
  var BLESSED_OPERATORS = new Set([
1453
1492
  ...MUTATING_OPS,
1454
1493
  ...UNARY_OPS,
1455
1494
  ...BINARY_OPS,
1456
- ...AGGREGATE_OPS
1495
+ ...AGGREGATE_OPS,
1496
+ ...CONDITIONAL_OPS,
1497
+ ...PURE_METHOD_OPS,
1498
+ "call"
1499
+ ]);
1500
+ var BLESSED_GLOBALS = new Set([
1501
+ "isNaN",
1502
+ "isFinite",
1503
+ "parseFloat",
1504
+ "parseInt",
1505
+ "encodeURIComponent",
1506
+ "decodeURIComponent",
1507
+ "structuredClone",
1508
+ "Math/abs",
1509
+ "Math/ceil",
1510
+ "Math/floor",
1511
+ "Math/round",
1512
+ "Math/trunc",
1513
+ "Math/sign",
1514
+ "Math/sqrt",
1515
+ "Math/cbrt",
1516
+ "Math/pow",
1517
+ "Math/exp",
1518
+ "Math/log",
1519
+ "Math/log2",
1520
+ "Math/log10",
1521
+ "Math/min",
1522
+ "Math/max",
1523
+ "Math/hypot",
1524
+ "Number/isInteger",
1525
+ "Number/isFinite",
1526
+ "Number/isNaN",
1527
+ "Number/parseFloat",
1528
+ "Number/parseInt",
1529
+ "JSON/parse",
1530
+ "JSON/stringify",
1531
+ "Object/keys",
1532
+ "Object/values",
1533
+ "Object/entries",
1534
+ "Object/fromEntries",
1535
+ "Array/from",
1536
+ "Array/isArray",
1537
+ "Array/of",
1538
+ "String/fromCharCode",
1539
+ "String/fromCodePoint",
1540
+ "Intl/formatNumber",
1541
+ "Intl/formatDate",
1542
+ "Intl/formatRelativeTime"
1457
1543
  ]);
1544
+ var BLESSED_HELPERS = {
1545
+ "Intl/formatDate": (value, locale, options) => new Intl.DateTimeFormat(locale, options).format(new Date(value)),
1546
+ "Intl/formatNumber": (value, locale, options) => new Intl.NumberFormat(locale, options).format(value),
1547
+ "Intl/formatRelativeTime": (value, unit, locale, options) => new Intl.RelativeTimeFormat(locale, options).format(value, unit)
1548
+ };
1549
+ function isBlessedGlobal(ref2) {
1550
+ return ref2.startsWith("window#/") && BLESSED_GLOBALS.has(ref2.slice("window#/".length));
1551
+ }
1458
1552
  function isMutating(op) {
1459
1553
  return MUTATING_OPS.has(op);
1460
1554
  }
1461
- function resolveOperand(operand, state, event, iterCtx) {
1555
+ var MAX_CALL_DEPTH = 64;
1556
+ var MAX_REPORT_DEPTH = 64;
1557
+ function subTrace(trace, seg) {
1558
+ if (!trace) {
1559
+ return;
1560
+ }
1561
+ const depth = (trace.depth ?? 0) + 1;
1562
+ if (depth > MAX_REPORT_DEPTH) {
1563
+ return;
1564
+ }
1565
+ return {
1566
+ depth,
1567
+ path: [...trace.path ?? [], ...Array.isArray(seg) ? seg : [seg]],
1568
+ report: trace.report
1569
+ };
1570
+ }
1571
+ function resolveOperand(operand, state, event, iterCtx, trace) {
1462
1572
  if (operand === null || operand === undefined) {
1463
1573
  return operand;
1464
1574
  }
1465
1575
  if (typeof operand === "object" && !Array.isArray(operand) && "operator" in operand) {
1466
- return evaluateExpression(operand, state, event, iterCtx);
1576
+ return evaluateExpression(operand, state, event, iterCtx, trace);
1467
1577
  }
1468
1578
  if (typeof operand === "object" && !Array.isArray(operand) && "$ref" in operand) {
1469
- return resolveExprRef(operand.$ref, state, event, iterCtx);
1579
+ const resolved = resolveExprRef(operand.$ref, state, event, iterCtx);
1580
+ trace?.report(trace.path ?? [], resolved);
1581
+ return resolved;
1470
1582
  }
1471
1583
  if (Array.isArray(operand)) {
1472
- return operand.map((o) => resolveOperand(o, state, event, iterCtx));
1584
+ return operand.map((o, i) => resolveOperand(o, state, event, iterCtx, subTrace(trace, i)));
1473
1585
  }
1586
+ trace?.report(trace.path ?? [], operand);
1474
1587
  return operand;
1475
1588
  }
1476
1589
  function resolveExprRef(ref2, state, event, iterCtx) {
@@ -1480,6 +1593,11 @@ function resolveExprRef(ref2, state, event, iterCtx) {
1480
1593
  if (ref2.startsWith("$reduce/")) {
1481
1594
  return iterCtx?.acc;
1482
1595
  }
1596
+ if (ref2.startsWith("$args/")) {
1597
+ const parts = ref2.slice("$args/".length).split("/");
1598
+ const base = iterCtx?.args?.[parts[0]];
1599
+ return parts.length > 1 ? getPath(base, parts.slice(1).join("/")) : base;
1600
+ }
1483
1601
  if (ref2.startsWith("event#/")) {
1484
1602
  const path = ref2.slice("event#/".length);
1485
1603
  return getPath(event, path);
@@ -1557,13 +1675,18 @@ function resolveWritableRef(ref2, state, _event, iterCtx) {
1557
1675
  }
1558
1676
  return { key: ref2, obj: state };
1559
1677
  }
1560
- function evaluateExpression(node, state, event, iterCtx) {
1678
+ function evaluateExpression(node, state, event, iterCtx, trace) {
1679
+ const result = evaluateNode(node, state, event, iterCtx, trace);
1680
+ trace?.report(trace.path ?? [], result);
1681
+ return result;
1682
+ }
1683
+ function evaluateNode(node, state, event, iterCtx, trace) {
1561
1684
  const { operator, target, value, initial } = node;
1562
1685
  if (!BLESSED_OPERATORS.has(operator)) {
1563
1686
  throw new Error(`$expression: unknown operator "${operator}"`);
1564
1687
  }
1565
1688
  if (UNARY_OPS.has(operator) && !("value" in node)) {
1566
- const operand = resolveOperand(target, state, event, iterCtx);
1689
+ const operand = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1567
1690
  if (operator === "!") {
1568
1691
  return !operand;
1569
1692
  }
@@ -1571,9 +1694,78 @@ function evaluateExpression(node, state, event, iterCtx) {
1571
1694
  return -operand;
1572
1695
  }
1573
1696
  }
1697
+ if (operator === "call") {
1698
+ const calleeRef = target?.$ref;
1699
+ if (typeof calleeRef !== "string") {
1700
+ throw new TypeError("$expression: call target must be a $ref pointer");
1701
+ }
1702
+ const callDepth = (iterCtx?.callDepth ?? 0) + 1;
1703
+ if (callDepth > MAX_CALL_DEPTH) {
1704
+ throw new Error(`$expression: call depth exceeded (${MAX_CALL_DEPTH})`);
1705
+ }
1706
+ const argValues = Array.isArray(value) ? value.map((o, i) => resolveOperand(o, state, event, iterCtx, subTrace(trace, ["value", i]))) : [];
1707
+ if (calleeRef.startsWith("window#/")) {
1708
+ if (!isBlessedGlobal(calleeRef)) {
1709
+ throw new Error(`$expression: "${calleeRef}" is not a blessed pure global`);
1710
+ }
1711
+ const globalPath = calleeRef.slice("window#/".length);
1712
+ const helper = BLESSED_HELPERS[globalPath];
1713
+ if (helper) {
1714
+ return helper(...argValues);
1715
+ }
1716
+ const fn = getPath(globalThis.window, globalPath);
1717
+ const lastSlash = globalPath.lastIndexOf("/");
1718
+ const thisArg = lastSlash === -1 ? globalThis.window : getPath(globalThis.window, globalPath.slice(0, lastSlash));
1719
+ return fn.apply(thisArg, argValues);
1720
+ }
1721
+ const callee = resolveExprRef(calleeRef, state, event, iterCtx);
1722
+ if (typeof callee === "function") {
1723
+ return callee(...argValues);
1724
+ }
1725
+ if (callee && typeof callee === "object" && "$expression" in callee) {
1726
+ const def2 = callee;
1727
+ const args = {};
1728
+ for (const [i, p] of (def2.parameters ?? []).entries()) {
1729
+ const name = typeof p === "string" ? p : p?.name ?? "";
1730
+ if (!name) {
1731
+ continue;
1732
+ }
1733
+ args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
1734
+ }
1735
+ return evaluateExpression(def2.$expression, state, event, { args, callDepth });
1736
+ }
1737
+ throw new TypeError(`$expression: call target "${calleeRef}" is not callable`);
1738
+ }
1739
+ if (CONDITIONAL_OPS.has(operator)) {
1740
+ if (operator === "?:") {
1741
+ const test = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1742
+ if (trace) {
1743
+ const consequent = resolveOperand(value, state, event, iterCtx, subTrace(trace, "value"));
1744
+ const alternate = resolveOperand(initial, state, event, iterCtx, subTrace(trace, "initial"));
1745
+ return test ? consequent : alternate;
1746
+ }
1747
+ return test ? resolveOperand(value, state, event, iterCtx) : resolveOperand(initial, state, event, iterCtx);
1748
+ }
1749
+ const discriminant = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1750
+ const key = String(discriminant);
1751
+ const cases = node.cases ?? {};
1752
+ const matched = Object.hasOwn(cases, key);
1753
+ if (trace) {
1754
+ let result;
1755
+ for (const [k, caseOperand] of Object.entries(cases)) {
1756
+ const v = resolveOperand(caseOperand, state, event, iterCtx, subTrace(trace, ["cases", k]));
1757
+ if (k === key) {
1758
+ result = v;
1759
+ }
1760
+ }
1761
+ const fallback = resolveOperand(node.default, state, event, iterCtx, subTrace(trace, "default"));
1762
+ return matched ? result : fallback;
1763
+ }
1764
+ return matched ? resolveOperand(cases[key], state, event, iterCtx) : resolveOperand(node.default, state, event, iterCtx);
1765
+ }
1574
1766
  if (BINARY_OPS.has(operator)) {
1575
- const left = resolveOperand(target, state, event, iterCtx);
1576
- const right = resolveOperand(value, state, event, iterCtx);
1767
+ const left = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1768
+ const right = resolveOperand(value, state, event, iterCtx, subTrace(trace, "value"));
1577
1769
  switch (operator) {
1578
1770
  case "+": {
1579
1771
  return left + right;
@@ -1614,13 +1806,16 @@ function evaluateExpression(node, state, event, iterCtx) {
1614
1806
  case "||": {
1615
1807
  return left || right;
1616
1808
  }
1809
+ case "??": {
1810
+ return left ?? right;
1811
+ }
1617
1812
  default: {
1618
1813
  break;
1619
1814
  }
1620
1815
  }
1621
1816
  }
1622
1817
  if (ASSIGNMENT_OPS.has(operator)) {
1623
- const rhs = resolveOperand(value, state, event, iterCtx);
1818
+ const rhs = resolveOperand(value, state, event, iterCtx, subTrace(trace, "value"));
1624
1819
  const { obj, key } = resolveWritableRef(target.$ref, state, event, iterCtx);
1625
1820
  switch (operator) {
1626
1821
  case "=": {
@@ -1654,13 +1849,13 @@ function evaluateExpression(node, state, event, iterCtx) {
1654
1849
  return;
1655
1850
  }
1656
1851
  if (ARRAY_METHOD_OPS.has(operator)) {
1657
- const arr = resolveOperand(target, state, event, iterCtx);
1852
+ const arr = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1658
1853
  switch (operator) {
1659
1854
  case "push": {
1660
- return arr.push(resolveOperand(value, state, event, iterCtx));
1855
+ return arr.push(resolveOperand(value, state, event, iterCtx, subTrace(trace, "value")));
1661
1856
  }
1662
1857
  case "unshift": {
1663
- return arr.unshift(resolveOperand(value, state, event, iterCtx));
1858
+ return arr.unshift(resolveOperand(value, state, event, iterCtx, subTrace(trace, "value")));
1664
1859
  }
1665
1860
  case "pop": {
1666
1861
  return arr.pop();
@@ -1669,7 +1864,7 @@ function evaluateExpression(node, state, event, iterCtx) {
1669
1864
  return arr.shift();
1670
1865
  }
1671
1866
  case "splice": {
1672
- const args = resolveOperand(value, state, event, iterCtx);
1867
+ const args = resolveOperand(value, state, event, iterCtx, subTrace(trace, "value"));
1673
1868
  return arr.splice(...args);
1674
1869
  }
1675
1870
  default: {
@@ -1677,32 +1872,29 @@ function evaluateExpression(node, state, event, iterCtx) {
1677
1872
  }
1678
1873
  }
1679
1874
  }
1875
+ if (PURE_METHOD_OPS.has(operator)) {
1876
+ const receiver = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1877
+ const args = value === undefined ? [] : Array.isArray(value) ? resolveOperand(value, state, event, iterCtx, subTrace(trace, "value")) : [resolveOperand(value, state, event, iterCtx, subTrace(trace, "value"))];
1878
+ const method = receiver?.[operator];
1879
+ if (typeof method !== "function") {
1880
+ return;
1881
+ }
1882
+ return method.apply(receiver, args);
1883
+ }
1680
1884
  if (AGGREGATE_OPS.has(operator)) {
1681
- const arr = resolveOperand(target, state, event, iterCtx);
1885
+ const arr = resolveOperand(target, state, event, iterCtx, subTrace(trace, "target"));
1682
1886
  if (operator === "reduce") {
1683
- let acc = resolveOperand(initial, state, event, iterCtx);
1887
+ let acc = resolveOperand(initial, state, event, iterCtx, subTrace(trace, "initial"));
1684
1888
  for (const [index, item] of arr.entries()) {
1685
- acc = evaluateExpression(value, state, event, {
1686
- acc,
1687
- index,
1688
- item
1689
- });
1889
+ acc = evaluateExpression(value, state, event, { acc, index, item }, index === 0 ? subTrace(trace, "value") : undefined);
1690
1890
  }
1691
1891
  return acc;
1692
1892
  }
1693
1893
  if (operator === "map") {
1694
- return arr.map((item, index) => evaluateExpression(value, state, event, {
1695
- ...iterCtx,
1696
- index,
1697
- item
1698
- }));
1894
+ return arr.map((item, index) => evaluateExpression(value, state, event, { ...iterCtx, index, item }, index === 0 ? subTrace(trace, "value") : undefined));
1699
1895
  }
1700
1896
  if (operator === "filter") {
1701
- return arr.filter((item, index) => evaluateExpression(value, state, event, {
1702
- ...iterCtx,
1703
- index,
1704
- item
1705
- }));
1897
+ return arr.filter((item, index) => evaluateExpression(value, state, event, { ...iterCtx, index, item }, index === 0 ? subTrace(trace, "value") : undefined));
1706
1898
  }
1707
1899
  }
1708
1900
  return;
@@ -1714,6 +1906,9 @@ function getPath(obj, path) {
1714
1906
  }
1715
1907
  return current;
1716
1908
  }
1909
+ function evaluateOperand(operand, state, event, iterCtx) {
1910
+ return resolveOperand(operand, state, event, iterCtx);
1911
+ }
1717
1912
 
1718
1913
  // ../schema/src/guards.ts
1719
1914
  function isJsonObject(value) {
@@ -1728,6 +1923,12 @@ function isFunctionDef(value) {
1728
1923
  function isExpressionDef(value) {
1729
1924
  return isJsonObject(value) && isJsonObject(value.$expression);
1730
1925
  }
1926
+ function isNamedFormulaDef(value) {
1927
+ return isExpressionDef(value) && Array.isArray(value.parameters) && value.parameters.length > 0;
1928
+ }
1929
+ function hasStructuredBody(value) {
1930
+ return isFunctionDef(value) && Array.isArray(value.body);
1931
+ }
1731
1932
  function isPrototypeDef(value) {
1732
1933
  return isJsonObject(value) && typeof value.$prototype === "string" && value.$prototype !== "Function";
1733
1934
  }
@@ -1763,6 +1964,74 @@ function paramNames(parameters) {
1763
1964
  return parameters.map((p) => typeof p === "string" ? p : p.name);
1764
1965
  }
1765
1966
 
1967
+ // ../runtime/src/statements.ts
1968
+ function statementKind(statement) {
1969
+ if ("operator" in statement) {
1970
+ return "expression";
1971
+ }
1972
+ if ("if" in statement) {
1973
+ return "if";
1974
+ }
1975
+ if ("$switch" in statement) {
1976
+ return "switch";
1977
+ }
1978
+ return "dispatch";
1979
+ }
1980
+ async function runStatements(statements, state, event, opts = {}) {
1981
+ const iterCtx = opts.args ? { args: opts.args } : undefined;
1982
+ for (const statement of statements) {
1983
+ switch (statementKind(statement)) {
1984
+ case "expression": {
1985
+ const result = evaluateExpression(statement, state, event, iterCtx);
1986
+ if (result && typeof result.then === "function") {
1987
+ await result;
1988
+ }
1989
+ break;
1990
+ }
1991
+ case "if": {
1992
+ const branch = statement;
1993
+ const taken = evaluateOperand(branch.if, state, event, iterCtx) ? branch.then : branch.else;
1994
+ if (Array.isArray(taken)) {
1995
+ await runStatements(taken, state, event, opts);
1996
+ }
1997
+ break;
1998
+ }
1999
+ case "switch": {
2000
+ const branch = statement;
2001
+ const key = String(evaluateOperand(branch.$switch, state, event, iterCtx));
2002
+ const cases = branch.cases ?? {};
2003
+ const taken = Object.hasOwn(cases, key) ? cases[key] : branch.default;
2004
+ if (Array.isArray(taken)) {
2005
+ await runStatements(taken, state, event, opts);
2006
+ }
2007
+ break;
2008
+ }
2009
+ case "dispatch": {
2010
+ const dispatch = statement;
2011
+ const target = opts.target ?? event?.currentTarget;
2012
+ if (!target) {
2013
+ break;
2014
+ }
2015
+ const init = {};
2016
+ if ("detail" in dispatch) {
2017
+ init.detail = evaluateOperand(dispatch.detail, state, event, iterCtx);
2018
+ }
2019
+ if (dispatch.bubbles !== undefined) {
2020
+ init.bubbles = dispatch.bubbles;
2021
+ }
2022
+ if (dispatch.composed !== undefined) {
2023
+ init.composed = dispatch.composed;
2024
+ }
2025
+ target.dispatchEvent(new CustomEvent(dispatch.dispatchEvent, init));
2026
+ break;
2027
+ }
2028
+ default: {
2029
+ break;
2030
+ }
2031
+ }
2032
+ }
2033
+ }
2034
+
1766
2035
  // ../runtime/src/runtime.ts
1767
2036
  var _resolveCache = new Map;
1768
2037
  async function resolve(source) {
@@ -1851,6 +2120,37 @@ function canvasAttrName(el, key) {
1851
2120
  }
1852
2121
  return key;
1853
2122
  }
2123
+ var _stampPropBindings = false;
2124
+ function setStampPropBindings(on) {
2125
+ _stampPropBindings = on;
2126
+ }
2127
+ var PURE_STATE_TEMPLATE = /^\s*\$\{\s*state\.(\w+)\s*\}\s*$/;
2128
+ function boundPropKey(key, val, state) {
2129
+ if (!_stampPropBindings || key !== "textContent") {
2130
+ return null;
2131
+ }
2132
+ let sub = null;
2133
+ if (isRefObj(val)) {
2134
+ if (!val.$ref.startsWith("#/state/")) {
2135
+ return null;
2136
+ }
2137
+ const rest = val.$ref.slice("#/state/".length);
2138
+ sub = rest && !rest.includes("/") ? rest : null;
2139
+ } else if (typeof val === "string") {
2140
+ sub = PURE_STATE_TEMPLATE.exec(val)?.[1] ?? null;
2141
+ }
2142
+ if (!sub) {
2143
+ return null;
2144
+ }
2145
+ const stored = toRaw(state)[sub];
2146
+ if (isRef(stored) || typeof stored === "function") {
2147
+ return null;
2148
+ }
2149
+ if (typeof stored === "object" && stored !== null) {
2150
+ return null;
2151
+ }
2152
+ return sub;
2153
+ }
1854
2154
  async function buildScope(doc, parentScope = {}, base = location.href) {
1855
2155
  const raw = {};
1856
2156
  for (const [key, val] of Object.entries(parentScope)) {
@@ -1914,7 +2214,20 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1914
2214
  for (const [key, def2] of Object.entries(defs)) {
1915
2215
  if (isExpressionDef(def2)) {
1916
2216
  const node = def2.$expression;
1917
- if (isMutating(node.operator)) {
2217
+ if (isNamedFormulaDef(def2)) {
2218
+ const params = def2.parameters;
2219
+ state[key] = (...argValues) => {
2220
+ const args = {};
2221
+ for (const [i, p] of params.entries()) {
2222
+ const name = typeof p === "string" ? p : p?.name ?? "";
2223
+ if (!name) {
2224
+ continue;
2225
+ }
2226
+ args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
2227
+ }
2228
+ return evaluateExpression(node, state, null, { args });
2229
+ };
2230
+ } else if (isMutating(node.operator)) {
1918
2231
  const handler = (s, event) => evaluateExpression(node, s, event);
1919
2232
  state[key] = handler;
1920
2233
  } else {
@@ -1923,7 +2236,28 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1923
2236
  }
1924
2237
  }
1925
2238
  for (const [key, def2] of Object.entries(defs)) {
1926
- if (isFunctionDef(def2)) {
2239
+ if (hasStructuredBody(def2)) {
2240
+ const { body } = def2;
2241
+ if (Array.isArray(def2.parameters) && def2.parameters.length > 0) {
2242
+ const params = def2.parameters;
2243
+ state[key] = (...argValues) => {
2244
+ const args = {};
2245
+ for (const [i, p] of params.entries()) {
2246
+ const name = typeof p === "string" ? p : p?.name ?? "";
2247
+ if (!name) {
2248
+ continue;
2249
+ }
2250
+ args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
2251
+ }
2252
+ return runStatements(body, state, null, { args });
2253
+ };
2254
+ } else {
2255
+ const handler = (s, event) => {
2256
+ runStatements(body, s, event ?? null);
2257
+ };
2258
+ state[key] = handler;
2259
+ }
2260
+ } else if (isFunctionDef(def2)) {
1927
2261
  state[key] = await resolveFunction(def2, state, key, base);
1928
2262
  }
1929
2263
  }
@@ -1974,7 +2308,7 @@ async function resolveFunction(def2, state, key, base) {
1974
2308
  return noop;
1975
2309
  }
1976
2310
  let fn;
1977
- if (def2.body) {
2311
+ if (typeof def2.body === "string") {
1978
2312
  const params = resolveParamNames(def2);
1979
2313
  fn = new Function(...params, def2.body);
1980
2314
  Object.defineProperty(fn, "name", {
@@ -2012,7 +2346,7 @@ async function resolveFunction(def2, state, key, base) {
2012
2346
  const hasParams = (def2.parameters ?? def2.arguments ?? []).length > 0;
2013
2347
  let isComputed = false;
2014
2348
  if (!hasParams) {
2015
- if (def2.body) {
2349
+ if (typeof def2.body === "string") {
2016
2350
  isComputed = /\breturn\b/.test(def2.body);
2017
2351
  } else if (fn) {
2018
2352
  isComputed = fn.length <= 1 && /\breturn\b/.test(fn.toString());
@@ -2138,7 +2472,15 @@ function applyProperties(el, def2, state) {
2138
2472
  }
2139
2473
  continue;
2140
2474
  }
2141
- if (isFunctionDef(val) && val.body) {
2475
+ if (hasStructuredBody(val)) {
2476
+ const { body } = val;
2477
+ const scope = state;
2478
+ el.addEventListener(key.slice(2), (e) => {
2479
+ runStatements(body, scope, e);
2480
+ });
2481
+ continue;
2482
+ }
2483
+ if (isFunctionDef(val) && typeof val.body === "string") {
2142
2484
  const params = resolveParamNames(val);
2143
2485
  const fn = new Function(...params, val.body);
2144
2486
  const scope = state;
@@ -2157,6 +2499,10 @@ function applyProperties(el, def2, state) {
2157
2499
  }
2158
2500
  function bindProperty(el, key, val, state) {
2159
2501
  const target = el;
2502
+ const boundProp = boundPropKey(key, val, state);
2503
+ if (boundProp) {
2504
+ el.dataset.jxBoundProp = boundProp;
2505
+ }
2160
2506
  if (isRefObj(val)) {
2161
2507
  const refVal = val;
2162
2508
  if (key === "id") {
@@ -3304,6 +3650,18 @@ var EDIT_PLACEHOLDER_CSS = `
3304
3650
  font-size: 13px;
3305
3651
  pointer-events: none;
3306
3652
  }
3653
+ [data-jx-bound-prop]:hover {
3654
+ cursor: text;
3655
+ outline: 1px dashed color-mix(in srgb, #808080 40%, transparent);
3656
+ outline-offset: 1px;
3657
+ }
3658
+ [data-jx-bound-prop]:empty:not([contenteditable="plaintext-only"]):not([contenteditable="true"])::after {
3659
+ content: "Empty \\2014 double-click to edit";
3660
+ color: color-mix(in srgb, #808080 40%, transparent);
3661
+ font-style: italic;
3662
+ font-size: 13px;
3663
+ pointer-events: none;
3664
+ }
3307
3665
  `;
3308
3666
  function syncEditModeCss(doc, mode) {
3309
3667
  const existing = doc.head.querySelector(`#${EDIT_PLACEHOLDER_STYLE_ID}`);
@@ -3509,6 +3867,7 @@ async function renderResolvedDocument(opts) {
3509
3867
  setSkipServerFunctions(opts.mode !== "preview");
3510
3868
  setCanvasViewportTranspose(true);
3511
3869
  setCanvasDelinkAnchors(opts.mode !== "preview");
3870
+ setStampPropBindings(opts.mode === "design" || opts.mode === "edit");
3512
3871
  applySiteStyle(opts.siteStyle);
3513
3872
  injectHead(opts.doc);
3514
3873
  syncEditModeCss(opts.container.ownerDocument, opts.mode);
@@ -4073,6 +4432,8 @@ var splitFn = null;
4073
4432
  var insertFn = null;
4074
4433
  var endFn = null;
4075
4434
  var _blurCloseSuspended = false;
4435
+ var _plainMode = false;
4436
+ var _plainOriginal = "";
4076
4437
  function suspendBlurClose() {
4077
4438
  _blurCloseSuspended = true;
4078
4439
  }
@@ -4082,7 +4443,7 @@ function resumeBlurClose() {
4082
4443
  function isEditableBlock(el) {
4083
4444
  return EDITABLE_BLOCKS.has(el.tagName.toLowerCase());
4084
4445
  }
4085
- function startEditing(el, path, callbacks) {
4446
+ function startEditing(el, path, callbacks, opts) {
4086
4447
  if (activeEl) {
4087
4448
  stopEditing(true);
4088
4449
  }
@@ -4092,7 +4453,17 @@ function startEditing(el, path, callbacks) {
4092
4453
  splitFn = callbacks.onSplit;
4093
4454
  insertFn = callbacks.onInsert;
4094
4455
  endFn = callbacks.onEnd;
4095
- el.contentEditable = "true";
4456
+ _plainMode = opts?.plainText === true;
4457
+ _plainOriginal = _plainMode ? el.textContent ?? "" : "";
4458
+ if (_plainMode) {
4459
+ try {
4460
+ el.contentEditable = "plaintext-only";
4461
+ } catch {
4462
+ el.contentEditable = "true";
4463
+ }
4464
+ } else {
4465
+ el.contentEditable = "true";
4466
+ }
4096
4467
  el.style.pointerEvents = "auto";
4097
4468
  el.style.outline = "2px solid var(--accent, #4a9eff)";
4098
4469
  el.style.outlineOffset = "1px";
@@ -4131,6 +4502,8 @@ function stopEditing(silent = false) {
4131
4502
  commitFn = null;
4132
4503
  splitFn = null;
4133
4504
  insertFn = null;
4505
+ _plainMode = false;
4506
+ _plainOriginal = "";
4134
4507
  if (silent) {
4135
4508
  endFn = null;
4136
4509
  } else if (endFn) {
@@ -4152,6 +4525,27 @@ function isSlashActive() {
4152
4525
  return slash.isOpen();
4153
4526
  }
4154
4527
  function handleKeydown(e) {
4528
+ if (_plainMode) {
4529
+ if (e.key === "Escape") {
4530
+ e.preventDefault();
4531
+ e.stopPropagation();
4532
+ if (activeEl) {
4533
+ activeEl.textContent = _plainOriginal;
4534
+ }
4535
+ stopEditing();
4536
+ return;
4537
+ }
4538
+ if (e.key === "Enter") {
4539
+ e.preventDefault();
4540
+ e.stopPropagation();
4541
+ stopEditing();
4542
+ return;
4543
+ }
4544
+ if ((e.ctrlKey || e.metaKey) && ["b", "i", "u", "`"].includes(e.key)) {
4545
+ e.preventDefault();
4546
+ }
4547
+ return;
4548
+ }
4155
4549
  if (e.key === "Escape") {
4156
4550
  e.preventDefault();
4157
4551
  e.stopPropagation();
@@ -4271,6 +4665,11 @@ function commitChanges() {
4271
4665
  if (!commitFn || !activeEl || !activePath) {
4272
4666
  return;
4273
4667
  }
4668
+ if (_plainMode) {
4669
+ commitFn(activePath, null, (activeEl.textContent ?? "").replaceAll(`
4670
+ `, " "));
4671
+ return;
4672
+ }
4274
4673
  normalizeInlineContent(activeEl);
4275
4674
  const result = elementToJx(activeEl);
4276
4675
  commitFn(activePath, result.children ?? null, result.textContent ?? null);
@@ -5152,6 +5551,31 @@ function findEditableTarget(target) {
5152
5551
  }
5153
5552
  return null;
5154
5553
  }
5554
+ function findPropBoundTarget(target) {
5555
+ let el = target instanceof Element ? target : null;
5556
+ while (el) {
5557
+ if (el instanceof HTMLElement) {
5558
+ if (el.dataset.jxBoundProp) {
5559
+ return { el, prop: el.dataset.jxBoundProp };
5560
+ }
5561
+ if (el.dataset.jxPath) {
5562
+ return null;
5563
+ }
5564
+ }
5565
+ el = el.parentElement;
5566
+ }
5567
+ return null;
5568
+ }
5569
+ function ownerInstanceOf(el) {
5570
+ let cur = el.parentElement;
5571
+ while (cur) {
5572
+ if (cur.tagName.includes("-")) {
5573
+ return cur;
5574
+ }
5575
+ cur = cur.parentElement;
5576
+ }
5577
+ return null;
5578
+ }
5155
5579
  function elementForPath(container, path) {
5156
5580
  const serialized = serializeJxPath(path);
5157
5581
  const esc = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
@@ -5268,6 +5692,36 @@ function startIframeInlineEdit(channel, container, opts) {
5268
5692
  cacheRange();
5269
5693
  onSelectionChange();
5270
5694
  };
5695
+ const propEditableAt = (hostPath, prop) => {
5696
+ const shadowDoc = opts?.getShadowDoc?.();
5697
+ if (!shadowDoc) {
5698
+ return true;
5699
+ }
5700
+ const node = getNodeAtPath(shadowDoc, hostPath);
5701
+ const raw = node?.$props?.[prop];
5702
+ if (raw == null) {
5703
+ return true;
5704
+ }
5705
+ if (typeof raw === "object") {
5706
+ return false;
5707
+ }
5708
+ return !(typeof raw === "string" && raw.includes("${"));
5709
+ };
5710
+ const enterPropEditAt = (el, hostPath, prop) => {
5711
+ channel.post({ kind: "editStart", path: hostPath, prop });
5712
+ suspendBlurClose();
5713
+ startEditing(el, hostPath, {
5714
+ onCommit: (p, _children, textContent) => channel.post({ kind: "editCommitProp", path: p, prop, value: textContent ?? "" }),
5715
+ onEnd: () => {
5716
+ resumeBlurClose();
5717
+ clearHighlight();
5718
+ lastNonEmptyRange = null;
5719
+ channel.post({ kind: "editEnd" });
5720
+ },
5721
+ onInsert: () => {},
5722
+ onSplit: () => {}
5723
+ }, { plainText: true });
5724
+ };
5271
5725
  const applyFormatIntent = (intent) => {
5272
5726
  const el = getActiveElement();
5273
5727
  if (!el) {
@@ -5296,6 +5750,18 @@ function startIframeInlineEdit(channel, container, opts) {
5296
5750
  if (!editingAllowed()) {
5297
5751
  return;
5298
5752
  }
5753
+ const propHit = findPropBoundTarget(e.target);
5754
+ if (propHit) {
5755
+ const host = ownerInstanceOf(propHit.el);
5756
+ const hostPathRaw = host?.dataset.jxPath;
5757
+ if (host && hostPathRaw) {
5758
+ const hostPath = parseJxPath(hostPathRaw);
5759
+ if (propEditableAt(hostPath, propHit.prop)) {
5760
+ enterPropEditAt(propHit.el, hostPath, propHit.prop);
5761
+ }
5762
+ }
5763
+ return;
5764
+ }
5299
5765
  const hit = findEditableTarget(e.target);
5300
5766
  if (hit) {
5301
5767
  enterEditAt(hit.el, hit.path);
@@ -5850,6 +6316,107 @@ function textDisplayValue(node) {
5850
6316
  return String(v);
5851
6317
  }
5852
6318
 
6319
+ // src/utils/preview-format.ts
6320
+ var MAX_BADGE_LENGTH = 48;
6321
+ function formatPreviewValue(value) {
6322
+ if (value === undefined) {
6323
+ return "undefined";
6324
+ }
6325
+ let text;
6326
+ try {
6327
+ text = typeof value === "string" ? JSON.stringify(value) : JSON.stringify(value) ?? "undefined";
6328
+ } catch {
6329
+ text = String(value);
6330
+ }
6331
+ if (text.length > MAX_BADGE_LENGTH) {
6332
+ text = `${text.slice(0, MAX_BADGE_LENGTH - 1)}…`;
6333
+ }
6334
+ return text;
6335
+ }
6336
+
6337
+ // src/canvas/iframe-eval.ts
6338
+ function isNamedFormulaEntry(entry) {
6339
+ return typeof entry === "object" && entry !== null && !Array.isArray(entry) && "$expression" in entry && Array.isArray(entry.parameters);
6340
+ }
6341
+ function cloneLiveScope(defs, rawState) {
6342
+ const out = {};
6343
+ for (const key of Object.keys(defs)) {
6344
+ try {
6345
+ const value = defs[key];
6346
+ if (typeof value === "function") {
6347
+ if (isNamedFormulaEntry(rawState?.[key])) {
6348
+ out[key] = value;
6349
+ }
6350
+ continue;
6351
+ }
6352
+ const json = JSON.stringify(value);
6353
+ out[key] = json === undefined ? null : JSON.parse(json);
6354
+ } catch {
6355
+ out[key] = null;
6356
+ }
6357
+ }
6358
+ return out;
6359
+ }
6360
+ function bindRepeaterContext(scope, shadowDoc, contextPath) {
6361
+ if (!contextPath || contextPath.length === 0) {
6362
+ return scope;
6363
+ }
6364
+ let node = shadowDoc;
6365
+ let bound = scope;
6366
+ for (const seg of contextPath) {
6367
+ if (!node || typeof node !== "object") {
6368
+ break;
6369
+ }
6370
+ const rec = node;
6371
+ if (rec.$prototype === "Array" && seg === "map") {
6372
+ const itemsDef = rec.items;
6373
+ const items = itemsDef && typeof itemsDef === "object" && "$ref" in itemsDef ? resolveRef(itemsDef.$ref, bound) : itemsDef;
6374
+ if (Array.isArray(items) && items.length > 0) {
6375
+ let item;
6376
+ try {
6377
+ item = structuredClone(items[0]);
6378
+ } catch {
6379
+ item = null;
6380
+ }
6381
+ const child = Object.create(bound);
6382
+ child.$map = { index: 0, item };
6383
+ child["$map/item"] = item;
6384
+ child["$map/index"] = 0;
6385
+ bound = child;
6386
+ }
6387
+ }
6388
+ node = rec[seg];
6389
+ }
6390
+ return bound;
6391
+ }
6392
+ function evaluateLiveExprs(exprs, defs, shadowDoc, contextPath) {
6393
+ const rawState = shadowDoc && typeof shadowDoc === "object" ? shadowDoc.state ?? null : null;
6394
+ return exprs.map(({ id, node }) => {
6395
+ const values = new Map;
6396
+ let scope;
6397
+ try {
6398
+ scope = bindRepeaterContext(cloneLiveScope(defs, rawState), shadowDoc, contextPath);
6399
+ } catch (error) {
6400
+ return { error: error instanceof Error ? error.message : String(error), id, values: [] };
6401
+ }
6402
+ try {
6403
+ if (!node || typeof node !== "object" || !("operator" in node)) {
6404
+ throw new Error("not an expression node");
6405
+ }
6406
+ evaluateExpression(node, scope, null, undefined, {
6407
+ report: (path, value) => values.set(path.join("/"), formatPreviewValue(value))
6408
+ });
6409
+ return { id, values: [...values.entries()] };
6410
+ } catch (error) {
6411
+ return {
6412
+ error: error instanceof Error ? error.message : String(error),
6413
+ id,
6414
+ values: [...values.entries()]
6415
+ };
6416
+ }
6417
+ });
6418
+ }
6419
+
5853
6420
  // src/canvas/serialize-scope.ts
5854
6421
  var MAX_VALUE_CHARS = 256000;
5855
6422
  var LARGE_VALUE_PLACEHOLDER = "[large value omitted]";
@@ -5983,7 +6550,8 @@ function startCanvasIframe(opts) {
5983
6550
  });
5984
6551
  const stopKeyForwarding = startKeyForwarding(channel, container.ownerDocument);
5985
6552
  const stopInlineEdit = startIframeInlineEdit(channel, container, {
5986
- getMode: () => currentMode
6553
+ getMode: () => currentMode,
6554
+ getShadowDoc: () => shadowDoc
5987
6555
  });
5988
6556
  const stopSlashBridge = startIframeSlashBridge(channel, container.ownerDocument);
5989
6557
  const stopGrabDetector = startGrabDetector(channel, container.ownerDocument, {
@@ -6074,6 +6642,11 @@ function startCanvasIframe(opts) {
6074
6642
  });
6075
6643
  return;
6076
6644
  }
6645
+ if (msg.kind === "evalExpr") {
6646
+ const results = msg.gen === renderedGen && renderCtx && shadowDoc ? evaluateLiveExprs(msg.exprs, renderCtx.defs, shadowDoc, msg.contextPath) : [];
6647
+ channel.post({ gen: msg.gen, kind: "evalResult", reqId: msg.reqId, results });
6648
+ return;
6649
+ }
6077
6650
  if (msg.kind === "patch") {
6078
6651
  const { gen: gen2 } = msg;
6079
6652
  if (gen2 < renderedGen) {
@@ -6243,5 +6816,5 @@ export {
6243
6816
  bootCanvasIframe
6244
6817
  };
6245
6818
 
6246
- //# debugId=FE3AC1CBD1F2F79E64756E2164756E21
6819
+ //# debugId=861006CEFE04044264756E2164756E21
6247
6820
  //# sourceMappingURL=iframe-entry.js.map