@pixerate/editor-react 0.28.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,70 +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
- 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}`)),
312
- 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 })
399
+ overlay.builder.hatches.map(
400
+ (d, i) => d ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
401
+ "path",
402
+ {
403
+ "data-vector-builder-hatch": "",
404
+ d,
405
+ fill: "none",
406
+ stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor,
407
+ strokeOpacity: import_vector3.VECTOR_OVERLAY_THEME.builderHatchOpacity,
408
+ strokeWidth: 1
409
+ },
410
+ `h${i}`
411
+ ) : null
412
+ ),
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 })
313
415
  ] }),
314
- overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
416
+ overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
315
417
  "path",
316
418
  {
317
419
  "data-vector-tool-stroke": overlay.toolStroke.tool,
318
420
  d: overlay.toolStroke.d,
319
- 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,
320
422
  strokeWidth: overlay.toolStroke.tool === "eraser" ? overlay.toolStroke.width : 1,
321
423
  strokeLinecap: "round",
322
424
  strokeLinejoin: "round",
323
425
  strokeDasharray: overlay.toolStroke.tool === "knife" ? "5 3" : void 0
324
426
  }
325
427
  ),
326
- overlay.pencilString && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
428
+ overlay.pencilString && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
327
429
  "line",
328
430
  {
329
431
  "data-vector-pencil-string": "",
@@ -335,8 +437,8 @@ function VectorCanvas({
335
437
  strokeDasharray: "2 2"
336
438
  }
337
439
  ),
338
- 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 }),
339
- 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)(
340
442
  "rect",
341
443
  {
342
444
  "data-vector-marquee": "",
@@ -344,13 +446,13 @@ function VectorCanvas({
344
446
  y: overlay.marquee.y,
345
447
  width: overlay.marquee.width,
346
448
  height: overlay.marquee.height,
347
- fill: import_vector2.VECTOR_OVERLAY_THEME.marqueeFill,
449
+ fill: import_vector3.VECTOR_OVERLAY_THEME.marqueeFill,
348
450
  stroke: accentColor,
349
451
  strokeDasharray: "3 2"
350
452
  }
351
453
  ),
352
- 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)) }),
353
- 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)(
354
456
  "text",
355
457
  {
356
458
  "data-vector-guide-label": l.kind,
@@ -366,46 +468,48 @@ function VectorCanvas({
366
468
  },
367
469
  i
368
470
  )),
369
- 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 })
370
- ] })
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}`) })
371
475
  ]
372
476
  }
373
477
  );
374
478
  }
375
479
 
376
480
  // src/vector/VectorToolbar.tsx
377
- var import_vector3 = require("@pixerate/editor/vector");
378
- 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");
379
483
  var VECTOR_SNAP_TITLE = "Snap to objects";
380
484
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
381
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";
382
- 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 }) {
383
487
  const state = useVectorEditorState(controller);
384
488
  const count = state.selection.length;
385
489
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
386
490
  const editing = state.editing;
387
491
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
388
- 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);
389
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
390
- /* @__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)(
391
495
  "button",
392
496
  {
393
497
  type: "button",
394
498
  "data-vector-tool": tool,
395
499
  className: buttonClass,
396
500
  "aria-pressed": state.tool === tool,
397
- title: `${(0, import_vector3.vectorToolLabel)(tool)} (${import_vector3.VECTOR_TOOL_SHORTCUTS[tool]})`,
398
- "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),
399
503
  onClick: () => controller.setTool(tool),
400
- children: import_vector3.VECTOR_TOOL_GLYPHS[tool]
504
+ children: import_vector4.VECTOR_TOOL_GLYPHS[tool]
401
505
  },
402
506
  tool
403
507
  )) }),
404
- 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: [
405
509
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
406
510
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
407
511
  ] }),
408
- 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)(
409
513
  "button",
410
514
  {
411
515
  type: "button",
@@ -418,8 +522,8 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
418
522
  children: "Snap"
419
523
  }
420
524
  ) }),
421
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
422
- 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)(
423
527
  "button",
424
528
  {
425
529
  type: "button",
@@ -433,7 +537,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
433
537
  },
434
538
  a.id
435
539
  )),
436
- 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)(
437
541
  "button",
438
542
  {
439
543
  type: "button",
@@ -447,9 +551,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
447
551
  },
448
552
  t.key
449
553
  )),
450
- 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: [
451
555
  "Smoothing",
452
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
556
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
453
557
  "input",
454
558
  {
455
559
  type: "range",
@@ -463,9 +567,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
463
567
  }
464
568
  )
465
569
  ] }),
466
- 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: [
467
571
  "Size",
468
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
572
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
469
573
  "input",
470
574
  {
471
575
  type: "number",
@@ -481,7 +585,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
481
585
  }
482
586
  )
483
587
  ] }),
484
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
588
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
485
589
  "button",
486
590
  {
487
591
  type: "button",
@@ -490,13 +594,13 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
490
594
  "aria-pressed": editing.fillGap > 0,
491
595
  title: "Close small gaps between path ends when filling",
492
596
  "aria-label": "Close gaps",
493
- 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 }),
494
598
  children: "Gaps"
495
599
  }
496
600
  )
497
601
  ] }),
498
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
499
- /* @__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: [
500
604
  act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
501
605
  act("\u2212", "Subtract", () => controller.booleanOp("subtract"), count < 2),
502
606
  act("\u2229", "Intersect", () => controller.booleanOp("intersect"), count < 2),
@@ -504,7 +608,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
504
608
  act("G", "Group", () => controller.group(), count < 1),
505
609
  act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
506
610
  ] }),
507
- /* @__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)(
508
612
  "button",
509
613
  {
510
614
  type: "button",
@@ -518,7 +622,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
518
622
  },
519
623
  a.id
520
624
  )) }),
521
- /* @__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: [
522
626
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
523
627
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
524
628
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -535,7 +639,7 @@ var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background
535
639
  var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
536
640
  function VectorStylePanel({ controller, className }) {
537
641
  const state = useVectorEditorState(controller);
538
- const style = (0, import_vector3.getVectorSelectionStyle)(state);
642
+ const style = (0, import_vector4.getVectorSelectionStyle)(state);
539
643
  const d = state.defaults;
540
644
  const source = style ?? {
541
645
  fill: d.fill ?? null,
@@ -548,7 +652,7 @@ function VectorStylePanel({ controller, className }) {
548
652
  arrowAlign: d.arrowAlign ?? "extend",
549
653
  startArrowScale: d.startArrowScale ?? 1,
550
654
  endArrowScale: d.endArrowScale ?? 1,
551
- widthProfile: (0, import_vector3.widthProfilePresetId)(d.strokeWidthProfile),
655
+ widthProfile: (0, import_vector4.widthProfilePresetId)(d.strokeWidthProfile),
552
656
  cornerRadius: d.cornerRadius,
553
657
  cornerKind: d.cornerKind ?? "round",
554
658
  corners: 1,
@@ -561,19 +665,19 @@ function VectorStylePanel({ controller, className }) {
561
665
  };
562
666
  const setProfile = (id) => {
563
667
  if (style) controller.setWidthProfile(id);
564
- 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 });
565
669
  };
566
- 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;
567
671
  const arrowRow = (end, label) => {
568
672
  const key = end === "start" ? "startArrow" : "endArrow";
569
673
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
570
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
571
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
572
- /* @__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: [
573
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: "None" }),
574
- 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))
575
679
  ] }),
576
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
680
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
577
681
  "input",
578
682
  {
579
683
  type: "number",
@@ -597,14 +701,14 @@ function VectorStylePanel({ controller, className }) {
597
701
  };
598
702
  const paintRow = (key, label) => {
599
703
  const paint = source[key];
600
- const hex = (0, import_vector3.vectorPaintToHex)(paint);
704
+ const hex = (0, import_vector4.vectorPaintToHex)(paint);
601
705
  const on = paint !== null && paint !== "none";
602
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
603
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
604
- /* @__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 }) }),
605
709
  label
606
710
  ] }),
607
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
711
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
608
712
  "input",
609
713
  {
610
714
  type: "color",
@@ -615,17 +719,17 @@ function VectorStylePanel({ controller, className }) {
615
719
  onChange: (e) => apply({ [key]: e.target.value })
616
720
  }
617
721
  ),
618
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
619
- 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" })
620
724
  ] });
621
725
  };
622
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
623
- /* @__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" }),
624
728
  paintRow("fill", "Fill"),
625
729
  paintRow("stroke", "Stroke"),
626
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
627
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Width" }),
628
- /* @__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)(
629
733
  "input",
630
734
  {
631
735
  type: "number",
@@ -641,14 +745,14 @@ function VectorStylePanel({ controller, className }) {
641
745
  }
642
746
  )
643
747
  ] }),
644
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
645
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Align" }),
646
- /* @__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)) }),
647
751
  mixed("strokeAlign")
648
752
  ] }),
649
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
650
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Profile" }),
651
- /* @__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)(
652
756
  "select",
653
757
  {
654
758
  "aria-label": "Width profile",
@@ -658,12 +762,12 @@ function VectorStylePanel({ controller, className }) {
658
762
  if (e.target.value !== "custom") setProfile(e.target.value);
659
763
  },
660
764
  children: [
661
- import_vector3.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: p.id, children: p.label }, p.id)),
662
- 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" })
663
767
  ]
664
768
  }
665
769
  ),
666
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
770
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
667
771
  "button",
668
772
  {
669
773
  type: "button",
@@ -677,20 +781,20 @@ function VectorStylePanel({ controller, className }) {
677
781
  ),
678
782
  mixed("widthProfile")
679
783
  ] }),
680
- 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: [
681
785
  arrowRow("start", "Start"),
682
786
  arrowRow("end", "End"),
683
- (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: [
684
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Heads" }),
685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
686
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "extend", children: "Tip past end" }),
687
- /* @__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" })
688
792
  ] })
689
793
  ] })
690
794
  ] }),
691
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
692
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
693
- /* @__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)(
694
798
  "input",
695
799
  {
696
800
  type: "number",
@@ -705,12 +809,12 @@ function VectorStylePanel({ controller, className }) {
705
809
  }
706
810
  }
707
811
  ),
708
- /* @__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)) }),
709
813
  mixed("cornerRadius")
710
814
  ] }),
711
- style && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
712
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-16", children: "Opacity" }),
713
- /* @__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)(
714
818
  "input",
715
819
  {
716
820
  type: "range",
@@ -728,13 +832,13 @@ function VectorStylePanel({ controller, className }) {
728
832
  }
729
833
 
730
834
  // src/vector/VectorEditor.tsx
731
- var import_jsx_runtime3 = require("react/jsx-runtime");
835
+ var import_jsx_runtime4 = require("react/jsx-runtime");
732
836
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
733
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
734
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VectorToolbar, { controller, tools }),
735
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
736
- /* @__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 }) }),
737
- 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" })
738
842
  ] })
739
843
  ] });
740
844
  }
@@ -746,6 +850,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
746
850
  VectorSceneNodes,
747
851
  VectorStylePanel,
748
852
  VectorToolbar,
853
+ VectorWidthPointEditor,
749
854
  useVectorCommands,
750
855
  useVectorEditor,
751
856
  useVectorEditorController,