@pixerate/editor-react 0.72.0 → 0.74.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.
@@ -32,10 +32,13 @@ var vector_exports = {};
32
32
  __export(vector_exports, {
33
33
  VectorAppearancePanel: () => VectorAppearancePanel,
34
34
  VectorCanvas: () => VectorCanvas,
35
+ VectorConnectorPanel: () => VectorConnectorPanel,
36
+ VectorConstraintsPanel: () => VectorConstraintsPanel,
35
37
  VectorEditor: () => VectorEditor,
36
38
  VectorEffectsPanel: () => VectorEffectsPanel,
37
39
  VectorEnvelopePanel: () => VectorEnvelopePanel,
38
40
  VectorFilterDefs: () => VectorFilterDefs,
41
+ VectorFramePanel: () => VectorFramePanel,
39
42
  VectorGradientAnnotator: () => VectorGradientAnnotator,
40
43
  VectorGradientDefs: () => VectorGradientDefs,
41
44
  VectorGradientEditor: () => VectorGradientEditor,
@@ -43,6 +46,9 @@ __export(vector_exports, {
43
46
  VectorMaskedNodes: () => VectorMaskedNodes,
44
47
  VectorSceneNodes: () => VectorSceneNodes,
45
48
  VectorStylePanel: () => VectorStylePanel,
49
+ VectorSymbolsPanel: () => VectorSymbolsPanel,
50
+ VectorTextElement: () => VectorTextElement,
51
+ VectorTextPanel: () => VectorTextPanel,
46
52
  VectorToolbar: () => VectorToolbar,
47
53
  VectorWidthPointEditor: () => VectorWidthPointEditor,
48
54
  useVectorCommands: () => useVectorCommands,
@@ -115,11 +121,11 @@ function VectorWidthPointEditor({ controller, edit, accentColor = import_vector2
115
121
  refocus();
116
122
  controller.closeWidthPointEdit();
117
123
  };
118
- const num = (v) => v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
124
+ const num2 = (v) => v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
119
125
  const apply = () => {
120
- const t = num(position);
126
+ const t = num2(position);
121
127
  refocus();
122
- controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left: num(left), right: num(right) });
128
+ controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left: num2(left), right: num2(right) });
123
129
  };
124
130
  const stop = (e) => e.stopPropagation();
125
131
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
@@ -235,9 +241,42 @@ function VectorFilterDefs({ filters }) {
235
241
  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)) });
236
242
  }
237
243
  var blendStyle = (blendMode) => blendMode ? { mixBlendMode: blendMode } : void 0;
244
+ function VectorTextElement({ node }) {
245
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, "data-vector-text": "", transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, filter: node.filter, style: blendStyle(node.blendMode), children: node.layers.map((layer, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
246
+ "text",
247
+ {
248
+ "data-vector-layer": layer.kind,
249
+ xmlSpace: "preserve",
250
+ fill: layer.fill,
251
+ stroke: layer.stroke,
252
+ strokeWidth: layer.stroke === "none" ? void 0 : layer.strokeWidth,
253
+ strokeLinejoin: layer.strokeLinejoin,
254
+ strokeDasharray: layer.strokeDasharray,
255
+ opacity: layer.opacity < 1 ? layer.opacity : void 0,
256
+ style: { whiteSpace: "pre", ...blendStyle(layer.blendMode) },
257
+ children: node.spans.map((sp, k) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
258
+ "tspan",
259
+ {
260
+ x: sp.x,
261
+ y: sp.y,
262
+ rotate: sp.rotate,
263
+ fontFamily: sp.fontFamily,
264
+ fontSize: sp.fontSize,
265
+ fontWeight: sp.fontWeight,
266
+ fontStyle: sp.fontStyle,
267
+ textDecoration: sp.textDecoration,
268
+ fill: layer.runFills && sp.fill ? sp.fill : void 0,
269
+ children: sp.text
270
+ },
271
+ k
272
+ ))
273
+ },
274
+ i
275
+ )) });
276
+ }
238
277
  function VectorSceneNodes({ nodes }) {
239
278
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map(
240
- (node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
279
+ (node) => node.kind === "text" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorTextElement, { node }, node.id) : node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
241
280
  "g",
242
281
  {
243
282
  "data-vector-id": node.id,
@@ -368,10 +407,13 @@ function VectorCanvas({
368
407
  }) {
369
408
  const state = useVectorEditorState(controller);
370
409
  const svgRef = (0, import_react3.useRef)(null);
410
+ const textRef = (0, import_react3.useRef)(null);
411
+ const editingText = state.textEdit?.nodeId ?? null;
371
412
  const fitted = (0, import_react3.useRef)(!fitOnMount);
372
413
  const uid = (0, import_react3.useId)().replace(/:/g, "");
373
414
  const scene = (0, import_vector3.getVectorSceneRenderData)(state, { idPrefix: `ve${uid}-` });
374
415
  const overlay = (0, import_vector3.getVectorOverlayRenderData)(state);
416
+ const host = (0, import_vector3.getVectorSymbolEditBackground)(state, { idPrefix: `ve${uid}-host-` });
375
417
  (0, import_react3.useEffect)(() => {
376
418
  const el = svgRef.current;
377
419
  if (!el || fitted.current) return;
@@ -387,6 +429,11 @@ function VectorCanvas({
387
429
  observer.observe(el);
388
430
  return () => observer.disconnect();
389
431
  }, [controller]);
432
+ (0, import_react3.useEffect)(() => {
433
+ if (!editingText) return;
434
+ const t = setTimeout(() => textRef.current?.focus({ preventScroll: true }), 0);
435
+ return () => clearTimeout(t);
436
+ }, [editingText]);
390
437
  (0, import_react3.useEffect)(() => {
391
438
  const el = svgRef.current;
392
439
  if (!el) return;
@@ -472,7 +519,30 @@ function VectorCanvas({
472
519
  stroke: "rgba(0,0,0,0.12)"
473
520
  }
474
521
  ),
522
+ host && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-symbol-host": "", opacity: 0.3, pointerEvents: "none", transform: host.transform, children: [
523
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorGradientDefs, { defs: host.defs }),
524
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorFilterDefs, { filters: host.filters }),
525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: host.nodes })
526
+ ] }),
475
527
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-scene": "", transform: scene.transform, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: scene.nodes }) }),
528
+ overlay.frames.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-frame-labels": "", children: overlay.frames.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
529
+ "text",
530
+ {
531
+ "data-vector-frame-label": f.id,
532
+ x: f.x,
533
+ y: f.y - 6,
534
+ fontSize: 11,
535
+ fontWeight: f.selected ? 600 : 400,
536
+ fill: f.selected ? accentColor : "#737373",
537
+ style: { fontFamily: "system-ui, sans-serif", cursor: "default", userSelect: "none" },
538
+ onPointerDown: (e) => {
539
+ e.stopPropagation();
540
+ controller.select([f.id]);
541
+ },
542
+ children: f.name
543
+ },
544
+ f.id
545
+ )) }),
476
546
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
477
547
  overlay.outlines.map((o) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
478
548
  overlay.penPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
@@ -614,6 +684,38 @@ function VectorCanvas({
614
684
  }
615
685
  ),
616
686
  overlay.cutPoint && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-cut-point": "", cx: overlay.cutPoint.x, cy: overlay.cutPoint.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + 1, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
687
+ overlay.text && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-text-edit": overlay.text.nodeId, "data-editing": overlay.text.editing || void 0, children: [
688
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: overlay.text.frame, stroke: accentColor, strokeDasharray: overlay.text.editing ? "3 2" : void 0, opacity: 0.8 }),
689
+ overlay.text.selection.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-text-selection": "", d, fill: accentColor, fillOpacity: 0.25, stroke: "none" }, i)),
690
+ overlay.text.caret && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { "data-vector-text-caret": "", x1: overlay.text.caret.x1, y1: overlay.text.caret.y1, x2: overlay.text.caret.x2, y2: overlay.text.caret.y2, stroke: accentColor, strokeWidth: 1.5 }),
691
+ overlay.text.overflow && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-text-overflow": "", transform: `translate(${overlay.text.overflow.x} ${overlay.text.overflow.y})`, children: [
692
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: -5, y: -5, width: 10, height: 10, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: "#dc2626" }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M-3 0H3M0 -3V3", stroke: "#dc2626" })
694
+ ] })
695
+ ] }),
696
+ overlay.trackingPoints.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
697
+ overlay.typedEntry && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
698
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: -3, y: -12, width: overlay.typedEntry.text.length * 7 + 8, height: 17, rx: 3, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
699
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: 1, y: 1, fontSize: 11, fill: accentColor, style: { fontFamily: "ui-monospace, monospace" }, children: overlay.typedEntry.text })
700
+ ] }),
701
+ overlay.connectorSites.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
702
+ "circle",
703
+ {
704
+ "data-vector-connection-site": s.side,
705
+ "data-active": s.active || void 0,
706
+ cx: s.x,
707
+ cy: s.y,
708
+ r: s.active ? 5 : 3.5,
709
+ fill: s.active ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill,
710
+ stroke: accentColor
711
+ },
712
+ `site:${s.nodeId}:${s.side}`
713
+ )),
714
+ overlay.constraints.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-constraint-glyph": c.kind, "data-satisfied": c.satisfied ? "true" : "false", children: [
715
+ c.dimension && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: c.dimension.x1, y1: c.dimension.y1, x2: c.dimension.x2, y2: c.dimension.y2, stroke: c.satisfied ? import_vector3.VECTOR_OVERLAY_THEME.guide : "#dc2626", strokeDasharray: "2 2" }),
716
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: c.x, y: c.y, fontSize: 10, textAnchor: "middle", fill: c.satisfied ? accentColor : "#dc2626", stroke: "#ffffff", strokeWidth: 3, paintOrder: "stroke", style: { fontFamily: "system-ui, sans-serif" }, children: c.label })
717
+ ] }, `constraint:${c.id}`)),
718
+ overlay.textBox && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-vector-text-box": "", x: overlay.textBox.x, y: overlay.textBox.y, width: overlay.textBox.width, height: overlay.textBox.height, stroke: accentColor, strokeDasharray: "3 2" }),
617
719
  overlay.marquee && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
618
720
  "rect",
619
721
  {
@@ -646,6 +748,78 @@ function VectorCanvas({
646
748
  )),
647
749
  overlay.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
648
750
  ] }),
751
+ state.textEdit && overlay.text && // A hidden textarea at the caret takes typing and composed (IME) text.
752
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("foreignObject", { x: overlay.text.caret?.x1 ?? 0, y: overlay.text.caret?.y1 ?? 0, width: 1, height: 1, style: { overflow: "visible" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
753
+ "textarea",
754
+ {
755
+ ref: textRef,
756
+ "data-testid": "vector-type-input",
757
+ "aria-label": "Text",
758
+ autoCapitalize: "off",
759
+ autoCorrect: "off",
760
+ spellCheck: false,
761
+ style: { position: "absolute", width: 1, height: 1, padding: 0, border: 0, opacity: 0, resize: "none", overflow: "hidden" },
762
+ onPointerDown: (e) => e.stopPropagation(),
763
+ onInput: (e) => {
764
+ const el = e.currentTarget;
765
+ if (e.nativeEvent.isComposing) return;
766
+ if (el.value) controller.textInput(el.value);
767
+ el.value = "";
768
+ },
769
+ onCompositionEnd: (e) => {
770
+ const el = e.currentTarget;
771
+ if (el.value) controller.textInput(el.value);
772
+ el.value = "";
773
+ },
774
+ onKeyDown: (e) => {
775
+ e.stopPropagation();
776
+ if (e.nativeEvent.isComposing) return;
777
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey) return;
778
+ if (controller.keyDown((0, import_vector3.toVectorKeyInput)(e))) e.preventDefault();
779
+ if (!controller.getState().textEdit) svgRef.current?.focus({ preventScroll: true });
780
+ },
781
+ onKeyUp: (e) => e.stopPropagation(),
782
+ onCopy: (e) => {
783
+ const text = controller.getSelectedText();
784
+ if (text) {
785
+ e.clipboardData.setData("text/plain", text);
786
+ e.preventDefault();
787
+ }
788
+ e.stopPropagation();
789
+ },
790
+ onCut: (e) => {
791
+ const text = controller.cutText();
792
+ if (text) {
793
+ e.clipboardData.setData("text/plain", text);
794
+ e.preventDefault();
795
+ }
796
+ e.stopPropagation();
797
+ },
798
+ onPaste: (e) => {
799
+ const text = e.clipboardData.getData("text/plain");
800
+ if (text) controller.textInput(text);
801
+ e.preventDefault();
802
+ e.stopPropagation();
803
+ }
804
+ }
805
+ ) }),
806
+ state.symbolEdit && // Isolation mode: which symbol, and the way out (Escape with nothing selected works too).
807
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("foreignObject", { "data-vector-symbol-edit": state.symbolEdit.symbolId, x: 8, y: 8, width: 320, height: 32, style: { overflow: "visible" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
808
+ "div",
809
+ {
810
+ style: { display: "inline-flex", alignItems: "center", gap: 8, padding: "4px 8px", borderRadius: 6, background: "rgba(15,23,42,0.85)", color: "#fff", font: "12px system-ui, sans-serif" },
811
+ onPointerDown: (e) => e.stopPropagation(),
812
+ onDoubleClick: (e) => e.stopPropagation(),
813
+ onKeyDown: (e) => e.stopPropagation(),
814
+ children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
816
+ "Symbol: ",
817
+ state.symbolEdit.name
818
+ ] }),
819
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", "aria-label": "Exit symbol", style: { border: "1px solid rgba(255,255,255,0.4)", borderRadius: 4, padding: "0 6px", background: "transparent", color: "#fff", cursor: "pointer" }, onClick: () => controller.exitSymbolEdit(), children: "Done" })
820
+ ]
821
+ }
822
+ ) }),
649
823
  edit && editAt && // Width Point Edit (double-click with the width tool), beside the point.
650
824
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("foreignObject", { "data-vector-width-point-edit": "", x: editAt.x + 12, y: editAt.y + 12, width: 180, height: 140, style: { overflow: "visible" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorWidthPointEditor, { controller, edit, accentColor }, `${edit.nodeId}:${edit.index}:${edit.t}`) })
651
825
  ]
@@ -654,8 +828,8 @@ function VectorCanvas({
654
828
  }
655
829
 
656
830
  // src/vector/VectorToolbar.tsx
657
- var import_react5 = __toESM(require("react"), 1);
658
- var import_vector9 = require("@pixerate/editor/vector");
831
+ var import_react8 = __toESM(require("react"), 1);
832
+ var import_vector14 = require("@pixerate/editor/vector");
659
833
 
660
834
  // src/vector/VectorEffectsPanel.tsx
661
835
  var import_vector4 = require("@pixerate/editor/vector");
@@ -1234,16 +1408,528 @@ function VectorMaskPanel({ controller, className }) {
1234
1408
  ] });
1235
1409
  }
1236
1410
 
1237
- // src/vector/VectorGradientEditor.tsx
1411
+ // src/vector/VectorTextPanel.tsx
1412
+ var import_react5 = require("react");
1238
1413
  var import_vector8 = require("@pixerate/editor/vector");
1239
1414
  var import_jsx_runtime7 = require("react/jsx-runtime");
1415
+ var buttonClass5 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1416
+ var inputClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1417
+ var labelClass4 = "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
1418
+ var num = (v) => {
1419
+ const n = parseFloat(v);
1420
+ return Number.isFinite(n) ? n : null;
1421
+ };
1422
+ function VectorTextPanel({ controller, className }) {
1423
+ const state = useVectorEditorState(controller);
1424
+ const listId = `vector-fonts-${(0, import_react5.useId)().replace(/:/g, "")}`;
1425
+ const panel = (0, import_vector8.getVectorTextPanel)(state);
1426
+ if (!panel) return null;
1427
+ const f = panel.font;
1428
+ const style = (patch, merge) => (0, import_vector8.applyVectorTextStyle)(controller, patch, merge ? { merge } : {});
1429
+ const where = panel.target === "range" ? "Selected characters" : panel.target === "nodes" ? "Selected text" : "New text";
1430
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-text-panel": panel.target, "aria-label": "Text", children: [
1431
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1", children: [
1432
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: labelClass4, children: "Text" }),
1433
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: where })
1434
+ ] }),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1", children: [
1436
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1437
+ "input",
1438
+ {
1439
+ "aria-label": "Font family",
1440
+ className: inputClass,
1441
+ list: listId,
1442
+ placeholder: "Mixed",
1443
+ defaultValue: f.fontFamily ?? "",
1444
+ onKeyDown: (e) => {
1445
+ if (e.key === "Enter") e.target.blur();
1446
+ },
1447
+ onBlur: (e) => {
1448
+ const v = e.target.value.trim();
1449
+ if (v && v !== f.fontFamily) style({ fontFamily: v });
1450
+ }
1451
+ },
1452
+ `family:${f.fontFamily}`
1453
+ ),
1454
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("datalist", { id: listId, children: panel.families.map((fam) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: fam }, fam)) })
1455
+ ] }),
1456
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1", children: [
1457
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": "Font weight", className: inputClass, value: f.fontWeight ?? "", onChange: (e) => style({ fontWeight: Number(e.target.value) }), children: [
1458
+ f.fontWeight === null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "Mixed" }),
1459
+ import_vector8.VECTOR_FONT_WEIGHTS.map((w) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: w.value, children: w.label }, w.value)),
1460
+ f.fontWeight !== null && !import_vector8.VECTOR_FONT_WEIGHTS.some((w) => w.value === f.fontWeight) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: f.fontWeight, children: f.fontWeight })
1461
+ ] }),
1462
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1463
+ "input",
1464
+ {
1465
+ type: "number",
1466
+ "aria-label": "Font size",
1467
+ title: "Font size",
1468
+ className: `${inputClass} w-14 flex-none`,
1469
+ min: 0.5,
1470
+ step: 1,
1471
+ placeholder: "\u2014",
1472
+ value: f.fontSize === null ? "" : Math.round(f.fontSize * 100) / 100,
1473
+ onChange: (e) => {
1474
+ const v = num(e.target.value);
1475
+ if (v !== null && v >= 0.5) style({ fontSize: v }, "text-size");
1476
+ },
1477
+ onBlur: () => controller.endMergedEdit()
1478
+ }
1479
+ )
1480
+ ] }),
1481
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1482
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Bold", "aria-pressed": (f.fontWeight ?? 0) >= 600, onClick: () => style({ fontWeight: (f.fontWeight ?? 400) >= 600 ? 400 : 700 }), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: "B" }) }),
1483
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Italic", "aria-pressed": f.italic === true, onClick: () => style({ italic: !f.italic }), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { children: "I" }) }),
1484
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Underline", "aria-pressed": f.underline === true, onClick: () => style({ underline: !f.underline }), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("u", { children: "U" }) }),
1485
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Strike through", "aria-pressed": f.strikethrough === true, onClick: () => style({ strikethrough: !f.strikethrough }), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("s", { children: "S" }) })
1486
+ ] }),
1487
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1", children: [
1488
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex flex-1 items-center gap-1 text-xs text-foreground", title: "Tracking (1/1000 em)", children: [
1489
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "VA" }),
1490
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1491
+ "input",
1492
+ {
1493
+ type: "number",
1494
+ "aria-label": "Tracking",
1495
+ className: inputClass,
1496
+ step: 10,
1497
+ placeholder: "\u2014",
1498
+ value: f.tracking ?? "",
1499
+ onChange: (e) => style({ tracking: num(e.target.value) ?? 0 }, "text-tracking"),
1500
+ onBlur: () => controller.endMergedEdit()
1501
+ }
1502
+ )
1503
+ ] }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex flex-1 items-center gap-1 text-xs text-foreground", title: "Leading (empty: auto)", children: [
1505
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "\u2195" }),
1506
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1507
+ "input",
1508
+ {
1509
+ type: "number",
1510
+ "aria-label": "Leading",
1511
+ className: inputClass,
1512
+ min: 0,
1513
+ step: 1,
1514
+ placeholder: "Auto",
1515
+ value: f.leading ?? "",
1516
+ onChange: (e) => style({ leading: num(e.target.value) }, "text-leading"),
1517
+ onBlur: () => controller.endMergedEdit()
1518
+ }
1519
+ )
1520
+ ] })
1521
+ ] }),
1522
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex flex-wrap items-center gap-1", role: "group", "aria-label": "Alignment", children: import_vector8.VECTOR_TEXT_ALIGN_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1523
+ "button",
1524
+ {
1525
+ type: "button",
1526
+ className: buttonClass5,
1527
+ "aria-label": `Align ${o.label}`,
1528
+ "aria-pressed": panel.align === o.value,
1529
+ onClick: () => (0, import_vector8.applyVectorParagraphStyle)(controller, { align: o.value }),
1530
+ children: o.label
1531
+ },
1532
+ o.value
1533
+ )) }),
1534
+ panel.target !== "defaults" && panel.ids.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1535
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1536
+ "button",
1537
+ {
1538
+ type: "button",
1539
+ className: buttonClass5,
1540
+ "aria-pressed": panel.frame === "point",
1541
+ title: "Point text: no wrapping",
1542
+ onClick: () => controller.setTextFrame(panel.ids[0], { kind: "point" }),
1543
+ children: "Point"
1544
+ }
1545
+ ),
1546
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1547
+ "button",
1548
+ {
1549
+ type: "button",
1550
+ className: buttonClass5,
1551
+ "aria-pressed": panel.frame === "area",
1552
+ title: "Area text: wraps in a box",
1553
+ onClick: () => controller.setTextFrame(panel.ids[0], { kind: "area" }),
1554
+ children: "Area"
1555
+ }
1556
+ )
1557
+ ] }),
1558
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1559
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1560
+ "button",
1561
+ {
1562
+ type: "button",
1563
+ className: buttonClass5,
1564
+ "data-vector-text-action": "type-on-path",
1565
+ disabled: !panel.pathId,
1566
+ title: panel.pathId ? "Type on a Path: set text along the selected path" : "Select a path to set text along it",
1567
+ onClick: () => {
1568
+ const r = controller.typeOnPath(panel.pathId, { text: "Text on a path" });
1569
+ if (r.ok && r.id) controller.editText(r.id, { anchor: 0, focus: "Text on a path".length });
1570
+ },
1571
+ children: "Type on path"
1572
+ }
1573
+ ),
1574
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1575
+ "button",
1576
+ {
1577
+ type: "button",
1578
+ className: buttonClass5,
1579
+ "data-vector-text-action": "create-outlines",
1580
+ disabled: panel.target !== "nodes" || !panel.outlines,
1581
+ title: panel.outlines ? "Create Outlines: turn the text into paths" : "Create Outlines needs a font with outlines (load one)",
1582
+ onClick: () => controller.createOutlines({ ids: panel.ids }),
1583
+ children: "Create outlines"
1584
+ }
1585
+ )
1586
+ ] })
1587
+ ] });
1588
+ }
1589
+
1590
+ // src/vector/VectorSymbolsPanel.tsx
1591
+ var import_react6 = require("react");
1592
+ var import_vector9 = require("@pixerate/editor/vector");
1593
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1594
+ var buttonClass6 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1240
1595
  var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1241
- var numberClass4 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
1242
- var buttonClass5 = "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";
1596
+ var labelClass5 = "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
1597
+ function VectorSymbolsPanel({ controller, className }) {
1598
+ const state = useVectorEditorState(controller);
1599
+ const uid = (0, import_react6.useId)().replace(/:/g, "");
1600
+ const panel = (0, import_vector9.getVectorSymbolsPanel)(state, { idPrefix: `vsym${uid}-` });
1601
+ const [error, setError] = (0, import_react6.useState)(null);
1602
+ const report = (r) => setError(r.ok ? null : r.message ?? null);
1603
+ const inst = panel.instance;
1604
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-symbols-panel": "", "aria-label": "Symbols", children: [
1605
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1", children: [
1606
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: labelClass5, children: "Symbols" }),
1607
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, "data-vector-symbol-action": "make", disabled: !panel.canMake, title: "New Symbol from the selection (F8)", onClick: () => report(controller.makeSymbol()), children: "New" }),
1608
+ panel.editing && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, "data-vector-symbol-action": "exit", title: "Leave the symbol (Escape)", onClick: () => controller.exitSymbolEdit(), children: "Done" })
1609
+ ] }),
1610
+ panel.symbols.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-1", children: panel.symbols.map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { "data-vector-symbol": s.id, "data-editing": s.editing || void 0, className: "flex items-center gap-1", children: [
1611
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { viewBox: s.viewBox, width: 28, height: 28, className: "shrink-0 rounded border border-border bg-background", "aria-hidden": "true", children: [
1612
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorGradientDefs, { defs: s.scene.defs }),
1613
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorFilterDefs, { filters: s.scene.filters }),
1614
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorSceneNodes, { nodes: s.scene.nodes })
1615
+ ] }),
1616
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "min-w-0 flex-1 truncate text-xs text-foreground", title: `${s.name} \xB7 ${s.instances} instance${s.instances === 1 ? "" : "s"}`, children: [
1617
+ s.name,
1618
+ " ",
1619
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-muted-foreground", children: [
1620
+ "\xB7 ",
1621
+ s.instances
1622
+ ] })
1623
+ ] }),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, "aria-label": `Place ${s.name}`, title: "Place an instance", onClick: () => report(controller.placeSymbol(s.id)), children: "+" }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, "aria-label": `Edit ${s.name}`, "aria-pressed": s.editing, title: "Edit Symbol (isolation mode)", onClick: () => report(controller.editSymbol(s.id)), children: "\u270E" }),
1626
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, "aria-label": `Delete ${s.name}`, title: "Delete the symbol (its instances become groups)", onClick: () => report(controller.deleteSymbol(s.id, { detach: true })), children: "\xD7" })
1627
+ ] }, s.id)) }),
1628
+ inst && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-1", "data-vector-instance": inst.id, children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1", children: [
1630
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("select", { "aria-label": "Instance symbol", className: selectClass5, value: inst.symbolId, onChange: (e) => report(controller.replaceSymbol(e.target.value, { ids: [inst.id] })), children: panel.symbols.map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: s.id, children: s.name }, s.id)) }),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, title: "Break Link to Symbol: a plain group of its art", onClick: () => report(controller.detachInstances({ ids: [inst.id] })), children: "Detach" })
1632
+ ] }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
1634
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", "aria-label": "Scale strokes", checked: inst.scaleStrokes, onChange: (e) => report(controller.setInstanceOptions({ scaleStrokes: e.target.checked }, { ids: [inst.id] })) }),
1636
+ "Scale strokes"
1637
+ ] }),
1638
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Paint every fill of the art in this instance", children: [
1639
+ "Tint",
1640
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1641
+ "input",
1642
+ {
1643
+ type: "color",
1644
+ "aria-label": "Instance fill",
1645
+ className: "h-6 w-8 rounded border border-border",
1646
+ value: (0, import_vector9.vectorPaintToHex)(inst.fill ?? void 0) ?? "#000000",
1647
+ onChange: (e) => report(controller.setInstanceOptions({ fill: e.target.value }, { ids: [inst.id] }))
1648
+ }
1649
+ )
1650
+ ] }),
1651
+ inst.fill !== null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, "aria-label": "Clear tint", onClick: () => report(controller.setInstanceOptions({ fill: null }, { ids: [inst.id] })), children: "\xD7" })
1652
+ ] })
1653
+ ] }),
1654
+ error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-destructive", children: error })
1655
+ ] });
1656
+ }
1657
+
1658
+ // src/vector/VectorConstraintsPanel.tsx
1659
+ var import_react7 = require("react");
1660
+ var import_vector10 = require("@pixerate/editor/vector");
1661
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1662
+ var buttonClass7 = "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";
1663
+ var inputClass2 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1664
+ var labelClass6 = "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
1665
+ function VectorConstraintsPanel({ controller, className }) {
1666
+ const state = useVectorEditorState(controller);
1667
+ const panel = (0, import_vector10.getVectorConstraintPanel)(state);
1668
+ const [error, setError] = (0, import_react7.useState)(null);
1669
+ const [param, setParam] = (0, import_react7.useState)({ name: "", value: "" });
1670
+ const report = (r) => setError(r.ok ? null : r.message ?? null);
1671
+ const add = (a) => report(controller.addConstraint({ kind: a.kind, refs: a.refs }));
1672
+ if (!panel.actions.length && !panel.constraints.length && !state.document.constraints?.length && state.tool !== "direct") return null;
1673
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-constraints-panel": "", "data-dof-status": panel.dof.status, "aria-label": "Constraints", children: [
1674
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1", children: [
1675
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: labelClass6, children: "Constraints" }),
1676
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "flex-1 truncate text-[11px] text-muted-foreground", "data-vector-dof": "", children: panel.dof.message }),
1677
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: buttonClass7, title: "Auto Constrain: add what the selection (or the drawing) nearly satisfies", onClick: () => report(controller.autoConstrain()), children: "Auto" })
1678
+ ] }),
1679
+ panel.actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Add constraint", children: panel.actions.map((a) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: buttonClass7, "data-vector-constraint-action": a.kind, title: `Add ${a.label}`, onClick: () => add(a), children: a.label }, a.kind)) }),
1680
+ panel.constraints.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ul", { className: "space-y-1", children: panel.constraints.map((c) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { "data-vector-constraint": c.id, "data-satisfied": c.satisfied && !c.conflict ? "true" : "false", className: "flex items-center gap-1 text-xs text-foreground", children: [
1681
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: `min-w-0 flex-1 truncate ${c.conflict || !c.satisfied ? "text-destructive" : ""}`, children: [
1682
+ c.label,
1683
+ c.name ? ` ${c.name}` : ""
1684
+ ] }),
1685
+ c.value !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1686
+ "input",
1687
+ {
1688
+ "aria-label": `Value of ${c.name ?? c.id}`,
1689
+ className: `${inputClass2} w-16 flex-none`,
1690
+ defaultValue: c.value,
1691
+ onKeyDown: (e) => {
1692
+ if (e.key === "Enter") e.target.blur();
1693
+ },
1694
+ onBlur: (e) => {
1695
+ if (e.target.value.trim() !== c.value) report(controller.setConstraintValue(c.id, e.target.value));
1696
+ }
1697
+ },
1698
+ `${c.id}:${c.value}`
1699
+ ),
1700
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: buttonClass7, "aria-label": `Remove ${c.label} constraint`, onClick: () => report(controller.removeConstraints([c.id])), children: "\xD7" })
1701
+ ] }, c.id)) }),
1702
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-1", "aria-label": "Parameters", children: [
1703
+ panel.parameters.map((p) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1 text-xs", children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "w-12 truncate font-mono", children: p.name }),
1705
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1706
+ "input",
1707
+ {
1708
+ "aria-label": `Parameter ${p.name}`,
1709
+ className: inputClass2,
1710
+ defaultValue: p.value,
1711
+ onKeyDown: (e) => {
1712
+ if (e.key === "Enter") e.target.blur();
1713
+ },
1714
+ onBlur: (e) => {
1715
+ if (e.target.value.trim() !== p.value) report(controller.setConstraintValue(p.name, e.target.value));
1716
+ }
1717
+ },
1718
+ `${p.name}:${p.value}`
1719
+ ),
1720
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "w-10 text-right text-muted-foreground", children: p.result === null ? "!" : Math.round(p.result * 100) / 100 })
1721
+ ] }, p.name)),
1722
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1723
+ "form",
1724
+ {
1725
+ className: "flex items-center gap-1",
1726
+ onSubmit: (e) => {
1727
+ e.preventDefault();
1728
+ if (!param.name.trim()) return;
1729
+ const r = controller.setConstraintValue(param.name.trim(), param.value);
1730
+ report(r);
1731
+ if (r.ok) setParam({ name: "", value: "" });
1732
+ },
1733
+ children: [
1734
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { "aria-label": "New parameter name", placeholder: "name", className: `${inputClass2} w-12 flex-none`, value: param.name, onChange: (e) => setParam({ ...param, name: e.target.value }) }),
1735
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { "aria-label": "New parameter value", placeholder: "value", className: inputClass2, value: param.value, onChange: (e) => setParam({ ...param, value: e.target.value }) }),
1736
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "submit", className: buttonClass7, "aria-label": "Add parameter", children: "+" })
1737
+ ]
1738
+ }
1739
+ )
1740
+ ] }),
1741
+ error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-[11px] text-destructive", children: error })
1742
+ ] });
1743
+ }
1744
+
1745
+ // src/vector/VectorFramePanel.tsx
1746
+ var import_vector11 = require("@pixerate/editor/vector");
1747
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1748
+ var buttonClass8 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
1749
+ var selectClass6 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1750
+ var numberClass4 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1751
+ function VectorFramePanel({ controller, className }) {
1752
+ const state = useVectorEditorState(controller);
1753
+ const panel = (0, import_vector11.getVectorFramePanel)(state);
1754
+ const f = panel.frame;
1755
+ const ids = f ? [f.id] : void 0;
1756
+ const fillHex = f ? (0, import_vector11.vectorPaintToHex)(f.frame.fill ?? null) : null;
1757
+ const number = (key, label) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1758
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "w-4 text-muted-foreground", children: label }),
1759
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1760
+ "input",
1761
+ {
1762
+ type: "number",
1763
+ step: "1",
1764
+ min: key === "width" || key === "height" ? 1 : void 0,
1765
+ "aria-label": `Frame ${key}`,
1766
+ className: numberClass4,
1767
+ defaultValue: Math.round(f.frame[key] * 100) / 100,
1768
+ onKeyDown: (e) => {
1769
+ if (e.key === "Enter") e.currentTarget.blur();
1770
+ },
1771
+ onBlur: (e) => {
1772
+ const n = parseFloat(e.target.value);
1773
+ if (Number.isFinite(n) && n !== Math.round(f.frame[key] * 100) / 100 && (key === "x" || key === "y" || n > 0)) controller.setFrame({ [key]: n }, { ids });
1774
+ }
1775
+ },
1776
+ `${f.id}:${f.frame[key]}`
1777
+ )
1778
+ ] });
1779
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-frame-panel": "", "aria-label": "Frame", children: [
1780
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1781
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Frame" }),
1782
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1783
+ "button",
1784
+ {
1785
+ type: "button",
1786
+ "data-vector-frame-action": "frame-selection",
1787
+ className: buttonClass8,
1788
+ title: "Frame Selection: put the selection in a frame fitted to it",
1789
+ "aria-label": "Frame Selection",
1790
+ disabled: !state.selection.length,
1791
+ onClick: () => controller.frameSelection(),
1792
+ children: "Frame selection"
1793
+ }
1794
+ )
1795
+ ] }),
1796
+ f && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2", "data-vector-frame": f.id, children: [
1797
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-2 gap-1", children: [
1798
+ number("x", "X"),
1799
+ number("y", "Y"),
1800
+ number("width", "W"),
1801
+ number("height", "H")
1802
+ ] }),
1803
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
1804
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1805
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1806
+ "input",
1807
+ {
1808
+ type: "checkbox",
1809
+ "aria-label": "Frame fill on",
1810
+ checked: f.frame.fill !== void 0 && f.frame.fill !== null,
1811
+ onChange: (e) => controller.setFrame({ fill: e.target.checked ? fillHex ?? "#ffffff" : null }, { ids })
1812
+ }
1813
+ ),
1814
+ "Fill"
1815
+ ] }),
1816
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1817
+ "input",
1818
+ {
1819
+ type: "color",
1820
+ "aria-label": "Frame fill colour",
1821
+ className: "h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40",
1822
+ disabled: f.frame.fill === void 0 || f.frame.fill === null,
1823
+ value: fillHex ?? "#ffffff",
1824
+ onChange: (e) => controller.setFrame({ fill: e.target.value }, { ids, merge: "frame-fill" })
1825
+ }
1826
+ ),
1827
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Hide what lies outside the frame", children: [
1828
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { type: "checkbox", "aria-label": "Clip content", checked: f.frame.clip !== false, onChange: (e) => controller.setFrame({ clip: e.target.checked }, { ids }) }),
1829
+ "Clip"
1830
+ ] })
1831
+ ] })
1832
+ ] }),
1833
+ panel.resizing && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-1", "data-vector-resizing": "", children: [
1834
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1835
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "w-16", children: "Horizontal" }),
1836
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1837
+ "select",
1838
+ {
1839
+ "aria-label": "Horizontal constraint",
1840
+ className: selectClass6,
1841
+ value: panel.resizing.horizontal,
1842
+ onChange: (e) => controller.setResizing({ horizontal: e.target.value }, { ids: panel.resizing.ids }),
1843
+ children: [
1844
+ panel.resizing.horizontal === "mixed" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "mixed", disabled: true, children: "Mixed" }),
1845
+ import_vector11.VECTOR_HORIZONTAL_RESIZING.map((h) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: h, children: import_vector11.VECTOR_HORIZONTAL_RESIZING_LABELS[h] }, h))
1846
+ ]
1847
+ }
1848
+ )
1849
+ ] }),
1850
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1851
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "w-16", children: "Vertical" }),
1852
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1853
+ "select",
1854
+ {
1855
+ "aria-label": "Vertical constraint",
1856
+ className: selectClass6,
1857
+ value: panel.resizing.vertical,
1858
+ onChange: (e) => controller.setResizing({ vertical: e.target.value }, { ids: panel.resizing.ids }),
1859
+ children: [
1860
+ panel.resizing.vertical === "mixed" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "mixed", disabled: true, children: "Mixed" }),
1861
+ import_vector11.VECTOR_VERTICAL_RESIZING.map((v) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: v, children: import_vector11.VECTOR_VERTICAL_RESIZING_LABELS[v] }, v))
1862
+ ]
1863
+ }
1864
+ )
1865
+ ] })
1866
+ ] })
1867
+ ] });
1868
+ }
1869
+
1870
+ // src/vector/VectorConnectorPanel.tsx
1871
+ var import_vector12 = require("@pixerate/editor/vector");
1872
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1873
+ var buttonClass9 = "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";
1874
+ var selectClass7 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1875
+ var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1876
+ function VectorConnectorPanel({ controller, className }) {
1877
+ const state = useVectorEditorState(controller);
1878
+ const panel = (0, import_vector12.getVectorConnectorPanel)(state);
1879
+ if (!panel) return null;
1880
+ const ids = [panel.id];
1881
+ const end = (key, label, e) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1", "data-vector-connector-end": key, children: [
1882
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1883
+ e ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1884
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1885
+ "select",
1886
+ {
1887
+ "aria-label": `${label} side`,
1888
+ className: selectClass7,
1889
+ value: e.side ?? "auto",
1890
+ onChange: (ev) => controller.setConnector({ [key]: { nodeId: e.nodeId, side: ev.target.value } }, { ids }),
1891
+ children: import_vector12.VECTOR_CONNECTOR_SIDES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: s, children: import_vector12.VECTOR_CONNECTOR_SIDE_LABELS[s] }, s))
1892
+ }
1893
+ ),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: buttonClass9, title: "Unglue: the end stays where it is", "aria-label": `Unglue ${label.toLowerCase()}`, onClick: () => controller.setConnector({ [key]: null }, { ids }), children: "Unglue" })
1895
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Free" })
1896
+ ] });
1897
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-connector-panel": panel.id, "aria-label": "Connector", children: [
1898
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1", children: [
1899
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Connector" }),
1900
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("select", { "aria-label": "Connector route", className: selectClass7, value: panel.route, onChange: (e) => controller.setConnector({ route: e.target.value }, { ids }), children: import_vector12.VECTOR_CONNECTOR_ROUTES.map((r) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: r, children: import_vector12.VECTOR_CONNECTOR_ROUTE_LABELS[r] }, r)) }),
1901
+ panel.route === "elbow" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1902
+ "input",
1903
+ {
1904
+ type: "number",
1905
+ min: "0",
1906
+ step: "1",
1907
+ "aria-label": "Elbow radius",
1908
+ title: "Corner radius of the bends",
1909
+ className: numberClass5,
1910
+ value: panel.radius,
1911
+ onChange: (e) => {
1912
+ const n = parseFloat(e.target.value);
1913
+ if (Number.isFinite(n) && n >= 0) controller.setConnector({ radius: n }, { ids });
1914
+ }
1915
+ }
1916
+ )
1917
+ ] }),
1918
+ end("start", "Start", panel.start),
1919
+ end("end", "End", panel.end)
1920
+ ] });
1921
+ }
1922
+
1923
+ // src/vector/VectorGradientEditor.tsx
1924
+ var import_vector13 = require("@pixerate/editor/vector");
1925
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1926
+ var selectClass8 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1927
+ var numberClass6 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
1928
+ var buttonClass10 = "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";
1243
1929
  var percent = (v) => Math.round(v * 1e3) / 10;
1244
1930
  function VectorGradientEditor({ controller, className }) {
1245
1931
  const state = useVectorEditorState(controller);
1246
- const ed = (0, import_vector8.getVectorGradientEditor)(state);
1932
+ const ed = (0, import_vector13.getVectorGradientEditor)(state);
1247
1933
  if (!ed) return null;
1248
1934
  const g = ed.gradient;
1249
1935
  const sel = ed.selected;
@@ -1251,26 +1937,26 @@ function VectorGradientEditor({ controller, className }) {
1251
1937
  const setType = (value) => {
1252
1938
  if (value === "solid") {
1253
1939
  const first = ed.stops[0];
1254
- controller.setStyle({ [ed.target]: (0, import_vector8.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
1940
+ controller.setStyle({ [ed.target]: (0, import_vector13.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
1255
1941
  } else controller.setGradient({ type: value });
1256
1942
  };
1257
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
1258
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1259
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
1260
- ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
1261
- ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1943
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
1944
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1945
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
1946
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
1947
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1262
1948
  ] }),
1263
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1264
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
1265
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": "Gradient type", className: selectClass5, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
1266
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "solid", children: "Solid" }),
1267
- import_vector8.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: t, children: import_vector8.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
1949
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1950
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
1951
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("select", { "aria-label": "Gradient type", className: selectClass8, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
1952
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: "solid", children: "Solid" }),
1953
+ import_vector13.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: t, children: import_vector13.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
1268
1954
  ] }),
1269
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
1955
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
1270
1956
  ] }),
1271
- g && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1272
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
1273
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1957
+ g && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1958
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1274
1960
  "div",
1275
1961
  {
1276
1962
  role: "button",
@@ -1284,7 +1970,7 @@ function VectorGradientEditor({ controller, className }) {
1284
1970
  }
1285
1971
  }
1286
1972
  ),
1287
- ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1973
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1288
1974
  "button",
1289
1975
  {
1290
1976
  type: "button",
@@ -1298,21 +1984,21 @@ function VectorGradientEditor({ controller, className }) {
1298
1984
  s.index
1299
1985
  ))
1300
1986
  ] }),
1301
- sel && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
1302
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1987
+ sel && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1303
1989
  "input",
1304
1990
  {
1305
1991
  type: "color",
1306
1992
  "aria-label": "Stop colour",
1307
1993
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
1308
- value: (0, import_vector8.vectorPaintToHex)(sel.color) ?? "#000000",
1994
+ value: (0, import_vector13.vectorPaintToHex)(sel.color) ?? "#000000",
1309
1995
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
1310
1996
  onBlur: end
1311
1997
  }
1312
1998
  ),
1313
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1999
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1314
2000
  "Opacity",
1315
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2001
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1316
2002
  "input",
1317
2003
  {
1318
2004
  type: "number",
@@ -1320,7 +2006,7 @@ function VectorGradientEditor({ controller, className }) {
1320
2006
  max: 100,
1321
2007
  step: 5,
1322
2008
  "aria-label": "Stop opacity",
1323
- className: numberClass4,
2009
+ className: numberClass6,
1324
2010
  value: percent(sel.opacity ?? 1),
1325
2011
  onChange: (e) => {
1326
2012
  const n = parseFloat(e.target.value);
@@ -1329,9 +2015,9 @@ function VectorGradientEditor({ controller, className }) {
1329
2015
  }
1330
2016
  )
1331
2017
  ] }),
1332
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2018
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1333
2019
  "At",
1334
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2020
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1335
2021
  "input",
1336
2022
  {
1337
2023
  type: "number",
@@ -1339,7 +2025,7 @@ function VectorGradientEditor({ controller, className }) {
1339
2025
  max: 100,
1340
2026
  step: 1,
1341
2027
  "aria-label": "Stop position",
1342
- className: numberClass4,
2028
+ className: numberClass6,
1343
2029
  value: percent(sel.offset),
1344
2030
  onChange: (e) => {
1345
2031
  const n = parseFloat(e.target.value);
@@ -1348,9 +2034,9 @@ function VectorGradientEditor({ controller, className }) {
1348
2034
  }
1349
2035
  )
1350
2036
  ] }),
1351
- !sel.last && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2037
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1352
2038
  "Midpoint",
1353
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2039
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1354
2040
  "input",
1355
2041
  {
1356
2042
  type: "number",
@@ -1358,7 +2044,7 @@ function VectorGradientEditor({ controller, className }) {
1358
2044
  max: 95,
1359
2045
  step: 1,
1360
2046
  "aria-label": "Stop midpoint",
1361
- className: numberClass4,
2047
+ className: numberClass6,
1362
2048
  value: percent(sel.midpoint ?? 0.5),
1363
2049
  onChange: (e) => {
1364
2050
  const n = parseFloat(e.target.value);
@@ -1367,15 +2053,15 @@ function VectorGradientEditor({ controller, className }) {
1367
2053
  }
1368
2054
  )
1369
2055
  ] }),
1370
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2056
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
1371
2057
  ] }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1373
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Colours" }),
1374
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass5, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector8.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: i, children: import_vector8.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2058
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16", children: "Colours" }),
2060
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass8, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector13.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: i, children: import_vector13.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
1375
2061
  ] }),
1376
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1377
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Smooth" }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2062
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2063
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16", children: "Smooth" }),
2064
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1379
2065
  "input",
1380
2066
  {
1381
2067
  type: "range",
@@ -1391,26 +2077,26 @@ function VectorGradientEditor({ controller, className }) {
1391
2077
  }
1392
2078
  )
1393
2079
  ] }),
1394
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1395
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Spread" }),
1396
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Gradient spread", className: selectClass5, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector8.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: sp, children: import_vector8.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2080
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16", children: "Spread" }),
2082
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("select", { "aria-label": "Gradient spread", className: selectClass8, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector13.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: sp, children: import_vector13.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
1397
2083
  ] }),
1398
- ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1399
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "On stroke" }),
1400
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass5, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector8.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: m, children: import_vector8.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2084
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2085
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16", children: "On stroke" }),
2086
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass8, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector13.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: m, children: import_vector13.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
1401
2087
  ] })
1402
2088
  ] })
1403
2089
  ] });
1404
2090
  }
1405
2091
 
1406
2092
  // src/vector/VectorToolbar.tsx
1407
- var import_jsx_runtime8 = require("react/jsx-runtime");
2093
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1408
2094
  var VECTOR_SNAP_TITLE = "Snap to objects";
1409
2095
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
1410
- var buttonClass6 = "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";
2096
+ var buttonClass11 = "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";
1411
2097
  function VectorToolbar({
1412
2098
  controller,
1413
- tools = import_vector9.VECTOR_TOOLS,
2099
+ tools = import_vector14.VECTOR_TOOLS,
1414
2100
  showHistory = true,
1415
2101
  showArrange = true,
1416
2102
  showSnap = true,
@@ -1424,49 +2110,69 @@ function VectorToolbar({
1424
2110
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
1425
2111
  const editing = state.editing;
1426
2112
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
1427
- const maskSelected = state.selection.some((id) => (0, import_vector9.isVectorMaskGroup)(controller.getNode(id)));
1428
- const [live, setLive] = import_react5.default.useState(false);
2113
+ const maskSelected = state.selection.some((id) => (0, import_vector14.isVectorMaskGroup)(controller.getNode(id)));
2114
+ const [live, setLive] = import_react8.default.useState(false);
1429
2115
  const booleanDisabled = live ? count < 1 : count < 2;
1430
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
1431
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
1432
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2116
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: buttonClass11, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2117
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2118
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1433
2119
  "button",
1434
2120
  {
1435
2121
  type: "button",
1436
2122
  "data-vector-tool": tool,
1437
- className: buttonClass6,
2123
+ className: buttonClass11,
1438
2124
  "aria-pressed": state.tool === tool,
1439
- title: `${(0, import_vector9.vectorToolLabel)(tool)} (${import_vector9.VECTOR_TOOL_SHORTCUTS[tool]})`,
1440
- "aria-label": (0, import_vector9.vectorToolLabel)(tool),
2125
+ title: `${(0, import_vector14.vectorToolLabel)(tool)} (${import_vector14.VECTOR_TOOL_SHORTCUTS[tool]})`,
2126
+ "aria-label": (0, import_vector14.vectorToolLabel)(tool),
1441
2127
  onClick: () => controller.setTool(tool),
1442
- children: import_vector9.VECTOR_TOOL_GLYPHS[tool]
2128
+ children: import_vector14.VECTOR_TOOL_GLYPHS[tool]
1443
2129
  },
1444
2130
  tool
1445
2131
  )) }),
1446
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2132
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
1447
2133
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
1448
2134
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
1449
2135
  ] }),
1450
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1451
- "button",
1452
- {
1453
- type: "button",
1454
- "data-vector-snap": "",
1455
- className: buttonClass6,
1456
- "aria-pressed": state.snap.objects,
1457
- title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
1458
- "aria-label": VECTOR_SNAP_TITLE,
1459
- onClick: () => controller.toggleSnapping(),
1460
- children: "Snap"
1461
- }
1462
- ) }),
1463
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
1464
- import_vector9.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2136
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2137
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2138
+ "button",
2139
+ {
2140
+ type: "button",
2141
+ "data-vector-snap": "",
2142
+ className: buttonClass11,
2143
+ "aria-pressed": state.snap.objects,
2144
+ title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
2145
+ "aria-label": VECTOR_SNAP_TITLE,
2146
+ onClick: () => controller.toggleSnapping(),
2147
+ children: "Snap"
2148
+ }
2149
+ ),
2150
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2151
+ "select",
2152
+ {
2153
+ "data-vector-polar": "",
2154
+ "aria-label": "Polar tracking increment",
2155
+ title: "Polar tracking: the angles drawn points snap to from where they leave",
2156
+ className: "h-8 rounded-md border border-border bg-background px-1 text-xs",
2157
+ value: String((0, import_vector14.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2158
+ onChange: (e) => controller.setSnap({ angles: (0, import_vector14.vectorPolarAngles)(Number(e.target.value)) }),
2159
+ children: [
2160
+ (0, import_vector14.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "", children: "Custom" }),
2161
+ [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("option", { value: n, children: [
2162
+ n,
2163
+ "\xB0"
2164
+ ] }, n))
2165
+ ]
2166
+ }
2167
+ )
2168
+ ] }),
2169
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2170
+ import_vector14.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1465
2171
  "button",
1466
2172
  {
1467
2173
  type: "button",
1468
2174
  "data-vector-path-action": a.id,
1469
- className: buttonClass6,
2175
+ className: buttonClass11,
1470
2176
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
1471
2177
  "aria-label": a.title,
1472
2178
  disabled: !a.enabled(count, points),
@@ -1475,12 +2181,12 @@ function VectorToolbar({
1475
2181
  },
1476
2182
  a.id
1477
2183
  )),
1478
- import_vector9.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2184
+ import_vector14.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1479
2185
  "button",
1480
2186
  {
1481
2187
  type: "button",
1482
2188
  "data-vector-edit": t.key,
1483
- className: buttonClass6,
2189
+ className: buttonClass11,
1484
2190
  "aria-pressed": editing[t.key],
1485
2191
  title: t.title,
1486
2192
  "aria-label": t.title,
@@ -1489,9 +2195,9 @@ function VectorToolbar({
1489
2195
  },
1490
2196
  t.key
1491
2197
  )),
1492
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2198
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1493
2199
  "Smoothing",
1494
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2200
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1495
2201
  "input",
1496
2202
  {
1497
2203
  type: "range",
@@ -1505,9 +2211,9 @@ function VectorToolbar({
1505
2211
  }
1506
2212
  )
1507
2213
  ] }),
1508
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2214
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1509
2215
  "Size",
1510
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2216
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1511
2217
  "input",
1512
2218
  {
1513
2219
  type: "number",
@@ -1523,26 +2229,26 @@ function VectorToolbar({
1523
2229
  }
1524
2230
  )
1525
2231
  ] }),
1526
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2232
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1527
2233
  "button",
1528
2234
  {
1529
2235
  type: "button",
1530
2236
  "data-vector-fill-gap": "",
1531
- className: buttonClass6,
2237
+ className: buttonClass11,
1532
2238
  "aria-pressed": editing.fillGap > 0,
1533
2239
  title: "Close small gaps between path ends when filling",
1534
2240
  "aria-label": "Close gaps",
1535
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector9.VECTOR_FILL_GAP }),
2241
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector14.VECTOR_FILL_GAP }),
1536
2242
  children: "Gaps"
1537
2243
  }
1538
2244
  )
1539
2245
  ] }),
1540
- showDistort && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2246
+ showDistort && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1541
2247
  "button",
1542
2248
  {
1543
2249
  type: "button",
1544
2250
  "data-vector-free-distort": "",
1545
- className: buttonClass6,
2251
+ className: buttonClass11,
1546
2252
  "aria-pressed": state.freeDistort,
1547
2253
  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)`,
1548
2254
  "aria-label": "Free Distort",
@@ -1550,15 +2256,15 @@ function VectorToolbar({
1550
2256
  children: "Distort"
1551
2257
  }
1552
2258
  ) }),
1553
- showBlend && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorBlendControls, { controller }),
1554
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1555
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
1556
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2259
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorBlendControls, { controller }),
2260
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2261
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2262
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1557
2263
  "button",
1558
2264
  {
1559
2265
  type: "button",
1560
2266
  "data-vector-live-boolean": "",
1561
- className: buttonClass6,
2267
+ className: buttonClass11,
1562
2268
  "aria-pressed": live,
1563
2269
  title: "Live booleans: keep the shapes editable",
1564
2270
  "aria-label": "Live booleans",
@@ -1575,12 +2281,12 @@ function VectorToolbar({
1575
2281
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
1576
2282
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
1577
2283
  ] }),
1578
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector9.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2284
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector14.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1579
2285
  "button",
1580
2286
  {
1581
2287
  type: "button",
1582
2288
  "data-vector-pathfinder": a.id,
1583
- className: buttonClass6,
2289
+ className: buttonClass11,
1584
2290
  title: a.title,
1585
2291
  "aria-label": a.title,
1586
2292
  disabled: count < a.min,
@@ -1589,7 +2295,7 @@ function VectorToolbar({
1589
2295
  },
1590
2296
  a.id
1591
2297
  )) }),
1592
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2298
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
1593
2299
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
1594
2300
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
1595
2301
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -1604,22 +2310,22 @@ function VectorToolbar({
1604
2310
  }
1605
2311
  function VectorBlendControls({ controller }) {
1606
2312
  const state = useVectorEditorState(controller);
1607
- const blend = (0, import_vector9.getVectorBlendControls)(state);
2313
+ const blend = (0, import_vector14.getVectorBlendControls)(state);
1608
2314
  const o = blend.options;
1609
2315
  const selected = blend.ids.length > 0;
1610
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
1611
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass6, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
1612
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2316
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2317
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass11, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2318
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1613
2319
  "select",
1614
2320
  {
1615
2321
  "aria-label": "Blend spacing",
1616
2322
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
1617
2323
  value: o.spacing,
1618
- onChange: (e) => (0, import_vector9.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
1619
- children: import_vector9.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: sp, children: import_vector9.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2324
+ onChange: (e) => (0, import_vector14.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2325
+ children: import_vector14.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: sp, children: import_vector14.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
1620
2326
  }
1621
2327
  ),
1622
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2328
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1623
2329
  "input",
1624
2330
  {
1625
2331
  type: "number",
@@ -1631,29 +2337,29 @@ function VectorBlendControls({ controller }) {
1631
2337
  onChange: (e) => {
1632
2338
  const n = parseFloat(e.target.value);
1633
2339
  if (!Number.isFinite(n) || n <= 0) return;
1634
- (0, import_vector9.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2340
+ (0, import_vector14.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
1635
2341
  }
1636
2342
  }
1637
2343
  ),
1638
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2344
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1639
2345
  "button",
1640
2346
  {
1641
2347
  type: "button",
1642
2348
  "data-vector-blend-action": "orient",
1643
- className: buttonClass6,
2349
+ className: buttonClass11,
1644
2350
  "aria-pressed": o.orientToSpine,
1645
2351
  title: "Orient steps to the spine",
1646
2352
  "aria-label": "Orient to spine",
1647
- onClick: () => (0, import_vector9.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2353
+ onClick: () => (0, import_vector14.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
1648
2354
  children: "\u293F"
1649
2355
  }
1650
2356
  ),
1651
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2357
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1652
2358
  "button",
1653
2359
  {
1654
2360
  type: "button",
1655
2361
  "data-vector-blend-action": "spine",
1656
- className: buttonClass6,
2362
+ className: buttonClass11,
1657
2363
  title: "Replace Spine (select a blend and a path)",
1658
2364
  "aria-label": "Replace Spine",
1659
2365
  disabled: !blend.canReplaceSpine,
@@ -1661,15 +2367,15 @@ function VectorBlendControls({ controller }) {
1661
2367
  children: "\u2307"
1662
2368
  }
1663
2369
  ),
1664
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass6, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
1665
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass6, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2370
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass11, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2371
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass11, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
1666
2372
  ] });
1667
2373
  }
1668
- var selectClass6 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1669
- var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2374
+ var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2375
+ var numberClass7 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1670
2376
  function VectorStylePanel({ controller, className }) {
1671
2377
  const state = useVectorEditorState(controller);
1672
- const style = (0, import_vector9.getVectorSelectionStyle)(state);
2378
+ const style = (0, import_vector14.getVectorSelectionStyle)(state);
1673
2379
  const d = state.defaults;
1674
2380
  const source = style ?? {
1675
2381
  fill: d.fill ?? null,
@@ -1682,9 +2388,10 @@ function VectorStylePanel({ controller, className }) {
1682
2388
  arrowAlign: d.arrowAlign ?? "extend",
1683
2389
  startArrowScale: d.startArrowScale ?? 1,
1684
2390
  endArrowScale: d.endArrowScale ?? 1,
1685
- widthProfile: (0, import_vector9.widthProfilePresetId)(d.strokeWidthProfile),
2391
+ widthProfile: (0, import_vector14.widthProfilePresetId)(d.strokeWidthProfile),
1686
2392
  cornerRadius: d.cornerRadius,
1687
2393
  cornerKind: d.cornerKind ?? "round",
2394
+ cornerSmoothing: 0,
1688
2395
  corners: 1,
1689
2396
  hasOpenPath: true
1690
2397
  };
@@ -1695,19 +2402,19 @@ function VectorStylePanel({ controller, className }) {
1695
2402
  };
1696
2403
  const setProfile = (id) => {
1697
2404
  if (style) controller.setWidthProfile(id);
1698
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector9.widthProfilePreset)(id)?.length ? (0, import_vector9.widthProfilePreset)(id) : null });
2405
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector14.widthProfilePreset)(id)?.length ? (0, import_vector14.widthProfilePreset)(id) : null });
1699
2406
  };
1700
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2407
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
1701
2408
  const arrowRow = (end, label) => {
1702
2409
  const key = end === "start" ? "startArrow" : "endArrow";
1703
2410
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
1704
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1705
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1706
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass6, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
1707
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "None" }),
1708
- import_vector9.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: h, children: import_vector9.VECTOR_ARROWHEAD_LABELS[h] }, h))
2411
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2412
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2413
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass9, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2414
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "", children: "None" }),
2415
+ import_vector14.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: h, children: import_vector14.VECTOR_ARROWHEAD_LABELS[h] }, h))
1709
2416
  ] }),
1710
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2417
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1711
2418
  "input",
1712
2419
  {
1713
2420
  type: "number",
@@ -1715,7 +2422,7 @@ function VectorStylePanel({ controller, className }) {
1715
2422
  step: 0.25,
1716
2423
  "aria-label": `${label} arrowhead scale`,
1717
2424
  title: "Scale (\xD7 stroke width)",
1718
- className: numberClass5,
2425
+ className: numberClass7,
1719
2426
  value: source[scaleKey],
1720
2427
  onChange: (e) => {
1721
2428
  const n = parseFloat(e.target.value);
@@ -1731,14 +2438,14 @@ function VectorStylePanel({ controller, className }) {
1731
2438
  };
1732
2439
  const paintRow = (key, label) => {
1733
2440
  const paint = source[key];
1734
- const hex = (0, import_vector9.vectorPaintToHex)(paint);
2441
+ const hex = (0, import_vector14.vectorPaintToHex)(paint);
1735
2442
  const on = paint !== null && paint !== "none";
1736
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1737
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1738
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2443
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2444
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2445
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
1739
2446
  label
1740
2447
  ] }),
1741
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2448
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1742
2449
  "input",
1743
2450
  {
1744
2451
  type: "color",
@@ -1749,18 +2456,18 @@ function VectorStylePanel({ controller, className }) {
1749
2456
  onChange: (e) => apply({ [key]: e.target.value })
1750
2457
  }
1751
2458
  ),
1752
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
1753
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2459
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2460
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1754
2461
  ] });
1755
2462
  };
1756
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
1757
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2463
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2464
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
1758
2465
  paintRow("fill", "Fill"),
1759
2466
  paintRow("stroke", "Stroke"),
1760
- style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorGradientEditor, { controller }),
1761
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1762
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Width" }),
1763
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2467
+ style && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorGradientEditor, { controller }),
2468
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2469
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16", children: "Width" }),
2470
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1764
2471
  "input",
1765
2472
  {
1766
2473
  type: "number",
@@ -1776,33 +2483,33 @@ function VectorStylePanel({ controller, className }) {
1776
2483
  }
1777
2484
  )
1778
2485
  ] }),
1779
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
1780
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Align" }),
1781
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass6, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector9.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: a, children: import_vector9.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2486
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2487
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16", children: "Align" }),
2488
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass9, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector14.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: a, children: import_vector14.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
1782
2489
  mixed("strokeAlign")
1783
2490
  ] }),
1784
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
1785
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Profile" }),
1786
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2491
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2492
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16", children: "Profile" }),
2493
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1787
2494
  "select",
1788
2495
  {
1789
2496
  "aria-label": "Width profile",
1790
- className: selectClass6,
2497
+ className: selectClass9,
1791
2498
  value: source.widthProfile,
1792
2499
  onChange: (e) => {
1793
2500
  if (e.target.value !== "custom") setProfile(e.target.value);
1794
2501
  },
1795
2502
  children: [
1796
- import_vector9.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: p.id, children: p.label }, p.id)),
1797
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2503
+ import_vector14.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: p.id, children: p.label }, p.id)),
2504
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
1798
2505
  ]
1799
2506
  }
1800
2507
  ),
1801
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2508
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1802
2509
  "button",
1803
2510
  {
1804
2511
  type: "button",
1805
- className: buttonClass6,
2512
+ className: buttonClass11,
1806
2513
  title: "Reset to uniform width",
1807
2514
  "aria-label": "Reset to uniform width",
1808
2515
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -1812,27 +2519,27 @@ function VectorStylePanel({ controller, className }) {
1812
2519
  ),
1813
2520
  mixed("widthProfile")
1814
2521
  ] }),
1815
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2522
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1816
2523
  arrowRow("start", "Start"),
1817
2524
  arrowRow("end", "End"),
1818
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
1819
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Heads" }),
1820
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass6, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
1821
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "extend", children: "Tip past end" }),
1822
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "tip", children: "Tip at end" })
2525
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2526
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16", children: "Heads" }),
2527
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass9, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2528
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "extend", children: "Tip past end" }),
2529
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "tip", children: "Tip at end" })
1823
2530
  ] })
1824
2531
  ] })
1825
2532
  ] }),
1826
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
1827
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
1828
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2533
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2534
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2535
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1829
2536
  "input",
1830
2537
  {
1831
2538
  type: "number",
1832
2539
  min: 0,
1833
2540
  step: 1,
1834
2541
  "aria-label": "Corner radius",
1835
- className: numberClass5,
2542
+ className: numberClass7,
1836
2543
  value: Math.round(source.cornerRadius * 100) / 100,
1837
2544
  onChange: (e) => {
1838
2545
  const n = parseFloat(e.target.value);
@@ -1840,12 +2547,29 @@ function VectorStylePanel({ controller, className }) {
1840
2547
  }
1841
2548
  }
1842
2549
  ),
1843
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("select", { "aria-label": "Corner kind", className: selectClass6, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector9.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: k, children: import_vector9.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2550
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("select", { "aria-label": "Corner kind", className: selectClass9, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector14.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: k, children: import_vector14.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
1844
2551
  mixed("cornerRadius")
1845
2552
  ] }),
1846
- style && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1847
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Opacity" }),
1848
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2553
+ style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
2554
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16", children: "Smoothing" }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2556
+ "input",
2557
+ {
2558
+ type: "range",
2559
+ min: 0,
2560
+ max: 1,
2561
+ step: 0.05,
2562
+ "aria-label": "Corner smoothing",
2563
+ className: "h-4 flex-1 accent-primary",
2564
+ value: source.cornerSmoothing,
2565
+ onChange: (e) => controller.setCornerSmoothing(parseFloat(e.target.value), { merge: "corner-smoothing" }),
2566
+ onPointerUp: () => controller.endMergedEdit()
2567
+ }
2568
+ )
2569
+ ] }),
2570
+ style && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2571
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16", children: "Opacity" }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1849
2573
  "input",
1850
2574
  {
1851
2575
  type: "range",
@@ -1859,21 +2583,26 @@ function VectorStylePanel({ controller, className }) {
1859
2583
  }
1860
2584
  )
1861
2585
  ] }),
1862
- style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorAppearancePanel, { controller }),
1863
- style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorEffectsPanel, { controller }),
1864
- style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorEnvelopePanel, { controller }),
1865
- style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorMaskPanel, { controller })
2586
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorTextPanel, { controller }),
2587
+ style && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorAppearancePanel, { controller }),
2588
+ style && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorEffectsPanel, { controller }),
2589
+ style && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorEnvelopePanel, { controller }),
2590
+ style && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorMaskPanel, { controller }),
2591
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorFramePanel, { controller }),
2592
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorConnectorPanel, { controller }),
2593
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorConstraintsPanel, { controller }),
2594
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VectorSymbolsPanel, { controller })
1866
2595
  ] });
1867
2596
  }
1868
2597
 
1869
2598
  // src/vector/VectorEditor.tsx
1870
- var import_jsx_runtime9 = require("react/jsx-runtime");
2599
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1871
2600
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
1872
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
1873
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(VectorToolbar, { controller, tools }),
1874
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
1875
- /* @__PURE__ */ (0, import_jsx_runtime9.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_runtime9.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
1876
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
2601
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VectorToolbar, { controller, tools }),
2603
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2604
+ /* @__PURE__ */ (0, import_jsx_runtime14.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_runtime14.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
2605
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
1877
2606
  ] })
1878
2607
  ] });
1879
2608
  }
@@ -1881,10 +2610,13 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
1881
2610
  0 && (module.exports = {
1882
2611
  VectorAppearancePanel,
1883
2612
  VectorCanvas,
2613
+ VectorConnectorPanel,
2614
+ VectorConstraintsPanel,
1884
2615
  VectorEditor,
1885
2616
  VectorEffectsPanel,
1886
2617
  VectorEnvelopePanel,
1887
2618
  VectorFilterDefs,
2619
+ VectorFramePanel,
1888
2620
  VectorGradientAnnotator,
1889
2621
  VectorGradientDefs,
1890
2622
  VectorGradientEditor,
@@ -1892,6 +2624,9 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
1892
2624
  VectorMaskedNodes,
1893
2625
  VectorSceneNodes,
1894
2626
  VectorStylePanel,
2627
+ VectorSymbolsPanel,
2628
+ VectorTextElement,
2629
+ VectorTextPanel,
1895
2630
  VectorToolbar,
1896
2631
  VectorWidthPointEditor,
1897
2632
  useVectorCommands,