@pixerate/editor-react 0.23.1 → 0.24.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.
@@ -112,6 +112,7 @@ function VectorCanvas({
112
112
  fitOnMount = true,
113
113
  checkerboard = true,
114
114
  accentColor = import_vector2.VECTOR_OVERLAY_THEME.accent,
115
+ guideColor = import_vector2.VECTOR_OVERLAY_THEME.guide,
115
116
  className,
116
117
  style,
117
118
  "aria-label": ariaLabel = "Vector canvas"
@@ -254,6 +255,23 @@ function VectorCanvas({
254
255
  strokeDasharray: "3 2"
255
256
  }
256
257
  ),
258
+ 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)) }),
259
+ overlay.guides.labels.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
260
+ "text",
261
+ {
262
+ "data-vector-guide-label": l.kind,
263
+ x: l.x + 6,
264
+ y: l.y - 6,
265
+ fontSize: 10,
266
+ fill: guideColor,
267
+ stroke: "#ffffff",
268
+ strokeWidth: 3,
269
+ paintOrder: "stroke",
270
+ style: { fontFamily: "system-ui, sans-serif" },
271
+ children: l.text
272
+ },
273
+ i
274
+ )),
257
275
  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 })
258
276
  ] })
259
277
  ]
@@ -264,8 +282,10 @@ function VectorCanvas({
264
282
  // src/vector/VectorToolbar.tsx
265
283
  var import_vector3 = require("@pixerate/editor/vector");
266
284
  var import_jsx_runtime2 = require("react/jsx-runtime");
285
+ var VECTOR_SNAP_TITLE = "Snap to objects";
286
+ var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
267
287
  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";
268
- function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHistory = true, showArrange = true, className }) {
288
+ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }) {
269
289
  const state = useVectorEditorState(controller);
270
290
  const count = state.selection.length;
271
291
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
@@ -289,6 +309,19 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
289
309
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
290
310
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
291
311
  ] }),
312
+ 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)(
313
+ "button",
314
+ {
315
+ type: "button",
316
+ "data-vector-snap": "",
317
+ className: buttonClass,
318
+ "aria-pressed": state.snap.objects,
319
+ title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
320
+ "aria-label": VECTOR_SNAP_TITLE,
321
+ onClick: () => controller.toggleSnapping(),
322
+ children: "Snap"
323
+ }
324
+ ) }),
292
325
  showArrange && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
293
326
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
294
327
  act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vector/index.ts","../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["export * from './useVectorEditor';\nexport * from './VectorCanvas';\nexport * from './VectorToolbar';\nexport * from './VectorEditor';\n","import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n transform={node.transform}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\" transform={box.transform || undefined}>\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_vector","THEME","text","import_vector","import_jsx_runtime","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/vector/index.ts","../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["export * from './useVectorEditor';\nexport * from './VectorCanvas';\nexport * from './VectorToolbar';\nexport * from './VectorEditor';\n","import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n /** Smart guide colour (default magenta). */\n guideColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n transform={node.transform}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n guideColor = THEME.guide,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\" transform={box.transform || undefined}>\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.guides.lines.length > 0 && (\n <g data-vector-guides=\"\" stroke={guideColor}>\n {overlay.guides.lines.map((l, i) => (\n <line key={i} data-vector-guide={l.kind} x1={l.x1} y1={l.y1} x2={l.x2} y2={l.y2} strokeDasharray={l.kind === 'construction' ? '4 3' : undefined} />\n ))}\n </g>\n )}\n {overlay.guides.labels.map((l, i) => (\n <text\n key={i}\n data-vector-guide-label={l.kind}\n x={l.x + 6}\n y={l.y - 6}\n fontSize={10}\n fill={guideColor}\n stroke=\"#ffffff\"\n strokeWidth={3}\n paintOrder=\"stroke\"\n style={{ fontFamily: 'system-ui, sans-serif' }}\n >\n {l.text}\n </text>\n ))}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst VECTOR_SNAP_TITLE = 'Snap to objects';\nconst modKeyLabel = () => (typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform ?? '') ? '⌘' : 'Ctrl');\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n /** The snapping toggle (default `true`). */\n showSnap?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showSnap && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Snapping\">\n <button\n type=\"button\"\n data-vector-snap=\"\"\n className={buttonClass}\n aria-pressed={state.snap.objects}\n title={`${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`}\n aria-label={VECTOR_SNAP_TITLE}\n onClick={() => controller.toggleSnapping()}\n >\n Snap\n </button>\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_vector","THEME","text","import_vector","import_jsx_runtime","import_jsx_runtime"]}
@@ -37,6 +37,8 @@ interface VectorCanvasProps {
37
37
  checkerboard?: boolean;
38
38
  /** Overlay accent colour (default sky blue). */
39
39
  accentColor?: string;
40
+ /** Smart guide colour (default magenta). */
41
+ guideColor?: string;
40
42
  className?: string;
41
43
  style?: React__default.CSSProperties;
42
44
  /** Accessible label (default "Vector canvas"). */
@@ -54,7 +56,7 @@ declare function VectorSceneNodes({ nodes }: {
54
56
  * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay
55
57
  * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.
56
58
  */
57
- declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
59
+ declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, guideColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
58
60
 
59
61
  interface VectorToolbarProps {
60
62
  controller: VectorEditorController;
@@ -64,10 +66,12 @@ interface VectorToolbarProps {
64
66
  showHistory?: boolean;
65
67
  /** Boolean, group, arrange and align buttons (default `true`). */
66
68
  showArrange?: boolean;
69
+ /** The snapping toggle (default `true`). */
70
+ showSnap?: boolean;
67
71
  className?: string;
68
72
  }
69
- /** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */
70
- declare function VectorToolbar({ controller, tools, showHistory, showArrange, className }: VectorToolbarProps): React__default.JSX.Element;
73
+ /** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */
74
+ declare function VectorToolbar({ controller, tools, showHistory, showArrange, showSnap, className }: VectorToolbarProps): React__default.JSX.Element;
71
75
  interface VectorStylePanelProps {
72
76
  controller: VectorEditorController;
73
77
  className?: string;
@@ -37,6 +37,8 @@ interface VectorCanvasProps {
37
37
  checkerboard?: boolean;
38
38
  /** Overlay accent colour (default sky blue). */
39
39
  accentColor?: string;
40
+ /** Smart guide colour (default magenta). */
41
+ guideColor?: string;
40
42
  className?: string;
41
43
  style?: React__default.CSSProperties;
42
44
  /** Accessible label (default "Vector canvas"). */
@@ -54,7 +56,7 @@ declare function VectorSceneNodes({ nodes }: {
54
56
  * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay
55
57
  * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.
56
58
  */
57
- declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
59
+ declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, guideColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
58
60
 
59
61
  interface VectorToolbarProps {
60
62
  controller: VectorEditorController;
@@ -64,10 +66,12 @@ interface VectorToolbarProps {
64
66
  showHistory?: boolean;
65
67
  /** Boolean, group, arrange and align buttons (default `true`). */
66
68
  showArrange?: boolean;
69
+ /** The snapping toggle (default `true`). */
70
+ showSnap?: boolean;
67
71
  className?: string;
68
72
  }
69
- /** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */
70
- declare function VectorToolbar({ controller, tools, showHistory, showArrange, className }: VectorToolbarProps): React__default.JSX.Element;
73
+ /** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */
74
+ declare function VectorToolbar({ controller, tools, showHistory, showArrange, showSnap, className }: VectorToolbarProps): React__default.JSX.Element;
71
75
  interface VectorStylePanelProps {
72
76
  controller: VectorEditorController;
73
77
  className?: string;
@@ -87,6 +87,7 @@ function VectorCanvas({
87
87
  fitOnMount = true,
88
88
  checkerboard = true,
89
89
  accentColor = THEME.accent,
90
+ guideColor = THEME.guide,
90
91
  className,
91
92
  style,
92
93
  "aria-label": ariaLabel = "Vector canvas"
@@ -229,6 +230,23 @@ function VectorCanvas({
229
230
  strokeDasharray: "3 2"
230
231
  }
231
232
  ),
233
+ overlay.guides.lines.length > 0 && /* @__PURE__ */ jsx("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ 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)) }),
234
+ overlay.guides.labels.map((l, i) => /* @__PURE__ */ jsx(
235
+ "text",
236
+ {
237
+ "data-vector-guide-label": l.kind,
238
+ x: l.x + 6,
239
+ y: l.y - 6,
240
+ fontSize: 10,
241
+ fill: guideColor,
242
+ stroke: "#ffffff",
243
+ strokeWidth: 3,
244
+ paintOrder: "stroke",
245
+ style: { fontFamily: "system-ui, sans-serif" },
246
+ children: l.text
247
+ },
248
+ i
249
+ )),
232
250
  overlay.status && /* @__PURE__ */ jsx("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
233
251
  ] })
234
252
  ]
@@ -246,8 +264,10 @@ import {
246
264
  vectorToolLabel
247
265
  } from "@pixerate/editor/vector";
248
266
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
267
+ var VECTOR_SNAP_TITLE = "Snap to objects";
268
+ var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
249
269
  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";
250
- function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, className }) {
270
+ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }) {
251
271
  const state = useVectorEditorState(controller);
252
272
  const count = state.selection.length;
253
273
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
@@ -271,6 +291,19 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
271
291
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
272
292
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
273
293
  ] }),
294
+ showSnap && /* @__PURE__ */ jsx2("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ jsx2(
295
+ "button",
296
+ {
297
+ type: "button",
298
+ "data-vector-snap": "",
299
+ className: buttonClass,
300
+ "aria-pressed": state.snap.objects,
301
+ title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
302
+ "aria-label": VECTOR_SNAP_TITLE,
303
+ onClick: () => controller.toggleSnapping(),
304
+ children: "Snap"
305
+ }
306
+ ) }),
274
307
  showArrange && /* @__PURE__ */ jsxs2(Fragment2, { children: [
275
308
  /* @__PURE__ */ jsxs2("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
276
309
  act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n transform={node.transform}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\" transform={box.transform || undefined}>\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","text","Fragment","jsx","jsxs","jsx","jsxs"]}
1
+ {"version":3,"sources":["../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n /** Smart guide colour (default magenta). */\n guideColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n transform={node.transform}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n guideColor = THEME.guide,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\" transform={box.transform || undefined}>\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.guides.lines.length > 0 && (\n <g data-vector-guides=\"\" stroke={guideColor}>\n {overlay.guides.lines.map((l, i) => (\n <line key={i} data-vector-guide={l.kind} x1={l.x1} y1={l.y1} x2={l.x2} y2={l.y2} strokeDasharray={l.kind === 'construction' ? '4 3' : undefined} />\n ))}\n </g>\n )}\n {overlay.guides.labels.map((l, i) => (\n <text\n key={i}\n data-vector-guide-label={l.kind}\n x={l.x + 6}\n y={l.y - 6}\n fontSize={10}\n fill={guideColor}\n stroke=\"#ffffff\"\n strokeWidth={3}\n paintOrder=\"stroke\"\n style={{ fontFamily: 'system-ui, sans-serif' }}\n >\n {l.text}\n </text>\n ))}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst VECTOR_SNAP_TITLE = 'Snap to objects';\nconst modKeyLabel = () => (typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform ?? '') ? '⌘' : 'Ctrl');\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n /** The snapping toggle (default `true`). */\n showSnap?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showSnap && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Snapping\">\n <button\n type=\"button\"\n data-vector-snap=\"\"\n className={buttonClass}\n aria-pressed={state.snap.objects}\n title={`${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`}\n aria-label={VECTOR_SNAP_TITLE}\n onClick={() => controller.toggleSnapping()}\n >\n Snap\n </button>\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","text","Fragment","jsx","jsxs","jsx","jsxs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-react",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "React bindings and prompt/rich-text components for @pixerate/editor",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -107,7 +107,7 @@
107
107
  },
108
108
  "peerDependencies": {
109
109
  "@dagrejs/dagre": "^1.1.8",
110
- "@pixerate/editor": ">=0.33.0 <1.0.0-0",
110
+ "@pixerate/editor": ">=0.34.0 <1.0.0-0",
111
111
  "@tiptap/core": "^2.11.5",
112
112
  "@tiptap/pm": "^2.11.5",
113
113
  "@tiptap/react": "^2.11.5",
@@ -134,7 +134,7 @@
134
134
  "tsup": "^8.4.0",
135
135
  "typescript": "^5.7.3",
136
136
  "vitest": "^4.1.11",
137
- "@pixerate/editor": "0.33.0"
137
+ "@pixerate/editor": "0.34.0"
138
138
  },
139
139
  "scripts": {
140
140
  "build": "tsup",