@pixerate/editor-react 0.31.0 → 0.33.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.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/vector/index.ts
@@ -22,7 +32,10 @@ var vector_exports = {};
22
32
  __export(vector_exports, {
23
33
  VectorCanvas: () => VectorCanvas,
24
34
  VectorEditor: () => VectorEditor,
35
+ VectorEffectsPanel: () => VectorEffectsPanel,
36
+ VectorGradientAnnotator: () => VectorGradientAnnotator,
25
37
  VectorGradientDefs: () => VectorGradientDefs,
38
+ VectorGradientEditor: () => VectorGradientEditor,
26
39
  VectorSceneNodes: () => VectorSceneNodes,
27
40
  VectorStylePanel: () => VectorStylePanel,
28
41
  VectorToolbar: () => VectorToolbar,
@@ -165,12 +178,50 @@ function VectorWidthPointEditor({ controller, edit, accentColor = import_vector2
165
178
 
166
179
  // src/vector/VectorCanvas.tsx
167
180
  var import_jsx_runtime2 = require("react/jsx-runtime");
181
+ function GradientElement({ def }) {
182
+ const stops = def.stops.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: s.offset, stopColor: s.stopColor, stopOpacity: s.stopOpacity }, i));
183
+ return def.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("radialGradient", { id: def.id, ...def.attrs, children: stops }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("linearGradient", { id: def.id, ...def.attrs, children: stops });
184
+ }
168
185
  function VectorGradientDefs({ defs }) {
169
186
  if (!defs.length) return null;
170
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: defs.map((def) => {
171
- const stops = def.stops.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: s.offset, stopColor: s.stopColor, stopOpacity: s.stopOpacity }, i));
172
- return def.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("radialGradient", { id: def.id, ...def.attrs, children: stops }, def.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("linearGradient", { id: def.id, ...def.attrs, children: stops }, def.id);
173
- }) });
187
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: defs.map(
188
+ (def) => def.type === "pattern" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("pattern", { id: def.id, ...def.attrs, children: [
189
+ def.gradients.map((g) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GradientElement, { def: g }, g.id)),
190
+ def.tiles.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: t.d, fill: t.fill }, i))
191
+ ] }, def.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GradientElement, { def }, def.id)
192
+ ) });
193
+ }
194
+ function VectorGradientAnnotator({ annotator: g, accentColor = import_vector3.VECTOR_OVERLAY_THEME.accent }) {
195
+ const r = import_vector3.VECTOR_OVERLAY_THEME.handleRadius + 1.5;
196
+ const ring = (p, part, size = r) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-gradient-handle": part, cx: p.x, cy: p.y, r: size + (p.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor, strokeWidth: p.hovered ? 2 : 1 });
197
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-gradient-annotator": g.type, children: [
198
+ g.extent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-gradient-extent": "", d: g.extent, fill: "none", stroke: accentColor, strokeDasharray: "4 3" }),
199
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: g.start.x, y1: g.start.y, x2: g.end.x, y2: g.end.y, stroke: "#ffffff", strokeWidth: 3 }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { "data-vector-gradient-bar": "", x1: g.start.x, y1: g.start.y, x2: g.end.x, y2: g.end.y, stroke: accentColor }),
201
+ g.aspect && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: g.start.x, y1: g.start.y, x2: g.aspect.x, y2: g.aspect.y, stroke: accentColor, strokeDasharray: "2 2" }),
202
+ ring(g.start, "start"),
203
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
204
+ "rect",
205
+ {
206
+ "data-vector-gradient-handle": g.end.handle,
207
+ x: g.end.x - r,
208
+ y: g.end.y - r,
209
+ width: 2 * r,
210
+ height: 2 * r,
211
+ fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill,
212
+ stroke: accentColor,
213
+ strokeWidth: g.end.hovered ? 2 : 1
214
+ }
215
+ ),
216
+ g.aspect && ring(g.aspect, "aspect", import_vector3.VECTOR_OVERLAY_THEME.handleRadius),
217
+ g.focal && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-gradient-handle": "focal", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: g.focal.x, cy: g.focal.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius - 1 + (g.focal.hovered ? 1 : 0), fill: accentColor, stroke: import_vector3.VECTOR_OVERLAY_THEME.handleFill }) }),
218
+ g.midpoints.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-gradient-midpoint": m.index, d: (0, import_vector3.vectorDiamondPath)(m.x, m.y, 4 + (m.hovered ? 1 : 0)), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, m.index)),
219
+ g.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-gradient-stop": s.index, "data-selected": s.selected || void 0, opacity: g.removing && s.selected ? 0.35 : 1, children: [
220
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: s.barX, y1: s.barY, x2: s.x, y2: s.y, stroke: accentColor }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: s.x - 5, y: s.y - 5, width: 10, height: 10, rx: 2, fill: s.color, fillOpacity: s.opacity, stroke: s.selected ? accentColor : "#ffffff", strokeWidth: s.selected || s.hovered ? 2 : 1 }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: s.x - 6.5, y: s.y - 6.5, width: 13, height: 13, rx: 3, fill: "none", stroke: s.selected ? "#ffffff" : accentColor })
223
+ ] }, s.index))
224
+ ] });
174
225
  }
175
226
  function VectorSceneNodes({ nodes }) {
176
227
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map(
@@ -394,6 +445,7 @@ function VectorCanvas({
394
445
  },
395
446
  `blend-anchor:${i}`
396
447
  )),
448
+ overlay.gradient && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorGradientAnnotator, { annotator: overlay.gradient, accentColor }),
397
449
  overlay.widthSpot && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-width-spot": "", children: [
398
450
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: overlay.widthSpot.left.x, y1: overlay.widthSpot.left.y, x2: overlay.widthSpot.right.x, y2: overlay.widthSpot.right.y, stroke: accentColor, strokeDasharray: "3 2" }),
399
451
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: (0, import_vector3.vectorDiamondPath)(overlay.widthSpot.x, overlay.widthSpot.y), fill: "none", stroke: accentColor })
@@ -499,14 +551,326 @@ function VectorCanvas({
499
551
  }
500
552
 
501
553
  // src/vector/VectorToolbar.tsx
554
+ var import_react4 = __toESM(require("react"), 1);
555
+ var import_vector6 = require("@pixerate/editor/vector");
556
+
557
+ // src/vector/VectorEffectsPanel.tsx
502
558
  var import_vector4 = require("@pixerate/editor/vector");
503
559
  var import_jsx_runtime3 = require("react/jsx-runtime");
560
+ 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";
561
+ var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
562
+ var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
563
+ var labelClass = "w-full truncate text-[11px] text-muted-foreground";
564
+ function VectorEffectsPanel({ controller, className }) {
565
+ const state = useVectorEditorState(controller);
566
+ const panel = (0, import_vector4.getVectorEffectsPanel)(state);
567
+ if (!panel) return null;
568
+ const nodeId = panel.nodeId;
569
+ const end = () => controller.endMergedEdit();
570
+ const set = (e, key, value, merge) => {
571
+ if (nodeId) controller.updateEffect(nodeId, e.id, { params: { [key]: value } }, { merge });
572
+ };
573
+ const number = (e, spec) => {
574
+ const value = Number(e.params[spec.key] ?? 0);
575
+ const label = `${e.label} ${spec.label}`;
576
+ const onChange = (ev) => {
577
+ const n = parseFloat(ev.target.value);
578
+ if (Number.isFinite(n)) set(e, spec.key, n, true);
579
+ };
580
+ 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: [
581
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: labelClass, children: spec.label }),
582
+ spec.slider && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
583
+ "input",
584
+ {
585
+ type: "range",
586
+ min: spec.min,
587
+ max: spec.max,
588
+ step: spec.step,
589
+ "aria-label": `${label} slider`,
590
+ className: "h-4 min-w-0 flex-1 accent-primary",
591
+ value,
592
+ onChange,
593
+ onPointerUp: end,
594
+ onKeyUp: end,
595
+ onBlur: end
596
+ }
597
+ ),
598
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
599
+ "input",
600
+ {
601
+ type: "number",
602
+ min: spec.min,
603
+ max: spec.max,
604
+ step: spec.step,
605
+ "aria-label": label,
606
+ className: numberClass,
607
+ value: Math.round(value * 1e3) / 1e3,
608
+ onChange,
609
+ onBlur: end
610
+ }
611
+ )
612
+ ] }, spec.key);
613
+ };
614
+ const vec2 = (e, spec) => {
615
+ const v = e.params[spec.key] ?? { x: 0, y: 0 };
616
+ const field = (axis) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
617
+ "input",
618
+ {
619
+ type: "number",
620
+ step: spec.kind === "vec2" ? spec.step : 1,
621
+ "aria-label": `${e.label} ${spec.label} ${axis}`,
622
+ className: numberClass,
623
+ value: Math.round(v[axis] * 1e3) / 1e3,
624
+ onChange: (ev) => {
625
+ const n = parseFloat(ev.target.value);
626
+ if (Number.isFinite(n)) set(e, spec.key, { ...v, [axis]: n }, true);
627
+ },
628
+ onBlur: end
629
+ }
630
+ );
631
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-effect-param": spec.key, children: [
632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: labelClass, children: spec.label }),
633
+ field("x"),
634
+ field("y")
635
+ ] }, spec.key);
636
+ };
637
+ const param = (e, spec) => {
638
+ if (spec.kind === "number") return number(e, spec);
639
+ if (spec.kind === "vec2") return vec2(e, spec);
640
+ if (spec.kind === "boolean")
641
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-effect-param": spec.key, children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "min-w-0 flex-1 truncate", children: spec.label }),
643
+ /* @__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) })
644
+ ] }, spec.key);
645
+ 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: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: labelClass, children: spec.label }),
647
+ /* @__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)) })
648
+ ] }, spec.key);
649
+ };
650
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-effects": "", "aria-label": "Effects", children: [
651
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
652
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Effects" }),
653
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
654
+ "select",
655
+ {
656
+ "aria-label": "Add effect",
657
+ className: selectClass,
658
+ value: "",
659
+ onChange: (ev) => {
660
+ if (ev.target.value) controller.addEffect(ev.target.value);
661
+ },
662
+ children: [
663
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "Add effect\u2026" }),
664
+ import_vector4.VECTOR_EFFECT_MENU.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: m.type, children: m.label }, m.type))
665
+ ]
666
+ }
667
+ ),
668
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: buttonClass, title: "Expand Appearance", "aria-label": "Expand Appearance", disabled: !panel.expandable, onClick: () => controller.expandAppearance(), children: "Expand" })
669
+ ] }),
670
+ panel.count > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Select one object to edit its effects." }),
671
+ nodeId && panel.effects.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-1 rounded border border-border p-1.5", "data-vector-effect": e.id, "data-vector-effect-type": e.type, children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-1", children: [
673
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-xs font-medium text-foreground", children: [
674
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", "aria-label": `${e.label} on`, checked: e.enabled, onChange: (ev) => controller.updateEffect(nodeId, e.id, { enabled: ev.target.checked }) }),
675
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate", children: e.label })
676
+ ] }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: buttonClass, title: "Move up", "aria-label": `Move ${e.label} up`, disabled: i === 0, onClick: () => controller.moveEffect(nodeId, e.id, i - 1), children: "\u2191" }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
679
+ "button",
680
+ {
681
+ type: "button",
682
+ className: buttonClass,
683
+ title: "Move down",
684
+ "aria-label": `Move ${e.label} down`,
685
+ disabled: i === panel.effects.length - 1,
686
+ onClick: () => controller.moveEffect(nodeId, e.id, i + 1),
687
+ children: "\u2193"
688
+ }
689
+ ),
690
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: buttonClass, title: "Remove", "aria-label": `Remove ${e.label}`, onClick: () => controller.removeEffect(nodeId, e.id), children: "\xD7" })
691
+ ] }),
692
+ e.specs.map((spec) => param(e, spec))
693
+ ] }, e.id))
694
+ ] });
695
+ }
696
+
697
+ // src/vector/VectorGradientEditor.tsx
698
+ var import_vector5 = require("@pixerate/editor/vector");
699
+ var import_jsx_runtime4 = require("react/jsx-runtime");
700
+ var selectClass2 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
701
+ var numberClass2 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
702
+ var buttonClass2 = "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";
703
+ var percent = (v) => Math.round(v * 1e3) / 10;
704
+ function VectorGradientEditor({ controller, className }) {
705
+ const state = useVectorEditorState(controller);
706
+ const ed = (0, import_vector5.getVectorGradientEditor)(state);
707
+ if (!ed) return null;
708
+ const g = ed.gradient;
709
+ const sel = ed.selected;
710
+ const end = () => controller.endMergedEdit();
711
+ const setType = (value) => {
712
+ if (value === "solid") {
713
+ const first = ed.stops[0];
714
+ controller.setStyle({ [ed.target]: (0, import_vector5.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
715
+ } else controller.setGradient({ type: value });
716
+ };
717
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
718
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1.5", children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
720
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
721
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
722
+ ] }),
723
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1.5", children: [
724
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
725
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { "aria-label": "Gradient type", className: selectClass2, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "solid", children: "Solid" }),
727
+ import_vector5.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: t, children: import_vector5.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
728
+ ] }),
729
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
730
+ ] }),
731
+ g && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
734
+ "div",
735
+ {
736
+ role: "button",
737
+ tabIndex: -1,
738
+ "aria-label": "Add gradient stop",
739
+ className: "absolute inset-x-1.5 top-0 h-4 cursor-copy rounded border border-border",
740
+ style: { backgroundImage: `${ed.preview}, repeating-conic-gradient(#ddd 0% 25%, #fff 0% 50%)`, backgroundSize: "auto, 8px 8px" },
741
+ onClick: (e) => {
742
+ const r = e.currentTarget.getBoundingClientRect();
743
+ controller.addGradientStop(r.width > 0 ? Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) : 0.5);
744
+ }
745
+ }
746
+ ),
747
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
748
+ "button",
749
+ {
750
+ type: "button",
751
+ "data-vector-gradient-stop": s.index,
752
+ "aria-label": `Gradient stop ${s.index + 1} at ${percent(s.offset)}%`,
753
+ "aria-pressed": s.selected,
754
+ className: "absolute top-4 h-3.5 w-3 -translate-x-1/2 rounded-sm border border-border aria-pressed:ring-2 aria-pressed:ring-primary",
755
+ style: { left: `calc(6px + (100% - 12px) * ${s.offset})`, background: s.color, opacity: 0.4 + 0.6 * (s.opacity ?? 1) },
756
+ onClick: () => controller.selectGradientStop(s.selected ? null : s.index)
757
+ },
758
+ s.index
759
+ ))
760
+ ] }),
761
+ sel && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
763
+ "input",
764
+ {
765
+ type: "color",
766
+ "aria-label": "Stop colour",
767
+ className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
768
+ value: (0, import_vector5.vectorPaintToHex)(sel.color) ?? "#000000",
769
+ onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
770
+ onBlur: end
771
+ }
772
+ ),
773
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
774
+ "Opacity",
775
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
776
+ "input",
777
+ {
778
+ type: "number",
779
+ min: 0,
780
+ max: 100,
781
+ step: 5,
782
+ "aria-label": "Stop opacity",
783
+ className: numberClass2,
784
+ value: percent(sel.opacity ?? 1),
785
+ onChange: (e) => {
786
+ const n = parseFloat(e.target.value);
787
+ if (Number.isFinite(n)) controller.setGradientStop(sel.index, { opacity: n / 100 });
788
+ }
789
+ }
790
+ )
791
+ ] }),
792
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
793
+ "At",
794
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
795
+ "input",
796
+ {
797
+ type: "number",
798
+ min: 0,
799
+ max: 100,
800
+ step: 1,
801
+ "aria-label": "Stop position",
802
+ className: numberClass2,
803
+ value: percent(sel.offset),
804
+ onChange: (e) => {
805
+ const n = parseFloat(e.target.value);
806
+ if (Number.isFinite(n)) controller.setGradientStop(sel.index, { offset: n / 100 });
807
+ }
808
+ }
809
+ )
810
+ ] }),
811
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
812
+ "Midpoint",
813
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
814
+ "input",
815
+ {
816
+ type: "number",
817
+ min: 5,
818
+ max: 95,
819
+ step: 1,
820
+ "aria-label": "Stop midpoint",
821
+ className: numberClass2,
822
+ value: percent(sel.midpoint ?? 0.5),
823
+ onChange: (e) => {
824
+ const n = parseFloat(e.target.value);
825
+ if (Number.isFinite(n)) controller.setGradientStop(sel.index, { midpoint: n / 100 });
826
+ }
827
+ }
828
+ )
829
+ ] }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
831
+ ] }),
832
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
833
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16", children: "Colours" }),
834
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass2, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector5.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: i, children: import_vector5.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
835
+ ] }),
836
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16", children: "Smooth" }),
838
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
839
+ "input",
840
+ {
841
+ type: "range",
842
+ min: 0,
843
+ max: 1,
844
+ step: 0.05,
845
+ "aria-label": "Gradient smoothness",
846
+ className: "h-4 flex-1 accent-primary",
847
+ value: ed.smoothness,
848
+ onChange: (e) => controller.setGradient({ smoothness: parseFloat(e.target.value) }, { merge: "gradient-smoothness" }),
849
+ onPointerUp: end,
850
+ onBlur: end
851
+ }
852
+ )
853
+ ] }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16", children: "Spread" }),
856
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Gradient spread", className: selectClass2, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector5.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: sp, children: import_vector5.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
857
+ ] }),
858
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
859
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16", children: "On stroke" }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass2, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector5.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m, children: import_vector5.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
861
+ ] })
862
+ ] })
863
+ ] });
864
+ }
865
+
866
+ // src/vector/VectorToolbar.tsx
867
+ var import_jsx_runtime5 = require("react/jsx-runtime");
504
868
  var VECTOR_SNAP_TITLE = "Snap to objects";
505
869
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
506
- var buttonClass = "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";
870
+ var buttonClass3 = "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";
507
871
  function VectorToolbar({
508
872
  controller,
509
- tools = import_vector4.VECTOR_TOOLS,
873
+ tools = import_vector6.VECTOR_TOOLS,
510
874
  showHistory = true,
511
875
  showArrange = true,
512
876
  showSnap = true,
@@ -519,32 +883,34 @@ function VectorToolbar({
519
883
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
520
884
  const editing = state.editing;
521
885
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
522
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: buttonClass, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
523
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
524
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
886
+ const [live, setLive] = import_react4.default.useState(false);
887
+ const booleanDisabled = live ? count < 1 : count < 2;
888
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: buttonClass3, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
889
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
525
891
  "button",
526
892
  {
527
893
  type: "button",
528
894
  "data-vector-tool": tool,
529
- className: buttonClass,
895
+ className: buttonClass3,
530
896
  "aria-pressed": state.tool === tool,
531
- title: `${(0, import_vector4.vectorToolLabel)(tool)} (${import_vector4.VECTOR_TOOL_SHORTCUTS[tool]})`,
532
- "aria-label": (0, import_vector4.vectorToolLabel)(tool),
897
+ title: `${(0, import_vector6.vectorToolLabel)(tool)} (${import_vector6.VECTOR_TOOL_SHORTCUTS[tool]})`,
898
+ "aria-label": (0, import_vector6.vectorToolLabel)(tool),
533
899
  onClick: () => controller.setTool(tool),
534
- children: import_vector4.VECTOR_TOOL_GLYPHS[tool]
900
+ children: import_vector6.VECTOR_TOOL_GLYPHS[tool]
535
901
  },
536
902
  tool
537
903
  )) }),
538
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
904
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
539
905
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
540
906
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
541
907
  ] }),
542
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
908
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
543
909
  "button",
544
910
  {
545
911
  type: "button",
546
912
  "data-vector-snap": "",
547
- className: buttonClass,
913
+ className: buttonClass3,
548
914
  "aria-pressed": state.snap.objects,
549
915
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
550
916
  "aria-label": VECTOR_SNAP_TITLE,
@@ -552,13 +918,13 @@ function VectorToolbar({
552
918
  children: "Snap"
553
919
  }
554
920
  ) }),
555
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
556
- import_vector4.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
921
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
922
+ import_vector6.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
557
923
  "button",
558
924
  {
559
925
  type: "button",
560
926
  "data-vector-path-action": a.id,
561
- className: buttonClass,
927
+ className: buttonClass3,
562
928
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
563
929
  "aria-label": a.title,
564
930
  disabled: !a.enabled(count, points),
@@ -567,12 +933,12 @@ function VectorToolbar({
567
933
  },
568
934
  a.id
569
935
  )),
570
- import_vector4.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
936
+ import_vector6.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
571
937
  "button",
572
938
  {
573
939
  type: "button",
574
940
  "data-vector-edit": t.key,
575
- className: buttonClass,
941
+ className: buttonClass3,
576
942
  "aria-pressed": editing[t.key],
577
943
  title: t.title,
578
944
  "aria-label": t.title,
@@ -581,9 +947,9 @@ function VectorToolbar({
581
947
  },
582
948
  t.key
583
949
  )),
584
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
950
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
585
951
  "Smoothing",
586
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
952
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
587
953
  "input",
588
954
  {
589
955
  type: "range",
@@ -597,9 +963,9 @@ function VectorToolbar({
597
963
  }
598
964
  )
599
965
  ] }),
600
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
966
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
601
967
  "Size",
602
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
968
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
603
969
  "input",
604
970
  {
605
971
  type: "number",
@@ -615,36 +981,49 @@ function VectorToolbar({
615
981
  }
616
982
  )
617
983
  ] }),
618
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
984
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
619
985
  "button",
620
986
  {
621
987
  type: "button",
622
988
  "data-vector-fill-gap": "",
623
- className: buttonClass,
989
+ className: buttonClass3,
624
990
  "aria-pressed": editing.fillGap > 0,
625
991
  title: "Close small gaps between path ends when filling",
626
992
  "aria-label": "Close gaps",
627
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector4.VECTOR_FILL_GAP }),
993
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector6.VECTOR_FILL_GAP }),
628
994
  children: "Gaps"
629
995
  }
630
996
  )
631
997
  ] }),
632
- showBlend && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VectorBlendControls, { controller }),
633
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
634
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
635
- act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
636
- act("\u2212", "Subtract", () => controller.booleanOp("subtract"), count < 2),
637
- act("\u2229", "Intersect", () => controller.booleanOp("intersect"), count < 2),
638
- act("\u22BB", "Exclude", () => controller.booleanOp("exclude"), count < 2),
998
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VectorBlendControls, { controller }),
999
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1000
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1002
+ "button",
1003
+ {
1004
+ type: "button",
1005
+ "data-vector-live-boolean": "",
1006
+ className: buttonClass3,
1007
+ "aria-pressed": live,
1008
+ title: "Live booleans: keep the shapes editable",
1009
+ "aria-label": "Live booleans",
1010
+ onClick: () => setLive(!live),
1011
+ children: "Live"
1012
+ }
1013
+ ),
1014
+ act("\u222A", "Union", () => controller.booleanOp("union", { live }), booleanDisabled),
1015
+ act("\u2212", "Subtract", () => controller.booleanOp("subtract", { live }), booleanDisabled),
1016
+ act("\u2229", "Intersect", () => controller.booleanOp("intersect", { live }), booleanDisabled),
1017
+ act("\u22BB", "Exclude", () => controller.booleanOp("exclude", { live }), booleanDisabled),
639
1018
  act("G", "Group", () => controller.group(), count < 1),
640
1019
  act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
641
1020
  ] }),
642
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector4.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1021
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector6.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
643
1022
  "button",
644
1023
  {
645
1024
  type: "button",
646
1025
  "data-vector-pathfinder": a.id,
647
- className: buttonClass,
1026
+ className: buttonClass3,
648
1027
  title: a.title,
649
1028
  "aria-label": a.title,
650
1029
  disabled: count < a.min,
@@ -653,7 +1032,7 @@ function VectorToolbar({
653
1032
  },
654
1033
  a.id
655
1034
  )) }),
656
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
1035
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
657
1036
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
658
1037
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
659
1038
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -668,22 +1047,22 @@ function VectorToolbar({
668
1047
  }
669
1048
  function VectorBlendControls({ controller }) {
670
1049
  const state = useVectorEditorState(controller);
671
- const blend = (0, import_vector4.getVectorBlendControls)(state);
1050
+ const blend = (0, import_vector6.getVectorBlendControls)(state);
672
1051
  const o = blend.options;
673
1052
  const selected = blend.ids.length > 0;
674
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
675
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
676
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1053
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
1054
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass3, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
1055
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
677
1056
  "select",
678
1057
  {
679
1058
  "aria-label": "Blend spacing",
680
1059
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
681
1060
  value: o.spacing,
682
- onChange: (e) => (0, import_vector4.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
683
- children: import_vector4.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: sp, children: import_vector4.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
1061
+ onChange: (e) => (0, import_vector6.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
1062
+ children: import_vector6.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: sp, children: import_vector6.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
684
1063
  }
685
1064
  ),
686
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1065
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
687
1066
  "input",
688
1067
  {
689
1068
  type: "number",
@@ -695,29 +1074,29 @@ function VectorBlendControls({ controller }) {
695
1074
  onChange: (e) => {
696
1075
  const n = parseFloat(e.target.value);
697
1076
  if (!Number.isFinite(n) || n <= 0) return;
698
- (0, import_vector4.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
1077
+ (0, import_vector6.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
699
1078
  }
700
1079
  }
701
1080
  ),
702
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1081
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
703
1082
  "button",
704
1083
  {
705
1084
  type: "button",
706
1085
  "data-vector-blend-action": "orient",
707
- className: buttonClass,
1086
+ className: buttonClass3,
708
1087
  "aria-pressed": o.orientToSpine,
709
1088
  title: "Orient steps to the spine",
710
1089
  "aria-label": "Orient to spine",
711
- onClick: () => (0, import_vector4.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
1090
+ onClick: () => (0, import_vector6.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
712
1091
  children: "\u293F"
713
1092
  }
714
1093
  ),
715
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1094
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
716
1095
  "button",
717
1096
  {
718
1097
  type: "button",
719
1098
  "data-vector-blend-action": "spine",
720
- className: buttonClass,
1099
+ className: buttonClass3,
721
1100
  title: "Replace Spine (select a blend and a path)",
722
1101
  "aria-label": "Replace Spine",
723
1102
  disabled: !blend.canReplaceSpine,
@@ -725,15 +1104,15 @@ function VectorBlendControls({ controller }) {
725
1104
  children: "\u2307"
726
1105
  }
727
1106
  ),
728
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
729
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
1107
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass3, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
1108
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass3, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
730
1109
  ] });
731
1110
  }
732
- var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
733
- var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1111
+ var selectClass3 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1112
+ var numberClass3 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
734
1113
  function VectorStylePanel({ controller, className }) {
735
1114
  const state = useVectorEditorState(controller);
736
- const style = (0, import_vector4.getVectorSelectionStyle)(state);
1115
+ const style = (0, import_vector6.getVectorSelectionStyle)(state);
737
1116
  const d = state.defaults;
738
1117
  const source = style ?? {
739
1118
  fill: d.fill ?? null,
@@ -746,7 +1125,7 @@ function VectorStylePanel({ controller, className }) {
746
1125
  arrowAlign: d.arrowAlign ?? "extend",
747
1126
  startArrowScale: d.startArrowScale ?? 1,
748
1127
  endArrowScale: d.endArrowScale ?? 1,
749
- widthProfile: (0, import_vector4.widthProfilePresetId)(d.strokeWidthProfile),
1128
+ widthProfile: (0, import_vector6.widthProfilePresetId)(d.strokeWidthProfile),
750
1129
  cornerRadius: d.cornerRadius,
751
1130
  cornerKind: d.cornerKind ?? "round",
752
1131
  corners: 1,
@@ -759,19 +1138,19 @@ function VectorStylePanel({ controller, className }) {
759
1138
  };
760
1139
  const setProfile = (id) => {
761
1140
  if (style) controller.setWidthProfile(id);
762
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector4.widthProfilePreset)(id)?.length ? (0, import_vector4.widthProfilePreset)(id) : null });
1141
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector6.widthProfilePreset)(id)?.length ? (0, import_vector6.widthProfilePreset)(id) : null });
763
1142
  };
764
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
1143
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
765
1144
  const arrowRow = (end, label) => {
766
1145
  const key = end === "start" ? "startArrow" : "endArrow";
767
1146
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
768
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
769
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
770
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
771
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "None" }),
772
- import_vector4.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: h, children: import_vector4.VECTOR_ARROWHEAD_LABELS[h] }, h))
1147
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1149
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass3, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
1150
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "", children: "None" }),
1151
+ import_vector6.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: h, children: import_vector6.VECTOR_ARROWHEAD_LABELS[h] }, h))
773
1152
  ] }),
774
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1153
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
775
1154
  "input",
776
1155
  {
777
1156
  type: "number",
@@ -779,7 +1158,7 @@ function VectorStylePanel({ controller, className }) {
779
1158
  step: 0.25,
780
1159
  "aria-label": `${label} arrowhead scale`,
781
1160
  title: "Scale (\xD7 stroke width)",
782
- className: numberClass,
1161
+ className: numberClass3,
783
1162
  value: source[scaleKey],
784
1163
  onChange: (e) => {
785
1164
  const n = parseFloat(e.target.value);
@@ -795,14 +1174,14 @@ function VectorStylePanel({ controller, className }) {
795
1174
  };
796
1175
  const paintRow = (key, label) => {
797
1176
  const paint = source[key];
798
- const hex = (0, import_vector4.vectorPaintToHex)(paint);
1177
+ const hex = (0, import_vector6.vectorPaintToHex)(paint);
799
1178
  const on = paint !== null && paint !== "none";
800
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
801
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
802
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1180
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1181
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
803
1182
  label
804
1183
  ] }),
805
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1184
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
806
1185
  "input",
807
1186
  {
808
1187
  type: "color",
@@ -813,17 +1192,18 @@ function VectorStylePanel({ controller, className }) {
813
1192
  onChange: (e) => apply({ [key]: e.target.value })
814
1193
  }
815
1194
  ),
816
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
817
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1195
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
1196
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
818
1197
  ] });
819
1198
  };
820
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
821
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
1199
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
822
1201
  paintRow("fill", "Fill"),
823
1202
  paintRow("stroke", "Stroke"),
824
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
825
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Width" }),
826
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1203
+ style && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VectorGradientEditor, { controller }),
1204
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1205
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Width" }),
1206
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
827
1207
  "input",
828
1208
  {
829
1209
  type: "number",
@@ -839,33 +1219,33 @@ function VectorStylePanel({ controller, className }) {
839
1219
  }
840
1220
  )
841
1221
  ] }),
842
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Align" }),
844
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector4.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: a, children: import_vector4.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
1222
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
1223
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Align" }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass3, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector6.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: a, children: import_vector6.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
845
1225
  mixed("strokeAlign")
846
1226
  ] }),
847
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
848
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Profile" }),
849
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1227
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
1228
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Profile" }),
1229
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
850
1230
  "select",
851
1231
  {
852
1232
  "aria-label": "Width profile",
853
- className: selectClass,
1233
+ className: selectClass3,
854
1234
  value: source.widthProfile,
855
1235
  onChange: (e) => {
856
1236
  if (e.target.value !== "custom") setProfile(e.target.value);
857
1237
  },
858
1238
  children: [
859
- import_vector4.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: p.id, children: p.label }, p.id)),
860
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
1239
+ import_vector6.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: p.id, children: p.label }, p.id)),
1240
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
861
1241
  ]
862
1242
  }
863
1243
  ),
864
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1244
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
865
1245
  "button",
866
1246
  {
867
1247
  type: "button",
868
- className: buttonClass,
1248
+ className: buttonClass3,
869
1249
  title: "Reset to uniform width",
870
1250
  "aria-label": "Reset to uniform width",
871
1251
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -875,27 +1255,27 @@ function VectorStylePanel({ controller, className }) {
875
1255
  ),
876
1256
  mixed("widthProfile")
877
1257
  ] }),
878
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1258
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
879
1259
  arrowRow("start", "Start"),
880
1260
  arrowRow("end", "End"),
881
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
882
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Heads" }),
883
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
884
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "extend", children: "Tip past end" }),
885
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "tip", children: "Tip at end" })
1261
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Heads" }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass3, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
1264
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "extend", children: "Tip past end" }),
1265
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "tip", children: "Tip at end" })
886
1266
  ] })
887
1267
  ] })
888
1268
  ] }),
889
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
890
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
891
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1269
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
1270
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
892
1272
  "input",
893
1273
  {
894
1274
  type: "number",
895
1275
  min: 0,
896
1276
  step: 1,
897
1277
  "aria-label": "Corner radius",
898
- className: numberClass,
1278
+ className: numberClass3,
899
1279
  value: Math.round(source.cornerRadius * 100) / 100,
900
1280
  onChange: (e) => {
901
1281
  const n = parseFloat(e.target.value);
@@ -903,12 +1283,12 @@ function VectorStylePanel({ controller, className }) {
903
1283
  }
904
1284
  }
905
1285
  ),
906
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { "aria-label": "Corner kind", className: selectClass, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector4.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: k, children: import_vector4.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { "aria-label": "Corner kind", className: selectClass3, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector6.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: k, children: import_vector6.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
907
1287
  mixed("cornerRadius")
908
1288
  ] }),
909
- style && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
910
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Opacity" }),
911
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1289
+ style && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Opacity" }),
1291
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
912
1292
  "input",
913
1293
  {
914
1294
  type: "range",
@@ -921,18 +1301,19 @@ function VectorStylePanel({ controller, className }) {
921
1301
  onChange: (e) => apply({ opacity: parseFloat(e.target.value) })
922
1302
  }
923
1303
  )
924
- ] })
1304
+ ] }),
1305
+ style && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VectorEffectsPanel, { controller })
925
1306
  ] });
926
1307
  }
927
1308
 
928
1309
  // src/vector/VectorEditor.tsx
929
- var import_jsx_runtime4 = require("react/jsx-runtime");
1310
+ var import_jsx_runtime6 = require("react/jsx-runtime");
930
1311
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
931
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
932
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorToolbar, { controller, tools }),
933
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
934
- /* @__PURE__ */ (0, import_jsx_runtime4.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_runtime4.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
935
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
1312
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
1313
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VectorToolbar, { controller, tools }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime6.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_runtime6.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
1316
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
936
1317
  ] })
937
1318
  ] });
938
1319
  }
@@ -940,7 +1321,10 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
940
1321
  0 && (module.exports = {
941
1322
  VectorCanvas,
942
1323
  VectorEditor,
1324
+ VectorEffectsPanel,
1325
+ VectorGradientAnnotator,
943
1326
  VectorGradientDefs,
1327
+ VectorGradientEditor,
944
1328
  VectorSceneNodes,
945
1329
  VectorStylePanel,
946
1330
  VectorToolbar,