@pixerate/editor-react 0.29.0 → 0.30.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.
@@ -26,6 +26,7 @@ __export(vector_exports, {
26
26
  VectorSceneNodes: () => VectorSceneNodes,
27
27
  VectorStylePanel: () => VectorStylePanel,
28
28
  VectorToolbar: () => VectorToolbar,
29
+ VectorWidthPointEditor: () => VectorWidthPointEditor,
29
30
  useVectorCommands: () => useVectorCommands,
30
31
  useVectorEditor: () => useVectorEditor,
31
32
  useVectorEditorController: () => useVectorEditorController,
@@ -76,19 +77,104 @@ function useVectorCommands(registry, controller, options = {}) {
76
77
  }
77
78
 
78
79
  // src/vector/VectorCanvas.tsx
80
+ var import_react3 = require("react");
81
+ var import_vector3 = require("@pixerate/editor/vector");
82
+
83
+ // src/vector/VectorWidthPointEditor.tsx
79
84
  var import_react2 = require("react");
80
85
  var import_vector2 = require("@pixerate/editor/vector");
81
86
  var import_jsx_runtime = require("react/jsx-runtime");
87
+ var round = (v, digits) => String(Math.round(v * 10 ** digits) / 10 ** digits);
88
+ var fieldStyle = { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 };
89
+ var inputStyle = { width: 72, font: "inherit", padding: "1px 4px" };
90
+ function VectorWidthPointEditor({ controller, edit, accentColor = import_vector2.VECTOR_OVERLAY_THEME.accent, className, style }) {
91
+ const [position, setPosition] = (0, import_react2.useState)(() => round(edit.t * 100, 2));
92
+ const [left, setLeft] = (0, import_react2.useState)(() => round(edit.left, 3));
93
+ const [right, setRight] = (0, import_react2.useState)(() => round(edit.right, 3));
94
+ const formRef = (0, import_react2.useRef)(null);
95
+ const refocus = () => formRef.current?.closest("svg")?.focus?.({ preventScroll: true });
96
+ const close = () => {
97
+ refocus();
98
+ controller.closeWidthPointEdit();
99
+ };
100
+ const num = (v) => v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
101
+ const apply = () => {
102
+ const t = num(position);
103
+ refocus();
104
+ controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left: num(left), right: num(right) });
105
+ };
106
+ const stop = (e) => e.stopPropagation();
107
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
108
+ "form",
109
+ {
110
+ ref: formRef,
111
+ "data-vector-width-point-editor": "",
112
+ "aria-label": "Width point",
113
+ className,
114
+ style: {
115
+ display: "grid",
116
+ gap: 4,
117
+ padding: 8,
118
+ background: "#ffffff",
119
+ border: "1px solid rgba(0,0,0,0.15)",
120
+ borderRadius: 6,
121
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
122
+ font: "12px system-ui, sans-serif",
123
+ color: "#18181b",
124
+ ...style
125
+ },
126
+ onPointerDown: stop,
127
+ onPointerMove: stop,
128
+ onPointerUp: stop,
129
+ onDoubleClick: stop,
130
+ onCopy: stop,
131
+ onCut: stop,
132
+ onPaste: stop,
133
+ onKeyDown: (e) => {
134
+ e.stopPropagation();
135
+ if (e.key === "Escape") {
136
+ e.preventDefault();
137
+ close();
138
+ }
139
+ },
140
+ onSubmit: (e) => {
141
+ e.preventDefault();
142
+ apply();
143
+ },
144
+ children: [
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldStyle, children: [
146
+ "Position %",
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { "aria-label": "Width point position", type: "number", min: 0, max: 100, step: "any", value: position, onChange: (e) => setPosition(e.target.value), style: inputStyle, autoFocus: true })
148
+ ] }),
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldStyle, children: [
150
+ "Left",
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { "aria-label": "Width point left side", type: "number", min: 0, step: "any", value: left, onChange: (e) => setLeft(e.target.value), style: inputStyle })
152
+ ] }),
153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldStyle, children: [
154
+ "Right",
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { "aria-label": "Width point right side", type: "number", min: 0, step: "any", value: right, onChange: (e) => setRight(e.target.value), style: inputStyle })
156
+ ] }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 4, marginTop: 2 }, children: [
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: close, children: "Cancel" }),
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", style: { background: accentColor, color: "#ffffff", border: "none", borderRadius: 4, padding: "2px 8px" }, children: edit.index === null ? "Add" : "Apply" })
160
+ ] })
161
+ ]
162
+ }
163
+ );
164
+ }
165
+
166
+ // src/vector/VectorCanvas.tsx
167
+ var import_jsx_runtime2 = require("react/jsx-runtime");
82
168
  function VectorGradientDefs({ defs }) {
83
169
  if (!defs.length) return null;
84
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: defs.map((def) => {
85
- const stops = def.stops.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: s.offset, stopColor: s.stopColor, stopOpacity: s.stopOpacity }, i));
86
- return def.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("radialGradient", { id: def.id, ...def.attrs, children: stops }, def.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("linearGradient", { id: def.id, ...def.attrs, children: stops }, def.id);
170
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: defs.map((def) => {
171
+ const stops = def.stops.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: s.offset, stopColor: s.stopColor, stopOpacity: s.stopOpacity }, i));
172
+ return def.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("radialGradient", { id: def.id, ...def.attrs, children: stops }, def.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("linearGradient", { id: def.id, ...def.attrs, children: stops }, def.id);
87
173
  }) });
88
174
  }
89
175
  function VectorSceneNodes({ nodes }) {
90
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: nodes.map(
91
- (node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VectorSceneNodes, { nodes: node.children }) }, node.id) : node.strokeParts ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VectorPartedPath, { node }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
176
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map(
177
+ (node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children }) }, node.id) : node.strokeParts ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPartedPath, { node }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
92
178
  "path",
93
179
  {
94
180
  "data-vector-id": node.id,
@@ -111,15 +197,15 @@ function VectorPartedPath({ node }) {
111
197
  const sp = node.strokeParts;
112
198
  const clip = sp.clip;
113
199
  const b = clip?.bounds;
114
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-id": node.id, "data-vector-stroke-align": sp.align, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: [
115
- clip && b && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("clipPath", { id: clip.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: node.d, clipRule: node.fillRule }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
116
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: b.x, y: b.y, width: b.width, height: b.height, fill: "#fff" }),
117
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: node.d, fill: "#000", fillRule: node.fillRule })
200
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-id": node.id, "data-vector-stroke-align": sp.align, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: [
201
+ clip && b && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: clip.id, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: node.d, clipRule: node.fillRule }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
202
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: b.x, y: b.y, width: b.width, height: b.height, fill: "#fff" }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: node.d, fill: "#000", fillRule: node.fillRule })
118
204
  ] }) }),
119
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { "data-vector-part": "fill", d: node.d, fill: node.fill, fillRule: node.fillRule, stroke: "none" }),
205
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "fill", d: node.d, fill: node.fill, fillRule: node.fillRule, stroke: "none" }),
120
206
  sp.outline !== void 0 ? (
121
207
  // A variable-width stroke: its outline, filled.
122
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
208
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
123
209
  "path",
124
210
  {
125
211
  "data-vector-part": "stroke-outline",
@@ -129,7 +215,7 @@ function VectorPartedPath({ node }) {
129
215
  mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
130
216
  }
131
217
  )
132
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
218
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
133
219
  "path",
134
220
  {
135
221
  "data-vector-part": "stroke",
@@ -144,26 +230,26 @@ function VectorPartedPath({ node }) {
144
230
  mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
145
231
  }
146
232
  ),
147
- sp.heads.map((head, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { "data-vector-part": "head", d: head, fill: node.stroke }, i))
233
+ sp.heads.map((head, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "head", d: head, fill: node.stroke }, i))
148
234
  ] });
149
235
  }
150
236
  function VectorCanvas({
151
237
  controller,
152
238
  fitOnMount = true,
153
239
  checkerboard = true,
154
- accentColor = import_vector2.VECTOR_OVERLAY_THEME.accent,
155
- guideColor = import_vector2.VECTOR_OVERLAY_THEME.guide,
240
+ accentColor = import_vector3.VECTOR_OVERLAY_THEME.accent,
241
+ guideColor = import_vector3.VECTOR_OVERLAY_THEME.guide,
156
242
  className,
157
243
  style,
158
244
  "aria-label": ariaLabel = "Vector canvas"
159
245
  }) {
160
246
  const state = useVectorEditorState(controller);
161
- const svgRef = (0, import_react2.useRef)(null);
162
- const fitted = (0, import_react2.useRef)(!fitOnMount);
163
- const uid = (0, import_react2.useId)().replace(/:/g, "");
164
- const scene = (0, import_vector2.getVectorSceneRenderData)(state, { idPrefix: `ve${uid}-` });
165
- const overlay = (0, import_vector2.getVectorOverlayRenderData)(state);
166
- (0, import_react2.useEffect)(() => {
247
+ const svgRef = (0, import_react3.useRef)(null);
248
+ const fitted = (0, import_react3.useRef)(!fitOnMount);
249
+ const uid = (0, import_react3.useId)().replace(/:/g, "");
250
+ const scene = (0, import_vector3.getVectorSceneRenderData)(state, { idPrefix: `ve${uid}-` });
251
+ const overlay = (0, import_vector3.getVectorOverlayRenderData)(state);
252
+ (0, import_react3.useEffect)(() => {
167
253
  const el = svgRef.current;
168
254
  if (!el || fitted.current) return;
169
255
  const fit = () => {
@@ -178,12 +264,12 @@ function VectorCanvas({
178
264
  observer.observe(el);
179
265
  return () => observer.disconnect();
180
266
  }, [controller]);
181
- (0, import_react2.useEffect)(() => {
267
+ (0, import_react3.useEffect)(() => {
182
268
  const el = svgRef.current;
183
269
  if (!el) return;
184
270
  const onWheel = (e) => {
185
271
  e.preventDefault();
186
- controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, (0, import_vector2.toVectorPointerInput)(e));
272
+ controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, (0, import_vector3.toVectorPointerInput)(e));
187
273
  };
188
274
  el.addEventListener("wheel", onWheel, { passive: false });
189
275
  return () => el.removeEventListener("wheel", onWheel);
@@ -207,10 +293,12 @@ function VectorCanvas({
207
293
  event.preventDefault?.();
208
294
  }
209
295
  };
210
- const hs = import_vector2.VECTOR_OVERLAY_THEME.transformHandleSize;
211
- const as = import_vector2.VECTOR_OVERLAY_THEME.anchorSize;
296
+ const hs = import_vector3.VECTOR_OVERLAY_THEME.transformHandleSize;
297
+ const as = import_vector3.VECTOR_OVERLAY_THEME.anchorSize;
212
298
  const box = overlay.selectionBox;
213
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
299
+ const edit = state.widthPointEdit;
300
+ const editAt = edit && { x: edit.point.x * state.viewport.zoom + state.viewport.x, y: edit.point.y * state.viewport.zoom + state.viewport.y };
301
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
214
302
  "svg",
215
303
  {
216
304
  ref: svgRef,
@@ -223,32 +311,32 @@ function VectorCanvas({
223
311
  onPointerDown: (e) => {
224
312
  svgRef.current?.focus({ preventScroll: true });
225
313
  e.currentTarget.setPointerCapture?.(e.pointerId);
226
- controller.pointerDown(local(e), (0, import_vector2.toVectorPointerInput)(e));
314
+ controller.pointerDown(local(e), (0, import_vector3.toVectorPointerInput)(e));
227
315
  },
228
- onPointerMove: (e) => controller.pointerMove(local(e), (0, import_vector2.toVectorPointerInput)(e)),
316
+ onPointerMove: (e) => controller.pointerMove(local(e), (0, import_vector3.toVectorPointerInput)(e)),
229
317
  onPointerUp: (e) => {
230
318
  e.currentTarget.releasePointerCapture?.(e.pointerId);
231
- controller.pointerUp(local(e), (0, import_vector2.toVectorPointerInput)(e));
319
+ controller.pointerUp(local(e), (0, import_vector3.toVectorPointerInput)(e));
232
320
  },
233
321
  onPointerCancel: () => controller.cancelDrag(),
234
322
  onPointerLeave: () => controller.pointerLeave(),
235
323
  onDoubleClick: (e) => controller.doubleClick(local(e)),
236
324
  onKeyDown: (e) => {
237
- if ((0, import_vector2.isVectorTextTarget)(e.target)) return;
238
- if (controller.keyDown((0, import_vector2.toVectorKeyInput)(e))) e.preventDefault();
325
+ if ((0, import_vector3.isVectorTextTarget)(e.target)) return;
326
+ if (controller.keyDown((0, import_vector3.toVectorKeyInput)(e))) e.preventDefault();
239
327
  },
240
- onKeyUp: (e) => controller.keyUp((0, import_vector2.toVectorKeyInput)(e)),
328
+ onKeyUp: (e) => controller.keyUp((0, import_vector3.toVectorKeyInput)(e)),
241
329
  onCopy: clipboard("copy"),
242
330
  onCut: clipboard("cut"),
243
331
  onPaste: clipboard("paste"),
244
332
  children: [
245
- checkerboard && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("pattern", { id: `ve${uid}-checker`, width: 16, height: 16, patternUnits: "userSpaceOnUse", children: [
246
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: 16, height: 16, fill: "#ffffff" }),
247
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: 8, height: 8, fill: "#e5e7eb" }),
248
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: 8, y: 8, width: 8, height: 8, fill: "#e5e7eb" })
333
+ checkerboard && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("pattern", { id: `ve${uid}-checker`, width: 16, height: 16, patternUnits: "userSpaceOnUse", children: [
334
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: 16, height: 16, fill: "#ffffff" }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: 8, height: 8, fill: "#e5e7eb" }),
336
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: 8, y: 8, width: 8, height: 8, fill: "#e5e7eb" })
249
337
  ] }) }),
250
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VectorGradientDefs, { defs: scene.defs }),
251
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
338
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorGradientDefs, { defs: scene.defs }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
252
340
  "rect",
253
341
  {
254
342
  "data-vector-artboard": "",
@@ -260,84 +348,84 @@ function VectorCanvas({
260
348
  stroke: "rgba(0,0,0,0.12)"
261
349
  }
262
350
  ),
263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-vector-scene": "", transform: scene.transform, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VectorSceneNodes, { nodes: scene.nodes }) }),
264
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
265
- overlay.outlines.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
266
- overlay.penPreview && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
267
- box && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-selection": "", transform: box.transform || void 0, children: [
268
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: box.x, y: box.y, width: box.width, height: box.height, stroke: accentColor }),
269
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: box.x + box.width / 2, y1: box.y, x2: box.x + box.width / 2, y2: box.y - 24, stroke: accentColor }),
351
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-scene": "", transform: scene.transform, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: scene.nodes }) }),
352
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
353
+ overlay.outlines.map((o) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
354
+ overlay.penPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
355
+ box && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-selection": "", transform: box.transform || void 0, children: [
356
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: box.x, y: box.y, width: box.width, height: box.height, stroke: accentColor }),
357
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: box.x + box.width / 2, y1: box.y, x2: box.x + box.width / 2, y2: box.y - 24, stroke: accentColor }),
270
358
  box.handles.map(
271
- (h) => h.handle === "rotate" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { "data-handle": h.handle, cx: h.x, cy: h.y, r: hs / 2 + 1, fill: import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, h.handle) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { "data-handle": h.handle, x: h.x - hs / 2, y: h.y - hs / 2, width: hs, height: hs, fill: import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, h.handle)
359
+ (h) => h.handle === "rotate" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-handle": h.handle, cx: h.x, cy: h.y, r: hs / 2 + 1, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, h.handle) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-handle": h.handle, x: h.x - hs / 2, y: h.y - hs / 2, width: hs, height: hs, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, h.handle)
272
360
  )
273
361
  ] }),
274
- overlay.handles.map((h) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-handle": h.ref.handle, children: [
275
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: h.anchorX, y1: h.anchorY, x2: h.x, y2: h.y, stroke: accentColor }),
276
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: h.x, cy: h.y, r: import_vector2.VECTOR_OVERLAY_THEME.handleRadius + (h.hovered ? 1 : 0), fill: import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor })
362
+ overlay.handles.map((h) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-handle": h.ref.handle, children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: h.anchorX, y1: h.anchorY, x2: h.x, y2: h.y, stroke: accentColor }),
364
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: h.x, cy: h.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + (h.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor })
277
365
  ] }, `${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`)),
278
366
  overlay.anchors.map((a) => {
279
367
  const size = as + (a.hovered ? 2 : 0);
280
- const fill = a.selected ? accentColor : import_vector2.VECTOR_OVERLAY_THEME.handleFill;
368
+ const fill = a.selected ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill;
281
369
  const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;
282
- return a.type === "corner" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { "data-vector-anchor": a.ref.index, x: a.x - size / 2, y: a.y - size / 2, width: size, height: size, fill, stroke: accentColor }, key) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { "data-vector-anchor": a.ref.index, cx: a.x, cy: a.y, r: size / 2, fill, stroke: accentColor }, key);
370
+ return a.type === "corner" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-vector-anchor": a.ref.index, x: a.x - size / 2, y: a.y - size / 2, width: size, height: size, fill, stroke: accentColor }, key) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-anchor": a.ref.index, cx: a.x, cy: a.y, r: size / 2, fill, stroke: accentColor }, key);
283
371
  }),
284
- overlay.cornerWidgets.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-corner-widget": w.ref.index, "data-corner-kind": w.kind, children: [
285
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: w.x, cy: w.y, r: import_vector2.VECTOR_OVERLAY_THEME.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: w.x, cy: w.y, r: 1.5, fill: accentColor })
372
+ overlay.cornerWidgets.map((w) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-corner-widget": w.ref.index, "data-corner-kind": w.kind, children: [
373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: import_vector3.VECTOR_OVERLAY_THEME.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
374
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: 1.5, fill: accentColor })
287
375
  ] }, `corner:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
288
- overlay.widthSpot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-width-spot": "", children: [
289
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: overlay.widthSpot.left.x, y1: overlay.widthSpot.left.y, x2: overlay.widthSpot.right.x, y2: overlay.widthSpot.right.y, stroke: accentColor, strokeDasharray: "3 2" }),
290
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: (0, import_vector2.vectorDiamondPath)(overlay.widthSpot.x, overlay.widthSpot.y), fill: "none", stroke: accentColor })
376
+ overlay.widthSpot && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-width-spot": "", children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: overlay.widthSpot.left.x, y1: overlay.widthSpot.left.y, x2: overlay.widthSpot.right.x, y2: overlay.widthSpot.right.y, stroke: accentColor, strokeDasharray: "3 2" }),
378
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: (0, import_vector3.vectorDiamondPath)(overlay.widthSpot.x, overlay.widthSpot.y), fill: "none", stroke: accentColor })
291
379
  ] }),
292
- overlay.widthPoints.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-width-point": w.ref.index, "data-selected": w.selected || void 0, children: [
293
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: w.left.x, y1: w.left.y, x2: w.right.x, y2: w.right.y, stroke: accentColor }),
294
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: (0, import_vector2.vectorDiamondPath)(w.x, w.y), fill: w.selected || w.active ? accentColor : import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor, strokeWidth: w.hovered === "center" ? 2 : 1 }),
295
- ["left", "right"].map((side) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { "data-vector-width-side": side, cx: w[side].x, cy: w[side].y, r: import_vector2.VECTOR_OVERLAY_THEME.handleRadius + (w.hovered === side ? 1 : 0), fill: import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, side))
380
+ overlay.widthPoints.map((w) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-width-point": w.ref.index, "data-selected": w.selected || void 0, children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: w.left.x, y1: w.left.y, x2: w.right.x, y2: w.right.y, stroke: accentColor }),
382
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: (0, import_vector3.vectorDiamondPath)(w.x, w.y), fill: w.selected || w.active ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor, strokeWidth: w.hovered === "center" ? 2 : 1 }),
383
+ ["left", "right"].map((side) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-width-side": side, cx: w[side].x, cy: w[side].y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + (w.hovered === side ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, side))
296
384
  ] }, `width:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
297
- state.tool === "pen" && overlay.anchors.some((a) => a.closes && a.hovered) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: overlay.anchors.find((a) => a.closes).x, cy: overlay.anchors.find((a) => a.closes).y, r: as, stroke: accentColor }),
298
- overlay.builder && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-builder": overlay.builder.mode, children: [
299
- overlay.builder.faces.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
385
+ state.tool === "pen" && overlay.anchors.some((a) => a.closes && a.hovered) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: overlay.anchors.find((a) => a.closes).x, cy: overlay.anchors.find((a) => a.closes).y, r: as, stroke: accentColor }),
386
+ overlay.builder && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-builder": overlay.builder.mode, children: [
387
+ overlay.builder.faces.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
300
388
  "path",
301
389
  {
302
390
  "data-vector-builder-face": "",
303
391
  d,
304
- fill: overlay.builder.mode === "erase" ? import_vector2.VECTOR_OVERLAY_THEME.builderEraseFill : import_vector2.VECTOR_OVERLAY_THEME.builderFill,
392
+ fill: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderEraseFill : import_vector3.VECTOR_OVERLAY_THEME.builderFill,
305
393
  fillRule: "nonzero",
306
- stroke: overlay.builder.mode === "erase" ? import_vector2.VECTOR_OVERLAY_THEME.builderErase : accentColor,
394
+ stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor,
307
395
  strokeWidth: 1
308
396
  },
309
397
  `f${i}`
310
398
  )),
311
399
  overlay.builder.hatches.map(
312
- (d, i) => d ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
400
+ (d, i) => d ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
313
401
  "path",
314
402
  {
315
403
  "data-vector-builder-hatch": "",
316
404
  d,
317
405
  fill: "none",
318
- stroke: overlay.builder.mode === "erase" ? import_vector2.VECTOR_OVERLAY_THEME.builderErase : accentColor,
319
- strokeOpacity: import_vector2.VECTOR_OVERLAY_THEME.builderHatchOpacity,
406
+ stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor,
407
+ strokeOpacity: import_vector3.VECTOR_OVERLAY_THEME.builderHatchOpacity,
320
408
  strokeWidth: 1
321
409
  },
322
410
  `h${i}`
323
411
  ) : null
324
412
  ),
325
- overlay.builder.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { "data-vector-builder-line": "", d, fill: "none", stroke: import_vector2.VECTOR_OVERLAY_THEME.builderErase, strokeWidth: 3, strokeLinecap: "round" }, `l${i}`)),
326
- overlay.builder.trail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { "data-vector-builder-trail": "", d: overlay.builder.trail, fill: "none", stroke: overlay.builder.mode === "erase" ? import_vector2.VECTOR_OVERLAY_THEME.builderErase : accentColor, strokeWidth: 1 })
413
+ 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}`)),
414
+ 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 })
327
415
  ] }),
328
- overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
416
+ overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
329
417
  "path",
330
418
  {
331
419
  "data-vector-tool-stroke": overlay.toolStroke.tool,
332
420
  d: overlay.toolStroke.d,
333
- stroke: overlay.toolStroke.tool === "eraser" ? import_vector2.VECTOR_OVERLAY_THEME.eraserFill : accentColor,
421
+ stroke: overlay.toolStroke.tool === "eraser" ? import_vector3.VECTOR_OVERLAY_THEME.eraserFill : accentColor,
334
422
  strokeWidth: overlay.toolStroke.tool === "eraser" ? overlay.toolStroke.width : 1,
335
423
  strokeLinecap: "round",
336
424
  strokeLinejoin: "round",
337
425
  strokeDasharray: overlay.toolStroke.tool === "knife" ? "5 3" : void 0
338
426
  }
339
427
  ),
340
- overlay.pencilString && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
428
+ overlay.pencilString && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
341
429
  "line",
342
430
  {
343
431
  "data-vector-pencil-string": "",
@@ -349,8 +437,8 @@ function VectorCanvas({
349
437
  strokeDasharray: "2 2"
350
438
  }
351
439
  ),
352
- overlay.cutPoint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { "data-vector-cut-point": "", cx: overlay.cutPoint.x, cy: overlay.cutPoint.y, r: import_vector2.VECTOR_OVERLAY_THEME.handleRadius + 1, fill: import_vector2.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
353
- overlay.marquee && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
440
+ overlay.cutPoint && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-cut-point": "", cx: overlay.cutPoint.x, cy: overlay.cutPoint.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + 1, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
441
+ overlay.marquee && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
354
442
  "rect",
355
443
  {
356
444
  "data-vector-marquee": "",
@@ -358,13 +446,13 @@ function VectorCanvas({
358
446
  y: overlay.marquee.y,
359
447
  width: overlay.marquee.width,
360
448
  height: overlay.marquee.height,
361
- fill: import_vector2.VECTOR_OVERLAY_THEME.marqueeFill,
449
+ fill: import_vector3.VECTOR_OVERLAY_THEME.marqueeFill,
362
450
  stroke: accentColor,
363
451
  strokeDasharray: "3 2"
364
452
  }
365
453
  ),
366
- overlay.guides.lines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { "data-vector-guide": l.kind, x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2, strokeDasharray: l.kind === "construction" ? "4 3" : void 0 }, i)) }),
367
- overlay.guides.labels.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
454
+ overlay.guides.lines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { "data-vector-guide": l.kind, x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2, strokeDasharray: l.kind === "construction" ? "4 3" : void 0 }, i)) }),
455
+ overlay.guides.labels.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
368
456
  "text",
369
457
  {
370
458
  "data-vector-guide-label": l.kind,
@@ -380,46 +468,48 @@ function VectorCanvas({
380
468
  },
381
469
  i
382
470
  )),
383
- overlay.status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
384
- ] })
471
+ overlay.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
472
+ ] }),
473
+ edit && editAt && // Width Point Edit (double-click with the width tool), beside the point.
474
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("foreignObject", { "data-vector-width-point-edit": "", x: editAt.x + 12, y: editAt.y + 12, width: 180, height: 140, style: { overflow: "visible" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorWidthPointEditor, { controller, edit, accentColor }, `${edit.nodeId}:${edit.index}:${edit.t}`) })
385
475
  ]
386
476
  }
387
477
  );
388
478
  }
389
479
 
390
480
  // src/vector/VectorToolbar.tsx
391
- var import_vector3 = require("@pixerate/editor/vector");
392
- var import_jsx_runtime2 = require("react/jsx-runtime");
481
+ var import_vector4 = require("@pixerate/editor/vector");
482
+ var import_jsx_runtime3 = require("react/jsx-runtime");
393
483
  var VECTOR_SNAP_TITLE = "Snap to objects";
394
484
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
395
485
  var buttonClass = "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";
396
- function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, showPathEdit = true, className }) {
486
+ function VectorToolbar({ controller, tools = import_vector4.VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, showPathEdit = true, className }) {
397
487
  const state = useVectorEditorState(controller);
398
488
  const count = state.selection.length;
399
489
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
400
490
  const editing = state.editing;
401
491
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
402
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: buttonClass, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
403
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
404
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
492
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: buttonClass, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
493
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
494
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
405
495
  "button",
406
496
  {
407
497
  type: "button",
408
498
  "data-vector-tool": tool,
409
499
  className: buttonClass,
410
500
  "aria-pressed": state.tool === tool,
411
- title: `${(0, import_vector3.vectorToolLabel)(tool)} (${import_vector3.VECTOR_TOOL_SHORTCUTS[tool]})`,
412
- "aria-label": (0, import_vector3.vectorToolLabel)(tool),
501
+ title: `${(0, import_vector4.vectorToolLabel)(tool)} (${import_vector4.VECTOR_TOOL_SHORTCUTS[tool]})`,
502
+ "aria-label": (0, import_vector4.vectorToolLabel)(tool),
413
503
  onClick: () => controller.setTool(tool),
414
- children: import_vector3.VECTOR_TOOL_GLYPHS[tool]
504
+ children: import_vector4.VECTOR_TOOL_GLYPHS[tool]
415
505
  },
416
506
  tool
417
507
  )) }),
418
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
508
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
419
509
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
420
510
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
421
511
  ] }),
422
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
512
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
423
513
  "button",
424
514
  {
425
515
  type: "button",
@@ -432,8 +522,8 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
432
522
  children: "Snap"
433
523
  }
434
524
  ) }),
435
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
436
- import_vector3.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
525
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
526
+ import_vector4.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
437
527
  "button",
438
528
  {
439
529
  type: "button",
@@ -447,7 +537,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
447
537
  },
448
538
  a.id
449
539
  )),
450
- import_vector3.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
540
+ import_vector4.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
451
541
  "button",
452
542
  {
453
543
  type: "button",
@@ -461,9 +551,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
461
551
  },
462
552
  t.key
463
553
  )),
464
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
554
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
465
555
  "Smoothing",
466
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
556
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
467
557
  "input",
468
558
  {
469
559
  type: "range",
@@ -477,9 +567,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
477
567
  }
478
568
  )
479
569
  ] }),
480
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
570
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
481
571
  "Size",
482
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
572
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
483
573
  "input",
484
574
  {
485
575
  type: "number",
@@ -495,7 +585,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
495
585
  }
496
586
  )
497
587
  ] }),
498
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
588
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
499
589
  "button",
500
590
  {
501
591
  type: "button",
@@ -504,13 +594,13 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
504
594
  "aria-pressed": editing.fillGap > 0,
505
595
  title: "Close small gaps between path ends when filling",
506
596
  "aria-label": "Close gaps",
507
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector3.VECTOR_FILL_GAP }),
597
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector4.VECTOR_FILL_GAP }),
508
598
  children: "Gaps"
509
599
  }
510
600
  )
511
601
  ] }),
512
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
513
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
602
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
603
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
514
604
  act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
515
605
  act("\u2212", "Subtract", () => controller.booleanOp("subtract"), count < 2),
516
606
  act("\u2229", "Intersect", () => controller.booleanOp("intersect"), count < 2),
@@ -518,7 +608,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
518
608
  act("G", "Group", () => controller.group(), count < 1),
519
609
  act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
520
610
  ] }),
521
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector3.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
611
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector4.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
522
612
  "button",
523
613
  {
524
614
  type: "button",
@@ -532,7 +622,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
532
622
  },
533
623
  a.id
534
624
  )) }),
535
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
625
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
536
626
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
537
627
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
538
628
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -549,7 +639,7 @@ var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background
549
639
  var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
550
640
  function VectorStylePanel({ controller, className }) {
551
641
  const state = useVectorEditorState(controller);
552
- const style = (0, import_vector3.getVectorSelectionStyle)(state);
642
+ const style = (0, import_vector4.getVectorSelectionStyle)(state);
553
643
  const d = state.defaults;
554
644
  const source = style ?? {
555
645
  fill: d.fill ?? null,
@@ -562,7 +652,7 @@ function VectorStylePanel({ controller, className }) {
562
652
  arrowAlign: d.arrowAlign ?? "extend",
563
653
  startArrowScale: d.startArrowScale ?? 1,
564
654
  endArrowScale: d.endArrowScale ?? 1,
565
- widthProfile: (0, import_vector3.widthProfilePresetId)(d.strokeWidthProfile),
655
+ widthProfile: (0, import_vector4.widthProfilePresetId)(d.strokeWidthProfile),
566
656
  cornerRadius: d.cornerRadius,
567
657
  cornerKind: d.cornerKind ?? "round",
568
658
  corners: 1,
@@ -575,19 +665,19 @@ function VectorStylePanel({ controller, className }) {
575
665
  };
576
666
  const setProfile = (id) => {
577
667
  if (style) controller.setWidthProfile(id);
578
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector3.widthProfilePreset)(id)?.length ? (0, import_vector3.widthProfilePreset)(id) : null });
668
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector4.widthProfilePreset)(id)?.length ? (0, import_vector4.widthProfilePreset)(id) : null });
579
669
  };
580
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
670
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
581
671
  const arrowRow = (end, label) => {
582
672
  const key = end === "start" ? "startArrow" : "endArrow";
583
673
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
584
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
585
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
586
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
587
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: "None" }),
588
- import_vector3.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: h, children: import_vector3.VECTOR_ARROWHEAD_LABELS[h] }, h))
674
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
677
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "None" }),
678
+ import_vector4.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: h, children: import_vector4.VECTOR_ARROWHEAD_LABELS[h] }, h))
589
679
  ] }),
590
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
680
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
591
681
  "input",
592
682
  {
593
683
  type: "number",
@@ -611,14 +701,14 @@ function VectorStylePanel({ controller, className }) {
611
701
  };
612
702
  const paintRow = (key, label) => {
613
703
  const paint = source[key];
614
- const hex = (0, import_vector3.vectorPaintToHex)(paint);
704
+ const hex = (0, import_vector4.vectorPaintToHex)(paint);
615
705
  const on = paint !== null && paint !== "none";
616
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
617
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
618
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
706
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
619
709
  label
620
710
  ] }),
621
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
711
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
622
712
  "input",
623
713
  {
624
714
  type: "color",
@@ -629,17 +719,17 @@ function VectorStylePanel({ controller, className }) {
629
719
  onChange: (e) => apply({ [key]: e.target.value })
630
720
  }
631
721
  ),
632
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
633
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
722
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
723
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
634
724
  ] });
635
725
  };
636
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
637
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
726
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
727
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
638
728
  paintRow("fill", "Fill"),
639
729
  paintRow("stroke", "Stroke"),
640
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
641
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Width" }),
642
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Width" }),
732
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
643
733
  "input",
644
734
  {
645
735
  type: "number",
@@ -655,14 +745,14 @@ function VectorStylePanel({ controller, className }) {
655
745
  }
656
746
  )
657
747
  ] }),
658
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
659
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Align" }),
660
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector3.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: a, children: import_vector3.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
748
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Align" }),
750
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector4.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: a, children: import_vector4.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
661
751
  mixed("strokeAlign")
662
752
  ] }),
663
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
664
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Profile" }),
665
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
753
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Profile" }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
666
756
  "select",
667
757
  {
668
758
  "aria-label": "Width profile",
@@ -672,12 +762,12 @@ function VectorStylePanel({ controller, className }) {
672
762
  if (e.target.value !== "custom") setProfile(e.target.value);
673
763
  },
674
764
  children: [
675
- import_vector3.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: p.id, children: p.label }, p.id)),
676
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
765
+ import_vector4.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: p.id, children: p.label }, p.id)),
766
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
677
767
  ]
678
768
  }
679
769
  ),
680
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
770
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
681
771
  "button",
682
772
  {
683
773
  type: "button",
@@ -691,20 +781,20 @@ function VectorStylePanel({ controller, className }) {
691
781
  ),
692
782
  mixed("widthProfile")
693
783
  ] }),
694
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
784
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
695
785
  arrowRow("start", "Start"),
696
786
  arrowRow("end", "End"),
697
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
698
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Heads" }),
699
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
700
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "extend", children: "Tip past end" }),
701
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "tip", children: "Tip at end" })
787
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
788
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Heads" }),
789
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
790
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "extend", children: "Tip past end" }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "tip", children: "Tip at end" })
702
792
  ] })
703
793
  ] })
704
794
  ] }),
705
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
706
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
707
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
795
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
796
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
708
798
  "input",
709
799
  {
710
800
  type: "number",
@@ -719,12 +809,12 @@ function VectorStylePanel({ controller, className }) {
719
809
  }
720
810
  }
721
811
  ),
722
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { "aria-label": "Corner kind", className: selectClass, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector3.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: k, children: import_vector3.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { "aria-label": "Corner kind", className: selectClass, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector4.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: k, children: import_vector4.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
723
813
  mixed("cornerRadius")
724
814
  ] }),
725
- style && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
726
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Opacity" }),
727
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
815
+ style && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
816
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Opacity" }),
817
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
728
818
  "input",
729
819
  {
730
820
  type: "range",
@@ -742,13 +832,13 @@ function VectorStylePanel({ controller, className }) {
742
832
  }
743
833
 
744
834
  // src/vector/VectorEditor.tsx
745
- var import_jsx_runtime3 = require("react/jsx-runtime");
835
+ var import_jsx_runtime4 = require("react/jsx-runtime");
746
836
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
747
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
748
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VectorToolbar, { controller, tools }),
749
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime3.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_runtime3.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
751
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
837
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorToolbar, { controller, tools }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime4.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_runtime4.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
841
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
752
842
  ] })
753
843
  ] });
754
844
  }
@@ -760,6 +850,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
760
850
  VectorSceneNodes,
761
851
  VectorStylePanel,
762
852
  VectorToolbar,
853
+ VectorWidthPointEditor,
763
854
  useVectorCommands,
764
855
  useVectorEditor,
765
856
  useVectorEditorController,