@pixerate/editor-react 0.66.0 → 0.67.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.
@@ -30,10 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/vector/index.ts
31
31
  var vector_exports = {};
32
32
  __export(vector_exports, {
33
+ VectorAppearancePanel: () => VectorAppearancePanel,
33
34
  VectorCanvas: () => VectorCanvas,
34
35
  VectorEditor: () => VectorEditor,
35
36
  VectorEffectsPanel: () => VectorEffectsPanel,
36
37
  VectorEnvelopePanel: () => VectorEnvelopePanel,
38
+ VectorFilterDefs: () => VectorFilterDefs,
37
39
  VectorGradientAnnotator: () => VectorGradientAnnotator,
38
40
  VectorGradientDefs: () => VectorGradientDefs,
39
41
  VectorGradientEditor: () => VectorGradientEditor,
@@ -91,7 +93,7 @@ function useVectorCommands(registry, controller, options = {}) {
91
93
  }
92
94
 
93
95
  // src/vector/VectorCanvas.tsx
94
- var import_react3 = require("react");
96
+ var import_react3 = __toESM(require("react"), 1);
95
97
  var import_vector3 = require("@pixerate/editor/vector");
96
98
 
97
99
  // src/vector/VectorWidthPointEditor.tsx
@@ -224,37 +226,95 @@ function VectorGradientAnnotator({ annotator: g, accentColor = import_vector3.VE
224
226
  ] }, s.index))
225
227
  ] });
226
228
  }
229
+ function VectorFilterDefs({ filters }) {
230
+ if (!filters.length) return null;
231
+ const primitive = (p, key) => import_react3.default.createElement(p.tag, { key, ...p.attrs }, p.children?.map(primitive));
232
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: filters.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("filter", { id: f.id, filterUnits: "userSpaceOnUse", x: f.x, y: f.y, width: f.width, height: f.height, colorInterpolationFilters: "sRGB", children: f.primitives.map(primitive) }, f.id)) });
233
+ }
234
+ var blendStyle = (blendMode) => blendMode ? { mixBlendMode: blendMode } : void 0;
227
235
  function VectorSceneNodes({ nodes }) {
228
236
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map(
229
- (node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children }) }, node.id) : node.strokeParts ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPartedPath, { node }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
230
- "path",
237
+ (node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
238
+ "g",
231
239
  {
232
240
  "data-vector-id": node.id,
233
241
  transform: node.transform,
242
+ opacity: node.opacity < 1 ? node.opacity : void 0,
243
+ filter: node.filter,
244
+ style: blendStyle(node.blendMode),
245
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children })
246
+ },
247
+ node.id
248
+ ) : node.layers ? (
249
+ // Fill and stroke stacks: each layer in order, bottom to top.
250
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
251
+ "g",
252
+ {
253
+ "data-vector-id": node.id,
254
+ transform: node.transform,
255
+ opacity: node.opacity < 1 ? node.opacity : void 0,
256
+ filter: node.filter,
257
+ style: blendStyle(node.blendMode),
258
+ children: node.layers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`))
259
+ },
260
+ node.id
261
+ )
262
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
263
+ VectorPaintPath,
264
+ {
265
+ paint: node,
234
266
  d: node.d,
235
- fill: node.fill,
236
267
  fillRule: node.fillRule,
237
- stroke: node.stroke,
238
- strokeWidth: node.stroke === "none" ? void 0 : node.strokeWidth,
239
- strokeLinecap: node.strokeLinecap,
240
- strokeLinejoin: node.strokeLinejoin,
241
- strokeDasharray: node.strokeDasharray,
242
- opacity: node.opacity < 1 ? node.opacity : void 0
268
+ id: node.id,
269
+ transform: node.transform,
270
+ filter: node.filter,
271
+ blendMode: node.blendMode
243
272
  },
244
273
  node.id
245
274
  )
246
275
  ) });
247
276
  }
248
- function VectorPartedPath({ node }) {
249
- const sp = node.strokeParts;
277
+ function VectorPaintPath({
278
+ paint,
279
+ d,
280
+ fillRule,
281
+ id,
282
+ transform,
283
+ filter,
284
+ blendMode,
285
+ layer
286
+ }) {
287
+ const opacity = paint.opacity < 1 ? paint.opacity : void 0;
288
+ const style = blendStyle(blendMode ?? (layer ? paint.blendMode : void 0));
289
+ const sp = paint.strokeParts;
290
+ if (!sp)
291
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
292
+ "path",
293
+ {
294
+ "data-vector-id": id,
295
+ "data-vector-layer": layer,
296
+ transform,
297
+ d,
298
+ fill: paint.fill,
299
+ fillRule,
300
+ stroke: paint.stroke,
301
+ strokeWidth: paint.stroke === "none" ? void 0 : paint.strokeWidth,
302
+ strokeLinecap: paint.strokeLinecap,
303
+ strokeLinejoin: paint.strokeLinejoin,
304
+ strokeDasharray: paint.strokeDasharray,
305
+ opacity,
306
+ filter,
307
+ style
308
+ }
309
+ );
250
310
  const clip = sp.clip;
251
311
  const b = clip?.bounds;
252
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-id": node.id, "data-vector-stroke-align": sp.align, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: [
253
- clip && b && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: clip.id, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: node.d, clipRule: node.fillRule }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
312
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-id": id, "data-vector-layer": layer, "data-vector-stroke-align": sp.align, transform, opacity, filter, style, children: [
313
+ clip && b && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: clip.id, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, clipRule: fillRule }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
254
314
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: b.x, y: b.y, width: b.width, height: b.height, fill: "#fff" }),
255
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: node.d, fill: "#000", fillRule: node.fillRule })
315
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, fill: "#000", fillRule })
256
316
  ] }) }),
257
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "fill", d: node.d, fill: node.fill, fillRule: node.fillRule, stroke: "none" }),
317
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "fill", d, fill: paint.fill, fillRule, stroke: "none" }),
258
318
  sp.outline !== void 0 ? (
259
319
  // A variable-width stroke: its outline, filled.
260
320
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -262,7 +322,7 @@ function VectorPartedPath({ node }) {
262
322
  {
263
323
  "data-vector-part": "stroke-outline",
264
324
  d: sp.outline,
265
- fill: node.stroke,
325
+ fill: paint.stroke,
266
326
  clipPath: clip?.kind === "clip" ? `url(#${clip.id})` : void 0,
267
327
  mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
268
328
  }
@@ -273,16 +333,16 @@ function VectorPartedPath({ node }) {
273
333
  "data-vector-part": "stroke",
274
334
  d: sp.d,
275
335
  fill: "none",
276
- stroke: node.stroke,
336
+ stroke: paint.stroke,
277
337
  strokeWidth: sp.width,
278
- strokeLinecap: node.strokeLinecap,
279
- strokeLinejoin: node.strokeLinejoin,
280
- strokeDasharray: node.strokeDasharray,
338
+ strokeLinecap: paint.strokeLinecap,
339
+ strokeLinejoin: paint.strokeLinejoin,
340
+ strokeDasharray: paint.strokeDasharray,
281
341
  clipPath: clip?.kind === "clip" ? `url(#${clip.id})` : void 0,
282
342
  mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
283
343
  }
284
344
  ),
285
- sp.heads.map((head, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "head", d: head, fill: node.stroke }, i))
345
+ sp.heads.map((head, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "head", d: head, fill: paint.stroke }, i))
286
346
  ] });
287
347
  }
288
348
  function VectorCanvas({
@@ -388,6 +448,7 @@ function VectorCanvas({
388
448
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: 8, y: 8, width: 8, height: 8, fill: "#e5e7eb" })
389
449
  ] }) }),
390
450
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorGradientDefs, { defs: scene.defs }),
451
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorFilterDefs, { filters: scene.filters }),
391
452
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
392
453
  "rect",
393
454
  {
@@ -568,7 +629,7 @@ function VectorCanvas({
568
629
 
569
630
  // src/vector/VectorToolbar.tsx
570
631
  var import_react5 = __toESM(require("react"), 1);
571
- var import_vector7 = require("@pixerate/editor/vector");
632
+ var import_vector8 = require("@pixerate/editor/vector");
572
633
 
573
634
  // src/vector/VectorEffectsPanel.tsx
574
635
  var import_vector4 = require("@pixerate/editor/vector");
@@ -710,14 +771,222 @@ function VectorEffectsPanel({ controller, className }) {
710
771
  ] });
711
772
  }
712
773
 
713
- // src/vector/VectorEnvelopePanel.tsx
714
- var import_react4 = __toESM(require("react"), 1);
774
+ // src/vector/VectorAppearancePanel.tsx
715
775
  var import_vector5 = require("@pixerate/editor/vector");
716
776
  var import_jsx_runtime4 = require("react/jsx-runtime");
717
777
  var buttonClass2 = "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";
718
778
  var numberClass2 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
719
779
  var selectClass2 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
780
+ var headingClass = "text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
720
781
  var labelClass2 = "w-full truncate text-[11px] text-muted-foreground";
782
+ function VectorAppearancePanel({ controller, className }) {
783
+ const state = useVectorEditorState(controller);
784
+ const panel = (0, import_vector5.getVectorAppearancePanel)(state);
785
+ if (!panel) return null;
786
+ const end = () => controller.endMergedEdit();
787
+ const ids = panel.pathIds;
788
+ const update = (l, patch, merge = false) => controller.updatePaintLayer(l.kind, l.index, patch, { ids, merge });
789
+ const layerRow = (l, count) => {
790
+ const name = `${l.kind === "fill" ? "Fill" : "Stroke"} ${l.index + 1}`;
791
+ if (l.unknownKind)
792
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "text-[11px] text-muted-foreground", "data-vector-layer": `${l.kind}-${l.index}`, children: [
793
+ name,
794
+ ": ",
795
+ l.unknownKind,
796
+ " (not drawn by this version)"
797
+ ] }, `${l.kind}-${l.index}`);
798
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-layer": `${l.kind}-${l.index}`, "data-vector-layer-editing": l.editing ? "" : void 0, children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1", children: [
800
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", "aria-label": `${name} visible`, title: "Show", checked: l.visible, onChange: (e) => update(l, { visible: e.target.checked }) }),
801
+ l.gradient ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
802
+ "button",
803
+ {
804
+ type: "button",
805
+ "aria-label": `${name} gradient`,
806
+ title: "Edit this gradient",
807
+ className: "h-7 w-10 rounded border border-border",
808
+ style: { background: l.swatch },
809
+ onClick: () => controller.setGradientTarget(l.kind, l.index)
810
+ }
811
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
812
+ "input",
813
+ {
814
+ type: "color",
815
+ "aria-label": `${name} colour`,
816
+ className: "h-7 w-10 cursor-pointer rounded border border-border bg-background",
817
+ value: l.color ?? "#000000",
818
+ onChange: (e) => update(l, { paint: e.target.value }, true),
819
+ onBlur: end
820
+ }
821
+ ),
822
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
823
+ "input",
824
+ {
825
+ type: "number",
826
+ min: 0,
827
+ max: 100,
828
+ step: 5,
829
+ "aria-label": `${name} opacity`,
830
+ title: "Opacity %",
831
+ className: numberClass2,
832
+ value: Math.round(l.opacity * 100),
833
+ onChange: (e) => {
834
+ const n = parseFloat(e.target.value);
835
+ if (Number.isFinite(n)) update(l, { opacity: Math.min(100, Math.max(0, n)) / 100 }, true);
836
+ },
837
+ onBlur: end
838
+ }
839
+ ),
840
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": `${name} blend mode`, className: selectClass2, value: l.blendMode, onChange: (e) => update(l, { blendMode: e.target.value }), children: import_vector5.VECTOR_BLEND_MODE_MENU.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.mode, children: m.label }, m.mode)) })
841
+ ] }),
842
+ l.kind === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1", children: [
843
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
844
+ "input",
845
+ {
846
+ type: "number",
847
+ min: 0,
848
+ step: 0.5,
849
+ "aria-label": `${name} width`,
850
+ title: "Width",
851
+ className: numberClass2,
852
+ value: l.strokeWidth ?? 1,
853
+ onChange: (e) => {
854
+ const n = parseFloat(e.target.value);
855
+ if (Number.isFinite(n) && n >= 0) update(l, { strokeWidth: n }, true);
856
+ },
857
+ onBlur: end
858
+ }
859
+ ),
860
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": `${name} alignment`, className: selectClass2, value: l.strokeAlign ?? "center", onChange: (e) => update(l, { strokeAlign: e.target.value }), children: Object.keys(import_vector5.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: a, children: import_vector5.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) })
861
+ ] }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1", children: [
863
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex-1" }),
864
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Edit gradient", "aria-label": `Edit ${name} gradient`, "aria-pressed": l.editing, onClick: () => controller.setGradientTarget(l.kind, l.index), children: "\u270E" }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Move up", "aria-label": `Move ${name} up`, disabled: l.index === count - 1, onClick: () => controller.movePaintLayer(l.kind, l.index, l.index + 1, { ids }), children: "\u2191" }),
866
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Move down", "aria-label": `Move ${name} down`, disabled: l.index === 0, onClick: () => controller.movePaintLayer(l.kind, l.index, l.index - 1, { ids }), children: "\u2193" }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Remove", "aria-label": `Remove ${name}`, onClick: () => controller.removePaintLayer(l.kind, l.index, { ids }), children: "\xD7" })
868
+ ] })
869
+ ] }, `${l.kind}-${l.index}`);
870
+ };
871
+ const stack = (kind, layers, mixed) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-1", "data-vector-stack": kind, children: [
872
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `flex-1 ${headingClass}`, children: kind === "fill" ? "Fills" : "Strokes" }),
874
+ mixed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
876
+ "button",
877
+ {
878
+ type: "button",
879
+ className: buttonClass2,
880
+ title: kind === "fill" ? "Add fill" : "Add stroke",
881
+ "aria-label": kind === "fill" ? "Add fill" : "Add stroke",
882
+ disabled: !ids.length,
883
+ onClick: () => controller.addPaintLayer(kind, {}, { ids }),
884
+ children: "+"
885
+ }
886
+ )
887
+ ] }),
888
+ [...layers].reverse().map((l) => layerRow(l, layers.length))
889
+ ] });
890
+ const filterNode = panel.filterNodeId;
891
+ const setFilter = (f, key, value, merge) => {
892
+ if (filterNode) controller.updateFilter(filterNode, f.id, { params: { [key]: value } }, { merge });
893
+ };
894
+ const filterParam = (f, spec) => {
895
+ const label = `${f.label} ${spec.label}`;
896
+ if (spec.kind === "color")
897
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-filter-param": spec.key, children: [
898
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-14 truncate text-[11px] text-muted-foreground", children: spec.label }),
899
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
900
+ "input",
901
+ {
902
+ type: "color",
903
+ "aria-label": label,
904
+ className: "h-7 w-10 cursor-pointer rounded border border-border bg-background",
905
+ value: /^#[0-9a-f]{6}$/i.test(String(f.params[spec.key])) ? String(f.params[spec.key]) : "#000000",
906
+ onChange: (e) => setFilter(f, spec.key, e.target.value, true),
907
+ onBlur: end
908
+ }
909
+ )
910
+ ] }, spec.key);
911
+ if (spec.kind !== "number") return null;
912
+ const value = Number(f.params[spec.key] ?? 0);
913
+ const onChange = (e) => {
914
+ const n = parseFloat(e.target.value);
915
+ if (Number.isFinite(n)) setFilter(f, spec.key, n, true);
916
+ };
917
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-filter-param": spec.key, children: [
918
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: labelClass2, children: spec.label }),
919
+ spec.slider && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
920
+ "input",
921
+ {
922
+ type: "range",
923
+ min: spec.min,
924
+ max: spec.max,
925
+ step: spec.step,
926
+ "aria-label": `${label} slider`,
927
+ className: "h-4 min-w-0 flex-1 accent-primary",
928
+ value,
929
+ onChange,
930
+ onPointerUp: end,
931
+ onKeyUp: end,
932
+ onBlur: end
933
+ }
934
+ ),
935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", min: spec.min, max: spec.max, step: spec.step, "aria-label": label, className: numberClass2, value: Math.round(value * 1e3) / 1e3, onChange, onBlur: end })
936
+ ] }, spec.key);
937
+ };
938
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-appearance": "", "aria-label": "Appearance", children: [
939
+ ids.length > 0 && stack("stroke", panel.strokes, panel.mixed.strokes),
940
+ ids.length > 0 && stack("fill", panel.fills, panel.mixed.fills),
941
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "blendMode", children: [
942
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16", children: "Blend" }),
943
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Blend mode", className: selectClass2, value: panel.blendMode, onChange: (e) => controller.setBlendMode(e.target.value, { ids: panel.nodeIds }), children: import_vector5.VECTOR_BLEND_MODE_MENU.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.mode, children: m.label }, m.mode)) }),
944
+ panel.mixed.blendMode && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
945
+ ] }),
946
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-2", "data-vector-filters": "", children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `w-16 ${headingClass}`, children: "Filters" }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
950
+ "select",
951
+ {
952
+ "aria-label": "Add filter",
953
+ className: selectClass2,
954
+ value: "",
955
+ onChange: (e) => {
956
+ if (e.target.value) controller.addFilter(e.target.value, { ids: panel.nodeIds });
957
+ },
958
+ children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "Add filter\u2026" }),
960
+ import_vector5.VECTOR_FILTER_MENU.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.type, children: m.label }, m.type))
961
+ ]
962
+ }
963
+ )
964
+ ] }),
965
+ !filterNode && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Select one object to edit its filters." }),
966
+ filterNode && panel.filters.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-1 rounded border border-border p-1.5", "data-vector-filter": f.id, "data-vector-filter-type": f.type, children: [
967
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1", children: [
968
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-xs font-medium text-foreground", children: [
969
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", "aria-label": `${f.label} on`, checked: f.enabled, onChange: (e) => controller.updateFilter(filterNode, f.id, { enabled: e.target.checked }) }),
970
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate", children: f.label })
971
+ ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Move up", "aria-label": `Move ${f.label} up`, disabled: i === 0, onClick: () => controller.moveFilter(filterNode, f.id, i - 1), children: "\u2191" }),
973
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Move down", "aria-label": `Move ${f.label} down`, disabled: i === panel.filters.length - 1, onClick: () => controller.moveFilter(filterNode, f.id, i + 1), children: "\u2193" }),
974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Remove", "aria-label": `Remove ${f.label}`, onClick: () => controller.removeFilter(filterNode, f.id), children: "\xD7" })
975
+ ] }),
976
+ f.specs.map((spec) => filterParam(f, spec))
977
+ ] }, f.id))
978
+ ] })
979
+ ] });
980
+ }
981
+
982
+ // src/vector/VectorEnvelopePanel.tsx
983
+ var import_react4 = __toESM(require("react"), 1);
984
+ var import_vector6 = require("@pixerate/editor/vector");
985
+ var import_jsx_runtime5 = require("react/jsx-runtime");
986
+ var buttonClass3 = "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";
987
+ var numberClass3 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
988
+ var selectClass3 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
989
+ var labelClass3 = "w-full truncate text-[11px] text-muted-foreground";
721
990
  var SLIDERS = [
722
991
  { key: "bend", label: "Bend %" },
723
992
  { key: "horizontal", label: "Horizontal distortion %" },
@@ -725,7 +994,7 @@ var SLIDERS = [
725
994
  ];
726
995
  function VectorEnvelopePanel({ controller, className }) {
727
996
  const state = useVectorEditorState(controller);
728
- const panel = (0, import_vector5.getVectorEnvelopePanel)(state);
997
+ const panel = (0, import_vector6.getVectorEnvelopePanel)(state);
729
998
  const [size, setSize] = import_react4.default.useState({ rows: 2, columns: 2 });
730
999
  if (!panel) return null;
731
1000
  const env = panel.envelope;
@@ -740,16 +1009,16 @@ function VectorEnvelopePanel({ controller, className }) {
740
1009
  if (env) set({ [key]: n });
741
1010
  else setSize({ ...size, [key]: n });
742
1011
  };
743
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-envelope": "", "aria-label": "Envelope", children: [
744
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
745
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Envelope" }),
746
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "data-vector-envelope-action": "warp", className: buttonClass2, title: "Make with Warp", "aria-label": "Make with Warp", disabled: !panel.canMake || !!env, onClick: () => set({ style: "arc", bend: 50 }), children: "Warp" }),
747
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1012
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-envelope": "", "aria-label": "Envelope", children: [
1013
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1014
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Envelope" }),
1015
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-vector-envelope-action": "warp", className: buttonClass3, title: "Make with Warp", "aria-label": "Make with Warp", disabled: !panel.canMake || !!env, onClick: () => set({ style: "arc", bend: 50 }), children: "Warp" }),
1016
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
748
1017
  "button",
749
1018
  {
750
1019
  type: "button",
751
1020
  "data-vector-envelope-action": "mesh",
752
- className: buttonClass2,
1021
+ className: buttonClass3,
753
1022
  title: "Make with Mesh",
754
1023
  "aria-label": "Make with Mesh",
755
1024
  disabled: !panel.canMake || !!env,
@@ -757,12 +1026,12 @@ function VectorEnvelopePanel({ controller, className }) {
757
1026
  children: "Mesh"
758
1027
  }
759
1028
  ),
760
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1029
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
761
1030
  "button",
762
1031
  {
763
1032
  type: "button",
764
1033
  "data-vector-envelope-action": "top",
765
- className: buttonClass2,
1034
+ className: buttonClass3,
766
1035
  title: "Make with Top Object",
767
1036
  "aria-label": "Make with Top Object",
768
1037
  disabled: !panel.canMakeWithTopObject,
@@ -770,12 +1039,12 @@ function VectorEnvelopePanel({ controller, className }) {
770
1039
  children: "Top"
771
1040
  }
772
1041
  ),
773
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "data-vector-envelope-action": "release", className: buttonClass2, title: "Release Envelope", "aria-label": "Release Envelope", disabled: !panel.canRelease, onClick: () => controller.releaseEnvelope(), children: "Release" }),
774
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "data-vector-envelope-action": "expand", className: buttonClass2, title: "Expand Envelope", "aria-label": "Expand Envelope", disabled: !panel.canRelease, onClick: () => controller.expandAppearance(), children: "Expand" })
1042
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-vector-envelope-action": "release", className: buttonClass3, title: "Release Envelope", "aria-label": "Release Envelope", disabled: !panel.canRelease, onClick: () => controller.releaseEnvelope(), children: "Release" }),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-vector-envelope-action": "expand", className: buttonClass3, title: "Expand Envelope", "aria-label": "Expand Envelope", disabled: !panel.canRelease, onClick: () => controller.expandAppearance(), children: "Expand" })
775
1044
  ] }),
776
- env && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": "style", children: [
777
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: labelClass2, children: "Warp" }),
778
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Envelope warp", className: selectClass2, value: env.params.style, onChange: (e) => set({ style: e.target.value }), children: import_vector5.VECTOR_ENVELOPE_STYLE_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: o.value, children: o.label }, o.value)) })
1045
+ env && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": "style", children: [
1046
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: labelClass3, children: "Warp" }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { "aria-label": "Envelope warp", className: selectClass3, value: env.params.style, onChange: (e) => set({ style: e.target.value }), children: import_vector6.VECTOR_ENVELOPE_STYLE_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: o.value, children: o.label }, o.value)) })
779
1048
  ] }),
780
1049
  env && env.params.style !== "mesh" && SLIDERS.map((s) => {
781
1050
  const value = env.params[s.key];
@@ -783,9 +1052,9 @@ function VectorEnvelopePanel({ controller, className }) {
783
1052
  const n = parseFloat(ev.target.value);
784
1053
  if (Number.isFinite(n)) set({ [s.key]: n }, true);
785
1054
  };
786
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": s.key, children: [
787
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: labelClass2, children: s.label }),
788
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1055
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": s.key, children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: labelClass3, children: s.label }),
1057
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
789
1058
  "input",
790
1059
  {
791
1060
  type: "range",
@@ -801,36 +1070,36 @@ function VectorEnvelopePanel({ controller, className }) {
801
1070
  onBlur: end
802
1071
  }
803
1072
  ),
804
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", min: -100, max: 100, step: 1, "aria-label": `Envelope ${s.label}`, className: numberClass2, value, onChange, onBlur: end })
1073
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "number", min: -100, max: 100, step: 1, "aria-label": `Envelope ${s.label}`, className: numberClass3, value, onChange, onBlur: end })
805
1074
  ] }, s.key);
806
1075
  }),
807
- env && env.params.style !== "mesh" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": "orientation", children: [
808
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: labelClass2, children: "Orientation" }),
809
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { "aria-label": "Envelope orientation", className: selectClass2, value: env.params.orientation, onChange: (e) => set({ orientation: e.target.value }), children: [
810
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "horizontal", children: "Horizontal" }),
811
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "vertical", children: "Vertical" })
1076
+ env && env.params.style !== "mesh" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": "orientation", children: [
1077
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: labelClass3, children: "Orientation" }),
1078
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { "aria-label": "Envelope orientation", className: selectClass3, value: env.params.orientation, onChange: (e) => set({ orientation: e.target.value }), children: [
1079
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "horizontal", children: "Horizontal" }),
1080
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "vertical", children: "Vertical" })
812
1081
  ] })
813
1082
  ] }),
814
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-envelope-param": "mesh", children: [
815
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0 flex-1 truncate text-[11px] text-muted-foreground", children: "Mesh rows \xD7 columns" }),
816
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", min: 1, max: 16, step: 1, "aria-label": "Envelope mesh rows", className: numberClass2, value: rows, onChange: (e) => setCount("rows", e.target.value) }),
817
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", min: 1, max: 16, step: 1, "aria-label": "Envelope mesh columns", className: numberClass2, value: columns, onChange: (e) => setCount("columns", e.target.value) })
1083
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-envelope-param": "mesh", children: [
1084
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "min-w-0 flex-1 truncate text-[11px] text-muted-foreground", children: "Mesh rows \xD7 columns" }),
1085
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "number", min: 1, max: 16, step: 1, "aria-label": "Envelope mesh rows", className: numberClass3, value: rows, onChange: (e) => setCount("rows", e.target.value) }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "number", min: 1, max: 16, step: 1, "aria-label": "Envelope mesh columns", className: numberClass3, value: columns, onChange: (e) => setCount("columns", e.target.value) })
818
1087
  ] }),
819
- panel.count > 1 && !panel.canMakeWithTopObject && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Select one object to edit its envelope." }),
820
- env && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Edit the mesh with the Envelope tool (U)." })
1088
+ panel.count > 1 && !panel.canMakeWithTopObject && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Select one object to edit its envelope." }),
1089
+ env && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Edit the mesh with the Envelope tool (U)." })
821
1090
  ] });
822
1091
  }
823
1092
 
824
1093
  // src/vector/VectorGradientEditor.tsx
825
- var import_vector6 = require("@pixerate/editor/vector");
826
- var import_jsx_runtime5 = require("react/jsx-runtime");
827
- var selectClass3 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
828
- var numberClass3 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
829
- var buttonClass3 = "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";
1094
+ var import_vector7 = require("@pixerate/editor/vector");
1095
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1096
+ var selectClass4 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1097
+ var numberClass4 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
1098
+ var buttonClass4 = "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";
830
1099
  var percent = (v) => Math.round(v * 1e3) / 10;
831
1100
  function VectorGradientEditor({ controller, className }) {
832
1101
  const state = useVectorEditorState(controller);
833
- const ed = (0, import_vector6.getVectorGradientEditor)(state);
1102
+ const ed = (0, import_vector7.getVectorGradientEditor)(state);
834
1103
  if (!ed) return null;
835
1104
  const g = ed.gradient;
836
1105
  const sel = ed.selected;
@@ -838,26 +1107,26 @@ function VectorGradientEditor({ controller, className }) {
838
1107
  const setType = (value) => {
839
1108
  if (value === "solid") {
840
1109
  const first = ed.stops[0];
841
- controller.setStyle({ [ed.target]: (0, import_vector6.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
1110
+ controller.setStyle({ [ed.target]: (0, import_vector7.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
842
1111
  } else controller.setGradient({ type: value });
843
1112
  };
844
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
845
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1.5", children: [
846
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
847
- ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: buttonClass3, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
848
- ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1113
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
1116
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
1117
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
849
1118
  ] }),
850
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1.5", children: [
851
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
852
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { "aria-label": "Gradient type", className: selectClass3, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
853
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "solid", children: "Solid" }),
854
- import_vector6.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: t, children: import_vector6.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
1119
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1120
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
1121
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { "aria-label": "Gradient type", className: selectClass4, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "solid", children: "Solid" }),
1123
+ import_vector7.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t, children: import_vector7.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
855
1124
  ] }),
856
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
1125
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
857
1126
  ] }),
858
- g && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
859
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
860
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1127
+ g && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
1129
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
861
1130
  "div",
862
1131
  {
863
1132
  role: "button",
@@ -871,7 +1140,7 @@ function VectorGradientEditor({ controller, className }) {
871
1140
  }
872
1141
  }
873
1142
  ),
874
- ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1143
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
875
1144
  "button",
876
1145
  {
877
1146
  type: "button",
@@ -885,21 +1154,21 @@ function VectorGradientEditor({ controller, className }) {
885
1154
  s.index
886
1155
  ))
887
1156
  ] }),
888
- sel && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
889
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1157
+ sel && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
1158
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
890
1159
  "input",
891
1160
  {
892
1161
  type: "color",
893
1162
  "aria-label": "Stop colour",
894
1163
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
895
- value: (0, import_vector6.vectorPaintToHex)(sel.color) ?? "#000000",
1164
+ value: (0, import_vector7.vectorPaintToHex)(sel.color) ?? "#000000",
896
1165
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
897
1166
  onBlur: end
898
1167
  }
899
1168
  ),
900
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1169
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
901
1170
  "Opacity",
902
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1171
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
903
1172
  "input",
904
1173
  {
905
1174
  type: "number",
@@ -907,7 +1176,7 @@ function VectorGradientEditor({ controller, className }) {
907
1176
  max: 100,
908
1177
  step: 5,
909
1178
  "aria-label": "Stop opacity",
910
- className: numberClass3,
1179
+ className: numberClass4,
911
1180
  value: percent(sel.opacity ?? 1),
912
1181
  onChange: (e) => {
913
1182
  const n = parseFloat(e.target.value);
@@ -916,9 +1185,9 @@ function VectorGradientEditor({ controller, className }) {
916
1185
  }
917
1186
  )
918
1187
  ] }),
919
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1188
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
920
1189
  "At",
921
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1190
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
922
1191
  "input",
923
1192
  {
924
1193
  type: "number",
@@ -926,7 +1195,7 @@ function VectorGradientEditor({ controller, className }) {
926
1195
  max: 100,
927
1196
  step: 1,
928
1197
  "aria-label": "Stop position",
929
- className: numberClass3,
1198
+ className: numberClass4,
930
1199
  value: percent(sel.offset),
931
1200
  onChange: (e) => {
932
1201
  const n = parseFloat(e.target.value);
@@ -935,9 +1204,9 @@ function VectorGradientEditor({ controller, className }) {
935
1204
  }
936
1205
  )
937
1206
  ] }),
938
- !sel.last && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1207
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
939
1208
  "Midpoint",
940
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1209
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
941
1210
  "input",
942
1211
  {
943
1212
  type: "number",
@@ -945,7 +1214,7 @@ function VectorGradientEditor({ controller, className }) {
945
1214
  max: 95,
946
1215
  step: 1,
947
1216
  "aria-label": "Stop midpoint",
948
- className: numberClass3,
1217
+ className: numberClass4,
949
1218
  value: percent(sel.midpoint ?? 0.5),
950
1219
  onChange: (e) => {
951
1220
  const n = parseFloat(e.target.value);
@@ -954,15 +1223,15 @@ function VectorGradientEditor({ controller, className }) {
954
1223
  }
955
1224
  )
956
1225
  ] }),
957
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
1226
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
958
1227
  ] }),
959
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
960
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Colours" }),
961
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass3, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector6.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: i, children: import_vector6.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
1228
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1229
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Colours" }),
1230
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass4, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector7.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: i, children: import_vector7.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
962
1231
  ] }),
963
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
964
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Smooth" }),
965
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1232
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Smooth" }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
966
1235
  "input",
967
1236
  {
968
1237
  type: "range",
@@ -978,26 +1247,26 @@ function VectorGradientEditor({ controller, className }) {
978
1247
  }
979
1248
  )
980
1249
  ] }),
981
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
982
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "Spread" }),
983
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { "aria-label": "Gradient spread", className: selectClass3, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector6.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: sp, children: import_vector6.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
1250
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1251
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Spread" }),
1252
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Gradient spread", className: selectClass4, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector7.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: sp, children: import_vector7.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
984
1253
  ] }),
985
- ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
986
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-16", children: "On stroke" }),
987
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass3, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector6.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: m, children: import_vector6.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
1254
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1255
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "On stroke" }),
1256
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass4, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector7.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: m, children: import_vector7.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
988
1257
  ] })
989
1258
  ] })
990
1259
  ] });
991
1260
  }
992
1261
 
993
1262
  // src/vector/VectorToolbar.tsx
994
- var import_jsx_runtime6 = require("react/jsx-runtime");
1263
+ var import_jsx_runtime7 = require("react/jsx-runtime");
995
1264
  var VECTOR_SNAP_TITLE = "Snap to objects";
996
1265
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
997
- var buttonClass4 = "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";
1266
+ var buttonClass5 = "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";
998
1267
  function VectorToolbar({
999
1268
  controller,
1000
- tools = import_vector7.VECTOR_TOOLS,
1269
+ tools = import_vector8.VECTOR_TOOLS,
1001
1270
  showHistory = true,
1002
1271
  showArrange = true,
1003
1272
  showSnap = true,
@@ -1013,32 +1282,32 @@ function VectorToolbar({
1013
1282
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
1014
1283
  const [live, setLive] = import_react5.default.useState(false);
1015
1284
  const booleanDisabled = live ? count < 1 : count < 2;
1016
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
1017
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
1018
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1285
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
1286
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
1287
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1019
1288
  "button",
1020
1289
  {
1021
1290
  type: "button",
1022
1291
  "data-vector-tool": tool,
1023
- className: buttonClass4,
1292
+ className: buttonClass5,
1024
1293
  "aria-pressed": state.tool === tool,
1025
- title: `${(0, import_vector7.vectorToolLabel)(tool)} (${import_vector7.VECTOR_TOOL_SHORTCUTS[tool]})`,
1026
- "aria-label": (0, import_vector7.vectorToolLabel)(tool),
1294
+ title: `${(0, import_vector8.vectorToolLabel)(tool)} (${import_vector8.VECTOR_TOOL_SHORTCUTS[tool]})`,
1295
+ "aria-label": (0, import_vector8.vectorToolLabel)(tool),
1027
1296
  onClick: () => controller.setTool(tool),
1028
- children: import_vector7.VECTOR_TOOL_GLYPHS[tool]
1297
+ children: import_vector8.VECTOR_TOOL_GLYPHS[tool]
1029
1298
  },
1030
1299
  tool
1031
1300
  )) }),
1032
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
1301
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
1033
1302
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
1034
1303
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
1035
1304
  ] }),
1036
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1305
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1037
1306
  "button",
1038
1307
  {
1039
1308
  type: "button",
1040
1309
  "data-vector-snap": "",
1041
- className: buttonClass4,
1310
+ className: buttonClass5,
1042
1311
  "aria-pressed": state.snap.objects,
1043
1312
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
1044
1313
  "aria-label": VECTOR_SNAP_TITLE,
@@ -1046,13 +1315,13 @@ function VectorToolbar({
1046
1315
  children: "Snap"
1047
1316
  }
1048
1317
  ) }),
1049
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
1050
- import_vector7.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1318
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
1319
+ import_vector8.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1051
1320
  "button",
1052
1321
  {
1053
1322
  type: "button",
1054
1323
  "data-vector-path-action": a.id,
1055
- className: buttonClass4,
1324
+ className: buttonClass5,
1056
1325
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
1057
1326
  "aria-label": a.title,
1058
1327
  disabled: !a.enabled(count, points),
@@ -1061,12 +1330,12 @@ function VectorToolbar({
1061
1330
  },
1062
1331
  a.id
1063
1332
  )),
1064
- import_vector7.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1333
+ import_vector8.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1065
1334
  "button",
1066
1335
  {
1067
1336
  type: "button",
1068
1337
  "data-vector-edit": t.key,
1069
- className: buttonClass4,
1338
+ className: buttonClass5,
1070
1339
  "aria-pressed": editing[t.key],
1071
1340
  title: t.title,
1072
1341
  "aria-label": t.title,
@@ -1075,9 +1344,9 @@ function VectorToolbar({
1075
1344
  },
1076
1345
  t.key
1077
1346
  )),
1078
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1347
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1079
1348
  "Smoothing",
1080
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1349
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1081
1350
  "input",
1082
1351
  {
1083
1352
  type: "range",
@@ -1091,9 +1360,9 @@ function VectorToolbar({
1091
1360
  }
1092
1361
  )
1093
1362
  ] }),
1094
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1363
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1095
1364
  "Size",
1096
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1365
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1097
1366
  "input",
1098
1367
  {
1099
1368
  type: "number",
@@ -1109,26 +1378,26 @@ function VectorToolbar({
1109
1378
  }
1110
1379
  )
1111
1380
  ] }),
1112
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1381
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1113
1382
  "button",
1114
1383
  {
1115
1384
  type: "button",
1116
1385
  "data-vector-fill-gap": "",
1117
- className: buttonClass4,
1386
+ className: buttonClass5,
1118
1387
  "aria-pressed": editing.fillGap > 0,
1119
1388
  title: "Close small gaps between path ends when filling",
1120
1389
  "aria-label": "Close gaps",
1121
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector7.VECTOR_FILL_GAP }),
1390
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector8.VECTOR_FILL_GAP }),
1122
1391
  children: "Gaps"
1123
1392
  }
1124
1393
  )
1125
1394
  ] }),
1126
- showDistort && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1395
+ showDistort && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1127
1396
  "button",
1128
1397
  {
1129
1398
  type: "button",
1130
1399
  "data-vector-free-distort": "",
1131
- className: buttonClass4,
1400
+ className: buttonClass5,
1132
1401
  "aria-pressed": state.freeDistort,
1133
1402
  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)`,
1134
1403
  "aria-label": "Free Distort",
@@ -1136,15 +1405,15 @@ function VectorToolbar({
1136
1405
  children: "Distort"
1137
1406
  }
1138
1407
  ) }),
1139
- showBlend && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VectorBlendControls, { controller }),
1140
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1141
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
1142
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1408
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorBlendControls, { controller }),
1409
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1410
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1143
1412
  "button",
1144
1413
  {
1145
1414
  type: "button",
1146
1415
  "data-vector-live-boolean": "",
1147
- className: buttonClass4,
1416
+ className: buttonClass5,
1148
1417
  "aria-pressed": live,
1149
1418
  title: "Live booleans: keep the shapes editable",
1150
1419
  "aria-label": "Live booleans",
@@ -1159,12 +1428,12 @@ function VectorToolbar({
1159
1428
  act("G", "Group", () => controller.group(), count < 1),
1160
1429
  act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
1161
1430
  ] }),
1162
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector7.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1431
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector8.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1163
1432
  "button",
1164
1433
  {
1165
1434
  type: "button",
1166
1435
  "data-vector-pathfinder": a.id,
1167
- className: buttonClass4,
1436
+ className: buttonClass5,
1168
1437
  title: a.title,
1169
1438
  "aria-label": a.title,
1170
1439
  disabled: count < a.min,
@@ -1173,7 +1442,7 @@ function VectorToolbar({
1173
1442
  },
1174
1443
  a.id
1175
1444
  )) }),
1176
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
1445
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
1177
1446
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
1178
1447
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
1179
1448
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -1188,22 +1457,22 @@ function VectorToolbar({
1188
1457
  }
1189
1458
  function VectorBlendControls({ controller }) {
1190
1459
  const state = useVectorEditorState(controller);
1191
- const blend = (0, import_vector7.getVectorBlendControls)(state);
1460
+ const blend = (0, import_vector8.getVectorBlendControls)(state);
1192
1461
  const o = blend.options;
1193
1462
  const selected = blend.ids.length > 0;
1194
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
1195
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass4, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
1196
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1463
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
1464
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass5, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
1465
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1197
1466
  "select",
1198
1467
  {
1199
1468
  "aria-label": "Blend spacing",
1200
1469
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
1201
1470
  value: o.spacing,
1202
- onChange: (e) => (0, import_vector7.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
1203
- children: import_vector7.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: sp, children: import_vector7.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
1471
+ onChange: (e) => (0, import_vector8.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
1472
+ children: import_vector8.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: sp, children: import_vector8.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
1204
1473
  }
1205
1474
  ),
1206
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1475
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1207
1476
  "input",
1208
1477
  {
1209
1478
  type: "number",
@@ -1215,29 +1484,29 @@ function VectorBlendControls({ controller }) {
1215
1484
  onChange: (e) => {
1216
1485
  const n = parseFloat(e.target.value);
1217
1486
  if (!Number.isFinite(n) || n <= 0) return;
1218
- (0, import_vector7.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
1487
+ (0, import_vector8.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
1219
1488
  }
1220
1489
  }
1221
1490
  ),
1222
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1491
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1223
1492
  "button",
1224
1493
  {
1225
1494
  type: "button",
1226
1495
  "data-vector-blend-action": "orient",
1227
- className: buttonClass4,
1496
+ className: buttonClass5,
1228
1497
  "aria-pressed": o.orientToSpine,
1229
1498
  title: "Orient steps to the spine",
1230
1499
  "aria-label": "Orient to spine",
1231
- onClick: () => (0, import_vector7.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
1500
+ onClick: () => (0, import_vector8.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
1232
1501
  children: "\u293F"
1233
1502
  }
1234
1503
  ),
1235
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1236
1505
  "button",
1237
1506
  {
1238
1507
  type: "button",
1239
1508
  "data-vector-blend-action": "spine",
1240
- className: buttonClass4,
1509
+ className: buttonClass5,
1241
1510
  title: "Replace Spine (select a blend and a path)",
1242
1511
  "aria-label": "Replace Spine",
1243
1512
  disabled: !blend.canReplaceSpine,
@@ -1245,15 +1514,15 @@ function VectorBlendControls({ controller }) {
1245
1514
  children: "\u2307"
1246
1515
  }
1247
1516
  ),
1248
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass4, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
1249
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass4, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
1517
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass5, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
1518
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass5, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
1250
1519
  ] });
1251
1520
  }
1252
- var selectClass4 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1253
- var numberClass4 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1521
+ var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1522
+ var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1254
1523
  function VectorStylePanel({ controller, className }) {
1255
1524
  const state = useVectorEditorState(controller);
1256
- const style = (0, import_vector7.getVectorSelectionStyle)(state);
1525
+ const style = (0, import_vector8.getVectorSelectionStyle)(state);
1257
1526
  const d = state.defaults;
1258
1527
  const source = style ?? {
1259
1528
  fill: d.fill ?? null,
@@ -1266,7 +1535,7 @@ function VectorStylePanel({ controller, className }) {
1266
1535
  arrowAlign: d.arrowAlign ?? "extend",
1267
1536
  startArrowScale: d.startArrowScale ?? 1,
1268
1537
  endArrowScale: d.endArrowScale ?? 1,
1269
- widthProfile: (0, import_vector7.widthProfilePresetId)(d.strokeWidthProfile),
1538
+ widthProfile: (0, import_vector8.widthProfilePresetId)(d.strokeWidthProfile),
1270
1539
  cornerRadius: d.cornerRadius,
1271
1540
  cornerKind: d.cornerKind ?? "round",
1272
1541
  corners: 1,
@@ -1279,19 +1548,19 @@ function VectorStylePanel({ controller, className }) {
1279
1548
  };
1280
1549
  const setProfile = (id) => {
1281
1550
  if (style) controller.setWidthProfile(id);
1282
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector7.widthProfilePreset)(id)?.length ? (0, import_vector7.widthProfilePreset)(id) : null });
1551
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector8.widthProfilePreset)(id)?.length ? (0, import_vector8.widthProfilePreset)(id) : null });
1283
1552
  };
1284
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
1553
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
1285
1554
  const arrowRow = (end, label) => {
1286
1555
  const key = end === "start" ? "startArrow" : "endArrow";
1287
1556
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
1288
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1289
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1290
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass4, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
1291
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "None" }),
1292
- import_vector7.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: h, children: import_vector7.VECTOR_ARROWHEAD_LABELS[h] }, h))
1557
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1558
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass5, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "None" }),
1561
+ import_vector8.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: h, children: import_vector8.VECTOR_ARROWHEAD_LABELS[h] }, h))
1293
1562
  ] }),
1294
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1563
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1295
1564
  "input",
1296
1565
  {
1297
1566
  type: "number",
@@ -1299,7 +1568,7 @@ function VectorStylePanel({ controller, className }) {
1299
1568
  step: 0.25,
1300
1569
  "aria-label": `${label} arrowhead scale`,
1301
1570
  title: "Scale (\xD7 stroke width)",
1302
- className: numberClass4,
1571
+ className: numberClass5,
1303
1572
  value: source[scaleKey],
1304
1573
  onChange: (e) => {
1305
1574
  const n = parseFloat(e.target.value);
@@ -1315,14 +1584,14 @@ function VectorStylePanel({ controller, className }) {
1315
1584
  };
1316
1585
  const paintRow = (key, label) => {
1317
1586
  const paint = source[key];
1318
- const hex = (0, import_vector7.vectorPaintToHex)(paint);
1587
+ const hex = (0, import_vector8.vectorPaintToHex)(paint);
1319
1588
  const on = paint !== null && paint !== "none";
1320
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1321
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1322
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
1589
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1590
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1591
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
1323
1592
  label
1324
1593
  ] }),
1325
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1594
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1326
1595
  "input",
1327
1596
  {
1328
1597
  type: "color",
@@ -1333,18 +1602,18 @@ function VectorStylePanel({ controller, className }) {
1333
1602
  onChange: (e) => apply({ [key]: e.target.value })
1334
1603
  }
1335
1604
  ),
1336
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
1337
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1605
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
1606
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1338
1607
  ] });
1339
1608
  };
1340
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
1341
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
1609
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
1610
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
1342
1611
  paintRow("fill", "Fill"),
1343
1612
  paintRow("stroke", "Stroke"),
1344
- style && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VectorGradientEditor, { controller }),
1345
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1346
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Width" }),
1347
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1613
+ style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorGradientEditor, { controller }),
1614
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1615
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Width" }),
1616
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1348
1617
  "input",
1349
1618
  {
1350
1619
  type: "number",
@@ -1360,33 +1629,33 @@ function VectorStylePanel({ controller, className }) {
1360
1629
  }
1361
1630
  )
1362
1631
  ] }),
1363
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
1364
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Align" }),
1365
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass4, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector7.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: a, children: import_vector7.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
1632
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
1633
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Align" }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass5, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector8.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: a, children: import_vector8.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
1366
1635
  mixed("strokeAlign")
1367
1636
  ] }),
1368
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
1369
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Profile" }),
1370
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1637
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
1638
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Profile" }),
1639
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1371
1640
  "select",
1372
1641
  {
1373
1642
  "aria-label": "Width profile",
1374
- className: selectClass4,
1643
+ className: selectClass5,
1375
1644
  value: source.widthProfile,
1376
1645
  onChange: (e) => {
1377
1646
  if (e.target.value !== "custom") setProfile(e.target.value);
1378
1647
  },
1379
1648
  children: [
1380
- import_vector7.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: p.id, children: p.label }, p.id)),
1381
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
1649
+ import_vector8.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: p.id, children: p.label }, p.id)),
1650
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
1382
1651
  ]
1383
1652
  }
1384
1653
  ),
1385
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1654
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1386
1655
  "button",
1387
1656
  {
1388
1657
  type: "button",
1389
- className: buttonClass4,
1658
+ className: buttonClass5,
1390
1659
  title: "Reset to uniform width",
1391
1660
  "aria-label": "Reset to uniform width",
1392
1661
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -1396,27 +1665,27 @@ function VectorStylePanel({ controller, className }) {
1396
1665
  ),
1397
1666
  mixed("widthProfile")
1398
1667
  ] }),
1399
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1668
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1400
1669
  arrowRow("start", "Start"),
1401
1670
  arrowRow("end", "End"),
1402
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
1403
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Heads" }),
1404
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass4, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
1405
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "extend", children: "Tip past end" }),
1406
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "tip", children: "Tip at end" })
1671
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
1672
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Heads" }),
1673
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass5, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
1674
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "extend", children: "Tip past end" }),
1675
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "tip", children: "Tip at end" })
1407
1676
  ] })
1408
1677
  ] })
1409
1678
  ] }),
1410
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
1411
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
1412
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1679
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
1680
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
1681
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1413
1682
  "input",
1414
1683
  {
1415
1684
  type: "number",
1416
1685
  min: 0,
1417
1686
  step: 1,
1418
1687
  "aria-label": "Corner radius",
1419
- className: numberClass4,
1688
+ className: numberClass5,
1420
1689
  value: Math.round(source.cornerRadius * 100) / 100,
1421
1690
  onChange: (e) => {
1422
1691
  const n = parseFloat(e.target.value);
@@ -1424,12 +1693,12 @@ function VectorStylePanel({ controller, className }) {
1424
1693
  }
1425
1694
  }
1426
1695
  ),
1427
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Corner kind", className: selectClass4, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector7.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: k, children: import_vector7.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
1696
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Corner kind", className: selectClass5, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector8.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: k, children: import_vector8.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
1428
1697
  mixed("cornerRadius")
1429
1698
  ] }),
1430
- style && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1431
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Opacity" }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1699
+ style && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Opacity" }),
1701
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1433
1702
  "input",
1434
1703
  {
1435
1704
  type: "range",
@@ -1443,28 +1712,31 @@ function VectorStylePanel({ controller, className }) {
1443
1712
  }
1444
1713
  )
1445
1714
  ] }),
1446
- style && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VectorEffectsPanel, { controller }),
1447
- style && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VectorEnvelopePanel, { controller })
1715
+ style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorAppearancePanel, { controller }),
1716
+ style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorEffectsPanel, { controller }),
1717
+ style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorEnvelopePanel, { controller })
1448
1718
  ] });
1449
1719
  }
1450
1720
 
1451
1721
  // src/vector/VectorEditor.tsx
1452
- var import_jsx_runtime7 = require("react/jsx-runtime");
1722
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1453
1723
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
1454
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
1455
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorToolbar, { controller, tools }),
1456
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
1457
- /* @__PURE__ */ (0, import_jsx_runtime7.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_runtime7.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
1458
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
1724
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
1725
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorToolbar, { controller, tools }),
1726
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
1727
+ /* @__PURE__ */ (0, import_jsx_runtime8.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_runtime8.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
1728
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
1459
1729
  ] })
1460
1730
  ] });
1461
1731
  }
1462
1732
  // Annotate the CommonJS export names for ESM import in node:
1463
1733
  0 && (module.exports = {
1734
+ VectorAppearancePanel,
1464
1735
  VectorCanvas,
1465
1736
  VectorEditor,
1466
1737
  VectorEffectsPanel,
1467
1738
  VectorEnvelopePanel,
1739
+ VectorFilterDefs,
1468
1740
  VectorGradientAnnotator,
1469
1741
  VectorGradientDefs,
1470
1742
  VectorGradientEditor,