@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.
@@ -1,4 +1,4 @@
1
- import { VectorEditorController, VectorEditorState, VectorTool, VectorCommandOptions, VectorEditorOptions, VectorEffectsToolProviderOptions, VectorRenderFilter, VectorRenderGradientAnnotator, VectorPaintDef, VectorRenderMask, VectorRenderNode, VectorRenderText, VectorWidthPointEdit } from '@pixerate/editor/vector';
1
+ import { VectorEditorController, VectorEditorState, VectorTool, VectorCommandOptions, VectorEditorOptions, VectorEffectsToolProviderOptions, VectorGenerationToolProviderOptions, VectorRenderFilter, VectorRenderGradientAnnotator, VectorPaintDef, VectorRenderMask, VectorRenderNode, VectorRenderText, VectorWidthPointEdit, VectorArtModel, VectorArtGenerator } from '@pixerate/editor/vector';
2
2
  import { ToolRegistry } from '@pixerate/editor/tools';
3
3
  import React__default from 'react';
4
4
  import { EffectProvider } from '@pixerate/editor/effects';
@@ -35,6 +35,12 @@ declare function useVectorCommands(registry: VectorCommandTarget | null | undefi
35
35
  * `registry` while mounted. They work once an effects provider is registered (see `useEffects`).
36
36
  */
37
37
  declare function useVectorEffectsTools(registry: ToolRegistry | null | undefined, controller: VectorEditorController, options?: VectorEffectsToolProviderOptions): void;
38
+ /**
39
+ * Registers the vector editor's generative-prompt tools (`list_vector_art_models`,
40
+ * `generate_vector_art`, `regenerate_vector_art`) for the lifetime of the component. `models` and
41
+ * `generate` are read fresh on every call, so they may change between renders.
42
+ */
43
+ declare function useVectorGenerationTools(registry: ToolRegistry | null | undefined, controller: VectorEditorController, options: VectorGenerationToolProviderOptions): void;
38
44
 
39
45
  interface VectorCanvasProps {
40
46
  controller: VectorEditorController;
@@ -311,8 +317,9 @@ interface VectorTracePanelProps {
311
317
  className?: string;
312
318
  }
313
319
  /**
314
- * Image Trace: pick an image file and trace it into paths, in one colour (dark areas) or a stacked
315
- * path per colour. The traced group goes on the artboard at its width. One undo step.
320
+ * Image Trace and stylise: pick an image file and trace it into paths, in one colour (dark areas)
321
+ * or a stacked path per colour, or stylise it as mosaic tiles, halftone dots, low-poly triangles
322
+ * or engraving lines. The group goes on the artboard at its width. One undo step.
316
323
  */
317
324
  declare function VectorTracePanel({ controller, className }: VectorTracePanelProps): React__default.JSX.Element;
318
325
 
@@ -360,4 +367,18 @@ interface VectorProviderEffectsPanelProps {
360
367
  */
361
368
  declare function VectorProviderEffectsPanel({ controller, className }: VectorProviderEffectsPanelProps): React__default.JSX.Element;
362
369
 
363
- export { type UseVectorEditorResult, VectorAppearancePanel, type VectorAppearancePanelProps, VectorCanvas, type VectorCanvasProps, type VectorCommandTarget, VectorConnectorPanel, type VectorConnectorPanelProps, VectorConstraintsPanel, type VectorConstraintsPanelProps, VectorEditor, type VectorEditorProps, VectorEffectsPanel, type VectorEffectsPanelProps, VectorEnvelopePanel, type VectorEnvelopePanelProps, VectorFilterDefs, VectorFramePanel, type VectorFramePanelProps, VectorGradientAnnotator, VectorGradientDefs, VectorGradientEditor, type VectorGradientEditorProps, VectorLensPanel, type VectorLensPanelProps, VectorMaskPanel, type VectorMaskPanelProps, VectorMaskedNodes, VectorPagesPanel, type VectorPagesPanelProps, VectorPerspectivePanel, type VectorPerspectivePanelProps, VectorProviderEffectsPanel, type VectorProviderEffectsPanelProps, VectorSceneNodes, VectorShadingPanel, type VectorShadingPanelProps, VectorStylePanel, type VectorStylePanelProps, VectorSymbolsPanel, type VectorSymbolsPanelProps, VectorSymmetryPanel, type VectorSymmetryPanelProps, VectorTextElement, VectorTextPanel, type VectorTextPanelProps, VectorToolbar, type VectorToolbarProps, VectorTracePanel, type VectorTracePanelProps, VectorWidthPointEditor, type VectorWidthPointEditorProps, useVectorCommands, useVectorEditor, useVectorEditorController, useVectorEditorState, useVectorEffectsTools };
370
+ interface VectorGeneratePanelProps {
371
+ controller: VectorEditorController;
372
+ /** The host's models (image models are traced or stylised; text models write SVG). */
373
+ models: readonly VectorArtModel[];
374
+ /** The host's generator (sends a planned request to its model). */
375
+ generate: VectorArtGenerator;
376
+ className?: string;
377
+ }
378
+ /**
379
+ * Generative prompts: describe art, pick a model (and, for image models, how the picture becomes
380
+ * vectors) and generate it into the artboard as one editable group. One undo step.
381
+ */
382
+ declare function VectorGeneratePanel({ controller, models, generate, className }: VectorGeneratePanelProps): React__default.JSX.Element | null;
383
+
384
+ export { type UseVectorEditorResult, VectorAppearancePanel, type VectorAppearancePanelProps, VectorCanvas, type VectorCanvasProps, type VectorCommandTarget, VectorConnectorPanel, type VectorConnectorPanelProps, VectorConstraintsPanel, type VectorConstraintsPanelProps, VectorEditor, type VectorEditorProps, VectorEffectsPanel, type VectorEffectsPanelProps, VectorEnvelopePanel, type VectorEnvelopePanelProps, VectorFilterDefs, VectorFramePanel, type VectorFramePanelProps, VectorGeneratePanel, type VectorGeneratePanelProps, VectorGradientAnnotator, VectorGradientDefs, VectorGradientEditor, type VectorGradientEditorProps, VectorLensPanel, type VectorLensPanelProps, VectorMaskPanel, type VectorMaskPanelProps, VectorMaskedNodes, VectorPagesPanel, type VectorPagesPanelProps, VectorPerspectivePanel, type VectorPerspectivePanelProps, VectorProviderEffectsPanel, type VectorProviderEffectsPanelProps, VectorSceneNodes, VectorShadingPanel, type VectorShadingPanelProps, VectorStylePanel, type VectorStylePanelProps, VectorSymbolsPanel, type VectorSymbolsPanelProps, VectorSymmetryPanel, type VectorSymmetryPanelProps, VectorTextElement, VectorTextPanel, type VectorTextPanelProps, VectorToolbar, type VectorToolbarProps, VectorTracePanel, type VectorTracePanelProps, VectorWidthPointEditor, type VectorWidthPointEditorProps, useVectorCommands, useVectorEditor, useVectorEditorController, useVectorEditorState, useVectorEffectsTools, useVectorGenerationTools };
@@ -1,4 +1,4 @@
1
- import { VectorEditorController, VectorEditorState, VectorTool, VectorCommandOptions, VectorEditorOptions, VectorEffectsToolProviderOptions, VectorRenderFilter, VectorRenderGradientAnnotator, VectorPaintDef, VectorRenderMask, VectorRenderNode, VectorRenderText, VectorWidthPointEdit } from '@pixerate/editor/vector';
1
+ import { VectorEditorController, VectorEditorState, VectorTool, VectorCommandOptions, VectorEditorOptions, VectorEffectsToolProviderOptions, VectorGenerationToolProviderOptions, VectorRenderFilter, VectorRenderGradientAnnotator, VectorPaintDef, VectorRenderMask, VectorRenderNode, VectorRenderText, VectorWidthPointEdit, VectorArtModel, VectorArtGenerator } from '@pixerate/editor/vector';
2
2
  import { ToolRegistry } from '@pixerate/editor/tools';
3
3
  import React__default from 'react';
4
4
  import { EffectProvider } from '@pixerate/editor/effects';
@@ -35,6 +35,12 @@ declare function useVectorCommands(registry: VectorCommandTarget | null | undefi
35
35
  * `registry` while mounted. They work once an effects provider is registered (see `useEffects`).
36
36
  */
37
37
  declare function useVectorEffectsTools(registry: ToolRegistry | null | undefined, controller: VectorEditorController, options?: VectorEffectsToolProviderOptions): void;
38
+ /**
39
+ * Registers the vector editor's generative-prompt tools (`list_vector_art_models`,
40
+ * `generate_vector_art`, `regenerate_vector_art`) for the lifetime of the component. `models` and
41
+ * `generate` are read fresh on every call, so they may change between renders.
42
+ */
43
+ declare function useVectorGenerationTools(registry: ToolRegistry | null | undefined, controller: VectorEditorController, options: VectorGenerationToolProviderOptions): void;
38
44
 
39
45
  interface VectorCanvasProps {
40
46
  controller: VectorEditorController;
@@ -311,8 +317,9 @@ interface VectorTracePanelProps {
311
317
  className?: string;
312
318
  }
313
319
  /**
314
- * Image Trace: pick an image file and trace it into paths, in one colour (dark areas) or a stacked
315
- * path per colour. The traced group goes on the artboard at its width. One undo step.
320
+ * Image Trace and stylise: pick an image file and trace it into paths, in one colour (dark areas)
321
+ * or a stacked path per colour, or stylise it as mosaic tiles, halftone dots, low-poly triangles
322
+ * or engraving lines. The group goes on the artboard at its width. One undo step.
316
323
  */
317
324
  declare function VectorTracePanel({ controller, className }: VectorTracePanelProps): React__default.JSX.Element;
318
325
 
@@ -360,4 +367,18 @@ interface VectorProviderEffectsPanelProps {
360
367
  */
361
368
  declare function VectorProviderEffectsPanel({ controller, className }: VectorProviderEffectsPanelProps): React__default.JSX.Element;
362
369
 
363
- export { type UseVectorEditorResult, VectorAppearancePanel, type VectorAppearancePanelProps, VectorCanvas, type VectorCanvasProps, type VectorCommandTarget, VectorConnectorPanel, type VectorConnectorPanelProps, VectorConstraintsPanel, type VectorConstraintsPanelProps, VectorEditor, type VectorEditorProps, VectorEffectsPanel, type VectorEffectsPanelProps, VectorEnvelopePanel, type VectorEnvelopePanelProps, VectorFilterDefs, VectorFramePanel, type VectorFramePanelProps, VectorGradientAnnotator, VectorGradientDefs, VectorGradientEditor, type VectorGradientEditorProps, VectorLensPanel, type VectorLensPanelProps, VectorMaskPanel, type VectorMaskPanelProps, VectorMaskedNodes, VectorPagesPanel, type VectorPagesPanelProps, VectorPerspectivePanel, type VectorPerspectivePanelProps, VectorProviderEffectsPanel, type VectorProviderEffectsPanelProps, VectorSceneNodes, VectorShadingPanel, type VectorShadingPanelProps, VectorStylePanel, type VectorStylePanelProps, VectorSymbolsPanel, type VectorSymbolsPanelProps, VectorSymmetryPanel, type VectorSymmetryPanelProps, VectorTextElement, VectorTextPanel, type VectorTextPanelProps, VectorToolbar, type VectorToolbarProps, VectorTracePanel, type VectorTracePanelProps, VectorWidthPointEditor, type VectorWidthPointEditorProps, useVectorCommands, useVectorEditor, useVectorEditorController, useVectorEditorState, useVectorEffectsTools };
370
+ interface VectorGeneratePanelProps {
371
+ controller: VectorEditorController;
372
+ /** The host's models (image models are traced or stylised; text models write SVG). */
373
+ models: readonly VectorArtModel[];
374
+ /** The host's generator (sends a planned request to its model). */
375
+ generate: VectorArtGenerator;
376
+ className?: string;
377
+ }
378
+ /**
379
+ * Generative prompts: describe art, pick a model (and, for image models, how the picture becomes
380
+ * vectors) and generate it into the artboard as one editable group. One undo step.
381
+ */
382
+ declare function VectorGeneratePanel({ controller, models, generate, className }: VectorGeneratePanelProps): React__default.JSX.Element | null;
383
+
384
+ export { type UseVectorEditorResult, VectorAppearancePanel, type VectorAppearancePanelProps, VectorCanvas, type VectorCanvasProps, type VectorCommandTarget, VectorConnectorPanel, type VectorConnectorPanelProps, VectorConstraintsPanel, type VectorConstraintsPanelProps, VectorEditor, type VectorEditorProps, VectorEffectsPanel, type VectorEffectsPanelProps, VectorEnvelopePanel, type VectorEnvelopePanelProps, VectorFilterDefs, VectorFramePanel, type VectorFramePanelProps, VectorGeneratePanel, type VectorGeneratePanelProps, VectorGradientAnnotator, VectorGradientDefs, VectorGradientEditor, type VectorGradientEditorProps, VectorLensPanel, type VectorLensPanelProps, VectorMaskPanel, type VectorMaskPanelProps, VectorMaskedNodes, VectorPagesPanel, type VectorPagesPanelProps, VectorPerspectivePanel, type VectorPerspectivePanelProps, VectorProviderEffectsPanel, type VectorProviderEffectsPanelProps, VectorSceneNodes, VectorShadingPanel, type VectorShadingPanelProps, VectorStylePanel, type VectorStylePanelProps, VectorSymbolsPanel, type VectorSymbolsPanelProps, VectorSymmetryPanel, type VectorSymmetryPanelProps, VectorTextElement, VectorTextPanel, type VectorTextPanelProps, VectorToolbar, type VectorToolbarProps, VectorTracePanel, type VectorTracePanelProps, VectorWidthPointEditor, type VectorWidthPointEditorProps, useVectorCommands, useVectorEditor, useVectorEditorController, useVectorEditorState, useVectorEffectsTools, useVectorGenerationTools };
@@ -11,7 +11,8 @@ import { useEffect, useMemo, useRef, useSyncExternalStore } from "react";
11
11
  import {
12
12
  VectorEditorController,
13
13
  createVectorCommands,
14
- createVectorEffectsToolProvider
14
+ createVectorEffectsToolProvider,
15
+ createVectorGenerationToolProvider
15
16
  } from "@pixerate/editor/vector";
16
17
  function useVectorEditorController(options = {}) {
17
18
  const ref = useRef(null);
@@ -58,6 +59,30 @@ function useVectorEffectsTools(registry, controller, options = {}) {
58
59
  return registry.register(createVectorEffectsToolProvider(controller, { id, namespace, readOnly }));
59
60
  }, [registry, controller, id, namespace, readOnly]);
60
61
  }
62
+ function useVectorGenerationTools(registry, controller, options) {
63
+ const latest = useRef(options);
64
+ latest.current = options;
65
+ const { id, namespace } = options;
66
+ useEffect(() => {
67
+ if (!registry) return;
68
+ const models = () => {
69
+ const m = latest.current.models;
70
+ return typeof m === "function" ? m() : m;
71
+ };
72
+ return registry.register(
73
+ createVectorGenerationToolProvider(controller, {
74
+ id,
75
+ namespace,
76
+ models,
77
+ generate: (request, context) => latest.current.generate(request, context),
78
+ readOnly: () => {
79
+ const r = latest.current.readOnly;
80
+ return (typeof r === "function" ? r() : r) ?? false;
81
+ }
82
+ })
83
+ );
84
+ }, [registry, controller, id, namespace]);
85
+ }
61
86
 
62
87
  // src/vector/VectorCanvas.tsx
63
88
  import React2, { useEffect as useEffect2, useId, useRef as useRef3, useState as useState2 } from "react";
@@ -252,7 +277,12 @@ var VectorSceneNode = React2.memo(function VectorSceneNode2({ node }) {
252
277
  if (node.layers)
253
278
  return (
254
279
  // Fill and stroke stacks: each layer in order, bottom to top.
255
- /* @__PURE__ */ jsx2("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__ */ jsx2(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`)) })
280
+ /* @__PURE__ */ jsx2("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(
281
+ (layer) => layer.kind === "brushstroke" ? (
282
+ // A brushstroke: its rendered brush images over their boxes.
283
+ /* @__PURE__ */ jsx2("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__ */ jsx2("image", { href: im.href, x: im.x, y: im.y, width: im.width, height: im.height, preserveAspectRatio: "none" }, i)) }, `${layer.kind}-${layer.index}`)
284
+ ) : /* @__PURE__ */ jsx2(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`)
285
+ ) })
256
286
  );
257
287
  return /* @__PURE__ */ jsx2(VectorPaintPath, { paint: node, d: node.d, fillRule: node.fillRule, id: node.id, transform: node.transform, filter: node.filter, blendMode: node.blendMode });
258
288
  });
@@ -611,6 +641,7 @@ function VectorCanvas({
611
641
  overlay.builder.lines.map((d, i) => /* @__PURE__ */ jsx2("path", { "data-vector-builder-line": "", d, fill: "none", stroke: THEME2.builderErase, strokeWidth: 3, strokeLinecap: "round" }, `l${i}`)),
612
642
  overlay.builder.trail && /* @__PURE__ */ jsx2("path", { "data-vector-builder-trail": "", d: overlay.builder.trail, fill: "none", stroke: overlay.builder.mode === "erase" ? THEME2.builderErase : accentColor, strokeWidth: 1 })
613
643
  ] }),
644
+ overlay.brush && /* @__PURE__ */ jsx2("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" }),
614
645
  overlay.toolStroke && /* @__PURE__ */ jsx2(
615
646
  "path",
616
647
  {
@@ -793,6 +824,7 @@ function VectorCanvas({
793
824
  import React10 from "react";
794
825
  import {
795
826
  VECTOR_TOOLS,
827
+ VECTOR_DISTORT_BRUSH_MODES,
796
828
  VECTOR_TOOL_GLYPHS,
797
829
  VECTOR_TOOL_SHORTCUTS,
798
830
  VECTOR_EDIT_TOGGLES,
@@ -977,7 +1009,8 @@ import {
977
1009
  VECTOR_BLEND_MODE_MENU,
978
1010
  VECTOR_FILTER_MENU,
979
1011
  VECTOR_STROKE_ALIGN_LABELS,
980
- getVectorAppearancePanel
1012
+ getVectorAppearancePanel,
1013
+ vectorBrushstrokeBrushes
981
1014
  } from "@pixerate/editor/vector";
982
1015
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
983
1016
  var buttonClass2 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
@@ -994,6 +1027,30 @@ function VectorAppearancePanel({ controller, className }) {
994
1027
  const update = (l, patch, merge = false) => controller.updatePaintLayer(l.kind, l.index, patch, { ids, merge });
995
1028
  const layerRow = (l, count) => {
996
1029
  const name = `${l.kind === "fill" ? "Fill" : "Stroke"} ${l.index + 1}`;
1030
+ if (l.brushstroke)
1031
+ return /* @__PURE__ */ jsx4("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-layer": `${l.kind}-${l.index}`, "data-vector-brushstroke": "", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
1032
+ /* @__PURE__ */ jsx4("input", { type: "checkbox", "aria-label": `${name} visible`, title: "Show", checked: l.visible, onChange: (e) => update(l, { visible: e.target.checked }) }),
1033
+ /* @__PURE__ */ jsx4("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 }),
1034
+ /* @__PURE__ */ jsx4("select", { "aria-label": `${name} brush`, className: selectClass2, value: l.brushstroke.brushId, onChange: (e) => controller.setBrushstroke(l.index, { brushId: e.target.value }, { ids }), children: vectorBrushstrokeBrushes().map((b) => /* @__PURE__ */ jsx4("option", { value: b.id, children: b.name }, b.id)) }),
1035
+ /* @__PURE__ */ jsx4(
1036
+ "input",
1037
+ {
1038
+ type: "number",
1039
+ min: 0.5,
1040
+ step: 0.5,
1041
+ "aria-label": `${name} brush size`,
1042
+ title: "Brush size",
1043
+ className: numberClass2,
1044
+ value: l.brushstroke.size,
1045
+ onChange: (e) => {
1046
+ const n = parseFloat(e.target.value);
1047
+ if (Number.isFinite(n) && n > 0) controller.setBrushstroke(l.index, { size: Math.min(500, n) }, { ids, merge: true });
1048
+ },
1049
+ onBlur: end
1050
+ }
1051
+ ),
1052
+ /* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Remove", "aria-label": `Remove ${name}`, onClick: () => controller.removePaintLayer("stroke", l.index, { ids }), children: "\xD7" })
1053
+ ] }) }, `${l.kind}-${l.index}`);
997
1054
  if (l.unknownKind)
998
1055
  return /* @__PURE__ */ jsxs4("div", { className: "text-[11px] text-muted-foreground", "data-vector-layer": `${l.kind}-${l.index}`, children: [
999
1056
  name,
@@ -1089,7 +1146,8 @@ function VectorAppearancePanel({ controller, className }) {
1089
1146
  onClick: () => controller.addPaintLayer(kind, {}, { ids }),
1090
1147
  children: "+"
1091
1148
  }
1092
- )
1149
+ ),
1150
+ kind === "stroke" && /* @__PURE__ */ jsx4("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: vectorBrushstrokeBrushes()[0].id }, { ids }), children: "\u270E+" })
1093
1151
  ] }),
1094
1152
  [...layers].reverse().map((l) => layerRow(l, layers.length))
1095
1153
  ] });
@@ -2028,6 +2086,7 @@ function VectorShadingPanel({ controller, className }) {
2028
2086
 
2029
2087
  // src/vector/VectorTracePanel.tsx
2030
2088
  import { useRef as useRef4, useState as useState6 } from "react";
2089
+ import { VECTOR_STYLIZE_LABELS, VECTOR_STYLIZE_STYLES } from "@pixerate/editor/vector";
2031
2090
  import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2032
2091
  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";
2033
2092
  var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
@@ -2054,7 +2113,8 @@ function VectorTracePanel({ controller, className }) {
2054
2113
  const pixels = await filePixels(file);
2055
2114
  const doc = controller.getState().document;
2056
2115
  const width = Math.min(doc.width, pixels.width);
2057
- const r = controller.traceImage(pixels, { mode, colors, width, name: file.name.replace(/\.[^.]+$/, "") || "Image Trace" });
2116
+ const name = file.name.replace(/\.[^.]+$/, "") || void 0;
2117
+ const r = mode === "threshold" || mode === "color" ? controller.traceImage(pixels, { mode, colors, width, name: name ?? "Image Trace" }) : controller.stylizeImage(pixels, mode, { width, ...name ? { name } : {} });
2058
2118
  setMessage(r.ok ? null : r.message);
2059
2119
  } catch {
2060
2120
  setMessage("That file could not be read as an image.");
@@ -2065,7 +2125,8 @@ function VectorTracePanel({ controller, className }) {
2065
2125
  /* @__PURE__ */ jsx14("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
2066
2126
  /* @__PURE__ */ jsxs14("select", { "aria-label": "Trace mode", className: selectClass9, value: mode, onChange: (e) => setMode(e.target.value), children: [
2067
2127
  /* @__PURE__ */ jsx14("option", { value: "threshold", children: "Black & white" }),
2068
- /* @__PURE__ */ jsx14("option", { value: "color", children: "Colours" })
2128
+ /* @__PURE__ */ jsx14("option", { value: "color", children: "Colours" }),
2129
+ VECTOR_STYLIZE_STYLES.map((s) => /* @__PURE__ */ jsx14("option", { value: s, children: VECTOR_STYLIZE_LABELS[s] }, s))
2069
2130
  ] }),
2070
2131
  mode === "color" && /* @__PURE__ */ jsx14("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))) }),
2071
2132
  /* @__PURE__ */ jsx14("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" }),
@@ -2543,6 +2604,20 @@ function VectorToolbar({
2543
2604
  }
2544
2605
  )
2545
2606
  ] }),
2607
+ state.tool === "distort" && /* @__PURE__ */ jsxs19("span", { className: "flex flex-wrap items-center gap-1", "data-vector-distort-options": "", children: [
2608
+ /* @__PURE__ */ jsx19("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: VECTOR_DISTORT_BRUSH_MODES.map((m) => /* @__PURE__ */ jsx19("option", { value: m, children: m[0].toUpperCase() + m.slice(1) }, m)) }),
2609
+ /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2610
+ "Size",
2611
+ /* @__PURE__ */ jsx19("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) => {
2612
+ const n = parseFloat(e.target.value);
2613
+ if (Number.isFinite(n) && n > 0) controller.setEditing({ distortSize: n });
2614
+ } })
2615
+ ] }),
2616
+ /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2617
+ "Strength",
2618
+ /* @__PURE__ */ jsx19("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) }) })
2619
+ ] })
2620
+ ] }),
2546
2621
  state.tool === "paintBucket" && /* @__PURE__ */ jsx19(
2547
2622
  "button",
2548
2623
  {
@@ -2997,6 +3072,44 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
2997
3072
  ] })
2998
3073
  ] });
2999
3074
  }
3075
+
3076
+ // src/vector/VectorGeneratePanel.tsx
3077
+ import { useState as useState8 } from "react";
3078
+ import { VECTOR_GENERATED_IMAGE_AS, generateVectorArt, vectorArtModels } from "@pixerate/editor/vector";
3079
+ import { jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
3080
+ 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";
3081
+ var selectClass15 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
3082
+ var AS_LABELS = { color: "Traced in colour", threshold: "Traced in one colour", mosaic: "Mosaic", halftone: "Halftone", lowPoly: "Low poly", hatch: "Engraving" };
3083
+ function VectorGeneratePanel({ controller, models, generate, className }) {
3084
+ const usable = vectorArtModels(models);
3085
+ const [prompt, setPrompt] = useState8("");
3086
+ const [model, setModel] = useState8("");
3087
+ const [as, setAs] = useState8("color");
3088
+ const [busy, setBusy] = useState8(false);
3089
+ const [message, setMessage] = useState8(null);
3090
+ const chosen = usable.find((m) => m.id === model) ?? usable[0];
3091
+ if (!usable.length) return null;
3092
+ const run = async () => {
3093
+ setBusy(true);
3094
+ setMessage(null);
3095
+ const r = await generateVectorArt(controller, { prompt, model: chosen.id, as, idempotencyKey: `${Date.now()}-${Math.random().toString(36).slice(2)}` }, { models, generate });
3096
+ setBusy(false);
3097
+ setMessage(r.ok ? null : r.message ?? "Generation failed.");
3098
+ };
3099
+ return /* @__PURE__ */ jsxs22("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-generate-panel": "", "aria-label": "Generate", children: [
3100
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1", children: [
3101
+ /* @__PURE__ */ jsx22("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Generate" }),
3102
+ /* @__PURE__ */ jsx22("select", { "aria-label": "Generation model", className: selectClass15, value: chosen.id, onChange: (e) => setModel(e.target.value), children: usable.map((m) => /* @__PURE__ */ jsx22("option", { value: m.id, children: m.name ?? m.id }, m.id)) }),
3103
+ chosen.media === "image" && /* @__PURE__ */ jsx22("select", { "aria-label": "Image to vectors", className: selectClass15, value: as, onChange: (e) => setAs(e.target.value), children: VECTOR_GENERATED_IMAGE_AS.map((a) => /* @__PURE__ */ jsx22("option", { value: a, children: AS_LABELS[a] }, a)) })
3104
+ ] }),
3105
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1", children: [
3106
+ /* @__PURE__ */ jsx22("span", { className: "w-16" }),
3107
+ /* @__PURE__ */ jsx22("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() }),
3108
+ /* @__PURE__ */ jsx22("button", { type: "button", className: buttonClass17, "data-vector-generate-action": "", disabled: !prompt.trim() || busy, onClick: () => void run(), children: busy ? "Generating\u2026" : "Generate" })
3109
+ ] }),
3110
+ message && /* @__PURE__ */ jsx22("p", { className: "text-[11px] text-red-600", children: message })
3111
+ ] });
3112
+ }
3000
3113
  export {
3001
3114
  VectorAppearancePanel,
3002
3115
  VectorCanvas,
@@ -3007,6 +3120,7 @@ export {
3007
3120
  VectorEnvelopePanel,
3008
3121
  VectorFilterDefs,
3009
3122
  VectorFramePanel,
3123
+ VectorGeneratePanel,
3010
3124
  VectorGradientAnnotator,
3011
3125
  VectorGradientDefs,
3012
3126
  VectorGradientEditor,
@@ -3030,6 +3144,7 @@ export {
3030
3144
  useVectorEditor,
3031
3145
  useVectorEditorController,
3032
3146
  useVectorEditorState,
3033
- useVectorEffectsTools
3147
+ useVectorEffectsTools,
3148
+ useVectorGenerationTools
3034
3149
  };
3035
3150
  //# sourceMappingURL=index.js.map