@pixerate/editor-react 0.20.0 → 0.22.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.
@@ -976,8 +976,220 @@ function CanvasGuidesLayer({
976
976
  );
977
977
  }
978
978
 
979
+ // src/canvas/useCanvasDraw.ts
980
+ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef7, useState as useState3 } from "react";
981
+ import {
982
+ createCanvasDrawController
983
+ } from "@pixerate/editor/canvas";
984
+ function useCanvasDraw(options) {
985
+ const nodesRef = useRef7(options.nodes);
986
+ nodesRef.current = options.nodes;
987
+ const optionsRef = useRef7(options);
988
+ optionsRef.current = options;
989
+ const [tool, setToolState] = useState3(options.initialTool ?? "select");
990
+ const [activeStroke, setActiveStroke] = useState3(null);
991
+ const controller = useMemo3(
992
+ () => createCanvasDrawController({
993
+ getNodes: () => nodesRef.current,
994
+ setNodes: (next) => {
995
+ nodesRef.current = next;
996
+ optionsRef.current.setNodes(next);
997
+ },
998
+ get history() {
999
+ return optionsRef.current.history;
1000
+ },
1001
+ tool: options.initialTool ?? "select",
1002
+ style: options.initialStyle,
1003
+ drawOptions: options.drawOptions,
1004
+ generateNodeId: options.generateNodeId,
1005
+ onToolChange: setToolState,
1006
+ onStyleChange: (s) => setStyleState(s),
1007
+ onActiveStrokeChange: setActiveStroke,
1008
+ onStrokeComplete: (n) => optionsRef.current.onStrokeComplete?.(n),
1009
+ onNodesErase: (erased) => optionsRef.current.onNodesErase?.(erased)
1010
+ }),
1011
+ []
1012
+ );
1013
+ const [style, setStyleState] = useState3(controller.style);
1014
+ useEffect4(() => {
1015
+ if (options.initialTool && options.initialTool !== controller.tool) {
1016
+ controller.setTool(options.initialTool);
1017
+ }
1018
+ }, [options.initialTool, controller]);
1019
+ return useMemo3(
1020
+ () => ({
1021
+ tool,
1022
+ style,
1023
+ activeStroke,
1024
+ isDrawing: activeStroke !== null,
1025
+ setTool: controller.setTool,
1026
+ setStyle: controller.setStyle,
1027
+ startStroke: controller.startStroke,
1028
+ continueStroke: controller.continueStroke,
1029
+ endStroke: controller.endStroke,
1030
+ cancelStroke: controller.cancelStroke,
1031
+ eraseAt: controller.eraseAt
1032
+ }),
1033
+ [tool, style, activeStroke, controller]
1034
+ );
1035
+ }
1036
+
1037
+ // src/canvas/CanvasDrawLayer.tsx
1038
+ import { useCallback as useCallback7, useEffect as useEffect5, useRef as useRef8, useState as useState4 } from "react";
1039
+ import {
1040
+ strokePointsToPathData,
1041
+ resolveCanvasGradient,
1042
+ gradientToSvgDef
1043
+ } from "@pixerate/editor/canvas";
1044
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1045
+ var DEFAULT_VIEWPORT2 = { x: 0, y: 0, zoom: 1 };
1046
+ function CanvasDrawLayer({
1047
+ draw,
1048
+ viewport = DEFAULT_VIEWPORT2,
1049
+ className = "",
1050
+ style,
1051
+ cursor,
1052
+ eraserRadius = 12
1053
+ }) {
1054
+ const svgRef = useRef8(null);
1055
+ const [spaceHeld, setSpaceHeld] = useState4(false);
1056
+ const [eraserPos, setEraserPos] = useState4(null);
1057
+ useEffect5(() => {
1058
+ function onKeyDown(e) {
1059
+ if (e.code === "Space" && !e.repeat) {
1060
+ const tag = e.target?.tagName;
1061
+ if (tag === "INPUT" || tag === "TEXTAREA" || e.target?.isContentEditable) {
1062
+ return;
1063
+ }
1064
+ setSpaceHeld(true);
1065
+ }
1066
+ }
1067
+ function onKeyUp(e) {
1068
+ if (e.code === "Space") {
1069
+ setSpaceHeld(false);
1070
+ }
1071
+ }
1072
+ window.addEventListener("keydown", onKeyDown);
1073
+ window.addEventListener("keyup", onKeyUp);
1074
+ return () => {
1075
+ window.removeEventListener("keydown", onKeyDown);
1076
+ window.removeEventListener("keyup", onKeyUp);
1077
+ };
1078
+ }, []);
1079
+ const clientToFlow = useCallback7(
1080
+ (clientX, clientY) => {
1081
+ if (!svgRef.current) return [0, 0];
1082
+ const rect = svgRef.current.getBoundingClientRect();
1083
+ const screenX = clientX - rect.left;
1084
+ const screenY = clientY - rect.top;
1085
+ const flowX = (screenX - viewport.x) / viewport.zoom;
1086
+ const flowY = (screenY - viewport.y) / viewport.zoom;
1087
+ return [flowX, flowY];
1088
+ },
1089
+ [viewport.x, viewport.y, viewport.zoom]
1090
+ );
1091
+ const isDrawingMode = draw.tool !== "select";
1092
+ const interactive = isDrawingMode && !spaceHeld;
1093
+ const handlePointerDown = (e) => {
1094
+ if (!interactive || e.button !== 0) return;
1095
+ e.preventDefault();
1096
+ e.stopPropagation();
1097
+ e.target.setPointerCapture?.(e.pointerId);
1098
+ const flowPt = clientToFlow(e.clientX, e.clientY);
1099
+ draw.startStroke(flowPt);
1100
+ if (draw.tool === "eraser") {
1101
+ setEraserPos([e.clientX, e.clientY]);
1102
+ }
1103
+ };
1104
+ const handlePointerMove = (e) => {
1105
+ if (draw.tool === "eraser" && !spaceHeld) {
1106
+ setEraserPos([e.clientX, e.clientY]);
1107
+ }
1108
+ if (!interactive) return;
1109
+ if (e.buttons === 1) {
1110
+ e.preventDefault();
1111
+ e.stopPropagation();
1112
+ const flowPt = clientToFlow(e.clientX, e.clientY);
1113
+ draw.continueStroke(flowPt);
1114
+ }
1115
+ };
1116
+ const handlePointerUp = (e) => {
1117
+ if (!interactive || e.button !== 0) return;
1118
+ e.preventDefault();
1119
+ e.stopPropagation();
1120
+ try {
1121
+ e.target.releasePointerCapture?.(e.pointerId);
1122
+ } catch {
1123
+ }
1124
+ draw.endStroke();
1125
+ };
1126
+ const handlePointerLeave = () => {
1127
+ setEraserPos(null);
1128
+ };
1129
+ const resolvedCursor = cursor ? cursor : spaceHeld ? "grab" : draw.tool === "eraser" ? "crosshair" : draw.tool === "pen" || draw.tool === "highlighter" ? "crosshair" : "default";
1130
+ const strokePath = draw.activeStroke ? strokePointsToPathData(draw.activeStroke.points) : null;
1131
+ const activeGradient = draw.activeStroke ? resolveCanvasGradient(draw.activeStroke.style.gradient ?? draw.activeStroke.style.color) : null;
1132
+ const resolvedGradientDef = activeGradient ? gradientToSvgDef(activeGradient, "active-draw-stroke-gradient") : null;
1133
+ const strokePaint = resolvedGradientDef ? `url(#${resolvedGradientDef.id})` : draw.activeStroke ? draw.activeStroke.style.color : "currentColor";
1134
+ return /* @__PURE__ */ jsxs3(
1135
+ "svg",
1136
+ {
1137
+ ref: svgRef,
1138
+ className: `pixerate-canvas-draw-layer ${className}`,
1139
+ style: {
1140
+ position: "absolute",
1141
+ inset: 0,
1142
+ width: "100%",
1143
+ height: "100%",
1144
+ pointerEvents: interactive ? "all" : "none",
1145
+ cursor: interactive ? resolvedCursor : void 0,
1146
+ overflow: "visible",
1147
+ zIndex: 20,
1148
+ touchAction: "none",
1149
+ ...style
1150
+ },
1151
+ onPointerDown: handlePointerDown,
1152
+ onPointerMove: handlePointerMove,
1153
+ onPointerUp: handlePointerUp,
1154
+ onPointerLeave: handlePointerLeave,
1155
+ children: [
1156
+ resolvedGradientDef && /* @__PURE__ */ jsxs3("defs", { children: [
1157
+ resolvedGradientDef.type === "linear" && /* @__PURE__ */ jsx3("linearGradient", { id: resolvedGradientDef.id, ...resolvedGradientDef.attrs, children: resolvedGradientDef.stops.map((stop, i) => /* @__PURE__ */ jsx3("stop", { offset: stop.offset, stopColor: stop.stopColor, stopOpacity: stop.stopOpacity }, i)) }),
1158
+ resolvedGradientDef.type === "radial" && /* @__PURE__ */ jsx3("radialGradient", { id: resolvedGradientDef.id, ...resolvedGradientDef.attrs, children: resolvedGradientDef.stops.map((stop, i) => /* @__PURE__ */ jsx3("stop", { offset: stop.offset, stopColor: stop.stopColor, stopOpacity: stop.stopOpacity }, i)) })
1159
+ ] }),
1160
+ /* @__PURE__ */ jsx3("g", { transform: `translate(${viewport.x}, ${viewport.y}) scale(${viewport.zoom})`, children: draw.activeStroke && strokePath && /* @__PURE__ */ jsx3(
1161
+ "path",
1162
+ {
1163
+ className: "pixerate-canvas-draw-active",
1164
+ d: strokePath,
1165
+ fill: "none",
1166
+ stroke: strokePaint,
1167
+ strokeWidth: draw.activeStroke.style.width,
1168
+ opacity: draw.activeStroke.style.opacity,
1169
+ strokeLinecap: draw.activeStroke.style.cap ?? "round",
1170
+ strokeLinejoin: draw.activeStroke.style.join ?? "round"
1171
+ }
1172
+ ) }),
1173
+ draw.tool === "eraser" && eraserPos && svgRef.current && /* @__PURE__ */ jsx3(
1174
+ "circle",
1175
+ {
1176
+ cx: eraserPos[0] - svgRef.current.getBoundingClientRect().left,
1177
+ cy: eraserPos[1] - svgRef.current.getBoundingClientRect().top,
1178
+ r: eraserRadius,
1179
+ fill: "rgba(239, 68, 68, 0.15)",
1180
+ stroke: "rgba(239, 68, 68, 0.8)",
1181
+ strokeWidth: 1.5,
1182
+ strokeDasharray: "3 3",
1183
+ pointerEvents: "none"
1184
+ }
1185
+ )
1186
+ ]
1187
+ }
1188
+ );
1189
+ }
1190
+
979
1191
  // src/canvas/useCanvasMeasure.ts
980
- import { useCallback as useCallback7, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef7, useState as useState3 } from "react";
1192
+ import { useCallback as useCallback8, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef9, useState as useState5 } from "react";
981
1193
  import {
982
1194
  isMeasureModifierHeld,
983
1195
  measureSelection
@@ -985,11 +1197,11 @@ import {
985
1197
  var EMPTY = { guides: [], target: null, selection: null };
986
1198
  function useCanvasMeasure(options) {
987
1199
  const { nodes, enabled = true, requireModifier = true, targetElement, rectOptions } = options;
988
- const [hoveredId, setHoveredId] = useState3(null);
989
- const [modifier, setModifier] = useState3(false);
990
- const optionsRef = useRef7(options);
1200
+ const [hoveredId, setHoveredId] = useState5(null);
1201
+ const [modifier, setModifier] = useState5(false);
1202
+ const optionsRef = useRef9(options);
991
1203
  optionsRef.current = options;
992
- useEffect4(() => {
1204
+ useEffect6(() => {
993
1205
  if (!enabled) return;
994
1206
  const target = targetElement ?? (typeof window !== "undefined" ? window : null);
995
1207
  if (!target) return;
@@ -1006,16 +1218,16 @@ function useCanvasMeasure(options) {
1006
1218
  };
1007
1219
  }, [enabled, targetElement]);
1008
1220
  const active = enabled && (!requireModifier || modifier);
1009
- const result = useMemo3(() => {
1221
+ const result = useMemo4(() => {
1010
1222
  if (!active) return EMPTY;
1011
1223
  const ids = optionsRef.current.getSelectedIds?.() ?? nodes.filter((n) => n.selected).map((n) => n.id);
1012
1224
  return measureSelection(nodes, ids, hoveredId, rectOptions);
1013
1225
  }, [active, nodes, hoveredId, rectOptions]);
1014
- const onNodeMouseEnter = useCallback7((event, node) => {
1226
+ const onNodeMouseEnter = useCallback8((event, node) => {
1015
1227
  setHoveredId(node.id);
1016
1228
  if (event && typeof event.altKey === "boolean") setModifier(event.altKey);
1017
1229
  }, []);
1018
- const onNodeMouseLeave = useCallback7((_event, node) => {
1230
+ const onNodeMouseLeave = useCallback8((_event, node) => {
1019
1231
  setHoveredId((current) => current === node.id ? null : current);
1020
1232
  }, []);
1021
1233
  return {
@@ -1094,12 +1306,12 @@ import {
1094
1306
 
1095
1307
  // src/canvas/ShapeNode.tsx
1096
1308
  import { getShapeRenderData, isShapeDescription } from "@pixerate/editor/canvas";
1097
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1098
- function ShapeNode({ data, width, height, selected, className = "", children }) {
1309
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1310
+ function ShapeNode({ id, data, width, height, selected, className = "", children }) {
1099
1311
  const shape = data?.shape;
1100
1312
  if (!isShapeDescription(shape)) return null;
1101
- const render = getShapeRenderData(shape, { width, height });
1102
- return /* @__PURE__ */ jsxs3(
1313
+ const render = getShapeRenderData(shape, { width, height }, { id: id || data?.id });
1314
+ return /* @__PURE__ */ jsxs4(
1103
1315
  "div",
1104
1316
  {
1105
1317
  className: `pixerate-shape-node ${className}`.trim(),
@@ -1107,7 +1319,7 @@ function ShapeNode({ data, width, height, selected, className = "", children })
1107
1319
  "data-selected": selected ? "true" : void 0,
1108
1320
  style: { position: "relative", width: "100%", height: "100%" },
1109
1321
  children: [
1110
- /* @__PURE__ */ jsx3(
1322
+ /* @__PURE__ */ jsxs4(
1111
1323
  "svg",
1112
1324
  {
1113
1325
  viewBox: render.viewBox,
@@ -1116,19 +1328,29 @@ function ShapeNode({ data, width, height, selected, className = "", children })
1116
1328
  height: "100%",
1117
1329
  style: { display: "block", overflow: "visible" },
1118
1330
  "aria-hidden": "true",
1119
- children: /* @__PURE__ */ jsx3(
1120
- "path",
1121
- {
1122
- d: render.d,
1123
- transform: render.transform,
1124
- fill: render.fill,
1125
- fillRule: render.fillRule,
1126
- stroke: render.stroke,
1127
- strokeWidth: render.strokeWidth,
1128
- opacity: render.opacity,
1129
- vectorEffect: "non-scaling-stroke"
1130
- }
1131
- )
1331
+ children: [
1332
+ (render.fillGradient || render.strokeGradient) && /* @__PURE__ */ jsxs4("defs", { children: [
1333
+ render.fillGradient && render.fillGradient.type === "linear" && /* @__PURE__ */ jsx4("linearGradient", { id: render.fillGradient.id, ...render.fillGradient.attrs, children: render.fillGradient.stops.map((stop, i) => /* @__PURE__ */ jsx4("stop", { offset: stop.offset, stopColor: stop.stopColor, stopOpacity: stop.stopOpacity }, i)) }),
1334
+ render.fillGradient && render.fillGradient.type === "radial" && /* @__PURE__ */ jsx4("radialGradient", { id: render.fillGradient.id, ...render.fillGradient.attrs, children: render.fillGradient.stops.map((stop, i) => /* @__PURE__ */ jsx4("stop", { offset: stop.offset, stopColor: stop.stopColor, stopOpacity: stop.stopOpacity }, i)) }),
1335
+ render.strokeGradient && render.strokeGradient.type === "linear" && /* @__PURE__ */ jsx4("linearGradient", { id: render.strokeGradient.id, ...render.strokeGradient.attrs, children: render.strokeGradient.stops.map((stop, i) => /* @__PURE__ */ jsx4("stop", { offset: stop.offset, stopColor: stop.stopColor, stopOpacity: stop.stopOpacity }, i)) }),
1336
+ render.strokeGradient && render.strokeGradient.type === "radial" && /* @__PURE__ */ jsx4("radialGradient", { id: render.strokeGradient.id, ...render.strokeGradient.attrs, children: render.strokeGradient.stops.map((stop, i) => /* @__PURE__ */ jsx4("stop", { offset: stop.offset, stopColor: stop.stopColor, stopOpacity: stop.stopOpacity }, i)) })
1337
+ ] }),
1338
+ /* @__PURE__ */ jsx4(
1339
+ "path",
1340
+ {
1341
+ d: render.d,
1342
+ transform: render.transform,
1343
+ fill: render.fill,
1344
+ fillRule: render.fillRule,
1345
+ stroke: render.stroke,
1346
+ strokeWidth: render.strokeWidth,
1347
+ strokeLinecap: render.strokeLinecap,
1348
+ strokeLinejoin: render.strokeLinejoin,
1349
+ opacity: render.opacity,
1350
+ vectorEffect: "non-scaling-stroke"
1351
+ }
1352
+ )
1353
+ ]
1132
1354
  }
1133
1355
  ),
1134
1356
  children
@@ -1264,17 +1486,17 @@ import {
1264
1486
  } from "@pixerate/editor/canvas";
1265
1487
 
1266
1488
  // src/canvas/useCanvasAutoLayout.ts
1267
- import { useLayoutEffect, useMemo as useMemo4, useRef as useRef8, useState as useState4 } from "react";
1489
+ import { useLayoutEffect, useMemo as useMemo5, useRef as useRef10, useState as useState6 } from "react";
1268
1490
  import {
1269
1491
  createCanvasAutoLayoutController
1270
1492
  } from "@pixerate/editor/canvas";
1271
1493
  function useCanvasAutoLayout(options) {
1272
- const [guides, setGuides] = useState4([]);
1273
- const nodesRef = useRef8(options.nodes);
1494
+ const [guides, setGuides] = useState6([]);
1495
+ const nodesRef = useRef10(options.nodes);
1274
1496
  nodesRef.current = options.nodes;
1275
- const optionsRef = useRef8(options);
1497
+ const optionsRef = useRef10(options);
1276
1498
  optionsRef.current = options;
1277
- const controller = useMemo4(
1499
+ const controller = useMemo5(
1278
1500
  () => createCanvasAutoLayoutController({
1279
1501
  getNodes: () => nodesRef.current,
1280
1502
  setNodes: (next) => {
@@ -1303,7 +1525,7 @@ function useCanvasAutoLayout(options) {
1303
1525
  useLayoutEffect(() => {
1304
1526
  if (controller.hasPending) controller.flush();
1305
1527
  }, [options.nodes, controller]);
1306
- return useMemo4(
1528
+ return useMemo5(
1307
1529
  () => ({
1308
1530
  ...controller,
1309
1531
  /** Drop marker of the active reorder drag (flow coordinates). */
@@ -1317,7 +1539,7 @@ function useCanvasAutoLayout(options) {
1317
1539
  }
1318
1540
 
1319
1541
  // src/canvas/useCanvasTokens.ts
1320
- import { useCallback as useCallback8, useEffect as useEffect5, useRef as useRef9, useSyncExternalStore as useSyncExternalStore2 } from "react";
1542
+ import { useCallback as useCallback9, useEffect as useEffect7, useRef as useRef11, useSyncExternalStore as useSyncExternalStore2 } from "react";
1321
1543
  import {
1322
1544
  bindCanvasToken,
1323
1545
  detachTokenBindings,
@@ -1328,10 +1550,10 @@ import {
1328
1550
  function useCanvasTokens(options) {
1329
1551
  const { store, nodes, setNodes, modes, properties } = options;
1330
1552
  const state = useSyncExternalStore2(store.subscribe, store.getState, store.getState);
1331
- const latest = useRef9({ nodes, setNodes, modes, properties });
1553
+ const latest = useRef11({ nodes, setNodes, modes, properties });
1332
1554
  latest.current = { nodes, setNodes, modes, properties };
1333
1555
  const opts = () => ({ modes: latest.current.modes, properties: latest.current.properties });
1334
- useEffect5(
1556
+ useEffect7(
1335
1557
  () => store.subscribe((event) => {
1336
1558
  if (event.deletedVariableIds.length === 0) return;
1337
1559
  latest.current.setNodes((prev) => detachTokenBindings(prev, event.previous, event.deletedVariableIds, opts()));
@@ -1340,11 +1562,11 @@ function useCanvasTokens(options) {
1340
1562
  [store]
1341
1563
  );
1342
1564
  const modesKey = JSON.stringify(modes ?? {});
1343
- useEffect5(() => {
1565
+ useEffect7(() => {
1344
1566
  const next = resolveCanvasTokens(nodes, state, opts());
1345
1567
  if (next !== nodes) setNodes(next);
1346
1568
  }, [nodes, state, modesKey, properties]);
1347
- const bind = useCallback8(
1569
+ const bind = useCallback9(
1348
1570
  (ids, propertyKey, variableId) => {
1349
1571
  const next = bindCanvasToken(latest.current.nodes, ids, propertyKey, variableId, store.getState(), opts());
1350
1572
  if (next !== latest.current.nodes) latest.current.setNodes(next);
@@ -1352,7 +1574,7 @@ function useCanvasTokens(options) {
1352
1574
  // eslint-disable-next-line react-hooks/exhaustive-deps
1353
1575
  [store]
1354
1576
  );
1355
- const setMode = useCallback8(
1577
+ const setMode = useCallback9(
1356
1578
  (ids, collectionId, modeId) => {
1357
1579
  const next = setCanvasTokenMode(latest.current.nodes, ids, collectionId, modeId, store.getState(), opts());
1358
1580
  if (next !== latest.current.nodes) latest.current.setNodes(next);
@@ -1360,7 +1582,7 @@ function useCanvasTokens(options) {
1360
1582
  // eslint-disable-next-line react-hooks/exhaustive-deps
1361
1583
  [store]
1362
1584
  );
1363
- const modesOf = useCallback8(
1585
+ const modesOf = useCallback9(
1364
1586
  (nodeId) => {
1365
1587
  const node = latest.current.nodes.find((n) => n.id === nodeId);
1366
1588
  return node ? getEffectiveTokenModes(node, latest.current.nodes, store.getState(), opts()) : {};
@@ -1382,7 +1604,7 @@ import {
1382
1604
  getTokensForProperty
1383
1605
  } from "@pixerate/editor/canvas";
1384
1606
  import { getCurrentMode, resolveToken } from "@pixerate/editor/tokens";
1385
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1607
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1386
1608
  function TokenBindingPicker({
1387
1609
  state,
1388
1610
  nodes,
@@ -1399,15 +1621,15 @@ function TokenBindingPicker({
1399
1621
  let rows = getBindableTokenProperties(first, options);
1400
1622
  if (properties) rows = properties.map((key) => rows.find((p) => p.key === key)).filter((p) => !!p);
1401
1623
  const modes = getEffectiveTokenModes2(first, nodes, state, options);
1402
- return /* @__PURE__ */ jsxs4("div", { "data-token-binding-picker": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, children: [
1624
+ return /* @__PURE__ */ jsxs5("div", { "data-token-binding-picker": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, children: [
1403
1625
  rows.map((property) => {
1404
1626
  const ids = new Set(selected.map((n) => getNodeTokenBindings(n)[property.key] ?? ""));
1405
1627
  const value = ids.size === 1 ? [...ids][0] : "__mixed";
1406
1628
  const candidates = getTokensForProperty(state, property, first);
1407
1629
  const resolved = value && value !== "__mixed" ? resolveToken(state, value, modes) : null;
1408
- return /* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2", "data-token-property": property.key, children: [
1409
- /* @__PURE__ */ jsx4("span", { className: "w-28 shrink-0 text-muted-foreground", children: property.label }),
1410
- /* @__PURE__ */ jsxs4(
1630
+ return /* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2", "data-token-property": property.key, children: [
1631
+ /* @__PURE__ */ jsx5("span", { className: "w-28 shrink-0 text-muted-foreground", children: property.label }),
1632
+ /* @__PURE__ */ jsxs5(
1411
1633
  "select",
1412
1634
  {
1413
1635
  "aria-label": `${property.label} variable`,
@@ -1422,11 +1644,11 @@ function TokenBindingPicker({
1422
1644
  }
1423
1645
  },
1424
1646
  children: [
1425
- value === "__mixed" ? /* @__PURE__ */ jsx4("option", { value: "__mixed", children: "Mixed" }) : null,
1426
- /* @__PURE__ */ jsx4("option", { value: "", children: "None" }),
1647
+ value === "__mixed" ? /* @__PURE__ */ jsx5("option", { value: "__mixed", children: "Mixed" }) : null,
1648
+ /* @__PURE__ */ jsx5("option", { value: "", children: "None" }),
1427
1649
  candidates.map((v) => {
1428
1650
  const collection = state.collections.find((c) => c.id === v.collectionId);
1429
- return /* @__PURE__ */ jsxs4("option", { value: v.id, children: [
1651
+ return /* @__PURE__ */ jsxs5("option", { value: v.id, children: [
1430
1652
  v.name,
1431
1653
  collection && state.collections.length > 1 ? ` (${collection.name})` : ""
1432
1654
  ] }, v.id);
@@ -1434,17 +1656,17 @@ function TokenBindingPicker({
1434
1656
  ]
1435
1657
  }
1436
1658
  ),
1437
- /* @__PURE__ */ jsx4("span", { className: "w-16 shrink-0 truncate text-right text-muted-foreground", "data-token-resolved": "", children: resolved?.ok ? property.type === "color" ? /* @__PURE__ */ jsx4(
1659
+ /* @__PURE__ */ jsx5("span", { className: "w-16 shrink-0 truncate text-right text-muted-foreground", "data-token-resolved": "", children: resolved?.ok ? property.type === "color" ? /* @__PURE__ */ jsx5(
1438
1660
  "span",
1439
1661
  {
1440
1662
  className: "inline-block h-3 w-3 rounded border border-border align-middle",
1441
1663
  style: { background: String(resolved.value) },
1442
1664
  title: String(resolved.value)
1443
1665
  }
1444
- ) : String(resolved.value) : resolved ? /* @__PURE__ */ jsx4("span", { title: resolved.message, children: "\u26A0" }) : null })
1666
+ ) : String(resolved.value) : resolved ? /* @__PURE__ */ jsx5("span", { title: resolved.message, children: "\u26A0" }) : null })
1445
1667
  ] }, property.key);
1446
1668
  }),
1447
- state.collections.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground", children: "No variables yet." }) : null
1669
+ state.collections.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "text-muted-foreground", children: "No variables yet." }) : null
1448
1670
  ] });
1449
1671
  }
1450
1672
  function TokenModeSwitcher({
@@ -1462,12 +1684,12 @@ function TokenModeSwitcher({
1462
1684
  const parent = first.parentId ? nodes.find((n) => n.id === first.parentId) : void 0;
1463
1685
  const inherited = parent ? getEffectiveTokenModes2(parent, nodes, state, options) : { ...options.modes };
1464
1686
  const collections = state.collections.filter((c) => showSingleMode || c.modes.length > 1);
1465
- return /* @__PURE__ */ jsx4("div", { "data-token-mode-switcher": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, children: collections.map((collection) => {
1687
+ return /* @__PURE__ */ jsx5("div", { "data-token-mode-switcher": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, children: collections.map((collection) => {
1466
1688
  const auto = getCurrentMode(collection, inherited);
1467
1689
  const explicit = collection.modes.some((m) => m.id === own[collection.id]) ? own[collection.id] : "";
1468
- return /* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2", "data-token-collection": collection.id, children: [
1469
- /* @__PURE__ */ jsx4("span", { className: "w-28 shrink-0 truncate text-muted-foreground", children: collection.name }),
1470
- /* @__PURE__ */ jsxs4(
1690
+ return /* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-2", "data-token-collection": collection.id, children: [
1691
+ /* @__PURE__ */ jsx5("span", { className: "w-28 shrink-0 truncate text-muted-foreground", children: collection.name }),
1692
+ /* @__PURE__ */ jsxs5(
1471
1693
  "select",
1472
1694
  {
1473
1695
  "aria-label": `${collection.name} mode`,
@@ -1475,12 +1697,12 @@ function TokenModeSwitcher({
1475
1697
  value: explicit,
1476
1698
  onChange: (e) => onChange(setCanvasTokenMode2(nodes, nodeIds, collection.id, e.currentTarget.value || null, state, options)),
1477
1699
  children: [
1478
- /* @__PURE__ */ jsxs4("option", { value: "", children: [
1700
+ /* @__PURE__ */ jsxs5("option", { value: "", children: [
1479
1701
  "Auto (",
1480
1702
  auto.name,
1481
1703
  ")"
1482
1704
  ] }),
1483
- collection.modes.map((m) => /* @__PURE__ */ jsx4("option", { value: m.id, children: m.name }, m.id))
1705
+ collection.modes.map((m) => /* @__PURE__ */ jsx5("option", { value: m.id, children: m.name }, m.id))
1484
1706
  ]
1485
1707
  }
1486
1708
  )
@@ -1511,16 +1733,16 @@ import {
1511
1733
  } from "@pixerate/editor/canvas";
1512
1734
 
1513
1735
  // src/canvas/useCanvasAnimation.ts
1514
- import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef10, useSyncExternalStore as useSyncExternalStore3 } from "react";
1736
+ import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef12, useSyncExternalStore as useSyncExternalStore3 } from "react";
1515
1737
  import {
1516
1738
  createCanvasAnimationController
1517
1739
  } from "@pixerate/editor/canvas";
1518
1740
  function useCanvasAnimation(options) {
1519
- const nodesRef = useRef10(options.nodes);
1741
+ const nodesRef = useRef12(options.nodes);
1520
1742
  nodesRef.current = options.nodes;
1521
- const optionsRef = useRef10(options);
1743
+ const optionsRef = useRef12(options);
1522
1744
  optionsRef.current = options;
1523
- const controller = useMemo5(
1745
+ const controller = useMemo6(
1524
1746
  () => createCanvasAnimationController({
1525
1747
  getNodes: () => nodesRef.current,
1526
1748
  setNodes: (next) => {
@@ -1547,14 +1769,14 @@ function useCanvasAnimation(options) {
1547
1769
  }),
1548
1770
  []
1549
1771
  );
1550
- useEffect6(() => () => controller.destroy(), [controller]);
1551
- useEffect6(() => {
1772
+ useEffect8(() => () => controller.destroy(), [controller]);
1773
+ useEffect8(() => {
1552
1774
  if (options.loop !== void 0) controller.setLoop(options.loop);
1553
1775
  }, [controller, options.loop]);
1554
- useEffect6(() => {
1776
+ useEffect8(() => {
1555
1777
  if (options.playbackRate !== void 0) controller.setPlaybackRate(options.playbackRate);
1556
1778
  }, [controller, options.playbackRate]);
1557
- useEffect6(() => {
1779
+ useEffect8(() => {
1558
1780
  if (options.recording !== void 0) controller.setRecording(options.recording);
1559
1781
  }, [controller, options.recording]);
1560
1782
  const state = useSyncExternalStore3(
@@ -1562,11 +1784,11 @@ function useCanvasAnimation(options) {
1562
1784
  () => controller.getState(),
1563
1785
  () => controller.getState()
1564
1786
  );
1565
- useEffect6(() => {
1787
+ useEffect8(() => {
1566
1788
  controller.refresh();
1567
1789
  }, [controller, options.nodes]);
1568
- const displayNodes = useMemo5(() => controller.display(options.nodes), [controller, options.nodes, state.time]);
1569
- return useMemo5(
1790
+ const displayNodes = useMemo6(() => controller.display(options.nodes), [controller, options.nodes, state.time]);
1791
+ return useMemo6(
1570
1792
  () => ({
1571
1793
  ...controller,
1572
1794
  state,
@@ -1587,7 +1809,7 @@ function useCanvasAnimation(options) {
1587
1809
  }
1588
1810
 
1589
1811
  // src/canvas/AnimationTimeline.tsx
1590
- import { useEffect as useEffect7, useRef as useRef11 } from "react";
1812
+ import { useEffect as useEffect9, useRef as useRef13 } from "react";
1591
1813
  import {
1592
1814
  ANIMATION_EASING_LABELS,
1593
1815
  ANIMATION_EASING_PRESETS,
@@ -1596,7 +1818,7 @@ import {
1596
1818
  timeToX,
1597
1819
  xToTime
1598
1820
  } from "@pixerate/editor/canvas";
1599
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1821
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1600
1822
  var LOOP_LABELS = { none: "Once", loop: "Loop", "ping-pong": "Ping-pong" };
1601
1823
  var easingValue = (easing) => typeof easing === "string" ? easing : easing ? "custom" : "linear";
1602
1824
  function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8, labelOf, className = "" }) {
@@ -1610,8 +1832,8 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1610
1832
  for (const row of rows) for (const k of row.keyframes) if (k.selected && k.ref.property === first.property && k.ref.nodeId === first.nodeId) return easingValue(k.easing);
1611
1833
  return "";
1612
1834
  })();
1613
- const dragRef = useRef11(null);
1614
- useEffect7(() => {
1835
+ const dragRef = useRef13(null);
1836
+ useEffect9(() => {
1615
1837
  const onKey = (event) => {
1616
1838
  if (event.key === "Escape" && dragRef.current) {
1617
1839
  dragRef.current = null;
@@ -1667,10 +1889,10 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1667
1889
  const btn = "rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-50";
1668
1890
  const playheadX = timeToX(state.time, duration, width);
1669
1891
  const labelWidth = 120;
1670
- return /* @__PURE__ */ jsxs5("div", { "data-animation-timeline": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, tabIndex: 0, onKeyDown: onTimelineKey, children: [
1671
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-2", role: "toolbar", "aria-label": "Animation", children: [
1672
- /* @__PURE__ */ jsx5("button", { type: "button", className: btn, "data-action": "toggle-play", "aria-label": state.playing ? "Pause" : "Play", onClick: () => animation.toggle(), children: state.playing ? "\u275A\u275A" : "\u25B6" }),
1673
- /* @__PURE__ */ jsx5(
1892
+ return /* @__PURE__ */ jsxs6("div", { "data-animation-timeline": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, tabIndex: 0, onKeyDown: onTimelineKey, children: [
1893
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-2", role: "toolbar", "aria-label": "Animation", children: [
1894
+ /* @__PURE__ */ jsx6("button", { type: "button", className: btn, "data-action": "toggle-play", "aria-label": state.playing ? "Pause" : "Play", onClick: () => animation.toggle(), children: state.playing ? "\u275A\u275A" : "\u25B6" }),
1895
+ /* @__PURE__ */ jsx6(
1674
1896
  "button",
1675
1897
  {
1676
1898
  type: "button",
@@ -1682,13 +1904,13 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1682
1904
  children: "\u25CF Rec"
1683
1905
  }
1684
1906
  ),
1685
- /* @__PURE__ */ jsx5("button", { type: "button", className: btn, "data-action": "add-keyframe", onClick: () => animation.addKeyframe(), children: "\u25C6 Key" }),
1686
- /* @__PURE__ */ jsxs5("span", { "data-time": "", className: "tabular-nums text-muted-foreground", children: [
1907
+ /* @__PURE__ */ jsx6("button", { type: "button", className: btn, "data-action": "add-keyframe", onClick: () => animation.addKeyframe(), children: "\u25C6 Key" }),
1908
+ /* @__PURE__ */ jsxs6("span", { "data-time": "", className: "tabular-nums text-muted-foreground", children: [
1687
1909
  formatAnimationTime(state.time),
1688
1910
  " / ",
1689
1911
  formatAnimationTime(duration)
1690
1912
  ] }),
1691
- /* @__PURE__ */ jsxs5(
1913
+ /* @__PURE__ */ jsxs6(
1692
1914
  "select",
1693
1915
  {
1694
1916
  "aria-label": "Easing",
@@ -1698,13 +1920,13 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1698
1920
  disabled: state.selection.length === 0,
1699
1921
  onChange: (e) => e.target.value && e.target.value !== "custom" && animation.setEasing(e.target.value),
1700
1922
  children: [
1701
- /* @__PURE__ */ jsx5("option", { value: "", children: "Easing\u2026" }),
1702
- ANIMATION_EASING_PRESETS.map((preset) => /* @__PURE__ */ jsx5("option", { value: preset, children: ANIMATION_EASING_LABELS[preset] }, preset)),
1703
- selectedEasing === "custom" ? /* @__PURE__ */ jsx5("option", { value: "custom", children: "Custom" }) : null
1923
+ /* @__PURE__ */ jsx6("option", { value: "", children: "Easing\u2026" }),
1924
+ ANIMATION_EASING_PRESETS.map((preset) => /* @__PURE__ */ jsx6("option", { value: preset, children: ANIMATION_EASING_LABELS[preset] }, preset)),
1925
+ selectedEasing === "custom" ? /* @__PURE__ */ jsx6("option", { value: "custom", children: "Custom" }) : null
1704
1926
  ]
1705
1927
  }
1706
1928
  ),
1707
- /* @__PURE__ */ jsx5(
1929
+ /* @__PURE__ */ jsx6(
1708
1930
  "select",
1709
1931
  {
1710
1932
  "aria-label": "Loop",
@@ -1712,13 +1934,13 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1712
1934
  className: "rounded border border-border bg-background px-1 py-0.5",
1713
1935
  value: state.loop,
1714
1936
  onChange: (e) => animation.setLoop(e.target.value),
1715
- children: Object.keys(LOOP_LABELS).map((mode) => /* @__PURE__ */ jsx5("option", { value: mode, children: LOOP_LABELS[mode] }, mode))
1937
+ children: Object.keys(LOOP_LABELS).map((mode) => /* @__PURE__ */ jsx6("option", { value: mode, children: LOOP_LABELS[mode] }, mode))
1716
1938
  }
1717
1939
  )
1718
1940
  ] }),
1719
- /* @__PURE__ */ jsxs5("div", { className: "flex", children: [
1720
- /* @__PURE__ */ jsx5("div", { style: { width: labelWidth }, className: "shrink-0" }),
1721
- /* @__PURE__ */ jsxs5(
1941
+ /* @__PURE__ */ jsxs6("div", { className: "flex", children: [
1942
+ /* @__PURE__ */ jsx6("div", { style: { width: labelWidth }, className: "shrink-0" }),
1943
+ /* @__PURE__ */ jsxs6(
1722
1944
  "div",
1723
1945
  {
1724
1946
  "data-timeline-ruler": "",
@@ -1726,15 +1948,15 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1726
1948
  style: { width },
1727
1949
  onPointerDown: onRulerDown,
1728
1950
  children: [
1729
- ticks.map((tick) => /* @__PURE__ */ jsx5("span", { className: "absolute top-0 -translate-x-1/2 text-[10px] text-muted-foreground", style: { left: tick.x }, children: tick.label }, tick.time)),
1730
- /* @__PURE__ */ jsx5("div", { "data-playhead": "", className: "absolute top-0 h-5 w-px bg-red-500", style: { left: playheadX } })
1951
+ ticks.map((tick) => /* @__PURE__ */ jsx6("span", { className: "absolute top-0 -translate-x-1/2 text-[10px] text-muted-foreground", style: { left: tick.x }, children: tick.label }, tick.time)),
1952
+ /* @__PURE__ */ jsx6("div", { "data-playhead": "", className: "absolute top-0 h-5 w-px bg-red-500", style: { left: playheadX } })
1731
1953
  ]
1732
1954
  }
1733
1955
  )
1734
1956
  ] }),
1735
- rows.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "text-muted-foreground", "data-timeline-empty": "", children: "Select an animated node, or press \u25C6 Key to add a keyframe." }) : rows.map((row) => /* @__PURE__ */ jsxs5("div", { className: "flex items-center", "data-track": row.id, children: [
1736
- /* @__PURE__ */ jsxs5("label", { className: "flex shrink-0 items-center gap-1 truncate pr-2", style: { width: labelWidth }, title: `${row.nodeLabel} \xB7 ${row.label}`, children: [
1737
- /* @__PURE__ */ jsx5(
1957
+ rows.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "text-muted-foreground", "data-timeline-empty": "", children: "Select an animated node, or press \u25C6 Key to add a keyframe." }) : rows.map((row) => /* @__PURE__ */ jsxs6("div", { className: "flex items-center", "data-track": row.id, children: [
1958
+ /* @__PURE__ */ jsxs6("label", { className: "flex shrink-0 items-center gap-1 truncate pr-2", style: { width: labelWidth }, title: `${row.nodeLabel} \xB7 ${row.label}`, children: [
1959
+ /* @__PURE__ */ jsx6(
1738
1960
  "input",
1739
1961
  {
1740
1962
  type: "checkbox",
@@ -1743,15 +1965,15 @@ function AnimationTimeline({ animation, nodeIds, width = 360, snapThreshold = 8,
1743
1965
  onChange: (e) => animation.setTrackEnabled(row.nodeId, row.property, e.target.checked)
1744
1966
  }
1745
1967
  ),
1746
- /* @__PURE__ */ jsxs5("span", { className: "truncate", children: [
1968
+ /* @__PURE__ */ jsxs6("span", { className: "truncate", children: [
1747
1969
  rows.filter((r) => r.nodeId === row.nodeId).length < rows.length ? `${row.nodeLabel} \xB7 ` : "",
1748
1970
  row.label
1749
1971
  ] })
1750
1972
  ] }),
1751
- /* @__PURE__ */ jsxs5("div", { "data-lane": "", className: `relative h-5 rounded bg-muted/40 ${row.enabled ? "" : "opacity-50"}`, style: { width }, children: [
1752
- /* @__PURE__ */ jsx5("div", { className: "pointer-events-none absolute top-0 h-5 w-px bg-red-500/60", style: { left: playheadX } }),
1753
- state.drag?.target != null ? /* @__PURE__ */ jsx5("div", { "data-snap-target": "", className: "pointer-events-none absolute top-0 h-5 w-px bg-sky-400", style: { left: timeToX(state.drag.target, duration, width) } }) : null,
1754
- row.keyframes.map((k) => /* @__PURE__ */ jsx5(
1973
+ /* @__PURE__ */ jsxs6("div", { "data-lane": "", className: `relative h-5 rounded bg-muted/40 ${row.enabled ? "" : "opacity-50"}`, style: { width }, children: [
1974
+ /* @__PURE__ */ jsx6("div", { className: "pointer-events-none absolute top-0 h-5 w-px bg-red-500/60", style: { left: playheadX } }),
1975
+ state.drag?.target != null ? /* @__PURE__ */ jsx6("div", { "data-snap-target": "", className: "pointer-events-none absolute top-0 h-5 w-px bg-sky-400", style: { left: timeToX(state.drag.target, duration, width) } }) : null,
1976
+ row.keyframes.map((k) => /* @__PURE__ */ jsx6(
1755
1977
  "button",
1756
1978
  {
1757
1979
  type: "button",
@@ -1849,16 +2071,16 @@ import {
1849
2071
  } from "@pixerate/editor/canvas";
1850
2072
 
1851
2073
  // src/canvas/useCanvasComponents.ts
1852
- import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo6, useRef as useRef12 } from "react";
2074
+ import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo7, useRef as useRef14 } from "react";
1853
2075
  import {
1854
2076
  createCanvasComponentsController
1855
2077
  } from "@pixerate/editor/canvas";
1856
2078
  function useCanvasComponents(options) {
1857
- const nodesRef = useRef12(options.nodes);
2079
+ const nodesRef = useRef14(options.nodes);
1858
2080
  nodesRef.current = options.nodes;
1859
- const optionsRef = useRef12(options);
2081
+ const optionsRef = useRef14(options);
1860
2082
  optionsRef.current = options;
1861
- const controller = useMemo6(
2083
+ const controller = useMemo7(
1862
2084
  () => createCanvasComponentsController({
1863
2085
  getNodes: () => nodesRef.current,
1864
2086
  setNodes: (next) => {
@@ -1894,15 +2116,15 @@ function useCanvasComponents(options) {
1894
2116
  }
1895
2117
 
1896
2118
  // src/canvas/CanvasComponentsPanel.tsx
1897
- import { useState as useState5 } from "react";
2119
+ import { useState as useState7 } from "react";
1898
2120
  import {
1899
2121
  CANVAS_COMPONENT_DRAG_TYPE,
1900
2122
  inspectCanvasInstance,
1901
2123
  listCanvasComponents
1902
2124
  } from "@pixerate/editor/canvas";
1903
- import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2125
+ import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1904
2126
  function InlineField({ value, label, onCommit }) {
1905
- return /* @__PURE__ */ jsx6(
2127
+ return /* @__PURE__ */ jsx7(
1906
2128
  "input",
1907
2129
  {
1908
2130
  type: "text",
@@ -1933,10 +2155,10 @@ function CanvasComponentsPanel({
1933
2155
  search = true,
1934
2156
  className = ""
1935
2157
  }) {
1936
- const [query, setQuery] = useState5("");
1937
- const [propsOpen, setPropsOpen] = useState5(null);
1938
- const [menuFor, setMenuFor] = useState5(null);
1939
- const [editing, setEditing] = useState5(null);
2158
+ const [query, setQuery] = useState7("");
2159
+ const [propsOpen, setPropsOpen] = useState7(null);
2160
+ const [menuFor, setMenuFor] = useState7(null);
2161
+ const [editing, setEditing] = useState7(null);
1940
2162
  const { components: all, sets } = listCanvasComponents(nodes);
1941
2163
  const q = query.trim().toLowerCase();
1942
2164
  const match = (name) => !q || name.toLowerCase().includes(q);
@@ -1948,7 +2170,7 @@ function CanvasComponentsPanel({
1948
2170
  };
1949
2171
  const small = "shrink-0 rounded px-1 text-[10px] text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40";
1950
2172
  const menuItem = "rounded px-2 py-0.5 text-left hover:bg-muted disabled:opacity-40";
1951
- const item = (id, name, count, extra = {}, onContextMenu) => /* @__PURE__ */ jsxs6(
2173
+ const item = (id, name, count, extra = {}, onContextMenu) => /* @__PURE__ */ jsxs7(
1952
2174
  "button",
1953
2175
  {
1954
2176
  type: "button",
@@ -1965,17 +2187,17 @@ function CanvasComponentsPanel({
1965
2187
  e.dataTransfer.effectAllowed = "copy";
1966
2188
  },
1967
2189
  children: [
1968
- /* @__PURE__ */ jsxs6("span", { className: "truncate", children: [
1969
- /* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "mr-1 text-muted-foreground", children: "\u25C7" }),
2190
+ /* @__PURE__ */ jsxs7("span", { className: "truncate", children: [
2191
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: "mr-1 text-muted-foreground", children: "\u25C7" }),
1970
2192
  name
1971
2193
  ] }),
1972
- /* @__PURE__ */ jsx6("span", { className: "text-[10px] text-muted-foreground", "data-instance-count": "", children: count })
2194
+ /* @__PURE__ */ jsx7("span", { className: "text-[10px] text-muted-foreground", "data-instance-count": "", children: count })
1973
2195
  ]
1974
2196
  },
1975
2197
  id
1976
2198
  );
1977
- return /* @__PURE__ */ jsxs6("div", { "data-components-panel": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, children: [
1978
- search ? /* @__PURE__ */ jsx6(
2199
+ return /* @__PURE__ */ jsxs7("div", { "data-components-panel": "", className: `flex flex-col gap-1 text-xs text-foreground ${className}`, children: [
2200
+ search ? /* @__PURE__ */ jsx7(
1979
2201
  "input",
1980
2202
  {
1981
2203
  type: "search",
@@ -1985,11 +2207,11 @@ function CanvasComponentsPanel({
1985
2207
  onInput: (e) => setQuery(e.target.value)
1986
2208
  }
1987
2209
  ) : null,
1988
- shownSets.map((set) => /* @__PURE__ */ jsxs6("div", { "data-component-set": set.id, className: "flex flex-col", children: [
1989
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
2210
+ shownSets.map((set) => /* @__PURE__ */ jsxs7("div", { "data-component-set": set.id, className: "flex flex-col", children: [
2211
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
1990
2212
  item(set.id, set.name, set.variants.reduce((sum, v) => sum + v.instanceCount, 0), { "data-kind": "set" }),
1991
- components ? /* @__PURE__ */ jsxs6(Fragment, { children: [
1992
- /* @__PURE__ */ jsx6(
2213
+ components ? /* @__PURE__ */ jsxs7(Fragment, { children: [
2214
+ /* @__PURE__ */ jsx7(
1993
2215
  "button",
1994
2216
  {
1995
2217
  type: "button",
@@ -2001,7 +2223,7 @@ function CanvasComponentsPanel({
2001
2223
  children: "+\xA0Variant"
2002
2224
  }
2003
2225
  ),
2004
- /* @__PURE__ */ jsx6(
2226
+ /* @__PURE__ */ jsx7(
2005
2227
  "button",
2006
2228
  {
2007
2229
  type: "button",
@@ -2016,8 +2238,8 @@ function CanvasComponentsPanel({
2016
2238
  )
2017
2239
  ] }) : null
2018
2240
  ] }),
2019
- components && propsOpen === set.id ? /* @__PURE__ */ jsx6("div", { "data-variant-properties": set.id, className: "ml-3 flex flex-col gap-1 border-l border-border py-1 pl-2", children: set.options.map((option) => /* @__PURE__ */ jsxs6("div", { "data-variant-option": option.name, className: "flex flex-col gap-0.5", children: [
2020
- /* @__PURE__ */ jsx6(
2241
+ components && propsOpen === set.id ? /* @__PURE__ */ jsx7("div", { "data-variant-properties": set.id, className: "ml-3 flex flex-col gap-1 border-l border-border py-1 pl-2", children: set.options.map((option) => /* @__PURE__ */ jsxs7("div", { "data-variant-option": option.name, className: "flex flex-col gap-0.5", children: [
2242
+ /* @__PURE__ */ jsx7(
2021
2243
  InlineField,
2022
2244
  {
2023
2245
  value: option.name,
@@ -2025,7 +2247,7 @@ function CanvasComponentsPanel({
2025
2247
  onCommit: (next) => report(components.renameVariantProperty(option.name, next, set.id))
2026
2248
  }
2027
2249
  ),
2028
- /* @__PURE__ */ jsx6("div", { className: "ml-2 flex flex-wrap gap-1", children: option.values.map((value) => /* @__PURE__ */ jsx6(
2250
+ /* @__PURE__ */ jsx7("div", { className: "ml-2 flex flex-wrap gap-1", children: option.values.map((value) => /* @__PURE__ */ jsx7(
2029
2251
  InlineField,
2030
2252
  {
2031
2253
  value,
@@ -2035,8 +2257,8 @@ function CanvasComponentsPanel({
2035
2257
  value
2036
2258
  )) })
2037
2259
  ] }, option.name)) }) : null,
2038
- /* @__PURE__ */ jsx6("div", { className: "ml-3 flex flex-col border-l border-border pl-1", children: set.variants.map((v) => /* @__PURE__ */ jsxs6("div", { "data-variant-row": v.id, className: "flex flex-col", children: [
2039
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
2260
+ /* @__PURE__ */ jsx7("div", { className: "ml-3 flex flex-col border-l border-border pl-1", children: set.variants.map((v) => /* @__PURE__ */ jsxs7("div", { "data-variant-row": v.id, className: "flex flex-col", children: [
2261
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
2040
2262
  item(
2041
2263
  v.id,
2042
2264
  v.name,
@@ -2047,7 +2269,7 @@ function CanvasComponentsPanel({
2047
2269
  setMenuFor(v.id);
2048
2270
  } : void 0
2049
2271
  ),
2050
- components ? /* @__PURE__ */ jsx6(
2272
+ components ? /* @__PURE__ */ jsx7(
2051
2273
  "button",
2052
2274
  {
2053
2275
  type: "button",
@@ -2061,8 +2283,8 @@ function CanvasComponentsPanel({
2061
2283
  }
2062
2284
  ) : null
2063
2285
  ] }),
2064
- components && menuFor === v.id ? /* @__PURE__ */ jsxs6("div", { role: "menu", "data-variant-menu": v.id, className: "ml-2 flex flex-col rounded border border-border bg-background p-1 shadow", children: [
2065
- /* @__PURE__ */ jsx6(
2286
+ components && menuFor === v.id ? /* @__PURE__ */ jsxs7("div", { role: "menu", "data-variant-menu": v.id, className: "ml-2 flex flex-col rounded border border-border bg-background p-1 shadow", children: [
2287
+ /* @__PURE__ */ jsx7(
2066
2288
  "button",
2067
2289
  {
2068
2290
  type: "button",
@@ -2076,7 +2298,7 @@ function CanvasComponentsPanel({
2076
2298
  children: "Edit values\u2026"
2077
2299
  }
2078
2300
  ),
2079
- /* @__PURE__ */ jsx6(
2301
+ /* @__PURE__ */ jsx7(
2080
2302
  "button",
2081
2303
  {
2082
2304
  type: "button",
@@ -2090,7 +2312,7 @@ function CanvasComponentsPanel({
2090
2312
  children: "Duplicate"
2091
2313
  }
2092
2314
  ),
2093
- /* @__PURE__ */ jsx6(
2315
+ /* @__PURE__ */ jsx7(
2094
2316
  "button",
2095
2317
  {
2096
2318
  type: "button",
@@ -2107,7 +2329,7 @@ function CanvasComponentsPanel({
2107
2329
  }
2108
2330
  )
2109
2331
  ] }) : null,
2110
- components && editing === v.id ? /* @__PURE__ */ jsxs6(
2332
+ components && editing === v.id ? /* @__PURE__ */ jsxs7(
2111
2333
  "form",
2112
2334
  {
2113
2335
  "data-variant-editor": v.id,
@@ -2121,9 +2343,9 @@ function CanvasComponentsPanel({
2121
2343
  if (report(components.setVariantProperties(values, v.id))) setEditing(null);
2122
2344
  },
2123
2345
  children: [
2124
- set.options.map((option) => /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-2", children: [
2125
- /* @__PURE__ */ jsx6("span", { className: "w-16 shrink-0 truncate text-muted-foreground", children: option.name }),
2126
- /* @__PURE__ */ jsx6(
2346
+ set.options.map((option) => /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2", children: [
2347
+ /* @__PURE__ */ jsx7("span", { className: "w-16 shrink-0 truncate text-muted-foreground", children: option.name }),
2348
+ /* @__PURE__ */ jsx7(
2127
2349
  "input",
2128
2350
  {
2129
2351
  type: "text",
@@ -2134,9 +2356,9 @@ function CanvasComponentsPanel({
2134
2356
  }
2135
2357
  )
2136
2358
  ] }, option.name)),
2137
- /* @__PURE__ */ jsxs6("div", { className: "flex gap-1", children: [
2138
- /* @__PURE__ */ jsx6("button", { type: "submit", className: "rounded border border-border px-2 py-0.5 hover:bg-muted", "data-action": "save-variant", children: "Save" }),
2139
- /* @__PURE__ */ jsx6("button", { type: "button", className: "rounded px-2 py-0.5 hover:bg-muted", "data-action": "cancel-variant", onClick: () => setEditing(null), children: "Cancel" })
2359
+ /* @__PURE__ */ jsxs7("div", { className: "flex gap-1", children: [
2360
+ /* @__PURE__ */ jsx7("button", { type: "submit", className: "rounded border border-border px-2 py-0.5 hover:bg-muted", "data-action": "save-variant", children: "Save" }),
2361
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "rounded px-2 py-0.5 hover:bg-muted", "data-action": "cancel-variant", onClick: () => setEditing(null), children: "Cancel" })
2140
2362
  ] })
2141
2363
  ]
2142
2364
  }
@@ -2144,7 +2366,7 @@ function CanvasComponentsPanel({
2144
2366
  ] }, v.id)) })
2145
2367
  ] }, set.id)),
2146
2368
  lone.map((c) => item(c.id, c.name, c.instanceCount, { "data-kind": "component" })),
2147
- all.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "text-muted-foreground", children: "No components yet. Select a frame and choose Create Component." }) : null
2369
+ all.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "text-muted-foreground", children: "No components yet. Select a frame and choose Create Component." }) : null
2148
2370
  ] });
2149
2371
  }
2150
2372
  function CanvasInstanceInspector({
@@ -2163,27 +2385,27 @@ function CanvasInstanceInspector({
2163
2385
  const allComponents = listCanvasComponents(nodes).components;
2164
2386
  const linked = info.status === "linked";
2165
2387
  const btn = "rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-40";
2166
- return /* @__PURE__ */ jsxs6("div", { "data-instance-inspector": "", "data-instance-id": info.id, className: `flex flex-col gap-2 text-xs text-foreground ${className}`, children: [
2167
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-2", children: [
2168
- /* @__PURE__ */ jsx6("span", { className: "truncate font-medium", "data-instance-main": "", children: linked ? info.componentName : "Main component missing" }),
2169
- info.nested ? /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground", children: "Nested" }) : null
2388
+ return /* @__PURE__ */ jsxs7("div", { "data-instance-inspector": "", "data-instance-id": info.id, className: `flex flex-col gap-2 text-xs text-foreground ${className}`, children: [
2389
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-2", children: [
2390
+ /* @__PURE__ */ jsx7("span", { className: "truncate font-medium", "data-instance-main": "", children: linked ? info.componentName : "Main component missing" }),
2391
+ info.nested ? /* @__PURE__ */ jsx7("span", { className: "text-muted-foreground", children: "Nested" }) : null
2170
2392
  ] }),
2171
- info.variantOptions.map((option) => /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-2", "data-variant-property": option.name, children: [
2172
- /* @__PURE__ */ jsx6("span", { className: "w-24 shrink-0 truncate text-muted-foreground", children: option.name }),
2173
- /* @__PURE__ */ jsx6(
2393
+ info.variantOptions.map((option) => /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2", "data-variant-property": option.name, children: [
2394
+ /* @__PURE__ */ jsx7("span", { className: "w-24 shrink-0 truncate text-muted-foreground", children: option.name }),
2395
+ /* @__PURE__ */ jsx7(
2174
2396
  "select",
2175
2397
  {
2176
2398
  "aria-label": `${option.name} variant`,
2177
2399
  className: "min-w-0 flex-1 rounded border border-border bg-background px-1 py-0.5 text-xs",
2178
2400
  value: info.variant[option.name] ?? "",
2179
2401
  onChange: (e) => setProp(option.name, e.currentTarget.value),
2180
- children: option.values.map((v) => /* @__PURE__ */ jsx6("option", { value: v, children: v }, v))
2402
+ children: option.values.map((v) => /* @__PURE__ */ jsx7("option", { value: v, children: v }, v))
2181
2403
  }
2182
2404
  )
2183
2405
  ] }, option.name)),
2184
- info.properties.map((p) => /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-2", "data-component-property": p.name, children: [
2185
- /* @__PURE__ */ jsx6("span", { className: "w-24 shrink-0 truncate text-muted-foreground", children: p.name }),
2186
- p.type === "boolean" ? /* @__PURE__ */ jsx6("input", { type: "checkbox", "aria-label": p.name, checked: p.value === true, onChange: (e) => setProp(p.name, e.currentTarget.checked) }) : p.type === "text" ? /* @__PURE__ */ jsx6(
2406
+ info.properties.map((p) => /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2", "data-component-property": p.name, children: [
2407
+ /* @__PURE__ */ jsx7("span", { className: "w-24 shrink-0 truncate text-muted-foreground", children: p.name }),
2408
+ p.type === "boolean" ? /* @__PURE__ */ jsx7("input", { type: "checkbox", "aria-label": p.name, checked: p.value === true, onChange: (e) => setProp(p.name, e.currentTarget.checked) }) : p.type === "text" ? /* @__PURE__ */ jsx7(
2187
2409
  "input",
2188
2410
  {
2189
2411
  type: "text",
@@ -2198,22 +2420,22 @@ function CanvasInstanceInspector({
2198
2420
  }
2199
2421
  },
2200
2422
  String(p.value)
2201
- ) : /* @__PURE__ */ jsx6(
2423
+ ) : /* @__PURE__ */ jsx7(
2202
2424
  "select",
2203
2425
  {
2204
2426
  "aria-label": p.name,
2205
2427
  className: "min-w-0 flex-1 rounded border border-border bg-background px-1 py-0.5 text-xs",
2206
2428
  value: String(p.value),
2207
2429
  onChange: (e) => setProp(p.name, e.currentTarget.value),
2208
- children: allComponents.filter((c) => !p.preferred?.length || p.preferred.includes(c.id) || c.id === p.value).map((c) => /* @__PURE__ */ jsx6("option", { value: c.id, children: c.name }, c.id))
2430
+ children: allComponents.filter((c) => !p.preferred?.length || p.preferred.includes(c.id) || c.id === p.value).map((c) => /* @__PURE__ */ jsx7("option", { value: c.id, children: c.name }, c.id))
2209
2431
  }
2210
2432
  )
2211
2433
  ] }, p.name)),
2212
- info.overrides.length ? /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", "data-instance-overrides": "", children: [
2213
- /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground", children: "Overrides" }),
2214
- info.overrides.map((o) => /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", "data-override-layer": o.id, children: [
2215
- /* @__PURE__ */ jsx6("span", { className: "mr-1 truncate", children: o.name }),
2216
- o.groups.map((g) => /* @__PURE__ */ jsxs6(
2434
+ info.overrides.length ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", "data-instance-overrides": "", children: [
2435
+ /* @__PURE__ */ jsx7("span", { className: "text-muted-foreground", children: "Overrides" }),
2436
+ info.overrides.map((o) => /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1", "data-override-layer": o.id, children: [
2437
+ /* @__PURE__ */ jsx7("span", { className: "mr-1 truncate", children: o.name }),
2438
+ o.groups.map((g) => /* @__PURE__ */ jsxs7(
2217
2439
  "button",
2218
2440
  {
2219
2441
  type: "button",
@@ -2230,11 +2452,11 @@ function CanvasInstanceInspector({
2230
2452
  ))
2231
2453
  ] }, o.id))
2232
2454
  ] }) : null,
2233
- /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap gap-1", children: [
2234
- linked ? /* @__PURE__ */ jsxs6(Fragment, { children: [
2235
- /* @__PURE__ */ jsx6("button", { type: "button", className: btn, "data-action": "reset", onClick: () => report(components.resetOverrides(info.id)), children: "Reset all" }),
2236
- /* @__PURE__ */ jsx6("button", { type: "button", className: btn, "data-action": "go-to-main", onClick: () => components.goToMainComponent(info.id), children: "Go to main" }),
2237
- /* @__PURE__ */ jsx6(
2455
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap gap-1", children: [
2456
+ linked ? /* @__PURE__ */ jsxs7(Fragment, { children: [
2457
+ /* @__PURE__ */ jsx7("button", { type: "button", className: btn, "data-action": "reset", onClick: () => report(components.resetOverrides(info.id)), children: "Reset all" }),
2458
+ /* @__PURE__ */ jsx7("button", { type: "button", className: btn, "data-action": "go-to-main", onClick: () => components.goToMainComponent(info.id), children: "Go to main" }),
2459
+ /* @__PURE__ */ jsx7(
2238
2460
  "button",
2239
2461
  {
2240
2462
  type: "button",
@@ -2245,8 +2467,8 @@ function CanvasInstanceInspector({
2245
2467
  children: "Push changes"
2246
2468
  }
2247
2469
  )
2248
- ] }) : /* @__PURE__ */ jsx6("button", { type: "button", className: btn, "data-action": "restore", onClick: () => report(components.restoreMainComponent(info.rootId)), children: "Restore main component" }),
2249
- /* @__PURE__ */ jsx6(
2470
+ ] }) : /* @__PURE__ */ jsx7("button", { type: "button", className: btn, "data-action": "restore", onClick: () => report(components.restoreMainComponent(info.rootId)), children: "Restore main component" }),
2471
+ /* @__PURE__ */ jsx7(
2250
2472
  "button",
2251
2473
  {
2252
2474
  type: "button",
@@ -2296,7 +2518,19 @@ import {
2296
2518
  isCanvasComponentSet,
2297
2519
  isCanvasInstance,
2298
2520
  wouldCreateComponentCycle,
2299
- CANVAS_COMPONENT_DRAG_TYPE as CANVAS_COMPONENT_DRAG_TYPE2
2521
+ CANVAS_COMPONENT_DRAG_TYPE as CANVAS_COMPONENT_DRAG_TYPE2,
2522
+ createCanvasDrawController as createCanvasDrawController2,
2523
+ createDefaultStrokeStyle,
2524
+ findStrokesAtPoint,
2525
+ streamlinePoints,
2526
+ strokePointsToPathData as strokePointsToPathData2,
2527
+ strokeToShapeNode,
2528
+ CANVAS_GRADIENT_PRESETS,
2529
+ isCanvasGradient,
2530
+ parseCssGradient,
2531
+ resolveCanvasGradient as resolveCanvasGradient2,
2532
+ angleToSvgLinearCoords,
2533
+ gradientToSvgDef as gradientToSvgDef2
2300
2534
  } from "@pixerate/editor/canvas";
2301
2535
  export {
2302
2536
  ANIMATABLE_PROPERTIES,
@@ -2310,12 +2544,14 @@ export {
2310
2544
  AnimationPlayer,
2311
2545
  AnimationTimeline,
2312
2546
  CANVAS_COMPONENT_DRAG_TYPE2 as CANVAS_COMPONENT_DRAG_TYPE,
2547
+ CANVAS_GRADIENT_PRESETS,
2313
2548
  CANVAS_TOKEN_PROPERTIES,
2314
2549
  CHILD_LAYOUT_DATA_KEY,
2315
2550
  CONNECTION_LINE_TYPES,
2316
2551
  CSS_ANIMATION_PROPERTIES,
2317
2552
  CUBIC_BEZIER_PRESETS,
2318
2553
  CanvasComponentsPanel,
2554
+ CanvasDrawLayer,
2319
2555
  CanvasGuidesLayer,
2320
2556
  CanvasInstanceInspector,
2321
2557
  CanvasScenesStore2 as CanvasScenesStore,
@@ -2335,6 +2571,7 @@ export {
2335
2571
  addCanvasVariant,
2336
2572
  addKeyframesAtTime,
2337
2573
  alignNodes,
2574
+ angleToSvgLinearCoords,
2338
2575
  animateNode,
2339
2576
  applyAnimation,
2340
2577
  applyAnimationOverrides,
@@ -2375,8 +2612,10 @@ export {
2375
2612
  createCanvasAutoLayoutController2 as createCanvasAutoLayoutController,
2376
2613
  createCanvasComponent,
2377
2614
  createCanvasComponentsController2 as createCanvasComponentsController,
2615
+ createCanvasDrawController2 as createCanvasDrawController,
2378
2616
  createCanvasInstance,
2379
2617
  createCanvasScenesStore,
2618
+ createDefaultStrokeStyle,
2380
2619
  createFanOutTrajectory2 as createFanOutTrajectory,
2381
2620
  createGuideIndex,
2382
2621
  createNodeGuideIndex,
@@ -2404,6 +2643,7 @@ export {
2404
2643
  excludePaths,
2405
2644
  exportCssAnimation,
2406
2645
  findConnectorSnap,
2646
+ findStrokesAtPoint,
2407
2647
  fitCubicRun,
2408
2648
  fitCubics,
2409
2649
  fitFreehandPath,
@@ -2453,6 +2693,7 @@ export {
2453
2693
  getTimelineTicks2 as getTimelineTicks,
2454
2694
  getTokenPropertyScope,
2455
2695
  getTokensForProperty2 as getTokensForProperty,
2696
+ gradientToSvgDef2 as gradientToSvgDef,
2456
2697
  handlePoint,
2457
2698
  hasVisibleStroke,
2458
2699
  inferResizeHandle,
@@ -2467,6 +2708,7 @@ export {
2467
2708
  isAutoLayoutFrame,
2468
2709
  isCanvasComponent,
2469
2710
  isCanvasComponentSet,
2711
+ isCanvasGradient,
2470
2712
  isCanvasInstance,
2471
2713
  isCssExportExact,
2472
2714
  isEventFromTextInput3 as isEventFromTextInput,
@@ -2514,6 +2756,7 @@ export {
2514
2756
  parseCanvasClipboardData2 as parseCanvasClipboardData,
2515
2757
  parseCanvasScenes,
2516
2758
  parseCssEasing,
2759
+ parseCssGradient,
2517
2760
  parseKeyframeClipboard,
2518
2761
  parseOklab,
2519
2762
  parseSvgPath,
@@ -2545,6 +2788,7 @@ export {
2545
2788
  repositionNodesTo,
2546
2789
  resetCanvasInstanceOverrides,
2547
2790
  resizeHandleAxes,
2791
+ resolveCanvasGradient2 as resolveCanvasGradient,
2548
2792
  resolveCanvasTokens2 as resolveCanvasTokens,
2549
2793
  resolveEasing,
2550
2794
  resolvePadding,
@@ -2577,7 +2821,10 @@ export {
2577
2821
  snapTimeToFrame,
2578
2822
  snapToGrid,
2579
2823
  springAt,
2824
+ streamlinePoints,
2580
2825
  strokeOutlineRings,
2826
+ strokePointsToPathData2 as strokePointsToPathData,
2827
+ strokeToShapeNode,
2581
2828
  subtractPaths,
2582
2829
  subtractPolygons,
2583
2830
  swapCanvasInstance,
@@ -2600,6 +2847,7 @@ export {
2600
2847
  useCanvasClipboard,
2601
2848
  useCanvasComponents,
2602
2849
  useCanvasDocking,
2850
+ useCanvasDraw,
2603
2851
  useCanvasExplosion,
2604
2852
  useCanvasGraph,
2605
2853
  useCanvasLayout,