@pixerate/editor-react 0.86.0 → 0.88.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.
@@ -39,6 +39,7 @@ __export(vector_exports, {
39
39
  VectorEnvelopePanel: () => VectorEnvelopePanel,
40
40
  VectorFilterDefs: () => VectorFilterDefs,
41
41
  VectorFramePanel: () => VectorFramePanel,
42
+ VectorGeneratePanel: () => VectorGeneratePanel,
42
43
  VectorGradientAnnotator: () => VectorGradientAnnotator,
43
44
  VectorGradientDefs: () => VectorGradientDefs,
44
45
  VectorGradientEditor: () => VectorGradientEditor,
@@ -62,7 +63,8 @@ __export(vector_exports, {
62
63
  useVectorEditor: () => useVectorEditor,
63
64
  useVectorEditorController: () => useVectorEditorController,
64
65
  useVectorEditorState: () => useVectorEditorState,
65
- useVectorEffectsTools: () => useVectorEffectsTools
66
+ useVectorEffectsTools: () => useVectorEffectsTools,
67
+ useVectorGenerationTools: () => useVectorGenerationTools
66
68
  });
67
69
  module.exports = __toCommonJS(vector_exports);
68
70
 
@@ -114,6 +116,30 @@ function useVectorEffectsTools(registry, controller, options = {}) {
114
116
  return registry.register((0, import_vector.createVectorEffectsToolProvider)(controller, { id, namespace, readOnly }));
115
117
  }, [registry, controller, id, namespace, readOnly]);
116
118
  }
119
+ function useVectorGenerationTools(registry, controller, options) {
120
+ const latest = (0, import_react.useRef)(options);
121
+ latest.current = options;
122
+ const { id, namespace } = options;
123
+ (0, import_react.useEffect)(() => {
124
+ if (!registry) return;
125
+ const models = () => {
126
+ const m = latest.current.models;
127
+ return typeof m === "function" ? m() : m;
128
+ };
129
+ return registry.register(
130
+ (0, import_vector.createVectorGenerationToolProvider)(controller, {
131
+ id,
132
+ namespace,
133
+ models,
134
+ generate: (request, context) => latest.current.generate(request, context),
135
+ readOnly: () => {
136
+ const r = latest.current.readOnly;
137
+ return (typeof r === "function" ? r() : r) ?? false;
138
+ }
139
+ })
140
+ );
141
+ }, [registry, controller, id, namespace]);
142
+ }
117
143
 
118
144
  // src/vector/VectorCanvas.tsx
119
145
  var import_react3 = __toESM(require("react"), 1);
@@ -299,7 +325,12 @@ var VectorSceneNode = import_react3.default.memo(function VectorSceneNode2({ nod
299
325
  if (node.layers)
300
326
  return (
301
327
  // Fill and stroke stacks: each layer in order, bottom to top.
302
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, filter: node.filter, style: blendStyle(node.blendMode), children: node.layers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`)) })
328
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, filter: node.filter, style: blendStyle(node.blendMode), children: node.layers.map(
329
+ (layer) => layer.kind === "brushstroke" ? (
330
+ // A brushstroke: its rendered brush images over their boxes.
331
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-layer": `brushstroke-${layer.index}`, opacity: layer.opacity < 1 ? layer.opacity : void 0, style: blendStyle(layer.blendMode), children: (layer.images ?? []).map((im, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("image", { href: im.href, x: im.x, y: im.y, width: im.width, height: im.height, preserveAspectRatio: "none" }, i)) }, `${layer.kind}-${layer.index}`)
332
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`)
333
+ ) })
303
334
  );
304
335
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPaintPath, { paint: node, d: node.d, fillRule: node.fillRule, id: node.id, transform: node.transform, filter: node.filter, blendMode: node.blendMode });
305
336
  });
@@ -658,6 +689,7 @@ function VectorCanvas({
658
689
  overlay.builder.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-builder-line": "", d, fill: "none", stroke: import_vector3.VECTOR_OVERLAY_THEME.builderErase, strokeWidth: 3, strokeLinecap: "round" }, `l${i}`)),
659
690
  overlay.builder.trail && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-builder-trail": "", d: overlay.builder.trail, fill: "none", stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor, strokeWidth: 1 })
660
691
  ] }),
692
+ overlay.brush && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-brush": "", cx: overlay.brush.x, cy: overlay.brush.y, r: overlay.brush.r, fill: "none", stroke: accentColor, strokeWidth: 1, strokeDasharray: "4 3", pointerEvents: "none" }),
661
693
  overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
662
694
  "path",
663
695
  {
@@ -838,7 +870,7 @@ function VectorCanvas({
838
870
 
839
871
  // src/vector/VectorToolbar.tsx
840
872
  var import_react11 = __toESM(require("react"), 1);
841
- var import_vector19 = require("@pixerate/editor/vector");
873
+ var import_vector20 = require("@pixerate/editor/vector");
842
874
 
843
875
  // src/vector/VectorEffectsPanel.tsx
844
876
  var import_vector4 = require("@pixerate/editor/vector");
@@ -1006,6 +1038,30 @@ function VectorAppearancePanel({ controller, className }) {
1006
1038
  const update = (l, patch, merge = false) => controller.updatePaintLayer(l.kind, l.index, patch, { ids, merge });
1007
1039
  const layerRow = (l, count) => {
1008
1040
  const name = `${l.kind === "fill" ? "Fill" : "Stroke"} ${l.index + 1}`;
1041
+ if (l.brushstroke)
1042
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-layer": `${l.kind}-${l.index}`, "data-vector-brushstroke": "", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1", children: [
1043
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", "aria-label": `${name} visible`, title: "Show", checked: l.visible, onChange: (e) => update(l, { visible: e.target.checked }) }),
1044
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "color", "aria-label": `${name} colour`, className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: l.color ?? "#000000", onChange: (e) => controller.setBrushstroke(l.index, { color: e.target.value }, { ids, merge: true }), onBlur: end }),
1045
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": `${name} brush`, className: selectClass2, value: l.brushstroke.brushId, onChange: (e) => controller.setBrushstroke(l.index, { brushId: e.target.value }, { ids }), children: (0, import_vector5.vectorBrushstrokeBrushes)().map((b) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: b.id, children: b.name }, b.id)) }),
1046
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1047
+ "input",
1048
+ {
1049
+ type: "number",
1050
+ min: 0.5,
1051
+ step: 0.5,
1052
+ "aria-label": `${name} brush size`,
1053
+ title: "Brush size",
1054
+ className: numberClass2,
1055
+ value: l.brushstroke.size,
1056
+ onChange: (e) => {
1057
+ const n = parseFloat(e.target.value);
1058
+ if (Number.isFinite(n) && n > 0) controller.setBrushstroke(l.index, { size: Math.min(500, n) }, { ids, merge: true });
1059
+ },
1060
+ onBlur: end
1061
+ }
1062
+ ),
1063
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Remove", "aria-label": `Remove ${name}`, onClick: () => controller.removePaintLayer("stroke", l.index, { ids }), children: "\xD7" })
1064
+ ] }) }, `${l.kind}-${l.index}`);
1009
1065
  if (l.unknownKind)
1010
1066
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "text-[11px] text-muted-foreground", "data-vector-layer": `${l.kind}-${l.index}`, children: [
1011
1067
  name,
@@ -1101,7 +1157,8 @@ function VectorAppearancePanel({ controller, className }) {
1101
1157
  onClick: () => controller.addPaintLayer(kind, {}, { ids }),
1102
1158
  children: "+"
1103
1159
  }
1104
- )
1160
+ ),
1161
+ kind === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass2, title: "Add a brushstroke (a natural-media brush along the path)", "aria-label": "Add brushstroke", "data-vector-add-brushstroke": "", disabled: !ids.length, onClick: () => controller.addBrushstroke({ brushId: (0, import_vector5.vectorBrushstrokeBrushes)()[0].id }, { ids }), children: "\u270E+" })
1105
1162
  ] }),
1106
1163
  [...layers].reverse().map((l) => layerRow(l, layers.length))
1107
1164
  ] });
@@ -2021,6 +2078,7 @@ function VectorShadingPanel({ controller, className }) {
2021
2078
 
2022
2079
  // src/vector/VectorTracePanel.tsx
2023
2080
  var import_react9 = require("react");
2081
+ var import_vector15 = require("@pixerate/editor/vector");
2024
2082
  var import_jsx_runtime14 = require("react/jsx-runtime");
2025
2083
  var buttonClass12 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
2026
2084
  var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
@@ -2047,7 +2105,8 @@ function VectorTracePanel({ controller, className }) {
2047
2105
  const pixels = await filePixels(file);
2048
2106
  const doc = controller.getState().document;
2049
2107
  const width = Math.min(doc.width, pixels.width);
2050
- const r = controller.traceImage(pixels, { mode, colors, width, name: file.name.replace(/\.[^.]+$/, "") || "Image Trace" });
2108
+ const name = file.name.replace(/\.[^.]+$/, "") || void 0;
2109
+ const r = mode === "threshold" || mode === "color" ? controller.traceImage(pixels, { mode, colors, width, name: name ?? "Image Trace" }) : controller.stylizeImage(pixels, mode, { width, ...name ? { name } : {} });
2051
2110
  setMessage(r.ok ? null : r.message);
2052
2111
  } catch {
2053
2112
  setMessage("That file could not be read as an image.");
@@ -2058,7 +2117,8 @@ function VectorTracePanel({ controller, className }) {
2058
2117
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
2059
2118
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("select", { "aria-label": "Trace mode", className: selectClass9, value: mode, onChange: (e) => setMode(e.target.value), children: [
2060
2119
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "threshold", children: "Black & white" }),
2061
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "color", children: "Colours" })
2120
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "color", children: "Colours" }),
2121
+ import_vector15.VECTOR_STYLIZE_STYLES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: s, children: import_vector15.VECTOR_STYLIZE_LABELS[s] }, s))
2062
2122
  ] }),
2063
2123
  mode === "color" && /* @__PURE__ */ (0, import_jsx_runtime14.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))) }),
2064
2124
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: buttonClass12, "data-vector-trace-action": "", title: "Image Trace: pick an image to turn into paths", onClick: () => input.current?.click(), children: "Image\u2026" }),
@@ -2082,14 +2142,14 @@ function VectorTracePanel({ controller, className }) {
2082
2142
  }
2083
2143
 
2084
2144
  // src/vector/VectorSymmetryPanel.tsx
2085
- var import_vector15 = require("@pixerate/editor/vector");
2145
+ var import_vector16 = require("@pixerate/editor/vector");
2086
2146
  var import_jsx_runtime15 = require("react/jsx-runtime");
2087
2147
  var buttonClass13 = "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";
2088
2148
  var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2089
2149
  var numberClass8 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
2090
2150
  function VectorSymmetryPanel({ controller, className }) {
2091
2151
  const state = useVectorEditorState(controller);
2092
- const group = state.selection.map((id) => controller.getNode(id)).find(import_vector15.isVectorSymmetryGroup) ?? null;
2152
+ const group = state.selection.map((id) => controller.getNode(id)).find(import_vector16.isVectorSymmetryGroup) ?? null;
2093
2153
  const s = group?.symmetry;
2094
2154
  const ids = group ? [group.id] : void 0;
2095
2155
  const drawing = !!group && state.symmetryTarget === group.id;
@@ -2099,7 +2159,7 @@ function VectorSymmetryPanel({ controller, className }) {
2099
2159
  !group && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "data-vector-symmetry-action": "make", title: "Make Symmetry: the selection (or nothing yet) mirrored live across a vertical axis", onClick: () => controller.makeSymmetry({ mode: "vertical" }), children: "Mirror" }),
2100
2160
  !group && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "data-vector-symmetry-action": "radial", title: "Make Symmetry: the selection repeated round its centre", onClick: () => controller.makeSymmetry({ mode: "radial", copies: 6 }), children: "Radial" }),
2101
2161
  group && s && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2102
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Symmetry mode", className: selectClass10, value: s.mode, onChange: (e) => controller.setSymmetry({ mode: e.target.value }, { ids }), children: import_vector15.VECTOR_SYMMETRY_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: m, children: import_vector15.VECTOR_SYMMETRY_MODE_LABELS[m] }, m)) }),
2162
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Symmetry mode", className: selectClass10, value: s.mode, onChange: (e) => controller.setSymmetry({ mode: e.target.value }, { ids }), children: import_vector16.VECTOR_SYMMETRY_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: m, children: import_vector16.VECTOR_SYMMETRY_MODE_LABELS[m] }, m)) }),
2103
2163
  s.mode === "radial" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2104
2164
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "number", min: 2, max: 64, "aria-label": "Symmetry copies", title: "Copies round the circle", className: numberClass8, value: s.copies ?? 6, onChange: (e) => controller.setSymmetry({ copies: Math.max(2, Math.min(64, parseInt(e.target.value, 10) || 2)) }, { ids }) }),
2105
2165
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Mirror each copy too (kaleidoscope)", children: [
@@ -2120,7 +2180,7 @@ function VectorSymmetryPanel({ controller, className }) {
2120
2180
 
2121
2181
  // src/vector/VectorPerspectivePanel.tsx
2122
2182
  var import_react10 = __toESM(require("react"), 1);
2123
- var import_vector16 = require("@pixerate/editor/vector");
2183
+ var import_vector17 = require("@pixerate/editor/vector");
2124
2184
  var import_jsx_runtime16 = require("react/jsx-runtime");
2125
2185
  var buttonClass14 = "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";
2126
2186
  var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
@@ -2128,10 +2188,10 @@ var PLANE_LABELS = { floor: "Floor", left: "Left wall", right: "Right wall", sid
2128
2188
  function VectorPerspectivePanel({ controller, className }) {
2129
2189
  const state = useVectorEditorState(controller);
2130
2190
  const grid = state.document.perspective ?? null;
2131
- const planes = grid ? (0, import_vector16.vectorPerspectivePlanes)(grid) : [];
2191
+ const planes = grid ? (0, import_vector17.vectorPerspectivePlanes)(grid) : [];
2132
2192
  const [attachTo, setAttachTo] = import_react10.default.useState("");
2133
2193
  const target = (state.perspectivePlane ?? attachTo) || planes[0];
2134
- const add = (n) => controller.setPerspective((0, import_vector16.vectorPerspectivePreset)(n, state.document.width, state.document.height));
2194
+ const add = (n) => controller.setPerspective((0, import_vector17.vectorPerspectivePreset)(n, state.document.width, state.document.height));
2135
2195
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-perspective-panel": "", "aria-label": "Perspective", children: [
2136
2196
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2137
2197
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Perspective" }),
@@ -2164,7 +2224,7 @@ function VectorPerspectivePanel({ controller, className }) {
2164
2224
  }
2165
2225
 
2166
2226
  // src/vector/VectorLensPanel.tsx
2167
- var import_vector17 = require("@pixerate/editor/vector");
2227
+ var import_vector18 = require("@pixerate/editor/vector");
2168
2228
  var import_jsx_runtime17 = require("react/jsx-runtime");
2169
2229
  var selectClass12 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2170
2230
  var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
@@ -2173,7 +2233,7 @@ function VectorLensPanel({ controller, className }) {
2173
2233
  const path = state.selection.map((id) => controller.getNode(id)).find((n) => n?.type === "path" && n.subpaths.some((sp) => sp.closed) && !n.connector);
2174
2234
  if (!path) return null;
2175
2235
  const lens = path.lens ?? null;
2176
- const spec = lens ? import_vector17.VECTOR_LENS_SPECS[lens.type] : null;
2236
+ const spec = lens ? import_vector18.VECTOR_LENS_SPECS[lens.type] : null;
2177
2237
  const ids = [path.id];
2178
2238
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-lens-panel": "", "aria-label": "Lens", children: [
2179
2239
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
@@ -2187,7 +2247,7 @@ function VectorLensPanel({ controller, className }) {
2187
2247
  onChange: (e) => e.target.value ? controller.setLens({ type: e.target.value }, { ids }) : controller.setLens(null, { ids }),
2188
2248
  children: [
2189
2249
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: "", children: "None" }),
2190
- import_vector17.VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: t, children: import_vector17.VECTOR_LENS_LABELS[t] }, t))
2250
+ import_vector18.VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: t, children: import_vector18.VECTOR_LENS_LABELS[t] }, t))
2191
2251
  ]
2192
2252
  }
2193
2253
  )
@@ -2217,7 +2277,7 @@ function VectorLensPanel({ controller, className }) {
2217
2277
  }
2218
2278
 
2219
2279
  // src/vector/VectorGradientEditor.tsx
2220
- var import_vector18 = require("@pixerate/editor/vector");
2280
+ var import_vector19 = require("@pixerate/editor/vector");
2221
2281
  var import_jsx_runtime18 = require("react/jsx-runtime");
2222
2282
  var selectClass13 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2223
2283
  var numberClass10 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
@@ -2225,7 +2285,7 @@ var buttonClass15 = "inline-flex h-7 min-w-7 items-center justify-center rounded
2225
2285
  var percent = (v) => Math.round(v * 1e3) / 10;
2226
2286
  function VectorGradientEditor({ controller, className }) {
2227
2287
  const state = useVectorEditorState(controller);
2228
- const ed = (0, import_vector18.getVectorGradientEditor)(state);
2288
+ const ed = (0, import_vector19.getVectorGradientEditor)(state);
2229
2289
  if (!ed) return null;
2230
2290
  const g = ed.gradient;
2231
2291
  const sel = ed.selected;
@@ -2233,7 +2293,7 @@ function VectorGradientEditor({ controller, className }) {
2233
2293
  const setType = (value) => {
2234
2294
  if (value === "solid") {
2235
2295
  const first = ed.stops[0];
2236
- controller.setStyle({ [ed.target]: (0, import_vector18.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2296
+ controller.setStyle({ [ed.target]: (0, import_vector19.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2237
2297
  } else controller.setGradient({ type: value });
2238
2298
  };
2239
2299
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
@@ -2246,7 +2306,7 @@ function VectorGradientEditor({ controller, className }) {
2246
2306
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2247
2307
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("select", { "aria-label": "Gradient type", className: selectClass13, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2248
2308
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "solid", children: "Solid" }),
2249
- import_vector18.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: t, children: import_vector18.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2309
+ import_vector19.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: t, children: import_vector19.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2250
2310
  ] }),
2251
2311
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", className: buttonClass15, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2252
2312
  ] }),
@@ -2287,7 +2347,7 @@ function VectorGradientEditor({ controller, className }) {
2287
2347
  type: "color",
2288
2348
  "aria-label": "Stop colour",
2289
2349
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
2290
- value: (0, import_vector18.vectorPaintToHex)(sel.color) ?? "#000000",
2350
+ value: (0, import_vector19.vectorPaintToHex)(sel.color) ?? "#000000",
2291
2351
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
2292
2352
  onBlur: end
2293
2353
  }
@@ -2353,7 +2413,7 @@ function VectorGradientEditor({ controller, className }) {
2353
2413
  ] }),
2354
2414
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2355
2415
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Colours" }),
2356
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass13, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector18.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: i, children: import_vector18.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2416
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass13, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector19.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: i, children: import_vector19.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2357
2417
  ] }),
2358
2418
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2359
2419
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Smooth" }),
@@ -2375,11 +2435,11 @@ function VectorGradientEditor({ controller, className }) {
2375
2435
  ] }),
2376
2436
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2377
2437
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Spread" }),
2378
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Gradient spread", className: selectClass13, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector18.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: sp, children: import_vector18.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2438
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Gradient spread", className: selectClass13, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector19.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: sp, children: import_vector19.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2379
2439
  ] }),
2380
2440
  ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2381
2441
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "On stroke" }),
2382
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass13, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector18.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: m, children: import_vector18.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2442
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass13, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector19.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: m, children: import_vector19.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2383
2443
  ] })
2384
2444
  ] })
2385
2445
  ] });
@@ -2392,7 +2452,7 @@ var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.te
2392
2452
  var buttonClass16 = "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";
2393
2453
  function VectorToolbar({
2394
2454
  controller,
2395
- tools = import_vector19.VECTOR_TOOLS,
2455
+ tools = import_vector20.VECTOR_TOOLS,
2396
2456
  showHistory = true,
2397
2457
  showArrange = true,
2398
2458
  showSnap = true,
@@ -2406,7 +2466,7 @@ function VectorToolbar({
2406
2466
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
2407
2467
  const editing = state.editing;
2408
2468
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
2409
- const maskSelected = state.selection.some((id) => (0, import_vector19.isVectorMaskGroup)(controller.getNode(id)));
2469
+ const maskSelected = state.selection.some((id) => (0, import_vector20.isVectorMaskGroup)(controller.getNode(id)));
2410
2470
  const [live, setLive] = import_react11.default.useState(false);
2411
2471
  const booleanDisabled = live ? count < 1 : count < 2;
2412
2472
  const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", className: buttonClass16, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
@@ -2418,10 +2478,10 @@ function VectorToolbar({
2418
2478
  "data-vector-tool": tool,
2419
2479
  className: buttonClass16,
2420
2480
  "aria-pressed": state.tool === tool,
2421
- title: `${(0, import_vector19.vectorToolLabel)(tool)} (${import_vector19.VECTOR_TOOL_SHORTCUTS[tool]})`,
2422
- "aria-label": (0, import_vector19.vectorToolLabel)(tool),
2481
+ title: `${(0, import_vector20.vectorToolLabel)(tool)} (${import_vector20.VECTOR_TOOL_SHORTCUTS[tool]})`,
2482
+ "aria-label": (0, import_vector20.vectorToolLabel)(tool),
2423
2483
  onClick: () => controller.setTool(tool),
2424
- children: import_vector19.VECTOR_TOOL_GLYPHS[tool]
2484
+ children: import_vector20.VECTOR_TOOL_GLYPHS[tool]
2425
2485
  },
2426
2486
  tool
2427
2487
  )) }),
@@ -2450,10 +2510,10 @@ function VectorToolbar({
2450
2510
  "aria-label": "Polar tracking increment",
2451
2511
  title: "Polar tracking: the angles drawn points snap to from where they leave",
2452
2512
  className: "h-8 rounded-md border border-border bg-background px-1 text-xs",
2453
- value: String((0, import_vector19.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2454
- onChange: (e) => controller.setSnap({ angles: (0, import_vector19.vectorPolarAngles)(Number(e.target.value)) }),
2513
+ value: String((0, import_vector20.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2514
+ onChange: (e) => controller.setSnap({ angles: (0, import_vector20.vectorPolarAngles)(Number(e.target.value)) }),
2455
2515
  children: [
2456
- (0, import_vector19.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "", children: "Custom" }),
2516
+ (0, import_vector20.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "", children: "Custom" }),
2457
2517
  [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("option", { value: n, children: [
2458
2518
  n,
2459
2519
  "\xB0"
@@ -2463,7 +2523,7 @@ function VectorToolbar({
2463
2523
  )
2464
2524
  ] }),
2465
2525
  showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2466
- import_vector19.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2526
+ import_vector20.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2467
2527
  "button",
2468
2528
  {
2469
2529
  type: "button",
@@ -2477,7 +2537,7 @@ function VectorToolbar({
2477
2537
  },
2478
2538
  a.id
2479
2539
  )),
2480
- import_vector19.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2540
+ import_vector20.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2481
2541
  "button",
2482
2542
  {
2483
2543
  type: "button",
@@ -2525,6 +2585,20 @@ function VectorToolbar({
2525
2585
  }
2526
2586
  )
2527
2587
  ] }),
2588
+ state.tool === "distort" && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "flex flex-wrap items-center gap-1", "data-vector-distort-options": "", children: [
2589
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("select", { "aria-label": "Distort brush", title: "What the brush does to the outlines it passes over", className: "h-7 rounded border border-border bg-background px-1 text-xs", value: editing.distortMode, onChange: (e) => controller.setEditing({ distortMode: e.target.value }), children: import_vector20.VECTOR_DISTORT_BRUSH_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: m, children: m[0].toUpperCase() + m.slice(1) }, m)) }),
2590
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2591
+ "Size",
2592
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { type: "number", min: 1, step: 1, "aria-label": "Distort brush size", className: "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs", value: editing.distortSize, onChange: (e) => {
2593
+ const n = parseFloat(e.target.value);
2594
+ if (Number.isFinite(n) && n > 0) controller.setEditing({ distortSize: n });
2595
+ } })
2596
+ ] }),
2597
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2598
+ "Strength",
2599
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Distort brush strength", className: "h-4 w-20 accent-primary", value: editing.distortStrength, onChange: (e) => controller.setEditing({ distortStrength: parseFloat(e.target.value) }) })
2600
+ ] })
2601
+ ] }),
2528
2602
  state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2529
2603
  "button",
2530
2604
  {
@@ -2534,7 +2608,7 @@ function VectorToolbar({
2534
2608
  "aria-pressed": editing.fillGap > 0,
2535
2609
  title: "Close small gaps between path ends when filling",
2536
2610
  "aria-label": "Close gaps",
2537
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector19.VECTOR_FILL_GAP }),
2611
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector20.VECTOR_FILL_GAP }),
2538
2612
  children: "Gaps"
2539
2613
  }
2540
2614
  )
@@ -2577,7 +2651,7 @@ function VectorToolbar({
2577
2651
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
2578
2652
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
2579
2653
  ] }),
2580
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector19.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2654
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector20.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2581
2655
  "button",
2582
2656
  {
2583
2657
  type: "button",
@@ -2606,7 +2680,7 @@ function VectorToolbar({
2606
2680
  }
2607
2681
  function VectorBlendControls({ controller }) {
2608
2682
  const state = useVectorEditorState(controller);
2609
- const blend = (0, import_vector19.getVectorBlendControls)(state);
2683
+ const blend = (0, import_vector20.getVectorBlendControls)(state);
2610
2684
  const o = blend.options;
2611
2685
  const selected = blend.ids.length > 0;
2612
2686
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
@@ -2617,8 +2691,8 @@ function VectorBlendControls({ controller }) {
2617
2691
  "aria-label": "Blend spacing",
2618
2692
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
2619
2693
  value: o.spacing,
2620
- onChange: (e) => (0, import_vector19.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2621
- children: import_vector19.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: sp, children: import_vector19.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2694
+ onChange: (e) => (0, import_vector20.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2695
+ children: import_vector20.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: sp, children: import_vector20.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2622
2696
  }
2623
2697
  ),
2624
2698
  o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -2633,7 +2707,7 @@ function VectorBlendControls({ controller }) {
2633
2707
  onChange: (e) => {
2634
2708
  const n = parseFloat(e.target.value);
2635
2709
  if (!Number.isFinite(n) || n <= 0) return;
2636
- (0, import_vector19.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2710
+ (0, import_vector20.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2637
2711
  }
2638
2712
  }
2639
2713
  ),
@@ -2646,7 +2720,7 @@ function VectorBlendControls({ controller }) {
2646
2720
  "aria-pressed": o.orientToSpine,
2647
2721
  title: "Orient steps to the spine",
2648
2722
  "aria-label": "Orient to spine",
2649
- onClick: () => (0, import_vector19.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2723
+ onClick: () => (0, import_vector20.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2650
2724
  children: "\u293F"
2651
2725
  }
2652
2726
  ),
@@ -2671,7 +2745,7 @@ var selectClass14 = "h-7 min-w-0 flex-1 rounded border border-border bg-backgrou
2671
2745
  var numberClass11 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2672
2746
  function VectorStylePanel({ controller, className }) {
2673
2747
  const state = useVectorEditorState(controller);
2674
- const style = (0, import_vector19.getVectorSelectionStyle)(state);
2748
+ const style = (0, import_vector20.getVectorSelectionStyle)(state);
2675
2749
  const d = state.defaults;
2676
2750
  const source = style ?? {
2677
2751
  fill: d.fill ?? null,
@@ -2684,7 +2758,7 @@ function VectorStylePanel({ controller, className }) {
2684
2758
  arrowAlign: d.arrowAlign ?? "extend",
2685
2759
  startArrowScale: d.startArrowScale ?? 1,
2686
2760
  endArrowScale: d.endArrowScale ?? 1,
2687
- widthProfile: (0, import_vector19.widthProfilePresetId)(d.strokeWidthProfile),
2761
+ widthProfile: (0, import_vector20.widthProfilePresetId)(d.strokeWidthProfile),
2688
2762
  cornerRadius: d.cornerRadius,
2689
2763
  cornerKind: d.cornerKind ?? "round",
2690
2764
  cornerSmoothing: 0,
@@ -2698,7 +2772,7 @@ function VectorStylePanel({ controller, className }) {
2698
2772
  };
2699
2773
  const setProfile = (id) => {
2700
2774
  if (style) controller.setWidthProfile(id);
2701
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector19.widthProfilePreset)(id)?.length ? (0, import_vector19.widthProfilePreset)(id) : null });
2775
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector20.widthProfilePreset)(id)?.length ? (0, import_vector20.widthProfilePreset)(id) : null });
2702
2776
  };
2703
2777
  const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2704
2778
  const arrowRow = (end, label) => {
@@ -2708,7 +2782,7 @@ function VectorStylePanel({ controller, className }) {
2708
2782
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2709
2783
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass14, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2710
2784
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "", children: "None" }),
2711
- import_vector19.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: h, children: import_vector19.VECTOR_ARROWHEAD_LABELS[h] }, h))
2785
+ import_vector20.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: h, children: import_vector20.VECTOR_ARROWHEAD_LABELS[h] }, h))
2712
2786
  ] }),
2713
2787
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2714
2788
  "input",
@@ -2734,7 +2808,7 @@ function VectorStylePanel({ controller, className }) {
2734
2808
  };
2735
2809
  const paintRow = (key, label) => {
2736
2810
  const paint = source[key];
2737
- const hex = (0, import_vector19.vectorPaintToHex)(paint);
2811
+ const hex = (0, import_vector20.vectorPaintToHex)(paint);
2738
2812
  const on = paint !== null && paint !== "none";
2739
2813
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2740
2814
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
@@ -2781,7 +2855,7 @@ function VectorStylePanel({ controller, className }) {
2781
2855
  ] }),
2782
2856
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2783
2857
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16", children: "Align" }),
2784
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass14, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector19.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: a, children: import_vector19.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2858
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass14, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector20.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: a, children: import_vector20.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2785
2859
  mixed("strokeAlign")
2786
2860
  ] }),
2787
2861
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
@@ -2796,7 +2870,7 @@ function VectorStylePanel({ controller, className }) {
2796
2870
  if (e.target.value !== "custom") setProfile(e.target.value);
2797
2871
  },
2798
2872
  children: [
2799
- import_vector19.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: p.id, children: p.label }, p.id)),
2873
+ import_vector20.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: p.id, children: p.label }, p.id)),
2800
2874
  source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2801
2875
  ]
2802
2876
  }
@@ -2843,7 +2917,7 @@ function VectorStylePanel({ controller, className }) {
2843
2917
  }
2844
2918
  }
2845
2919
  ),
2846
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("select", { "aria-label": "Corner kind", className: selectClass14, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector19.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: k, children: import_vector19.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2920
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("select", { "aria-label": "Corner kind", className: selectClass14, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector20.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: k, children: import_vector20.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2847
2921
  mixed("cornerRadius")
2848
2922
  ] }),
2849
2923
  style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.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: [
@@ -3537,6 +3611,44 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
3537
3611
  ] })
3538
3612
  ] });
3539
3613
  }
3614
+
3615
+ // src/vector/VectorGeneratePanel.tsx
3616
+ var import_react18 = require("react");
3617
+ var import_vector21 = require("@pixerate/editor/vector");
3618
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3619
+ var buttonClass17 = "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";
3620
+ var selectClass15 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
3621
+ var AS_LABELS = { color: "Traced in colour", threshold: "Traced in one colour", mosaic: "Mosaic", halftone: "Halftone", lowPoly: "Low poly", hatch: "Engraving" };
3622
+ function VectorGeneratePanel({ controller, models, generate, className }) {
3623
+ const usable = (0, import_vector21.vectorArtModels)(models);
3624
+ const [prompt, setPrompt] = (0, import_react18.useState)("");
3625
+ const [model, setModel] = (0, import_react18.useState)("");
3626
+ const [as, setAs] = (0, import_react18.useState)("color");
3627
+ const [busy, setBusy] = (0, import_react18.useState)(false);
3628
+ const [message, setMessage] = (0, import_react18.useState)(null);
3629
+ const chosen = usable.find((m) => m.id === model) ?? usable[0];
3630
+ if (!usable.length) return null;
3631
+ const run = async () => {
3632
+ setBusy(true);
3633
+ setMessage(null);
3634
+ const r = await (0, import_vector21.generateVectorArt)(controller, { prompt, model: chosen.id, as, idempotencyKey: `${Date.now()}-${Math.random().toString(36).slice(2)}` }, { models, generate });
3635
+ setBusy(false);
3636
+ setMessage(r.ok ? null : r.message ?? "Generation failed.");
3637
+ };
3638
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-generate-panel": "", "aria-label": "Generate", children: [
3639
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-1", children: [
3640
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Generate" }),
3641
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("select", { "aria-label": "Generation model", className: selectClass15, value: chosen.id, onChange: (e) => setModel(e.target.value), children: usable.map((m) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("option", { value: m.id, children: m.name ?? m.id }, m.id)) }),
3642
+ chosen.media === "image" && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("select", { "aria-label": "Image to vectors", className: selectClass15, value: as, onChange: (e) => setAs(e.target.value), children: import_vector21.VECTOR_GENERATED_IMAGE_AS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("option", { value: a, children: AS_LABELS[a] }, a)) })
3643
+ ] }),
3644
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-1", children: [
3645
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "w-16" }),
3646
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("input", { type: "text", "aria-label": "Prompt", placeholder: "Describe the art\u2026", className: "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1.5 text-xs", value: prompt, onChange: (e) => setPrompt(e.target.value), onKeyDown: (e) => e.key === "Enter" && prompt.trim() && !busy && void run() }),
3647
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", className: buttonClass17, "data-vector-generate-action": "", disabled: !prompt.trim() || busy, onClick: () => void run(), children: busy ? "Generating\u2026" : "Generate" })
3648
+ ] }),
3649
+ message && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-[11px] text-red-600", children: message })
3650
+ ] });
3651
+ }
3540
3652
  // Annotate the CommonJS export names for ESM import in node:
3541
3653
  0 && (module.exports = {
3542
3654
  VectorAppearancePanel,
@@ -3548,6 +3660,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
3548
3660
  VectorEnvelopePanel,
3549
3661
  VectorFilterDefs,
3550
3662
  VectorFramePanel,
3663
+ VectorGeneratePanel,
3551
3664
  VectorGradientAnnotator,
3552
3665
  VectorGradientDefs,
3553
3666
  VectorGradientEditor,
@@ -3571,6 +3684,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
3571
3684
  useVectorEditor,
3572
3685
  useVectorEditorController,
3573
3686
  useVectorEditorState,
3574
- useVectorEffectsTools
3687
+ useVectorEffectsTools,
3688
+ useVectorGenerationTools
3575
3689
  });
3576
3690
  //# sourceMappingURL=index.cjs.map