@pixerate/editor-react 0.75.1 → 0.76.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.
@@ -633,6 +633,7 @@ function VectorCanvas({
633
633
  sh.lines.map((d, i) => /* @__PURE__ */ jsx2("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
634
634
  sh.points.map((p) => /* @__PURE__ */ jsx2("circle", { "data-vector-shading-point": p.index, "data-selected": p.selected || void 0, cx: p.x, cy: p.y, r: p.selected ? 6 : 4.5, fill: p.color, stroke: p.selected ? accentColor : "#ffffff", strokeWidth: p.selected ? 2 : 1.5 }, p.index))
635
635
  ] }, `shading:${sh.nodeId}`)),
636
+ overlay.symmetry.map((sy) => /* @__PURE__ */ jsx2("g", { "data-vector-symmetry-axes": sy.nodeId, stroke: accentColor, strokeDasharray: sy.drawing ? void 0 : "6 4", opacity: sy.drawing ? 0.9 : 0.6, children: sy.lines.map((l, i) => /* @__PURE__ */ jsx2("line", { x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2 }, i)) }, `sym:${sy.nodeId}`)),
636
637
  overlay.trackingPoints.map((t, i) => /* @__PURE__ */ jsx2("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
637
638
  overlay.typedEntry && /* @__PURE__ */ jsxs2("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
638
639
  /* @__PURE__ */ jsx2("rect", { x: -3, y: -12, width: overlay.typedEntry.text.length * 7 + 8, height: 17, rx: 3, fill: THEME2.handleFill, stroke: accentColor }),
@@ -791,7 +792,7 @@ import {
791
792
  isVectorMaskGroup,
792
793
  widthProfilePreset,
793
794
  widthProfilePresetId,
794
- vectorPaintToHex as vectorPaintToHex5,
795
+ vectorPaintToHex as vectorPaintToHex6,
795
796
  vectorToolLabel,
796
797
  vectorPolarAngles,
797
798
  vectorPolarIncrementOf
@@ -800,7 +801,8 @@ import {
800
801
  // src/vector/VectorEffectsPanel.tsx
801
802
  import {
802
803
  VECTOR_EFFECT_MENU,
803
- getVectorEffectsPanel
804
+ getVectorEffectsPanel,
805
+ vectorPaintToHex
804
806
  } from "@pixerate/editor/vector";
805
807
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
806
808
  var buttonClass = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
@@ -888,6 +890,15 @@ function VectorEffectsPanel({ controller, className }) {
888
890
  /* @__PURE__ */ jsx3("span", { className: "min-w-0 flex-1 truncate", children: spec.label }),
889
891
  /* @__PURE__ */ jsx3("input", { type: "checkbox", "aria-label": `${e.label} ${spec.label}`, checked: !!e.params[spec.key], onChange: (ev) => set(e, spec.key, ev.target.checked, false) })
890
892
  ] }, spec.key);
893
+ if (spec.kind === "color") {
894
+ const c = e.params[spec.key];
895
+ return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-effect-param": spec.key, children: [
896
+ /* @__PURE__ */ jsx3("span", { className: labelClass, children: spec.label }),
897
+ /* @__PURE__ */ jsx3("input", { type: "checkbox", "aria-label": `${e.label} ${spec.label} on`, checked: c !== null, onChange: (ev) => set(e, spec.key, ev.target.checked ? "#ffffff" : null, false) }),
898
+ /* @__PURE__ */ jsx3("input", { type: "color", "aria-label": `${e.label} ${spec.label}`, className: "h-6 w-8 cursor-pointer rounded border border-border bg-background disabled:opacity-40", disabled: c === null, value: vectorPaintToHex(c ?? "#ffffff") ?? "#ffffff", onChange: (ev) => set(e, spec.key, ev.target.value, true), onBlur: end })
899
+ ] }, spec.key);
900
+ }
901
+ if (spec.kind !== "select") return null;
891
902
  return /* @__PURE__ */ jsxs3("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-effect-param": spec.key, children: [
892
903
  /* @__PURE__ */ jsx3("span", { className: labelClass, children: spec.label }),
893
904
  /* @__PURE__ */ jsx3("select", { "aria-label": `${e.label} ${spec.label}`, className: selectClass, value: String(e.params[spec.key] ?? ""), onChange: (ev) => set(e, spec.key, ev.target.value, false), children: spec.options.map((o) => /* @__PURE__ */ jsx3("option", { value: o.value, children: o.label }, o.value)) })
@@ -1569,7 +1580,7 @@ function VectorTextPanel({ controller, className }) {
1569
1580
 
1570
1581
  // src/vector/VectorSymbolsPanel.tsx
1571
1582
  import { useId as useId3, useState as useState3 } from "react";
1572
- import { getVectorSymbolsPanel, vectorPaintToHex } from "@pixerate/editor/vector";
1583
+ import { getVectorSymbolsPanel, vectorPaintToHex as vectorPaintToHex2 } from "@pixerate/editor/vector";
1573
1584
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1574
1585
  var buttonClass6 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1575
1586
  var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
@@ -1623,7 +1634,7 @@ function VectorSymbolsPanel({ controller, className }) {
1623
1634
  type: "color",
1624
1635
  "aria-label": "Instance fill",
1625
1636
  className: "h-6 w-8 rounded border border-border",
1626
- value: vectorPaintToHex(inst.fill ?? void 0) ?? "#000000",
1637
+ value: vectorPaintToHex2(inst.fill ?? void 0) ?? "#000000",
1627
1638
  onChange: (e) => report(controller.setInstanceOptions({ fill: e.target.value }, { ids: [inst.id] }))
1628
1639
  }
1629
1640
  )
@@ -1729,7 +1740,7 @@ import {
1729
1740
  VECTOR_VERTICAL_RESIZING,
1730
1741
  VECTOR_VERTICAL_RESIZING_LABELS,
1731
1742
  getVectorFramePanel,
1732
- vectorPaintToHex as vectorPaintToHex2
1743
+ vectorPaintToHex as vectorPaintToHex3
1733
1744
  } from "@pixerate/editor/vector";
1734
1745
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1735
1746
  var buttonClass8 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
@@ -1740,7 +1751,7 @@ function VectorFramePanel({ controller, className }) {
1740
1751
  const panel = getVectorFramePanel(state);
1741
1752
  const f = panel.frame;
1742
1753
  const ids = f ? [f.id] : void 0;
1743
- const fillHex = f ? vectorPaintToHex2(f.frame.fill ?? null) : null;
1754
+ const fillHex = f ? vectorPaintToHex3(f.frame.fill ?? null) : null;
1744
1755
  const number = (key, label) => /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1745
1756
  /* @__PURE__ */ jsx10("span", { className: "w-4 text-muted-foreground", children: label }),
1746
1757
  /* @__PURE__ */ jsx10(
@@ -1915,7 +1926,7 @@ function VectorConnectorPanel({ controller, className }) {
1915
1926
 
1916
1927
  // src/vector/VectorShadingPanel.tsx
1917
1928
  import { useState as useState5 } from "react";
1918
- import { isVectorFreeformGradient, isVectorMeshGradient, vectorPaintToHex as vectorPaintToHex3 } from "@pixerate/editor/vector";
1929
+ import { isVectorFreeformGradient, isVectorMeshGradient, vectorPaintToHex as vectorPaintToHex4 } from "@pixerate/editor/vector";
1919
1930
  import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1920
1931
  var buttonClass10 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
1921
1932
  var numberClass6 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
@@ -1955,7 +1966,7 @@ function VectorShadingPanel({ controller, className }) {
1955
1966
  "Point ",
1956
1967
  sel.index
1957
1968
  ] }),
1958
- /* @__PURE__ */ jsx12("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: vectorPaintToHex3(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
1969
+ /* @__PURE__ */ jsx12("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: vectorPaintToHex4(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
1959
1970
  /* @__PURE__ */ jsx12("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point opacity", title: "Opacity", className: "h-4 w-20 accent-primary", value: opacity, onChange: (e) => edit({ opacity: parseFloat(e.target.value) }, "shading-opacity") }),
1960
1971
  free && /* @__PURE__ */ jsxs12(Fragment3, { children: [
1961
1972
  /* @__PURE__ */ jsx12("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point spread", title: "Spread: pure colour round the point", className: "h-4 w-20 accent-primary", value: spread, onChange: (e) => edit({ spread: parseFloat(e.target.value) }, "shading-spread") }),
@@ -2027,6 +2038,43 @@ function VectorTracePanel({ controller, className }) {
2027
2038
  ] });
2028
2039
  }
2029
2040
 
2041
+ // src/vector/VectorSymmetryPanel.tsx
2042
+ import { VECTOR_SYMMETRY_MODES, VECTOR_SYMMETRY_MODE_LABELS, isVectorSymmetryGroup } from "@pixerate/editor/vector";
2043
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2044
+ var buttonClass12 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
2045
+ var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2046
+ var numberClass8 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
2047
+ function VectorSymmetryPanel({ controller, className }) {
2048
+ const state = useVectorEditorState(controller);
2049
+ const group = state.selection.map((id) => controller.getNode(id)).find(isVectorSymmetryGroup) ?? null;
2050
+ const s = group?.symmetry;
2051
+ const ids = group ? [group.id] : void 0;
2052
+ const drawing = !!group && state.symmetryTarget === group.id;
2053
+ return /* @__PURE__ */ jsxs14("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-symmetry-panel": "", "aria-label": "Symmetry", children: [
2054
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1", children: [
2055
+ /* @__PURE__ */ jsx14("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Symmetry" }),
2056
+ !group && /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "data-vector-symmetry-action": "make", title: "Make Symmetry: the selection (or nothing yet) mirrored live across a vertical axis", onClick: () => controller.makeSymmetry({ mode: "vertical" }), children: "Mirror" }),
2057
+ !group && /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "data-vector-symmetry-action": "radial", title: "Make Symmetry: the selection repeated round its centre", onClick: () => controller.makeSymmetry({ mode: "radial", copies: 6 }), children: "Radial" }),
2058
+ group && s && /* @__PURE__ */ jsxs14(Fragment4, { children: [
2059
+ /* @__PURE__ */ jsx14("select", { "aria-label": "Symmetry mode", className: selectClass9, value: s.mode, onChange: (e) => controller.setSymmetry({ mode: e.target.value }, { ids }), children: VECTOR_SYMMETRY_MODES.map((m) => /* @__PURE__ */ jsx14("option", { value: m, children: VECTOR_SYMMETRY_MODE_LABELS[m] }, m)) }),
2060
+ s.mode === "radial" && /* @__PURE__ */ jsxs14(Fragment4, { children: [
2061
+ /* @__PURE__ */ jsx14("input", { type: "number", min: 2, max: 64, "aria-label": "Symmetry copies", title: "Copies round the circle", className: numberClass8, value: s.copies ?? 6, onChange: (e) => controller.setSymmetry({ copies: Math.max(2, Math.min(64, parseInt(e.target.value, 10) || 2)) }, { ids }) }),
2062
+ /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Mirror each copy too (kaleidoscope)", children: [
2063
+ /* @__PURE__ */ jsx14("input", { type: "checkbox", "aria-label": "Mirror copies", checked: !!s.mirror, onChange: (e) => controller.setSymmetry({ mirror: e.target.checked }, { ids }) }),
2064
+ "Mirror"
2065
+ ] })
2066
+ ] })
2067
+ ] })
2068
+ ] }),
2069
+ group && /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1", children: [
2070
+ /* @__PURE__ */ jsx14("span", { className: "w-16" }),
2071
+ /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-pressed": drawing, "data-vector-symmetry-action": "draw", title: "Draw inside: new pen, pencil, shape and text art goes into the group, mirrored as you draw (Escape stops)", onClick: () => drawing ? controller.exitSymmetry() : controller.enterSymmetry(group.id), children: "Draw inside" }),
2072
+ /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, title: "Expand: keep the copies as real objects", onClick: () => controller.releaseSymmetry({ ids, expand: true }), children: "Expand" }),
2073
+ /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, title: "Release: stop repeating (the copies go)", onClick: () => controller.releaseSymmetry({ ids }), children: "Release" })
2074
+ ] })
2075
+ ] });
2076
+ }
2077
+
2030
2078
  // src/vector/VectorGradientEditor.tsx
2031
2079
  import {
2032
2080
  VECTOR_GRADIENT_TYPES,
@@ -2038,12 +2086,12 @@ import {
2038
2086
  VECTOR_GRADIENT_INTERPOLATION_LABELS,
2039
2087
  VECTOR_STROKE_GRADIENT_MODE_LABELS,
2040
2088
  getVectorGradientEditor,
2041
- vectorPaintToHex as vectorPaintToHex4
2089
+ vectorPaintToHex as vectorPaintToHex5
2042
2090
  } from "@pixerate/editor/vector";
2043
- import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2044
- var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2045
- var numberClass8 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2046
- var buttonClass12 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
2091
+ import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2092
+ var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2093
+ var numberClass9 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2094
+ var buttonClass13 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
2047
2095
  var percent = (v) => Math.round(v * 1e3) / 10;
2048
2096
  function VectorGradientEditor({ controller, className }) {
2049
2097
  const state = useVectorEditorState(controller);
@@ -2055,26 +2103,26 @@ function VectorGradientEditor({ controller, className }) {
2055
2103
  const setType = (value) => {
2056
2104
  if (value === "solid") {
2057
2105
  const first = ed.stops[0];
2058
- controller.setStyle({ [ed.target]: vectorPaintToHex4(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2106
+ controller.setStyle({ [ed.target]: vectorPaintToHex5(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2059
2107
  } else controller.setGradient({ type: value });
2060
2108
  };
2061
- return /* @__PURE__ */ jsxs14("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2062
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5", children: [
2063
- /* @__PURE__ */ jsx14("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2064
- ["fill", "stroke"].map((t) => /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2065
- ed.mixed && /* @__PURE__ */ jsx14("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2109
+ return /* @__PURE__ */ jsxs15("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2110
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1.5", children: [
2111
+ /* @__PURE__ */ jsx15("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2112
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2113
+ ed.mixed && /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2066
2114
  ] }),
2067
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5", children: [
2068
- /* @__PURE__ */ jsx14("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2069
- /* @__PURE__ */ jsxs14("select", { "aria-label": "Gradient type", className: selectClass9, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2070
- /* @__PURE__ */ jsx14("option", { value: "solid", children: "Solid" }),
2071
- VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx14("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2115
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1.5", children: [
2116
+ /* @__PURE__ */ jsx15("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2117
+ /* @__PURE__ */ jsxs15("select", { "aria-label": "Gradient type", className: selectClass10, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2118
+ /* @__PURE__ */ jsx15("option", { value: "solid", children: "Solid" }),
2119
+ VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx15("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2072
2120
  ] }),
2073
- /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2121
+ /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2074
2122
  ] }),
2075
- g && /* @__PURE__ */ jsxs14(Fragment4, { children: [
2076
- /* @__PURE__ */ jsxs14("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2077
- /* @__PURE__ */ jsx14(
2123
+ g && /* @__PURE__ */ jsxs15(Fragment5, { children: [
2124
+ /* @__PURE__ */ jsxs15("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2125
+ /* @__PURE__ */ jsx15(
2078
2126
  "div",
2079
2127
  {
2080
2128
  role: "button",
@@ -2088,7 +2136,7 @@ function VectorGradientEditor({ controller, className }) {
2088
2136
  }
2089
2137
  }
2090
2138
  ),
2091
- ed.stops.map((s) => /* @__PURE__ */ jsx14(
2139
+ ed.stops.map((s) => /* @__PURE__ */ jsx15(
2092
2140
  "button",
2093
2141
  {
2094
2142
  type: "button",
@@ -2102,21 +2150,21 @@ function VectorGradientEditor({ controller, className }) {
2102
2150
  s.index
2103
2151
  ))
2104
2152
  ] }),
2105
- sel && /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2106
- /* @__PURE__ */ jsx14(
2153
+ sel && /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2154
+ /* @__PURE__ */ jsx15(
2107
2155
  "input",
2108
2156
  {
2109
2157
  type: "color",
2110
2158
  "aria-label": "Stop colour",
2111
2159
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
2112
- value: vectorPaintToHex4(sel.color) ?? "#000000",
2160
+ value: vectorPaintToHex5(sel.color) ?? "#000000",
2113
2161
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
2114
2162
  onBlur: end
2115
2163
  }
2116
2164
  ),
2117
- /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2165
+ /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2118
2166
  "Opacity",
2119
- /* @__PURE__ */ jsx14(
2167
+ /* @__PURE__ */ jsx15(
2120
2168
  "input",
2121
2169
  {
2122
2170
  type: "number",
@@ -2124,7 +2172,7 @@ function VectorGradientEditor({ controller, className }) {
2124
2172
  max: 100,
2125
2173
  step: 5,
2126
2174
  "aria-label": "Stop opacity",
2127
- className: numberClass8,
2175
+ className: numberClass9,
2128
2176
  value: percent(sel.opacity ?? 1),
2129
2177
  onChange: (e) => {
2130
2178
  const n = parseFloat(e.target.value);
@@ -2133,9 +2181,9 @@ function VectorGradientEditor({ controller, className }) {
2133
2181
  }
2134
2182
  )
2135
2183
  ] }),
2136
- /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2184
+ /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2137
2185
  "At",
2138
- /* @__PURE__ */ jsx14(
2186
+ /* @__PURE__ */ jsx15(
2139
2187
  "input",
2140
2188
  {
2141
2189
  type: "number",
@@ -2143,7 +2191,7 @@ function VectorGradientEditor({ controller, className }) {
2143
2191
  max: 100,
2144
2192
  step: 1,
2145
2193
  "aria-label": "Stop position",
2146
- className: numberClass8,
2194
+ className: numberClass9,
2147
2195
  value: percent(sel.offset),
2148
2196
  onChange: (e) => {
2149
2197
  const n = parseFloat(e.target.value);
@@ -2152,9 +2200,9 @@ function VectorGradientEditor({ controller, className }) {
2152
2200
  }
2153
2201
  )
2154
2202
  ] }),
2155
- !sel.last && /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2203
+ !sel.last && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2156
2204
  "Midpoint",
2157
- /* @__PURE__ */ jsx14(
2205
+ /* @__PURE__ */ jsx15(
2158
2206
  "input",
2159
2207
  {
2160
2208
  type: "number",
@@ -2162,7 +2210,7 @@ function VectorGradientEditor({ controller, className }) {
2162
2210
  max: 95,
2163
2211
  step: 1,
2164
2212
  "aria-label": "Stop midpoint",
2165
- className: numberClass8,
2213
+ className: numberClass9,
2166
2214
  value: percent(sel.midpoint ?? 0.5),
2167
2215
  onChange: (e) => {
2168
2216
  const n = parseFloat(e.target.value);
@@ -2171,15 +2219,15 @@ function VectorGradientEditor({ controller, className }) {
2171
2219
  }
2172
2220
  )
2173
2221
  ] }),
2174
- /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2222
+ /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2175
2223
  ] }),
2176
- /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2177
- /* @__PURE__ */ jsx14("span", { className: "w-16", children: "Colours" }),
2178
- /* @__PURE__ */ jsx14("select", { "aria-label": "Gradient interpolation", className: selectClass9, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx14("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2224
+ /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2225
+ /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Colours" }),
2226
+ /* @__PURE__ */ jsx15("select", { "aria-label": "Gradient interpolation", className: selectClass10, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx15("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2179
2227
  ] }),
2180
- /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2181
- /* @__PURE__ */ jsx14("span", { className: "w-16", children: "Smooth" }),
2182
- /* @__PURE__ */ jsx14(
2228
+ /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2229
+ /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Smooth" }),
2230
+ /* @__PURE__ */ jsx15(
2183
2231
  "input",
2184
2232
  {
2185
2233
  type: "range",
@@ -2195,23 +2243,23 @@ function VectorGradientEditor({ controller, className }) {
2195
2243
  }
2196
2244
  )
2197
2245
  ] }),
2198
- /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2199
- /* @__PURE__ */ jsx14("span", { className: "w-16", children: "Spread" }),
2200
- /* @__PURE__ */ jsx14("select", { "aria-label": "Gradient spread", className: selectClass9, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx14("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2246
+ /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2247
+ /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Spread" }),
2248
+ /* @__PURE__ */ jsx15("select", { "aria-label": "Gradient spread", className: selectClass10, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx15("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2201
2249
  ] }),
2202
- ed.target === "stroke" && /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2203
- /* @__PURE__ */ jsx14("span", { className: "w-16", children: "On stroke" }),
2204
- /* @__PURE__ */ jsx14("select", { "aria-label": "Gradient on stroke", className: selectClass9, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx14("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2250
+ ed.target === "stroke" && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2251
+ /* @__PURE__ */ jsx15("span", { className: "w-16", children: "On stroke" }),
2252
+ /* @__PURE__ */ jsx15("select", { "aria-label": "Gradient on stroke", className: selectClass10, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx15("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2205
2253
  ] })
2206
2254
  ] })
2207
2255
  ] });
2208
2256
  }
2209
2257
 
2210
2258
  // src/vector/VectorToolbar.tsx
2211
- import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2259
+ import { Fragment as Fragment6, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2212
2260
  var VECTOR_SNAP_TITLE = "Snap to objects";
2213
2261
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
2214
- var buttonClass13 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
2262
+ var buttonClass14 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
2215
2263
  function VectorToolbar({
2216
2264
  controller,
2217
2265
  tools = VECTOR_TOOLS,
@@ -2231,14 +2279,14 @@ function VectorToolbar({
2231
2279
  const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
2232
2280
  const [live, setLive] = React9.useState(false);
2233
2281
  const booleanDisabled = live ? count < 1 : count < 2;
2234
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2235
- return /* @__PURE__ */ jsxs15("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2236
- /* @__PURE__ */ jsx15("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx15(
2282
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx16("button", { type: "button", className: buttonClass14, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2283
+ return /* @__PURE__ */ jsxs16("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2284
+ /* @__PURE__ */ jsx16("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx16(
2237
2285
  "button",
2238
2286
  {
2239
2287
  type: "button",
2240
2288
  "data-vector-tool": tool,
2241
- className: buttonClass13,
2289
+ className: buttonClass14,
2242
2290
  "aria-pressed": state.tool === tool,
2243
2291
  title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
2244
2292
  "aria-label": vectorToolLabel(tool),
@@ -2247,17 +2295,17 @@ function VectorToolbar({
2247
2295
  },
2248
2296
  tool
2249
2297
  )) }),
2250
- showHistory && /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2298
+ showHistory && /* @__PURE__ */ jsxs16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2251
2299
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
2252
2300
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
2253
2301
  ] }),
2254
- showSnap && /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2255
- /* @__PURE__ */ jsx15(
2302
+ showSnap && /* @__PURE__ */ jsxs16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2303
+ /* @__PURE__ */ jsx16(
2256
2304
  "button",
2257
2305
  {
2258
2306
  type: "button",
2259
2307
  "data-vector-snap": "",
2260
- className: buttonClass13,
2308
+ className: buttonClass14,
2261
2309
  "aria-pressed": state.snap.objects,
2262
2310
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
2263
2311
  "aria-label": VECTOR_SNAP_TITLE,
@@ -2265,7 +2313,7 @@ function VectorToolbar({
2265
2313
  children: "Snap"
2266
2314
  }
2267
2315
  ),
2268
- /* @__PURE__ */ jsxs15(
2316
+ /* @__PURE__ */ jsxs16(
2269
2317
  "select",
2270
2318
  {
2271
2319
  "data-vector-polar": "",
@@ -2275,8 +2323,8 @@ function VectorToolbar({
2275
2323
  value: String(vectorPolarIncrementOf(state.snap.angles) ?? ""),
2276
2324
  onChange: (e) => controller.setSnap({ angles: vectorPolarAngles(Number(e.target.value)) }),
2277
2325
  children: [
2278
- vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx15("option", { value: "", children: "Custom" }),
2279
- [90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs15("option", { value: n, children: [
2326
+ vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx16("option", { value: "", children: "Custom" }),
2327
+ [90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs16("option", { value: n, children: [
2280
2328
  n,
2281
2329
  "\xB0"
2282
2330
  ] }, n))
@@ -2284,13 +2332,13 @@ function VectorToolbar({
2284
2332
  }
2285
2333
  )
2286
2334
  ] }),
2287
- showPathEdit && /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2288
- VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx15(
2335
+ showPathEdit && /* @__PURE__ */ jsxs16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2336
+ VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx16(
2289
2337
  "button",
2290
2338
  {
2291
2339
  type: "button",
2292
2340
  "data-vector-path-action": a.id,
2293
- className: buttonClass13,
2341
+ className: buttonClass14,
2294
2342
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
2295
2343
  "aria-label": a.title,
2296
2344
  disabled: !a.enabled(count, points),
@@ -2299,12 +2347,12 @@ function VectorToolbar({
2299
2347
  },
2300
2348
  a.id
2301
2349
  )),
2302
- VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx15(
2350
+ VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx16(
2303
2351
  "button",
2304
2352
  {
2305
2353
  type: "button",
2306
2354
  "data-vector-edit": t.key,
2307
- className: buttonClass13,
2355
+ className: buttonClass14,
2308
2356
  "aria-pressed": editing[t.key],
2309
2357
  title: t.title,
2310
2358
  "aria-label": t.title,
@@ -2313,9 +2361,9 @@ function VectorToolbar({
2313
2361
  },
2314
2362
  t.key
2315
2363
  )),
2316
- state.tool === "pencil" && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2364
+ state.tool === "pencil" && /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2317
2365
  "Smoothing",
2318
- /* @__PURE__ */ jsx15(
2366
+ /* @__PURE__ */ jsx16(
2319
2367
  "input",
2320
2368
  {
2321
2369
  type: "range",
@@ -2329,9 +2377,9 @@ function VectorToolbar({
2329
2377
  }
2330
2378
  )
2331
2379
  ] }),
2332
- state.tool === "eraser" && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2380
+ state.tool === "eraser" && /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2333
2381
  "Size",
2334
- /* @__PURE__ */ jsx15(
2382
+ /* @__PURE__ */ jsx16(
2335
2383
  "input",
2336
2384
  {
2337
2385
  type: "number",
@@ -2347,12 +2395,12 @@ function VectorToolbar({
2347
2395
  }
2348
2396
  )
2349
2397
  ] }),
2350
- state.tool === "paintBucket" && /* @__PURE__ */ jsx15(
2398
+ state.tool === "paintBucket" && /* @__PURE__ */ jsx16(
2351
2399
  "button",
2352
2400
  {
2353
2401
  type: "button",
2354
2402
  "data-vector-fill-gap": "",
2355
- className: buttonClass13,
2403
+ className: buttonClass14,
2356
2404
  "aria-pressed": editing.fillGap > 0,
2357
2405
  title: "Close small gaps between path ends when filling",
2358
2406
  "aria-label": "Close gaps",
@@ -2361,12 +2409,12 @@ function VectorToolbar({
2361
2409
  }
2362
2410
  )
2363
2411
  ] }),
2364
- showDistort && /* @__PURE__ */ jsx15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx15(
2412
+ showDistort && /* @__PURE__ */ jsx16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx16(
2365
2413
  "button",
2366
2414
  {
2367
2415
  type: "button",
2368
2416
  "data-vector-free-distort": "",
2369
- className: buttonClass13,
2417
+ className: buttonClass14,
2370
2418
  "aria-pressed": state.freeDistort,
2371
2419
  title: `Free Distort: drag a corner of the selection box to distort in perspective (Shift+Alt+${modKeyLabel()}: symmetric perspective; ${modKeyLabel()}-drag a side to skew)`,
2372
2420
  "aria-label": "Free Distort",
@@ -2374,15 +2422,15 @@ function VectorToolbar({
2374
2422
  children: "Distort"
2375
2423
  }
2376
2424
  ) }),
2377
- showBlend && /* @__PURE__ */ jsx15(VectorBlendControls, { controller }),
2378
- showArrange && /* @__PURE__ */ jsxs15(Fragment5, { children: [
2379
- /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2380
- /* @__PURE__ */ jsx15(
2425
+ showBlend && /* @__PURE__ */ jsx16(VectorBlendControls, { controller }),
2426
+ showArrange && /* @__PURE__ */ jsxs16(Fragment6, { children: [
2427
+ /* @__PURE__ */ jsxs16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2428
+ /* @__PURE__ */ jsx16(
2381
2429
  "button",
2382
2430
  {
2383
2431
  type: "button",
2384
2432
  "data-vector-live-boolean": "",
2385
- className: buttonClass13,
2433
+ className: buttonClass14,
2386
2434
  "aria-pressed": live,
2387
2435
  title: "Live booleans: keep the shapes editable",
2388
2436
  "aria-label": "Live booleans",
@@ -2399,12 +2447,12 @@ function VectorToolbar({
2399
2447
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
2400
2448
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
2401
2449
  ] }),
2402
- /* @__PURE__ */ jsx15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx15(
2450
+ /* @__PURE__ */ jsx16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx16(
2403
2451
  "button",
2404
2452
  {
2405
2453
  type: "button",
2406
2454
  "data-vector-pathfinder": a.id,
2407
- className: buttonClass13,
2455
+ className: buttonClass14,
2408
2456
  title: a.title,
2409
2457
  "aria-label": a.title,
2410
2458
  disabled: count < a.min,
@@ -2413,7 +2461,7 @@ function VectorToolbar({
2413
2461
  },
2414
2462
  a.id
2415
2463
  )) }),
2416
- /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2464
+ /* @__PURE__ */ jsxs16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2417
2465
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
2418
2466
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
2419
2467
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -2431,19 +2479,19 @@ function VectorBlendControls({ controller }) {
2431
2479
  const blend = getVectorBlendControls(state);
2432
2480
  const o = blend.options;
2433
2481
  const selected = blend.ids.length > 0;
2434
- return /* @__PURE__ */ jsxs15("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2435
- /* @__PURE__ */ jsx15("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass13, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2436
- /* @__PURE__ */ jsx15(
2482
+ return /* @__PURE__ */ jsxs16("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2483
+ /* @__PURE__ */ jsx16("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass14, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2484
+ /* @__PURE__ */ jsx16(
2437
2485
  "select",
2438
2486
  {
2439
2487
  "aria-label": "Blend spacing",
2440
2488
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
2441
2489
  value: o.spacing,
2442
2490
  onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
2443
- children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx15("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2491
+ children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx16("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2444
2492
  }
2445
2493
  ),
2446
- o.spacing !== "smoothColor" && /* @__PURE__ */ jsx15(
2494
+ o.spacing !== "smoothColor" && /* @__PURE__ */ jsx16(
2447
2495
  "input",
2448
2496
  {
2449
2497
  type: "number",
@@ -2459,12 +2507,12 @@ function VectorBlendControls({ controller }) {
2459
2507
  }
2460
2508
  }
2461
2509
  ),
2462
- /* @__PURE__ */ jsx15(
2510
+ /* @__PURE__ */ jsx16(
2463
2511
  "button",
2464
2512
  {
2465
2513
  type: "button",
2466
2514
  "data-vector-blend-action": "orient",
2467
- className: buttonClass13,
2515
+ className: buttonClass14,
2468
2516
  "aria-pressed": o.orientToSpine,
2469
2517
  title: "Orient steps to the spine",
2470
2518
  "aria-label": "Orient to spine",
@@ -2472,12 +2520,12 @@ function VectorBlendControls({ controller }) {
2472
2520
  children: "\u293F"
2473
2521
  }
2474
2522
  ),
2475
- /* @__PURE__ */ jsx15(
2523
+ /* @__PURE__ */ jsx16(
2476
2524
  "button",
2477
2525
  {
2478
2526
  type: "button",
2479
2527
  "data-vector-blend-action": "spine",
2480
- className: buttonClass13,
2528
+ className: buttonClass14,
2481
2529
  title: "Replace Spine (select a blend and a path)",
2482
2530
  "aria-label": "Replace Spine",
2483
2531
  disabled: !blend.canReplaceSpine,
@@ -2485,12 +2533,12 @@ function VectorBlendControls({ controller }) {
2485
2533
  children: "\u2307"
2486
2534
  }
2487
2535
  ),
2488
- /* @__PURE__ */ jsx15("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass13, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2489
- /* @__PURE__ */ jsx15("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass13, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2536
+ /* @__PURE__ */ jsx16("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass14, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2537
+ /* @__PURE__ */ jsx16("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass14, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2490
2538
  ] });
2491
2539
  }
2492
- var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2493
- var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2540
+ var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2541
+ var numberClass10 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2494
2542
  function VectorStylePanel({ controller, className }) {
2495
2543
  const state = useVectorEditorState(controller);
2496
2544
  const style = getVectorSelectionStyle(state);
@@ -2522,17 +2570,17 @@ function VectorStylePanel({ controller, className }) {
2522
2570
  if (style) controller.setWidthProfile(id);
2523
2571
  else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
2524
2572
  };
2525
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2573
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx16("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2526
2574
  const arrowRow = (end, label) => {
2527
2575
  const key = end === "start" ? "startArrow" : "endArrow";
2528
2576
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
2529
- return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2530
- /* @__PURE__ */ jsx15("span", { className: "w-16 text-xs text-foreground", children: label }),
2531
- /* @__PURE__ */ jsxs15("select", { "aria-label": `${label} arrowhead`, className: selectClass10, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2532
- /* @__PURE__ */ jsx15("option", { value: "", children: "None" }),
2533
- VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx15("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
2577
+ return /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2578
+ /* @__PURE__ */ jsx16("span", { className: "w-16 text-xs text-foreground", children: label }),
2579
+ /* @__PURE__ */ jsxs16("select", { "aria-label": `${label} arrowhead`, className: selectClass11, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2580
+ /* @__PURE__ */ jsx16("option", { value: "", children: "None" }),
2581
+ VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx16("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
2534
2582
  ] }),
2535
- /* @__PURE__ */ jsx15(
2583
+ /* @__PURE__ */ jsx16(
2536
2584
  "input",
2537
2585
  {
2538
2586
  type: "number",
@@ -2540,7 +2588,7 @@ function VectorStylePanel({ controller, className }) {
2540
2588
  step: 0.25,
2541
2589
  "aria-label": `${label} arrowhead scale`,
2542
2590
  title: "Scale (\xD7 stroke width)",
2543
- className: numberClass9,
2591
+ className: numberClass10,
2544
2592
  value: source[scaleKey],
2545
2593
  onChange: (e) => {
2546
2594
  const n = parseFloat(e.target.value);
@@ -2556,14 +2604,14 @@ function VectorStylePanel({ controller, className }) {
2556
2604
  };
2557
2605
  const paintRow = (key, label) => {
2558
2606
  const paint = source[key];
2559
- const hex = vectorPaintToHex5(paint);
2607
+ const hex = vectorPaintToHex6(paint);
2560
2608
  const on = paint !== null && paint !== "none";
2561
- return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2562
- /* @__PURE__ */ jsxs15("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2563
- /* @__PURE__ */ jsx15("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2609
+ return /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2610
+ /* @__PURE__ */ jsxs16("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2611
+ /* @__PURE__ */ jsx16("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2564
2612
  label
2565
2613
  ] }),
2566
- /* @__PURE__ */ jsx15(
2614
+ /* @__PURE__ */ jsx16(
2567
2615
  "input",
2568
2616
  {
2569
2617
  type: "color",
@@ -2574,18 +2622,18 @@ function VectorStylePanel({ controller, className }) {
2574
2622
  onChange: (e) => apply({ [key]: e.target.value })
2575
2623
  }
2576
2624
  ),
2577
- on && !hex && /* @__PURE__ */ jsx15("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2578
- style?.mixed[key] && /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2625
+ on && !hex && /* @__PURE__ */ jsx16("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2626
+ style?.mixed[key] && /* @__PURE__ */ jsx16("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2579
2627
  ] });
2580
2628
  };
2581
- return /* @__PURE__ */ jsxs15("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2582
- /* @__PURE__ */ jsx15("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2629
+ return /* @__PURE__ */ jsxs16("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2630
+ /* @__PURE__ */ jsx16("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2583
2631
  paintRow("fill", "Fill"),
2584
2632
  paintRow("stroke", "Stroke"),
2585
- style && /* @__PURE__ */ jsx15(VectorGradientEditor, { controller }),
2586
- /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2587
- /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Width" }),
2588
- /* @__PURE__ */ jsx15(
2633
+ style && /* @__PURE__ */ jsx16(VectorGradientEditor, { controller }),
2634
+ /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2635
+ /* @__PURE__ */ jsx16("span", { className: "w-16", children: "Width" }),
2636
+ /* @__PURE__ */ jsx16(
2589
2637
  "input",
2590
2638
  {
2591
2639
  type: "number",
@@ -2601,33 +2649,33 @@ function VectorStylePanel({ controller, className }) {
2601
2649
  }
2602
2650
  )
2603
2651
  ] }),
2604
- /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2605
- /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Align" }),
2606
- /* @__PURE__ */ jsx15("select", { "aria-label": "Stroke alignment", className: selectClass10, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx15("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
2652
+ /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2653
+ /* @__PURE__ */ jsx16("span", { className: "w-16", children: "Align" }),
2654
+ /* @__PURE__ */ jsx16("select", { "aria-label": "Stroke alignment", className: selectClass11, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx16("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
2607
2655
  mixed("strokeAlign")
2608
2656
  ] }),
2609
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2610
- /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Profile" }),
2611
- /* @__PURE__ */ jsxs15(
2657
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2658
+ /* @__PURE__ */ jsx16("span", { className: "w-16", children: "Profile" }),
2659
+ /* @__PURE__ */ jsxs16(
2612
2660
  "select",
2613
2661
  {
2614
2662
  "aria-label": "Width profile",
2615
- className: selectClass10,
2663
+ className: selectClass11,
2616
2664
  value: source.widthProfile,
2617
2665
  onChange: (e) => {
2618
2666
  if (e.target.value !== "custom") setProfile(e.target.value);
2619
2667
  },
2620
2668
  children: [
2621
- VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx15("option", { value: p.id, children: p.label }, p.id)),
2622
- source.widthProfile === "custom" && /* @__PURE__ */ jsx15("option", { value: "custom", disabled: true, children: "Custom" })
2669
+ VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx16("option", { value: p.id, children: p.label }, p.id)),
2670
+ source.widthProfile === "custom" && /* @__PURE__ */ jsx16("option", { value: "custom", disabled: true, children: "Custom" })
2623
2671
  ]
2624
2672
  }
2625
2673
  ),
2626
- /* @__PURE__ */ jsx15(
2674
+ /* @__PURE__ */ jsx16(
2627
2675
  "button",
2628
2676
  {
2629
2677
  type: "button",
2630
- className: buttonClass13,
2678
+ className: buttonClass14,
2631
2679
  title: "Reset to uniform width",
2632
2680
  "aria-label": "Reset to uniform width",
2633
2681
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -2637,27 +2685,27 @@ function VectorStylePanel({ controller, className }) {
2637
2685
  ),
2638
2686
  mixed("widthProfile")
2639
2687
  ] }),
2640
- source.hasOpenPath && /* @__PURE__ */ jsxs15(Fragment5, { children: [
2688
+ source.hasOpenPath && /* @__PURE__ */ jsxs16(Fragment6, { children: [
2641
2689
  arrowRow("start", "Start"),
2642
2690
  arrowRow("end", "End"),
2643
- (source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2644
- /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Heads" }),
2645
- /* @__PURE__ */ jsxs15("select", { "aria-label": "Arrowhead alignment", className: selectClass10, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2646
- /* @__PURE__ */ jsx15("option", { value: "extend", children: "Tip past end" }),
2647
- /* @__PURE__ */ jsx15("option", { value: "tip", children: "Tip at end" })
2691
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2692
+ /* @__PURE__ */ jsx16("span", { className: "w-16", children: "Heads" }),
2693
+ /* @__PURE__ */ jsxs16("select", { "aria-label": "Arrowhead alignment", className: selectClass11, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2694
+ /* @__PURE__ */ jsx16("option", { value: "extend", children: "Tip past end" }),
2695
+ /* @__PURE__ */ jsx16("option", { value: "tip", children: "Tip at end" })
2648
2696
  ] })
2649
2697
  ] })
2650
2698
  ] }),
2651
- source.corners > 0 && /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2652
- /* @__PURE__ */ jsx15("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2653
- /* @__PURE__ */ jsx15(
2699
+ source.corners > 0 && /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2700
+ /* @__PURE__ */ jsx16("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2701
+ /* @__PURE__ */ jsx16(
2654
2702
  "input",
2655
2703
  {
2656
2704
  type: "number",
2657
2705
  min: 0,
2658
2706
  step: 1,
2659
2707
  "aria-label": "Corner radius",
2660
- className: numberClass9,
2708
+ className: numberClass10,
2661
2709
  value: Math.round(source.cornerRadius * 100) / 100,
2662
2710
  onChange: (e) => {
2663
2711
  const n = parseFloat(e.target.value);
@@ -2665,12 +2713,12 @@ function VectorStylePanel({ controller, className }) {
2665
2713
  }
2666
2714
  }
2667
2715
  ),
2668
- /* @__PURE__ */ jsx15("select", { "aria-label": "Corner kind", className: selectClass10, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx15("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2716
+ /* @__PURE__ */ jsx16("select", { "aria-label": "Corner kind", className: selectClass11, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx16("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2669
2717
  mixed("cornerRadius")
2670
2718
  ] }),
2671
- style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
2672
- /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Smoothing" }),
2673
- /* @__PURE__ */ jsx15(
2719
+ style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
2720
+ /* @__PURE__ */ jsx16("span", { className: "w-16", children: "Smoothing" }),
2721
+ /* @__PURE__ */ jsx16(
2674
2722
  "input",
2675
2723
  {
2676
2724
  type: "range",
@@ -2685,9 +2733,9 @@ function VectorStylePanel({ controller, className }) {
2685
2733
  }
2686
2734
  )
2687
2735
  ] }),
2688
- style && /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2689
- /* @__PURE__ */ jsx15("span", { className: "w-16", children: "Opacity" }),
2690
- /* @__PURE__ */ jsx15(
2736
+ style && /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2737
+ /* @__PURE__ */ jsx16("span", { className: "w-16", children: "Opacity" }),
2738
+ /* @__PURE__ */ jsx16(
2691
2739
  "input",
2692
2740
  {
2693
2741
  type: "range",
@@ -2701,28 +2749,29 @@ function VectorStylePanel({ controller, className }) {
2701
2749
  }
2702
2750
  )
2703
2751
  ] }),
2704
- /* @__PURE__ */ jsx15(VectorTextPanel, { controller }),
2705
- style && /* @__PURE__ */ jsx15(VectorAppearancePanel, { controller }),
2706
- style && /* @__PURE__ */ jsx15(VectorEffectsPanel, { controller }),
2707
- style && /* @__PURE__ */ jsx15(VectorEnvelopePanel, { controller }),
2708
- style && /* @__PURE__ */ jsx15(VectorMaskPanel, { controller }),
2709
- /* @__PURE__ */ jsx15(VectorFramePanel, { controller }),
2710
- /* @__PURE__ */ jsx15(VectorConnectorPanel, { controller }),
2711
- /* @__PURE__ */ jsx15(VectorShadingPanel, { controller }),
2712
- /* @__PURE__ */ jsx15(VectorTracePanel, { controller }),
2713
- /* @__PURE__ */ jsx15(VectorConstraintsPanel, { controller }),
2714
- /* @__PURE__ */ jsx15(VectorSymbolsPanel, { controller })
2752
+ /* @__PURE__ */ jsx16(VectorTextPanel, { controller }),
2753
+ style && /* @__PURE__ */ jsx16(VectorAppearancePanel, { controller }),
2754
+ style && /* @__PURE__ */ jsx16(VectorEffectsPanel, { controller }),
2755
+ style && /* @__PURE__ */ jsx16(VectorEnvelopePanel, { controller }),
2756
+ style && /* @__PURE__ */ jsx16(VectorMaskPanel, { controller }),
2757
+ /* @__PURE__ */ jsx16(VectorFramePanel, { controller }),
2758
+ /* @__PURE__ */ jsx16(VectorConnectorPanel, { controller }),
2759
+ /* @__PURE__ */ jsx16(VectorShadingPanel, { controller }),
2760
+ /* @__PURE__ */ jsx16(VectorSymmetryPanel, { controller }),
2761
+ /* @__PURE__ */ jsx16(VectorTracePanel, { controller }),
2762
+ /* @__PURE__ */ jsx16(VectorConstraintsPanel, { controller }),
2763
+ /* @__PURE__ */ jsx16(VectorSymbolsPanel, { controller })
2715
2764
  ] });
2716
2765
  }
2717
2766
 
2718
2767
  // src/vector/VectorEditor.tsx
2719
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2768
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
2720
2769
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
2721
- return /* @__PURE__ */ jsxs16("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2722
- /* @__PURE__ */ jsx16(VectorToolbar, { controller, tools }),
2723
- /* @__PURE__ */ jsxs16("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2724
- /* @__PURE__ */ jsx16("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx16(VectorCanvas, { controller, ...canvasProps }) }),
2725
- showStylePanel && /* @__PURE__ */ jsx16(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
2770
+ return /* @__PURE__ */ jsxs17("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2771
+ /* @__PURE__ */ jsx17(VectorToolbar, { controller, tools }),
2772
+ /* @__PURE__ */ jsxs17("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2773
+ /* @__PURE__ */ jsx17("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx17(VectorCanvas, { controller, ...canvasProps }) }),
2774
+ showStylePanel && /* @__PURE__ */ jsx17(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
2726
2775
  ] })
2727
2776
  ] });
2728
2777
  }
@@ -2745,6 +2794,7 @@ export {
2745
2794
  VectorShadingPanel,
2746
2795
  VectorStylePanel,
2747
2796
  VectorSymbolsPanel,
2797
+ VectorSymmetryPanel,
2748
2798
  VectorTextElement,
2749
2799
  VectorTextPanel,
2750
2800
  VectorToolbar,