stickyflow 0.10.0 → 0.11.1

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 (60) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/binding/index.cjs +9 -9
  3. package/dist/binding/index.d.cts +4 -4
  4. package/dist/binding/index.d.ts +4 -4
  5. package/dist/binding/index.js +3 -3
  6. package/dist/{chunk-TECWZVIM.cjs → chunk-4KBU4CFU.cjs} +23 -23
  7. package/dist/{chunk-TECWZVIM.cjs.map → chunk-4KBU4CFU.cjs.map} +1 -1
  8. package/dist/{chunk-EGUPN56K.js → chunk-BFQWCOEC.js} +3 -3
  9. package/dist/{chunk-EGUPN56K.js.map → chunk-BFQWCOEC.js.map} +1 -1
  10. package/dist/{chunk-VPNOVRWP.js → chunk-DGAVCN4D.js} +262 -15
  11. package/dist/chunk-DGAVCN4D.js.map +1 -0
  12. package/dist/{chunk-5PJ55GPG.cjs → chunk-EHH6ZDK6.cjs} +1076 -713
  13. package/dist/chunk-EHH6ZDK6.cjs.map +1 -0
  14. package/dist/{chunk-HRU6VOEC.js → chunk-G6IW2IX3.js} +44 -7
  15. package/dist/chunk-G6IW2IX3.js.map +1 -0
  16. package/dist/{chunk-SDOHUNBW.js → chunk-TQIYK5OA.js} +435 -72
  17. package/dist/chunk-TQIYK5OA.js.map +1 -0
  18. package/dist/{chunk-L7CEX5JV.js → chunk-UD6IG2PG.js} +4 -4
  19. package/dist/{chunk-L7CEX5JV.js.map → chunk-UD6IG2PG.js.map} +1 -1
  20. package/dist/{chunk-LSEMXRB5.cjs → chunk-UHKFJBY5.cjs} +44 -7
  21. package/dist/chunk-UHKFJBY5.cjs.map +1 -0
  22. package/dist/{chunk-KRSX4UZ7.cjs → chunk-VYQ6N5SI.cjs} +293 -46
  23. package/dist/chunk-VYQ6N5SI.cjs.map +1 -0
  24. package/dist/{chunk-SCD3AHMY.cjs → chunk-WZA3DJJH.cjs} +15 -15
  25. package/dist/{chunk-SCD3AHMY.cjs.map → chunk-WZA3DJJH.cjs.map} +1 -1
  26. package/dist/index.cjs +36 -36
  27. package/dist/index.d.cts +5 -5
  28. package/dist/index.d.ts +5 -5
  29. package/dist/index.js +5 -5
  30. package/dist/interaction/index.cjs +308 -256
  31. package/dist/interaction/index.cjs.map +1 -1
  32. package/dist/interaction/index.d.cts +5 -4
  33. package/dist/interaction/index.d.ts +5 -4
  34. package/dist/interaction/index.js +74 -23
  35. package/dist/interaction/index.js.map +1 -1
  36. package/dist/{render-DU4lYbRl.d.cts → render-BDO4S5uF.d.cts} +16 -0
  37. package/dist/{render-DU4lYbRl.d.ts → render-BDO4S5uF.d.ts} +16 -0
  38. package/dist/{runtime-Bkv7QW7H.d.cts → runtime-C-C5v9po.d.cts} +2 -2
  39. package/dist/{runtime-DJ4K4_HZ.d.ts → runtime-lCAfU-1Q.d.ts} +2 -2
  40. package/dist/{runtimeTypes-C1shzoyr.d.ts → runtimeTypes-B7p6b1Bi.d.cts} +2 -2
  41. package/dist/{runtimeTypes-BvJBZLkr.d.cts → runtimeTypes-BDA_CfWh.d.ts} +2 -2
  42. package/dist/stickyflow-diagram.global.js +197 -44
  43. package/dist/stickyflow-diagram.global.js.map +1 -1
  44. package/dist/{types-B5jrEEcF.d.cts → types-CZJzycKw.d.cts} +1 -1
  45. package/dist/{types-B7IkZ0w4.d.ts → types-Dp8JVZLB.d.ts} +1 -1
  46. package/dist/viewer/index.cjs +9 -9
  47. package/dist/viewer/index.d.cts +4 -4
  48. package/dist/viewer/index.d.ts +4 -4
  49. package/dist/viewer/index.js +4 -4
  50. package/dist/web-component/index.cjs +6 -6
  51. package/dist/web-component/index.d.cts +2 -2
  52. package/dist/web-component/index.d.ts +2 -2
  53. package/dist/web-component/index.js +4 -4
  54. package/package.json +1 -1
  55. package/dist/chunk-5PJ55GPG.cjs.map +0 -1
  56. package/dist/chunk-HRU6VOEC.js.map +0 -1
  57. package/dist/chunk-KRSX4UZ7.cjs.map +0 -1
  58. package/dist/chunk-LSEMXRB5.cjs.map +0 -1
  59. package/dist/chunk-SDOHUNBW.js.map +0 -1
  60. package/dist/chunk-VPNOVRWP.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { componentRegistryVersion, isDimensionObject, isIsometricObject, sflToCanvasState, registerComponentDescriptor, isBuiltInComponentType, getComponentDescriptor, tag, EMPTY_BOUNDS, DOM_CONTRACT_VERSION, edgePointToward, elementCenter, VISIBLE_ISOMETRIC_FACES, isometricFaceCenter, finite, freeformPathData, rotatedBounds, DEFAULT_FONT, isGenericComponent, cleanFootprint, pointAtNormalizedPort, boxBounds, cornersRoundedRectPath, calculateLinkRoute, labelGapDash, getLineMidpoint, personLabelArea, wrapText, heuristicRichMeasure, shadeFill, extrudeFootprint, isometricFaces, roundedPolygonPath, stackSequence, stackRank, topRoundedRectPath, rightRoundedRectPath, leftRoundedRectPath, bottomRoundedRectPath, paint, CHART_TITLE_PROP, chartText, isHorizontalBar, chartTicks, mapChartValue, roundedOrthogonalPathData, localShapePath, pillCornerRadius, isometricBounds, AXIS_SIN, deriveFaceFills, resolveChartShadow, CHART_COLORS, angleForAxisValue, horizontalRows, rowTop, categoryProp, isometricTopFaceCenter, ISOMETRIC_SIZE_ATTR, CORNER_RADIUS_ATTR, ISOMETRIC_LABEL_OFFSET_ATTR, PATH_POINTS_ATTR, PATH_TENSION_ATTR, PATH_CLOSED_ATTR, SYMBOL_BOUNDS_ATTR, SYMBOL_POINTS_ATTR, SYMBOL_TENSION_ATTR, SQUARE_CORNER_ATTR, labelTilt, tiltedLabelRoom, resolvePieRadii, radarCategoryCount, radarRadius, createPieGeometry, DEFAULT_HEX_RADIUS, DEFAULT_HEATMAP_ZONES, axisBands, DEFAULT_ZONE_FILL, representativeColor, computeBoxplotStats, barCategoryCount, ruleAtProp, ruleTextProp, labelBelowFrame, seriesLabelProp, axisLabelProp, seriesColorProp, seriesValueProp, radarSeriesPath, radarRadiusForValue, createPieSlicePath, renderPieLabels, interpolateHexColorStops, contrastTextColor, formatFieldValue, sameHueTint, isLightColor, barShapePath, linePathData, colorWithAlpha, estimateTextWidth, segmentedBarPath, roundedBarPath, radarPointAt, gapInsetAngle, pieUsesInnerArc, maxPieCornerRadius, pieApexRadius, maxApexCornerRadius, pieArcPoint, sameHueContrastColor, clipSegmentToRect, zoneColorAtValue, ellipsizeToWidth, barPath, labelAboveFrame, seriesValueColorProp, stackedSegment, errorBarRange, createVerticalErrorWhiskerNode, formatBarLabel, barLabelSize, CATEGORY_LABEL_ROW, BAR_LABEL, pillPath, resolveRollingNumberStyle, formatRollingNumber } from './chunk-HRU6VOEC.js';
1
+ import { componentRegistryVersion, isDimensionObject, isIsometricObject, sflToCanvasState, registerComponentDescriptor, isBuiltInComponentType, getComponentDescriptor, tag, EMPTY_BOUNDS, DOM_CONTRACT_VERSION, edgePointToward, elementCenter, VISIBLE_ISOMETRIC_FACES, isometricFaceCenter, finite, freeformPathData, rotatedBounds, DEFAULT_FONT, isGenericComponent, cleanFootprint, pointAtNormalizedPort, boxBounds, cornersRoundedRectPath, calculateLinkRoute, labelGapDash, getLineMidpoint, personLabelArea, wrapText, heuristicRichMeasure, shadeFill, extrudeFootprint, isometricFaces, roundedPolygonPath, stackSequence, stackRank, topRoundedRectPath, rightRoundedRectPath, leftRoundedRectPath, bottomRoundedRectPath, paint, CHART_TITLE_PROP, chartText, isHorizontalBar, chartTicks, mapChartValue, roundedOrthogonalPathData, localShapePath, pillCornerRadius, isometricBounds, AXIS_SIN, deriveFaceFills, resolveChartShadow, CHART_COLORS, angleForAxisValue, horizontalRows, rowTop, categoryProp, isometricTopFaceCenter, ISOMETRIC_SIZE_ATTR, CORNER_RADIUS_ATTR, ISOMETRIC_LABEL_OFFSET_ATTR, PATH_POINTS_ATTR, PATH_TENSION_ATTR, PATH_CLOSED_ATTR, SYMBOL_BOUNDS_ATTR, SYMBOL_POINTS_ATTR, SYMBOL_TENSION_ATTR, SQUARE_CORNER_ATTR, labelTilt, tiltedLabelRoom, resolvePieRadii, radarCategoryCount, radarRadius, createPieGeometry, DEFAULT_HEX_RADIUS, DEFAULT_HEATMAP_ZONES, axisBands, DEFAULT_ZONE_FILL, representativeColor, computeBoxplotStats, barCategoryCount, ruleAtProp, ruleTextProp, labelBelowFrame, seriesLabelProp, axisLabelProp, seriesColorProp, seriesValueProp, radarSeriesPath, radarRadiusForValue, createPieSlicePath, renderPieLabels, interpolateHexColorStops, contrastTextColor, formatFieldValue, sameHueTint, isLightColor, barShapePath, linePathData, colorWithAlpha, estimateTextWidth, segmentedBarPath, roundedBarPath, radarPointAt, gapInsetAngle, pieUsesInnerArc, maxPieCornerRadius, pieApexRadius, maxApexCornerRadius, pieArcPoint, sameHueContrastColor, clipSegmentToRect, zoneColorAtValue, ellipsizeToWidth, barPath, labelAboveFrame, seriesValueColorProp, stackedSegment, errorBarRange, createVerticalErrorWhiskerNode, formatBarLabel, barLabelSize, CATEGORY_LABEL_ROW, BAR_LABEL, pillPath, resolveRollingNumberStyle, formatRollingNumber, parseHexRgb } from './chunk-G6IW2IX3.js';
2
2
 
3
3
  // src/renderInput/styleDecoding/colorToken.ts
4
4
  var COLOR_TOKEN_PATTERN = String.raw`rgba?\([^)]*\)|#[0-9a-fA-F]+|[a-zA-Z][\w-]*`;
@@ -117,8 +117,8 @@ function dashArray(value) {
117
117
  if (!Array.isArray(parsed)) {
118
118
  return void 0;
119
119
  }
120
- const numbers = parsed.filter((entry) => typeof entry === "number" && Number.isFinite(entry));
121
- return numbers.length === parsed.length ? numbers : void 0;
120
+ const numbers2 = parsed.filter((entry) => typeof entry === "number" && Number.isFinite(entry));
121
+ return numbers2.length === parsed.length ? numbers2 : void 0;
122
122
  }
123
123
  function solidColor(value) {
124
124
  const decoded = decodeColor(value);
@@ -630,7 +630,7 @@ function readProperty(descriptor, raw) {
630
630
  case "boolean":
631
631
  return raw === void 0 ? descriptor.default : raw === "true";
632
632
  case "color":
633
- return cssColor(raw) ?? descriptor.default;
633
+ return descriptor.optional && raw === "none" ? "none" : cssColor(raw) ?? descriptor.default;
634
634
  case "enum":
635
635
  return raw !== void 0 && descriptor.options.includes(raw) ? raw : descriptor.default;
636
636
  case "string":
@@ -1010,12 +1010,29 @@ function pathObject(e) {
1010
1010
  }
1011
1011
 
1012
1012
  // src/renderInput/fromSfl/canvasObjects/shape.ts
1013
+ var IMAGE_FITS = ["cover", "contain", "fill"];
1013
1014
  function shapeTypeOf(value) {
1014
1015
  return value || "rectangle";
1015
1016
  }
1017
+ function numbers(value, count) {
1018
+ const parts = value?.split(",").map(Number);
1019
+ return parts?.length === count && parts.every(Number.isFinite) ? parts : void 0;
1020
+ }
1021
+ function imageOf(p) {
1022
+ const size = numbers(p.imageSize, 2);
1023
+ const crop = numbers(p.crop, 4);
1024
+ return {
1025
+ src: p.src,
1026
+ fit: oneOf(p.fit, IMAGE_FITS, "cover"),
1027
+ pixels: size && size[0] > 0 && size[1] > 0 ? [size[0], size[1]] : void 0,
1028
+ crop: crop && crop[2] > 0 && crop[3] > 0 ? { x: crop[0], y: crop[1], width: crop[2], height: crop[3] } : void 0
1029
+ };
1030
+ }
1016
1031
  function shapeObject(e, shaders) {
1017
1032
  const p = e.props;
1018
1033
  const isText = e.kind === "text";
1034
+ const isImage = e.kind === "image";
1035
+ const unpainted = isText || isImage;
1019
1036
  return {
1020
1037
  id: e.id,
1021
1038
  type: "shape",
@@ -1029,8 +1046,8 @@ function shapeObject(e, shaders) {
1029
1046
  w: e.width,
1030
1047
  h: e.height,
1031
1048
  rotation: e.rotation ?? 0,
1032
- fillColor: decodeColor(p.fill ?? (isText ? "none" : void 0)),
1033
- strokeColor: strokePaint(p.stroke ?? (isText ? "none" : void 0)),
1049
+ fillColor: decodeColor(p.fill ?? (unpainted ? "none" : void 0)),
1050
+ strokeColor: strokePaint(p.stroke ?? (unpainted ? "none" : void 0)),
1034
1051
  isText: isText || void 0,
1035
1052
  strokeWidth: numOf(p.strokeWidth),
1036
1053
  textColor: p.textColor,
@@ -1051,7 +1068,8 @@ function shapeObject(e, shaders) {
1051
1068
  parentFrameId: e.parentFrameId,
1052
1069
  order: e.order,
1053
1070
  sourceIndex: e.sourceIndex,
1054
- shader: p.shader ? shaders.get(p.shader) : void 0
1071
+ shader: p.shader ? shaders.get(p.shader) : void 0,
1072
+ image: isImage && p.src ? imageOf(p) : void 0
1055
1073
  };
1056
1074
  }
1057
1075
 
@@ -1482,7 +1500,7 @@ function fade(component, style) {
1482
1500
  const shown = component.disabled ? { ...style, opacity: 0.4 } : style;
1483
1501
  return animate > 0 ? { ...shown, transition: animate } : shown;
1484
1502
  }
1485
- function hitArea(component, box) {
1503
+ function hitArea(component, box, takesClicks = true) {
1486
1504
  return {
1487
1505
  id: `widget-${component.id}`,
1488
1506
  type: "rect",
@@ -1492,7 +1510,7 @@ function hitArea(component, box) {
1492
1510
  y: box.y,
1493
1511
  width: box.width,
1494
1512
  height: box.height,
1495
- style: { fill: { kind: "none" }, stroke: { kind: "none" }, pointerEvents: "all" }
1513
+ style: { fill: { kind: "none" }, stroke: { kind: "none" }, pointerEvents: takesClicks ? "all" : "none" }
1496
1514
  };
1497
1515
  }
1498
1516
  function labelText(component, part, area, label, order) {
@@ -1613,6 +1631,268 @@ function buildCheckbox(component, index) {
1613
1631
  ];
1614
1632
  }
1615
1633
 
1634
+ // src/renderScene/artifacts/component/hand.ts
1635
+ var MIN_TIP = 0.25;
1636
+ function withAlpha(color, alpha) {
1637
+ const rgb = parseHexRgb(color);
1638
+ return rgb && alpha < 1 ? `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${Number(alpha.toFixed(3))})` : color;
1639
+ }
1640
+ var num = (value) => String(Number(value.toFixed(3)));
1641
+ var isOff = (component, key) => textProperty(component, key) === "none";
1642
+ var colorOr = (component, key, fallback) => isOff(component, key) ? fallback : textProperty(component, key) || fallback;
1643
+ function handOf(component, box) {
1644
+ const radius = Math.min(box.width, box.height) / 2;
1645
+ const root = Math.max(0.5, numberProperty2(component, "width"));
1646
+ const taper = Math.min(1, Math.max(0, numberProperty2(component, "taper")));
1647
+ return {
1648
+ cx: box.x + box.width / 2,
1649
+ cy: box.y + box.height / 2,
1650
+ radius,
1651
+ length: Math.max(1, numberProperty2(component, "length") * radius),
1652
+ tail: Math.max(0, numberProperty2(component, "tail") * radius),
1653
+ gap: Math.max(0, numberProperty2(component, "gap") * radius),
1654
+ rootHalf: root / 2,
1655
+ tipHalf: Math.max(MIN_TIP, root * taper / 2)
1656
+ };
1657
+ }
1658
+ function outline({ cx, cy, length, tail, gap, rootHalf, tipHalf }) {
1659
+ const root = gap > 0 ? cy - gap : cy;
1660
+ const back = gap > 0 ? 0 : tail;
1661
+ const tipLeft = `L${num(cx - tipHalf)} ${num(cy - length)}`;
1662
+ const tip = `A${num(tipHalf)} ${num(tipHalf)} 0 0 1 ${num(cx + tipHalf)} ${num(cy - length)}`;
1663
+ const round2 = `A${num(rootHalf)} ${num(rootHalf)} 0 0 1 ${num(cx - rootHalf)} ${num(root + back)}`;
1664
+ if (gap > 0) {
1665
+ return `M${num(cx - rootHalf)} ${num(root)}${tipLeft}${tip}L${num(cx + rootHalf)} ${num(root)}${round2}Z`;
1666
+ }
1667
+ return [
1668
+ `M${num(cx - rootHalf)} ${num(root + back)}`,
1669
+ `L${num(cx - rootHalf)} ${num(root)}`,
1670
+ tipLeft,
1671
+ tip,
1672
+ `L${num(cx + rootHalf)} ${num(root)}`,
1673
+ `L${num(cx + rootHalf)} ${num(root + back)}`,
1674
+ back > 0 ? round2 : `L${num(cx - rootHalf)} ${num(root)}`,
1675
+ "Z"
1676
+ ].join("");
1677
+ }
1678
+ function shaped(hand, layer) {
1679
+ const rootHalf = hand.rootHalf * layer.scale;
1680
+ const tipHalf = layer.taper === void 0 ? hand.tipHalf * layer.scale : rootHalf * layer.taper;
1681
+ return { ...hand, length: hand.length * (layer.length ?? 1), rootHalf, tipHalf: Math.max(MIN_TIP, tipHalf) };
1682
+ }
1683
+ function stopsPaint(hand, stops, fadeOut, clear) {
1684
+ const tail = hand.gap > 0 ? 0 : hand.tail;
1685
+ const offset = (along2) => (tail + along2 * hand.length) / (hand.length + tail) * 100;
1686
+ const [, firstColor, firstAlpha] = stops[0];
1687
+ return {
1688
+ kind: "linear-gradient",
1689
+ angle: -90,
1690
+ stops: [
1691
+ ...tail > 0 ? [{ offset: 0, color: withAlpha(firstColor, firstAlpha * (clear ? 0 : 1 - fadeOut)) }] : [],
1692
+ ...stops.map(([along2, color, alpha], position) => ({
1693
+ offset: offset(along2),
1694
+ color: withAlpha(color, clear && position === 0 ? 0 : alpha)
1695
+ }))
1696
+ ]
1697
+ };
1698
+ }
1699
+ function plainPaint(hand, layer, fadeOut, tipFade) {
1700
+ if (fadeOut <= 0 && tipFade <= 0 && !layer.clear) {
1701
+ return solid(layer.color);
1702
+ }
1703
+ return {
1704
+ kind: "linear-gradient",
1705
+ angle: -90,
1706
+ stops: [
1707
+ { offset: 0, color: withAlpha(layer.root, layer.clear ? 0 : 1 - fadeOut) },
1708
+ // A tail is lit at the pivot and dissolves at its end.
1709
+ ...hand.gap === 0 && hand.tail > 0 ? [{ offset: hand.tail / (hand.length + hand.tail) * 100, color: layer.root }] : [],
1710
+ { offset: 100, color: withAlpha(layer.color, 1 - tipFade) }
1711
+ ]
1712
+ };
1713
+ }
1714
+ function handLayer(component, box, base, angle, layer, order) {
1715
+ const hand = shaped(base, layer);
1716
+ const fadeOut = Math.min(1, Math.max(0, numberProperty2(component, "fade")));
1717
+ const tipFade = Math.min(1, Math.max(0, layer.tipFade ?? numberProperty2(component, "tipFade")));
1718
+ const glow = numberProperty2(component, "glow");
1719
+ const soft = layer.blur > 0;
1720
+ const reach = Math.max(hand.rootHalf, hand.tipHalf);
1721
+ const near = hand.gap > 0 ? -hand.gap : hand.tail;
1722
+ const extent = { x: hand.cx - reach, y: hand.cy - hand.length, width: reach * 2, height: hand.length + near };
1723
+ return {
1724
+ id: `widget-${component.id}-${layer.part}`,
1725
+ type: "path",
1726
+ order: box.order + order,
1727
+ bounds: rotatedBounds(extent, 0, 0),
1728
+ d: outline(hand),
1729
+ rotation: angle,
1730
+ rotationCenter: { x: hand.cx, y: hand.cy },
1731
+ style: fade(component, {
1732
+ // The gradient runs from the tail to the tip, and turns with the hand.
1733
+ fill: layer.stops ? stopsPaint(hand, layer.stops, fadeOut, Boolean(layer.clear)) : plainPaint(hand, layer, fadeOut, tipFade),
1734
+ stroke: { kind: "none" },
1735
+ ...soft ? { blur: layer.blur, opacity: layer.opacity } : {},
1736
+ ...!soft && glow > 0 && !isOff(component, "glowColor") ? {
1737
+ shadow: {
1738
+ color: colorOr(component, "glowColor", layer.color),
1739
+ opacity: 1,
1740
+ blur: glow,
1741
+ dx: 0,
1742
+ dy: 0
1743
+ }
1744
+ } : {}
1745
+ })
1746
+ };
1747
+ }
1748
+ function trailLayers(color, root, light, soft, clear, lit) {
1749
+ const trail = [
1750
+ {
1751
+ part: "hand-light",
1752
+ scale: 4.5,
1753
+ length: 0.55,
1754
+ taper: 0.15,
1755
+ blur: soft * 3,
1756
+ opacity: 1,
1757
+ color: light,
1758
+ root: light,
1759
+ stops: [
1760
+ [0, light, 0.95],
1761
+ [0.35, light, 0.45],
1762
+ [1, light, 0]
1763
+ ]
1764
+ },
1765
+ {
1766
+ part: "hand-body",
1767
+ scale: 1.6,
1768
+ taper: 0.25,
1769
+ blur: soft,
1770
+ opacity: 1,
1771
+ color,
1772
+ root,
1773
+ clear,
1774
+ stops: [
1775
+ [0, root, 1],
1776
+ [0.18, color, 1],
1777
+ [0.6, color, 0.55],
1778
+ [1, color, 0]
1779
+ ]
1780
+ },
1781
+ {
1782
+ part: "hand",
1783
+ scale: 0.55,
1784
+ length: 0.8,
1785
+ taper: 0.3,
1786
+ blur: soft * 0.3,
1787
+ opacity: 1,
1788
+ color: root,
1789
+ root,
1790
+ clear,
1791
+ stops: [
1792
+ [0, root, 1],
1793
+ [0.45, root, 0.6],
1794
+ [1, root, 0]
1795
+ ]
1796
+ }
1797
+ ];
1798
+ return lit ? trail : trail.slice(1);
1799
+ }
1800
+ function handLayers(component, box, hand, angle) {
1801
+ const color = textProperty(component, "color");
1802
+ const root = colorOr(component, "root", color);
1803
+ const light = colorOr(component, "glowColor", color);
1804
+ const lit = !isOff(component, "glowColor");
1805
+ const clear = isOff(component, "root");
1806
+ const soft = numberProperty2(component, "soft");
1807
+ const bloom = numberProperty2(component, "bloom");
1808
+ if (soft > 0) {
1809
+ return trailLayers(color, root, light, soft, clear, lit).map(
1810
+ (layer, position) => handLayer(component, box, hand, angle, layer, 0.26 + position * 0.02)
1811
+ );
1812
+ }
1813
+ return [
1814
+ // A blurred light under the hand, bright at the pivot and dark at the tip.
1815
+ ...bloom > 0 && lit ? [
1816
+ handLayer(
1817
+ component,
1818
+ box,
1819
+ hand,
1820
+ angle,
1821
+ { part: "hand-bloom", scale: 4, blur: bloom, opacity: 0.9, color: light, root: light, tipFade: 1 },
1822
+ 0.26
1823
+ )
1824
+ ] : [],
1825
+ handLayer(component, box, hand, angle, { part: "hand", scale: 1, blur: 0, opacity: 1, color, root, clear }, 0.3)
1826
+ ];
1827
+ }
1828
+ function hubNode(component, box, hand, radius) {
1829
+ const ringWidth = numberProperty2(component, "hubRing");
1830
+ return {
1831
+ id: `widget-${component.id}-hub`,
1832
+ type: "ellipse",
1833
+ order: box.order + 0.4,
1834
+ bounds: rotatedBounds(
1835
+ { x: hand.cx - radius, y: hand.cy - radius, width: radius * 2, height: radius * 2 },
1836
+ 0,
1837
+ ringWidth
1838
+ ),
1839
+ cx: hand.cx,
1840
+ cy: hand.cy,
1841
+ rx: Math.max(0.5, radius - ringWidth / 2),
1842
+ ry: Math.max(0.5, radius - ringWidth / 2),
1843
+ style: fade(component, {
1844
+ fill: isOff(component, "hubFill") ? { kind: "none" } : solid(textProperty(component, "hubFill")),
1845
+ stroke: ringWidth > 0 ? solid(textProperty(component, "color")) : { kind: "none" },
1846
+ strokeWidth: ringWidth,
1847
+ ...numberProperty2(component, "glow") > 0 && !isOff(component, "glowColor") ? {
1848
+ shadow: {
1849
+ color: colorOr(component, "glowColor", textProperty(component, "color")),
1850
+ opacity: 1,
1851
+ blur: numberProperty2(component, "glow"),
1852
+ dx: 0,
1853
+ dy: 0
1854
+ }
1855
+ } : {}
1856
+ })
1857
+ };
1858
+ }
1859
+ var HIT_HALF = 8;
1860
+ function pickNodes(component, box, hand, angle) {
1861
+ const reach = { ...hand, rootHalf: Math.max(hand.rootHalf, HIT_HALF), tipHalf: Math.max(hand.tipHalf, HIT_HALF) };
1862
+ const near = reach.gap > 0 ? -reach.gap : reach.tail;
1863
+ const extent = {
1864
+ x: reach.cx - HIT_HALF,
1865
+ y: reach.cy - reach.length,
1866
+ width: HIT_HALF * 2,
1867
+ height: reach.length + near
1868
+ };
1869
+ return [
1870
+ hitArea(component, box, false),
1871
+ {
1872
+ id: `widget-${component.id}-pick`,
1873
+ type: "path",
1874
+ order: box.order + 0.5,
1875
+ bounds: rotatedBounds(extent, 0, 0),
1876
+ d: outline(reach),
1877
+ rotation: angle,
1878
+ rotationCenter: { x: reach.cx, y: reach.cy },
1879
+ style: { fill: { kind: "none" }, stroke: { kind: "none" }, pointerEvents: "all" }
1880
+ }
1881
+ ];
1882
+ }
1883
+ function buildHand(component, index) {
1884
+ const box = controlBox(component, index, { width: 200, height: 200 });
1885
+ const hand = handOf(component, box);
1886
+ const max = numberProperty2(component, "max") || 1;
1887
+ const angle = numberProperty2(component, "start") + Number(component.value) / max * 360;
1888
+ const hub = numberProperty2(component, "hub");
1889
+ return [
1890
+ ...pickNodes(component, box, hand, angle),
1891
+ ...handLayers(component, box, hand, angle),
1892
+ ...hub > 0 ? [hubNode(component, box, hand, hub)] : []
1893
+ ];
1894
+ }
1895
+
1616
1896
  // src/renderScene/artifacts/component/knob.ts
1617
1897
  var DOT_RATIO = 0.62;
1618
1898
  function decimals(step) {
@@ -2190,6 +2470,7 @@ var builders = /* @__PURE__ */ new Map([
2190
2470
  ["progress", asComponentBuilder(buildProgress)],
2191
2471
  ["segmented", asComponentBuilder(buildSegmented)],
2192
2472
  ["knob", asComponentBuilder(buildKnob)],
2473
+ ["hand", asComponentBuilder(buildHand)],
2193
2474
  ["range", asComponentBuilder(buildSlider)]
2194
2475
  ]);
2195
2476
  function registerComponentBuilder(type, builder) {
@@ -3595,6 +3876,68 @@ function renderShapeGeometry(context) {
3595
3876
  return renderer(context);
3596
3877
  }
3597
3878
 
3879
+ // src/renderScene/artifacts/shape/image.ts
3880
+ function clipOf(outline2, box) {
3881
+ if (outline2?.type === "path") {
3882
+ return { type: "path", d: outline2.d };
3883
+ }
3884
+ if (outline2?.type === "ellipse") {
3885
+ const { cx, cy, rx, ry } = outline2;
3886
+ return {
3887
+ type: "path",
3888
+ d: `M${cx - rx} ${cy}A${rx} ${ry} 0 1 0 ${cx + rx} ${cy}A${rx} ${ry} 0 1 0 ${cx - rx} ${cy}Z`
3889
+ };
3890
+ }
3891
+ return { type: "rect", ...box, rx: outline2?.type === "rect" ? outline2.rx ?? 0 : 0 };
3892
+ }
3893
+ function croppedBox(image, crop, pixels, box) {
3894
+ const scaleX = box.width / crop.width;
3895
+ const scaleY = box.height / crop.height;
3896
+ const scale = image.fit === "contain" ? Math.min(scaleX, scaleY) : Math.max(scaleX, scaleY);
3897
+ const [sx, sy] = image.fit === "fill" ? [scaleX, scaleY] : [scale, scale];
3898
+ return {
3899
+ x: box.x + (box.width - crop.width * sx) / 2 - crop.x * sx,
3900
+ y: box.y + (box.height - crop.height * sy) / 2 - crop.y * sy,
3901
+ width: pixels[0] * sx,
3902
+ height: pixels[1] * sy
3903
+ };
3904
+ }
3905
+ var ASPECTS = { cover: "slice", contain: "meet", fill: "none" };
3906
+ function renderShapeImage(context, outline2, diagnostics) {
3907
+ const image = context.shape.image;
3908
+ if (!image) {
3909
+ return void 0;
3910
+ }
3911
+ if (image.crop && !image.pixels) {
3912
+ diagnostics.push({
3913
+ severity: "warning",
3914
+ code: "image-crop-without-size",
3915
+ ownerId: context.shape.id,
3916
+ message: `The image '${context.shape.id}' has a crop but no imageSize, so it is shown uncropped. Give it the size of the file, as imageSize: width, height.`
3917
+ });
3918
+ }
3919
+ const box = { x: context.x, y: context.y, width: context.width, height: context.height };
3920
+ const cropped = image.crop && image.pixels;
3921
+ return {
3922
+ type: "image",
3923
+ id: `${context.id}-image`,
3924
+ order: context.order,
3925
+ bounds: context.bounds,
3926
+ ...cropped ? croppedBox(image, image.crop, image.pixels, box) : box,
3927
+ href: image.src,
3928
+ aspect: cropped ? "none" : ASPECTS[image.fit],
3929
+ rotation: context.rotation,
3930
+ rotationCenter: context.rotationCenter,
3931
+ style: {
3932
+ opacity: context.style.opacity,
3933
+ blur: context.style.blur,
3934
+ blend: context.style.blend,
3935
+ clip: clipOf(outline2, box),
3936
+ pointerEvents: "none"
3937
+ }
3938
+ };
3939
+ }
3940
+
3598
3941
  // src/renderScene/artifacts/shape/shaderSurface.ts
3599
3942
  var RECT_PROGRAMS = /* @__PURE__ */ new Set([
3600
3943
  "reactivePanel",
@@ -3901,6 +4244,22 @@ function buildShape(shape, index, diagnostics, measureText) {
3901
4244
  }
3902
4245
  nodes.push(shader);
3903
4246
  }
4247
+ const image = renderShapeImage(context, nodes[0], diagnostics);
4248
+ if (image) {
4249
+ for (const node of nodes) {
4250
+ if (node.type === "ellipse" || node.type === "path" || node.type === "rect") {
4251
+ node.style = { ...node.style, pointerEvents: "all" };
4252
+ }
4253
+ }
4254
+ nodes.push(image);
4255
+ }
4256
+ if (shape.isText) {
4257
+ for (const node of nodes) {
4258
+ if (node.type === "ellipse" || node.type === "path" || node.type === "rect") {
4259
+ node.style = { ...node.style, pointerEvents: "all" };
4260
+ }
4261
+ }
4262
+ }
3904
4263
  const text = renderShapeText(context, strokeWidth, measureText);
3905
4264
  if (text) {
3906
4265
  nodes.push(text);
@@ -9713,7 +10072,7 @@ function buildRenderScene(input, options = {}) {
9713
10072
 
9714
10073
  // src/svg/format.ts
9715
10074
  var xml = (value) => String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
9716
- var num = (value) => Number(value.toFixed(3)).toString();
10075
+ var num2 = (value) => Number(value.toFixed(3)).toString();
9717
10076
  var safeId = (value) => `sf-${value.replace(/[^A-Za-z0-9_.:-]/g, "-")}`;
9718
10077
 
9719
10078
  // src/svg/clipDefinitions.ts
@@ -9735,7 +10094,7 @@ var SvgClipDefinitions = class {
9735
10094
  const outer = clip.within ? this.define(clip.within) : void 0;
9736
10095
  id = safeId(`${this.prefix}clip-${this.ids.size}`);
9737
10096
  this.ids.set(key, id);
9738
- const shape = clip.type === "path" ? `<path clip-rule="evenodd" d="${xml(clip.d)}"/>` : `<rect x="${num(clip.x)}" y="${num(clip.y)}" width="${num(clip.width)}" height="${num(clip.height)}" rx="${num(clip.rx)}"/>`;
10097
+ const shape = clip.type === "path" ? `<path clip-rule="evenodd" d="${xml(clip.d)}"/>` : `<rect x="${num2(clip.x)}" y="${num2(clip.y)}" width="${num2(clip.width)}" height="${num2(clip.height)}" rx="${num2(clip.rx)}"/>`;
9739
10098
  this.addDefinition(`<clipPath id="${id}"${outer ? ` clip-path="url(#${outer})"` : ""}>${shape}</clipPath>`);
9740
10099
  }
9741
10100
  return id;
@@ -9777,18 +10136,18 @@ var SvgFilterDefinitions = class {
9777
10136
  id = safeId(`${this.prefix}shadow-${nodeId}-${this.ids.size}`);
9778
10137
  this.ids.set(key, id);
9779
10138
  this.addDefinition(
9780
- `<filter id="${id}" x="${num(-margin * 100)}%" y="${num(-margin * 100)}%" width="${num((1 + margin * 2) * 100)}%" height="${num((1 + margin * 2) * 100)}%">${primitives(value)}</filter>`
10139
+ `<filter id="${id}" x="${num2(-margin * 100)}%" y="${num2(-margin * 100)}%" width="${num2((1 + margin * 2) * 100)}%" height="${num2((1 + margin * 2) * 100)}%">${primitives(value)}</filter>`
9781
10140
  );
9782
10141
  }
9783
10142
  return id;
9784
10143
  }
9785
10144
  };
9786
10145
  function outerShadowPrimitives(shadow) {
9787
- return `<feDropShadow dx="${num(shadow.dx)}" dy="${num(shadow.dy)}" stdDeviation="${num(shadow.blur / 2)}" flood-color="${xml(shadow.color)}" flood-opacity="${num(shadow.opacity)}"/>`;
10146
+ return `<feDropShadow dx="${num2(shadow.dx)}" dy="${num2(shadow.dy)}" stdDeviation="${num2(shadow.blur / 2)}" flood-color="${xml(shadow.color)}" flood-opacity="${num2(shadow.opacity)}"/>`;
9788
10147
  }
9789
10148
  function blurPrimitives(effect) {
9790
- const blur = effect.blur > 0 ? `<feGaussianBlur stdDeviation="${num(effect.blur / 2)}"/>` : "";
9791
- const saturate = effect.saturate === 1 ? "" : `<feColorMatrix type="saturate" values="${num(effect.saturate)}"/>`;
10149
+ const blur = effect.blur > 0 ? `<feGaussianBlur stdDeviation="${num2(effect.blur / 2)}"/>` : "";
10150
+ const saturate = effect.saturate === 1 ? "" : `<feColorMatrix type="saturate" values="${num2(effect.saturate)}"/>`;
9792
10151
  return blur + saturate;
9793
10152
  }
9794
10153
  function innerShadowPrimitives(shadow) {
@@ -9796,9 +10155,9 @@ function innerShadowPrimitives(shadow) {
9796
10155
  '<feComponentTransfer in="SourceAlpha" result="sf-inverted-alpha">',
9797
10156
  '<feFuncA type="table" tableValues="1 0"/>',
9798
10157
  "</feComponentTransfer>",
9799
- `<feGaussianBlur in="sf-inverted-alpha" stdDeviation="${num(shadow.blur / 2)}" result="sf-blurred"/>`,
9800
- `<feOffset in="sf-blurred" dx="${num(shadow.dx)}" dy="${num(shadow.dy)}" result="sf-offset"/>`,
9801
- `<feFlood flood-color="${xml(shadow.color)}" flood-opacity="${num(shadow.opacity)}" result="sf-color"/>`,
10158
+ `<feGaussianBlur in="sf-inverted-alpha" stdDeviation="${num2(shadow.blur / 2)}" result="sf-blurred"/>`,
10159
+ `<feOffset in="sf-blurred" dx="${num2(shadow.dx)}" dy="${num2(shadow.dy)}" result="sf-offset"/>`,
10160
+ `<feFlood flood-color="${xml(shadow.color)}" flood-opacity="${num2(shadow.opacity)}" result="sf-color"/>`,
9802
10161
  '<feComposite in="sf-color" in2="sf-offset" operator="in" result="sf-halo"/>',
9803
10162
  '<feComposite in="sf-halo" in2="SourceGraphic" operator="in" result="sf-inset"/>',
9804
10163
  '<feComposite in="sf-inset" in2="SourceGraphic" operator="over"/>'
@@ -9808,19 +10167,19 @@ function innerShadowPrimitives(shadow) {
9808
10167
  // src/svg/markerBodies.ts
9809
10168
  function markerBody(type, size, color, strokeWidth) {
9810
10169
  const bodies = {
9811
- arrow: `<path d="M ${num(-size)} ${num(-size * 0.6)} L 0 0 L ${num(-size)} ${num(size * 0.6)}" fill="none" stroke="${color}" stroke-width="${num(strokeWidth)}" stroke-linecap="round" stroke-linejoin="round"/>`,
9812
- filled_arrow: `<path d="M ${num(-size)} ${num(-size * 0.6)} L 0 0 L ${num(-size)} ${num(size * 0.6)} Z" fill="${color}" stroke="${color}" stroke-width="${num(strokeWidth * 0.5)}" stroke-linejoin="round"/>`,
9813
- circle: `<circle cx="0" cy="0" r="${num(size * 0.5)}" fill="white" stroke="${color}" stroke-width="${num(strokeWidth)}"/>`,
9814
- filled_circle: `<circle cx="0" cy="0" r="${num(size * 0.5)}" fill="${color}"/>`,
9815
- diamond: `<path d="M ${num(-size * 0.6)} 0 L 0 ${num(-size * 0.6)} L ${num(size * 0.6)} 0 L 0 ${num(size * 0.6)} Z" fill="white" stroke="${color}" stroke-width="${num(strokeWidth)}"/>`,
9816
- filled_diamond: `<path d="M ${num(-size * 0.6)} 0 L 0 ${num(-size * 0.6)} L ${num(size * 0.6)} 0 L 0 ${num(size * 0.6)} Z" fill="${color}" stroke="${color}" stroke-width="${num(strokeWidth * 0.5)}"/>`,
9817
- bar: `<path d="M 0 ${num(-size * 0.6)} V ${num(size * 0.6)}" stroke="${color}" stroke-width="${num(strokeWidth)}" stroke-linecap="round"/>`,
9818
- triangle: `<path d="M ${num(-size)} ${num(-size * 0.72)} L 0 0 L ${num(-size)} ${num(size * 0.72)} Z" fill="white" stroke="${color}" stroke-width="${num(strokeWidth)}" stroke-linejoin="round"/>`,
9819
- one: `<path d="M 0 ${num(-size * 0.62)} V ${num(size * 0.62)} M ${num(-size * 0.72)} ${num(-size * 0.62)} V ${num(size * 0.62)}" stroke="${color}" stroke-width="${num(strokeWidth)}"/>`,
9820
- zero_one: `<circle cx="${num(-size * 0.8)}" cy="0" r="${num(size * 0.38)}" fill="white" stroke="${color}" stroke-width="${num(strokeWidth)}"/><path d="M 0 ${num(-size * 0.62)} V ${num(size * 0.62)}" stroke="${color}" stroke-width="${num(strokeWidth)}"/>`,
9821
- many: `<path d="M ${num(-size)} ${num(-size * 0.72)} L 0 0 M ${num(-size)} 0 L 0 0 M ${num(-size)} ${num(size * 0.72)} L 0 0" stroke="${color}" stroke-width="${num(strokeWidth)}" stroke-linecap="round"/>`,
9822
- one_many: `<path d="M ${num(-size)} ${num(-size * 0.72)} L 0 0 M ${num(-size)} 0 L 0 0 M ${num(-size)} ${num(size * 0.72)} L 0 0 M ${num(-size * 1.45)} ${num(-size * 0.62)} V ${num(size * 0.62)}" stroke="${color}" stroke-width="${num(strokeWidth)}" stroke-linecap="round"/>`,
9823
- zero_many: `<circle cx="${num(-size * 1.45)}" cy="0" r="${num(size * 0.38)}" fill="white" stroke="${color}" stroke-width="${num(strokeWidth)}"/><path d="M ${num(-size)} ${num(-size * 0.72)} L 0 0 M ${num(-size)} 0 L 0 0 M ${num(-size)} ${num(size * 0.72)} L 0 0" stroke="${color}" stroke-width="${num(strokeWidth)}" stroke-linecap="round"/>`
10170
+ arrow: `<path d="M ${num2(-size)} ${num2(-size * 0.6)} L 0 0 L ${num2(-size)} ${num2(size * 0.6)}" fill="none" stroke="${color}" stroke-width="${num2(strokeWidth)}" stroke-linecap="round" stroke-linejoin="round"/>`,
10171
+ filled_arrow: `<path d="M ${num2(-size)} ${num2(-size * 0.6)} L 0 0 L ${num2(-size)} ${num2(size * 0.6)} Z" fill="${color}" stroke="${color}" stroke-width="${num2(strokeWidth * 0.5)}" stroke-linejoin="round"/>`,
10172
+ circle: `<circle cx="0" cy="0" r="${num2(size * 0.5)}" fill="white" stroke="${color}" stroke-width="${num2(strokeWidth)}"/>`,
10173
+ filled_circle: `<circle cx="0" cy="0" r="${num2(size * 0.5)}" fill="${color}"/>`,
10174
+ diamond: `<path d="M ${num2(-size * 0.6)} 0 L 0 ${num2(-size * 0.6)} L ${num2(size * 0.6)} 0 L 0 ${num2(size * 0.6)} Z" fill="white" stroke="${color}" stroke-width="${num2(strokeWidth)}"/>`,
10175
+ filled_diamond: `<path d="M ${num2(-size * 0.6)} 0 L 0 ${num2(-size * 0.6)} L ${num2(size * 0.6)} 0 L 0 ${num2(size * 0.6)} Z" fill="${color}" stroke="${color}" stroke-width="${num2(strokeWidth * 0.5)}"/>`,
10176
+ bar: `<path d="M 0 ${num2(-size * 0.6)} V ${num2(size * 0.6)}" stroke="${color}" stroke-width="${num2(strokeWidth)}" stroke-linecap="round"/>`,
10177
+ triangle: `<path d="M ${num2(-size)} ${num2(-size * 0.72)} L 0 0 L ${num2(-size)} ${num2(size * 0.72)} Z" fill="white" stroke="${color}" stroke-width="${num2(strokeWidth)}" stroke-linejoin="round"/>`,
10178
+ one: `<path d="M 0 ${num2(-size * 0.62)} V ${num2(size * 0.62)} M ${num2(-size * 0.72)} ${num2(-size * 0.62)} V ${num2(size * 0.62)}" stroke="${color}" stroke-width="${num2(strokeWidth)}"/>`,
10179
+ zero_one: `<circle cx="${num2(-size * 0.8)}" cy="0" r="${num2(size * 0.38)}" fill="white" stroke="${color}" stroke-width="${num2(strokeWidth)}"/><path d="M 0 ${num2(-size * 0.62)} V ${num2(size * 0.62)}" stroke="${color}" stroke-width="${num2(strokeWidth)}"/>`,
10180
+ many: `<path d="M ${num2(-size)} ${num2(-size * 0.72)} L 0 0 M ${num2(-size)} 0 L 0 0 M ${num2(-size)} ${num2(size * 0.72)} L 0 0" stroke="${color}" stroke-width="${num2(strokeWidth)}" stroke-linecap="round"/>`,
10181
+ one_many: `<path d="M ${num2(-size)} ${num2(-size * 0.72)} L 0 0 M ${num2(-size)} 0 L 0 0 M ${num2(-size)} ${num2(size * 0.72)} L 0 0 M ${num2(-size * 1.45)} ${num2(-size * 0.62)} V ${num2(size * 0.62)}" stroke="${color}" stroke-width="${num2(strokeWidth)}" stroke-linecap="round"/>`,
10182
+ zero_many: `<circle cx="${num2(-size * 1.45)}" cy="0" r="${num2(size * 0.38)}" fill="white" stroke="${color}" stroke-width="${num2(strokeWidth)}"/><path d="M ${num2(-size)} ${num2(-size * 0.72)} L 0 0 M ${num2(-size)} 0 L 0 0 M ${num2(-size)} ${num2(size * 0.72)} L 0 0" stroke="${color}" stroke-width="${num2(strokeWidth)}" stroke-linecap="round"/>`
9824
10183
  };
9825
10184
  return bodies[type];
9826
10185
  }
@@ -9831,7 +10190,7 @@ function markerExtent(type, size, strokeWidth) {
9831
10190
  return Math.max(size * (isCompoundErMarker ? 2 : 1.4), strokeWidth * 2);
9832
10191
  }
9833
10192
  function markerAttributes(id, extent) {
9834
- return `id="${id}" markerWidth="${num(extent * 2)}" markerHeight="${num(extent * 2)}" viewBox="${num(-extent)} ${num(-extent)} ${num(extent * 2)} ${num(extent * 2)}" refX="0" refY="0" orient="auto-start-reverse" markerUnits="userSpaceOnUse" overflow="visible"`;
10193
+ return `id="${id}" markerWidth="${num2(extent * 2)}" markerHeight="${num2(extent * 2)}" viewBox="${num2(-extent)} ${num2(-extent)} ${num2(extent * 2)} ${num2(extent * 2)}" refX="0" refY="0" orient="auto-start-reverse" markerUnits="userSpaceOnUse" overflow="visible"`;
9835
10194
  }
9836
10195
  var SvgMarkerDefinitions = class {
9837
10196
  constructor(addDefinition, prefix) {
@@ -9866,7 +10225,7 @@ function meshGradientDefinition(id, paint2) {
9866
10225
  const radialDefinitions = paint2.points.map((point, index) => {
9867
10226
  const centerX = Math.max(0, Math.min(100, point.x));
9868
10227
  const centerY = Math.max(0, Math.min(100, point.y));
9869
- return `<radialGradient id="${radialIds[index]}" gradientUnits="objectBoundingBox" cx="${num(centerX)}%" cy="${num(centerY)}%" r="75%"><stop offset="0%" stop-color="${xml(point.color)}" stop-opacity="1"/><stop offset="100%" stop-color="${xml(point.color)}" stop-opacity="0"/></radialGradient>`;
10228
+ return `<radialGradient id="${radialIds[index]}" gradientUnits="objectBoundingBox" cx="${num2(centerX)}%" cy="${num2(centerY)}%" r="75%"><stop offset="0%" stop-color="${xml(point.color)}" stop-opacity="1"/><stop offset="100%" stop-color="${xml(point.color)}" stop-opacity="0"/></radialGradient>`;
9870
10229
  }).join("");
9871
10230
  const layers = paint2.points.map((_point, index) => `<rect width="1" height="1" fill="url(#${radialIds[index]})"/>`).join("");
9872
10231
  return `${radialDefinitions}<pattern id="${id}" patternUnits="objectBoundingBox" patternContentUnits="objectBoundingBox" width="1" height="1"><rect width="1" height="1" fill="${xml(paint2.points[0]?.color ?? "transparent")}"/>${layers}</pattern>`;
@@ -9878,8 +10237,8 @@ function linearGradientDefinition(id, paint2, bounds) {
9878
10237
  const radius = Math.max(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2;
9879
10238
  const deltaX = Math.cos(angle) * radius;
9880
10239
  const deltaY = Math.sin(angle) * radius;
9881
- const stops = paint2.stops.map((stop) => `<stop offset="${num(Math.max(0, Math.min(100, stop.offset)))}%" stop-color="${xml(stop.color)}"/>`).join("");
9882
- return `<linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${num(centerX - deltaX)}" y1="${num(centerY - deltaY)}" x2="${num(centerX + deltaX)}" y2="${num(centerY + deltaY)}">${stops}</linearGradient>`;
10240
+ const stops = paint2.stops.map((stop) => `<stop offset="${num2(Math.max(0, Math.min(100, stop.offset)))}%" stop-color="${xml(stop.color)}"/>`).join("");
10241
+ return `<linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${num2(centerX - deltaX)}" y1="${num2(centerY - deltaY)}" x2="${num2(centerX + deltaX)}" y2="${num2(centerY + deltaY)}">${stops}</linearGradient>`;
9883
10242
  }
9884
10243
  var SvgPaintDefinitions = class {
9885
10244
  constructor(addDefinition, prefix) {
@@ -9996,10 +10355,10 @@ function styleAttrs(node, defs) {
9996
10355
  }
9997
10356
  function addStrokeAttrs(attrs, style) {
9998
10357
  if (style.strokeWidth !== void 0) {
9999
- attrs.push(`stroke-width="${num(style.strokeWidth)}"`);
10358
+ attrs.push(`stroke-width="${num2(style.strokeWidth)}"`);
10000
10359
  }
10001
10360
  if (style.dash?.length) {
10002
- attrs.push(`stroke-dasharray="${style.dash.map(num).join(" ")}"`);
10361
+ attrs.push(`stroke-dasharray="${style.dash.map(num2).join(" ")}"`);
10003
10362
  }
10004
10363
  if (style.lineCap) {
10005
10364
  attrs.push(`stroke-linecap="${style.lineCap}"`);
@@ -10008,14 +10367,14 @@ function addStrokeAttrs(attrs, style) {
10008
10367
  attrs.push(`stroke-linejoin="${style.lineJoin}"`);
10009
10368
  }
10010
10369
  if (style.opacity !== void 0) {
10011
- attrs.push(`opacity="${num(style.opacity)}"`);
10370
+ attrs.push(`opacity="${num2(style.opacity)}"`);
10012
10371
  }
10013
10372
  if (style.pointerEvents) {
10014
10373
  attrs.push(`pointer-events="${style.pointerEvents}"`);
10015
10374
  }
10016
10375
  const css = [];
10017
10376
  if (style.transition && style.transition > 0) {
10018
- const time = `${num(style.transition)}ms ease`;
10377
+ const time = `${num2(style.transition)}ms ease`;
10019
10378
  css.push(
10020
10379
  `transition: x ${time}, y ${time}, width ${time}, height ${time}, cx ${time}, cy ${time}, rx ${time}, ry ${time}, fill ${time}, stroke ${time}, stroke-width ${time}, stroke-dasharray ${time}, opacity ${time}`
10021
10380
  );
@@ -10035,6 +10394,10 @@ function elementSize(node) {
10035
10394
  if (shape.type === "rect" && shape.width !== void 0 && shape.height !== void 0) {
10036
10395
  return Math.min(shape.width, shape.height);
10037
10396
  }
10397
+ if (shape.type === "path" && shape.bounds) {
10398
+ const side = Math.min(shape.bounds.maxX - shape.bounds.minX, shape.bounds.maxY - shape.bounds.minY);
10399
+ return side > 0 ? side : void 0;
10400
+ }
10038
10401
  return void 0;
10039
10402
  }
10040
10403
  function addVisualEffectAttrs(attrs, node, defs) {
@@ -10061,12 +10424,12 @@ function addPathMarkerAttrs(attrs, node, defs) {
10061
10424
  }
10062
10425
  }
10063
10426
  function transform(rotation, center) {
10064
- return rotation && center ? ` transform="rotate(${num(rotation)} ${num(center.x)} ${num(center.y)})"` : "";
10427
+ return rotation && center ? ` transform="rotate(${num2(rotation)} ${num2(center.x)} ${num2(center.y)})"` : "";
10065
10428
  }
10066
10429
  function imageAttrs(node, defs) {
10067
10430
  const attrs = [];
10068
10431
  if (node.style.opacity !== void 0) {
10069
- attrs.push(`opacity="${num(node.style.opacity)}"`);
10432
+ attrs.push(`opacity="${num2(node.style.opacity)}"`);
10070
10433
  }
10071
10434
  if (node.style.pointerEvents) {
10072
10435
  attrs.push(`pointer-events="${node.style.pointerEvents}"`);
@@ -10128,12 +10491,12 @@ function chartValueAttrs(detail) {
10128
10491
  `data-sf-series="${xml(detail.series)}"`,
10129
10492
  `data-sf-value-index="${detail.valueIndex}"`,
10130
10493
  detail.mark ? `data-sf-mark="${detail.mark}"` : void 0,
10131
- detail.radius !== void 0 ? `data-sf-bar-radius="${num(detail.radius)}"` : void 0,
10494
+ detail.radius !== void 0 ? `data-sf-bar-radius="${num2(detail.radius)}"` : void 0,
10132
10495
  detail.colorProp ? `data-sf-color-prop="${xml(detail.colorProp)}"` : void 0,
10133
10496
  detail.hasColorOverride ? 'data-sf-color-override="true"' : void 0,
10134
- detail.pieStart !== void 0 ? `data-sf-pie-start="${num(detail.pieStart)}"` : void 0,
10135
- detail.pieEnd !== void 0 ? `data-sf-pie-end="${num(detail.pieEnd)}"` : void 0,
10136
- detail.pieValue !== void 0 ? `data-sf-pie-value="${num(detail.pieValue)}"` : void 0
10497
+ detail.pieStart !== void 0 ? `data-sf-pie-start="${num2(detail.pieStart)}"` : void 0,
10498
+ detail.pieEnd !== void 0 ? `data-sf-pie-end="${num2(detail.pieEnd)}"` : void 0,
10499
+ detail.pieValue !== void 0 ? `data-sf-pie-value="${num2(detail.pieValue)}"` : void 0
10137
10500
  ];
10138
10501
  }
10139
10502
  function pieGeometryAttrs(detail) {
@@ -10148,39 +10511,39 @@ function chartScaleAttrs(detail) {
10148
10511
  function chartSeriesAttrs(detail) {
10149
10512
  return [
10150
10513
  `data-sf-series="${xml(detail.series)}"`,
10151
- detail.lineTension !== void 0 ? `data-sf-line-tension="${num(detail.lineTension)}"` : void 0,
10514
+ detail.lineTension !== void 0 ? `data-sf-line-tension="${num2(detail.lineTension)}"` : void 0,
10152
10515
  detail.mark ? `data-sf-mark="${xml(detail.mark)}"` : void 0,
10153
10516
  detail.pulled ? 'data-sf-label-pulled="true"' : void 0,
10154
- detail.midAngle !== void 0 ? `data-sf-pie-mid-angle="${num(detail.midAngle)}"` : void 0,
10155
- detail.wedgeRadius !== void 0 ? `data-sf-pie-wedge-radius="${num(detail.wedgeRadius)}"` : void 0
10517
+ detail.midAngle !== void 0 ? `data-sf-pie-mid-angle="${num2(detail.midAngle)}"` : void 0,
10518
+ detail.wedgeRadius !== void 0 ? `data-sf-pie-wedge-radius="${num2(detail.wedgeRadius)}"` : void 0
10156
10519
  ];
10157
10520
  }
10158
10521
  function frameBoundaryAttrs(detail) {
10159
- return [`${CORNER_RADIUS_ATTR}="${num(detail.cornerRadius)}"`, `${SQUARE_CORNER_ATTR}="${detail.squareCorner}"`];
10522
+ return [`${CORNER_RADIUS_ATTR}="${num2(detail.cornerRadius)}"`, `${SQUARE_CORNER_ATTR}="${detail.squareCorner}"`];
10160
10523
  }
10161
10524
  function declaredBoundsAttr(bounds) {
10162
- return `${SYMBOL_BOUNDS_ATTR}="${num(bounds.x)},${num(bounds.y)},${num(bounds.width)},${num(bounds.height)}"`;
10525
+ return `${SYMBOL_BOUNDS_ATTR}="${num2(bounds.x)},${num2(bounds.y)},${num2(bounds.width)},${num2(bounds.height)}"`;
10163
10526
  }
10164
10527
  function symbolGeometryAttrs(detail) {
10165
10528
  return [
10166
10529
  `${SYMBOL_POINTS_ATTR}="${xml(JSON.stringify(detail.points))}"`,
10167
- `${SYMBOL_TENSION_ATTR}="${num(detail.tension)}"`,
10530
+ `${SYMBOL_TENSION_ATTR}="${num2(detail.tension)}"`,
10168
10531
  declaredBoundsAttr(detail.declaredBounds)
10169
10532
  ];
10170
10533
  }
10171
10534
  function pathGeometryAttrs(detail) {
10172
10535
  return [
10173
10536
  `${PATH_POINTS_ATTR}="${xml(JSON.stringify(detail.points))}"`,
10174
- `${PATH_TENSION_ATTR}="${num(detail.tension)}"`,
10537
+ `${PATH_TENSION_ATTR}="${num2(detail.tension)}"`,
10175
10538
  `${PATH_CLOSED_ATTR}="${detail.closed}"`
10176
10539
  ];
10177
10540
  }
10178
10541
  function isometricGeometryAttrs(detail) {
10179
10542
  return [
10180
- `${ISOMETRIC_SIZE_ATTR}="${num(detail.width)},${num(detail.height)},${num(detail.depth)}"`,
10181
- `${CORNER_RADIUS_ATTR}="${num(detail.cornerRadius)}"`,
10543
+ `${ISOMETRIC_SIZE_ATTR}="${num2(detail.width)},${num2(detail.height)},${num2(detail.depth)}"`,
10544
+ `${CORNER_RADIUS_ATTR}="${num2(detail.cornerRadius)}"`,
10182
10545
  declaredBoundsAttr(detail.bounds),
10183
- `${ISOMETRIC_LABEL_OFFSET_ATTR}="${num(detail.labelOffsetX)},${num(detail.labelOffsetY)}"`
10546
+ `${ISOMETRIC_LABEL_OFFSET_ATTR}="${num2(detail.labelOffsetX)},${num2(detail.labelOffsetY)}"`
10184
10547
  ];
10185
10548
  }
10186
10549
  function linkDetailAttrs(detail) {
@@ -10206,8 +10569,8 @@ function contractAttrs(node) {
10206
10569
  node.propSolidOnly ? 'data-sf-prop-solid-only="true"' : void 0,
10207
10570
  node.mark ? `data-sf-mark="${xml(node.mark)}"` : void 0,
10208
10571
  node.zoneGradient ? 'data-sf-zone-gradient="true"' : void 0,
10209
- node.zoneGradient ? `data-sf-zone-axis-min="${num(node.zoneGradient.min)}"` : void 0,
10210
- node.zoneGradient ? `data-sf-zone-axis-max="${num(node.zoneGradient.max)}"` : void 0,
10572
+ node.zoneGradient ? `data-sf-zone-axis-min="${num2(node.zoneGradient.min)}"` : void 0,
10573
+ node.zoneGradient ? `data-sf-zone-axis-max="${num2(node.zoneGradient.max)}"` : void 0,
10211
10574
  label ? `tabindex="0" aria-label="${xml(label.replace(/\s+/g, " ").trim())}"` : void 0
10212
10575
  ].filter((attribute) => attribute !== void 0);
10213
10576
  return attributes.length ? ` ${attributes.join(" ")}` : "";
@@ -10216,7 +10579,7 @@ function contractAttrs(node) {
10216
10579
  // src/svg/text.ts
10217
10580
  function richLineTspans(words, x, dy, fontSize) {
10218
10581
  if (!words.length) {
10219
- return `<tspan x="${num(x)}"${dy} font-size="${fontSize}"></tspan>`;
10582
+ return `<tspan x="${num2(x)}"${dy} font-size="${fontSize}"></tspan>`;
10220
10583
  }
10221
10584
  const groups = [];
10222
10585
  for (const word of words) {
@@ -10231,15 +10594,15 @@ function richLineTspans(words, x, dy, fontSize) {
10231
10594
  }
10232
10595
  }
10233
10596
  return groups.map(
10234
- (group, i) => `<tspan${i === 0 ? ` x="${num(x)}"${dy}` : ""} font-size="${fontSize}" font-weight="${group.bold ? "bold" : "normal"}">${xml(group.text)}</tspan>`
10597
+ (group, i) => `<tspan${i === 0 ? ` x="${num2(x)}"${dy}` : ""} font-size="${fontSize}" font-weight="${group.bold ? "bold" : "normal"}">${xml(group.text)}</tspan>`
10235
10598
  ).join("");
10236
10599
  }
10237
10600
  function textSvg(node, defs) {
10238
10601
  const position = textPosition(node);
10239
- const fontSize = `${num(node.fontSize)}px`;
10602
+ const fontSize = `${num2(node.fontSize)}px`;
10240
10603
  const content = textContent(node, position, fontSize);
10241
10604
  const preserve = node.richLines ? ' xml:space="preserve"' : "";
10242
- return `<text id="${defs.elementId(node.id)}"${contractAttrs(node)} x="${num(position.x)}" y="${num(position.firstY)}" font-family="${xml(node.fontFamily)}" font-size="${fontSize}" font-weight="${node.fontWeight}"${node.letterSpacing ? ` letter-spacing="${num(node.letterSpacing)}"` : ""} fill="${xml(node.color)}" text-anchor="${position.anchor}" data-sf-valign="${node.verticalAlign}" data-sf-line-height="${num(node.lineHeight)}"${preserve}${transform(node.rotation, node.rotationCenter)}>${content}</text>`;
10605
+ return `<text id="${defs.elementId(node.id)}"${contractAttrs(node)} x="${num2(position.x)}" y="${num2(position.firstY)}" font-family="${xml(node.fontFamily)}" font-size="${fontSize}" font-weight="${node.fontWeight}"${node.letterSpacing ? ` letter-spacing="${num2(node.letterSpacing)}"` : ""} fill="${xml(node.color)}" text-anchor="${position.anchor}" data-sf-valign="${node.verticalAlign}" data-sf-line-height="${num2(node.lineHeight)}"${preserve}${transform(node.rotation, node.rotationCenter)}>${content}</text>`;
10243
10606
  }
10244
10607
  function textPosition(node) {
10245
10608
  const linePx = node.fontSize * node.lineHeight;
@@ -10265,10 +10628,10 @@ function textContent(node, position, fontSize) {
10265
10628
  return node.lines.map((line, index) => lineTspan(line, index, position, fontSize)).join("");
10266
10629
  }
10267
10630
  function lineTspan(line, index, position, fontSize) {
10268
- return `<tspan x="${num(position.x)}" font-size="${fontSize}"${lineOffset(index, position.linePx)}>${xml(line)}</tspan>`;
10631
+ return `<tspan x="${num2(position.x)}" font-size="${fontSize}"${lineOffset(index, position.linePx)}>${xml(line)}</tspan>`;
10269
10632
  }
10270
10633
  function lineOffset(index, linePx) {
10271
- return index ? ` dy="${num(linePx)}px"` : "";
10634
+ return index ? ` dy="${num2(linePx)}px"` : "";
10272
10635
  }
10273
10636
 
10274
10637
  // src/svg/nodes.ts
@@ -10277,7 +10640,7 @@ function shaderSurfaceSvg(node, defs) {
10277
10640
  const parameters = xml(JSON.stringify(node.shader.parameters));
10278
10641
  const surfacePadding = node.shader.surfacePadding ?? 0;
10279
10642
  const fillColor = node.fillColor ? ` data-sf-shader-fill="${xml(node.fillColor)}"` : "";
10280
- return `<foreignObject id="${defs.elementId(node.id)}"${contractAttrs(node)} data-sf-shader="${xml(node.shader.program)}" data-sf-shader-name="${xml(node.shader.name)}" data-sf-shader-params="${parameters}" data-sf-shader-mask="${node.mask}" data-sf-shader-padding="${num(surfacePadding)}"${fillColor} x="${num(node.x)}" y="${num(node.y)}" width="${num(node.width)}" height="${num(node.height)}"${rotation} pointer-events="${node.shader.program === "ruler" || node.identity?.role === "widget" ? "all" : "none"}"><canvas xmlns="http://www.w3.org/1999/xhtml" data-sf-shader-canvas="true" style="display:block;width:100%;height:100%;pointer-events:none"></canvas></foreignObject>`;
10643
+ return `<foreignObject id="${defs.elementId(node.id)}"${contractAttrs(node)} data-sf-shader="${xml(node.shader.program)}" data-sf-shader-name="${xml(node.shader.name)}" data-sf-shader-params="${parameters}" data-sf-shader-mask="${node.mask}" data-sf-shader-padding="${num2(surfacePadding)}"${fillColor} x="${num2(node.x)}" y="${num2(node.y)}" width="${num2(node.width)}" height="${num2(node.height)}"${rotation} pointer-events="${node.shader.program === "ruler" || node.identity?.role === "widget" ? "all" : "none"}"><canvas xmlns="http://www.w3.org/1999/xhtml" data-sf-shader-canvas="true" style="display:block;width:100%;height:100%;pointer-events:none"></canvas></foreignObject>`;
10281
10644
  }
10282
10645
  function rollingDigitSvg(character, node) {
10283
10646
  const style = node.componentStyle;
@@ -10292,7 +10655,7 @@ function rollingNumberSvg(node, defs) {
10292
10655
  const label = node.label ? `<span xmlns="http://www.w3.org/1999/xhtml" style="margin-bottom:.14em;color:${xml(node.componentStyle.labelColor)};font-size:.16em;font-weight:600;letter-spacing:.24em;line-height:1">${xml(node.label)}</span>` : "";
10293
10656
  const digits = [...node.value].map((character) => rollingDigitSvg(character, node)).join("");
10294
10657
  const componentStyle = xml(JSON.stringify(node.componentStyle));
10295
- return `<foreignObject id="${defs.elementId(node.id)}"${contractAttrs(node)} data-sf-widget="rollingNumber" data-sf-widget-value="${xml(node.value)}" data-sf-widget-digits="${node.digits ?? ""}" data-sf-widget-duration="${num(node.duration)}" data-sf-widget-style="${componentStyle}" x="${num(node.x)}" y="${num(node.y)}" width="${num(node.width)}" height="${num(node.height)}"${rotation}><div xmlns="http://www.w3.org/1999/xhtml" style="width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;font-family:${xml(node.componentStyle.fontFamily)};font-size:${num(fontSize)}px">${label}<div data-sf-rolling-digits="true" aria-label="${xml(node.value)}" style="display:flex;gap:${node.componentStyle.variant === "plain" ? ".02em" : ".1em"};font-size:1em">${digits}</div></div></foreignObject>`;
10658
+ return `<foreignObject id="${defs.elementId(node.id)}"${contractAttrs(node)} data-sf-widget="rollingNumber" data-sf-widget-value="${xml(node.value)}" data-sf-widget-digits="${node.digits ?? ""}" data-sf-widget-duration="${num2(node.duration)}" data-sf-widget-style="${componentStyle}" x="${num2(node.x)}" y="${num2(node.y)}" width="${num2(node.width)}" height="${num2(node.height)}"${rotation}><div xmlns="http://www.w3.org/1999/xhtml" style="width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;font-family:${xml(node.componentStyle.fontFamily)};font-size:${num2(fontSize)}px">${label}<div data-sf-rolling-digits="true" aria-label="${xml(node.value)}" style="display:flex;gap:${node.componentStyle.variant === "plain" ? ".02em" : ".1em"};font-size:1em">${digits}</div></div></foreignObject>`;
10296
10659
  }
10297
10660
  function nodeSvg(node, defs) {
10298
10661
  if (node.type === "text") {
@@ -10305,14 +10668,14 @@ function nodeSvg(node, defs) {
10305
10668
  return rollingNumberSvg(node, defs);
10306
10669
  }
10307
10670
  if (node.type === "image") {
10308
- return `<image id="${defs.elementId(node.id)}"${contractAttrs(node)} ${imageAttrs(node, defs)} href="${xml(node.href)}" x="${num(node.x)}" y="${num(node.y)}" width="${num(node.width)}" height="${num(node.height)}" preserveAspectRatio="xMidYMid slice"/>`;
10671
+ return `<image id="${defs.elementId(node.id)}"${contractAttrs(node)} ${imageAttrs(node, defs)} href="${xml(node.href)}" x="${num2(node.x)}" y="${num2(node.y)}" width="${num2(node.width)}" height="${num2(node.height)}" preserveAspectRatio="${node.aspect === "none" ? "none" : `xMidYMid ${node.aspect ?? "slice"}`}"${transform(node.rotation, node.rotationCenter)}/>`;
10309
10672
  }
10310
10673
  const attrs = `id="${defs.elementId(node.id)}"${contractAttrs(node)} ${styleAttrs(node, defs)}`;
10311
10674
  if (node.type === "rect") {
10312
- return `<rect ${attrs} x="${num(node.x)}" y="${num(node.y)}" width="${num(node.width)}" height="${num(node.height)}"${node.rx ? ` rx="${num(node.rx)}"` : ""}${transform(node.rotation, { x: node.x + node.width / 2, y: node.y + node.height / 2 })}/>`;
10675
+ return `<rect ${attrs} x="${num2(node.x)}" y="${num2(node.y)}" width="${num2(node.width)}" height="${num2(node.height)}"${node.rx ? ` rx="${num2(node.rx)}"` : ""}${transform(node.rotation, { x: node.x + node.width / 2, y: node.y + node.height / 2 })}/>`;
10313
10676
  }
10314
10677
  if (node.type === "ellipse") {
10315
- return `<ellipse ${attrs} cx="${num(node.cx)}" cy="${num(node.cy)}" rx="${num(node.rx)}" ry="${num(node.ry)}"${transform(node.rotation, { x: node.cx, y: node.cy })}/>`;
10678
+ return `<ellipse ${attrs} cx="${num2(node.cx)}" cy="${num2(node.cy)}" rx="${num2(node.rx)}" ry="${num2(node.ry)}"${transform(node.rotation, { x: node.cx, y: node.cy })}/>`;
10316
10679
  }
10317
10680
  return `<path ${attrs} d="${xml(node.d)}"${transform(node.rotation, node.rotationCenter)}/>`;
10318
10681
  }
@@ -10322,12 +10685,12 @@ function renderSvgDocument(scene, options = {}) {
10322
10685
  const padding = options.padding ?? 16;
10323
10686
  const width = Math.max(1, scene.bounds.maxX - scene.bounds.minX + padding * 2);
10324
10687
  const height = Math.max(1, scene.bounds.maxY - scene.bounds.minY + padding * 2);
10325
- const viewBox = `${num(scene.bounds.minX - padding)} ${num(scene.bounds.minY - padding)} ${num(width)} ${num(height)}`;
10688
+ const viewBox = `${num2(scene.bounds.minX - padding)} ${num2(scene.bounds.minY - padding)} ${num2(width)} ${num2(height)}`;
10326
10689
  const defs = new SvgDefinitionRegistry(options.idPrefix ? `${options.idPrefix}-` : "");
10327
10690
  const nodes = options.linkHitAreas ? withLinkHitAreas(scene.nodes) : scene.nodes;
10328
10691
  const body = nodes.map((node) => nodeSvg(node, defs)).join("\n ");
10329
10692
  const declaration = options.includeXmlDeclaration === false ? "" : '<?xml version="1.0" encoding="UTF-8"?>\n';
10330
- const svg = `${declaration}<svg xmlns="http://www.w3.org/2000/svg" width="${num(width)}px" height="${num(height)}px" viewBox="${viewBox}" role="img" data-sf-contract="${DOM_CONTRACT_VERSION}">
10693
+ const svg = `${declaration}<svg xmlns="http://www.w3.org/2000/svg" width="${num2(width)}px" height="${num2(height)}px" viewBox="${viewBox}" role="img" data-sf-contract="${DOM_CONTRACT_VERSION}">
10331
10694
  <defs>${defs.entries.join("")}</defs>
10332
10695
  ${body}
10333
10696
  </svg>`;
@@ -10393,5 +10756,5 @@ function defineComponent(definition) {
10393
10756
  }
10394
10757
 
10395
10758
  export { collectCanvasObjects, decodeColor, decodeShadow, defineComponent, render, renderCanvas, sflToRenderInput, sliderReach };
10396
- //# sourceMappingURL=chunk-SDOHUNBW.js.map
10397
- //# sourceMappingURL=chunk-SDOHUNBW.js.map
10759
+ //# sourceMappingURL=chunk-TQIYK5OA.js.map
10760
+ //# sourceMappingURL=chunk-TQIYK5OA.js.map