@pixerate/editor-react 0.73.0 → 0.75.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.
@@ -45,11 +45,13 @@ __export(vector_exports, {
45
45
  VectorMaskPanel: () => VectorMaskPanel,
46
46
  VectorMaskedNodes: () => VectorMaskedNodes,
47
47
  VectorSceneNodes: () => VectorSceneNodes,
48
+ VectorShadingPanel: () => VectorShadingPanel,
48
49
  VectorStylePanel: () => VectorStylePanel,
49
50
  VectorSymbolsPanel: () => VectorSymbolsPanel,
50
51
  VectorTextElement: () => VectorTextElement,
51
52
  VectorTextPanel: () => VectorTextPanel,
52
53
  VectorToolbar: () => VectorToolbar,
54
+ VectorTracePanel: () => VectorTracePanel,
53
55
  VectorWidthPointEditor: () => VectorWidthPointEditor,
54
56
  useVectorCommands: () => useVectorCommands,
55
57
  useVectorEditor: () => useVectorEditor,
@@ -693,6 +695,10 @@ function VectorCanvas({
693
695
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M-3 0H3M0 -3V3", stroke: "#dc2626" })
694
696
  ] })
695
697
  ] }),
698
+ overlay.shading.map((sh) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-shading": sh.kind, children: [
699
+ sh.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
700
+ sh.points.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-shading-point": p.index, "data-selected": p.selected || void 0, cx: p.x, cy: p.y, r: p.selected ? 6 : 4.5, fill: p.color, stroke: p.selected ? accentColor : "#ffffff", strokeWidth: p.selected ? 2 : 1.5 }, p.index))
701
+ ] }, `shading:${sh.nodeId}`)),
696
702
  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
703
  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
704
  /* @__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 }),
@@ -828,8 +834,8 @@ function VectorCanvas({
828
834
  }
829
835
 
830
836
  // src/vector/VectorToolbar.tsx
831
- var import_react8 = __toESM(require("react"), 1);
832
- var import_vector14 = require("@pixerate/editor/vector");
837
+ var import_react10 = __toESM(require("react"), 1);
838
+ var import_vector15 = require("@pixerate/editor/vector");
833
839
 
834
840
  // src/vector/VectorEffectsPanel.tsx
835
841
  var import_vector4 = require("@pixerate/editor/vector");
@@ -1920,16 +1926,130 @@ function VectorConnectorPanel({ controller, className }) {
1920
1926
  ] });
1921
1927
  }
1922
1928
 
1923
- // src/vector/VectorGradientEditor.tsx
1929
+ // src/vector/VectorShadingPanel.tsx
1930
+ var import_react8 = require("react");
1924
1931
  var import_vector13 = require("@pixerate/editor/vector");
1925
1932
  var import_jsx_runtime12 = require("react/jsx-runtime");
1933
+ var buttonClass10 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
1934
+ var numberClass6 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
1935
+ function VectorShadingPanel({ controller, className }) {
1936
+ const state = useVectorEditorState(controller);
1937
+ const [rows, setRows] = (0, import_react8.useState)(2);
1938
+ const [columns, setColumns] = (0, import_react8.useState)(2);
1939
+ const path = state.selection.map((id) => controller.getNode(id)).find((n) => n?.type === "path");
1940
+ if (!path) return null;
1941
+ const fill = path.fill;
1942
+ const sel = state.shadingPoint?.nodeId === path.id ? state.shadingPoint : null;
1943
+ const mesh = (0, import_vector13.isVectorMeshGradient)(fill) ? fill : null;
1944
+ const free = (0, import_vector13.isVectorFreeformGradient)(fill) ? fill : null;
1945
+ const color = sel ? mesh ? mesh.colors[sel.index] : free?.points[sel.index]?.color : void 0;
1946
+ const opacity = sel ? mesh ? mesh.opacities?.[sel.index] ?? 1 : free?.points[sel.index]?.opacity ?? 1 : 1;
1947
+ const spread = sel && free ? free.points[sel.index]?.spread ?? 0 : 0;
1948
+ const edit = (patch, merge) => {
1949
+ if (!sel) return;
1950
+ if (mesh) controller.setMeshPoint(path.id, sel.index, patch, merge ? { merge } : {});
1951
+ else controller.setFreeformPoint(path.id, sel.index, patch, merge ? { merge } : {});
1952
+ };
1953
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-shading-panel": path.id, "aria-label": "Mesh and freeform gradient", children: [
1954
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1955
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Shading" }),
1956
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh rows", title: "Mesh rows", className: numberClass6, value: rows, onChange: (e) => setRows(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
1957
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-xs text-muted-foreground", children: "\xD7" }),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh columns", title: "Mesh columns", className: numberClass6, value: columns, onChange: (e) => setColumns(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
1959
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "data-vector-shading-action": "mesh", title: "Create Gradient Mesh: smooth patches whose points blend colours (edit them with the Gradient tool)", onClick: () => controller.setMeshGradient({ rows, columns }, { ids: [path.id] }), children: "Mesh" }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "data-vector-shading-action": "freeform", title: "Freeform Gradient: colour points blended across the shape (click inside with the Gradient tool to add one)", onClick: () => controller.setFreeformGradient({}, { ids: [path.id] }), children: "Freeform" })
1961
+ ] }),
1962
+ (mesh || free) && !sel && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[11px] text-muted-foreground", children: [
1963
+ "Gradient tool (G): drag points; ",
1964
+ free ? "click inside to add one, Alt-click to remove." : "pick one to colour it."
1965
+ ] }),
1966
+ sel && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap items-center gap-2", "data-vector-shading-point": sel.index, children: [
1967
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "w-16 text-xs text-foreground", children: [
1968
+ "Point ",
1969
+ sel.index
1970
+ ] }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: (0, import_vector13.vectorPaintToHex)(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
1972
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point opacity", title: "Opacity", className: "h-4 w-20 accent-primary", value: opacity, onChange: (e) => edit({ opacity: parseFloat(e.target.value) }, "shading-opacity") }),
1973
+ free && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1974
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point spread", title: "Spread: pure colour round the point", className: "h-4 w-20 accent-primary", value: spread, onChange: (e) => edit({ spread: parseFloat(e.target.value) }, "shading-spread") }),
1975
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass10, "aria-label": "Remove point", onClick: () => controller.removeFreeformPoint(path.id, sel.index), children: "Remove" })
1976
+ ] })
1977
+ ] })
1978
+ ] });
1979
+ }
1980
+
1981
+ // src/vector/VectorTracePanel.tsx
1982
+ var import_react9 = require("react");
1983
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1984
+ var buttonClass11 = "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";
1926
1985
  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";
1986
+ var numberClass7 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
1987
+ async function filePixels(file, max = 1e6) {
1988
+ const bitmap = await createImageBitmap(file);
1989
+ const k = Math.min(1, Math.sqrt(max / (bitmap.width * bitmap.height)));
1990
+ const width = Math.max(1, Math.round(bitmap.width * k));
1991
+ const height = Math.max(1, Math.round(bitmap.height * k));
1992
+ const canvas = document.createElement("canvas");
1993
+ canvas.width = width;
1994
+ canvas.height = height;
1995
+ const ctx = canvas.getContext("2d");
1996
+ ctx.drawImage(bitmap, 0, 0, width, height);
1997
+ return { width, height, data: ctx.getImageData(0, 0, width, height).data };
1998
+ }
1999
+ function VectorTracePanel({ controller, className }) {
2000
+ const input = (0, import_react9.useRef)(null);
2001
+ const [mode, setMode] = (0, import_react9.useState)("threshold");
2002
+ const [colors, setColors] = (0, import_react9.useState)(6);
2003
+ const [message, setMessage] = (0, import_react9.useState)(null);
2004
+ const trace = async (file) => {
2005
+ try {
2006
+ const pixels = await filePixels(file);
2007
+ const doc = controller.getState().document;
2008
+ const width = Math.min(doc.width, pixels.width);
2009
+ const r = controller.traceImage(pixels, { mode, colors, width, name: file.name.replace(/\.[^.]+$/, "") || "Image Trace" });
2010
+ setMessage(r.ok ? null : r.message);
2011
+ } catch {
2012
+ setMessage("That file could not be read as an image.");
2013
+ }
2014
+ };
2015
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-trace-panel": "", "aria-label": "Image trace", children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1", children: [
2017
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
2018
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("select", { "aria-label": "Trace mode", className: selectClass8, value: mode, onChange: (e) => setMode(e.target.value), children: [
2019
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "threshold", children: "Black & white" }),
2020
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "color", children: "Colours" })
2021
+ ] }),
2022
+ mode === "color" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("input", { type: "number", min: 2, max: 32, "aria-label": "Trace colours", title: "Colours", className: numberClass7, value: colors, onChange: (e) => setColors(Math.max(2, Math.min(32, parseInt(e.target.value, 10) || 2))) }),
2023
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: buttonClass11, "data-vector-trace-action": "", title: "Image Trace: pick an image to turn into paths", onClick: () => input.current?.click(), children: "Image\u2026" }),
2024
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2025
+ "input",
2026
+ {
2027
+ ref: input,
2028
+ type: "file",
2029
+ accept: "image/*",
2030
+ hidden: true,
2031
+ onChange: (e) => {
2032
+ const file = e.target.files?.[0];
2033
+ e.target.value = "";
2034
+ if (file) void trace(file);
2035
+ }
2036
+ }
2037
+ )
2038
+ ] }),
2039
+ message && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[11px] text-red-600", children: message })
2040
+ ] });
2041
+ }
2042
+
2043
+ // src/vector/VectorGradientEditor.tsx
2044
+ var import_vector14 = require("@pixerate/editor/vector");
2045
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2046
+ var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2047
+ var numberClass8 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2048
+ var buttonClass12 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1929
2049
  var percent = (v) => Math.round(v * 1e3) / 10;
1930
2050
  function VectorGradientEditor({ controller, className }) {
1931
2051
  const state = useVectorEditorState(controller);
1932
- const ed = (0, import_vector13.getVectorGradientEditor)(state);
2052
+ const ed = (0, import_vector14.getVectorGradientEditor)(state);
1933
2053
  if (!ed) return null;
1934
2054
  const g = ed.gradient;
1935
2055
  const sel = ed.selected;
@@ -1937,26 +2057,26 @@ function VectorGradientEditor({ controller, className }) {
1937
2057
  const setType = (value) => {
1938
2058
  if (value === "solid") {
1939
2059
  const first = ed.stops[0];
1940
- controller.setStyle({ [ed.target]: (0, import_vector13.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2060
+ controller.setStyle({ [ed.target]: (0, import_vector14.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
1941
2061
  } else controller.setGradient({ type: value });
1942
2062
  };
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" })
2063
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2064
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2065
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2066
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2067
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1948
2068
  ] }),
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))
2069
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2070
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2071
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("select", { "aria-label": "Gradient type", className: selectClass9, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2072
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "solid", children: "Solid" }),
2073
+ import_vector14.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: t, children: import_vector14.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
1954
2074
  ] }),
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" })
2075
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
1956
2076
  ] }),
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)(
2077
+ g && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2078
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2079
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1960
2080
  "div",
1961
2081
  {
1962
2082
  role: "button",
@@ -1970,7 +2090,7 @@ function VectorGradientEditor({ controller, className }) {
1970
2090
  }
1971
2091
  }
1972
2092
  ),
1973
- ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2093
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1974
2094
  "button",
1975
2095
  {
1976
2096
  type: "button",
@@ -1984,21 +2104,21 @@ function VectorGradientEditor({ controller, className }) {
1984
2104
  s.index
1985
2105
  ))
1986
2106
  ] }),
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)(
2107
+ sel && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2108
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1989
2109
  "input",
1990
2110
  {
1991
2111
  type: "color",
1992
2112
  "aria-label": "Stop colour",
1993
2113
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
1994
- value: (0, import_vector13.vectorPaintToHex)(sel.color) ?? "#000000",
2114
+ value: (0, import_vector14.vectorPaintToHex)(sel.color) ?? "#000000",
1995
2115
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
1996
2116
  onBlur: end
1997
2117
  }
1998
2118
  ),
1999
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2119
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2000
2120
  "Opacity",
2001
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2121
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2002
2122
  "input",
2003
2123
  {
2004
2124
  type: "number",
@@ -2006,7 +2126,7 @@ function VectorGradientEditor({ controller, className }) {
2006
2126
  max: 100,
2007
2127
  step: 5,
2008
2128
  "aria-label": "Stop opacity",
2009
- className: numberClass6,
2129
+ className: numberClass8,
2010
2130
  value: percent(sel.opacity ?? 1),
2011
2131
  onChange: (e) => {
2012
2132
  const n = parseFloat(e.target.value);
@@ -2015,9 +2135,9 @@ function VectorGradientEditor({ controller, className }) {
2015
2135
  }
2016
2136
  )
2017
2137
  ] }),
2018
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2138
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2019
2139
  "At",
2020
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2140
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2021
2141
  "input",
2022
2142
  {
2023
2143
  type: "number",
@@ -2025,7 +2145,7 @@ function VectorGradientEditor({ controller, className }) {
2025
2145
  max: 100,
2026
2146
  step: 1,
2027
2147
  "aria-label": "Stop position",
2028
- className: numberClass6,
2148
+ className: numberClass8,
2029
2149
  value: percent(sel.offset),
2030
2150
  onChange: (e) => {
2031
2151
  const n = parseFloat(e.target.value);
@@ -2034,9 +2154,9 @@ function VectorGradientEditor({ controller, className }) {
2034
2154
  }
2035
2155
  )
2036
2156
  ] }),
2037
- !sel.last && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2157
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2038
2158
  "Midpoint",
2039
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2159
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2040
2160
  "input",
2041
2161
  {
2042
2162
  type: "number",
@@ -2044,7 +2164,7 @@ function VectorGradientEditor({ controller, className }) {
2044
2164
  max: 95,
2045
2165
  step: 1,
2046
2166
  "aria-label": "Stop midpoint",
2047
- className: numberClass6,
2167
+ className: numberClass8,
2048
2168
  value: percent(sel.midpoint ?? 0.5),
2049
2169
  onChange: (e) => {
2050
2170
  const n = parseFloat(e.target.value);
@@ -2053,15 +2173,15 @@ function VectorGradientEditor({ controller, className }) {
2053
2173
  }
2054
2174
  )
2055
2175
  ] }),
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" })
2176
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2057
2177
  ] }),
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)) })
2178
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2179
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Colours" }),
2180
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass9, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector14.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: i, children: import_vector14.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2061
2181
  ] }),
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)(
2182
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2183
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Smooth" }),
2184
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2065
2185
  "input",
2066
2186
  {
2067
2187
  type: "range",
@@ -2077,26 +2197,26 @@ function VectorGradientEditor({ controller, className }) {
2077
2197
  }
2078
2198
  )
2079
2199
  ] }),
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)) })
2200
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2201
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "Spread" }),
2202
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient spread", className: selectClass9, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector14.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: sp, children: import_vector14.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2083
2203
  ] }),
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)) })
2204
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2205
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16", children: "On stroke" }),
2206
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass9, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector14.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: m, children: import_vector14.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2087
2207
  ] })
2088
2208
  ] })
2089
2209
  ] });
2090
2210
  }
2091
2211
 
2092
2212
  // src/vector/VectorToolbar.tsx
2093
- var import_jsx_runtime13 = require("react/jsx-runtime");
2213
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2094
2214
  var VECTOR_SNAP_TITLE = "Snap to objects";
2095
2215
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
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";
2216
+ var buttonClass13 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
2097
2217
  function VectorToolbar({
2098
2218
  controller,
2099
- tools = import_vector14.VECTOR_TOOLS,
2219
+ tools = import_vector15.VECTOR_TOOLS,
2100
2220
  showHistory = true,
2101
2221
  showArrange = true,
2102
2222
  showSnap = true,
@@ -2110,36 +2230,36 @@ function VectorToolbar({
2110
2230
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
2111
2231
  const editing = state.editing;
2112
2232
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
2113
- const maskSelected = state.selection.some((id) => (0, import_vector14.isVectorMaskGroup)(controller.getNode(id)));
2114
- const [live, setLive] = import_react8.default.useState(false);
2233
+ const maskSelected = state.selection.some((id) => (0, import_vector15.isVectorMaskGroup)(controller.getNode(id)));
2234
+ const [live, setLive] = import_react10.default.useState(false);
2115
2235
  const booleanDisabled = live ? count < 1 : count < 2;
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)(
2236
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2237
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2238
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2119
2239
  "button",
2120
2240
  {
2121
2241
  type: "button",
2122
2242
  "data-vector-tool": tool,
2123
- className: buttonClass11,
2243
+ className: buttonClass13,
2124
2244
  "aria-pressed": state.tool === tool,
2125
- title: `${(0, import_vector14.vectorToolLabel)(tool)} (${import_vector14.VECTOR_TOOL_SHORTCUTS[tool]})`,
2126
- "aria-label": (0, import_vector14.vectorToolLabel)(tool),
2245
+ title: `${(0, import_vector15.vectorToolLabel)(tool)} (${import_vector15.VECTOR_TOOL_SHORTCUTS[tool]})`,
2246
+ "aria-label": (0, import_vector15.vectorToolLabel)(tool),
2127
2247
  onClick: () => controller.setTool(tool),
2128
- children: import_vector14.VECTOR_TOOL_GLYPHS[tool]
2248
+ children: import_vector15.VECTOR_TOOL_GLYPHS[tool]
2129
2249
  },
2130
2250
  tool
2131
2251
  )) }),
2132
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2252
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2133
2253
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
2134
2254
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
2135
2255
  ] }),
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)(
2256
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2257
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2138
2258
  "button",
2139
2259
  {
2140
2260
  type: "button",
2141
2261
  "data-vector-snap": "",
2142
- className: buttonClass11,
2262
+ className: buttonClass13,
2143
2263
  "aria-pressed": state.snap.objects,
2144
2264
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
2145
2265
  "aria-label": VECTOR_SNAP_TITLE,
@@ -2147,18 +2267,18 @@ function VectorToolbar({
2147
2267
  children: "Snap"
2148
2268
  }
2149
2269
  ),
2150
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2270
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2151
2271
  "select",
2152
2272
  {
2153
2273
  "data-vector-polar": "",
2154
2274
  "aria-label": "Polar tracking increment",
2155
2275
  title: "Polar tracking: the angles drawn points snap to from where they leave",
2156
2276
  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)) }),
2277
+ value: String((0, import_vector15.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2278
+ onChange: (e) => controller.setSnap({ angles: (0, import_vector15.vectorPolarAngles)(Number(e.target.value)) }),
2159
2279
  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: [
2280
+ (0, import_vector15.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "Custom" }),
2281
+ [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("option", { value: n, children: [
2162
2282
  n,
2163
2283
  "\xB0"
2164
2284
  ] }, n))
@@ -2166,13 +2286,13 @@ function VectorToolbar({
2166
2286
  }
2167
2287
  )
2168
2288
  ] }),
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)(
2289
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2290
+ import_vector15.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2171
2291
  "button",
2172
2292
  {
2173
2293
  type: "button",
2174
2294
  "data-vector-path-action": a.id,
2175
- className: buttonClass11,
2295
+ className: buttonClass13,
2176
2296
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
2177
2297
  "aria-label": a.title,
2178
2298
  disabled: !a.enabled(count, points),
@@ -2181,12 +2301,12 @@ function VectorToolbar({
2181
2301
  },
2182
2302
  a.id
2183
2303
  )),
2184
- import_vector14.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2304
+ import_vector15.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2185
2305
  "button",
2186
2306
  {
2187
2307
  type: "button",
2188
2308
  "data-vector-edit": t.key,
2189
- className: buttonClass11,
2309
+ className: buttonClass13,
2190
2310
  "aria-pressed": editing[t.key],
2191
2311
  title: t.title,
2192
2312
  "aria-label": t.title,
@@ -2195,9 +2315,9 @@ function VectorToolbar({
2195
2315
  },
2196
2316
  t.key
2197
2317
  )),
2198
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2318
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2199
2319
  "Smoothing",
2200
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2320
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2201
2321
  "input",
2202
2322
  {
2203
2323
  type: "range",
@@ -2211,9 +2331,9 @@ function VectorToolbar({
2211
2331
  }
2212
2332
  )
2213
2333
  ] }),
2214
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2334
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2215
2335
  "Size",
2216
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2336
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2217
2337
  "input",
2218
2338
  {
2219
2339
  type: "number",
@@ -2229,26 +2349,26 @@ function VectorToolbar({
2229
2349
  }
2230
2350
  )
2231
2351
  ] }),
2232
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2352
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2233
2353
  "button",
2234
2354
  {
2235
2355
  type: "button",
2236
2356
  "data-vector-fill-gap": "",
2237
- className: buttonClass11,
2357
+ className: buttonClass13,
2238
2358
  "aria-pressed": editing.fillGap > 0,
2239
2359
  title: "Close small gaps between path ends when filling",
2240
2360
  "aria-label": "Close gaps",
2241
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector14.VECTOR_FILL_GAP }),
2361
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector15.VECTOR_FILL_GAP }),
2242
2362
  children: "Gaps"
2243
2363
  }
2244
2364
  )
2245
2365
  ] }),
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)(
2366
+ showDistort && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2247
2367
  "button",
2248
2368
  {
2249
2369
  type: "button",
2250
2370
  "data-vector-free-distort": "",
2251
- className: buttonClass11,
2371
+ className: buttonClass13,
2252
2372
  "aria-pressed": state.freeDistort,
2253
2373
  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)`,
2254
2374
  "aria-label": "Free Distort",
@@ -2256,15 +2376,15 @@ function VectorToolbar({
2256
2376
  children: "Distort"
2257
2377
  }
2258
2378
  ) }),
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)(
2379
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorBlendControls, { controller }),
2380
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2381
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2382
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2263
2383
  "button",
2264
2384
  {
2265
2385
  type: "button",
2266
2386
  "data-vector-live-boolean": "",
2267
- className: buttonClass11,
2387
+ className: buttonClass13,
2268
2388
  "aria-pressed": live,
2269
2389
  title: "Live booleans: keep the shapes editable",
2270
2390
  "aria-label": "Live booleans",
@@ -2281,12 +2401,12 @@ function VectorToolbar({
2281
2401
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
2282
2402
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
2283
2403
  ] }),
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)(
2404
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector15.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2285
2405
  "button",
2286
2406
  {
2287
2407
  type: "button",
2288
2408
  "data-vector-pathfinder": a.id,
2289
- className: buttonClass11,
2409
+ className: buttonClass13,
2290
2410
  title: a.title,
2291
2411
  "aria-label": a.title,
2292
2412
  disabled: count < a.min,
@@ -2295,7 +2415,7 @@ function VectorToolbar({
2295
2415
  },
2296
2416
  a.id
2297
2417
  )) }),
2298
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2418
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2299
2419
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
2300
2420
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
2301
2421
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -2310,22 +2430,22 @@ function VectorToolbar({
2310
2430
  }
2311
2431
  function VectorBlendControls({ controller }) {
2312
2432
  const state = useVectorEditorState(controller);
2313
- const blend = (0, import_vector14.getVectorBlendControls)(state);
2433
+ const blend = (0, import_vector15.getVectorBlendControls)(state);
2314
2434
  const o = blend.options;
2315
2435
  const selected = blend.ids.length > 0;
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)(
2436
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2437
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass13, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2438
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2319
2439
  "select",
2320
2440
  {
2321
2441
  "aria-label": "Blend spacing",
2322
2442
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
2323
2443
  value: o.spacing,
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))
2444
+ onChange: (e) => (0, import_vector15.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2445
+ children: import_vector15.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: sp, children: import_vector15.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2326
2446
  }
2327
2447
  ),
2328
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2448
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2329
2449
  "input",
2330
2450
  {
2331
2451
  type: "number",
@@ -2337,29 +2457,29 @@ function VectorBlendControls({ controller }) {
2337
2457
  onChange: (e) => {
2338
2458
  const n = parseFloat(e.target.value);
2339
2459
  if (!Number.isFinite(n) || n <= 0) return;
2340
- (0, import_vector14.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2460
+ (0, import_vector15.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2341
2461
  }
2342
2462
  }
2343
2463
  ),
2344
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2464
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2345
2465
  "button",
2346
2466
  {
2347
2467
  type: "button",
2348
2468
  "data-vector-blend-action": "orient",
2349
- className: buttonClass11,
2469
+ className: buttonClass13,
2350
2470
  "aria-pressed": o.orientToSpine,
2351
2471
  title: "Orient steps to the spine",
2352
2472
  "aria-label": "Orient to spine",
2353
- onClick: () => (0, import_vector14.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2473
+ onClick: () => (0, import_vector15.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2354
2474
  children: "\u293F"
2355
2475
  }
2356
2476
  ),
2357
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2477
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2358
2478
  "button",
2359
2479
  {
2360
2480
  type: "button",
2361
2481
  "data-vector-blend-action": "spine",
2362
- className: buttonClass11,
2482
+ className: buttonClass13,
2363
2483
  title: "Replace Spine (select a blend and a path)",
2364
2484
  "aria-label": "Replace Spine",
2365
2485
  disabled: !blend.canReplaceSpine,
@@ -2367,15 +2487,15 @@ function VectorBlendControls({ controller }) {
2367
2487
  children: "\u2307"
2368
2488
  }
2369
2489
  ),
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" })
2490
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass13, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2491
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass13, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2372
2492
  ] });
2373
2493
  }
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";
2494
+ var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2495
+ var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2376
2496
  function VectorStylePanel({ controller, className }) {
2377
2497
  const state = useVectorEditorState(controller);
2378
- const style = (0, import_vector14.getVectorSelectionStyle)(state);
2498
+ const style = (0, import_vector15.getVectorSelectionStyle)(state);
2379
2499
  const d = state.defaults;
2380
2500
  const source = style ?? {
2381
2501
  fill: d.fill ?? null,
@@ -2388,7 +2508,7 @@ function VectorStylePanel({ controller, className }) {
2388
2508
  arrowAlign: d.arrowAlign ?? "extend",
2389
2509
  startArrowScale: d.startArrowScale ?? 1,
2390
2510
  endArrowScale: d.endArrowScale ?? 1,
2391
- widthProfile: (0, import_vector14.widthProfilePresetId)(d.strokeWidthProfile),
2511
+ widthProfile: (0, import_vector15.widthProfilePresetId)(d.strokeWidthProfile),
2392
2512
  cornerRadius: d.cornerRadius,
2393
2513
  cornerKind: d.cornerKind ?? "round",
2394
2514
  cornerSmoothing: 0,
@@ -2402,19 +2522,19 @@ function VectorStylePanel({ controller, className }) {
2402
2522
  };
2403
2523
  const setProfile = (id) => {
2404
2524
  if (style) controller.setWidthProfile(id);
2405
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector14.widthProfilePreset)(id)?.length ? (0, import_vector14.widthProfilePreset)(id) : null });
2525
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector15.widthProfilePreset)(id)?.length ? (0, import_vector15.widthProfilePreset)(id) : null });
2406
2526
  };
2407
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2527
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2408
2528
  const arrowRow = (end, label) => {
2409
2529
  const key = end === "start" ? "startArrow" : "endArrow";
2410
2530
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
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))
2531
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2532
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2533
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass10, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2534
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "None" }),
2535
+ import_vector15.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: h, children: import_vector15.VECTOR_ARROWHEAD_LABELS[h] }, h))
2416
2536
  ] }),
2417
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2537
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2418
2538
  "input",
2419
2539
  {
2420
2540
  type: "number",
@@ -2422,7 +2542,7 @@ function VectorStylePanel({ controller, className }) {
2422
2542
  step: 0.25,
2423
2543
  "aria-label": `${label} arrowhead scale`,
2424
2544
  title: "Scale (\xD7 stroke width)",
2425
- className: numberClass7,
2545
+ className: numberClass9,
2426
2546
  value: source[scaleKey],
2427
2547
  onChange: (e) => {
2428
2548
  const n = parseFloat(e.target.value);
@@ -2438,14 +2558,14 @@ function VectorStylePanel({ controller, className }) {
2438
2558
  };
2439
2559
  const paintRow = (key, label) => {
2440
2560
  const paint = source[key];
2441
- const hex = (0, import_vector14.vectorPaintToHex)(paint);
2561
+ const hex = (0, import_vector15.vectorPaintToHex)(paint);
2442
2562
  const on = paint !== null && paint !== "none";
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 }) }),
2563
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2565
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2446
2566
  label
2447
2567
  ] }),
2448
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2568
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2449
2569
  "input",
2450
2570
  {
2451
2571
  type: "color",
@@ -2456,18 +2576,18 @@ function VectorStylePanel({ controller, className }) {
2456
2576
  onChange: (e) => apply({ [key]: e.target.value })
2457
2577
  }
2458
2578
  ),
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" })
2579
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2580
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2461
2581
  ] });
2462
2582
  };
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" }),
2583
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2584
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2465
2585
  paintRow("fill", "Fill"),
2466
2586
  paintRow("stroke", "Stroke"),
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)(
2587
+ style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorGradientEditor, { controller }),
2588
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2589
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Width" }),
2590
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2471
2591
  "input",
2472
2592
  {
2473
2593
  type: "number",
@@ -2483,33 +2603,33 @@ function VectorStylePanel({ controller, className }) {
2483
2603
  }
2484
2604
  )
2485
2605
  ] }),
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)) }),
2606
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2607
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Align" }),
2608
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass10, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector15.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: a, children: import_vector15.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2489
2609
  mixed("strokeAlign")
2490
2610
  ] }),
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)(
2611
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2612
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Profile" }),
2613
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2494
2614
  "select",
2495
2615
  {
2496
2616
  "aria-label": "Width profile",
2497
- className: selectClass9,
2617
+ className: selectClass10,
2498
2618
  value: source.widthProfile,
2499
2619
  onChange: (e) => {
2500
2620
  if (e.target.value !== "custom") setProfile(e.target.value);
2501
2621
  },
2502
2622
  children: [
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" })
2623
+ import_vector15.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: p.id, children: p.label }, p.id)),
2624
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2505
2625
  ]
2506
2626
  }
2507
2627
  ),
2508
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2628
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2509
2629
  "button",
2510
2630
  {
2511
2631
  type: "button",
2512
- className: buttonClass11,
2632
+ className: buttonClass13,
2513
2633
  title: "Reset to uniform width",
2514
2634
  "aria-label": "Reset to uniform width",
2515
2635
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -2519,27 +2639,27 @@ function VectorStylePanel({ controller, className }) {
2519
2639
  ),
2520
2640
  mixed("widthProfile")
2521
2641
  ] }),
2522
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2642
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2523
2643
  arrowRow("start", "Start"),
2524
2644
  arrowRow("end", "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" })
2645
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2646
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Heads" }),
2647
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass10, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2648
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "extend", children: "Tip past end" }),
2649
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "tip", children: "Tip at end" })
2530
2650
  ] })
2531
2651
  ] })
2532
2652
  ] }),
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)(
2653
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2654
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2655
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2536
2656
  "input",
2537
2657
  {
2538
2658
  type: "number",
2539
2659
  min: 0,
2540
2660
  step: 1,
2541
2661
  "aria-label": "Corner radius",
2542
- className: numberClass7,
2662
+ className: numberClass9,
2543
2663
  value: Math.round(source.cornerRadius * 100) / 100,
2544
2664
  onChange: (e) => {
2545
2665
  const n = parseFloat(e.target.value);
@@ -2547,12 +2667,12 @@ function VectorStylePanel({ controller, className }) {
2547
2667
  }
2548
2668
  }
2549
2669
  ),
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)) }),
2670
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Corner kind", className: selectClass10, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector15.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: k, children: import_vector15.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2551
2671
  mixed("cornerRadius")
2552
2672
  ] }),
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)(
2673
+ style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
2674
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Smoothing" }),
2675
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2556
2676
  "input",
2557
2677
  {
2558
2678
  type: "range",
@@ -2567,9 +2687,9 @@ function VectorStylePanel({ controller, className }) {
2567
2687
  }
2568
2688
  )
2569
2689
  ] }),
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)(
2690
+ style && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2691
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Opacity" }),
2692
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2573
2693
  "input",
2574
2694
  {
2575
2695
  type: "range",
@@ -2583,26 +2703,28 @@ function VectorStylePanel({ controller, className }) {
2583
2703
  }
2584
2704
  )
2585
2705
  ] }),
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 })
2706
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorTextPanel, { controller }),
2707
+ style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorAppearancePanel, { controller }),
2708
+ style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorEffectsPanel, { controller }),
2709
+ style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorEnvelopePanel, { controller }),
2710
+ style && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorMaskPanel, { controller }),
2711
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorFramePanel, { controller }),
2712
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorConnectorPanel, { controller }),
2713
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorShadingPanel, { controller }),
2714
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorTracePanel, { controller }),
2715
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorConstraintsPanel, { controller }),
2716
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VectorSymbolsPanel, { controller })
2595
2717
  ] });
2596
2718
  }
2597
2719
 
2598
2720
  // src/vector/VectorEditor.tsx
2599
- var import_jsx_runtime14 = require("react/jsx-runtime");
2721
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2600
2722
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
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" })
2723
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2724
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorToolbar, { controller, tools }),
2725
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2726
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
2727
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
2606
2728
  ] })
2607
2729
  ] });
2608
2730
  }
@@ -2623,11 +2745,13 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
2623
2745
  VectorMaskPanel,
2624
2746
  VectorMaskedNodes,
2625
2747
  VectorSceneNodes,
2748
+ VectorShadingPanel,
2626
2749
  VectorStylePanel,
2627
2750
  VectorSymbolsPanel,
2628
2751
  VectorTextElement,
2629
2752
  VectorTextPanel,
2630
2753
  VectorToolbar,
2754
+ VectorTracePanel,
2631
2755
  VectorWidthPointEditor,
2632
2756
  useVectorCommands,
2633
2757
  useVectorEditor,