@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.
@@ -48,6 +48,7 @@ __export(vector_exports, {
48
48
  VectorShadingPanel: () => VectorShadingPanel,
49
49
  VectorStylePanel: () => VectorStylePanel,
50
50
  VectorSymbolsPanel: () => VectorSymbolsPanel,
51
+ VectorSymmetryPanel: () => VectorSymmetryPanel,
51
52
  VectorTextElement: () => VectorTextElement,
52
53
  VectorTextPanel: () => VectorTextPanel,
53
54
  VectorToolbar: () => VectorToolbar,
@@ -683,6 +684,7 @@ function VectorCanvas({
683
684
  sh.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
684
685
  sh.points.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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))
685
686
  ] }, `shading:${sh.nodeId}`)),
687
+ overlay.symmetry.map((sy) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("line", { x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2 }, i)) }, `sym:${sy.nodeId}`)),
686
688
  overlay.trackingPoints.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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}`)),
687
689
  overlay.typedEntry && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
688
690
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: -3, y: -12, width: overlay.typedEntry.text.length * 7 + 8, height: 17, rx: 3, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
@@ -819,7 +821,7 @@ function VectorCanvas({
819
821
 
820
822
  // src/vector/VectorToolbar.tsx
821
823
  var import_react10 = __toESM(require("react"), 1);
822
- var import_vector15 = require("@pixerate/editor/vector");
824
+ var import_vector16 = require("@pixerate/editor/vector");
823
825
 
824
826
  // src/vector/VectorEffectsPanel.tsx
825
827
  var import_vector4 = require("@pixerate/editor/vector");
@@ -909,6 +911,15 @@ function VectorEffectsPanel({ controller, className }) {
909
911
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "min-w-0 flex-1 truncate", children: spec.label }),
910
912
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", "aria-label": `${e.label} ${spec.label}`, checked: !!e.params[spec.key], onChange: (ev) => set(e, spec.key, ev.target.checked, false) })
911
913
  ] }, spec.key);
914
+ if (spec.kind === "color") {
915
+ const c = e.params[spec.key];
916
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-effect-param": spec.key, children: [
917
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: labelClass, children: spec.label }),
918
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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) }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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: (0, import_vector4.vectorPaintToHex)(c ?? "#ffffff") ?? "#ffffff", onChange: (ev) => set(e, spec.key, ev.target.value, true), onBlur: end })
920
+ ] }, spec.key);
921
+ }
922
+ if (spec.kind !== "select") return null;
912
923
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("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: [
913
924
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: labelClass, children: spec.label }),
914
925
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsx)("option", { value: o.value, children: o.label }, o.value)) })
@@ -2024,16 +2035,53 @@ function VectorTracePanel({ controller, className }) {
2024
2035
  ] });
2025
2036
  }
2026
2037
 
2027
- // src/vector/VectorGradientEditor.tsx
2038
+ // src/vector/VectorSymmetryPanel.tsx
2028
2039
  var import_vector14 = require("@pixerate/editor/vector");
2029
2040
  var import_jsx_runtime14 = require("react/jsx-runtime");
2041
+ 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";
2030
2042
  var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2031
- var numberClass8 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2032
- 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";
2043
+ var numberClass8 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
2044
+ function VectorSymmetryPanel({ controller, className }) {
2045
+ const state = useVectorEditorState(controller);
2046
+ const group = state.selection.map((id) => controller.getNode(id)).find(import_vector14.isVectorSymmetryGroup) ?? null;
2047
+ const s = group?.symmetry;
2048
+ const ids = group ? [group.id] : void 0;
2049
+ const drawing = !!group && state.symmetryTarget === group.id;
2050
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-symmetry-panel": "", "aria-label": "Symmetry", children: [
2051
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2052
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Symmetry" }),
2053
+ !group && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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" }),
2054
+ !group && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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" }),
2055
+ group && s && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2056
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Symmetry mode", className: selectClass9, value: s.mode, onChange: (e) => controller.setSymmetry({ mode: e.target.value }, { ids }), children: import_vector14.VECTOR_SYMMETRY_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: m, children: import_vector14.VECTOR_SYMMETRY_MODE_LABELS[m] }, m)) }),
2057
+ s.mode === "radial" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2058
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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 }) }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Mirror each copy too (kaleidoscope)", children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { type: "checkbox", "aria-label": "Mirror copies", checked: !!s.mirror, onChange: (e) => controller.setSymmetry({ mirror: e.target.checked }, { ids }) }),
2061
+ "Mirror"
2062
+ ] })
2063
+ ] })
2064
+ ] })
2065
+ ] }),
2066
+ group && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2067
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16" }),
2068
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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" }),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, title: "Expand: keep the copies as real objects", onClick: () => controller.releaseSymmetry({ ids, expand: true }), children: "Expand" }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, title: "Release: stop repeating (the copies go)", onClick: () => controller.releaseSymmetry({ ids }), children: "Release" })
2071
+ ] })
2072
+ ] });
2073
+ }
2074
+
2075
+ // src/vector/VectorGradientEditor.tsx
2076
+ var import_vector15 = require("@pixerate/editor/vector");
2077
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2078
+ var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2079
+ var numberClass9 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2080
+ 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";
2033
2081
  var percent = (v) => Math.round(v * 1e3) / 10;
2034
2082
  function VectorGradientEditor({ controller, className }) {
2035
2083
  const state = useVectorEditorState(controller);
2036
- const ed = (0, import_vector14.getVectorGradientEditor)(state);
2084
+ const ed = (0, import_vector15.getVectorGradientEditor)(state);
2037
2085
  if (!ed) return null;
2038
2086
  const g = ed.gradient;
2039
2087
  const sel = ed.selected;
@@ -2041,26 +2089,26 @@ function VectorGradientEditor({ controller, className }) {
2041
2089
  const setType = (value) => {
2042
2090
  if (value === "solid") {
2043
2091
  const first = ed.stops[0];
2044
- controller.setStyle({ [ed.target]: (0, import_vector14.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2092
+ controller.setStyle({ [ed.target]: (0, import_vector15.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2045
2093
  } else controller.setGradient({ type: value });
2046
2094
  };
2047
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2048
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2049
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2050
- ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2051
- ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2095
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2096
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2097
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2098
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2099
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2052
2100
  ] }),
2053
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2054
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2055
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("select", { "aria-label": "Gradient type", className: selectClass9, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2056
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "solid", children: "Solid" }),
2057
- import_vector14.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: t, children: import_vector14.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2101
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2102
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2103
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": "Gradient type", className: selectClass10, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2104
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "solid", children: "Solid" }),
2105
+ import_vector15.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: t, children: import_vector15.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2058
2106
  ] }),
2059
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2107
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2060
2108
  ] }),
2061
- g && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2062
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2063
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2109
+ g && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2110
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2111
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2064
2112
  "div",
2065
2113
  {
2066
2114
  role: "button",
@@ -2074,7 +2122,7 @@ function VectorGradientEditor({ controller, className }) {
2074
2122
  }
2075
2123
  }
2076
2124
  ),
2077
- ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2125
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2078
2126
  "button",
2079
2127
  {
2080
2128
  type: "button",
@@ -2088,21 +2136,21 @@ function VectorGradientEditor({ controller, className }) {
2088
2136
  s.index
2089
2137
  ))
2090
2138
  ] }),
2091
- sel && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2092
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2139
+ sel && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2140
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2093
2141
  "input",
2094
2142
  {
2095
2143
  type: "color",
2096
2144
  "aria-label": "Stop colour",
2097
2145
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
2098
- value: (0, import_vector14.vectorPaintToHex)(sel.color) ?? "#000000",
2146
+ value: (0, import_vector15.vectorPaintToHex)(sel.color) ?? "#000000",
2099
2147
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
2100
2148
  onBlur: end
2101
2149
  }
2102
2150
  ),
2103
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2151
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2104
2152
  "Opacity",
2105
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2153
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2106
2154
  "input",
2107
2155
  {
2108
2156
  type: "number",
@@ -2110,7 +2158,7 @@ function VectorGradientEditor({ controller, className }) {
2110
2158
  max: 100,
2111
2159
  step: 5,
2112
2160
  "aria-label": "Stop opacity",
2113
- className: numberClass8,
2161
+ className: numberClass9,
2114
2162
  value: percent(sel.opacity ?? 1),
2115
2163
  onChange: (e) => {
2116
2164
  const n = parseFloat(e.target.value);
@@ -2119,9 +2167,9 @@ function VectorGradientEditor({ controller, className }) {
2119
2167
  }
2120
2168
  )
2121
2169
  ] }),
2122
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2170
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2123
2171
  "At",
2124
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2172
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2125
2173
  "input",
2126
2174
  {
2127
2175
  type: "number",
@@ -2129,7 +2177,7 @@ function VectorGradientEditor({ controller, className }) {
2129
2177
  max: 100,
2130
2178
  step: 1,
2131
2179
  "aria-label": "Stop position",
2132
- className: numberClass8,
2180
+ className: numberClass9,
2133
2181
  value: percent(sel.offset),
2134
2182
  onChange: (e) => {
2135
2183
  const n = parseFloat(e.target.value);
@@ -2138,9 +2186,9 @@ function VectorGradientEditor({ controller, className }) {
2138
2186
  }
2139
2187
  )
2140
2188
  ] }),
2141
- !sel.last && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2189
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2142
2190
  "Midpoint",
2143
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2191
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2144
2192
  "input",
2145
2193
  {
2146
2194
  type: "number",
@@ -2148,7 +2196,7 @@ function VectorGradientEditor({ controller, className }) {
2148
2196
  max: 95,
2149
2197
  step: 1,
2150
2198
  "aria-label": "Stop midpoint",
2151
- className: numberClass8,
2199
+ className: numberClass9,
2152
2200
  value: percent(sel.midpoint ?? 0.5),
2153
2201
  onChange: (e) => {
2154
2202
  const n = parseFloat(e.target.value);
@@ -2157,15 +2205,15 @@ function VectorGradientEditor({ controller, className }) {
2157
2205
  }
2158
2206
  )
2159
2207
  ] }),
2160
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2208
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2161
2209
  ] }),
2162
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2163
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Colours" }),
2164
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass9, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector14.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: i, children: import_vector14.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2210
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2211
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Colours" }),
2212
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass10, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector15.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: i, children: import_vector15.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2165
2213
  ] }),
2166
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2167
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Smooth" }),
2168
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2214
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2215
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Smooth" }),
2216
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2169
2217
  "input",
2170
2218
  {
2171
2219
  type: "range",
@@ -2181,26 +2229,26 @@ function VectorGradientEditor({ controller, className }) {
2181
2229
  }
2182
2230
  )
2183
2231
  ] }),
2184
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2185
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Spread" }),
2186
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient spread", className: selectClass9, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector14.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: sp, children: import_vector14.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2232
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2233
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Spread" }),
2234
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Gradient spread", className: selectClass10, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector15.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: sp, children: import_vector15.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2187
2235
  ] }),
2188
- ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2189
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "On stroke" }),
2190
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass9, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector14.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: m, children: import_vector14.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2236
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2237
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "On stroke" }),
2238
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass10, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector15.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: m, children: import_vector15.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2191
2239
  ] })
2192
2240
  ] })
2193
2241
  ] });
2194
2242
  }
2195
2243
 
2196
2244
  // src/vector/VectorToolbar.tsx
2197
- var import_jsx_runtime15 = require("react/jsx-runtime");
2245
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2198
2246
  var VECTOR_SNAP_TITLE = "Snap to objects";
2199
2247
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
2200
- 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";
2248
+ 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";
2201
2249
  function VectorToolbar({
2202
2250
  controller,
2203
- tools = import_vector15.VECTOR_TOOLS,
2251
+ tools = import_vector16.VECTOR_TOOLS,
2204
2252
  showHistory = true,
2205
2253
  showArrange = true,
2206
2254
  showSnap = true,
@@ -2214,36 +2262,36 @@ function VectorToolbar({
2214
2262
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
2215
2263
  const editing = state.editing;
2216
2264
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
2217
- const maskSelected = state.selection.some((id) => (0, import_vector15.isVectorMaskGroup)(controller.getNode(id)));
2265
+ const maskSelected = state.selection.some((id) => (0, import_vector16.isVectorMaskGroup)(controller.getNode(id)));
2218
2266
  const [live, setLive] = import_react10.default.useState(false);
2219
2267
  const booleanDisabled = live ? count < 1 : count < 2;
2220
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2221
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2222
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2268
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: buttonClass14, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2269
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2270
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2223
2271
  "button",
2224
2272
  {
2225
2273
  type: "button",
2226
2274
  "data-vector-tool": tool,
2227
- className: buttonClass13,
2275
+ className: buttonClass14,
2228
2276
  "aria-pressed": state.tool === tool,
2229
- title: `${(0, import_vector15.vectorToolLabel)(tool)} (${import_vector15.VECTOR_TOOL_SHORTCUTS[tool]})`,
2230
- "aria-label": (0, import_vector15.vectorToolLabel)(tool),
2277
+ title: `${(0, import_vector16.vectorToolLabel)(tool)} (${import_vector16.VECTOR_TOOL_SHORTCUTS[tool]})`,
2278
+ "aria-label": (0, import_vector16.vectorToolLabel)(tool),
2231
2279
  onClick: () => controller.setTool(tool),
2232
- children: import_vector15.VECTOR_TOOL_GLYPHS[tool]
2280
+ children: import_vector16.VECTOR_TOOL_GLYPHS[tool]
2233
2281
  },
2234
2282
  tool
2235
2283
  )) }),
2236
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2284
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2237
2285
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
2238
2286
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
2239
2287
  ] }),
2240
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2241
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2288
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2289
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2242
2290
  "button",
2243
2291
  {
2244
2292
  type: "button",
2245
2293
  "data-vector-snap": "",
2246
- className: buttonClass13,
2294
+ className: buttonClass14,
2247
2295
  "aria-pressed": state.snap.objects,
2248
2296
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
2249
2297
  "aria-label": VECTOR_SNAP_TITLE,
@@ -2251,18 +2299,18 @@ function VectorToolbar({
2251
2299
  children: "Snap"
2252
2300
  }
2253
2301
  ),
2254
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2302
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
2255
2303
  "select",
2256
2304
  {
2257
2305
  "data-vector-polar": "",
2258
2306
  "aria-label": "Polar tracking increment",
2259
2307
  title: "Polar tracking: the angles drawn points snap to from where they leave",
2260
2308
  className: "h-8 rounded-md border border-border bg-background px-1 text-xs",
2261
- value: String((0, import_vector15.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2262
- onChange: (e) => controller.setSnap({ angles: (0, import_vector15.vectorPolarAngles)(Number(e.target.value)) }),
2309
+ value: String((0, import_vector16.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2310
+ onChange: (e) => controller.setSnap({ angles: (0, import_vector16.vectorPolarAngles)(Number(e.target.value)) }),
2263
2311
  children: [
2264
- (0, import_vector15.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "Custom" }),
2265
- [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("option", { value: n, children: [
2312
+ (0, import_vector16.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "", children: "Custom" }),
2313
+ [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("option", { value: n, children: [
2266
2314
  n,
2267
2315
  "\xB0"
2268
2316
  ] }, n))
@@ -2270,13 +2318,13 @@ function VectorToolbar({
2270
2318
  }
2271
2319
  )
2272
2320
  ] }),
2273
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2274
- import_vector15.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2321
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2322
+ import_vector16.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2275
2323
  "button",
2276
2324
  {
2277
2325
  type: "button",
2278
2326
  "data-vector-path-action": a.id,
2279
- className: buttonClass13,
2327
+ className: buttonClass14,
2280
2328
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
2281
2329
  "aria-label": a.title,
2282
2330
  disabled: !a.enabled(count, points),
@@ -2285,12 +2333,12 @@ function VectorToolbar({
2285
2333
  },
2286
2334
  a.id
2287
2335
  )),
2288
- import_vector15.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2336
+ import_vector16.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2289
2337
  "button",
2290
2338
  {
2291
2339
  type: "button",
2292
2340
  "data-vector-edit": t.key,
2293
- className: buttonClass13,
2341
+ className: buttonClass14,
2294
2342
  "aria-pressed": editing[t.key],
2295
2343
  title: t.title,
2296
2344
  "aria-label": t.title,
@@ -2299,9 +2347,9 @@ function VectorToolbar({
2299
2347
  },
2300
2348
  t.key
2301
2349
  )),
2302
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2350
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2303
2351
  "Smoothing",
2304
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2352
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2305
2353
  "input",
2306
2354
  {
2307
2355
  type: "range",
@@ -2315,9 +2363,9 @@ function VectorToolbar({
2315
2363
  }
2316
2364
  )
2317
2365
  ] }),
2318
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2366
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2319
2367
  "Size",
2320
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2368
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2321
2369
  "input",
2322
2370
  {
2323
2371
  type: "number",
@@ -2333,26 +2381,26 @@ function VectorToolbar({
2333
2381
  }
2334
2382
  )
2335
2383
  ] }),
2336
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2384
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2337
2385
  "button",
2338
2386
  {
2339
2387
  type: "button",
2340
2388
  "data-vector-fill-gap": "",
2341
- className: buttonClass13,
2389
+ className: buttonClass14,
2342
2390
  "aria-pressed": editing.fillGap > 0,
2343
2391
  title: "Close small gaps between path ends when filling",
2344
2392
  "aria-label": "Close gaps",
2345
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector15.VECTOR_FILL_GAP }),
2393
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector16.VECTOR_FILL_GAP }),
2346
2394
  children: "Gaps"
2347
2395
  }
2348
2396
  )
2349
2397
  ] }),
2350
- showDistort && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2398
+ showDistort && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2351
2399
  "button",
2352
2400
  {
2353
2401
  type: "button",
2354
2402
  "data-vector-free-distort": "",
2355
- className: buttonClass13,
2403
+ className: buttonClass14,
2356
2404
  "aria-pressed": state.freeDistort,
2357
2405
  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)`,
2358
2406
  "aria-label": "Free Distort",
@@ -2360,15 +2408,15 @@ function VectorToolbar({
2360
2408
  children: "Distort"
2361
2409
  }
2362
2410
  ) }),
2363
- showBlend && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorBlendControls, { controller }),
2364
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2365
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2366
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2411
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorBlendControls, { controller }),
2412
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
2413
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2414
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2367
2415
  "button",
2368
2416
  {
2369
2417
  type: "button",
2370
2418
  "data-vector-live-boolean": "",
2371
- className: buttonClass13,
2419
+ className: buttonClass14,
2372
2420
  "aria-pressed": live,
2373
2421
  title: "Live booleans: keep the shapes editable",
2374
2422
  "aria-label": "Live booleans",
@@ -2385,12 +2433,12 @@ function VectorToolbar({
2385
2433
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
2386
2434
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
2387
2435
  ] }),
2388
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector15.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2436
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector16.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2389
2437
  "button",
2390
2438
  {
2391
2439
  type: "button",
2392
2440
  "data-vector-pathfinder": a.id,
2393
- className: buttonClass13,
2441
+ className: buttonClass14,
2394
2442
  title: a.title,
2395
2443
  "aria-label": a.title,
2396
2444
  disabled: count < a.min,
@@ -2399,7 +2447,7 @@ function VectorToolbar({
2399
2447
  },
2400
2448
  a.id
2401
2449
  )) }),
2402
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2450
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2403
2451
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
2404
2452
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
2405
2453
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -2414,22 +2462,22 @@ function VectorToolbar({
2414
2462
  }
2415
2463
  function VectorBlendControls({ controller }) {
2416
2464
  const state = useVectorEditorState(controller);
2417
- const blend = (0, import_vector15.getVectorBlendControls)(state);
2465
+ const blend = (0, import_vector16.getVectorBlendControls)(state);
2418
2466
  const o = blend.options;
2419
2467
  const selected = blend.ids.length > 0;
2420
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("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" }),
2422
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2468
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2469
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("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" }),
2470
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2423
2471
  "select",
2424
2472
  {
2425
2473
  "aria-label": "Blend spacing",
2426
2474
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
2427
2475
  value: o.spacing,
2428
- onChange: (e) => (0, import_vector15.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2429
- children: import_vector15.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: sp, children: import_vector15.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2476
+ onChange: (e) => (0, import_vector16.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2477
+ children: import_vector16.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: sp, children: import_vector16.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2430
2478
  }
2431
2479
  ),
2432
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2480
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2433
2481
  "input",
2434
2482
  {
2435
2483
  type: "number",
@@ -2441,29 +2489,29 @@ function VectorBlendControls({ controller }) {
2441
2489
  onChange: (e) => {
2442
2490
  const n = parseFloat(e.target.value);
2443
2491
  if (!Number.isFinite(n) || n <= 0) return;
2444
- (0, import_vector15.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2492
+ (0, import_vector16.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2445
2493
  }
2446
2494
  }
2447
2495
  ),
2448
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2496
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2449
2497
  "button",
2450
2498
  {
2451
2499
  type: "button",
2452
2500
  "data-vector-blend-action": "orient",
2453
- className: buttonClass13,
2501
+ className: buttonClass14,
2454
2502
  "aria-pressed": o.orientToSpine,
2455
2503
  title: "Orient steps to the spine",
2456
2504
  "aria-label": "Orient to spine",
2457
- onClick: () => (0, import_vector15.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2505
+ onClick: () => (0, import_vector16.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2458
2506
  children: "\u293F"
2459
2507
  }
2460
2508
  ),
2461
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2509
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2462
2510
  "button",
2463
2511
  {
2464
2512
  type: "button",
2465
2513
  "data-vector-blend-action": "spine",
2466
- className: buttonClass13,
2514
+ className: buttonClass14,
2467
2515
  title: "Replace Spine (select a blend and a path)",
2468
2516
  "aria-label": "Replace Spine",
2469
2517
  disabled: !blend.canReplaceSpine,
@@ -2471,15 +2519,15 @@ function VectorBlendControls({ controller }) {
2471
2519
  children: "\u2307"
2472
2520
  }
2473
2521
  ),
2474
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass13, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2475
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass13, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2522
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass14, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2523
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass14, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2476
2524
  ] });
2477
2525
  }
2478
- var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2479
- var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2526
+ var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2527
+ var numberClass10 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2480
2528
  function VectorStylePanel({ controller, className }) {
2481
2529
  const state = useVectorEditorState(controller);
2482
- const style = (0, import_vector15.getVectorSelectionStyle)(state);
2530
+ const style = (0, import_vector16.getVectorSelectionStyle)(state);
2483
2531
  const d = state.defaults;
2484
2532
  const source = style ?? {
2485
2533
  fill: d.fill ?? null,
@@ -2492,7 +2540,7 @@ function VectorStylePanel({ controller, className }) {
2492
2540
  arrowAlign: d.arrowAlign ?? "extend",
2493
2541
  startArrowScale: d.startArrowScale ?? 1,
2494
2542
  endArrowScale: d.endArrowScale ?? 1,
2495
- widthProfile: (0, import_vector15.widthProfilePresetId)(d.strokeWidthProfile),
2543
+ widthProfile: (0, import_vector16.widthProfilePresetId)(d.strokeWidthProfile),
2496
2544
  cornerRadius: d.cornerRadius,
2497
2545
  cornerKind: d.cornerKind ?? "round",
2498
2546
  cornerSmoothing: 0,
@@ -2506,19 +2554,19 @@ function VectorStylePanel({ controller, className }) {
2506
2554
  };
2507
2555
  const setProfile = (id) => {
2508
2556
  if (style) controller.setWidthProfile(id);
2509
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector15.widthProfilePreset)(id)?.length ? (0, import_vector15.widthProfilePreset)(id) : null });
2557
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector16.widthProfilePreset)(id)?.length ? (0, import_vector16.widthProfilePreset)(id) : null });
2510
2558
  };
2511
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2559
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2512
2560
  const arrowRow = (end, label) => {
2513
2561
  const key = end === "start" ? "startArrow" : "endArrow";
2514
2562
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
2515
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2516
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2517
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass10, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2518
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "None" }),
2519
- import_vector15.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: h, children: import_vector15.VECTOR_ARROWHEAD_LABELS[h] }, h))
2563
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2565
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass11, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2566
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "", children: "None" }),
2567
+ import_vector16.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: h, children: import_vector16.VECTOR_ARROWHEAD_LABELS[h] }, h))
2520
2568
  ] }),
2521
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2569
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2522
2570
  "input",
2523
2571
  {
2524
2572
  type: "number",
@@ -2526,7 +2574,7 @@ function VectorStylePanel({ controller, className }) {
2526
2574
  step: 0.25,
2527
2575
  "aria-label": `${label} arrowhead scale`,
2528
2576
  title: "Scale (\xD7 stroke width)",
2529
- className: numberClass9,
2577
+ className: numberClass10,
2530
2578
  value: source[scaleKey],
2531
2579
  onChange: (e) => {
2532
2580
  const n = parseFloat(e.target.value);
@@ -2542,14 +2590,14 @@ function VectorStylePanel({ controller, className }) {
2542
2590
  };
2543
2591
  const paintRow = (key, label) => {
2544
2592
  const paint = source[key];
2545
- const hex = (0, import_vector15.vectorPaintToHex)(paint);
2593
+ const hex = (0, import_vector16.vectorPaintToHex)(paint);
2546
2594
  const on = paint !== null && paint !== "none";
2547
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2548
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2549
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2595
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2596
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2597
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2550
2598
  label
2551
2599
  ] }),
2552
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2600
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2553
2601
  "input",
2554
2602
  {
2555
2603
  type: "color",
@@ -2560,18 +2608,18 @@ function VectorStylePanel({ controller, className }) {
2560
2608
  onChange: (e) => apply({ [key]: e.target.value })
2561
2609
  }
2562
2610
  ),
2563
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2564
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2611
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2612
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2565
2613
  ] });
2566
2614
  };
2567
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2568
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2615
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2616
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2569
2617
  paintRow("fill", "Fill"),
2570
2618
  paintRow("stroke", "Stroke"),
2571
- style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorGradientEditor, { controller }),
2572
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2573
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Width" }),
2574
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2619
+ style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorGradientEditor, { controller }),
2620
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2621
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Width" }),
2622
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2575
2623
  "input",
2576
2624
  {
2577
2625
  type: "number",
@@ -2587,33 +2635,33 @@ function VectorStylePanel({ controller, className }) {
2587
2635
  }
2588
2636
  )
2589
2637
  ] }),
2590
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2591
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Align" }),
2592
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass10, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector15.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: a, children: import_vector15.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2638
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2639
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Align" }),
2640
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass11, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector16.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: a, children: import_vector16.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2593
2641
  mixed("strokeAlign")
2594
2642
  ] }),
2595
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2596
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Profile" }),
2597
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2643
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2644
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Profile" }),
2645
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
2598
2646
  "select",
2599
2647
  {
2600
2648
  "aria-label": "Width profile",
2601
- className: selectClass10,
2649
+ className: selectClass11,
2602
2650
  value: source.widthProfile,
2603
2651
  onChange: (e) => {
2604
2652
  if (e.target.value !== "custom") setProfile(e.target.value);
2605
2653
  },
2606
2654
  children: [
2607
- import_vector15.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: p.id, children: p.label }, p.id)),
2608
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2655
+ import_vector16.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: p.id, children: p.label }, p.id)),
2656
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2609
2657
  ]
2610
2658
  }
2611
2659
  ),
2612
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2660
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2613
2661
  "button",
2614
2662
  {
2615
2663
  type: "button",
2616
- className: buttonClass13,
2664
+ className: buttonClass14,
2617
2665
  title: "Reset to uniform width",
2618
2666
  "aria-label": "Reset to uniform width",
2619
2667
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -2623,27 +2671,27 @@ function VectorStylePanel({ controller, className }) {
2623
2671
  ),
2624
2672
  mixed("widthProfile")
2625
2673
  ] }),
2626
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2674
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
2627
2675
  arrowRow("start", "Start"),
2628
2676
  arrowRow("end", "End"),
2629
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2630
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Heads" }),
2631
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass10, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2632
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "extend", children: "Tip past end" }),
2633
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "tip", children: "Tip at end" })
2677
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2678
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Heads" }),
2679
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass11, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2680
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "extend", children: "Tip past end" }),
2681
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "tip", children: "Tip at end" })
2634
2682
  ] })
2635
2683
  ] })
2636
2684
  ] }),
2637
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2638
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2639
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2685
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2686
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2687
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2640
2688
  "input",
2641
2689
  {
2642
2690
  type: "number",
2643
2691
  min: 0,
2644
2692
  step: 1,
2645
2693
  "aria-label": "Corner radius",
2646
- className: numberClass9,
2694
+ className: numberClass10,
2647
2695
  value: Math.round(source.cornerRadius * 100) / 100,
2648
2696
  onChange: (e) => {
2649
2697
  const n = parseFloat(e.target.value);
@@ -2651,12 +2699,12 @@ function VectorStylePanel({ controller, className }) {
2651
2699
  }
2652
2700
  }
2653
2701
  ),
2654
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Corner kind", className: selectClass10, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector15.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: k, children: import_vector15.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2702
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("select", { "aria-label": "Corner kind", className: selectClass11, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector16.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: k, children: import_vector16.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2655
2703
  mixed("cornerRadius")
2656
2704
  ] }),
2657
- style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("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: [
2658
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Smoothing" }),
2659
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2705
+ style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("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: [
2706
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Smoothing" }),
2707
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2660
2708
  "input",
2661
2709
  {
2662
2710
  type: "range",
@@ -2671,9 +2719,9 @@ function VectorStylePanel({ controller, className }) {
2671
2719
  }
2672
2720
  )
2673
2721
  ] }),
2674
- style && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2675
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Opacity" }),
2676
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2722
+ style && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2723
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Opacity" }),
2724
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2677
2725
  "input",
2678
2726
  {
2679
2727
  type: "range",
@@ -2687,28 +2735,29 @@ function VectorStylePanel({ controller, className }) {
2687
2735
  }
2688
2736
  )
2689
2737
  ] }),
2690
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorTextPanel, { controller }),
2691
- style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorAppearancePanel, { controller }),
2692
- style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorEffectsPanel, { controller }),
2693
- style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorEnvelopePanel, { controller }),
2694
- style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorMaskPanel, { controller }),
2695
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorFramePanel, { controller }),
2696
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorConnectorPanel, { controller }),
2697
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorShadingPanel, { controller }),
2698
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorTracePanel, { controller }),
2699
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorConstraintsPanel, { controller }),
2700
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorSymbolsPanel, { controller })
2738
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorTextPanel, { controller }),
2739
+ style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorAppearancePanel, { controller }),
2740
+ style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorEffectsPanel, { controller }),
2741
+ style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorEnvelopePanel, { controller }),
2742
+ style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorMaskPanel, { controller }),
2743
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorFramePanel, { controller }),
2744
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorConnectorPanel, { controller }),
2745
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorShadingPanel, { controller }),
2746
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorSymmetryPanel, { controller }),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorTracePanel, { controller }),
2748
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorConstraintsPanel, { controller }),
2749
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorSymbolsPanel, { controller })
2701
2750
  ] });
2702
2751
  }
2703
2752
 
2704
2753
  // src/vector/VectorEditor.tsx
2705
- var import_jsx_runtime16 = require("react/jsx-runtime");
2754
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2706
2755
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
2707
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2708
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorToolbar, { controller, tools }),
2709
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2710
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
2711
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
2756
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2757
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VectorToolbar, { controller, tools }),
2758
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2759
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
2760
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
2712
2761
  ] })
2713
2762
  ] });
2714
2763
  }
@@ -2732,6 +2781,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
2732
2781
  VectorShadingPanel,
2733
2782
  VectorStylePanel,
2734
2783
  VectorSymbolsPanel,
2784
+ VectorSymmetryPanel,
2735
2785
  VectorTextElement,
2736
2786
  VectorTextPanel,
2737
2787
  VectorToolbar,