@bwp-web/canvas 0.6.2 → 0.7.1

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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fabricAugmentation.ts","../src/Canvas/Canvas.tsx","../src/keyboard.ts","../src/hooks/useEditCanvas.ts","../src/viewport.ts","../src/constants.ts","../src/hooks/shared.ts","../src/background.ts","../src/alignment/snapPoints.ts","../src/alignment/objectAlignmentUtils.ts","../src/alignment/objectAlignment.ts","../src/alignment/objectAlignmentMath.ts","../src/alignment/rotationSnap.ts","../src/alignment/cursorSnapping.ts","../src/interactions/shared.ts","../src/interactions/clickToCreate.ts","../src/interactions/dragToCreate.ts","../src/styles.ts","../src/interactions/interactionSnapping.ts","../src/interactions/drawToCreate.ts","../src/shapes/rectangle.ts","../src/shapes/circle.ts","../src/shapes/polygon.ts","../src/interactions/vertexEdit.ts","../src/serialization.ts","../src/history.ts","../src/hooks/useViewCanvas.ts","../src/hooks/useCanvasEvents.ts","../src/hooks/useCanvasTooltip.ts","../src/hooks/useCanvasClick.ts","../src/hooks/useObjectOverlay.ts","../src/index.ts"],"sourcesContent":["import 'fabric';\n\nexport type ShapeType = 'circle';\n\n/** Valid `data.type` values for canvas objects. */\nexport type ObjectDataType =\n | 'PLACE'\n | 'DEVICE'\n | 'DESK'\n | 'PARKING_SPACE'\n | 'FACILITY';\n\ndeclare module 'fabric' {\n interface FabricObject {\n shapeType?: ShapeType;\n data?: {\n type: ObjectDataType;\n id: string;\n };\n }\n interface Canvas {\n lockLightMode?: boolean;\n }\n}\n","import { Canvas as FabricCanvas } from 'fabric';\nimport { type CSSProperties, useEffect, useRef } from 'react';\nimport { enableKeyboardShortcuts } from '../keyboard';\n\nexport interface CanvasProps {\n /**\n * Canvas width in pixels. When both `width` and `height` are provided,\n * the canvas uses fixed dimensions. When omitted, the canvas auto-fills\n * its parent container and resizes with it.\n */\n width?: number;\n /**\n * Canvas height in pixels. When both `width` and `height` are provided,\n * the canvas uses fixed dimensions. When omitted, the canvas auto-fills\n * its parent container and resizes with it.\n */\n height?: number;\n className?: string;\n style?: CSSProperties;\n onReady?: (canvas: FabricCanvas) => void;\n /**\n * Enable Delete/Backspace keyboard shortcuts for removing selected objects.\n * Default: `false`. Set to `true` when using `<Canvas>` without `useEditCanvas`\n * (which registers its own shortcuts).\n */\n keyboardShortcuts?: boolean;\n /**\n * Additional options passed to the Fabric.js `Canvas` constructor.\n * Merged before `width`/`height`, so those props take precedence.\n */\n fabricOptions?: Record<string, unknown>;\n}\n\nexport function Canvas({\n width,\n height,\n className,\n style,\n onReady,\n keyboardShortcuts,\n fabricOptions,\n}: CanvasProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const isFixedSize = width !== undefined && height !== undefined;\n\n useEffect(() => {\n const el = canvasRef.current;\n const wrapper = wrapperRef.current;\n if (!el || !wrapper) return;\n\n const initialWidth = isFixedSize ? width : wrapper.clientWidth || 800;\n const initialHeight = isFixedSize ? height : wrapper.clientHeight || 600;\n\n const fabricCanvas = new FabricCanvas(el, {\n ...fabricOptions,\n width: initialWidth,\n height: initialHeight,\n });\n\n onReady?.(fabricCanvas);\n\n const cleanupShortcuts = keyboardShortcuts\n ? enableKeyboardShortcuts(fabricCanvas)\n : undefined;\n\n let observer: ResizeObserver | undefined;\n let rafId = 0;\n\n if (!isFixedSize) {\n let currentWidth = initialWidth;\n let currentHeight = initialHeight;\n\n observer = new ResizeObserver((entries) => {\n cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n const entry = entries[0];\n if (!entry) return;\n const { width: newWidth, height: newHeight } = entry.contentRect;\n if (\n newWidth > 0 &&\n newHeight > 0 &&\n (newWidth !== currentWidth || newHeight !== currentHeight)\n ) {\n currentWidth = newWidth;\n currentHeight = newHeight;\n fabricCanvas.setDimensions({ width: newWidth, height: newHeight });\n }\n });\n });\n observer.observe(wrapper);\n }\n\n return () => {\n cancelAnimationFrame(rafId);\n observer?.disconnect();\n cleanupShortcuts?.();\n fabricCanvas.dispose();\n };\n // onReady is intentionally excluded — we only want to initialise once on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const wrapperStyle: CSSProperties = isFixedSize\n ? { ...style }\n : { width: '100%', height: '100%', ...style };\n\n return (\n <div ref={wrapperRef} className={className} style={wrapperStyle}>\n <canvas ref={canvasRef} />\n </div>\n );\n}\n","import { Canvas as FabricCanvas, FabricObject } from 'fabric';\n\n/**\n * Remove one or more objects from the canvas.\n */\nexport function deleteObjects(\n canvas: FabricCanvas,\n ...objects: FabricObject[]\n): void {\n canvas.remove(...objects);\n canvas.requestRenderAll();\n}\n\n/**\n * Enable default keyboard shortcuts on the canvas.\n * - Escape / Delete / Backspace: delete selected objects.\n *\n * Returns a cleanup function that removes the listener.\n */\nexport function enableKeyboardShortcuts(canvas: FabricCanvas): () => void {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' || e.key === 'Delete' || e.key === 'Backspace') {\n const active = canvas.getActiveObjects();\n if (active.length > 0) {\n canvas.discardActiveObject();\n deleteObjects(canvas, ...active);\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject, Polygon } from 'fabric';\nimport {\n enablePanAndZoom,\n type PanAndZoomOptions,\n type ViewportController,\n type ViewportMode,\n} from '../viewport';\nimport {\n useViewportActions,\n resolveAlignmentEnabled,\n syncZoom,\n} from './shared';\nimport {\n enableObjectAlignment,\n type ObjectAlignmentOptions,\n enableRotationSnap,\n type RotationSnapOptions,\n} from '../alignment';\nimport {\n enableVertexEdit,\n setCanvasAlignmentEnabled,\n type VertexEditOptions,\n} from '../interactions';\nimport {\n enableScaledStrokes,\n enableScaledBorderRadius,\n type ScaledBorderRadiusOptions,\n} from '../serialization';\nimport { enableKeyboardShortcuts } from '../keyboard';\nimport {\n fitViewportToBackground,\n setBackgroundImage as setBackgroundImageFn,\n type ResizeImageOptions,\n type SetBackgroundImageOptions,\n} from '../background';\nimport {\n createHistoryTracker,\n type HistoryOptions,\n type HistoryTracker,\n} from '../history';\nimport type { ModeSetup } from '../types';\n\nexport interface UseEditCanvasOptions {\n /** Configure pan and zoom. Pass `false` to disable, or options to customize. Default: enabled. */\n panAndZoom?: boolean | PanAndZoomOptions;\n /** Enable alignment guidelines for object movement/scaling. Pass `false` to disable, or options to customize. Default: enabled. */\n alignment?: boolean | ObjectAlignmentOptions;\n /**\n * Snap rotation to angle intervals when Shift is held. Pass `false` to\n * disable, or `{ interval }` to change the snap interval. Default: enabled at 15° intervals.\n */\n rotationSnap?: boolean | RotationSnapOptions;\n /**\n * Master toggle for all alignment and snapping functionality.\n * - `undefined`: each feature uses its own prop (default).\n * - `true`: all alignment/snapping is force-enabled.\n * - `false`: all alignment/snapping is force-disabled.\n */\n enableAlignment?: boolean;\n /**\n * Enable double-click-to-vertex-edit on polygons.\n * Pass `false` to disable, or a `VertexEditOptions` object to customize handle appearance.\n * Default: enabled.\n */\n vertexEdit?: boolean | VertexEditOptions;\n /**\n * Keep stroke widths visually constant as the user zooms in/out.\n * Pass `false` to disable. Default: enabled.\n */\n scaledStrokes?: boolean;\n /**\n * Enable keyboard shortcuts (Delete/Backspace to delete selected objects).\n * Pass `false` to disable. Default: enabled.\n */\n keyboardShortcuts?: boolean;\n /** Called after the canvas is initialized and viewport is set up. */\n onReady?: (canvas: FabricCanvas) => void | Promise<void>;\n /**\n * Automatically fit the viewport to the background image after `onReady`\n * completes, if a background image is present. Also applies when\n * `viewport.reset` is called while a background image is set.\n * Pass `false` to disable. Default: enabled.\n */\n autoFitToBackground?: boolean;\n /**\n * Automatically resize background images when set via `setBackground`.\n * Images exceeding `maxSize` are downscaled to fit; images smaller than\n * `minSize` on both dimensions are rejected with an error.\n * Pass `false` to disable. Default: enabled (maxSize: 4096, minSize: 480).\n */\n backgroundResize?: boolean | ResizeImageOptions;\n /**\n * Track object add/remove/modify events and expose an `isDirty` flag.\n * Call `resetDirty()` after a successful save to clear the flag.\n * Default: disabled.\n */\n trackChanges?: boolean;\n /**\n * Visual border radius applied to loaded Rects (via `loadCanvas`).\n * Pass a number to customize (default: 4), or `false` to disable.\n */\n borderRadius?: number | false;\n /**\n * Enable snapshot-based undo/redo. Pass `true` for defaults, or an options\n * object to customize `maxSize` (default: 50) and `debounce` (default: 300ms).\n * Default: disabled.\n */\n history?: boolean | HistoryOptions;\n}\n\n/**\n * Hook that provides a batteries-included canvas experience with full\n * editing capabilities (create, edit, delete, select, pan, zoom).\n *\n * Handles canvas ref management, viewport setup (pan/zoom), reactive state\n * (zoom level, selected objects), and interaction mode lifecycle.\n *\n * @example\n * ```tsx\n * const canvas = useEditCanvas();\n *\n * // Activate a creation mode:\n * canvas.setMode((c, viewport) =>\n * enableClickToCreate(c, factory, { viewport, onCreated: () => canvas.setMode(null) })\n * );\n *\n * // Return to select mode:\n * canvas.setMode(null);\n *\n * return <Canvas onReady={canvas.onReady} width={800} height={600} />;\n * ```\n */\nexport function useEditCanvas(options?: UseEditCanvasOptions) {\n const canvasRef = useRef<FabricCanvas | null>(null);\n const viewportRef = useRef<ViewportController | null>(null);\n const alignmentCleanupRef = useRef<(() => void) | null>(null);\n const rotationSnapCleanupRef = useRef<(() => void) | null>(null);\n const modeCleanupRef = useRef<(() => void) | null>(null);\n const vertexEditCleanupRef = useRef<(() => void) | null>(null);\n const keyboardCleanupRef = useRef<(() => void) | null>(null);\n const historyRef = useRef<HistoryTracker | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // WeakMap to save per-object selectability before entering a mode\n const savedSelectabilityRef = useRef(\n new WeakMap<FabricObject, { selectable: boolean; evented: boolean }>(),\n );\n\n const [zoom, setZoom] = useState(1);\n const [selected, setSelected] = useState<FabricObject[]>([]);\n const [viewportMode, setViewportModeState] = useState<ViewportMode>('select');\n const [isEditingVertices, setIsEditingVertices] = useState(false);\n const [isDirty, setIsDirty] = useState(false);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n /**\n * Activate an interaction mode, or pass `null` to return to select mode.\n *\n * When a setup function is provided, the hook automatically:\n * - Cleans up the previous mode\n * - Saves and disables object selectability\n * - Calls `setup(canvas, viewport)` and stores its cleanup\n *\n * When `null` is passed, saved selectability is restored.\n */\n const setMode = useCallback((setup: ModeSetup | null) => {\n // Exit any active vertex edit session before switching modes\n vertexEditCleanupRef.current?.();\n vertexEditCleanupRef.current = null;\n setIsEditingVertices(false);\n\n modeCleanupRef.current?.();\n modeCleanupRef.current = null;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n if (setup === null) {\n // Restore saved selectability, defaulting to true for objects added during mode\n canvas.selection = true;\n canvas.forEachObject((obj) => {\n const saved = savedSelectabilityRef.current.get(obj);\n obj.selectable = saved?.selectable ?? true;\n obj.evented = saved?.evented ?? true;\n });\n } else {\n // Save current selectability before disabling\n canvas.forEachObject((obj) => {\n savedSelectabilityRef.current.set(obj, {\n selectable: obj.selectable,\n evented: obj.evented,\n });\n });\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n obj.evented = false;\n });\n const cleanup = setup(canvas, viewportRef.current ?? undefined);\n if (cleanup) {\n modeCleanupRef.current = cleanup;\n }\n }\n }, []);\n\n const onReady = useCallback(\n (canvas: FabricCanvas) => {\n canvasRef.current = canvas;\n const opts = optionsRef.current;\n\n setupFeatures();\n setupEventListeners();\n invokeOnReady();\n\n // --- Feature setup (strokes, viewport, alignment, etc.) ---\n\n function setupFeatures() {\n if (opts?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (opts?.borderRadius !== false) {\n const borderRadiusOpts: ScaledBorderRadiusOptions | undefined =\n typeof opts?.borderRadius === 'number'\n ? { radius: opts.borderRadius }\n : undefined;\n enableScaledBorderRadius(canvas, borderRadiusOpts);\n }\n\n if (opts?.keyboardShortcuts !== false) {\n keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);\n }\n\n setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);\n\n if (opts?.panAndZoom !== false) {\n viewportRef.current = enablePanAndZoom(\n canvas,\n typeof opts?.panAndZoom === 'object' ? opts.panAndZoom : undefined,\n );\n }\n\n const alignmentEnabled = resolveAlignmentEnabled(\n opts?.enableAlignment,\n opts?.alignment,\n );\n if (alignmentEnabled) {\n alignmentCleanupRef.current = enableObjectAlignment(\n canvas,\n typeof opts?.alignment === 'object' ? opts.alignment : undefined,\n );\n }\n\n if (opts?.rotationSnap !== false) {\n rotationSnapCleanupRef.current = enableRotationSnap(\n canvas,\n typeof opts?.rotationSnap === 'object'\n ? opts.rotationSnap\n : undefined,\n );\n }\n\n if (opts?.history) {\n const historyOpts =\n typeof opts.history === 'object' ? opts.history : undefined;\n historyRef.current = createHistoryTracker(canvas, historyOpts);\n }\n }\n\n // --- Event listeners (selection, zoom, dirty tracking, vertex edit) ---\n\n function setupEventListeners() {\n canvas.on('mouse:wheel', () => setZoom(canvas.getZoom()));\n\n canvas.on('selection:created', (e) => setSelected(e.selected ?? []));\n canvas.on('selection:updated', (e) => setSelected(e.selected ?? []));\n canvas.on('selection:cleared', () => setSelected([]));\n\n if (opts?.trackChanges) {\n canvas.on('object:added', () => setIsDirty(true));\n canvas.on('object:removed', () => setIsDirty(true));\n canvas.on('object:modified', () => setIsDirty(true));\n }\n\n if (opts?.history) {\n const syncHistoryState = () => {\n const h = historyRef.current;\n if (!h) return;\n setTimeout(() => {\n setCanUndo(h.canUndo());\n setCanRedo(h.canRedo());\n }, 350);\n };\n canvas.on('object:added', syncHistoryState);\n canvas.on('object:removed', syncHistoryState);\n canvas.on('object:modified', syncHistoryState);\n }\n\n if (opts?.vertexEdit !== false) {\n const vertexOpts =\n typeof opts?.vertexEdit === 'object' ? opts.vertexEdit : undefined;\n canvas.on('mouse:dblclick', (e) => {\n if (e.target && e.target instanceof Polygon) {\n vertexEditCleanupRef.current?.();\n vertexEditCleanupRef.current = enableVertexEdit(\n canvas,\n e.target,\n {\n ...vertexOpts,\n onExit: () => {\n vertexEditCleanupRef.current = null;\n setIsEditingVertices(false);\n },\n },\n );\n setIsEditingVertices(true);\n }\n });\n }\n }\n\n // --- Invoke consumer onReady, then auto-fit viewport + push initial history snapshot ---\n\n function invokeOnReady() {\n const onReadyResult = opts?.onReady?.(canvas);\n Promise.resolve(onReadyResult).then(() => {\n if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n historyRef.current?.pushSnapshot();\n });\n }\n },\n // Dependency array intentionally empty — we only initialize once on mount\n [],\n );\n\n // React to enableAlignment changes — tear down or set up object alignment,\n // and update the canvas-level alignment state for interaction modes.\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n // Keep canvas-level alignment state in sync\n setCanvasAlignmentEnabled(canvas, options?.enableAlignment);\n\n const shouldEnable = resolveAlignmentEnabled(\n options?.enableAlignment,\n options?.alignment,\n );\n\n if (shouldEnable && !alignmentCleanupRef.current) {\n alignmentCleanupRef.current = enableObjectAlignment(\n canvas,\n typeof options?.alignment === 'object' ? options.alignment : undefined,\n );\n } else if (!shouldEnable && alignmentCleanupRef.current) {\n alignmentCleanupRef.current();\n alignmentCleanupRef.current = null;\n }\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options?.enableAlignment]);\n\n const setViewportMode = useCallback((mode: ViewportMode) => {\n viewportRef.current?.setMode(mode);\n setViewportModeState(mode);\n }, []);\n\n const { resetViewport, zoomIn, zoomOut, panToObject, zoomToFit } =\n useViewportActions(canvasRef, viewportRef, setZoom);\n\n const setBackground = useCallback(\n async (url: string, bgOpts?: { preserveContrast?: boolean }) => {\n const canvas = canvasRef.current;\n if (!canvas) throw new Error('Canvas not ready');\n\n const opts = optionsRef.current;\n const resizeOpts: SetBackgroundImageOptions | undefined =\n opts?.backgroundResize !== false\n ? typeof opts?.backgroundResize === 'object'\n ? { ...opts.backgroundResize, ...bgOpts }\n : { ...bgOpts }\n : bgOpts?.preserveContrast\n ? { preserveContrast: true }\n : undefined;\n\n const img = await setBackgroundImageFn(canvas, url, resizeOpts);\n\n if (opts?.autoFitToBackground !== false) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n\n return img;\n },\n [],\n );\n\n return {\n /** Pass this to `<Canvas onReady={...} />` */\n onReady,\n /** Ref to the underlying Fabric canvas instance. */\n canvasRef,\n /** Current zoom level (reactive). */\n zoom,\n /** Currently selected objects (reactive). */\n selected,\n /** Viewport controls. */\n viewport: {\n /** Current viewport mode (reactive). */\n mode: viewportMode,\n /** Switch between 'select' and 'pan' viewport modes. */\n setMode: setViewportMode,\n /** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */\n reset: resetViewport,\n /** Zoom in toward the canvas center. Default step: 0.2. */\n zoomIn,\n /** Zoom out from the canvas center. Default step: 0.2. */\n zoomOut,\n /** Pan the viewport to center on a specific object. */\n panToObject,\n /** Zoom and pan to fit a specific object in the viewport. */\n zoomToFit,\n },\n /** Whether vertex edit mode is currently active (reactive). */\n isEditingVertices,\n /**\n * Activate an interaction mode or return to select mode.\n *\n * Pass a setup function to activate a creation mode:\n * ```ts\n * canvas.setMode((c, viewport) =>\n * enableClickToCreate(c, factory, { viewport })\n * );\n * ```\n *\n * Pass `null` to deactivate and return to select mode:\n * ```ts\n * canvas.setMode(null);\n * ```\n */\n setMode,\n /**\n * Set a background image from a URL. Automatically resizes if the image\n * exceeds the configured limits (opt out via `backgroundResize: false`),\n * and fits the viewport after setting if `autoFitToBackground` is enabled.\n *\n * Pass `{ preserveContrast: true }` to keep the current background contrast\n * when replacing the image.\n */\n setBackground,\n /** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */\n isDirty,\n /** Reset the dirty flag (e.g., after a successful save). */\n resetDirty: useCallback(() => setIsDirty(false), []),\n /** Undo the last change. Requires `history: true`. */\n undo: useCallback(async () => {\n const h = historyRef.current;\n if (!h) return;\n await h.undo();\n setCanUndo(h.canUndo());\n setCanRedo(h.canRedo());\n }, []),\n /** Redo a previously undone change. Requires `history: true`. */\n redo: useCallback(async () => {\n const h = historyRef.current;\n if (!h) return;\n await h.redo();\n setCanUndo(h.canUndo());\n setCanRedo(h.canRedo());\n }, []),\n /** Whether an undo operation is available (reactive). Requires `history: true`. */\n canUndo,\n /** Whether a redo operation is available (reactive). Requires `history: true`. */\n canRedo,\n };\n}\n","import {\n Canvas as FabricCanvas,\n type FabricObject,\n Point,\n TPointerEvent,\n} from 'fabric';\nimport {\n DEFAULT_MIN_ZOOM,\n DEFAULT_MAX_ZOOM,\n DEFAULT_ZOOM_FACTOR,\n DEFAULT_ZOOM_STEP,\n} from './constants';\n\nexport type ViewportMode = 'select' | 'pan';\n\nexport interface PanToObjectOptions {\n /** Animate the pan with an easing transition. Default: false. */\n animate?: boolean;\n /** Animation duration in milliseconds when `animate` is true. Default: 300. */\n duration?: number;\n}\n\nexport interface PanAndZoomOptions {\n /** Minimum zoom level (default: 0.2) */\n minZoom?: number;\n /** Maximum zoom level (default: 10) */\n maxZoom?: number;\n /** Zoom sensitivity base — raised to the power of deltaY (default: 0.999). */\n zoomFactor?: number;\n /** Initial viewport mode (default: 'select') */\n initialMode?: ViewportMode;\n}\n\nexport interface ZoomToFitOptions {\n /** Padding fraction around the object (default: 0.1 = 10% on each side). */\n padding?: number;\n}\n\nexport interface ViewportController {\n /** Switch between select and pan modes. */\n setMode: (mode: ViewportMode) => void;\n /** Get the current viewport mode. */\n getMode: () => ViewportMode;\n /** Temporarily disable all pan and zoom input (e.g. during draw modes). */\n setEnabled: (enabled: boolean) => void;\n /**\n * Zoom in toward the canvas center by a multiplicative factor.\n * Respects the configured min/max zoom bounds. Default factor: 1.2 (20%).\n */\n zoomIn: (factor?: number) => void;\n /**\n * Zoom out from the canvas center by a multiplicative factor.\n * Respects the configured min/max zoom bounds. Default factor: 1.2 (20%).\n */\n zoomOut: (factor?: number) => void;\n /**\n * Pan the viewport so the given object is centered on the canvas.\n * Optionally animate the transition. Automatically cancels any\n * in-progress panToObject animation.\n */\n panToObject: (object: FabricObject, options?: PanToObjectOptions) => void;\n /**\n * Zoom and pan the viewport so the given object fills the canvas\n * with optional padding.\n */\n zoomToFit: (object: FabricObject, options?: ZoomToFitOptions) => void;\n /** Remove all event listeners. */\n cleanup: () => void;\n}\n\ninterface ZoomBounds {\n minZoom: number;\n maxZoom: number;\n}\n\n// --- Pointer helpers ---\n\n/** Extract clientX/Y from any pointer-style event (Mouse, Pointer, or Touch). */\nfunction getPointerXY(e: Event): { x: number; y: number } | null {\n // PointerEvent extends MouseEvent — both are handled by this branch\n if (e instanceof MouseEvent) return { x: e.clientX, y: e.clientY };\n if (\n typeof TouchEvent !== 'undefined' &&\n e instanceof TouchEvent &&\n e.touches.length > 0\n ) {\n return { x: e.touches[0].clientX, y: e.touches[0].clientY };\n }\n return null;\n}\n\n/** Distance between two touch points. */\nfunction touchDistance(touches: TouchList): number {\n const dx = touches[0].clientX - touches[1].clientX;\n const dy = touches[0].clientY - touches[1].clientY;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n/** Midpoint of two touch points relative to a canvas element. */\nfunction touchMidpoint(touches: TouchList, el: HTMLElement): Point {\n const rect = el.getBoundingClientRect();\n return new Point(\n (touches[0].clientX + touches[1].clientX) / 2 - rect.left,\n (touches[0].clientY + touches[1].clientY) / 2 - rect.top,\n );\n}\n\n// --- Wheel zoom ---\n\nfunction setupWheelZoom(\n canvas: FabricCanvas,\n bounds: ZoomBounds,\n zoomFactor: number,\n isEnabled: () => boolean,\n) {\n const handleWheel = (opt: { e: WheelEvent }) => {\n if (!isEnabled()) return;\n\n const e = opt.e;\n e.preventDefault();\n e.stopPropagation();\n\n const delta = e.deltaY;\n let zoom = canvas.getZoom();\n zoom *= zoomFactor ** delta;\n zoom = Math.min(Math.max(zoom, bounds.minZoom), bounds.maxZoom);\n\n canvas.zoomToPoint(new Point(e.offsetX, e.offsetY), zoom);\n };\n\n canvas.on('mouse:wheel', handleWheel);\n return handleWheel;\n}\n\n// --- Mouse / single-touch pan ---\n\nfunction setupMousePan(\n canvas: FabricCanvas,\n getMode: () => ViewportMode,\n isEnabled: () => boolean,\n) {\n let isPanning = false;\n let lastPanX = 0;\n let lastPanY = 0;\n let didDisableSelection = false;\n\n const handleMouseDown = (opt: { e: TPointerEvent; target?: unknown }) => {\n if (!isEnabled()) return;\n\n const pos = getPointerXY(opt.e);\n if (!pos) return;\n\n const e = opt.e;\n const mode = getMode();\n const isMiddleButton = e instanceof MouseEvent && e.button === 1;\n const isModifiedSelect =\n e instanceof MouseEvent && mode === 'select' && (e.metaKey || e.ctrlKey);\n\n const shouldPan =\n mode === 'pan' ||\n isMiddleButton ||\n isModifiedSelect ||\n (mode === 'select' && !opt.target);\n\n if (shouldPan) {\n isPanning = true;\n lastPanX = pos.x;\n lastPanY = pos.y;\n\n if (canvas.selection) {\n didDisableSelection = true;\n canvas.selection = false;\n }\n canvas.setCursor('grab');\n }\n };\n\n const handleMouseMove = (opt: { e: TPointerEvent }) => {\n if (!isPanning) return;\n\n const pos = getPointerXY(opt.e);\n if (!pos) return;\n\n const dx = pos.x - lastPanX;\n const dy = pos.y - lastPanY;\n lastPanX = pos.x;\n lastPanY = pos.y;\n\n canvas.relativePan(new Point(dx, dy));\n canvas.setCursor('grab');\n };\n\n const handleMouseUp = () => {\n if (isPanning) {\n isPanning = false;\n\n if (didDisableSelection) {\n canvas.selection = true;\n didDisableSelection = false;\n }\n canvas.setCursor(getMode() === 'pan' ? 'grab' : 'default');\n }\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n canvas.on('mouse:up', handleMouseUp);\n\n return { handleMouseDown, handleMouseMove, handleMouseUp };\n}\n\n// --- Pinch-to-zoom ---\n\nfunction setupPinchZoom(\n canvas: FabricCanvas,\n bounds: ZoomBounds,\n isEnabled: () => boolean,\n): () => void {\n const canvasEl = canvas.getElement();\n if (typeof TouchEvent === 'undefined') return () => {};\n\n let lastPinchDist = 0;\n\n const onTouchStart = (e: TouchEvent) => {\n if (e.touches.length === 2) {\n e.preventDefault();\n lastPinchDist = touchDistance(e.touches);\n }\n };\n\n const onTouchMove = (e: TouchEvent) => {\n if (!isEnabled() || e.touches.length !== 2) return;\n e.preventDefault();\n\n const dist = touchDistance(e.touches);\n if (lastPinchDist === 0) {\n lastPinchDist = dist;\n return;\n }\n\n const ratio = dist / lastPinchDist;\n lastPinchDist = dist;\n\n const mid = touchMidpoint(e.touches, canvasEl);\n canvas.zoomToPoint(\n mid,\n Math.min(\n Math.max(canvas.getZoom() * ratio, bounds.minZoom),\n bounds.maxZoom,\n ),\n );\n };\n\n const onTouchEnd = (e: TouchEvent) => {\n if (e.touches.length < 2) lastPinchDist = 0;\n };\n\n canvasEl.addEventListener('touchstart', onTouchStart, { passive: false });\n canvasEl.addEventListener('touchmove', onTouchMove, { passive: false });\n canvasEl.addEventListener('touchend', onTouchEnd);\n\n return () => {\n canvasEl.removeEventListener('touchstart', onTouchStart);\n canvasEl.removeEventListener('touchmove', onTouchMove);\n canvasEl.removeEventListener('touchend', onTouchEnd);\n };\n}\n\n// --- Main function ---\n\n/**\n * Enable pan and zoom on the canvas viewport.\n *\n * - **Zoom**: Scroll the mouse wheel to zoom in/out centred on the cursor\n * (works in both modes). On touch devices, pinch with two fingers to zoom.\n * - **Select mode**: Normal object selection. Hold Cmd (Mac) / Ctrl (Win) and\n * drag to pan. Middle-button drag also pans.\n * - **Pan mode**: Click or single-touch drag to pan freely. Object selection\n * is disabled.\n *\n * Returns a {@link ViewportController} for switching modes, programmatic zoom,\n * and cleanup.\n */\nexport function enablePanAndZoom(\n canvas: FabricCanvas,\n options?: PanAndZoomOptions,\n): ViewportController {\n const bounds: ZoomBounds = {\n minZoom: options?.minZoom ?? DEFAULT_MIN_ZOOM,\n maxZoom: options?.maxZoom ?? DEFAULT_MAX_ZOOM,\n };\n const zoomFactor = options?.zoomFactor ?? DEFAULT_ZOOM_FACTOR;\n\n let mode: ViewportMode = options?.initialMode ?? 'select';\n let enabled = true;\n let currentAnimRafId: number | null = null;\n const isEnabled = () => enabled;\n const getMode = () => mode;\n\n function cancelAnimation() {\n if (currentAnimRafId !== null) {\n cancelAnimationFrame(currentAnimRafId);\n currentAnimRafId = null;\n }\n }\n\n const handleWheel = setupWheelZoom(canvas, bounds, zoomFactor, isEnabled);\n const panHandlers = setupMousePan(canvas, getMode, isEnabled);\n const cleanupPinch = setupPinchZoom(canvas, bounds, isEnabled);\n\n // --- Controller ---\n\n return {\n setMode(newMode: ViewportMode) {\n mode = newMode;\n if (newMode === 'pan') {\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n obj.evented = false;\n });\n canvas.discardActiveObject();\n canvas.setCursor('grab');\n } else {\n canvas.selection = true;\n canvas.forEachObject((obj) => {\n obj.selectable = true;\n obj.evented = true;\n });\n canvas.setCursor('default');\n }\n canvas.requestRenderAll();\n },\n\n getMode() {\n return mode;\n },\n\n setEnabled(value: boolean) {\n enabled = value;\n },\n\n zoomIn(factor = DEFAULT_ZOOM_STEP) {\n const z = Math.min(canvas.getZoom() * factor, bounds.maxZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n zoomOut(factor = DEFAULT_ZOOM_STEP) {\n const z = Math.max(canvas.getZoom() / factor, bounds.minZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n panToObject(object: FabricObject, panOpts?: PanToObjectOptions) {\n cancelAnimation();\n\n const zoom = canvas.getZoom();\n const objectCenter = object.getCenterPoint();\n const canvasCenterX = canvas.getWidth() / 2;\n const canvasCenterY = canvas.getHeight() / 2;\n\n const targetX = canvasCenterX - objectCenter.x * zoom;\n const targetY = canvasCenterY - objectCenter.y * zoom;\n\n if (!panOpts?.animate) {\n const viewportTransform = canvas.viewportTransform;\n if (!viewportTransform) return;\n canvas.setViewportTransform([\n viewportTransform[0],\n viewportTransform[1],\n viewportTransform[2],\n viewportTransform[3],\n targetX,\n targetY,\n ]);\n return;\n }\n\n const duration = panOpts.duration ?? 300;\n const viewportTransform = canvas.viewportTransform;\n if (!viewportTransform) return;\n const startX = viewportTransform[4];\n const startY = viewportTransform[5];\n const startTime = performance.now();\n\n function step(now: number) {\n const elapsed = now - startTime;\n const t = Math.min(elapsed / duration, 1);\n // Ease-out cubic\n const eased = 1 - Math.pow(1 - t, 3);\n\n const currentX = startX + (targetX - startX) * eased;\n const currentY = startY + (targetY - startY) * eased;\n\n const currentTransform = canvas.viewportTransform;\n if (!currentTransform) return;\n canvas.setViewportTransform([\n currentTransform[0],\n currentTransform[1],\n currentTransform[2],\n currentTransform[3],\n currentX,\n currentY,\n ]);\n\n if (t < 1) {\n currentAnimRafId = requestAnimationFrame(step);\n } else {\n currentAnimRafId = null;\n }\n }\n\n currentAnimRafId = requestAnimationFrame(step);\n },\n\n zoomToFit(object: FabricObject, fitOpts?: ZoomToFitOptions) {\n cancelAnimation();\n\n const padding = fitOpts?.padding ?? 0.1;\n const objWidth = (object.width ?? 0) * (object.scaleX ?? 1);\n const objHeight = (object.height ?? 0) * (object.scaleY ?? 1);\n if (!objWidth || !objHeight) return;\n\n const canvasWidth = canvas.getWidth();\n const canvasHeight = canvas.getHeight();\n const availableWidth = canvasWidth * (1 - padding * 2);\n const availableHeight = canvasHeight * (1 - padding * 2);\n\n const zoom = Math.min(\n Math.max(\n Math.min(availableWidth / objWidth, availableHeight / objHeight),\n bounds.minZoom,\n ),\n bounds.maxZoom,\n );\n\n const objectCenter = object.getCenterPoint();\n const offsetX = canvasWidth / 2 - objectCenter.x * zoom;\n const offsetY = canvasHeight / 2 - objectCenter.y * zoom;\n\n canvas.setViewportTransform([zoom, 0, 0, zoom, offsetX, offsetY]);\n },\n\n cleanup() {\n cancelAnimation();\n canvas.off('mouse:wheel', handleWheel);\n canvas.off('mouse:down', panHandlers.handleMouseDown);\n canvas.off('mouse:move', panHandlers.handleMouseMove);\n canvas.off('mouse:up', panHandlers.handleMouseUp);\n cleanupPinch();\n },\n };\n}\n\n/**\n * Reset the canvas viewport to the default (no pan, zoom = 1).\n */\nexport function resetViewport(canvas: FabricCanvas): void {\n canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);\n}\n","// --- Viewport ---\n\n/** Minimum zoom level. */\nexport const DEFAULT_MIN_ZOOM = 0.2;\n/** Maximum zoom level. */\nexport const DEFAULT_MAX_ZOOM = 10;\n/** Zoom sensitivity base — raised to the power of deltaY for proportional scroll zoom. */\nexport const DEFAULT_ZOOM_FACTOR = 0.999;\n/** Default multiplier for programmatic zoomIn/zoomOut (1.2 = 20% per click). */\nexport const DEFAULT_ZOOM_STEP = 1.2;\n/** Default viewport padding fraction (5% on each side). */\nexport const DEFAULT_VIEWPORT_PADDING = 0.05;\n\n// --- Alignment & snapping ---\n\n/** Reference canvas dimension for scaling snap margins proportionally. */\nexport const BASE_CANVAS_SIZE = 1000;\n/** Default snap margin in screen-pixel-equivalent units (shared by all snap systems). */\nexport const DEFAULT_SNAP_MARGIN = 6;\n/** Default angle snap interval in degrees. */\nexport const DEFAULT_ANGLE_SNAP_INTERVAL = 15;\n\n/**\n * Compute the effective snap margin in scene-space units.\n *\n * When `scaleWithCanvasSize` is true (default), the margin grows\n * proportionally with `max(canvasWidth, canvasHeight) / BASE_CANVAS_SIZE`\n * so snapping feels consistent on large canvases (e.g. floor plans).\n * The result is also divided by the current zoom level.\n */\nexport function computeSnapMargin(\n canvasWidth: number,\n canvasHeight: number,\n zoom: number,\n baseMargin: number = DEFAULT_SNAP_MARGIN,\n scaleWithCanvasSize: boolean = true,\n): number {\n const sizeScale = scaleWithCanvasSize\n ? Math.max(canvasWidth || 800, canvasHeight || 600) / BASE_CANVAS_SIZE\n : 1;\n return (baseMargin * sizeScale) / zoom;\n}\n\n// --- Interactions ---\n\n/**\n * Minimum drag distance (in scene units) before a drag is treated as intentional.\n * Prevents accidental micro-drags from creating objects.\n */\nexport const MIN_DRAG_SIZE = 3;\n\n/**\n * Distance (in scene units) from the first vertex at which a click closes the polygon.\n */\nexport const POLYGON_CLOSE_THRESHOLD = 10;\n\n// --- Background image ---\n\n/** Default max image dimension before downscale. */\nexport const DEFAULT_IMAGE_MAX_SIZE = 4096;\n/** Default min image dimension (images smaller on both axes are rejected). */\nexport const DEFAULT_IMAGE_MIN_SIZE = 480;\n\n// --- Vertex edit handles ---\n\nexport const DEFAULT_VERTEX_HANDLE_RADIUS = 6;\nexport const DEFAULT_VERTEX_HANDLE_FILL = '#ffffff';\nexport const DEFAULT_VERTEX_HANDLE_STROKE = '#2196f3';\nexport const DEFAULT_VERTEX_HANDLE_STROKE_WIDTH = 2;\n","import { type RefObject, useMemo } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject } from 'fabric';\nimport {\n resetViewport as resetViewportFn,\n type PanToObjectOptions,\n type ViewportController,\n type ZoomToFitOptions,\n} from '../viewport';\nimport { fitViewportToBackground } from '../background';\n\n/** Sync React zoom state with the canvas zoom level. */\nexport function syncZoom(\n canvasRef: RefObject<FabricCanvas | null>,\n setZoom: (z: number) => void,\n): void {\n const canvas = canvasRef.current;\n if (canvas) setZoom(canvas.getZoom());\n}\n\n/**\n * Create memoized viewport action callbacks shared between\n * useEditCanvas and useViewCanvas. The returned object is referentially\n * stable across renders.\n */\nexport function useViewportActions(\n canvasRef: RefObject<FabricCanvas | null>,\n viewportRef: RefObject<ViewportController | null>,\n setZoom: (z: number) => void,\n) {\n return useMemo(() => {\n const resetViewport = () => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n if (canvas.backgroundImage) {\n fitViewportToBackground(canvas);\n } else {\n resetViewportFn(canvas);\n }\n setZoom(canvas.getZoom());\n };\n\n const zoomIn = (step?: number) => {\n viewportRef.current?.zoomIn(step);\n syncZoom(canvasRef, setZoom);\n };\n\n const zoomOut = (step?: number) => {\n viewportRef.current?.zoomOut(step);\n syncZoom(canvasRef, setZoom);\n };\n\n const panToObject = (\n object: FabricObject,\n panOpts?: PanToObjectOptions,\n ) => {\n viewportRef.current?.panToObject(object, panOpts);\n };\n\n const zoomToFit = (object: FabricObject, fitOpts?: ZoomToFitOptions) => {\n viewportRef.current?.zoomToFit(object, fitOpts);\n syncZoom(canvasRef, setZoom);\n };\n\n return { resetViewport, zoomIn, zoomOut, panToObject, zoomToFit };\n // Refs are stable — this only runs once\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}\n\n/**\n * Resolve whether alignment should be enabled based on the master toggle\n * and the per-feature alignment prop.\n */\nexport function resolveAlignmentEnabled(\n enableAlignment: boolean | undefined,\n alignmentProp: boolean | object | undefined,\n): boolean {\n if (enableAlignment !== undefined) return enableAlignment;\n return alignmentProp !== false;\n}\n","import { Canvas as FabricCanvas, FabricImage, filters } from 'fabric';\nimport {\n DEFAULT_VIEWPORT_PADDING,\n DEFAULT_IMAGE_MAX_SIZE,\n DEFAULT_IMAGE_MIN_SIZE,\n} from './constants';\n\nfunction getBackgroundImage(canvas: FabricCanvas): FabricImage | undefined {\n return canvas.backgroundImage as FabricImage | undefined;\n}\n\n/**\n * Get the source URL of the canvas background image, or `null` if no\n * background image is set.\n */\nexport function getBackgroundSrc(canvas: FabricCanvas): string | null {\n const bg = getBackgroundImage(canvas);\n if (!bg) return null;\n return bg.getSrc() || null;\n}\n\n// --- Viewport fitting ---\n\nexport interface FitViewportOptions {\n /**\n * Fraction of the canvas to leave as empty margin around the image.\n * Default: 0.05 (5% on each side).\n */\n padding?: number;\n}\n\n/**\n * Adjust the canvas viewport transform so the background image fills the\n * canvas with an optional padding margin.\n *\n * Does nothing if no background image is set.\n */\nexport function fitViewportToBackground(\n canvas: FabricCanvas,\n options?: FitViewportOptions,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n\n // Use scaled dimensions so any scaleX/scaleY on the image is respected.\n const bgWidth = bg.getScaledWidth();\n const bgHeight = bg.getScaledHeight();\n if (!bgWidth || !bgHeight) return;\n\n // getCenterPoint() returns the geometric centre of the object in canvas space,\n // reliably regardless of how left/top are stored in this Fabric version.\n // The actual top-left corner is always centre − half-size.\n const center = bg.getCenterPoint();\n const imgLeft = center.x - bgWidth / 2;\n const imgTop = center.y - bgHeight / 2;\n\n const padding = options?.padding ?? DEFAULT_VIEWPORT_PADDING;\n const canvasWidth = canvas.getWidth();\n const canvasHeight = canvas.getHeight();\n const availableWidth = canvasWidth * (1 - padding * 2);\n const availableHeight = canvasHeight * (1 - padding * 2);\n\n const scale = Math.min(availableWidth / bgWidth, availableHeight / bgHeight);\n\n const scaledW = bgWidth * scale;\n const scaledH = bgHeight * scale;\n\n // Shift so the image's top-left maps to the padded centre of the viewport.\n const offsetX = (canvasWidth - scaledW) / 2 - imgLeft * scale;\n const offsetY = (canvasHeight - scaledH) / 2 - imgTop * scale;\n\n canvas.setViewportTransform([scale, 0, 0, scale, offsetX, offsetY]);\n canvas.requestRenderAll();\n}\n\n// --- Contrast ---\n\n/**\n * Set the contrast of the canvas background image.\n *\n * Value is expressed as a 0–2 scale:\n * - **0**: minimum contrast (flat grey).\n * - **1**: normal / unmodified (no filter applied).\n * - **2**: maximum contrast (darks are truly dark, lights truly light).\n *\n * Clamped to the 0–2 range.\n */\nexport function setBackgroundContrast(\n canvas: FabricCanvas,\n value: number,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n\n // Map 0–2 scale to Fabric Contrast range -1..+1 (0 = normal)\n const contrast = Math.min(Math.max(value, 0), 2) - 1;\n\n const currentFilters: filters.BaseFilter<string>[] = bg.filters ?? [];\n const contrastIdx = currentFilters.findIndex(\n (f) => f instanceof filters.Contrast,\n );\n\n if (contrast === 0) {\n // Normal — remove filter if present\n if (contrastIdx >= 0) {\n bg.filters = currentFilters.filter(\n (f) => !(f instanceof filters.Contrast),\n );\n bg.applyFilters();\n }\n } else if (contrastIdx >= 0) {\n (currentFilters[contrastIdx] as filters.Contrast).contrast = contrast;\n bg.applyFilters();\n } else {\n bg.filters = [...currentFilters, new filters.Contrast({ contrast })];\n bg.applyFilters();\n }\n\n canvas.requestRenderAll();\n}\n\n/**\n * Get the current contrast of the canvas background image.\n *\n * Returns a value in the 0–2 range where 1 is normal (no filter).\n * Returns 1 if no background image is set.\n */\nexport function getBackgroundContrast(canvas: FabricCanvas): number {\n const bg = getBackgroundImage(canvas);\n if (!bg) return 1;\n\n const contrastFilter = bg.filters?.find(\n (f) => f instanceof filters.Contrast,\n ) as filters.Contrast | undefined;\n\n // Map Fabric -1..+1 back to 0..2 scale\n return 1 + (contrastFilter?.contrast ?? 0);\n}\n\n// --- Invert filter ---\n\n/**\n * Add or remove the Invert filter from the canvas background image.\n */\nexport function setBackgroundInverted(\n canvas: FabricCanvas,\n inverted: boolean,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n\n const currentFilters: filters.BaseFilter<string>[] = bg.filters ?? [];\n const hasInvert = currentFilters.some((f) => f instanceof filters.Invert);\n\n if (inverted && !hasInvert) {\n bg.filters = [...currentFilters, new filters.Invert()];\n bg.applyFilters();\n } else if (!inverted && hasInvert) {\n bg.filters = currentFilters.filter((f) => !(f instanceof filters.Invert));\n bg.applyFilters();\n }\n\n canvas.requestRenderAll();\n}\n\n/**\n * Returns whether the canvas background image currently has an Invert filter.\n */\nexport function getBackgroundInverted(canvas: FabricCanvas): boolean {\n const bg = getBackgroundImage(canvas);\n if (!bg?.filters) return false;\n return bg.filters.some((f) => f instanceof filters.Invert);\n}\n\n// --- Image resizing ---\n\nexport interface ResizeResult {\n /** The (possibly new) image URL. Same as input URL if no resize was needed. */\n url: string;\n /** Final image width in pixels. */\n width: number;\n /** Final image height in pixels. */\n height: number;\n /** Whether the image was actually downscaled. */\n wasResized: boolean;\n}\n\nexport interface ResizeImageOptions {\n /**\n * Maximum dimension (width or height) in pixels.\n * Images larger than this are downscaled while maintaining aspect ratio.\n * Default: 4096.\n */\n maxSize?: number;\n /**\n * Minimum dimension (width or height) in pixels.\n * Images smaller than this are rejected with an error.\n * Default: 480.\n */\n minSize?: number;\n}\n\n/**\n * Load an image from a URL and downscale it if either dimension exceeds\n * `maxSize`. The resulting image is returned as a new blob URL (PNG).\n *\n * Rejects if the image fails to load, or if both dimensions are smaller\n * than `minSize`.\n *\n * @example\n * ```ts\n * const result = await resizeImageUrl(uploadedUrl, { maxSize: 4096 });\n * await canvas.setBackgroundImage(new FabricImage(result.url), canvas.renderAll.bind(canvas));\n * ```\n */\nexport function resizeImageUrl(\n url: string,\n options?: ResizeImageOptions,\n): Promise<ResizeResult> {\n const maxSize = options?.maxSize ?? DEFAULT_IMAGE_MAX_SIZE;\n const minSize = options?.minSize ?? DEFAULT_IMAGE_MIN_SIZE;\n\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n\n img.onload = () => {\n const { naturalWidth: w, naturalHeight: h } = img;\n\n if (w < minSize && h < minSize) {\n reject(\n new Error(\n `Image is too small (${w}×${h}). Minimum size is ${minSize}px on either dimension.`,\n ),\n );\n return;\n }\n\n const needsResize = w > maxSize || h > maxSize;\n if (!needsResize) {\n resolve({ url, width: w, height: h, wasResized: false });\n return;\n }\n\n const scale = Math.min(maxSize / w, maxSize / h);\n const newW = Math.round(w * scale);\n const newH = Math.round(h * scale);\n\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = newW;\n tempCanvas.height = newH;\n const ctx = tempCanvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Could not get 2D context for image resize.'));\n return;\n }\n ctx.drawImage(img, 0, 0, newW, newH);\n resolve({\n url: tempCanvas.toDataURL('image/png'),\n width: newW,\n height: newH,\n wasResized: true,\n });\n };\n\n img.onerror = () => reject(new Error(`Failed to load image: ${url}`));\n img.src = url;\n });\n}\n\n// --- Background image loading helper ---\n\nexport interface SetBackgroundImageOptions extends ResizeImageOptions {\n /** Preserve the current background contrast when replacing the image. */\n preserveContrast?: boolean;\n}\n\n/**\n * Set an image URL as the canvas background image.\n *\n * Pass options to control auto-resize (`maxSize`, `minSize`) and/or\n * preserve the current background contrast when replacing the image.\n * Omit to load the URL as-is without resizing.\n *\n * Returns the created FabricImage for further manipulation.\n */\nexport async function setBackgroundImage(\n canvas: FabricCanvas,\n url: string,\n options?: SetBackgroundImageOptions,\n): Promise<FabricImage> {\n const prevContrast = options?.preserveContrast\n ? getBackgroundContrast(canvas)\n : undefined;\n\n let imageUrl = url;\n const hasResizeOptions =\n options?.maxSize !== undefined || options?.minSize !== undefined;\n if (hasResizeOptions) {\n const result = await resizeImageUrl(url, options);\n imageUrl = result.url;\n }\n const img = await FabricImage.fromURL(imageUrl, { crossOrigin: 'anonymous' });\n\n // Position the image so its top-left corner sits at canvas origin (0, 0).\n // Fabric 7 defaults to center/center origin, so we set left/top to\n // half-dimensions. This ensures serializeCanvas produces left=0, top=0\n // with left/top origin, matching the old (Fabric 6) canvas data format.\n img.set({ left: img.width / 2, top: img.height / 2 });\n\n canvas.backgroundImage = img;\n\n if (prevContrast !== undefined && prevContrast !== 1) {\n setBackgroundContrast(canvas, prevContrast);\n }\n\n canvas.requestRenderAll();\n return img;\n}\n","import { type FabricObject, Point, Polygon, Rect, util } from 'fabric';\nimport { getStrokeFreeCoords } from './objectAlignmentUtils';\n\n/**\n * Extracts alignment-relevant snap points from a FabricObject.\n * All returned points must be in scene (world) coordinates.\n */\nexport type SnapPointExtractor = (object: FabricObject) => Point[];\n\ninterface SnapPointRegistration {\n matcher: (object: FabricObject) => boolean;\n extractor: SnapPointExtractor;\n}\n\nconst registry: SnapPointRegistration[] = [];\n\n/**\n * Register a snap point extractor for a shape type.\n * Matchers are checked in LIFO order (last registered wins),\n * allowing consumers to override built-in extractors.\n */\nexport function registerSnapPointExtractor(\n matcher: (object: FabricObject) => boolean,\n extractor: SnapPointExtractor,\n): void {\n registry.unshift({ matcher, extractor });\n}\n\n/**\n * Get snap target points for a FabricObject by finding the first matching\n * registered extractor. Falls back to bounding box corners + center.\n */\nexport function getSnapPoints(object: FabricObject): Point[] {\n for (const { matcher, extractor } of registry) {\n if (matcher(object)) {\n return extractor(object);\n }\n }\n return getDefaultSnapPoints(object);\n}\n\nfunction getDefaultSnapPoints(object: FabricObject): Point[] {\n const coords = getStrokeFreeCoords(object);\n return [...coords, object.getCenterPoint()];\n}\n\n// --- Built-in extractors ---\n\n// Rectangle: 4 rotation-aware corners + 4 edge midpoints + center\nregisterSnapPointExtractor(\n (obj) => obj instanceof Rect,\n (obj) => {\n const [tl, tr, br, bl] = getStrokeFreeCoords(obj);\n const mt = tl.add(tr).scalarDivide(2);\n const mr = tr.add(br).scalarDivide(2);\n const mb = br.add(bl).scalarDivide(2);\n const ml = bl.add(tl).scalarDivide(2);\n return [tl, tr, br, bl, mt, mr, mb, ml, obj.getCenterPoint()];\n },\n);\n\n// Polygon: all vertices transformed to scene space + center\nregisterSnapPointExtractor(\n (obj) => obj instanceof Polygon,\n (obj) => {\n const polygon = obj as Polygon;\n const matrix = polygon.calcTransformMatrix();\n const points: Point[] = polygon.points.map((pt) => {\n const local = new Point(\n pt.x - polygon.pathOffset.x,\n pt.y - polygon.pathOffset.y,\n );\n return util.transformPoint(local, matrix);\n });\n points.push(polygon.getCenterPoint());\n return points;\n },\n);\n","import {\n ActiveSelection,\n type BasicTransformEvent,\n type FabricObject,\n Group,\n Point,\n type TOriginX,\n type TOriginY,\n type TPointerEvent,\n} from 'fabric';\n\n/** A line segment between two alignment points, used for guideline drawing. */\nexport interface AlignmentLine {\n origin: Point;\n target: Point;\n}\n\nexport type TransformEvent = BasicTransformEvent<TPointerEvent> & {\n target: FabricObject;\n};\n\nexport type OriginMap = Record<string, [TOriginX, TOriginY]>;\n\n/**\n * Get the four bounding-box corners of a FabricObject in scene space using\n * only geometric dimensions (width × height through the transform matrix),\n * without stroke-width contribution. This ensures snap/alignment targets\n * are stable regardless of stroke scaling.\n */\nexport function getStrokeFreeCoords(\n obj: FabricObject,\n): [Point, Point, Point, Point] {\n const m = obj.calcTransformMatrix();\n const w = obj.width / 2;\n const h = obj.height / 2;\n return [\n new Point(-w, -h).transform(m), // tl\n new Point(w, -h).transform(m), // tr\n new Point(w, h).transform(m), // br\n new Point(-w, h).transform(m), // bl\n ];\n}\n\n/** Return the absolute distance between two values. */\nexport function getAbsoluteDistance(a: number, b: number): number {\n return Math.abs(a - b);\n}\n\n/**\n * Find the closest value on a given axis from a list of points.\n * Returns the minimum distance and all points at that distance.\n */\nexport function findNearestOnAxis(\n point: Point,\n list: Point[],\n axis: 'x' | 'y',\n): { distance: number; matches: Point[] } {\n let distance = Infinity;\n let matches: Point[] = [];\n for (const item of list) {\n const d = getAbsoluteDistance(point[axis], item[axis]);\n if (distance > d) {\n matches = [];\n distance = d;\n }\n if (distance === d) {\n matches.push(item);\n }\n }\n return { distance, matches };\n}\n\n/**\n * Get a map of bounding-box key points for an object:\n * corners (tl, tr, br, bl) and edge midpoints (mt, mr, mb, ml).\n */\nexport function getBoundingPointMap(\n target: FabricObject,\n): Record<string, Point> {\n const coords = getStrokeFreeCoords(target);\n return {\n tl: coords[0],\n tr: coords[1],\n br: coords[2],\n bl: coords[3],\n mt: coords[0].add(coords[1]).scalarDivide(2),\n mr: coords[1].add(coords[2]).scalarDivide(2),\n mb: coords[2].add(coords[3]).scalarDivide(2),\n ml: coords[3].add(coords[0]).scalarDivide(2),\n };\n}\n\n/**\n * Get a map of the opposite (diagonal) corners for each control point.\n * Used during scaling to anchor the opposite side.\n */\nexport function getOppositeCornerMap(\n target: FabricObject,\n): Record<string, Point> {\n const aCoords = target.aCoords ?? target.calcACoords();\n return {\n tl: aCoords.br,\n tr: aCoords.bl,\n br: aCoords.tl,\n bl: aCoords.tr,\n mt: aCoords.br.add(aCoords.bl).scalarDivide(2),\n mr: aCoords.bl.add(aCoords.tl).scalarDivide(2),\n mb: aCoords.tl.add(aCoords.tr).scalarDivide(2),\n ml: aCoords.tr.add(aCoords.br).scalarDivide(2),\n };\n}\n\n/**\n * Collect all canvas objects that should be considered as alignment targets\n * for a given moving/scaling object. Excludes the target itself and its children\n * (for groups/active selections). Flattens groups into leaf objects.\n */\nexport function getAlignmentTargets(target: FabricObject): Set<FabricObject> {\n const objects = new Set<FabricObject>();\n const canvas = target.canvas;\n if (!canvas) return objects;\n\n const children =\n target instanceof ActiveSelection ? target.getObjects() : [target];\n\n canvas.forEachObject((o) => {\n if (!o.isOnScreen() || !o.visible) return;\n if (o.constructor === Group) {\n collectGroupChildren(objects, o as Group);\n return;\n }\n objects.add(o);\n });\n\n for (const child of children) {\n if (child.constructor === Group) {\n for (const gc of (child as Group).getObjects()) objects.delete(gc);\n } else {\n objects.delete(child);\n }\n }\n\n return objects;\n}\n\n/** Recursively collect leaf objects from a group, skipping invisible children. */\nfunction collectGroupChildren(objects: Set<FabricObject>, group: Group): void {\n for (const child of group.getObjects()) {\n if (!child.visible) continue;\n if (child.constructor === Group) {\n collectGroupChildren(objects, child as Group);\n } else {\n objects.add(child);\n }\n }\n}\n","import { type Canvas, type FabricObject, Point, util } from 'fabric';\nimport { computeSnapMargin, DEFAULT_SNAP_MARGIN } from '../constants';\nimport { getSnapPoints } from './snapPoints';\nimport {\n type TransformEvent,\n type AlignmentLine,\n getAlignmentTargets,\n getBoundingPointMap,\n getOppositeCornerMap,\n} from './objectAlignmentUtils';\nimport {\n collectMovingAlignmentLines,\n collectVerticalSnapOffset,\n collectHorizontalSnapOffset,\n} from './objectAlignmentMath';\n\n// --- Alignment guideline rendering (inlined from objectAlignmentRendering) ---\n\ninterface AlignmentRenderConfig {\n canvas: Canvas;\n width: number;\n color: string;\n xSize: number;\n lineDash?: number[];\n}\n\nfunction drawXMarker(\n ctx: CanvasRenderingContext2D,\n point: Point,\n size: number,\n): void {\n ctx.save();\n ctx.translate(point.x, point.y);\n ctx.beginPath();\n ctx.moveTo(-size, -size);\n ctx.lineTo(size, size);\n ctx.moveTo(size, -size);\n ctx.lineTo(-size, size);\n ctx.stroke();\n ctx.restore();\n}\n\nfunction drawAlignmentLine(\n config: AlignmentRenderConfig,\n origin: Point,\n target: Point,\n): void {\n const ctx = config.canvas.getTopContext();\n const vt = config.canvas.viewportTransform;\n const zoom = config.canvas.getZoom();\n ctx.save();\n ctx.transform(...vt);\n ctx.lineWidth = config.width / zoom;\n if (config.lineDash) ctx.setLineDash(config.lineDash);\n ctx.strokeStyle = config.color;\n ctx.beginPath();\n ctx.moveTo(origin.x, origin.y);\n ctx.lineTo(target.x, target.y);\n ctx.stroke();\n if (config.lineDash) ctx.setLineDash([]);\n drawXMarker(ctx, origin, config.xSize / zoom);\n drawXMarker(ctx, target, config.xSize / zoom);\n ctx.restore();\n}\n\nfunction drawMarkerList(\n config: AlignmentRenderConfig,\n lines: AlignmentLine[],\n): void {\n const ctx = config.canvas.getTopContext();\n const vt = config.canvas.viewportTransform;\n const zoom = config.canvas.getZoom();\n const markerSize = config.xSize / zoom;\n ctx.save();\n ctx.transform(...vt);\n ctx.lineWidth = config.width / zoom;\n ctx.strokeStyle = config.color;\n for (const item of lines) drawXMarker(ctx, item.target, markerSize);\n ctx.restore();\n}\n\nfunction drawVerticalAlignmentLines(\n config: AlignmentRenderConfig,\n lines: Set<string>,\n): void {\n for (const v of lines) {\n const { origin, target }: AlignmentLine = JSON.parse(v);\n const from = { x: target.x, y: origin.y } as Point;\n drawAlignmentLine(config, from, target as Point);\n }\n}\n\nfunction drawHorizontalAlignmentLines(\n config: AlignmentRenderConfig,\n lines: Set<string>,\n): void {\n for (const h of lines) {\n const { origin, target }: AlignmentLine = JSON.parse(h);\n const from = { x: origin.x, y: target.y } as Point;\n drawAlignmentLine(config, from, target as Point);\n }\n}\n\n// --- Object alignment options & guides ---\n\nexport interface ObjectAlignmentOptions {\n /** At what distance from the shape does alignment begin? Default: 6. */\n margin?: number;\n /** Aligning line width. Default: 1. */\n width?: number;\n /** Aligning line color. Default: 'rgba(255,0,0,0.9)'. */\n color?: string;\n /** Endpoint marker size. Default: 2.4. */\n xSize?: number;\n /** Dashed line style. */\n lineDash?: number[];\n /**\n * Scale the snap margin proportionally with canvas size.\n * Uses `max(width, height) / 1000` as a multiplier so large canvases\n * (e.g. floor plans) get proportionally larger snap zones.\n * Default: `true`. Pass `false` to use a fixed margin regardless of size.\n */\n scaleWithCanvasSize?: boolean;\n}\n\n/** Adjust a corner string for flipped objects (e.g. 'tl' → 'tr' when flipX). */\nfunction adjustCornerForFlip(corner: string, target: FabricObject): string {\n let adjusted = corner;\n if (target.flipX) {\n if (adjusted.includes('l')) adjusted = adjusted.replace('l', 'r');\n else if (adjusted.includes('r')) adjusted = adjusted.replace('r', 'l');\n }\n if (target.flipY) {\n if (adjusted.includes('t')) adjusted = adjusted.replace('t', 'b');\n else if (adjusted.includes('b')) adjusted = adjusted.replace('b', 't');\n }\n return adjusted;\n}\n\n/**\n * Orchestrates object alignment guidelines on a Fabric canvas.\n * Listens to move/scale/resize events and delegates to math and rendering modules.\n */\nclass ObjectAlignmentGuides {\n private canvas: Canvas;\n private margin: number;\n private scaleWithCanvasSize: boolean;\n private renderConfig: AlignmentRenderConfig;\n\n private horizontalLines = new Set<string>();\n private verticalLines = new Set<string>();\n private cacheMap = new Map<string, Point[]>();\n private markersOnly = false;\n\n constructor(canvas: Canvas, opts?: ObjectAlignmentOptions) {\n this.canvas = canvas;\n this.margin = opts?.margin ?? DEFAULT_SNAP_MARGIN;\n this.scaleWithCanvasSize = opts?.scaleWithCanvasSize ?? true;\n this.renderConfig = {\n canvas,\n width: opts?.width ?? 1,\n color: opts?.color ?? 'rgba(255,0,0,0.9)',\n xSize: opts?.xSize ?? 2.4,\n lineDash: opts?.lineDash,\n };\n\n this.mouseUp = this.mouseUp.bind(this);\n this.onMoving = this.onMoving.bind(this);\n this.onScalingOrResizing = this.onScalingOrResizing.bind(this);\n this.beforeRender = this.beforeRender.bind(this);\n this.afterRender = this.afterRender.bind(this);\n\n canvas.on('mouse:up', this.mouseUp);\n canvas.on('object:moving', this.onMoving);\n canvas.on('object:scaling', this.onScalingOrResizing);\n canvas.on('object:resizing', this.onScalingOrResizing);\n canvas.on('before:render', this.beforeRender);\n canvas.on('after:render', this.afterRender);\n }\n\n dispose() {\n this.canvas.off('mouse:up', this.mouseUp);\n this.canvas.off('object:moving', this.onMoving);\n this.canvas.off('object:scaling', this.onScalingOrResizing);\n this.canvas.off('object:resizing', this.onScalingOrResizing);\n this.canvas.off('before:render', this.beforeRender);\n this.canvas.off('after:render', this.afterRender);\n }\n\n // --- Margin calculation ---\n\n private computeMargin(): number {\n return computeSnapMargin(\n this.canvas.width ?? 800,\n this.canvas.height ?? 600,\n this.canvas.getZoom(),\n this.margin,\n this.scaleWithCanvasSize,\n );\n }\n\n // --- Snap point caching ---\n\n private getCachedSnapPoints(object: FabricObject): Point[] {\n const cacheKey = [\n object.calcTransformMatrix().toString(),\n object.width,\n object.height,\n 'points' in object && Array.isArray(object.points)\n ? object.points.length\n : 0,\n ].join();\n\n const cached = this.cacheMap.get(cacheKey);\n if (cached) return cached;\n\n const value = getSnapPoints(object);\n this.cacheMap.set(cacheKey, value);\n return value;\n }\n\n private collectSnapPointsFromTargets(target: FabricObject): Point[] {\n const objects = getAlignmentTargets(target);\n const points: Point[] = [];\n for (const obj of objects) points.push(...this.getCachedSnapPoints(obj));\n return points;\n }\n\n // --- Event handlers ---\n\n private mouseUp() {\n this.verticalLines.clear();\n this.horizontalLines.clear();\n this.cacheMap.clear();\n this.canvas.requestRenderAll();\n }\n\n private onMoving(e: TransformEvent) {\n const target = e.target;\n target.setCoords();\n this.markersOnly = false;\n this.verticalLines.clear();\n this.horizontalLines.clear();\n\n const points = this.collectSnapPointsFromTargets(target);\n const margin = this.computeMargin();\n const { verticalLines, horizontalLines } = collectMovingAlignmentLines(\n target,\n points,\n margin,\n );\n for (const l of verticalLines) this.verticalLines.add(JSON.stringify(l));\n for (const l of horizontalLines)\n this.horizontalLines.add(JSON.stringify(l));\n }\n\n private onScalingOrResizing(e: TransformEvent) {\n const target = e.target;\n target.setCoords();\n const isScale = String(e.transform.action).startsWith('scale');\n this.verticalLines.clear();\n this.horizontalLines.clear();\n\n const corner = adjustCornerForFlip(e.transform.corner, target);\n\n const pointMap = getBoundingPointMap(target);\n if (!(corner in pointMap)) return;\n\n this.markersOnly = corner.includes('m');\n if (this.markersOnly) {\n if (target.getTotalAngle() % 90 !== 0) return;\n }\n\n const oppositeMap = getOppositeCornerMap(target);\n const point = pointMap[corner];\n let diagonalPoint = oppositeMap[corner];\n\n const isCenter =\n e.transform.original.originX === 'center' &&\n e.transform.original.originY === 'center';\n if (isCenter) {\n const p = target.group\n ? point.transform(\n util.invertTransform(target.group.calcTransformMatrix()),\n )\n : point;\n diagonalPoint = diagonalPoint.add(p).scalarDivide(2);\n }\n\n const uniformIsToggled = e.e[this.canvas.uniScaleKey!];\n let isUniform =\n (this.canvas.uniformScaling && !uniformIsToggled) ||\n (!this.canvas.uniformScaling && uniformIsToggled);\n if (this.markersOnly) isUniform = false;\n\n const allPoints = this.collectSnapPointsFromTargets(target);\n const margin = this.computeMargin();\n\n const props = {\n target,\n point,\n diagonalPoint,\n corner,\n list: allPoints,\n isScale,\n isUniform,\n isCenter,\n margin,\n };\n\n const skipVertical =\n this.markersOnly && (corner.includes('t') || corner.includes('b'));\n const skipHorizontal =\n this.markersOnly && (corner.includes('l') || corner.includes('r'));\n\n const vList = skipVertical ? [] : collectVerticalSnapOffset(props);\n // Vertical snap may have mutated scaleX (and scaleY for uniform scaling),\n // shifting the active corner's Y position. Refresh point so horizontal snap\n // computes its offset against the post-snap corner location, not the stale one.\n if (vList.length > 0) {\n const updatedPointMap = getBoundingPointMap(target);\n if (corner in updatedPointMap) props.point = updatedPointMap[corner];\n }\n const hList = skipHorizontal ? [] : collectHorizontalSnapOffset(props);\n\n for (const l of vList) this.verticalLines.add(JSON.stringify(l));\n for (const l of hList) this.horizontalLines.add(JSON.stringify(l));\n }\n\n // --- Render ---\n\n private beforeRender() {\n this.canvas.clearContext(this.canvas.contextTop);\n }\n\n private afterRender() {\n if (this.markersOnly) {\n const lines: AlignmentLine[] = [];\n for (const v of this.verticalLines)\n lines.push(JSON.parse(v) as AlignmentLine);\n for (const h of this.horizontalLines)\n lines.push(JSON.parse(h) as AlignmentLine);\n drawMarkerList(this.renderConfig, lines);\n } else {\n drawVerticalAlignmentLines(this.renderConfig, this.verticalLines);\n drawHorizontalAlignmentLines(this.renderConfig, this.horizontalLines);\n }\n }\n}\n\n/**\n * Enable object alignment guidelines on a canvas.\n * Draws visual guidelines and snaps objects during movement and scaling.\n * Returns a dispose function for cleanup.\n */\nexport function enableObjectAlignment(\n canvas: Canvas,\n options?: ObjectAlignmentOptions,\n): () => void {\n const alignment = new ObjectAlignmentGuides(canvas, options);\n return () => alignment.dispose();\n}\n","import { type FabricObject, Point } from 'fabric';\nimport {\n type AlignmentLine,\n type OriginMap,\n findNearestOnAxis,\n getStrokeFreeCoords,\n} from './objectAlignmentUtils';\n\n/** Maps each corner/midpoint to the opposite origin for anchoring during scale. */\nexport const OPPOSITE_ORIGIN_MAP: OriginMap = {\n tl: ['right', 'bottom'],\n tr: ['left', 'bottom'],\n br: ['left', 'top'],\n bl: ['right', 'top'],\n mt: ['center', 'bottom'],\n mr: ['left', 'center'],\n mb: ['center', 'top'],\n ml: ['right', 'center'],\n};\n\n// --- Moving alignment ---\n\n/**\n * Collect alignment lines when an object is being moved.\n * Checks the target's corners + center against all snap points.\n */\nexport function collectMovingAlignmentLines(\n target: FabricObject,\n points: Point[],\n margin: number,\n): { verticalLines: AlignmentLine[]; horizontalLines: AlignmentLine[] } {\n const list: Point[] = [...getStrokeFreeCoords(target)];\n list.push(target.getCenterPoint());\n const opts = { target, list, points, margin };\n const verticalLines = collectMovingAxisMatches({ ...opts, axis: 'x' });\n const horizontalLines = collectMovingAxisMatches({ ...opts, axis: 'y' });\n return { verticalLines, horizontalLines };\n}\n\n/**\n * For a single axis, find the closest matching snap points for the target's\n * bounding points, snap the object to align, and return the alignment lines.\n */\nfunction collectMovingAxisMatches(props: {\n target: FabricObject;\n list: Point[];\n points: Point[];\n margin: number;\n axis: 'x' | 'y';\n}): AlignmentLine[] {\n const { target, list, points, margin, axis } = props;\n const result: AlignmentLine[] = [];\n const distances: ReturnType<typeof findNearestOnAxis>[] = [];\n let min = Infinity;\n\n for (const item of list) {\n const nearest = findNearestOnAxis(item, points, axis);\n distances.push(nearest);\n if (min > nearest.distance) min = nearest.distance;\n }\n if (min > margin) return result;\n\n let snapped = false;\n for (let i = 0; i < list.length; i++) {\n if (distances[i].distance !== min) continue;\n for (const item of distances[i].matches) {\n result.push({ origin: list[i], target: item });\n }\n\n if (snapped) continue;\n snapped = true;\n const snapOffset = distances[i].matches[0][axis] - list[i][axis];\n list.forEach((item) => {\n item[axis] += snapOffset;\n });\n // Use center-based positioning to avoid stroke/origin offset issues.\n // The center is stroke-independent, so shifting it by snapOffset\n // moves the geometric edges by exactly that amount.\n const center = target.getCenterPoint();\n center[axis] += snapOffset;\n target.setXY(center, 'center', 'center');\n target.setCoords();\n }\n\n return result;\n}\n\n// --- Scaling alignment ---\n\ninterface ScalingAlignmentProps {\n target: FabricObject;\n point: Point;\n diagonalPoint: Point;\n list: Point[];\n isScale: boolean;\n isUniform: boolean;\n isCenter: boolean;\n corner: string;\n margin: number;\n}\n\n/**\n * Collect vertical (X-axis) snap offset during scaling/resizing.\n * Adjusts the target's scaleX (or width) to align with the nearest snap point.\n */\nexport function collectVerticalSnapOffset(\n props: ScalingAlignmentProps,\n): AlignmentLine[] {\n const {\n target,\n isScale,\n isUniform,\n corner,\n point,\n diagonalPoint,\n list,\n isCenter,\n margin,\n } = props;\n const { distance, matches } = findNearestOnAxis(point, list, 'x');\n if (distance > margin) return [];\n\n let snapOffset = matches[matches.length - 1].x - point.x;\n const dirX = corner.includes('l') ? -1 : 1;\n snapOffset *= dirX;\n\n const { width, height, scaleX, scaleY } = target;\n const scaleWidth = scaleX * width;\n const sx = (snapOffset + scaleWidth) / scaleWidth;\n if (sx === 0) return [];\n\n if (isScale) {\n target.set('scaleX', scaleX * sx);\n if (isUniform) target.set('scaleY', scaleY * sx);\n } else {\n target.set('width', width * sx);\n if (isUniform) target.set('height', height * sx);\n }\n\n if (isCenter) {\n target.setRelativeXY(diagonalPoint, 'center', 'center');\n } else {\n target.setRelativeXY(diagonalPoint, ...OPPOSITE_ORIGIN_MAP[corner]);\n }\n target.setCoords();\n\n return matches.map((t) => ({ origin: point, target: t }));\n}\n\n/**\n * Collect horizontal (Y-axis) snap offset during scaling/resizing.\n * Adjusts the target's scaleY (or height) to align with the nearest snap point.\n */\nexport function collectHorizontalSnapOffset(\n props: ScalingAlignmentProps,\n): AlignmentLine[] {\n const {\n target,\n isScale,\n isUniform,\n corner,\n point,\n diagonalPoint,\n list,\n isCenter,\n margin,\n } = props;\n const { distance, matches } = findNearestOnAxis(point, list, 'y');\n if (distance > margin) return [];\n\n let snapOffset = matches[matches.length - 1].y - point.y;\n const dirY = corner.includes('t') ? -1 : 1;\n snapOffset *= dirY;\n\n const { width, height, scaleX, scaleY } = target;\n const scaleHeight = scaleY * height;\n const sy = (snapOffset + scaleHeight) / scaleHeight;\n if (sy === 0) return [];\n\n if (isScale) {\n target.set('scaleY', scaleY * sy);\n if (isUniform) target.set('scaleX', scaleX * sy);\n } else {\n target.set('height', height * sy);\n if (isUniform) target.set('width', width * sy);\n }\n\n if (isCenter) {\n target.setRelativeXY(diagonalPoint, 'center', 'center');\n } else {\n target.setRelativeXY(diagonalPoint, ...OPPOSITE_ORIGIN_MAP[corner]);\n }\n target.setCoords();\n\n return matches.map((t) => ({ origin: point, target: t }));\n}\n","import type { Canvas } from 'fabric';\nimport type { TransformEvent } from './objectAlignmentUtils';\nimport { DEFAULT_ANGLE_SNAP_INTERVAL } from '../constants';\n\nexport interface RotationSnapOptions {\n /**\n * Snap angle to multiples of this interval (degrees) when Shift is held.\n * Default: 15.\n */\n interval?: number;\n}\n\nfunction snapToInterval(angle: number, interval: number): number {\n return Math.round(angle / interval) * interval;\n}\n\n/**\n * Enable rotation snapping on a Fabric canvas.\n * When Shift is held while rotating an object via its rotation handle,\n * the angle snaps to the nearest multiple of `interval` degrees (default: 15°).\n * Returns a cleanup function.\n */\nexport function enableRotationSnap(\n canvas: Canvas,\n options?: RotationSnapOptions,\n): () => void {\n const interval = options?.interval ?? DEFAULT_ANGLE_SNAP_INTERVAL;\n\n const onRotating = (e: TransformEvent) => {\n if (!('shiftKey' in e.e) || !e.e.shiftKey) return;\n e.target.angle = snapToInterval(e.target.angle as number, interval);\n };\n\n canvas.on('object:rotating', onRotating);\n return () => canvas.off('object:rotating', onRotating);\n}\n","import { Canvas as FabricCanvas, type FabricObject, Point } from 'fabric';\nimport { getSnapPoints } from './snapPoints';\nimport { computeSnapMargin, DEFAULT_SNAP_MARGIN } from '../constants';\n\nexport interface CursorSnapResult {\n /** The snapped point (or original if no snap occurred). */\n point: Point;\n /** Whether a snap occurred on either axis. */\n snapped: boolean;\n /** Whether the X coordinate was snapped. */\n snapX: boolean;\n /** Whether the Y coordinate was snapped. */\n snapY: boolean;\n /** All target points that triggered the X-axis snap (for drawing guidelines). */\n alignTargetsX?: Point[];\n /** All target points that triggered the Y-axis snap (for drawing guidelines). */\n alignTargetsY?: Point[];\n}\n\nexport interface CursorSnapOptions {\n /**\n * Base snap margin in screen-pixel-equivalent units. Default: 6.\n * The effective scene-space margin is `margin * sizeScale / zoom`, where\n * `sizeScale` grows proportionally with canvas size so that snapping feels\n * consistent regardless of how large the canvas coordinate space is.\n */\n margin?: number;\n /** Objects to exclude from snap targets (e.g., preview elements). */\n exclude?: Set<FabricObject>;\n /** Pre-computed target points. If provided, skips object iteration. */\n targetPoints?: Point[];\n /**\n * Scale the snap margin proportionally with canvas size.\n * Uses `max(width, height) / 1000` as a multiplier so large canvases\n * (e.g. floor plans) get proportionally larger snap zones.\n * Default: `true`. Pass `false` to use a fixed margin regardless of size.\n */\n scaleWithCanvasSize?: boolean;\n}\n\nexport interface GuidelineStyle {\n color?: string;\n width?: number;\n xSize?: number;\n}\n\n/**\n * Compute a snapped cursor position given a raw scene-space point.\n * Finds the closest X and closest Y alignment independently,\n * snapping each axis if within margin.\n */\nexport function snapCursorPoint(\n canvas: FabricCanvas,\n rawPoint: Point,\n options?: CursorSnapOptions,\n): CursorSnapResult {\n const margin = computeSnapMargin(\n canvas.width ?? 800,\n canvas.height ?? 600,\n canvas.getZoom(),\n options?.margin ?? DEFAULT_SNAP_MARGIN,\n options?.scaleWithCanvasSize !== false,\n );\n const exclude = options?.exclude ?? new Set();\n\n let targetPoints: Point[];\n if (options?.targetPoints) {\n targetPoints = options.targetPoints;\n } else {\n targetPoints = [];\n canvas.forEachObject((obj) => {\n if (!obj.visible || !obj.isOnScreen()) return;\n if (exclude.has(obj)) return;\n targetPoints.push(...getSnapPoints(obj));\n });\n }\n\n let bestDx = Infinity;\n let bestDy = Infinity;\n let snapTargetsX: Point[] = [];\n let snapTargetsY: Point[] = [];\n\n for (const tp of targetPoints) {\n const dx = Math.abs(rawPoint.x - tp.x);\n const dy = Math.abs(rawPoint.y - tp.y);\n if (dx < bestDx) {\n bestDx = dx;\n snapTargetsX = [];\n }\n if (dx === bestDx) {\n snapTargetsX.push(tp);\n }\n if (dy < bestDy) {\n bestDy = dy;\n snapTargetsY = [];\n }\n if (dy === bestDy) {\n snapTargetsY.push(tp);\n }\n }\n\n const snapX = bestDx <= margin && snapTargetsX.length > 0;\n const snapY = bestDy <= margin && snapTargetsY.length > 0;\n\n return {\n point: new Point(\n snapX ? snapTargetsX[0].x : rawPoint.x,\n snapY ? snapTargetsY[0].y : rawPoint.y,\n ),\n snapped: snapX || snapY,\n snapX,\n snapY,\n alignTargetsX: snapX ? snapTargetsX : undefined,\n alignTargetsY: snapY ? snapTargetsY : undefined,\n };\n}\n\n/**\n * Draw alignment guidelines on the canvas top context based on a snap result.\n * Renders lines from the snapped point to the alignment target points,\n * with X markers at both endpoints.\n */\nexport function drawCursorGuidelines(\n canvas: FabricCanvas,\n snapResult: CursorSnapResult,\n style?: GuidelineStyle,\n): void {\n if (!snapResult.snapped) return;\n\n const ctx = canvas.getTopContext();\n const vt = canvas.viewportTransform;\n const zoom = canvas.getZoom();\n const color = style?.color ?? 'rgba(255,0,0,0.9)';\n const width = style?.width ?? 1;\n const xSize = (style?.xSize ?? 2.4) / zoom;\n\n ctx.save();\n ctx.transform(vt[0], vt[1], vt[2], vt[3], vt[4], vt[5]);\n ctx.lineWidth = width / zoom;\n ctx.strokeStyle = color;\n\n if (snapResult.snapX && snapResult.alignTargetsX) {\n const to = snapResult.point;\n for (const from of snapResult.alignTargetsX) {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n drawXMarker(ctx, from, xSize);\n }\n drawXMarker(ctx, new Point(to.x, to.y), xSize);\n }\n\n if (snapResult.snapY && snapResult.alignTargetsY) {\n const to = snapResult.point;\n for (const from of snapResult.alignTargetsY) {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n drawXMarker(ctx, from, xSize);\n }\n drawXMarker(ctx, new Point(to.x, to.y), xSize);\n }\n\n ctx.restore();\n}\n\nfunction drawXMarker(\n ctx: CanvasRenderingContext2D,\n point: Point,\n size: number,\n): void {\n ctx.beginPath();\n ctx.moveTo(point.x - size, point.y - size);\n ctx.lineTo(point.x + size, point.y + size);\n ctx.moveTo(point.x + size, point.y - size);\n ctx.lineTo(point.x - size, point.y + size);\n ctx.stroke();\n}\n\n/** Clear the canvas top context to remove previous guidelines. */\nexport function clearCursorGuidelines(canvas: FabricCanvas): void {\n canvas.clearContext(canvas.getTopContext());\n}\n","import type { ViewportController } from '../viewport';\n\n/** Restore the viewport to select mode if a controller was provided. */\nexport function restoreViewport(viewport?: ViewportController) {\n if (!viewport) return;\n viewport.setEnabled(true);\n viewport.setMode('select');\n}\n\n/**\n * Track whether the Shift key is currently held.\n * Optionally calls `onChange` whenever the state changes (useful for\n * triggering preview updates while a drag is in progress).\n */\nexport function createShiftKeyTracker(onChange?: (held: boolean) => void): {\n readonly held: boolean;\n cleanup: () => void;\n} {\n let shiftHeld = false;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Shift' && !shiftHeld) {\n shiftHeld = true;\n onChange?.(true);\n }\n };\n const onKeyUp = (e: KeyboardEvent) => {\n if (e.key === 'Shift' && shiftHeld) {\n shiftHeld = false;\n onChange?.(false);\n }\n };\n document.addEventListener('keydown', onKeyDown);\n document.addEventListener('keyup', onKeyUp);\n\n return {\n get held() {\n return shiftHeld;\n },\n cleanup() {\n document.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('keyup', onKeyUp);\n },\n };\n}\n","import { Canvas as FabricCanvas, FabricObject } from 'fabric';\nimport type { Point2D, InteractionModeOptions } from '../types';\nimport { restoreViewport } from './shared';\n\n/**\n * Enable click-to-create mode.\n * Each click calls the factory with the canvas and the click point.\n * The factory creates and adds the object to the canvas, returning it.\n * Returns a cleanup function that disables the mode.\n */\nexport function enableClickToCreate(\n canvas: FabricCanvas,\n factory: (canvas: FabricCanvas, point: Point2D) => FabricObject,\n options?: InteractionModeOptions,\n): () => void {\n options?.viewport?.setEnabled(false);\n\n const handleMouseDown = (event: { scenePoint: Point2D }) => {\n const obj = factory(canvas, event.scenePoint);\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n };\n\n canvas.on('mouse:down', handleMouseDown);\n\n return () => {\n canvas.off('mouse:down', handleMouseDown);\n restoreViewport(options?.viewport);\n };\n}\n","import { Canvas as FabricCanvas, FabricObject, Rect } from 'fabric';\nimport type {\n DragBounds,\n Point2D,\n ShapeStyleOptions,\n SnappableInteractionOptions,\n} from '../types';\nimport { DEFAULT_GUIDELINE_SHAPE_STYLE } from '../styles';\nimport { MIN_DRAG_SIZE } from '../constants';\nimport { restoreViewport, createShiftKeyTracker } from './shared';\nimport { createInteractionSnapping } from './interactionSnapping';\n\nexport interface DragToCreateOptions extends SnappableInteractionOptions {\n /** Style applied to the preview rectangle shown during drag. */\n previewStyle?: ShapeStyleOptions & { rx?: number; ry?: number };\n /** When true, constrain the drag to a 1:1 aspect ratio (square). */\n constrainToSquare?: boolean;\n /**\n * Factory called when the user clicks without dragging (below MIN_DRAG_SIZE).\n * Receives the canvas and the click point. If not provided, clicks are ignored.\n */\n clickFactory?: (canvas: FabricCanvas, point: Point2D) => FabricObject;\n /** Called when the user cancels the drag via Escape. */\n onCancel?: () => void;\n}\n\n/**\n * Enable drag-to-create mode.\n * A preview rectangle is shown while dragging. On mouse-up the factory\n * receives the drag bounds and creates the final object.\n * Holding Shift during drag constrains the shape to a 1:1 aspect ratio.\n * Returns a cleanup function that disables the mode.\n */\nexport function enableDragToCreate(\n canvas: FabricCanvas,\n factory: (canvas: FabricCanvas, bounds: DragBounds) => FabricObject,\n options?: DragToCreateOptions,\n): () => void {\n let isDrawing = false;\n let startX = 0;\n let startY = 0;\n let lastEndX = 0;\n let lastEndY = 0;\n let previewRect: Rect | null = null;\n let previousSelection: boolean;\n\n const snapping = createInteractionSnapping(canvas, options);\n const shiftTracker = createShiftKeyTracker(() => {\n updatePreview(lastEndX, lastEndY);\n });\n\n options?.viewport?.setEnabled(false);\n\n const shouldConstrain = () =>\n !!(options?.constrainToSquare || shiftTracker.held);\n\n const computeDimensions = (\n endX: number,\n endY: number,\n ): { width: number; height: number } => {\n let width = Math.max(0, endX - startX);\n let height = Math.max(0, endY - startY);\n if (shouldConstrain()) {\n const size = Math.max(width, height);\n width = size;\n height = size;\n }\n return { width, height };\n };\n\n const updatePreview = (endX: number, endY: number) => {\n lastEndX = endX;\n lastEndY = endY;\n if (!isDrawing || !previewRect) return;\n\n const { width, height } = computeDimensions(endX, endY);\n previewRect.set({\n left: startX + width / 2,\n top: startY + height / 2,\n width,\n height,\n });\n previewRect.setCoords();\n canvas.requestRenderAll();\n };\n\n const handleMouseDown = (event: { scenePoint: Point2D }) => {\n isDrawing = true;\n\n const snapped = snapping.snap(event.scenePoint.x, event.scenePoint.y);\n startX = snapped.x;\n startY = snapped.y;\n lastEndX = startX;\n lastEndY = startY;\n\n previousSelection = canvas.selection;\n canvas.selection = false;\n\n previewRect = new Rect({\n ...DEFAULT_GUIDELINE_SHAPE_STYLE,\n left: startX,\n top: startY,\n width: 0,\n height: 0,\n ...options?.previewStyle,\n selectable: false,\n evented: false,\n });\n snapping.excludeSet.add(previewRect);\n canvas.add(previewRect);\n };\n\n const handleMouseMove = (event: { scenePoint: Point2D }) => {\n if (!isDrawing || !previewRect) return;\n\n const { x: endX, y: endY } = snapping.snapWithGuidelines(\n event.scenePoint.x,\n event.scenePoint.y,\n );\n updatePreview(endX, endY);\n };\n\n const handleMouseUp = () => {\n if (!isDrawing || !previewRect) return;\n\n isDrawing = false;\n snapping.clearSnapResult();\n canvas.selection = previousSelection;\n\n const { width, height } = computeDimensions(lastEndX, lastEndY);\n\n snapping.excludeSet.delete(previewRect);\n canvas.remove(previewRect);\n\n if (width < MIN_DRAG_SIZE && height < MIN_DRAG_SIZE) {\n canvas.requestRenderAll();\n previewRect = null;\n\n if (options?.clickFactory) {\n const obj = options.clickFactory(canvas, { x: startX, y: startY });\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n }\n return;\n }\n\n const obj = factory(canvas, { startX, startY, width, height });\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n previewRect = null;\n };\n\n // Cancel drag on Escape (capture phase to prevent other handlers)\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopImmediatePropagation();\n e.preventDefault();\n cleanup('cancel');\n }\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n canvas.on('mouse:up', handleMouseUp);\n document.addEventListener('keydown', handleKeyDown, true);\n\n let exited = false;\n\n function cleanup(reason?: 'cancel') {\n if (exited) return;\n exited = true;\n\n canvas.off('mouse:down', handleMouseDown);\n canvas.off('mouse:move', handleMouseMove);\n canvas.off('mouse:up', handleMouseUp);\n document.removeEventListener('keydown', handleKeyDown, true);\n shiftTracker.cleanup();\n\n snapping.cleanup();\n\n if (isDrawing && previewRect) {\n snapping.excludeSet.delete(previewRect);\n canvas.remove(previewRect);\n canvas.selection = previousSelection;\n canvas.requestRenderAll();\n }\n restoreViewport(options?.viewport);\n\n if (reason === 'cancel') {\n options?.onCancel?.();\n }\n }\n\n return () => cleanup();\n}\n","import { alpha } from '@mui/material/styles';\nimport { biampTheme } from '@bwp-web/styles';\n\nconst { palette } = biampTheme();\n\n/** Selection handle appearance (border, corner color). Applied to all objects. */\nexport const DEFAULT_CONTROL_STYLE = {\n borderColor: palette.info.main,\n cornerColor: palette.info.main,\n cornerStrokeColor: palette.info.main,\n transparentCorners: true,\n};\n\n/** Default fill/stroke for rectangles and polygons. */\nexport const DEFAULT_SHAPE_STYLE = {\n fill: alpha(palette.info.main, 0.3),\n stroke: palette.info.main,\n strokeWidth: 2.5,\n strokeUniform: true,\n ...DEFAULT_CONTROL_STYLE,\n};\n\n/** Default fill for circles (Rects with full border-radius). */\nexport const DEFAULT_CIRCLE_STYLE = {\n fill: palette.info.main,\n stroke: palette.info.main,\n strokeWidth: 2.5,\n strokeUniform: true,\n ...DEFAULT_CONTROL_STYLE,\n};\n\n/** Preview rectangle shown during drag-to-create (dashed stroke). */\nexport const DEFAULT_DRAG_SHAPE_STYLE = {\n fill: alpha(palette.info.main, 0.1),\n stroke: palette.info.main,\n strokeWidth: 2.5,\n strokeUniform: true,\n strokeDashArray: [5, 5],\n};\n\n/** Preview polygon shown during draw-to-create (semi-transparent dashed stroke). */\nexport const DEFAULT_GUIDELINE_SHAPE_STYLE = {\n fill: alpha(palette.info.main, 0.1),\n stroke: alpha(palette.info.main, 0.5),\n strokeWidth: 2.5,\n strokeUniform: true,\n strokeDashArray: [5, 5],\n};\n\n/** Alignment guideline appearance (color, width, x-marker size). */\nexport const DEFAULT_ALIGNMENT_STYLE = {\n color: 'rgba(255, 0, 0, 0.9)',\n width: 1,\n xSize: 2.4,\n} as const;\n","import { Canvas as FabricCanvas, FabricObject, Point } from 'fabric';\nimport type { SnappableInteractionOptions } from '../types';\nimport {\n snapCursorPoint,\n getSnapPoints,\n drawCursorGuidelines,\n type CursorSnapResult,\n type GuidelineStyle,\n} from '../alignment';\n\n/**\n * Canvas-level alignment state.\n * Allows `useEditCanvas` to set the master alignment toggle once so that all\n * interaction modes created via `setMode` inherit it automatically.\n */\nconst canvasAlignmentState = new WeakMap<FabricCanvas, boolean | undefined>();\n\n/** Set the canvas-level alignment toggle (called by useEditCanvas). */\nexport function setCanvasAlignmentEnabled(\n canvas: FabricCanvas,\n enabled?: boolean,\n): void {\n canvasAlignmentState.set(canvas, enabled);\n}\n\n/** Context object for managing snapping within an interaction mode. */\nexport interface InteractionSnappingContext {\n /** Whether snapping is enabled. */\n enabled: boolean;\n /** Snap a raw point, returning the snapped coordinates. */\n snap(rawX: number, rawY: number): { x: number; y: number };\n /** Snap a raw point and store the result for guideline rendering on next frame. */\n snapWithGuidelines(rawX: number, rawY: number): { x: number; y: number };\n /** Clear the stored snap result (call when finalizing or on mouse-up). */\n clearSnapResult(): void;\n /** Objects to exclude from snap targets (e.g. preview elements). */\n excludeSet: Set<FabricObject>;\n /** Remove all event listeners and clear state. */\n cleanup(): void;\n}\n\n/**\n * Create a snapping context for an interaction mode.\n * Handles target point caching, snap calculations, and guideline rendering.\n *\n * @param getAdditionalTargets Optional callback that returns extra snap targets\n * (e.g. placed polygon vertices for self-alignment in drawToCreate).\n */\nexport function createInteractionSnapping(\n canvas: FabricCanvas,\n options?: Pick<SnappableInteractionOptions, 'snapping' | 'enableAlignment'>,\n getAdditionalTargets?: () => Point[],\n): InteractionSnappingContext {\n const canvasAlignment = canvasAlignmentState.get(canvas);\n const snapEnabled =\n options?.enableAlignment !== undefined\n ? options.enableAlignment\n : canvasAlignment !== undefined\n ? canvasAlignment\n : options?.snapping !== false;\n\n const snapMargin =\n typeof options?.snapping === 'object' ? options.snapping.margin : undefined;\n const guidelineStyle: GuidelineStyle | undefined =\n typeof options?.snapping === 'object'\n ? options.snapping.guidelineStyle\n : undefined;\n\n const excludeSet = new Set<FabricObject>();\n let cachedTargetPoints: Point[] | null = null;\n let lastSnapResult: CursorSnapResult | null = null;\n\n function getTargetPoints(): Point[] {\n if (cachedTargetPoints) return cachedTargetPoints;\n cachedTargetPoints = [];\n canvas.forEachObject((obj) => {\n if (!obj.visible) return;\n if (excludeSet.has(obj)) return;\n cachedTargetPoints!.push(...getSnapPoints(obj));\n });\n return cachedTargetPoints;\n }\n\n function getAllTargetPoints(): Point[] {\n const base = getTargetPoints();\n if (!getAdditionalTargets) return base;\n const additional = getAdditionalTargets();\n return additional.length > 0 ? [...base, ...additional] : base;\n }\n\n const invalidateCache = () => {\n cachedTargetPoints = null;\n };\n\n const beforeRender = () => {\n canvas.clearContext(canvas.getTopContext());\n };\n\n const afterRender = () => {\n if (lastSnapResult) {\n drawCursorGuidelines(canvas, lastSnapResult, guidelineStyle);\n }\n };\n\n if (snapEnabled) {\n canvas.on('object:added', invalidateCache);\n canvas.on('object:removed', invalidateCache);\n canvas.on('before:render', beforeRender);\n canvas.on('after:render', afterRender);\n }\n\n function snap(rawX: number, rawY: number): { x: number; y: number } {\n if (!snapEnabled) return { x: rawX, y: rawY };\n\n const result = snapCursorPoint(canvas, new Point(rawX, rawY), {\n margin: snapMargin,\n exclude: excludeSet,\n targetPoints: getAllTargetPoints(),\n });\n return { x: result.point.x, y: result.point.y };\n }\n\n function snapWithGuidelines(\n rawX: number,\n rawY: number,\n ): { x: number; y: number } {\n if (!snapEnabled) {\n lastSnapResult = null;\n return { x: rawX, y: rawY };\n }\n\n lastSnapResult = snapCursorPoint(canvas, new Point(rawX, rawY), {\n margin: snapMargin,\n exclude: excludeSet,\n targetPoints: getAllTargetPoints(),\n });\n return { x: lastSnapResult.point.x, y: lastSnapResult.point.y };\n }\n\n return {\n enabled: snapEnabled,\n snap,\n snapWithGuidelines,\n clearSnapResult() {\n lastSnapResult = null;\n },\n excludeSet,\n cleanup() {\n if (snapEnabled) {\n canvas.off('object:added', invalidateCache);\n canvas.off('object:removed', invalidateCache);\n canvas.off('before:render', beforeRender);\n canvas.off('after:render', afterRender);\n // Clear any lingering guideline drawings since the before:render\n // handler that would normally clear them has been removed.\n canvas.clearContext(canvas.getTopContext());\n }\n lastSnapResult = null;\n },\n };\n}\n","import {\n Canvas as FabricCanvas,\n Circle,\n FabricObject,\n Line,\n Point,\n} from 'fabric';\nimport type {\n Point2D,\n ShapeStyleOptions,\n SnappableInteractionOptions,\n} from '../types';\nimport { createPolygonFromVertices } from '../shapes';\nimport {\n DEFAULT_DRAG_SHAPE_STYLE,\n DEFAULT_GUIDELINE_SHAPE_STYLE,\n DEFAULT_SHAPE_STYLE,\n} from '../styles';\nimport {\n POLYGON_CLOSE_THRESHOLD,\n DEFAULT_ANGLE_SNAP_INTERVAL,\n} from '../constants';\nimport { restoreViewport, createShiftKeyTracker } from './shared';\nimport { createInteractionSnapping } from './interactionSnapping';\n\nexport interface DrawToCreateOptions extends SnappableInteractionOptions {\n /** Style applied to the polygon being drawn. */\n style?: ShapeStyleOptions;\n /**\n * Metadata to attach to the created polygon. If provided, this is set on\n * the polygon's `data` property after creation. Takes precedence over\n * `style.data` if both are specified.\n */\n data?: FabricObject['data'];\n /**\n * Factory function to create the final object from placed vertices.\n * Receives the canvas and the array of placed points.\n * Default: creates a polygon via `createPolygonFromVertices`.\n */\n factory?: (canvas: FabricCanvas, points: Point2D[]) => FabricObject;\n /**\n * Snap vertex positions to multiples of `interval` degrees when Shift is\n * held while placing a vertex. The angle is measured relative to the\n * previous vertex.\n *\n * Pass `false` to disable. Default: enabled at 15° intervals.\n */\n angleSnap?: boolean | { interval?: number };\n /** Called when the user cancels drawing via Escape or Backspace. */\n onCancel?: () => void;\n}\n\nfunction snapAngleToInterval(\n point: { x: number; y: number },\n ref: { x: number; y: number },\n intervalDeg: number,\n): { x: number; y: number } {\n const dx = point.x - ref.x;\n const dy = point.y - ref.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist === 0) return point;\n const radInterval = (intervalDeg * Math.PI) / 180;\n const snappedAngle =\n Math.round(Math.atan2(dy, dx) / radInterval) * radInterval;\n return {\n x: ref.x + Math.cos(snappedAngle) * dist,\n y: ref.y + Math.sin(snappedAngle) * dist,\n };\n}\n\n/**\n * Apply cursor snapping while maintaining an angle constraint.\n * For each axis that cursor-snapped, compute the intersection of the angle\n * ray with that axis value (preserving both angle and axis alignment).\n * Pick the candidate closest to the original position and re-snap there\n * so guidelines render from the correct on-ray point.\n */\nfunction snapAlongRay(\n angleSnapped: { x: number; y: number },\n ref: { x: number; y: number },\n doSnap: (x: number, y: number) => { x: number; y: number },\n): { x: number; y: number } {\n const cursorSnapped = doSnap(angleSnapped.x, angleSnapped.y);\n\n const snappedX = cursorSnapped.x !== angleSnapped.x;\n const snappedY = cursorSnapped.y !== angleSnapped.y;\n\n if (!snappedX && !snappedY) return angleSnapped;\n\n const rayDx = angleSnapped.x - ref.x;\n const rayDy = angleSnapped.y - ref.y;\n\n const candidates: Array<{ x: number; y: number; dist: number }> = [];\n\n // X-snap: find where the ray crosses x = cursorSnapped.x\n if (snappedX && Math.abs(rayDx) > 1e-9) {\n const t = (cursorSnapped.x - ref.x) / rayDx;\n const onRayY = ref.y + t * rayDy;\n candidates.push({\n x: cursorSnapped.x,\n y: onRayY,\n dist: Math.abs(onRayY - angleSnapped.y),\n });\n }\n\n // Y-snap: find where the ray crosses y = cursorSnapped.y\n if (snappedY && Math.abs(rayDy) > 1e-9) {\n const t = (cursorSnapped.y - ref.y) / rayDy;\n const onRayX = ref.x + t * rayDx;\n candidates.push({\n x: onRayX,\n y: cursorSnapped.y,\n dist: Math.abs(onRayX - angleSnapped.x),\n });\n }\n\n if (candidates.length === 0) return angleSnapped;\n\n // Pick candidate closest to the original angle-snapped position\n candidates.sort((a, b) => a.dist - b.dist);\n const best = candidates[0];\n\n // Re-snap at the on-ray position so guidelines render correctly\n doSnap(best.x, best.y);\n\n return best;\n}\n\n/**\n * Enable draw mode for polygons.\n * Click to place vertices one by one. A preview shows edges and a tracking line\n * to the cursor. Click near the first vertex (within threshold) to close the\n * polygon once at least 3 points have been placed.\n * Returns a cleanup function that disables the mode.\n */\nexport function enableDrawToCreate(\n canvas: FabricCanvas,\n options?: DrawToCreateOptions,\n): () => void {\n let exited = false;\n\n const angleSnapEnabled = options?.angleSnap !== false;\n const angleInterval =\n typeof options?.angleSnap === 'object'\n ? (options.angleSnap.interval ?? DEFAULT_ANGLE_SNAP_INTERVAL)\n : DEFAULT_ANGLE_SNAP_INTERVAL;\n\n const shiftTracker = createShiftKeyTracker();\n\n const points: Point2D[] = [];\n const markers: Circle[] = [];\n const edgeLines: Line[] = [];\n let trackingLine: Line | null = null;\n let closingLine: Line | null = null;\n let previousSelection: boolean;\n\n // Use shared snapping context, with placed vertices as additional snap targets\n const snapping = createInteractionSnapping(canvas, options, () =>\n points.map((p) => new Point(p.x, p.y)),\n );\n\n /** Track a preview element in the snapping exclude set. */\n function trackPreviewElement(obj: FabricObject) {\n snapping.excludeSet.add(obj);\n }\n function untrackPreviewElement(obj: FabricObject) {\n snapping.excludeSet.delete(obj);\n }\n\n options?.viewport?.setEnabled(false);\n\n const lineStyle = {\n stroke: options?.style?.stroke ?? DEFAULT_DRAG_SHAPE_STYLE.stroke,\n strokeWidth:\n options?.style?.strokeWidth ?? DEFAULT_DRAG_SHAPE_STYLE.strokeWidth,\n strokeUniform: true,\n selectable: false,\n evented: false,\n };\n\n const guideLineStyle = {\n stroke: options?.style?.stroke ?? DEFAULT_GUIDELINE_SHAPE_STYLE.stroke,\n strokeWidth:\n options?.style?.strokeWidth ?? DEFAULT_GUIDELINE_SHAPE_STYLE.strokeWidth,\n strokeUniform: true,\n selectable: false,\n evented: false,\n } as const;\n\n const removePreviewElements = () => {\n for (const marker of markers) {\n canvas.remove(marker);\n untrackPreviewElement(marker);\n }\n markers.length = 0;\n\n for (const line of edgeLines) {\n canvas.remove(line);\n untrackPreviewElement(line);\n }\n edgeLines.length = 0;\n\n if (trackingLine) {\n canvas.remove(trackingLine);\n untrackPreviewElement(trackingLine);\n trackingLine = null;\n }\n if (closingLine) {\n canvas.remove(closingLine);\n untrackPreviewElement(closingLine);\n closingLine = null;\n }\n };\n\n const finalize = () => {\n removePreviewElements();\n snapping.clearSnapResult();\n\n const obj = options?.factory\n ? options.factory(canvas, [...points])\n : createPolygonFromVertices(canvas, points, options?.style);\n if (options?.data) {\n obj.data = options.data;\n }\n canvas.selection = previousSelection;\n canvas.requestRenderAll();\n\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n points.length = 0;\n };\n\n const handleMouseDown = (event: { scenePoint: Point2D }) => {\n let { x, y } = event.scenePoint;\n if (angleSnapEnabled && shiftTracker.held && points.length > 0) {\n const ref = points[points.length - 1];\n const angleSnapped = snapAngleToInterval({ x, y }, ref, angleInterval);\n ({ x, y } = snapAlongRay(angleSnapped, ref, (sx, sy) =>\n snapping.snap(sx, sy),\n ));\n } else {\n ({ x, y } = snapping.snap(x, y));\n }\n snapping.clearSnapResult();\n\n // Close the polygon if clicking near the first vertex with 3+ points\n if (points.length >= 3) {\n const dx = x - points[0].x;\n const dy = y - points[0].y;\n if (Math.sqrt(dx * dx + dy * dy) <= POLYGON_CLOSE_THRESHOLD) {\n finalize();\n return;\n }\n }\n\n // Disable selection on first point\n if (points.length === 0) {\n previousSelection = canvas.selection;\n canvas.selection = false;\n }\n\n points.push({ x, y });\n\n // Add vertex marker\n const marker = new Circle({\n left: x,\n top: y,\n radius: 4,\n fill: DEFAULT_SHAPE_STYLE.stroke,\n stroke: DEFAULT_SHAPE_STYLE.stroke,\n strokeWidth: 1,\n strokeUniform: true,\n selectable: false,\n evented: false,\n });\n markers.push(marker);\n trackPreviewElement(marker);\n canvas.add(marker);\n\n // Add edge line from previous vertex to this one\n if (points.length >= 2) {\n const prev = points[points.length - 2];\n const edge = new Line([prev.x, prev.y, x, y], lineStyle);\n edgeLines.push(edge);\n trackPreviewElement(edge);\n canvas.add(edge);\n }\n\n canvas.requestRenderAll();\n };\n\n const handleMouseMove = (event: { scenePoint: Point2D }) => {\n if (points.length === 0) return;\n\n const lastPoint = points[points.length - 1];\n let { x, y } = event.scenePoint;\n if (angleSnapEnabled && shiftTracker.held) {\n const angleSnapped = snapAngleToInterval(\n { x, y },\n lastPoint,\n angleInterval,\n );\n ({ x, y } = snapAlongRay(angleSnapped, lastPoint, (sx, sy) =>\n snapping.snapWithGuidelines(sx, sy),\n ));\n } else {\n ({ x, y } = snapping.snapWithGuidelines(x, y));\n }\n\n // Update tracking line from last vertex to cursor\n if (trackingLine) {\n untrackPreviewElement(trackingLine);\n canvas.remove(trackingLine);\n }\n trackingLine = new Line([lastPoint.x, lastPoint.y, x, y], {\n ...guideLineStyle,\n strokeDashArray: [5, 5],\n });\n trackPreviewElement(trackingLine);\n canvas.add(trackingLine);\n\n // Show closing line from cursor to first vertex when 3+ points\n if (closingLine) {\n untrackPreviewElement(closingLine);\n canvas.remove(closingLine);\n closingLine = null;\n }\n if (points.length >= 3) {\n closingLine = new Line([x, y, points[0].x, points[0].y], {\n ...guideLineStyle,\n strokeDashArray: [5, 5],\n });\n trackPreviewElement(closingLine);\n canvas.add(closingLine);\n }\n\n canvas.requestRenderAll();\n };\n\n // Cancel drawing on Escape or Backspace (capture phase to prevent other handlers)\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' || e.key === 'Backspace') {\n e.stopImmediatePropagation();\n e.preventDefault();\n cleanup('cancel');\n }\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n document.addEventListener('keydown', handleKeyDown, true);\n\n function cleanup(reason?: 'cancel') {\n if (exited) return;\n exited = true;\n\n canvas.off('mouse:down', handleMouseDown);\n canvas.off('mouse:move', handleMouseMove);\n document.removeEventListener('keydown', handleKeyDown, true);\n shiftTracker.cleanup();\n\n snapping.cleanup();\n removePreviewElements();\n\n if (points.length > 0) {\n canvas.selection = previousSelection;\n }\n points.length = 0;\n canvas.requestRenderAll();\n restoreViewport(options?.viewport);\n\n if (reason === 'cancel') {\n options?.onCancel?.();\n }\n }\n\n return () => cleanup();\n}\n","import { Canvas as FabricCanvas, Rect } from 'fabric';\nimport type { Point2D, ShapeStyleOptions } from '../types';\nimport { DEFAULT_SHAPE_STYLE } from '../styles';\n\nexport interface RectangleOptions extends ShapeStyleOptions {\n left: number;\n top: number;\n width: number;\n height: number;\n rx?: number;\n ry?: number;\n}\n\nexport interface RectangleAtPointOptions extends ShapeStyleOptions {\n width: number;\n height: number;\n rx?: number;\n ry?: number;\n}\n\n/**\n * Create a rectangle and add it to the canvas.\n * Returns the fabric Rect instance for further manipulation.\n */\nexport function createRectangle(\n canvas: FabricCanvas,\n options: RectangleOptions,\n): Rect {\n const rect = new Rect({ ...DEFAULT_SHAPE_STYLE, ...options });\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Create a rectangle at the given scene point and add it to the canvas.\n * Returns the fabric Rect instance.\n */\nexport function createRectangleAtPoint(\n canvas: FabricCanvas,\n point: Point2D,\n options: RectangleAtPointOptions,\n): Rect {\n const { width, height, ...style } = options;\n const rect = new Rect({\n ...DEFAULT_SHAPE_STYLE,\n left: point.x,\n top: point.y,\n width,\n height,\n ...style,\n });\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Edit an existing rectangle's properties (position, dimensions, appearance, etc.).\n */\nexport function editRectangle(\n canvas: FabricCanvas,\n rect: Rect,\n changes: Partial<RectangleOptions>,\n): void {\n rect.set(changes);\n rect.setCoords();\n canvas.requestRenderAll();\n}\n","import { Canvas as FabricCanvas, Rect } from 'fabric';\nimport type { Point2D, ShapeStyleOptions } from '../types';\nimport { DEFAULT_CIRCLE_STYLE } from '../styles';\n\nexport interface CircleOptions extends ShapeStyleOptions {\n left: number;\n top: number;\n size: number;\n}\n\nexport interface CircleAtPointOptions extends ShapeStyleOptions {\n size: number;\n}\n\n/** Constraints applied to every circle: uniform-only scaling, no rotation. */\nconst CIRCLE_CONSTRAINTS = {\n lockRotation: true,\n lockUniScaling: true,\n} as const;\n\n/** Controls hidden on circles: mid-edge handles (non-uniform) and rotation. */\nconst HIDDEN_CIRCLE_CONTROLS = {\n mt: false,\n mb: false,\n ml: false,\n mr: false,\n mtr: false,\n} as const;\n\n/** Apply circle tag and interaction constraints to a Rect. */\nfunction applyCircleConstraints(rect: Rect): void {\n rect.shapeType = 'circle';\n rect.setControlsVisibility(HIDDEN_CIRCLE_CONTROLS);\n}\n\n/**\n * Re-apply circle control visibility after deserialization.\n *\n * Fabric's `loadFromJSON` does not restore `setControlsVisibility` state, so\n * this must be called on any deserialized object whose `shapeType === 'circle'`\n * to restore the correct handle layout (no mid-edge or rotation handles).\n *\n * Not part of the public package API — imported directly by `serialization.ts`.\n */\nexport function restoreCircleConstraints(rect: Rect): void {\n rect.set(CIRCLE_CONSTRAINTS);\n rect.setControlsVisibility(HIDDEN_CIRCLE_CONTROLS);\n}\n\n/**\n * Create a circle (a square Rect with full border-radius) and add it to the canvas.\n * Returns the fabric Rect instance for further manipulation.\n */\nexport function createCircle(\n canvas: FabricCanvas,\n options: CircleOptions,\n): Rect {\n const { size, ...rest } = options;\n const rect = new Rect({\n ...DEFAULT_CIRCLE_STYLE,\n ...CIRCLE_CONSTRAINTS,\n width: size,\n height: size,\n rx: size / 2,\n ry: size / 2,\n ...rest,\n });\n applyCircleConstraints(rect);\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Create a circle at the given scene point and add it to the canvas.\n * Returns the fabric Rect instance.\n */\nexport function createCircleAtPoint(\n canvas: FabricCanvas,\n point: Point2D,\n options: CircleAtPointOptions,\n): Rect {\n const { size, ...style } = options;\n const rect = new Rect({\n ...DEFAULT_CIRCLE_STYLE,\n ...CIRCLE_CONSTRAINTS,\n left: point.x,\n top: point.y,\n width: size,\n height: size,\n rx: size / 2,\n ry: size / 2,\n ...style,\n });\n applyCircleConstraints(rect);\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Edit an existing circle's properties (position, size, appearance).\n * When size changes, rx and ry are automatically updated to maintain the circular shape.\n */\nexport function editCircle(\n canvas: FabricCanvas,\n rect: Rect,\n changes: Partial<CircleOptions>,\n): void {\n const { size, ...rest } = changes;\n if (size !== undefined) {\n rect.set({\n width: size,\n height: size,\n rx: size / 2,\n ry: size / 2,\n ...rest,\n });\n } else {\n rect.set(rest);\n }\n rect.setCoords();\n canvas.requestRenderAll();\n}\n","import { Canvas as FabricCanvas, Polygon } from 'fabric';\nimport type { Point2D, ShapeStyleOptions } from '../types';\nimport { DEFAULT_SHAPE_STYLE } from '../styles';\n\nexport interface PolygonOptions extends ShapeStyleOptions {\n points: Point2D[];\n left?: number;\n top?: number;\n}\n\n/** @deprecated Use `ShapeStyleOptions` directly. This alias will be removed in a future major version. */\nexport type PolygonStyleOptions = ShapeStyleOptions;\n\n/**\n * Create a polygon and add it to the canvas.\n * Returns the fabric Polygon instance for further manipulation.\n */\nexport function createPolygon(\n canvas: FabricCanvas,\n options: PolygonOptions,\n): Polygon {\n const { points, ...rest } = options;\n const polygon = new Polygon(points, { ...DEFAULT_SHAPE_STYLE, ...rest });\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Create a rectangular polygon at the given scene point and add it to the canvas.\n * Returns the fabric Polygon instance.\n */\nexport function createPolygonAtPoint(\n canvas: FabricCanvas,\n point: Point2D,\n options: PolygonStyleOptions & { width: number; height: number },\n): Polygon {\n const { width, height, ...style } = options;\n const polygon = new Polygon(\n [\n { x: 0, y: 0 },\n { x: width, y: 0 },\n { x: width, y: height },\n { x: 0, y: height },\n ],\n { ...DEFAULT_SHAPE_STYLE, left: point.x, top: point.y, ...style },\n );\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Create a rectangular polygon from drag bounds (two corner points) and add it to the canvas.\n * Returns the fabric Polygon instance.\n */\nexport function createPolygonFromDrag(\n canvas: FabricCanvas,\n start: Point2D,\n end: Point2D,\n options?: ShapeStyleOptions,\n): Polygon {\n const width = Math.abs(end.x - start.x);\n const height = Math.abs(end.y - start.y);\n const left = Math.min(start.x, end.x) + width / 2;\n const top = Math.min(start.y, end.y) + height / 2;\n const polygon = new Polygon(\n [\n { x: 0, y: 0 },\n { x: width, y: 0 },\n { x: width, y: height },\n { x: 0, y: height },\n ],\n { ...DEFAULT_SHAPE_STYLE, left, top, ...options },\n );\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Create a polygon from an arbitrary array of vertices and add it to the canvas.\n * Returns the fabric Polygon instance.\n */\nexport function createPolygonFromVertices(\n canvas: FabricCanvas,\n points: Point2D[],\n options?: ShapeStyleOptions,\n): Polygon {\n const polygon = new Polygon(\n points.map((p) => ({ x: p.x, y: p.y })),\n { ...DEFAULT_SHAPE_STYLE, ...options },\n );\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Edit an existing polygon's properties (position, points, appearance, etc.).\n */\nexport function editPolygon(\n canvas: FabricCanvas,\n polygon: Polygon,\n changes: Partial<PolygonOptions>,\n): void {\n const { points, ...rest } = changes;\n if (points) {\n polygon.points = points;\n polygon.setDimensions();\n }\n polygon.set(rest);\n polygon.setCoords();\n canvas.requestRenderAll();\n}\n","import {\n Canvas as FabricCanvas,\n FabricObject,\n Point,\n Polygon,\n util,\n} from 'fabric';\nimport type { Point2D } from '../types';\nimport {\n DEFAULT_VERTEX_HANDLE_RADIUS,\n DEFAULT_VERTEX_HANDLE_FILL,\n DEFAULT_VERTEX_HANDLE_STROKE,\n DEFAULT_VERTEX_HANDLE_STROKE_WIDTH,\n} from '../constants';\nimport { createInteractionSnapping } from './interactionSnapping';\n\nexport interface VertexEditOptions {\n handleRadius?: number;\n handleFill?: string;\n handleStroke?: string;\n handleStrokeWidth?: number;\n /** Called when vertex editing is exited (via Escape, empty-canvas click, or cleanup). */\n onExit?: () => void;\n}\n\n// --- Coordinate helpers ---\n\nfunction localPointToScene(polygon: Polygon, point: Point2D): Point {\n const matrix = polygon.calcTransformMatrix();\n const localPoint = new Point(\n point.x - polygon.pathOffset.x,\n point.y - polygon.pathOffset.y,\n );\n return util.transformPoint(localPoint, matrix);\n}\n\nfunction scenePointToLocal(polygon: Polygon, scenePoint: Point): Point2D {\n const matrix = polygon.calcTransformMatrix();\n const invMatrix = util.invertTransform(matrix);\n const localPoint = util.transformPoint(scenePoint, invMatrix);\n return {\n x: localPoint.x + polygon.pathOffset.x,\n y: localPoint.y + polygon.pathOffset.y,\n };\n}\n\n// --- DOM handle helpers ---\n\nfunction sceneToScreen(scenePoint: Point, canvas: FabricCanvas): Point {\n return util.transformPoint(scenePoint, canvas.viewportTransform!);\n}\n\nfunction screenToScene(\n screenX: number,\n screenY: number,\n canvas: FabricCanvas,\n): Point {\n const inv = util.invertTransform(canvas.viewportTransform!);\n return util.transformPoint(new Point(screenX, screenY), inv);\n}\n\nfunction createHandleElement(\n radius: number,\n fill: string,\n stroke: string,\n strokeWidth: number,\n): HTMLDivElement {\n const el = document.createElement('div');\n const size = radius * 2;\n el.style.cssText = [\n 'position: absolute',\n `width: ${size}px`,\n `height: ${size}px`,\n `margin-left: ${-radius}px`,\n `margin-top: ${-radius}px`,\n 'border-radius: 50%',\n `background: ${fill}`,\n `border: ${strokeWidth}px solid ${stroke}`,\n 'box-sizing: border-box',\n 'pointer-events: auto',\n 'cursor: grab',\n 'touch-action: none',\n ].join('; ');\n return el;\n}\n\nfunction positionHandle(\n handle: HTMLDivElement,\n scenePoint: Point,\n canvas: FabricCanvas,\n): void {\n const screen = sceneToScreen(scenePoint, canvas);\n handle.style.left = `${screen.x}px`;\n handle.style.top = `${screen.y}px`;\n}\n\n// --- Main function ---\n\n/**\n * Enable vertex editing on a polygon.\n * Creates draggable DOM circle handles at each vertex. Dragging a handle\n * updates the polygon's shape in real-time with cursor snapping support.\n *\n * Exit by pressing Escape or clicking on empty canvas.\n *\n * Returns a cleanup function that removes handles and exits edit mode.\n */\nexport function enableVertexEdit(\n canvas: FabricCanvas,\n polygon: Polygon,\n options?: VertexEditOptions,\n /** @deprecated Pass `onExit` in options instead. */\n onExit?: () => void,\n): () => void {\n let exited = false;\n let draggingIndex: number | null = null;\n\n const handleRadius = options?.handleRadius ?? DEFAULT_VERTEX_HANDLE_RADIUS;\n const handleFill = options?.handleFill ?? DEFAULT_VERTEX_HANDLE_FILL;\n const handleStroke = options?.handleStroke ?? DEFAULT_VERTEX_HANDLE_STROKE;\n const handleStrokeWidth =\n options?.handleStrokeWidth ?? DEFAULT_VERTEX_HANDLE_STROKE_WIDTH;\n\n // Save previous state so we can restore on cleanup\n const previousState = {\n selectable: polygon.selectable,\n evented: polygon.evented,\n hasControls: polygon.hasControls,\n canvasSelection: canvas.selection,\n };\n\n const prevObjectStates = new Map<\n FabricObject,\n { selectable: boolean; evented: boolean }\n >();\n\n // Disable polygon interaction and canvas selection\n polygon.selectable = false;\n polygon.evented = false;\n polygon.hasControls = false;\n canvas.selection = false;\n canvas.discardActiveObject();\n\n // Make all other objects non-interactive\n canvas.forEachObject((obj) => {\n if (obj !== polygon) {\n prevObjectStates.set(obj, {\n selectable: obj.selectable,\n evented: obj.evented,\n });\n obj.selectable = false;\n obj.evented = false;\n }\n });\n\n // Set up snapping — provides snap-to-object-points + guideline rendering.\n // Additional targets: the other vertices of this polygon (for self-alignment).\n const snapping = createInteractionSnapping(canvas, undefined, () => {\n if (draggingIndex === null) return [];\n return polygon.points\n .filter((_, i) => i !== draggingIndex)\n .map((pt) => localPointToScene(polygon, pt));\n });\n snapping.excludeSet.add(polygon);\n\n // Create handle container overlay\n const container = document.createElement('div');\n container.style.cssText =\n 'position: absolute; inset: 0; pointer-events: none; overflow: hidden;';\n canvas.wrapperEl.appendChild(container);\n\n // Create DOM handles at each vertex\n const handles: HTMLDivElement[] = [];\n const points = polygon.points;\n\n for (let i = 0; i < points.length; i++) {\n const handle = createHandleElement(\n handleRadius,\n handleFill,\n handleStroke,\n handleStrokeWidth,\n );\n positionHandle(handle, localPointToScene(polygon, points[i]), canvas);\n container.appendChild(handle);\n handles.push(handle);\n\n // --- Pointer event handlers for dragging ---\n handle.addEventListener('pointerdown', (e: PointerEvent) => {\n if (exited) return;\n e.preventDefault();\n e.stopPropagation();\n draggingIndex = i;\n handle.setPointerCapture(e.pointerId);\n handle.style.cursor = 'grabbing';\n });\n\n handle.addEventListener('pointermove', (e: PointerEvent) => {\n if (draggingIndex !== i) return;\n\n // Convert pointer position to canvas-relative coordinates\n const wrapperRect = canvas.wrapperEl.getBoundingClientRect();\n const canvasRelX = e.clientX - wrapperRect.left;\n const canvasRelY = e.clientY - wrapperRect.top;\n\n // Convert to scene space\n const rawScene = screenToScene(canvasRelX, canvasRelY, canvas);\n\n // Apply snapping\n const snapped = snapping.snapWithGuidelines(rawScene.x, rawScene.y);\n const scenePoint = new Point(snapped.x, snapped.y);\n\n // Use a non-dragged vertex as an anchor to measure visual drift\n const anchorIdx = i === 0 ? 1 : 0;\n const anchorBefore = localPointToScene(\n polygon,\n polygon.points[anchorIdx],\n );\n\n const localPoint = scenePointToLocal(polygon, scenePoint);\n polygon.points[i] = localPoint;\n polygon.setDimensions();\n\n // setDimensions recalculates pathOffset, shifting all vertices visually.\n // Compensate by adjusting polygon position so the anchor stays in place.\n const anchorAfter = localPointToScene(polygon, polygon.points[anchorIdx]);\n polygon.left += anchorBefore.x - anchorAfter.x;\n polygon.top += anchorBefore.y - anchorAfter.y;\n polygon.dirty = true;\n polygon.setCoords();\n\n repositionAllHandles();\n canvas.requestRenderAll();\n });\n\n handle.addEventListener('pointerup', (e: PointerEvent) => {\n if (draggingIndex !== i) return;\n handle.releasePointerCapture(e.pointerId);\n draggingIndex = null;\n handle.style.cursor = 'grab';\n snapping.clearSnapResult();\n canvas.requestRenderAll();\n });\n }\n\n // Reposition all handles based on current polygon state\n function repositionAllHandles() {\n const pts = polygon.points;\n for (let j = 0; j < pts.length; j++) {\n positionHandle(handles[j], localPointToScene(polygon, pts[j]), canvas);\n }\n }\n\n // Reposition handles when viewport changes (zoom/pan)\n const afterRender = () => {\n if (draggingIndex === null) {\n repositionAllHandles();\n }\n };\n canvas.on('after:render', afterRender);\n\n // Exit on Escape (capture phase to prevent deletion shortcut)\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopImmediatePropagation();\n cleanup();\n }\n };\n document.addEventListener('keydown', handleKeyDown, true);\n\n // Exit on clicking empty canvas (clicks on DOM handles don't reach fabric)\n const handleMouseDown = () => {\n cleanup();\n };\n canvas.on('mouse:down', handleMouseDown);\n\n function cleanup() {\n if (exited) return;\n exited = true;\n\n snapping.cleanup();\n canvas.off('after:render', afterRender);\n canvas.off('mouse:down', handleMouseDown);\n document.removeEventListener('keydown', handleKeyDown, true);\n\n container.remove();\n\n polygon.selectable = previousState.selectable;\n polygon.evented = previousState.evented;\n polygon.hasControls = previousState.hasControls;\n canvas.selection = previousState.canvasSelection;\n\n prevObjectStates.forEach((state, obj) => {\n obj.selectable = state.selectable;\n obj.evented = state.evented;\n });\n\n canvas.discardActiveObject();\n canvas.requestRenderAll();\n\n (options?.onExit ?? onExit)?.();\n }\n\n return cleanup;\n}\n","import {\n Canvas as FabricCanvas,\n FabricImage,\n Rect,\n type FabricObject,\n type TOriginX,\n type TOriginY,\n} from 'fabric';\nimport { getBackgroundContrast, getBackgroundInverted } from './background';\nimport { restoreCircleConstraints } from './shapes/circle';\nimport { DEFAULT_CONTROL_STYLE } from './styles';\nimport type { CanvasJSON } from './types';\n\n/**\n * Module-level map from FabricObject to its \"base\" (unscaled) stroke width.\n * Shared between enableScaledStrokes and serializeCanvas so they stay in sync\n * without leaking mutable state outside the module.\n */\nconst strokeBaseMap = new WeakMap<FabricObject, number>();\n\n/**\n * Module-level map from Rect to its original rx/ry values before the\n * view border radius was applied. Used by serializeCanvas to strip\n * the visual-only border radius before saving.\n */\nconst borderRadiusBaseMap = new WeakMap<Rect, { rx: number; ry: number }>();\n\nconst DEFAULT_VIEW_BORDER_RADIUS = 4;\n\n/**\n * Enable zoom-independent stroke widths on a canvas.\n *\n * On every render, each object's `strokeWidth` is set to `base / zoom` so\n * strokes always appear as one visual pixel regardless of zoom level. The\n * original (base) stroke width is stored in an internal WeakMap and is\n * automatically restored before serialization by {@link serializeCanvas}.\n *\n * Returns a cleanup function that removes the listener and restores all\n * stroke widths to their base values.\n */\nexport function enableScaledStrokes(canvas: FabricCanvas): () => void {\n function applyScaledStrokes() {\n const zoom = canvas.getZoom();\n canvas.forEachObject((obj) => {\n if (!obj.strokeWidth && obj.strokeWidth !== 0) return;\n if (!strokeBaseMap.has(obj)) {\n // Record the base value on first encounter\n strokeBaseMap.set(obj, obj.strokeWidth ?? 0);\n }\n const base = strokeBaseMap.get(obj)!;\n if (base === 0) return;\n obj.strokeWidth = base / zoom;\n });\n }\n\n canvas.on('before:render', applyScaledStrokes);\n\n return () => {\n canvas.off('before:render', applyScaledStrokes);\n // Restore all objects to their base stroke widths on cleanup\n canvas.forEachObject((obj) => {\n const base = strokeBaseMap.get(obj);\n if (base !== undefined) {\n obj.strokeWidth = base;\n }\n });\n };\n}\n\n/**\n * Keep border radii visually constant on Rects loaded via {@link loadCanvas}.\n *\n * On every render, each tracked Rect's `rx`/`ry` is recalculated as\n * `VIEW_BORDER_RADIUS / scale` so corners always appear circular regardless\n * of non-uniform scaling. The original rx/ry values are stored in an internal\n * WeakMap and automatically restored before serialization by\n * {@link serializeCanvas}.\n *\n * Returns a cleanup function that removes the listener and restores all\n * rx/ry values to their originals.\n */\nexport interface ScaledBorderRadiusOptions {\n /** Visual border radius in pixels. Default: 4. */\n radius?: number;\n}\n\nexport function enableScaledBorderRadius(\n canvas: FabricCanvas,\n options?: ScaledBorderRadiusOptions,\n): () => void {\n const radius = options?.radius ?? DEFAULT_VIEW_BORDER_RADIUS;\n\n function applyScaledBorderRadius() {\n canvas.forEachObject((obj) => {\n if (!(obj instanceof Rect)) return;\n if (!borderRadiusBaseMap.has(obj)) return;\n const rx = radius / (obj.scaleX ?? 1);\n const ry = radius / (obj.scaleY ?? 1);\n obj.set({ rx, ry });\n });\n }\n\n canvas.on('before:render', applyScaledBorderRadius);\n\n return () => {\n canvas.off('before:render', applyScaledBorderRadius);\n canvas.forEachObject((obj) => {\n if (!(obj instanceof Rect)) return;\n const base = borderRadiusBaseMap.get(obj);\n if (base !== undefined) {\n obj.set({ rx: base.rx, ry: base.ry });\n }\n });\n };\n}\n\n/**\n * Options for {@link serializeCanvas}.\n */\nexport interface SerializeOptions {\n /**\n * Additional Fabric object properties to include in the JSON output.\n * The `'data'` and `'shapeType'` properties are always included.\n * Default: `['data', 'shapeType']`.\n */\n properties?: string[];\n}\n\n/**\n * Return the base (unscaled) stroke width for an object.\n *\n * When {@link enableScaledStrokes} is active, `obj.strokeWidth` is a\n * zoom-adjusted value. This function returns the original intended width that\n * was recorded before any zoom adjustment was applied. Falls back to the\n * object's current `strokeWidth` if the object has never been seen by\n * `enableScaledStrokes` (e.g. if the feature is disabled).\n */\nexport function getBaseStrokeWidth(obj: FabricObject): number {\n return strokeBaseMap.get(obj) ?? obj.strokeWidth ?? 0;\n}\n\n// --- Serialization helpers ---\n// Each helper temporarily mutates canvas/object state for serialization\n// and returns a function that restores the original runtime state.\n\ntype SavedOrigin = {\n originX: TOriginX;\n originY: TOriginY;\n left: number;\n top: number;\n};\n\n/** Strip zoom-scaled stroke widths, restoring base values for serialization. */\nfunction prepareStrokeWidths(canvas: FabricCanvas): () => void {\n const scaledWidths = new Map<FabricObject, number>();\n canvas.forEachObject((obj) => {\n const base = strokeBaseMap.get(obj);\n if (base !== undefined && obj.strokeWidth !== base) {\n scaledWidths.set(obj, obj.strokeWidth ?? 0);\n obj.strokeWidth = base;\n }\n });\n return () =>\n scaledWidths.forEach((scaled, obj) => {\n obj.strokeWidth = scaled;\n });\n}\n\n/** Strip visual-only border radii, restoring original rx/ry for serialization. */\nfunction prepareBorderRadii(canvas: FabricCanvas): () => void {\n const appliedRadii = new Map<Rect, { rx: number; ry: number }>();\n canvas.forEachObject((obj) => {\n if (!(obj instanceof Rect)) return;\n const base = borderRadiusBaseMap.get(obj);\n if (base !== undefined) {\n appliedRadii.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });\n obj.set({ rx: base.rx, ry: base.ry });\n }\n });\n return () =>\n appliedRadii.forEach((radii, obj) => {\n obj.set(radii);\n });\n}\n\n/** Convert objects from center/center to left/top origin for Fabric 6 compatibility. */\nfunction prepareObjectOrigins(canvas: FabricCanvas): () => void {\n const savedOrigins = new Map<FabricObject, SavedOrigin>();\n canvas.forEachObject((obj) => {\n if (obj.originX === 'left' && obj.originY === 'top') return;\n savedOrigins.set(obj, {\n originX: obj.originX,\n originY: obj.originY,\n left: obj.left ?? 0,\n top: obj.top ?? 0,\n });\n const leftTop = obj.getPositionByOrigin('left', 'top');\n obj.set({\n originX: 'left',\n originY: 'top',\n left: leftTop.x,\n top: leftTop.y,\n });\n });\n return () =>\n savedOrigins.forEach((saved, obj) => {\n obj.set(saved);\n });\n}\n\n/** Convert background image from center/center to left/top origin. */\nfunction prepareBackgroundOrigin(canvas: FabricCanvas): () => void {\n const bg = canvas.backgroundImage;\n if (\n !(bg instanceof FabricImage) ||\n (bg.originX === 'left' && bg.originY === 'top')\n ) {\n return () => {};\n }\n const saved: SavedOrigin = {\n originX: bg.originX,\n originY: bg.originY,\n left: bg.left ?? 0,\n top: bg.top ?? 0,\n };\n const leftTop = bg.getPositionByOrigin('left', 'top');\n bg.set({ originX: 'left', originY: 'top', left: leftTop.x, top: leftTop.y });\n return () => {\n bg.set(saved);\n };\n}\n\n/** Add strokeWidthBase to obj.data for backward compatibility with old canvas. */\nfunction prepareStrokeWidthBaseData(canvas: FabricCanvas): () => void {\n const savedData = new Map<FabricObject, FabricObject['data']>();\n canvas.forEachObject((obj) => {\n const base = strokeBaseMap.get(obj) ?? obj.strokeWidth;\n if (base !== undefined && base !== 0 && obj.data) {\n savedData.set(obj, obj.data);\n (obj as unknown as { data: Record<string, unknown> }).data = {\n ...(obj.data as Record<string, unknown>),\n strokeWidthBase: base,\n };\n }\n });\n return () =>\n savedData.forEach((originalData, obj) => {\n (obj as unknown as { data: FabricObject['data'] }).data = originalData;\n });\n}\n\n// --- Public serialization API ---\n\n/**\n * Serialize the canvas to a plain object, ready for `JSON.stringify`.\n *\n * The output uses Fabric 6 conventions (`originX: 'left'`, `originY: 'top'`,\n * `backgroundFilters`, `data.strokeWidthBase`) so saved data is readable by\n * both old (Fabric 6) and new (Fabric 7) canvas implementations.\n *\n * Internally, the canvas keeps `center/center` origins at runtime. This\n * function temporarily converts objects to `left/top` origin before calling\n * `toObject()`, then restores the runtime state immediately after.\n */\nexport function serializeCanvas(\n canvas: FabricCanvas,\n options?: SerializeOptions,\n): CanvasJSON {\n const properties = [\n 'data',\n 'shapeType',\n // Control styling — absent from Fabric's default toObject output\n 'borderColor',\n 'cornerColor',\n 'cornerStrokeColor',\n 'transparentCorners',\n // Interaction locks — absent from Fabric's default toObject output\n 'lockRotation',\n 'lockUniScaling',\n ...(options?.properties ?? []),\n ];\n\n // Temporarily mutate canvas state for backward-compatible serialization.\n // Each prepare* call returns a restore function to undo the mutation.\n const restoreStrokeWidths = prepareStrokeWidths(canvas);\n const restoreBorderRadii = prepareBorderRadii(canvas);\n const restoreOrigins = prepareObjectOrigins(canvas);\n const restoreBgOrigin = prepareBackgroundOrigin(canvas);\n const restoreData = prepareStrokeWidthBaseData(canvas);\n\n const json = canvas.toObject(properties) as CanvasJSON;\n\n // Strip backgroundColor — it's theme-dependent, not user data.\n delete json.backgroundColor;\n\n // Add backward-compatible canvas-level properties.\n (json as Record<string, unknown>).backgroundFilters = {\n opacity: getBackgroundContrast(canvas),\n inverted: getBackgroundInverted(canvas),\n };\n if (canvas.lockLightMode !== undefined) {\n (json as Record<string, unknown>).lockLightMode = canvas.lockLightMode;\n }\n\n // Restore all runtime state.\n restoreStrokeWidths();\n restoreBorderRadii();\n restoreOrigins();\n restoreBgOrigin();\n restoreData();\n\n return json;\n}\n\n/**\n * Options for {@link loadCanvas}.\n */\nexport interface LoadCanvasOptions {\n /**\n * If provided, objects for which this function returns `false` are removed\n * from the canvas after loading. Useful for filtering out objects whose IDs\n * no longer exist in the application's data model.\n */\n filter?: (obj: FabricObject) => boolean;\n /**\n * Visual border radius applied to loaded Rects (excluding circles and DEVICE objects).\n * Pass a number to customize, or `false` to skip entirely. Default: `4`.\n */\n borderRadius?: number | false;\n}\n\n/**\n * Load a canvas from a previously serialized JSON object (from {@link serializeCanvas}).\n *\n * Clears the canvas and restores all objects, then requests a re-render.\n * The returned promise resolves once the canvas is fully loaded.\n */\nexport async function loadCanvas(\n canvas: FabricCanvas,\n json: CanvasJSON | object,\n options?: LoadCanvasOptions,\n): Promise<FabricObject[]> {\n await canvas.loadFromJSON(json);\n\n // Strip backgroundColor restored from old canvas data —\n // background color is a runtime/theme concern, not persisted data.\n // Mirrors serializeCanvas which already deletes backgroundColor on save.\n canvas.backgroundColor = '';\n\n // Strip legacy `backgroundFilters` custom property from old canvas data.\n // The old implementation stored contrast/inversion state as a custom canvas\n // property; the new canvas reads actual Fabric filters on the image directly.\n delete (canvas as unknown as Record<string, unknown>).backgroundFilters;\n\n // Restore lockLightMode from serialized data. Fabric's loadFromJSON sets\n // unknown top-level properties on the canvas instance, so we read and\n // re-assign it to the typed augmented property.\n const rawCanvas = canvas as unknown as Record<string, unknown>;\n if (rawCanvas.lockLightMode !== undefined) {\n canvas.lockLightMode = rawCanvas.lockLightMode as boolean;\n }\n\n // Normalize background image origin: old data (Fabric 6) uses originX/Y\n // 'left'/'top' while the new canvas uses 'center'/'center'. Compute the\n // visual center before switching so the image stays in the same position.\n const bg = canvas.backgroundImage;\n if (bg instanceof FabricImage) {\n if (bg.originX !== 'center' || bg.originY !== 'center') {\n const center = bg.getCenterPoint();\n bg.set({\n originX: 'center',\n originY: 'center',\n left: center.x,\n top: center.y,\n });\n bg.setCoords();\n }\n }\n\n // Filter out non-matching objects before applying styles\n if (options?.filter) {\n const toRemove: FabricObject[] = [];\n canvas.forEachObject((obj) => {\n if (!options.filter!(obj)) toRemove.push(obj);\n });\n for (const obj of toRemove) canvas.remove(obj);\n }\n\n // Normalize legacy origin: old data uses originX/Y 'left'/'top', but the\n // new canvas expects 'center'/'center'. Compute the visual center before\n // switching origins so objects stay in the same position.\n canvas.forEachObject((obj) => {\n if (obj.originX === 'center' && obj.originY === 'center') return;\n const center = obj.getCenterPoint();\n obj.set({\n originX: 'center',\n originY: 'center',\n left: center.x,\n top: center.y,\n });\n obj.setCoords();\n });\n\n // Re-apply per-object state that Fabric does not persist through serialization.\n canvas.forEachObject((obj) => {\n // Strip legacy `strokeWidthBase` from obj.data — the old canvas stored the\n // base stroke width by mutating data. The new canvas uses internal WeakMaps.\n const data = obj.data as Record<string, unknown> | undefined;\n if (data?.strokeWidthBase !== undefined) {\n delete data.strokeWidthBase;\n }\n // Control styling (borderColor, cornerColor, etc.) is absent from Fabric's\n // default toObject output, so we restore it explicitly for all objects.\n obj.set(DEFAULT_CONTROL_STYLE);\n // Circle-specific constraints (control visibility, lock flags).\n if (obj.shapeType === 'circle' && obj instanceof Rect) {\n restoreCircleConstraints(obj);\n }\n // Apply visual border radius to Rects (excluding circles and DEVICE objects).\n // Compensate for non-uniform scaling so corners appear circular.\n // Original values are stored so serializeCanvas can strip them before saving.\n const borderRadius = options?.borderRadius ?? DEFAULT_VIEW_BORDER_RADIUS;\n if (\n borderRadius !== false &&\n obj instanceof Rect &&\n obj.shapeType !== 'circle' &&\n obj.data?.type !== 'DEVICE'\n ) {\n borderRadiusBaseMap.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });\n const rx = borderRadius / (obj.scaleX ?? 1);\n const ry = borderRadius / (obj.scaleY ?? 1);\n obj.set({ rx, ry });\n }\n });\n canvas.requestRenderAll();\n\n return canvas.getObjects() as FabricObject[];\n}\n","import { Canvas as FabricCanvas } from 'fabric';\nimport { serializeCanvas, loadCanvas } from './serialization';\nimport type { CanvasJSON } from './types';\n\nexport interface HistoryOptions {\n /** Maximum number of snapshots to keep. Oldest are dropped when exceeded. Default: 50. */\n maxSize?: number;\n /** Debounce delay in milliseconds before capturing a snapshot after a change. Default: 300. */\n debounce?: number;\n}\n\nexport interface HistoryTracker {\n /** Undo the last change. No-op if at the beginning of history. */\n undo: () => Promise<void>;\n /** Redo a previously undone change. No-op if at the end of history. */\n redo: () => Promise<void>;\n /** Whether an undo operation is available. */\n canUndo: () => boolean;\n /** Whether a redo operation is available. */\n canRedo: () => boolean;\n /** Manually push the current canvas state as a snapshot. */\n pushSnapshot: () => void;\n /** Remove all event listeners and clear history. */\n cleanup: () => void;\n}\n\n/**\n * Create a snapshot-based undo/redo tracker for a canvas.\n *\n * Listens to `object:added`, `object:modified`, and `object:removed` events\n * and captures a serialized snapshot of the canvas state (debounced).\n *\n * `undo()` and `redo()` load adjacent snapshots via `loadCanvas`.\n * During undo/redo operations, event-triggered captures are suppressed.\n */\nexport function createHistoryTracker(\n canvas: FabricCanvas,\n options?: HistoryOptions,\n): HistoryTracker {\n const maxSize = options?.maxSize ?? 50;\n const debounceMs = options?.debounce ?? 300;\n\n const snapshots: CanvasJSON[] = [];\n let currentIndex = -1;\n let isUndoRedo = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n function captureSnapshot() {\n if (isUndoRedo) return;\n\n const snapshot = serializeCanvas(canvas);\n\n // Drop any redo history beyond current position\n if (currentIndex < snapshots.length - 1) {\n snapshots.length = currentIndex + 1;\n }\n\n snapshots.push(snapshot);\n\n // Enforce max size\n if (snapshots.length > maxSize) {\n snapshots.shift();\n }\n\n currentIndex = snapshots.length - 1;\n }\n\n function debouncedCapture() {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n }\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n captureSnapshot();\n }, debounceMs);\n }\n\n const onChange = () => {\n if (!isUndoRedo) debouncedCapture();\n };\n\n canvas.on('object:added', onChange);\n canvas.on('object:modified', onChange);\n canvas.on('object:removed', onChange);\n\n async function loadSnapshot(index: number) {\n if (index < 0 || index >= snapshots.length) return;\n\n isUndoRedo = true;\n currentIndex = index;\n\n try {\n await loadCanvas(canvas, snapshots[index]);\n } finally {\n isUndoRedo = false;\n }\n }\n\n return {\n async undo() {\n if (currentIndex <= 0) return;\n await loadSnapshot(currentIndex - 1);\n },\n\n async redo() {\n if (currentIndex >= snapshots.length - 1) return;\n await loadSnapshot(currentIndex + 1);\n },\n\n canUndo() {\n return currentIndex > 0;\n },\n\n canRedo() {\n return currentIndex < snapshots.length - 1;\n },\n\n pushSnapshot() {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n captureSnapshot();\n },\n\n cleanup() {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n canvas.off('object:added', onChange);\n canvas.off('object:modified', onChange);\n canvas.off('object:removed', onChange);\n snapshots.length = 0;\n currentIndex = -1;\n },\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject } from 'fabric';\nimport {\n enablePanAndZoom,\n type PanAndZoomOptions,\n type ViewportController,\n} from '../viewport';\nimport {\n enableScaledStrokes,\n enableScaledBorderRadius,\n type ScaledBorderRadiusOptions,\n} from '../serialization';\nimport { fitViewportToBackground } from '../background';\nimport { useViewportActions, syncZoom } from './shared';\n\n/** Visual properties that can be updated on view-canvas objects. */\nexport interface ViewObjectStyle {\n fill?: string;\n stroke?: string;\n strokeWidth?: number;\n opacity?: number;\n visible?: boolean;\n}\n\nexport interface UseViewCanvasOptions {\n /** Configure pan and zoom. Pass `false` to disable, or options to customize. Default: enabled. */\n panAndZoom?: boolean | PanAndZoomOptions;\n /**\n * Keep stroke widths visually constant as the user zooms in/out.\n * Pass `false` to disable. Default: enabled.\n */\n scaledStrokes?: boolean;\n /** Called after the canvas is initialized and viewport is set up. */\n onReady?: (canvas: FabricCanvas) => void | Promise<void>;\n /**\n * Automatically fit the viewport to the background image after `onReady`\n * completes, if a background image is present. Also applies when\n * `viewport.reset` is called while a background image is set.\n * Pass `false` to disable. Default: enabled.\n */\n autoFitToBackground?: boolean;\n /**\n * Visual border radius applied to loaded Rects (via `loadCanvas`).\n * Pass a number to customize (default: 4), or `false` to disable.\n */\n borderRadius?: number | false;\n}\n\n/** Disable selection on all objects. Border radius is applied by loadCanvas. */\nfunction lockCanvas(canvas: FabricCanvas) {\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n });\n}\n\n/**\n * Hook that provides a view-only canvas experience.\n *\n * Like {@link useEditCanvas} but without create, edit, delete, or selection\n * capabilities. The canvas is always in pan mode — click and drag to pan,\n * scroll to zoom.\n *\n * @example\n * ```tsx\n * const canvas = useViewCanvas();\n *\n * return <Canvas onReady={canvas.onReady} width={800} height={600} />;\n * ```\n */\nexport function useViewCanvas(options?: UseViewCanvasOptions) {\n const canvasRef = useRef<FabricCanvas | null>(null);\n const viewportRef = useRef<ViewportController | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const [zoom, setZoom] = useState(1);\n\n const onReady = useCallback(\n (canvas: FabricCanvas) => {\n canvasRef.current = canvas;\n const opts = optionsRef.current;\n\n if (opts?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (opts?.borderRadius !== false) {\n const borderRadiusOpts: ScaledBorderRadiusOptions | undefined =\n typeof opts?.borderRadius === 'number'\n ? { radius: opts.borderRadius }\n : undefined;\n enableScaledBorderRadius(canvas, borderRadiusOpts);\n }\n\n if (opts?.panAndZoom !== false) {\n const panAndZoomOpts =\n typeof opts?.panAndZoom === 'object' ? opts.panAndZoom : {};\n\n viewportRef.current = enablePanAndZoom(canvas, {\n ...panAndZoomOpts,\n initialMode: 'pan',\n });\n }\n\n lockCanvas(canvas);\n\n // Lock any objects added after initialisation\n canvas.on('object:added', () => {\n lockCanvas(canvas);\n });\n\n canvas.on('mouse:wheel', () => {\n setZoom(canvas.getZoom());\n });\n\n const onReadyResult = opts?.onReady?.(canvas);\n if (opts?.autoFitToBackground !== false) {\n Promise.resolve(onReadyResult).then(() => {\n if (canvas.backgroundImage) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n });\n }\n },\n // onReady and panAndZoom are intentionally excluded — we only initialize once\n [],\n );\n\n const { resetViewport, zoomIn, zoomOut, panToObject, zoomToFit } =\n useViewportActions(canvasRef, viewportRef, setZoom);\n\n /** Find a canvas object by its `data.id`. */\n const findObject = (id: string): FabricObject | undefined => {\n const c = canvasRef.current;\n if (!c) return undefined;\n return c.getObjects().find((o) => o.data?.id === id);\n };\n\n /** Update a single object's visual style by its `data.id`. */\n const setObjectStyle = useCallback((id: string, style: ViewObjectStyle) => {\n const obj = findObject(id);\n if (!obj) return;\n obj.set(style);\n canvasRef.current!.requestRenderAll();\n }, []);\n\n /** Batch-update multiple objects' visual styles. Keyed by `data.id`. */\n const setObjectStyles = useCallback(\n (styles: Record<string, ViewObjectStyle>) => {\n const c = canvasRef.current;\n if (!c) return;\n const objects = c.getObjects();\n const idMap = new Map<string, FabricObject>();\n for (const obj of objects) {\n if (obj.data?.id) idMap.set(obj.data.id, obj);\n }\n let updated = false;\n for (const [id, style] of Object.entries(styles)) {\n const obj = idMap.get(id);\n if (obj) {\n obj.set(style);\n updated = true;\n }\n }\n if (updated) c.requestRenderAll();\n },\n [],\n );\n\n /** Apply a visual style to all objects whose `data.type` matches the given type. */\n const setObjectStyleByType = useCallback(\n (type: string, style: ViewObjectStyle) => {\n const c = canvasRef.current;\n if (!c) return;\n let updated = false;\n for (const obj of c.getObjects()) {\n if (obj.data?.type === type) {\n obj.set(style);\n updated = true;\n }\n }\n if (updated) c.requestRenderAll();\n },\n [],\n );\n\n return {\n /** Pass this to `<Canvas onReady={...} />` */\n onReady,\n /** Ref to the underlying Fabric canvas instance. */\n canvasRef,\n /** Current zoom level (reactive). */\n zoom,\n /** Viewport controls. */\n viewport: {\n /** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */\n reset: resetViewport,\n /** Zoom in toward the canvas center. Default step: 0.2. */\n zoomIn,\n /** Zoom out from the canvas center. Default step: 0.2. */\n zoomOut,\n /** Pan the viewport to center on a specific object. */\n panToObject,\n /** Zoom and pan to fit a specific object in the viewport. */\n zoomToFit,\n },\n /** Update a single object's visual style by its `data.id`. */\n setObjectStyle,\n /** Batch-update multiple objects' visual styles in one render. Keyed by `data.id`. */\n setObjectStyles,\n /** Apply a visual style to all objects whose `data.type` matches. */\n setObjectStyleByType,\n };\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport type { Canvas as FabricCanvas, CanvasEvents } from 'fabric';\n\n/**\n * A map of Fabric canvas event names to their handler functions.\n *\n * Each key is a valid `CanvasEvents` event name and each handler receives the\n * correctly-typed event payload. Set a handler to `undefined` to skip it.\n *\n * Common events include:\n * - `'object:added'`, `'object:removed'`, `'object:modified'`\n * - `'mouse:over'`, `'mouse:out'`, `'mouse:down'`, `'mouse:up'`, `'mouse:move'`\n * - `'mouse:wheel'`, `'after:render'`\n * - `'selection:created'`, `'selection:updated'`, `'selection:cleared'`\n */\nexport type CanvasEventHandlers = {\n [K in keyof CanvasEvents]?: (event: CanvasEvents[K]) => void;\n};\n\n/**\n * Subscribe to Fabric canvas events with automatic cleanup.\n *\n * Handlers are stored in a ref so they always call the latest version without\n * re-subscribing. The hook subscribes once when the canvas becomes available\n * (child `<Canvas>` effects fire before parent effects) and cleans up on unmount.\n *\n * @example\n * ```tsx\n * useCanvasEvents(editor.canvasRef, {\n * 'object:added': (e) => updateList(),\n * 'object:modified': () => setDirty(true),\n * 'object:removed': () => updateList(),\n * });\n * ```\n */\nexport function useCanvasEvents(\n canvasRef: RefObject<FabricCanvas | null>,\n events: CanvasEventHandlers,\n): void {\n const eventsRef = useRef(events);\n eventsRef.current = events;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n type EventName = keyof CanvasEvents;\n // Wrapper uses `unknown` because TypeScript can't correlate a dynamic key\n // with its matching handler type inside a loop (correlated-union limitation).\n // The public `CanvasEventHandlers` type is fully typed per-event.\n type EventCallback = (options: unknown) => void;\n const wrappers = new Map<EventName, EventCallback>();\n\n for (const key of Object.keys(eventsRef.current) as EventName[]) {\n if (!eventsRef.current[key]) continue;\n const wrapped: EventCallback = (options) => {\n (eventsRef.current[key] as EventCallback | undefined)?.(options);\n };\n canvas.on(key, wrapped);\n wrappers.set(key, wrapped);\n }\n\n return () => {\n wrappers.forEach((handler, name) => {\n canvas.off(name, handler);\n });\n };\n }, [canvasRef]);\n}\n","import { useEffect, useRef, useState, type RefObject } from 'react';\nimport type {\n Canvas as FabricCanvas,\n CanvasEvents,\n FabricObject,\n} from 'fabric';\n\nexport interface UseCanvasTooltipOptions<T> {\n /** Extract tooltip content from a hovered object. Return `null` to skip the tooltip. */\n getContent: (obj: FabricObject) => T | null;\n}\n\nexport interface CanvasTooltipState<T> {\n /** Whether the tooltip is currently visible. */\n visible: boolean;\n /** The content extracted from the hovered object, or `null` when hidden. */\n content: T | null;\n /** Screen-space position (relative to the canvas container) for the tooltip. */\n position: { x: number; y: number };\n}\n\n/**\n * Track mouse hover over canvas objects and return tooltip state.\n *\n * Listens to `mouse:over`, `mouse:out`, `after:render`, and `mouse:wheel` to\n * maintain a reactive tooltip state. The returned position is in screen-space\n * coordinates relative to the canvas container element — suitable for absolute\n * positioning of a tooltip component.\n *\n * @example\n * ```tsx\n * const tooltip = useCanvasTooltip(view.canvasRef, {\n * getContent: (obj) => obj.data ? { id: obj.data.id, type: obj.data.type } : null,\n * });\n *\n * return (\n * <>\n * <Canvas onReady={view.onReady} />\n * {tooltip.visible && (\n * <div style={{ position: 'absolute', left: tooltip.position.x, top: tooltip.position.y }}>\n * {tooltip.content?.id}\n * </div>\n * )}\n * </>\n * );\n * ```\n */\nexport function useCanvasTooltip<T>(\n canvasRef: RefObject<FabricCanvas | null>,\n options: UseCanvasTooltipOptions<T>,\n): CanvasTooltipState<T> {\n const [state, setState] = useState<CanvasTooltipState<T>>({\n visible: false,\n content: null,\n position: { x: 0, y: 0 },\n });\n\n const hoveredObjectRef = useRef<FabricObject | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n function calculatePosition(\n target: FabricObject,\n ): { x: number; y: number } | null {\n const bounds = target.getBoundingRect();\n const zoom = canvas!.getZoom();\n const vt = canvas!.viewportTransform;\n if (!vt) return null;\n\n return {\n x: (bounds.left + bounds.width / 2) * zoom + vt[4],\n y: bounds.top * zoom + vt[5] - 10,\n };\n }\n\n const handleMouseOver = (e: CanvasEvents['mouse:over']) => {\n const target = e.target;\n if (!target) return;\n\n const content = optionsRef.current.getContent(target);\n if (content === null) return;\n\n if (hoveredObjectRef.current !== target) {\n hoveredObjectRef.current = target;\n const position = calculatePosition(target);\n if (position) {\n setState({ visible: true, content, position });\n }\n }\n };\n\n const handleMouseOut = (e: CanvasEvents['mouse:out']) => {\n if (e.target && hoveredObjectRef.current === e.target) {\n setState((prev) => ({ ...prev, visible: false }));\n hoveredObjectRef.current = null;\n }\n };\n\n const updatePosition = () => {\n if (!hoveredObjectRef.current) return;\n const position = calculatePosition(hoveredObjectRef.current);\n if (position) {\n setState((prev) => (prev.visible ? { ...prev, position } : prev));\n }\n };\n\n canvas.on('mouse:over', handleMouseOver);\n canvas.on('mouse:out', handleMouseOut);\n canvas.on('after:render', updatePosition);\n canvas.on('mouse:wheel', updatePosition);\n\n return () => {\n canvas.off('mouse:over', handleMouseOver);\n canvas.off('mouse:out', handleMouseOut);\n canvas.off('after:render', updatePosition);\n canvas.off('mouse:wheel', updatePosition);\n };\n }, [canvasRef]);\n\n return state;\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport type {\n Canvas as FabricCanvas,\n CanvasEvents,\n FabricObject,\n} from 'fabric';\n\nexport interface UseCanvasClickOptions {\n /** Maximum movement in pixels before the gesture is treated as a pan. Default: 5. */\n threshold?: number;\n /** Maximum duration in milliseconds for the gesture to count as a click. Default: 300. */\n maxDuration?: number;\n}\n\n/**\n * Distinguish clicks from pan gestures on a canvas.\n *\n * On view-mode canvases where pan is always active, a regular Fabric `mouse:up`\n * fires for both clicks and drag-to-pan. This hook tracks pointer movement and\n * timing to determine whether the user intended a click or a pan, then calls\n * `onClick` only for genuine clicks.\n *\n * @example\n * ```tsx\n * useCanvasClick(view.canvasRef, (target) => {\n * if (target?.data?.id) {\n * navigate(`/locations/${target.data.id}`);\n * }\n * });\n * ```\n */\nexport function useCanvasClick(\n canvasRef: RefObject<FabricCanvas | null>,\n onClick: (target: FabricObject | undefined) => void,\n options?: UseCanvasClickOptions,\n): void {\n const onClickRef = useRef(onClick);\n onClickRef.current = onClick;\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n let mouseDown: { x: number; y: number; time: number } | null = null;\n let isPanning = false;\n\n const handleMouseDown = (e: CanvasEvents['mouse:down']) => {\n const native = e.e instanceof TouchEvent ? e.e.touches[0] : e.e;\n if (native) {\n mouseDown = { x: native.clientX, y: native.clientY, time: Date.now() };\n isPanning = false;\n }\n };\n\n const handleMouseMove = (e: CanvasEvents['mouse:move']) => {\n if (!mouseDown) return;\n const native = e.e instanceof TouchEvent ? e.e.touches[0] : e.e;\n if (!native) return;\n\n const threshold = optionsRef.current?.threshold ?? 5;\n const deltaX = Math.abs(native.clientX - mouseDown.x);\n const deltaY = Math.abs(native.clientY - mouseDown.y);\n if (deltaX > threshold || deltaY > threshold) {\n isPanning = true;\n }\n };\n\n const handleMouseUp = (e: CanvasEvents['mouse:up']) => {\n if (!mouseDown) return;\n\n const maxDuration = optionsRef.current?.maxDuration ?? 300;\n const elapsed = Date.now() - mouseDown.time;\n\n if (!isPanning && elapsed < maxDuration) {\n onClickRef.current(e.target);\n }\n\n mouseDown = null;\n isPanning = false;\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n canvas.on('mouse:up', handleMouseUp);\n\n return () => {\n canvas.off('mouse:down', handleMouseDown);\n canvas.off('mouse:move', handleMouseMove);\n canvas.off('mouse:up', handleMouseUp);\n };\n }, [canvasRef]);\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport type { Canvas as FabricCanvas, FabricObject } from 'fabric';\nimport { util } from 'fabric';\n\nexport interface UseObjectOverlayOptions {\n /**\n * Scale the overlay container to the object's actual dimensions and apply\n * a CSS `scale(zoom)` transform so content inside lays out in canvas units.\n * Pass `false` to opt out and receive screen-space pixel dimensions instead.\n * Default: true.\n */\n autoScaleContent?: boolean;\n /**\n * CSS selector for a text element inside the overlay. When\n * `autoScaleContent` is enabled and the computed scale drops below a\n * threshold, elements matching this selector are hidden (via\n * `display: none`) so only icons remain visible.\n * Default: undefined (no text hiding).\n */\n textSelector?: string;\n /**\n * Minimum content scale at which `textSelector` elements remain visible.\n * Below this threshold, matched elements are hidden.\n * Default: 0.5.\n */\n textMinScale?: number;\n}\n\n/**\n * Position a DOM element over a Fabric canvas object, kept in sync with\n * pan, zoom, move, scale, and rotate transforms.\n *\n * Returns a ref to attach to the overlay container element. The element\n * must be positioned absolutely within a relative-positioned parent that\n * wraps the `<Canvas>` component.\n *\n * @example\n * ```tsx\n * const overlayRef = useObjectOverlay(canvasRef, object, {\n * autoScaleContent: true,\n * textSelector: '.desk-text',\n * });\n *\n * return (\n * <div ref={overlayRef} style={{ position: 'absolute', pointerEvents: 'none' }}>\n * <CanvasDeskIcon />\n * <span className=\"desk-text\">{name}</span>\n * </div>\n * );\n * ```\n */\nexport function useObjectOverlay(\n canvasRef: RefObject<FabricCanvas | null>,\n object: FabricObject | null | undefined,\n options?: UseObjectOverlayOptions,\n): RefObject<HTMLDivElement | null> {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas || !object) return;\n\n function update() {\n const el = containerRef.current;\n if (!el || !canvas || !object) return;\n\n const zoom = canvas.getZoom();\n const vt = canvas.viewportTransform;\n if (!vt) return;\n\n const center = object.getCenterPoint();\n const actualWidth = (object.width ?? 0) * (object.scaleX ?? 1);\n const actualHeight = (object.height ?? 0) * (object.scaleY ?? 1);\n const screenCoords = util.transformPoint(center, vt);\n\n const screenWidth = actualWidth * zoom;\n const screenHeight = actualHeight * zoom;\n\n const angle = object.angle ?? 0;\n const opts = optionsRef.current;\n\n if (opts?.autoScaleContent !== false) {\n // Container dimensions match the object's actual size (canvas units).\n // A CSS scale transform maps it to the correct screen size so content\n // inside lays out relative to the real object dimensions.\n el.style.left = `${screenCoords.x - actualWidth / 2}px`;\n el.style.top = `${screenCoords.y - actualHeight / 2}px`;\n el.style.width = `${actualWidth}px`;\n el.style.height = `${actualHeight}px`;\n el.style.transformOrigin = 'center center';\n el.style.transform =\n angle !== 0 ? `scale(${zoom}) rotate(${angle}deg)` : `scale(${zoom})`;\n el.style.rotate = '';\n el.style.setProperty('--overlay-scale', String(zoom));\n\n if (opts?.textSelector) {\n const textMinScale = opts?.textMinScale ?? 0.5;\n const textEls = el.querySelectorAll<HTMLElement>(opts.textSelector);\n const display = zoom < textMinScale ? 'none' : '';\n textEls.forEach((t) => {\n t.style.display = display;\n });\n }\n } else {\n // No auto-scaling: container sized to screen-space object bounds.\n el.style.left = `${screenCoords.x - screenWidth / 2}px`;\n el.style.top = `${screenCoords.y - screenHeight / 2}px`;\n el.style.width = `${screenWidth}px`;\n el.style.height = `${screenHeight}px`;\n el.style.transform = '';\n el.style.rotate = angle !== 0 ? `${angle}deg` : '';\n }\n }\n\n // Initial position\n update();\n\n // Subscribe to events that affect object screen position\n canvas.on('after:render', update);\n object.on('moving', update);\n object.on('scaling', update);\n object.on('rotating', update);\n\n return () => {\n canvas.off('after:render', update);\n object.off('moving', update);\n object.off('scaling', update);\n object.off('rotating', update);\n };\n }, [canvasRef, object]);\n\n return containerRef;\n}\n","// Fabric.js module augmentation — adds `shapeType`, `data`, and `lockLightMode`\n// to FabricObject/Canvas. Importing here ensures the augmentation is emitted in\n// the dist and automatically applied for consumers of the package.\nimport './fabricAugmentation';\n\n// --- Component ---\nexport { Canvas } from './Canvas';\nexport type { CanvasProps } from './Canvas';\n\n// --- Hooks ---\nexport { useEditCanvas } from './hooks';\nexport type { UseEditCanvasOptions } from './hooks';\nexport { useViewCanvas } from './hooks';\nexport type { UseViewCanvasOptions, ViewObjectStyle } from './hooks';\nexport { useCanvasEvents } from './hooks';\nexport type { CanvasEventHandlers } from './hooks';\nexport { useCanvasTooltip } from './hooks';\nexport type { UseCanvasTooltipOptions, CanvasTooltipState } from './hooks';\nexport { useCanvasClick } from './hooks';\nexport type { UseCanvasClickOptions } from './hooks';\nexport { useObjectOverlay } from './hooks';\nexport type { UseObjectOverlayOptions } from './hooks';\n\n// --- Types ---\nexport type {\n Point2D,\n ShapeStyleOptions,\n SnappingOptions,\n InteractionModeOptions,\n SnappableInteractionOptions,\n DragBounds,\n ModeSetup,\n CanvasJSON,\n} from './types';\nexport type { ObjectDataType } from './fabricAugmentation';\n\n// --- Shapes ---\nexport {\n createRectangle,\n createRectangleAtPoint,\n editRectangle,\n} from './shapes';\nexport type { RectangleOptions, RectangleAtPointOptions } from './shapes';\n\nexport { createCircle, createCircleAtPoint, editCircle } from './shapes';\nexport type { CircleOptions, CircleAtPointOptions } from './shapes';\n\nexport {\n createPolygon,\n createPolygonAtPoint,\n createPolygonFromDrag,\n createPolygonFromVertices,\n editPolygon,\n} from './shapes';\nexport type { PolygonOptions } from './shapes';\n\n// --- Interactions ---\nexport { enableClickToCreate } from './interactions';\nexport { enableDragToCreate } from './interactions';\nexport type { DragToCreateOptions } from './interactions';\nexport { enableDrawToCreate } from './interactions';\nexport type { DrawToCreateOptions } from './interactions';\nexport { enableVertexEdit } from './interactions';\nexport type { VertexEditOptions } from './interactions';\n\n// --- Viewport ---\nexport { enablePanAndZoom, resetViewport } from './viewport';\nexport type {\n ViewportController,\n ViewportMode,\n PanAndZoomOptions,\n PanToObjectOptions,\n ZoomToFitOptions,\n} from './viewport';\n\n// --- Alignment ---\nexport { getSnapPoints, registerSnapPointExtractor } from './alignment';\nexport type { SnapPointExtractor } from './alignment';\nexport { enableObjectAlignment } from './alignment';\nexport type { ObjectAlignmentOptions } from './alignment';\nexport { enableRotationSnap } from './alignment';\nexport type { RotationSnapOptions } from './alignment';\nexport { snapCursorPoint } from './alignment';\nexport type {\n CursorSnapResult,\n CursorSnapOptions,\n GuidelineStyle,\n} from './alignment';\n\n// --- Keyboard ---\nexport { deleteObjects, enableKeyboardShortcuts } from './keyboard';\n\n// --- History ---\nexport { createHistoryTracker } from './history';\nexport type { HistoryOptions, HistoryTracker } from './history';\n\n// --- Serialization ---\nexport {\n enableScaledStrokes,\n enableScaledBorderRadius,\n serializeCanvas,\n loadCanvas,\n getBaseStrokeWidth,\n} from './serialization';\nexport type {\n SerializeOptions,\n LoadCanvasOptions,\n ScaledBorderRadiusOptions,\n} from './serialization';\n\n// --- Background ---\nexport {\n fitViewportToBackground,\n getBackgroundSrc,\n setBackgroundContrast,\n getBackgroundContrast,\n setBackgroundInverted,\n getBackgroundInverted,\n resizeImageUrl,\n setBackgroundImage,\n} from './background';\nexport type {\n FitViewportOptions,\n ResizeResult,\n ResizeImageOptions,\n SetBackgroundImageOptions,\n} from './background';\n\n// --- Styles ---\nexport {\n DEFAULT_CONTROL_STYLE,\n DEFAULT_SHAPE_STYLE,\n DEFAULT_CIRCLE_STYLE,\n DEFAULT_DRAG_SHAPE_STYLE,\n DEFAULT_GUIDELINE_SHAPE_STYLE,\n DEFAULT_ALIGNMENT_STYLE,\n} from './styles';\n\n// --- Fabric re-exports ---\n// Re-export commonly used Fabric types so consumers don't need to import\n// from both '@bwp-web/canvas' and 'fabric' (avoids type boundary mismatches).\nexport {\n Canvas as FabricCanvas,\n FabricObject,\n FabricImage,\n Rect,\n Polygon,\n Point,\n util,\n} from 'fabric';\n"],"mappings":"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","names":["useEffect","useRef","Polygon","viewportTransform","resetViewport","Point","Point","Point","util","util","Point","Point","drawXMarker","Rect","Point","Point","Rect","obj","Point","Rect","Rect","Rect","Rect","Polygon","Polygon","Point","Point","util","Point","util","FabricImage","Rect","Rect","FabricImage","useRef","Polygon","useEffect","resetViewport","useCallback","useRef","useState","useRef","useState","useCallback","resetViewport","useEffect","useRef","useEffect","useRef","useState","useEffect","useRef","useEffect","useRef","util","Canvas","FabricObject","FabricImage","Rect","Polygon","Point","util"]}
1
+ {"version":3,"sources":["../src/fabricAugmentation.ts","../src/Canvas/Canvas.tsx","../src/keyboard.ts","../src/hooks/useEditCanvas.ts","../src/viewport.ts","../src/constants.ts","../src/hooks/shared.ts","../src/background.ts","../src/alignment/snapPoints.ts","../src/alignment/objectAlignmentUtils.ts","../src/alignment/objectAlignment.ts","../src/alignment/objectAlignmentMath.ts","../src/alignment/rotationSnap.ts","../src/alignment/cursorSnapping.ts","../src/interactions/shared.ts","../src/interactions/clickToCreate.ts","../src/interactions/dragToCreate.ts","../src/styles.ts","../src/interactions/interactionSnapping.ts","../src/interactions/drawToCreate.ts","../src/shapes/rectangle.ts","../src/shapes/circle.ts","../src/shapes/polygon.ts","../src/interactions/vertexEdit.ts","../src/serialization.ts","../src/history.ts","../src/hooks/useViewCanvas.ts","../src/hooks/useCanvasEvents.ts","../src/hooks/useCanvasTooltip.ts","../src/hooks/useCanvasClick.ts","../src/overlay/ObjectOverlay.tsx","../src/overlay/OverlayContent.tsx","../src/overlay/FixedSizeContent.tsx","../src/index.ts"],"sourcesContent":["import 'fabric';\n\nexport type ShapeType = 'circle';\n\n/** Valid `data.type` values for canvas objects. */\nexport type ObjectDataType =\n | 'PLACE'\n | 'DEVICE'\n | 'DESK'\n | 'PARKING_SPACE'\n | 'FACILITY';\n\ndeclare module 'fabric' {\n interface FabricObject {\n shapeType?: ShapeType;\n data?: {\n type: ObjectDataType;\n id: string;\n };\n }\n interface Canvas {\n lockLightMode?: boolean;\n }\n}\n","import { Canvas as FabricCanvas } from 'fabric';\nimport { type CSSProperties, useEffect, useRef } from 'react';\nimport { enableKeyboardShortcuts } from '../keyboard';\n\nexport interface CanvasProps {\n /**\n * Canvas width in pixels. When both `width` and `height` are provided,\n * the canvas uses fixed dimensions. When omitted, the canvas auto-fills\n * its parent container and resizes with it.\n */\n width?: number;\n /**\n * Canvas height in pixels. When both `width` and `height` are provided,\n * the canvas uses fixed dimensions. When omitted, the canvas auto-fills\n * its parent container and resizes with it.\n */\n height?: number;\n className?: string;\n style?: CSSProperties;\n onReady?: (canvas: FabricCanvas) => void;\n /**\n * Enable Delete/Backspace keyboard shortcuts for removing selected objects.\n * Default: `false`. Set to `true` when using `<Canvas>` without `useEditCanvas`\n * (which registers its own shortcuts).\n */\n keyboardShortcuts?: boolean;\n /**\n * Additional options passed to the Fabric.js `Canvas` constructor.\n * Merged before `width`/`height`, so those props take precedence.\n */\n fabricOptions?: Record<string, unknown>;\n}\n\nexport function Canvas({\n width,\n height,\n className,\n style,\n onReady,\n keyboardShortcuts,\n fabricOptions,\n}: CanvasProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const isFixedSize = width !== undefined && height !== undefined;\n\n useEffect(() => {\n const el = canvasRef.current;\n const wrapper = wrapperRef.current;\n if (!el || !wrapper) return;\n\n const initialWidth = isFixedSize ? width : wrapper.clientWidth || 800;\n const initialHeight = isFixedSize ? height : wrapper.clientHeight || 600;\n\n const fabricCanvas = new FabricCanvas(el, {\n ...fabricOptions,\n width: initialWidth,\n height: initialHeight,\n });\n\n onReady?.(fabricCanvas);\n\n const cleanupShortcuts = keyboardShortcuts\n ? enableKeyboardShortcuts(fabricCanvas)\n : undefined;\n\n let observer: ResizeObserver | undefined;\n let rafId = 0;\n\n if (!isFixedSize) {\n let currentWidth = initialWidth;\n let currentHeight = initialHeight;\n\n observer = new ResizeObserver((entries) => {\n cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n const entry = entries[0];\n if (!entry) return;\n const { width: newWidth, height: newHeight } = entry.contentRect;\n if (\n newWidth > 0 &&\n newHeight > 0 &&\n (newWidth !== currentWidth || newHeight !== currentHeight)\n ) {\n currentWidth = newWidth;\n currentHeight = newHeight;\n fabricCanvas.setDimensions({ width: newWidth, height: newHeight });\n }\n });\n });\n observer.observe(wrapper);\n }\n\n return () => {\n cancelAnimationFrame(rafId);\n observer?.disconnect();\n cleanupShortcuts?.();\n fabricCanvas.dispose();\n };\n // onReady is intentionally excluded — we only want to initialise once on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const wrapperStyle: CSSProperties = isFixedSize\n ? { ...style }\n : { width: '100%', height: '100%', ...style };\n\n return (\n <div ref={wrapperRef} className={className} style={wrapperStyle}>\n <canvas ref={canvasRef} />\n </div>\n );\n}\n","import { Canvas as FabricCanvas, FabricObject } from 'fabric';\n\n/**\n * Remove one or more objects from the canvas.\n */\nexport function deleteObjects(\n canvas: FabricCanvas,\n ...objects: FabricObject[]\n): void {\n canvas.remove(...objects);\n canvas.requestRenderAll();\n}\n\n/**\n * Enable default keyboard shortcuts on the canvas.\n * - Escape / Delete / Backspace: delete selected objects.\n *\n * Returns a cleanup function that removes the listener.\n */\nexport function enableKeyboardShortcuts(canvas: FabricCanvas): () => void {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' || e.key === 'Delete' || e.key === 'Backspace') {\n const active = canvas.getActiveObjects();\n if (active.length > 0) {\n canvas.discardActiveObject();\n deleteObjects(canvas, ...active);\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject, Polygon } from 'fabric';\nimport {\n enablePanAndZoom,\n type PanAndZoomOptions,\n type ViewportController,\n type ViewportMode,\n} from '../viewport';\nimport {\n useViewportActions,\n resolveAlignmentEnabled,\n syncZoom,\n} from './shared';\nimport {\n enableObjectAlignment,\n type ObjectAlignmentOptions,\n enableRotationSnap,\n type RotationSnapOptions,\n} from '../alignment';\nimport {\n enableVertexEdit,\n setCanvasAlignmentEnabled,\n type VertexEditOptions,\n} from '../interactions';\nimport {\n enableScaledStrokes,\n enableScaledBorderRadius,\n type ScaledBorderRadiusOptions,\n} from '../serialization';\nimport { enableKeyboardShortcuts } from '../keyboard';\nimport {\n fitViewportToBackground,\n setBackgroundImage as setBackgroundImageFn,\n type ResizeImageOptions,\n type SetBackgroundImageOptions,\n} from '../background';\nimport {\n createHistoryTracker,\n type HistoryOptions,\n type HistoryTracker,\n} from '../history';\nimport type { ModeSetup } from '../types';\n\nexport interface UseEditCanvasOptions {\n /** Configure pan and zoom. Pass `false` to disable, or options to customize. Default: enabled. */\n panAndZoom?: boolean | PanAndZoomOptions;\n /** Enable alignment guidelines for object movement/scaling. Pass `false` to disable, or options to customize. Default: enabled. */\n alignment?: boolean | ObjectAlignmentOptions;\n /**\n * Snap rotation to angle intervals when Shift is held. Pass `false` to\n * disable, or `{ interval }` to change the snap interval. Default: enabled at 15° intervals.\n */\n rotationSnap?: boolean | RotationSnapOptions;\n /**\n * Master toggle for all alignment and snapping functionality.\n * - `undefined`: each feature uses its own prop (default).\n * - `true`: all alignment/snapping is force-enabled.\n * - `false`: all alignment/snapping is force-disabled.\n */\n enableAlignment?: boolean;\n /**\n * Enable double-click-to-vertex-edit on polygons.\n * Pass `false` to disable, or a `VertexEditOptions` object to customize handle appearance.\n * Default: enabled.\n */\n vertexEdit?: boolean | VertexEditOptions;\n /**\n * Keep stroke widths visually constant as the user zooms in/out.\n * Pass `false` to disable. Default: enabled.\n */\n scaledStrokes?: boolean;\n /**\n * Enable keyboard shortcuts (Delete/Backspace to delete selected objects).\n * Pass `false` to disable. Default: enabled.\n */\n keyboardShortcuts?: boolean;\n /** Called after the canvas is initialized and viewport is set up. */\n onReady?: (canvas: FabricCanvas) => void | Promise<void>;\n /**\n * Automatically fit the viewport to the background image after `onReady`\n * completes, if a background image is present. Also applies when\n * `viewport.reset` is called while a background image is set.\n * Pass `false` to disable. Default: enabled.\n */\n autoFitToBackground?: boolean;\n /**\n * Automatically resize background images when set via `setBackground`.\n * Images exceeding `maxSize` are downscaled to fit; images smaller than\n * `minSize` on both dimensions are rejected with an error.\n * Pass `false` to disable. Default: enabled (maxSize: 4096, minSize: 480).\n */\n backgroundResize?: boolean | ResizeImageOptions;\n /**\n * Track object add/remove/modify events and expose an `isDirty` flag.\n * Call `resetDirty()` after a successful save to clear the flag.\n * Default: disabled.\n */\n trackChanges?: boolean;\n /**\n * Visual border radius applied to loaded Rects (via `loadCanvas`).\n * Pass a number to customize (default: 4), or `false` to disable.\n */\n borderRadius?: number | false;\n /**\n * Enable snapshot-based undo/redo. Pass `true` for defaults, or an options\n * object to customize `maxSize` (default: 50) and `debounce` (default: 300ms).\n * Default: disabled.\n */\n history?: boolean | HistoryOptions;\n}\n\n/**\n * Hook that provides a batteries-included canvas experience with full\n * editing capabilities (create, edit, delete, select, pan, zoom).\n *\n * Handles canvas ref management, viewport setup (pan/zoom), reactive state\n * (zoom level, selected objects), and interaction mode lifecycle.\n *\n * @example\n * ```tsx\n * const canvas = useEditCanvas();\n *\n * // Activate a creation mode:\n * canvas.setMode((c, viewport) =>\n * enableClickToCreate(c, factory, { viewport, onCreated: () => canvas.setMode(null) })\n * );\n *\n * // Return to select mode:\n * canvas.setMode(null);\n *\n * return <Canvas onReady={canvas.onReady} width={800} height={600} />;\n * ```\n */\nexport function useEditCanvas(options?: UseEditCanvasOptions) {\n const canvasRef = useRef<FabricCanvas | null>(null);\n const viewportRef = useRef<ViewportController | null>(null);\n const alignmentCleanupRef = useRef<(() => void) | null>(null);\n const rotationSnapCleanupRef = useRef<(() => void) | null>(null);\n const modeCleanupRef = useRef<(() => void) | null>(null);\n const vertexEditCleanupRef = useRef<(() => void) | null>(null);\n const keyboardCleanupRef = useRef<(() => void) | null>(null);\n const historyRef = useRef<HistoryTracker | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // WeakMap to save per-object selectability before entering a mode\n const savedSelectabilityRef = useRef(\n new WeakMap<FabricObject, { selectable: boolean; evented: boolean }>(),\n );\n\n const [zoom, setZoom] = useState(1);\n const [selected, setSelected] = useState<FabricObject[]>([]);\n const [viewportMode, setViewportModeState] = useState<ViewportMode>('select');\n const [isEditingVertices, setIsEditingVertices] = useState(false);\n const [isDirty, setIsDirty] = useState(false);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n /**\n * Activate an interaction mode, or pass `null` to return to select mode.\n *\n * When a setup function is provided, the hook automatically:\n * - Cleans up the previous mode\n * - Saves and disables object selectability\n * - Calls `setup(canvas, viewport)` and stores its cleanup\n *\n * When `null` is passed, saved selectability is restored.\n */\n const setMode = useCallback((setup: ModeSetup | null) => {\n // Exit any active vertex edit session before switching modes\n vertexEditCleanupRef.current?.();\n vertexEditCleanupRef.current = null;\n setIsEditingVertices(false);\n\n modeCleanupRef.current?.();\n modeCleanupRef.current = null;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n if (setup === null) {\n // Restore saved selectability, defaulting to true for objects added during mode\n canvas.selection = true;\n canvas.forEachObject((obj) => {\n const saved = savedSelectabilityRef.current.get(obj);\n obj.selectable = saved?.selectable ?? true;\n obj.evented = saved?.evented ?? true;\n });\n } else {\n // Save current selectability before disabling\n canvas.forEachObject((obj) => {\n savedSelectabilityRef.current.set(obj, {\n selectable: obj.selectable,\n evented: obj.evented,\n });\n });\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n obj.evented = false;\n });\n const cleanup = setup(canvas, viewportRef.current ?? undefined);\n if (cleanup) {\n modeCleanupRef.current = cleanup;\n }\n }\n }, []);\n\n const onReady = useCallback(\n (canvas: FabricCanvas) => {\n canvasRef.current = canvas;\n const opts = optionsRef.current;\n\n setupFeatures();\n setupEventListeners();\n invokeOnReady();\n\n // --- Feature setup (strokes, viewport, alignment, etc.) ---\n\n function setupFeatures() {\n if (opts?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (opts?.borderRadius !== false) {\n const borderRadiusOpts: ScaledBorderRadiusOptions | undefined =\n typeof opts?.borderRadius === 'number'\n ? { radius: opts.borderRadius }\n : undefined;\n enableScaledBorderRadius(canvas, borderRadiusOpts);\n }\n\n if (opts?.keyboardShortcuts !== false) {\n keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);\n }\n\n setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);\n\n if (opts?.panAndZoom !== false) {\n viewportRef.current = enablePanAndZoom(\n canvas,\n typeof opts?.panAndZoom === 'object' ? opts.panAndZoom : undefined,\n );\n }\n\n const alignmentEnabled = resolveAlignmentEnabled(\n opts?.enableAlignment,\n opts?.alignment,\n );\n if (alignmentEnabled) {\n alignmentCleanupRef.current = enableObjectAlignment(\n canvas,\n typeof opts?.alignment === 'object' ? opts.alignment : undefined,\n );\n }\n\n if (opts?.rotationSnap !== false) {\n rotationSnapCleanupRef.current = enableRotationSnap(\n canvas,\n typeof opts?.rotationSnap === 'object'\n ? opts.rotationSnap\n : undefined,\n );\n }\n\n if (opts?.history) {\n const historyOpts =\n typeof opts.history === 'object' ? opts.history : undefined;\n historyRef.current = createHistoryTracker(canvas, historyOpts);\n }\n }\n\n // --- Event listeners (selection, zoom, dirty tracking, vertex edit) ---\n\n function setupEventListeners() {\n canvas.on('mouse:wheel', () => setZoom(canvas.getZoom()));\n\n canvas.on('selection:created', (e) => setSelected(e.selected ?? []));\n canvas.on('selection:updated', (e) => setSelected(e.selected ?? []));\n canvas.on('selection:cleared', () => setSelected([]));\n\n if (opts?.trackChanges) {\n canvas.on('object:added', () => setIsDirty(true));\n canvas.on('object:removed', () => setIsDirty(true));\n canvas.on('object:modified', () => setIsDirty(true));\n }\n\n if (opts?.history) {\n const syncHistoryState = () => {\n const h = historyRef.current;\n if (!h) return;\n setTimeout(() => {\n setCanUndo(h.canUndo());\n setCanRedo(h.canRedo());\n }, 350);\n };\n canvas.on('object:added', syncHistoryState);\n canvas.on('object:removed', syncHistoryState);\n canvas.on('object:modified', syncHistoryState);\n }\n\n if (opts?.vertexEdit !== false) {\n const vertexOpts =\n typeof opts?.vertexEdit === 'object' ? opts.vertexEdit : undefined;\n canvas.on('mouse:dblclick', (e) => {\n if (e.target && e.target instanceof Polygon) {\n vertexEditCleanupRef.current?.();\n vertexEditCleanupRef.current = enableVertexEdit(\n canvas,\n e.target,\n {\n ...vertexOpts,\n onExit: () => {\n vertexEditCleanupRef.current = null;\n setIsEditingVertices(false);\n },\n },\n );\n setIsEditingVertices(true);\n }\n });\n }\n }\n\n // --- Invoke consumer onReady, then auto-fit viewport + push initial history snapshot ---\n\n function invokeOnReady() {\n const onReadyResult = opts?.onReady?.(canvas);\n Promise.resolve(onReadyResult).then(() => {\n if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n historyRef.current?.pushSnapshot();\n });\n }\n },\n // Dependency array intentionally empty — we only initialize once on mount\n [],\n );\n\n // React to enableAlignment changes — tear down or set up object alignment,\n // and update the canvas-level alignment state for interaction modes.\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n // Keep canvas-level alignment state in sync\n setCanvasAlignmentEnabled(canvas, options?.enableAlignment);\n\n const shouldEnable = resolveAlignmentEnabled(\n options?.enableAlignment,\n options?.alignment,\n );\n\n if (shouldEnable && !alignmentCleanupRef.current) {\n alignmentCleanupRef.current = enableObjectAlignment(\n canvas,\n typeof options?.alignment === 'object' ? options.alignment : undefined,\n );\n } else if (!shouldEnable && alignmentCleanupRef.current) {\n alignmentCleanupRef.current();\n alignmentCleanupRef.current = null;\n }\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options?.enableAlignment]);\n\n const setViewportMode = useCallback((mode: ViewportMode) => {\n viewportRef.current?.setMode(mode);\n setViewportModeState(mode);\n }, []);\n\n const { resetViewport, zoomIn, zoomOut, panToObject, zoomToFit } =\n useViewportActions(canvasRef, viewportRef, setZoom);\n\n const setBackground = useCallback(\n async (url: string, bgOpts?: { preserveContrast?: boolean }) => {\n const canvas = canvasRef.current;\n if (!canvas) throw new Error('Canvas not ready');\n\n const opts = optionsRef.current;\n const resizeOpts: SetBackgroundImageOptions | undefined =\n opts?.backgroundResize !== false\n ? typeof opts?.backgroundResize === 'object'\n ? { ...opts.backgroundResize, ...bgOpts }\n : { ...bgOpts }\n : bgOpts?.preserveContrast\n ? { preserveContrast: true }\n : undefined;\n\n const img = await setBackgroundImageFn(canvas, url, resizeOpts);\n\n if (opts?.autoFitToBackground !== false) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n\n return img;\n },\n [],\n );\n\n return {\n /** Pass this to `<Canvas onReady={...} />` */\n onReady,\n /** Ref to the underlying Fabric canvas instance. */\n canvasRef,\n /** Current zoom level (reactive). */\n zoom,\n /** Currently selected objects (reactive). */\n selected,\n /** Viewport controls. */\n viewport: {\n /** Current viewport mode (reactive). */\n mode: viewportMode,\n /** Switch between 'select' and 'pan' viewport modes. */\n setMode: setViewportMode,\n /** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */\n reset: resetViewport,\n /** Zoom in toward the canvas center. Default step: 0.2. */\n zoomIn,\n /** Zoom out from the canvas center. Default step: 0.2. */\n zoomOut,\n /** Pan the viewport to center on a specific object. */\n panToObject,\n /** Zoom and pan to fit a specific object in the viewport. */\n zoomToFit,\n },\n /** Whether vertex edit mode is currently active (reactive). */\n isEditingVertices,\n /**\n * Activate an interaction mode or return to select mode.\n *\n * Pass a setup function to activate a creation mode:\n * ```ts\n * canvas.setMode((c, viewport) =>\n * enableClickToCreate(c, factory, { viewport })\n * );\n * ```\n *\n * Pass `null` to deactivate and return to select mode:\n * ```ts\n * canvas.setMode(null);\n * ```\n */\n setMode,\n /**\n * Set a background image from a URL. Automatically resizes if the image\n * exceeds the configured limits (opt out via `backgroundResize: false`),\n * and fits the viewport after setting if `autoFitToBackground` is enabled.\n *\n * Pass `{ preserveContrast: true }` to keep the current background contrast\n * when replacing the image.\n */\n setBackground,\n /** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */\n isDirty,\n /** Reset the dirty flag (e.g., after a successful save). */\n resetDirty: useCallback(() => setIsDirty(false), []),\n /** Undo the last change. Requires `history: true`. */\n undo: useCallback(async () => {\n const h = historyRef.current;\n if (!h) return;\n await h.undo();\n setCanUndo(h.canUndo());\n setCanRedo(h.canRedo());\n }, []),\n /** Redo a previously undone change. Requires `history: true`. */\n redo: useCallback(async () => {\n const h = historyRef.current;\n if (!h) return;\n await h.redo();\n setCanUndo(h.canUndo());\n setCanRedo(h.canRedo());\n }, []),\n /** Whether an undo operation is available (reactive). Requires `history: true`. */\n canUndo,\n /** Whether a redo operation is available (reactive). Requires `history: true`. */\n canRedo,\n };\n}\n","import {\n Canvas as FabricCanvas,\n type FabricObject,\n Point,\n TPointerEvent,\n} from 'fabric';\nimport {\n DEFAULT_MIN_ZOOM,\n DEFAULT_MAX_ZOOM,\n DEFAULT_ZOOM_FACTOR,\n DEFAULT_ZOOM_STEP,\n} from './constants';\n\nexport type ViewportMode = 'select' | 'pan';\n\nexport interface PanToObjectOptions {\n /** Animate the pan with an easing transition. Default: false. */\n animate?: boolean;\n /** Animation duration in milliseconds when `animate` is true. Default: 300. */\n duration?: number;\n}\n\nexport interface PanAndZoomOptions {\n /** Minimum zoom level (default: 0.2) */\n minZoom?: number;\n /** Maximum zoom level (default: 10) */\n maxZoom?: number;\n /** Zoom sensitivity base — raised to the power of deltaY (default: 0.999). */\n zoomFactor?: number;\n /** Initial viewport mode (default: 'select') */\n initialMode?: ViewportMode;\n}\n\nexport interface ZoomToFitOptions {\n /** Padding fraction around the object (default: 0.1 = 10% on each side). */\n padding?: number;\n}\n\nexport interface ViewportController {\n /** Switch between select and pan modes. */\n setMode: (mode: ViewportMode) => void;\n /** Get the current viewport mode. */\n getMode: () => ViewportMode;\n /** Temporarily disable all pan and zoom input (e.g. during draw modes). */\n setEnabled: (enabled: boolean) => void;\n /**\n * Zoom in toward the canvas center by a multiplicative factor.\n * Respects the configured min/max zoom bounds. Default factor: 1.2 (20%).\n */\n zoomIn: (factor?: number) => void;\n /**\n * Zoom out from the canvas center by a multiplicative factor.\n * Respects the configured min/max zoom bounds. Default factor: 1.2 (20%).\n */\n zoomOut: (factor?: number) => void;\n /**\n * Pan the viewport so the given object is centered on the canvas.\n * Optionally animate the transition. Automatically cancels any\n * in-progress panToObject animation.\n */\n panToObject: (object: FabricObject, options?: PanToObjectOptions) => void;\n /**\n * Zoom and pan the viewport so the given object fills the canvas\n * with optional padding.\n */\n zoomToFit: (object: FabricObject, options?: ZoomToFitOptions) => void;\n /** Remove all event listeners. */\n cleanup: () => void;\n}\n\ninterface ZoomBounds {\n minZoom: number;\n maxZoom: number;\n}\n\n// --- Pointer helpers ---\n\n/** Extract clientX/Y from any pointer-style event (Mouse, Pointer, or Touch). */\nfunction getPointerXY(e: Event): { x: number; y: number } | null {\n // PointerEvent extends MouseEvent — both are handled by this branch\n if (e instanceof MouseEvent) return { x: e.clientX, y: e.clientY };\n if (\n typeof TouchEvent !== 'undefined' &&\n e instanceof TouchEvent &&\n e.touches.length > 0\n ) {\n return { x: e.touches[0].clientX, y: e.touches[0].clientY };\n }\n return null;\n}\n\n/** Distance between two touch points. */\nfunction touchDistance(touches: TouchList): number {\n const dx = touches[0].clientX - touches[1].clientX;\n const dy = touches[0].clientY - touches[1].clientY;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n/** Midpoint of two touch points relative to a canvas element. */\nfunction touchMidpoint(touches: TouchList, el: HTMLElement): Point {\n const rect = el.getBoundingClientRect();\n return new Point(\n (touches[0].clientX + touches[1].clientX) / 2 - rect.left,\n (touches[0].clientY + touches[1].clientY) / 2 - rect.top,\n );\n}\n\n// --- Wheel zoom ---\n\nfunction setupWheelZoom(\n canvas: FabricCanvas,\n bounds: ZoomBounds,\n zoomFactor: number,\n isEnabled: () => boolean,\n) {\n const handleWheel = (opt: { e: WheelEvent }) => {\n if (!isEnabled()) return;\n\n const e = opt.e;\n e.preventDefault();\n e.stopPropagation();\n\n const delta = e.deltaY;\n let zoom = canvas.getZoom();\n zoom *= zoomFactor ** delta;\n zoom = Math.min(Math.max(zoom, bounds.minZoom), bounds.maxZoom);\n\n canvas.zoomToPoint(new Point(e.offsetX, e.offsetY), zoom);\n };\n\n canvas.on('mouse:wheel', handleWheel);\n return handleWheel;\n}\n\n// --- Mouse / single-touch pan ---\n\nfunction setupMousePan(\n canvas: FabricCanvas,\n getMode: () => ViewportMode,\n isEnabled: () => boolean,\n) {\n let isPanning = false;\n let lastPanX = 0;\n let lastPanY = 0;\n let didDisableSelection = false;\n\n const handleMouseDown = (opt: { e: TPointerEvent; target?: unknown }) => {\n if (!isEnabled()) return;\n\n const pos = getPointerXY(opt.e);\n if (!pos) return;\n\n const e = opt.e;\n const mode = getMode();\n const isMiddleButton = e instanceof MouseEvent && e.button === 1;\n const isModifiedSelect =\n e instanceof MouseEvent && mode === 'select' && (e.metaKey || e.ctrlKey);\n\n const shouldPan =\n mode === 'pan' ||\n isMiddleButton ||\n isModifiedSelect ||\n (mode === 'select' && !opt.target);\n\n if (shouldPan) {\n isPanning = true;\n lastPanX = pos.x;\n lastPanY = pos.y;\n\n if (canvas.selection) {\n didDisableSelection = true;\n canvas.selection = false;\n }\n canvas.setCursor('grab');\n }\n };\n\n const handleMouseMove = (opt: { e: TPointerEvent }) => {\n if (!isPanning) return;\n\n const pos = getPointerXY(opt.e);\n if (!pos) return;\n\n const dx = pos.x - lastPanX;\n const dy = pos.y - lastPanY;\n lastPanX = pos.x;\n lastPanY = pos.y;\n\n canvas.relativePan(new Point(dx, dy));\n canvas.setCursor('grab');\n };\n\n const handleMouseUp = () => {\n if (isPanning) {\n isPanning = false;\n\n if (didDisableSelection) {\n canvas.selection = true;\n didDisableSelection = false;\n }\n canvas.setCursor(getMode() === 'pan' ? 'grab' : 'default');\n }\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n canvas.on('mouse:up', handleMouseUp);\n\n return { handleMouseDown, handleMouseMove, handleMouseUp };\n}\n\n// --- Pinch-to-zoom ---\n\nfunction setupPinchZoom(\n canvas: FabricCanvas,\n bounds: ZoomBounds,\n isEnabled: () => boolean,\n): () => void {\n const canvasEl = canvas.getElement();\n if (typeof TouchEvent === 'undefined') return () => {};\n\n let lastPinchDist = 0;\n\n const onTouchStart = (e: TouchEvent) => {\n if (e.touches.length === 2) {\n e.preventDefault();\n lastPinchDist = touchDistance(e.touches);\n }\n };\n\n const onTouchMove = (e: TouchEvent) => {\n if (!isEnabled() || e.touches.length !== 2) return;\n e.preventDefault();\n\n const dist = touchDistance(e.touches);\n if (lastPinchDist === 0) {\n lastPinchDist = dist;\n return;\n }\n\n const ratio = dist / lastPinchDist;\n lastPinchDist = dist;\n\n const mid = touchMidpoint(e.touches, canvasEl);\n canvas.zoomToPoint(\n mid,\n Math.min(\n Math.max(canvas.getZoom() * ratio, bounds.minZoom),\n bounds.maxZoom,\n ),\n );\n };\n\n const onTouchEnd = (e: TouchEvent) => {\n if (e.touches.length < 2) lastPinchDist = 0;\n };\n\n canvasEl.addEventListener('touchstart', onTouchStart, { passive: false });\n canvasEl.addEventListener('touchmove', onTouchMove, { passive: false });\n canvasEl.addEventListener('touchend', onTouchEnd);\n\n return () => {\n canvasEl.removeEventListener('touchstart', onTouchStart);\n canvasEl.removeEventListener('touchmove', onTouchMove);\n canvasEl.removeEventListener('touchend', onTouchEnd);\n };\n}\n\n// --- Main function ---\n\n/**\n * Enable pan and zoom on the canvas viewport.\n *\n * - **Zoom**: Scroll the mouse wheel to zoom in/out centred on the cursor\n * (works in both modes). On touch devices, pinch with two fingers to zoom.\n * - **Select mode**: Normal object selection. Hold Cmd (Mac) / Ctrl (Win) and\n * drag to pan. Middle-button drag also pans.\n * - **Pan mode**: Click or single-touch drag to pan freely. Object selection\n * is disabled.\n *\n * Returns a {@link ViewportController} for switching modes, programmatic zoom,\n * and cleanup.\n */\nexport function enablePanAndZoom(\n canvas: FabricCanvas,\n options?: PanAndZoomOptions,\n): ViewportController {\n const bounds: ZoomBounds = {\n minZoom: options?.minZoom ?? DEFAULT_MIN_ZOOM,\n maxZoom: options?.maxZoom ?? DEFAULT_MAX_ZOOM,\n };\n const zoomFactor = options?.zoomFactor ?? DEFAULT_ZOOM_FACTOR;\n\n let mode: ViewportMode = options?.initialMode ?? 'select';\n let enabled = true;\n let currentAnimRafId: number | null = null;\n const isEnabled = () => enabled;\n const getMode = () => mode;\n\n function cancelAnimation() {\n if (currentAnimRafId !== null) {\n cancelAnimationFrame(currentAnimRafId);\n currentAnimRafId = null;\n }\n }\n\n const handleWheel = setupWheelZoom(canvas, bounds, zoomFactor, isEnabled);\n const panHandlers = setupMousePan(canvas, getMode, isEnabled);\n const cleanupPinch = setupPinchZoom(canvas, bounds, isEnabled);\n\n // --- Controller ---\n\n return {\n setMode(newMode: ViewportMode) {\n mode = newMode;\n if (newMode === 'pan') {\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n obj.evented = false;\n });\n canvas.discardActiveObject();\n canvas.setCursor('grab');\n } else {\n canvas.selection = true;\n canvas.forEachObject((obj) => {\n obj.selectable = true;\n obj.evented = true;\n });\n canvas.setCursor('default');\n }\n canvas.requestRenderAll();\n },\n\n getMode() {\n return mode;\n },\n\n setEnabled(value: boolean) {\n enabled = value;\n },\n\n zoomIn(factor = DEFAULT_ZOOM_STEP) {\n const z = Math.min(canvas.getZoom() * factor, bounds.maxZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n zoomOut(factor = DEFAULT_ZOOM_STEP) {\n const z = Math.max(canvas.getZoom() / factor, bounds.minZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n panToObject(object: FabricObject, panOpts?: PanToObjectOptions) {\n cancelAnimation();\n\n const zoom = canvas.getZoom();\n const objectCenter = object.getCenterPoint();\n const canvasCenterX = canvas.getWidth() / 2;\n const canvasCenterY = canvas.getHeight() / 2;\n\n const targetX = canvasCenterX - objectCenter.x * zoom;\n const targetY = canvasCenterY - objectCenter.y * zoom;\n\n if (!panOpts?.animate) {\n const viewportTransform = canvas.viewportTransform;\n if (!viewportTransform) return;\n canvas.setViewportTransform([\n viewportTransform[0],\n viewportTransform[1],\n viewportTransform[2],\n viewportTransform[3],\n targetX,\n targetY,\n ]);\n return;\n }\n\n const duration = panOpts.duration ?? 300;\n const viewportTransform = canvas.viewportTransform;\n if (!viewportTransform) return;\n const startX = viewportTransform[4];\n const startY = viewportTransform[5];\n const startTime = performance.now();\n\n function step(now: number) {\n const elapsed = now - startTime;\n const t = Math.min(elapsed / duration, 1);\n // Ease-out cubic\n const eased = 1 - Math.pow(1 - t, 3);\n\n const currentX = startX + (targetX - startX) * eased;\n const currentY = startY + (targetY - startY) * eased;\n\n const currentTransform = canvas.viewportTransform;\n if (!currentTransform) return;\n canvas.setViewportTransform([\n currentTransform[0],\n currentTransform[1],\n currentTransform[2],\n currentTransform[3],\n currentX,\n currentY,\n ]);\n\n if (t < 1) {\n currentAnimRafId = requestAnimationFrame(step);\n } else {\n currentAnimRafId = null;\n }\n }\n\n currentAnimRafId = requestAnimationFrame(step);\n },\n\n zoomToFit(object: FabricObject, fitOpts?: ZoomToFitOptions) {\n cancelAnimation();\n\n const padding = fitOpts?.padding ?? 0.1;\n const objWidth = (object.width ?? 0) * (object.scaleX ?? 1);\n const objHeight = (object.height ?? 0) * (object.scaleY ?? 1);\n if (!objWidth || !objHeight) return;\n\n const canvasWidth = canvas.getWidth();\n const canvasHeight = canvas.getHeight();\n const availableWidth = canvasWidth * (1 - padding * 2);\n const availableHeight = canvasHeight * (1 - padding * 2);\n\n const zoom = Math.min(\n Math.max(\n Math.min(availableWidth / objWidth, availableHeight / objHeight),\n bounds.minZoom,\n ),\n bounds.maxZoom,\n );\n\n const objectCenter = object.getCenterPoint();\n const offsetX = canvasWidth / 2 - objectCenter.x * zoom;\n const offsetY = canvasHeight / 2 - objectCenter.y * zoom;\n\n canvas.setViewportTransform([zoom, 0, 0, zoom, offsetX, offsetY]);\n },\n\n cleanup() {\n cancelAnimation();\n canvas.off('mouse:wheel', handleWheel);\n canvas.off('mouse:down', panHandlers.handleMouseDown);\n canvas.off('mouse:move', panHandlers.handleMouseMove);\n canvas.off('mouse:up', panHandlers.handleMouseUp);\n cleanupPinch();\n },\n };\n}\n\n/**\n * Reset the canvas viewport to the default (no pan, zoom = 1).\n */\nexport function resetViewport(canvas: FabricCanvas): void {\n canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);\n}\n","// --- Viewport ---\n\n/** Minimum zoom level. */\nexport const DEFAULT_MIN_ZOOM = 0.2;\n/** Maximum zoom level. */\nexport const DEFAULT_MAX_ZOOM = 10;\n/** Zoom sensitivity base — raised to the power of deltaY for proportional scroll zoom. */\nexport const DEFAULT_ZOOM_FACTOR = 0.999;\n/** Default multiplier for programmatic zoomIn/zoomOut (1.2 = 20% per click). */\nexport const DEFAULT_ZOOM_STEP = 1.2;\n/** Default viewport padding fraction (5% on each side). */\nexport const DEFAULT_VIEWPORT_PADDING = 0.05;\n\n// --- Alignment & snapping ---\n\n/** Reference canvas dimension for scaling snap margins proportionally. */\nexport const BASE_CANVAS_SIZE = 1000;\n/** Default snap margin in screen-pixel-equivalent units (shared by all snap systems). */\nexport const DEFAULT_SNAP_MARGIN = 6;\n/** Default angle snap interval in degrees. */\nexport const DEFAULT_ANGLE_SNAP_INTERVAL = 15;\n\n/**\n * Compute the effective snap margin in scene-space units.\n *\n * When `scaleWithCanvasSize` is true (default), the margin grows\n * proportionally with `max(canvasWidth, canvasHeight) / BASE_CANVAS_SIZE`\n * so snapping feels consistent on large canvases (e.g. floor plans).\n * The result is also divided by the current zoom level.\n */\nexport function computeSnapMargin(\n canvasWidth: number,\n canvasHeight: number,\n zoom: number,\n baseMargin: number = DEFAULT_SNAP_MARGIN,\n scaleWithCanvasSize: boolean = true,\n): number {\n const sizeScale = scaleWithCanvasSize\n ? Math.max(canvasWidth || 800, canvasHeight || 600) / BASE_CANVAS_SIZE\n : 1;\n return (baseMargin * sizeScale) / zoom;\n}\n\n// --- Interactions ---\n\n/**\n * Minimum drag distance (in scene units) before a drag is treated as intentional.\n * Prevents accidental micro-drags from creating objects.\n */\nexport const MIN_DRAG_SIZE = 3;\n\n/**\n * Distance (in scene units) from the first vertex at which a click closes the polygon.\n */\nexport const POLYGON_CLOSE_THRESHOLD = 10;\n\n// --- Background image ---\n\n/** Default max image dimension before downscale. */\nexport const DEFAULT_IMAGE_MAX_SIZE = 4096;\n/** Default min image dimension (images smaller on both axes are rejected). */\nexport const DEFAULT_IMAGE_MIN_SIZE = 480;\n\n// --- Vertex edit handles ---\n\nexport const DEFAULT_VERTEX_HANDLE_RADIUS = 6;\nexport const DEFAULT_VERTEX_HANDLE_FILL = '#ffffff';\nexport const DEFAULT_VERTEX_HANDLE_STROKE = '#2196f3';\nexport const DEFAULT_VERTEX_HANDLE_STROKE_WIDTH = 2;\n","import { type RefObject, useMemo } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject } from 'fabric';\nimport {\n resetViewport as resetViewportFn,\n type PanToObjectOptions,\n type ViewportController,\n type ZoomToFitOptions,\n} from '../viewport';\nimport { fitViewportToBackground } from '../background';\n\n/** Sync React zoom state with the canvas zoom level. */\nexport function syncZoom(\n canvasRef: RefObject<FabricCanvas | null>,\n setZoom: (z: number) => void,\n): void {\n const canvas = canvasRef.current;\n if (canvas) setZoom(canvas.getZoom());\n}\n\n/**\n * Create memoized viewport action callbacks shared between\n * useEditCanvas and useViewCanvas. The returned object is referentially\n * stable across renders.\n */\nexport function useViewportActions(\n canvasRef: RefObject<FabricCanvas | null>,\n viewportRef: RefObject<ViewportController | null>,\n setZoom: (z: number) => void,\n) {\n return useMemo(() => {\n const resetViewport = () => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n if (canvas.backgroundImage) {\n fitViewportToBackground(canvas);\n } else {\n resetViewportFn(canvas);\n }\n setZoom(canvas.getZoom());\n };\n\n const zoomIn = (step?: number) => {\n viewportRef.current?.zoomIn(step);\n syncZoom(canvasRef, setZoom);\n };\n\n const zoomOut = (step?: number) => {\n viewportRef.current?.zoomOut(step);\n syncZoom(canvasRef, setZoom);\n };\n\n const panToObject = (\n object: FabricObject,\n panOpts?: PanToObjectOptions,\n ) => {\n viewportRef.current?.panToObject(object, panOpts);\n };\n\n const zoomToFit = (object: FabricObject, fitOpts?: ZoomToFitOptions) => {\n viewportRef.current?.zoomToFit(object, fitOpts);\n syncZoom(canvasRef, setZoom);\n };\n\n return { resetViewport, zoomIn, zoomOut, panToObject, zoomToFit };\n // Refs are stable — this only runs once\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}\n\n/**\n * Resolve whether alignment should be enabled based on the master toggle\n * and the per-feature alignment prop.\n */\nexport function resolveAlignmentEnabled(\n enableAlignment: boolean | undefined,\n alignmentProp: boolean | object | undefined,\n): boolean {\n if (enableAlignment !== undefined) return enableAlignment;\n return alignmentProp !== false;\n}\n","import { Canvas as FabricCanvas, FabricImage, filters } from 'fabric';\nimport {\n DEFAULT_VIEWPORT_PADDING,\n DEFAULT_IMAGE_MAX_SIZE,\n DEFAULT_IMAGE_MIN_SIZE,\n} from './constants';\n\nfunction getBackgroundImage(canvas: FabricCanvas): FabricImage | undefined {\n return canvas.backgroundImage as FabricImage | undefined;\n}\n\n/**\n * Get the source URL of the canvas background image, or `null` if no\n * background image is set.\n */\nexport function getBackgroundSrc(canvas: FabricCanvas): string | null {\n const bg = getBackgroundImage(canvas);\n if (!bg) return null;\n return bg.getSrc() || null;\n}\n\n// --- Viewport fitting ---\n\nexport interface FitViewportOptions {\n /**\n * Fraction of the canvas to leave as empty margin around the image.\n * Default: 0.05 (5% on each side).\n */\n padding?: number;\n}\n\n/**\n * Adjust the canvas viewport transform so the background image fills the\n * canvas with an optional padding margin.\n *\n * Does nothing if no background image is set.\n */\nexport function fitViewportToBackground(\n canvas: FabricCanvas,\n options?: FitViewportOptions,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n\n // Use scaled dimensions so any scaleX/scaleY on the image is respected.\n const bgWidth = bg.getScaledWidth();\n const bgHeight = bg.getScaledHeight();\n if (!bgWidth || !bgHeight) return;\n\n // getCenterPoint() returns the geometric centre of the object in canvas space,\n // reliably regardless of how left/top are stored in this Fabric version.\n // The actual top-left corner is always centre − half-size.\n const center = bg.getCenterPoint();\n const imgLeft = center.x - bgWidth / 2;\n const imgTop = center.y - bgHeight / 2;\n\n const padding = options?.padding ?? DEFAULT_VIEWPORT_PADDING;\n const canvasWidth = canvas.getWidth();\n const canvasHeight = canvas.getHeight();\n const availableWidth = canvasWidth * (1 - padding * 2);\n const availableHeight = canvasHeight * (1 - padding * 2);\n\n const scale = Math.min(availableWidth / bgWidth, availableHeight / bgHeight);\n\n const scaledW = bgWidth * scale;\n const scaledH = bgHeight * scale;\n\n // Shift so the image's top-left maps to the padded centre of the viewport.\n const offsetX = (canvasWidth - scaledW) / 2 - imgLeft * scale;\n const offsetY = (canvasHeight - scaledH) / 2 - imgTop * scale;\n\n canvas.setViewportTransform([scale, 0, 0, scale, offsetX, offsetY]);\n canvas.requestRenderAll();\n}\n\n// --- Contrast ---\n\n/**\n * Set the contrast of the canvas background image.\n *\n * Value is expressed as a 0–2 scale:\n * - **0**: minimum contrast (flat grey).\n * - **1**: normal / unmodified (no filter applied).\n * - **2**: maximum contrast (darks are truly dark, lights truly light).\n *\n * Clamped to the 0–2 range.\n */\nexport function setBackgroundContrast(\n canvas: FabricCanvas,\n value: number,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n\n // Map 0–2 scale to Fabric Contrast range -1..+1 (0 = normal)\n const contrast = Math.min(Math.max(value, 0), 2) - 1;\n\n const currentFilters: filters.BaseFilter<string>[] = bg.filters ?? [];\n const contrastIdx = currentFilters.findIndex(\n (f) => f instanceof filters.Contrast,\n );\n\n if (contrast === 0) {\n // Normal — remove filter if present\n if (contrastIdx >= 0) {\n bg.filters = currentFilters.filter(\n (f) => !(f instanceof filters.Contrast),\n );\n bg.applyFilters();\n }\n } else if (contrastIdx >= 0) {\n (currentFilters[contrastIdx] as filters.Contrast).contrast = contrast;\n bg.applyFilters();\n } else {\n bg.filters = [...currentFilters, new filters.Contrast({ contrast })];\n bg.applyFilters();\n }\n\n canvas.requestRenderAll();\n}\n\n/**\n * Get the current contrast of the canvas background image.\n *\n * Returns a value in the 0–2 range where 1 is normal (no filter).\n * Returns 1 if no background image is set.\n */\nexport function getBackgroundContrast(canvas: FabricCanvas): number {\n const bg = getBackgroundImage(canvas);\n if (!bg) return 1;\n\n const contrastFilter = bg.filters?.find(\n (f) => f instanceof filters.Contrast,\n ) as filters.Contrast | undefined;\n\n // Map Fabric -1..+1 back to 0..2 scale\n return 1 + (contrastFilter?.contrast ?? 0);\n}\n\n// --- Invert filter ---\n\n/**\n * Add or remove the Invert filter from the canvas background image.\n */\nexport function setBackgroundInverted(\n canvas: FabricCanvas,\n inverted: boolean,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n\n const currentFilters: filters.BaseFilter<string>[] = bg.filters ?? [];\n const hasInvert = currentFilters.some((f) => f instanceof filters.Invert);\n\n if (inverted && !hasInvert) {\n bg.filters = [...currentFilters, new filters.Invert()];\n bg.applyFilters();\n } else if (!inverted && hasInvert) {\n bg.filters = currentFilters.filter((f) => !(f instanceof filters.Invert));\n bg.applyFilters();\n }\n\n canvas.requestRenderAll();\n}\n\n/**\n * Returns whether the canvas background image currently has an Invert filter.\n */\nexport function getBackgroundInverted(canvas: FabricCanvas): boolean {\n const bg = getBackgroundImage(canvas);\n if (!bg?.filters) return false;\n return bg.filters.some((f) => f instanceof filters.Invert);\n}\n\n// --- Image resizing ---\n\nexport interface ResizeResult {\n /** The (possibly new) image URL. Same as input URL if no resize was needed. */\n url: string;\n /** Final image width in pixels. */\n width: number;\n /** Final image height in pixels. */\n height: number;\n /** Whether the image was actually downscaled. */\n wasResized: boolean;\n}\n\nexport interface ResizeImageOptions {\n /**\n * Maximum dimension (width or height) in pixels.\n * Images larger than this are downscaled while maintaining aspect ratio.\n * Default: 4096.\n */\n maxSize?: number;\n /**\n * Minimum dimension (width or height) in pixels.\n * Images smaller than this are rejected with an error.\n * Default: 480.\n */\n minSize?: number;\n}\n\n/**\n * Load an image from a URL and downscale it if either dimension exceeds\n * `maxSize`. The resulting image is returned as a new blob URL (PNG).\n *\n * Rejects if the image fails to load, or if both dimensions are smaller\n * than `minSize`.\n *\n * @example\n * ```ts\n * const result = await resizeImageUrl(uploadedUrl, { maxSize: 4096 });\n * await canvas.setBackgroundImage(new FabricImage(result.url), canvas.renderAll.bind(canvas));\n * ```\n */\nexport function resizeImageUrl(\n url: string,\n options?: ResizeImageOptions,\n): Promise<ResizeResult> {\n const maxSize = options?.maxSize ?? DEFAULT_IMAGE_MAX_SIZE;\n const minSize = options?.minSize ?? DEFAULT_IMAGE_MIN_SIZE;\n\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n\n img.onload = () => {\n const { naturalWidth: w, naturalHeight: h } = img;\n\n if (w < minSize && h < minSize) {\n reject(\n new Error(\n `Image is too small (${w}×${h}). Minimum size is ${minSize}px on either dimension.`,\n ),\n );\n return;\n }\n\n const needsResize = w > maxSize || h > maxSize;\n if (!needsResize) {\n resolve({ url, width: w, height: h, wasResized: false });\n return;\n }\n\n const scale = Math.min(maxSize / w, maxSize / h);\n const newW = Math.round(w * scale);\n const newH = Math.round(h * scale);\n\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = newW;\n tempCanvas.height = newH;\n const ctx = tempCanvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Could not get 2D context for image resize.'));\n return;\n }\n ctx.drawImage(img, 0, 0, newW, newH);\n resolve({\n url: tempCanvas.toDataURL('image/png'),\n width: newW,\n height: newH,\n wasResized: true,\n });\n };\n\n img.onerror = () => reject(new Error(`Failed to load image: ${url}`));\n img.src = url;\n });\n}\n\n// --- Background image loading helper ---\n\nexport interface SetBackgroundImageOptions extends ResizeImageOptions {\n /** Preserve the current background contrast when replacing the image. */\n preserveContrast?: boolean;\n}\n\n/**\n * Set an image URL as the canvas background image.\n *\n * Pass options to control auto-resize (`maxSize`, `minSize`) and/or\n * preserve the current background contrast when replacing the image.\n * Omit to load the URL as-is without resizing.\n *\n * Returns the created FabricImage for further manipulation.\n */\nexport async function setBackgroundImage(\n canvas: FabricCanvas,\n url: string,\n options?: SetBackgroundImageOptions,\n): Promise<FabricImage> {\n const prevContrast = options?.preserveContrast\n ? getBackgroundContrast(canvas)\n : undefined;\n\n let imageUrl = url;\n const hasResizeOptions =\n options?.maxSize !== undefined || options?.minSize !== undefined;\n if (hasResizeOptions) {\n const result = await resizeImageUrl(url, options);\n imageUrl = result.url;\n }\n const img = await FabricImage.fromURL(imageUrl, { crossOrigin: 'anonymous' });\n\n // Position the image so its top-left corner sits at canvas origin (0, 0).\n // Fabric 7 defaults to center/center origin, so we set left/top to\n // half-dimensions. This ensures serializeCanvas produces left=0, top=0\n // with left/top origin, matching the old (Fabric 6) canvas data format.\n img.set({ left: img.width / 2, top: img.height / 2 });\n\n canvas.backgroundImage = img;\n\n if (prevContrast !== undefined && prevContrast !== 1) {\n setBackgroundContrast(canvas, prevContrast);\n }\n\n canvas.requestRenderAll();\n return img;\n}\n","import { type FabricObject, Point, Polygon, Rect, util } from 'fabric';\nimport { getStrokeFreeCoords } from './objectAlignmentUtils';\n\n/**\n * Extracts alignment-relevant snap points from a FabricObject.\n * All returned points must be in scene (world) coordinates.\n */\nexport type SnapPointExtractor = (object: FabricObject) => Point[];\n\ninterface SnapPointRegistration {\n matcher: (object: FabricObject) => boolean;\n extractor: SnapPointExtractor;\n}\n\nconst registry: SnapPointRegistration[] = [];\n\n/**\n * Register a snap point extractor for a shape type.\n * Matchers are checked in LIFO order (last registered wins),\n * allowing consumers to override built-in extractors.\n */\nexport function registerSnapPointExtractor(\n matcher: (object: FabricObject) => boolean,\n extractor: SnapPointExtractor,\n): void {\n registry.unshift({ matcher, extractor });\n}\n\n/**\n * Get snap target points for a FabricObject by finding the first matching\n * registered extractor. Falls back to bounding box corners + center.\n */\nexport function getSnapPoints(object: FabricObject): Point[] {\n for (const { matcher, extractor } of registry) {\n if (matcher(object)) {\n return extractor(object);\n }\n }\n return getDefaultSnapPoints(object);\n}\n\nfunction getDefaultSnapPoints(object: FabricObject): Point[] {\n const coords = getStrokeFreeCoords(object);\n return [...coords, object.getCenterPoint()];\n}\n\n// --- Built-in extractors ---\n\n// Rectangle: 4 rotation-aware corners + 4 edge midpoints + center\nregisterSnapPointExtractor(\n (obj) => obj instanceof Rect,\n (obj) => {\n const [tl, tr, br, bl] = getStrokeFreeCoords(obj);\n const mt = tl.add(tr).scalarDivide(2);\n const mr = tr.add(br).scalarDivide(2);\n const mb = br.add(bl).scalarDivide(2);\n const ml = bl.add(tl).scalarDivide(2);\n return [tl, tr, br, bl, mt, mr, mb, ml, obj.getCenterPoint()];\n },\n);\n\n// Polygon: all vertices transformed to scene space + center\nregisterSnapPointExtractor(\n (obj) => obj instanceof Polygon,\n (obj) => {\n const polygon = obj as Polygon;\n const matrix = polygon.calcTransformMatrix();\n const points: Point[] = polygon.points.map((pt) => {\n const local = new Point(\n pt.x - polygon.pathOffset.x,\n pt.y - polygon.pathOffset.y,\n );\n return util.transformPoint(local, matrix);\n });\n points.push(polygon.getCenterPoint());\n return points;\n },\n);\n","import {\n ActiveSelection,\n type BasicTransformEvent,\n type FabricObject,\n Group,\n Point,\n type TOriginX,\n type TOriginY,\n type TPointerEvent,\n} from 'fabric';\n\n/** A line segment between two alignment points, used for guideline drawing. */\nexport interface AlignmentLine {\n origin: Point;\n target: Point;\n}\n\nexport type TransformEvent = BasicTransformEvent<TPointerEvent> & {\n target: FabricObject;\n};\n\nexport type OriginMap = Record<string, [TOriginX, TOriginY]>;\n\n/**\n * Get the four bounding-box corners of a FabricObject in scene space using\n * only geometric dimensions (width × height through the transform matrix),\n * without stroke-width contribution. This ensures snap/alignment targets\n * are stable regardless of stroke scaling.\n */\nexport function getStrokeFreeCoords(\n obj: FabricObject,\n): [Point, Point, Point, Point] {\n const m = obj.calcTransformMatrix();\n const w = obj.width / 2;\n const h = obj.height / 2;\n return [\n new Point(-w, -h).transform(m), // tl\n new Point(w, -h).transform(m), // tr\n new Point(w, h).transform(m), // br\n new Point(-w, h).transform(m), // bl\n ];\n}\n\n/** Return the absolute distance between two values. */\nexport function getAbsoluteDistance(a: number, b: number): number {\n return Math.abs(a - b);\n}\n\n/**\n * Find the closest value on a given axis from a list of points.\n * Returns the minimum distance and all points at that distance.\n */\nexport function findNearestOnAxis(\n point: Point,\n list: Point[],\n axis: 'x' | 'y',\n): { distance: number; matches: Point[] } {\n let distance = Infinity;\n let matches: Point[] = [];\n for (const item of list) {\n const d = getAbsoluteDistance(point[axis], item[axis]);\n if (distance > d) {\n matches = [];\n distance = d;\n }\n if (distance === d) {\n matches.push(item);\n }\n }\n return { distance, matches };\n}\n\n/**\n * Get a map of bounding-box key points for an object:\n * corners (tl, tr, br, bl) and edge midpoints (mt, mr, mb, ml).\n */\nexport function getBoundingPointMap(\n target: FabricObject,\n): Record<string, Point> {\n const coords = getStrokeFreeCoords(target);\n return {\n tl: coords[0],\n tr: coords[1],\n br: coords[2],\n bl: coords[3],\n mt: coords[0].add(coords[1]).scalarDivide(2),\n mr: coords[1].add(coords[2]).scalarDivide(2),\n mb: coords[2].add(coords[3]).scalarDivide(2),\n ml: coords[3].add(coords[0]).scalarDivide(2),\n };\n}\n\n/**\n * Get a map of the opposite (diagonal) corners for each control point.\n * Used during scaling to anchor the opposite side.\n */\nexport function getOppositeCornerMap(\n target: FabricObject,\n): Record<string, Point> {\n const aCoords = target.aCoords ?? target.calcACoords();\n return {\n tl: aCoords.br,\n tr: aCoords.bl,\n br: aCoords.tl,\n bl: aCoords.tr,\n mt: aCoords.br.add(aCoords.bl).scalarDivide(2),\n mr: aCoords.bl.add(aCoords.tl).scalarDivide(2),\n mb: aCoords.tl.add(aCoords.tr).scalarDivide(2),\n ml: aCoords.tr.add(aCoords.br).scalarDivide(2),\n };\n}\n\n/**\n * Collect all canvas objects that should be considered as alignment targets\n * for a given moving/scaling object. Excludes the target itself and its children\n * (for groups/active selections). Flattens groups into leaf objects.\n */\nexport function getAlignmentTargets(target: FabricObject): Set<FabricObject> {\n const objects = new Set<FabricObject>();\n const canvas = target.canvas;\n if (!canvas) return objects;\n\n const children =\n target instanceof ActiveSelection ? target.getObjects() : [target];\n\n canvas.forEachObject((o) => {\n if (!o.isOnScreen() || !o.visible) return;\n if (o.constructor === Group) {\n collectGroupChildren(objects, o as Group);\n return;\n }\n objects.add(o);\n });\n\n for (const child of children) {\n if (child.constructor === Group) {\n for (const gc of (child as Group).getObjects()) objects.delete(gc);\n } else {\n objects.delete(child);\n }\n }\n\n return objects;\n}\n\n/** Recursively collect leaf objects from a group, skipping invisible children. */\nfunction collectGroupChildren(objects: Set<FabricObject>, group: Group): void {\n for (const child of group.getObjects()) {\n if (!child.visible) continue;\n if (child.constructor === Group) {\n collectGroupChildren(objects, child as Group);\n } else {\n objects.add(child);\n }\n }\n}\n","import { type Canvas, type FabricObject, Point, util } from 'fabric';\nimport { computeSnapMargin, DEFAULT_SNAP_MARGIN } from '../constants';\nimport { getSnapPoints } from './snapPoints';\nimport {\n type TransformEvent,\n type AlignmentLine,\n getAlignmentTargets,\n getBoundingPointMap,\n getOppositeCornerMap,\n} from './objectAlignmentUtils';\nimport {\n collectMovingAlignmentLines,\n collectVerticalSnapOffset,\n collectHorizontalSnapOffset,\n} from './objectAlignmentMath';\n\n// --- Alignment guideline rendering (inlined from objectAlignmentRendering) ---\n\ninterface AlignmentRenderConfig {\n canvas: Canvas;\n width: number;\n color: string;\n xSize: number;\n lineDash?: number[];\n}\n\nfunction drawXMarker(\n ctx: CanvasRenderingContext2D,\n point: Point,\n size: number,\n): void {\n ctx.save();\n ctx.translate(point.x, point.y);\n ctx.beginPath();\n ctx.moveTo(-size, -size);\n ctx.lineTo(size, size);\n ctx.moveTo(size, -size);\n ctx.lineTo(-size, size);\n ctx.stroke();\n ctx.restore();\n}\n\nfunction drawAlignmentLine(\n config: AlignmentRenderConfig,\n origin: Point,\n target: Point,\n): void {\n const ctx = config.canvas.getTopContext();\n const vt = config.canvas.viewportTransform;\n const zoom = config.canvas.getZoom();\n ctx.save();\n ctx.transform(...vt);\n ctx.lineWidth = config.width / zoom;\n if (config.lineDash) ctx.setLineDash(config.lineDash);\n ctx.strokeStyle = config.color;\n ctx.beginPath();\n ctx.moveTo(origin.x, origin.y);\n ctx.lineTo(target.x, target.y);\n ctx.stroke();\n if (config.lineDash) ctx.setLineDash([]);\n drawXMarker(ctx, origin, config.xSize / zoom);\n drawXMarker(ctx, target, config.xSize / zoom);\n ctx.restore();\n}\n\nfunction drawMarkerList(\n config: AlignmentRenderConfig,\n lines: AlignmentLine[],\n): void {\n const ctx = config.canvas.getTopContext();\n const vt = config.canvas.viewportTransform;\n const zoom = config.canvas.getZoom();\n const markerSize = config.xSize / zoom;\n ctx.save();\n ctx.transform(...vt);\n ctx.lineWidth = config.width / zoom;\n ctx.strokeStyle = config.color;\n for (const item of lines) drawXMarker(ctx, item.target, markerSize);\n ctx.restore();\n}\n\nfunction drawVerticalAlignmentLines(\n config: AlignmentRenderConfig,\n lines: Set<string>,\n): void {\n for (const v of lines) {\n const { origin, target }: AlignmentLine = JSON.parse(v);\n const from = { x: target.x, y: origin.y } as Point;\n drawAlignmentLine(config, from, target as Point);\n }\n}\n\nfunction drawHorizontalAlignmentLines(\n config: AlignmentRenderConfig,\n lines: Set<string>,\n): void {\n for (const h of lines) {\n const { origin, target }: AlignmentLine = JSON.parse(h);\n const from = { x: origin.x, y: target.y } as Point;\n drawAlignmentLine(config, from, target as Point);\n }\n}\n\n// --- Object alignment options & guides ---\n\nexport interface ObjectAlignmentOptions {\n /** At what distance from the shape does alignment begin? Default: 6. */\n margin?: number;\n /** Aligning line width. Default: 1. */\n width?: number;\n /** Aligning line color. Default: 'rgba(255,0,0,0.9)'. */\n color?: string;\n /** Endpoint marker size. Default: 2.4. */\n xSize?: number;\n /** Dashed line style. */\n lineDash?: number[];\n /**\n * Scale the snap margin proportionally with canvas size.\n * Uses `max(width, height) / 1000` as a multiplier so large canvases\n * (e.g. floor plans) get proportionally larger snap zones.\n * Default: `true`. Pass `false` to use a fixed margin regardless of size.\n */\n scaleWithCanvasSize?: boolean;\n}\n\n/** Adjust a corner string for flipped objects (e.g. 'tl' → 'tr' when flipX). */\nfunction adjustCornerForFlip(corner: string, target: FabricObject): string {\n let adjusted = corner;\n if (target.flipX) {\n if (adjusted.includes('l')) adjusted = adjusted.replace('l', 'r');\n else if (adjusted.includes('r')) adjusted = adjusted.replace('r', 'l');\n }\n if (target.flipY) {\n if (adjusted.includes('t')) adjusted = adjusted.replace('t', 'b');\n else if (adjusted.includes('b')) adjusted = adjusted.replace('b', 't');\n }\n return adjusted;\n}\n\n/**\n * Orchestrates object alignment guidelines on a Fabric canvas.\n * Listens to move/scale/resize events and delegates to math and rendering modules.\n */\nclass ObjectAlignmentGuides {\n private canvas: Canvas;\n private margin: number;\n private scaleWithCanvasSize: boolean;\n private renderConfig: AlignmentRenderConfig;\n\n private horizontalLines = new Set<string>();\n private verticalLines = new Set<string>();\n private cacheMap = new Map<string, Point[]>();\n private markersOnly = false;\n\n constructor(canvas: Canvas, opts?: ObjectAlignmentOptions) {\n this.canvas = canvas;\n this.margin = opts?.margin ?? DEFAULT_SNAP_MARGIN;\n this.scaleWithCanvasSize = opts?.scaleWithCanvasSize ?? true;\n this.renderConfig = {\n canvas,\n width: opts?.width ?? 1,\n color: opts?.color ?? 'rgba(255,0,0,0.9)',\n xSize: opts?.xSize ?? 2.4,\n lineDash: opts?.lineDash,\n };\n\n this.mouseUp = this.mouseUp.bind(this);\n this.onMoving = this.onMoving.bind(this);\n this.onScalingOrResizing = this.onScalingOrResizing.bind(this);\n this.beforeRender = this.beforeRender.bind(this);\n this.afterRender = this.afterRender.bind(this);\n\n canvas.on('mouse:up', this.mouseUp);\n canvas.on('object:moving', this.onMoving);\n canvas.on('object:scaling', this.onScalingOrResizing);\n canvas.on('object:resizing', this.onScalingOrResizing);\n canvas.on('before:render', this.beforeRender);\n canvas.on('after:render', this.afterRender);\n }\n\n dispose() {\n this.canvas.off('mouse:up', this.mouseUp);\n this.canvas.off('object:moving', this.onMoving);\n this.canvas.off('object:scaling', this.onScalingOrResizing);\n this.canvas.off('object:resizing', this.onScalingOrResizing);\n this.canvas.off('before:render', this.beforeRender);\n this.canvas.off('after:render', this.afterRender);\n }\n\n // --- Margin calculation ---\n\n private computeMargin(): number {\n return computeSnapMargin(\n this.canvas.width ?? 800,\n this.canvas.height ?? 600,\n this.canvas.getZoom(),\n this.margin,\n this.scaleWithCanvasSize,\n );\n }\n\n // --- Snap point caching ---\n\n private getCachedSnapPoints(object: FabricObject): Point[] {\n const cacheKey = [\n object.calcTransformMatrix().toString(),\n object.width,\n object.height,\n 'points' in object && Array.isArray(object.points)\n ? object.points.length\n : 0,\n ].join();\n\n const cached = this.cacheMap.get(cacheKey);\n if (cached) return cached;\n\n const value = getSnapPoints(object);\n this.cacheMap.set(cacheKey, value);\n return value;\n }\n\n private collectSnapPointsFromTargets(target: FabricObject): Point[] {\n const objects = getAlignmentTargets(target);\n const points: Point[] = [];\n for (const obj of objects) points.push(...this.getCachedSnapPoints(obj));\n return points;\n }\n\n // --- Event handlers ---\n\n private mouseUp() {\n this.verticalLines.clear();\n this.horizontalLines.clear();\n this.cacheMap.clear();\n this.canvas.requestRenderAll();\n }\n\n private onMoving(e: TransformEvent) {\n const target = e.target;\n target.setCoords();\n this.markersOnly = false;\n this.verticalLines.clear();\n this.horizontalLines.clear();\n\n const points = this.collectSnapPointsFromTargets(target);\n const margin = this.computeMargin();\n const { verticalLines, horizontalLines } = collectMovingAlignmentLines(\n target,\n points,\n margin,\n );\n for (const l of verticalLines) this.verticalLines.add(JSON.stringify(l));\n for (const l of horizontalLines)\n this.horizontalLines.add(JSON.stringify(l));\n }\n\n private onScalingOrResizing(e: TransformEvent) {\n const target = e.target;\n target.setCoords();\n const isScale = String(e.transform.action).startsWith('scale');\n this.verticalLines.clear();\n this.horizontalLines.clear();\n\n const corner = adjustCornerForFlip(e.transform.corner, target);\n\n const pointMap = getBoundingPointMap(target);\n if (!(corner in pointMap)) return;\n\n this.markersOnly = corner.includes('m');\n if (this.markersOnly) {\n if (target.getTotalAngle() % 90 !== 0) return;\n }\n\n const oppositeMap = getOppositeCornerMap(target);\n const point = pointMap[corner];\n let diagonalPoint = oppositeMap[corner];\n\n const isCenter =\n e.transform.original.originX === 'center' &&\n e.transform.original.originY === 'center';\n if (isCenter) {\n const p = target.group\n ? point.transform(\n util.invertTransform(target.group.calcTransformMatrix()),\n )\n : point;\n diagonalPoint = diagonalPoint.add(p).scalarDivide(2);\n }\n\n const uniformIsToggled = e.e[this.canvas.uniScaleKey!];\n let isUniform =\n (this.canvas.uniformScaling && !uniformIsToggled) ||\n (!this.canvas.uniformScaling && uniformIsToggled);\n if (this.markersOnly) isUniform = false;\n\n const allPoints = this.collectSnapPointsFromTargets(target);\n const margin = this.computeMargin();\n\n const props = {\n target,\n point,\n diagonalPoint,\n corner,\n list: allPoints,\n isScale,\n isUniform,\n isCenter,\n margin,\n };\n\n const skipVertical =\n this.markersOnly && (corner.includes('t') || corner.includes('b'));\n const skipHorizontal =\n this.markersOnly && (corner.includes('l') || corner.includes('r'));\n\n const vList = skipVertical ? [] : collectVerticalSnapOffset(props);\n // Vertical snap may have mutated scaleX (and scaleY for uniform scaling),\n // shifting the active corner's Y position. Refresh point so horizontal snap\n // computes its offset against the post-snap corner location, not the stale one.\n if (vList.length > 0) {\n const updatedPointMap = getBoundingPointMap(target);\n if (corner in updatedPointMap) props.point = updatedPointMap[corner];\n }\n const hList = skipHorizontal ? [] : collectHorizontalSnapOffset(props);\n\n for (const l of vList) this.verticalLines.add(JSON.stringify(l));\n for (const l of hList) this.horizontalLines.add(JSON.stringify(l));\n }\n\n // --- Render ---\n\n private beforeRender() {\n this.canvas.clearContext(this.canvas.contextTop);\n }\n\n private afterRender() {\n if (this.markersOnly) {\n const lines: AlignmentLine[] = [];\n for (const v of this.verticalLines)\n lines.push(JSON.parse(v) as AlignmentLine);\n for (const h of this.horizontalLines)\n lines.push(JSON.parse(h) as AlignmentLine);\n drawMarkerList(this.renderConfig, lines);\n } else {\n drawVerticalAlignmentLines(this.renderConfig, this.verticalLines);\n drawHorizontalAlignmentLines(this.renderConfig, this.horizontalLines);\n }\n }\n}\n\n/**\n * Enable object alignment guidelines on a canvas.\n * Draws visual guidelines and snaps objects during movement and scaling.\n * Returns a dispose function for cleanup.\n */\nexport function enableObjectAlignment(\n canvas: Canvas,\n options?: ObjectAlignmentOptions,\n): () => void {\n const alignment = new ObjectAlignmentGuides(canvas, options);\n return () => alignment.dispose();\n}\n","import { type FabricObject, Point } from 'fabric';\nimport {\n type AlignmentLine,\n type OriginMap,\n findNearestOnAxis,\n getStrokeFreeCoords,\n} from './objectAlignmentUtils';\n\n/** Maps each corner/midpoint to the opposite origin for anchoring during scale. */\nexport const OPPOSITE_ORIGIN_MAP: OriginMap = {\n tl: ['right', 'bottom'],\n tr: ['left', 'bottom'],\n br: ['left', 'top'],\n bl: ['right', 'top'],\n mt: ['center', 'bottom'],\n mr: ['left', 'center'],\n mb: ['center', 'top'],\n ml: ['right', 'center'],\n};\n\n// --- Moving alignment ---\n\n/**\n * Collect alignment lines when an object is being moved.\n * Checks the target's corners + center against all snap points.\n */\nexport function collectMovingAlignmentLines(\n target: FabricObject,\n points: Point[],\n margin: number,\n): { verticalLines: AlignmentLine[]; horizontalLines: AlignmentLine[] } {\n const list: Point[] = [...getStrokeFreeCoords(target)];\n list.push(target.getCenterPoint());\n const opts = { target, list, points, margin };\n const verticalLines = collectMovingAxisMatches({ ...opts, axis: 'x' });\n const horizontalLines = collectMovingAxisMatches({ ...opts, axis: 'y' });\n return { verticalLines, horizontalLines };\n}\n\n/**\n * For a single axis, find the closest matching snap points for the target's\n * bounding points, snap the object to align, and return the alignment lines.\n */\nfunction collectMovingAxisMatches(props: {\n target: FabricObject;\n list: Point[];\n points: Point[];\n margin: number;\n axis: 'x' | 'y';\n}): AlignmentLine[] {\n const { target, list, points, margin, axis } = props;\n const result: AlignmentLine[] = [];\n const distances: ReturnType<typeof findNearestOnAxis>[] = [];\n let min = Infinity;\n\n for (const item of list) {\n const nearest = findNearestOnAxis(item, points, axis);\n distances.push(nearest);\n if (min > nearest.distance) min = nearest.distance;\n }\n if (min > margin) return result;\n\n let snapped = false;\n for (let i = 0; i < list.length; i++) {\n if (distances[i].distance !== min) continue;\n for (const item of distances[i].matches) {\n result.push({ origin: list[i], target: item });\n }\n\n if (snapped) continue;\n snapped = true;\n const snapOffset = distances[i].matches[0][axis] - list[i][axis];\n list.forEach((item) => {\n item[axis] += snapOffset;\n });\n // Use center-based positioning to avoid stroke/origin offset issues.\n // The center is stroke-independent, so shifting it by snapOffset\n // moves the geometric edges by exactly that amount.\n const center = target.getCenterPoint();\n center[axis] += snapOffset;\n target.setXY(center, 'center', 'center');\n target.setCoords();\n }\n\n return result;\n}\n\n// --- Scaling alignment ---\n\ninterface ScalingAlignmentProps {\n target: FabricObject;\n point: Point;\n diagonalPoint: Point;\n list: Point[];\n isScale: boolean;\n isUniform: boolean;\n isCenter: boolean;\n corner: string;\n margin: number;\n}\n\n/**\n * Collect vertical (X-axis) snap offset during scaling/resizing.\n * Adjusts the target's scaleX (or width) to align with the nearest snap point.\n */\nexport function collectVerticalSnapOffset(\n props: ScalingAlignmentProps,\n): AlignmentLine[] {\n const {\n target,\n isScale,\n isUniform,\n corner,\n point,\n diagonalPoint,\n list,\n isCenter,\n margin,\n } = props;\n const { distance, matches } = findNearestOnAxis(point, list, 'x');\n if (distance > margin) return [];\n\n let snapOffset = matches[matches.length - 1].x - point.x;\n const dirX = corner.includes('l') ? -1 : 1;\n snapOffset *= dirX;\n\n const { width, height, scaleX, scaleY } = target;\n const scaleWidth = scaleX * width;\n const sx = (snapOffset + scaleWidth) / scaleWidth;\n if (sx === 0) return [];\n\n if (isScale) {\n target.set('scaleX', scaleX * sx);\n if (isUniform) target.set('scaleY', scaleY * sx);\n } else {\n target.set('width', width * sx);\n if (isUniform) target.set('height', height * sx);\n }\n\n if (isCenter) {\n target.setRelativeXY(diagonalPoint, 'center', 'center');\n } else {\n target.setRelativeXY(diagonalPoint, ...OPPOSITE_ORIGIN_MAP[corner]);\n }\n target.setCoords();\n\n return matches.map((t) => ({ origin: point, target: t }));\n}\n\n/**\n * Collect horizontal (Y-axis) snap offset during scaling/resizing.\n * Adjusts the target's scaleY (or height) to align with the nearest snap point.\n */\nexport function collectHorizontalSnapOffset(\n props: ScalingAlignmentProps,\n): AlignmentLine[] {\n const {\n target,\n isScale,\n isUniform,\n corner,\n point,\n diagonalPoint,\n list,\n isCenter,\n margin,\n } = props;\n const { distance, matches } = findNearestOnAxis(point, list, 'y');\n if (distance > margin) return [];\n\n let snapOffset = matches[matches.length - 1].y - point.y;\n const dirY = corner.includes('t') ? -1 : 1;\n snapOffset *= dirY;\n\n const { width, height, scaleX, scaleY } = target;\n const scaleHeight = scaleY * height;\n const sy = (snapOffset + scaleHeight) / scaleHeight;\n if (sy === 0) return [];\n\n if (isScale) {\n target.set('scaleY', scaleY * sy);\n if (isUniform) target.set('scaleX', scaleX * sy);\n } else {\n target.set('height', height * sy);\n if (isUniform) target.set('width', width * sy);\n }\n\n if (isCenter) {\n target.setRelativeXY(diagonalPoint, 'center', 'center');\n } else {\n target.setRelativeXY(diagonalPoint, ...OPPOSITE_ORIGIN_MAP[corner]);\n }\n target.setCoords();\n\n return matches.map((t) => ({ origin: point, target: t }));\n}\n","import type { Canvas } from 'fabric';\nimport type { TransformEvent } from './objectAlignmentUtils';\nimport { DEFAULT_ANGLE_SNAP_INTERVAL } from '../constants';\n\nexport interface RotationSnapOptions {\n /**\n * Snap angle to multiples of this interval (degrees) when Shift is held.\n * Default: 15.\n */\n interval?: number;\n}\n\nfunction snapToInterval(angle: number, interval: number): number {\n return Math.round(angle / interval) * interval;\n}\n\n/**\n * Enable rotation snapping on a Fabric canvas.\n * When Shift is held while rotating an object via its rotation handle,\n * the angle snaps to the nearest multiple of `interval` degrees (default: 15°).\n * Returns a cleanup function.\n */\nexport function enableRotationSnap(\n canvas: Canvas,\n options?: RotationSnapOptions,\n): () => void {\n const interval = options?.interval ?? DEFAULT_ANGLE_SNAP_INTERVAL;\n\n const onRotating = (e: TransformEvent) => {\n if (!('shiftKey' in e.e) || !e.e.shiftKey) return;\n e.target.angle = snapToInterval(e.target.angle as number, interval);\n };\n\n canvas.on('object:rotating', onRotating);\n return () => canvas.off('object:rotating', onRotating);\n}\n","import { Canvas as FabricCanvas, type FabricObject, Point } from 'fabric';\nimport { getSnapPoints } from './snapPoints';\nimport { computeSnapMargin, DEFAULT_SNAP_MARGIN } from '../constants';\n\nexport interface CursorSnapResult {\n /** The snapped point (or original if no snap occurred). */\n point: Point;\n /** Whether a snap occurred on either axis. */\n snapped: boolean;\n /** Whether the X coordinate was snapped. */\n snapX: boolean;\n /** Whether the Y coordinate was snapped. */\n snapY: boolean;\n /** All target points that triggered the X-axis snap (for drawing guidelines). */\n alignTargetsX?: Point[];\n /** All target points that triggered the Y-axis snap (for drawing guidelines). */\n alignTargetsY?: Point[];\n}\n\nexport interface CursorSnapOptions {\n /**\n * Base snap margin in screen-pixel-equivalent units. Default: 6.\n * The effective scene-space margin is `margin * sizeScale / zoom`, where\n * `sizeScale` grows proportionally with canvas size so that snapping feels\n * consistent regardless of how large the canvas coordinate space is.\n */\n margin?: number;\n /** Objects to exclude from snap targets (e.g., preview elements). */\n exclude?: Set<FabricObject>;\n /** Pre-computed target points. If provided, skips object iteration. */\n targetPoints?: Point[];\n /**\n * Scale the snap margin proportionally with canvas size.\n * Uses `max(width, height) / 1000` as a multiplier so large canvases\n * (e.g. floor plans) get proportionally larger snap zones.\n * Default: `true`. Pass `false` to use a fixed margin regardless of size.\n */\n scaleWithCanvasSize?: boolean;\n}\n\nexport interface GuidelineStyle {\n color?: string;\n width?: number;\n xSize?: number;\n}\n\n/**\n * Compute a snapped cursor position given a raw scene-space point.\n * Finds the closest X and closest Y alignment independently,\n * snapping each axis if within margin.\n */\nexport function snapCursorPoint(\n canvas: FabricCanvas,\n rawPoint: Point,\n options?: CursorSnapOptions,\n): CursorSnapResult {\n const margin = computeSnapMargin(\n canvas.width ?? 800,\n canvas.height ?? 600,\n canvas.getZoom(),\n options?.margin ?? DEFAULT_SNAP_MARGIN,\n options?.scaleWithCanvasSize !== false,\n );\n const exclude = options?.exclude ?? new Set();\n\n let targetPoints: Point[];\n if (options?.targetPoints) {\n targetPoints = options.targetPoints;\n } else {\n targetPoints = [];\n canvas.forEachObject((obj) => {\n if (!obj.visible || !obj.isOnScreen()) return;\n if (exclude.has(obj)) return;\n targetPoints.push(...getSnapPoints(obj));\n });\n }\n\n let bestDx = Infinity;\n let bestDy = Infinity;\n let snapTargetsX: Point[] = [];\n let snapTargetsY: Point[] = [];\n\n for (const tp of targetPoints) {\n const dx = Math.abs(rawPoint.x - tp.x);\n const dy = Math.abs(rawPoint.y - tp.y);\n if (dx < bestDx) {\n bestDx = dx;\n snapTargetsX = [];\n }\n if (dx === bestDx) {\n snapTargetsX.push(tp);\n }\n if (dy < bestDy) {\n bestDy = dy;\n snapTargetsY = [];\n }\n if (dy === bestDy) {\n snapTargetsY.push(tp);\n }\n }\n\n const snapX = bestDx <= margin && snapTargetsX.length > 0;\n const snapY = bestDy <= margin && snapTargetsY.length > 0;\n\n return {\n point: new Point(\n snapX ? snapTargetsX[0].x : rawPoint.x,\n snapY ? snapTargetsY[0].y : rawPoint.y,\n ),\n snapped: snapX || snapY,\n snapX,\n snapY,\n alignTargetsX: snapX ? snapTargetsX : undefined,\n alignTargetsY: snapY ? snapTargetsY : undefined,\n };\n}\n\n/**\n * Draw alignment guidelines on the canvas top context based on a snap result.\n * Renders lines from the snapped point to the alignment target points,\n * with X markers at both endpoints.\n */\nexport function drawCursorGuidelines(\n canvas: FabricCanvas,\n snapResult: CursorSnapResult,\n style?: GuidelineStyle,\n): void {\n if (!snapResult.snapped) return;\n\n const ctx = canvas.getTopContext();\n const vt = canvas.viewportTransform;\n const zoom = canvas.getZoom();\n const color = style?.color ?? 'rgba(255,0,0,0.9)';\n const width = style?.width ?? 1;\n const xSize = (style?.xSize ?? 2.4) / zoom;\n\n ctx.save();\n ctx.transform(vt[0], vt[1], vt[2], vt[3], vt[4], vt[5]);\n ctx.lineWidth = width / zoom;\n ctx.strokeStyle = color;\n\n if (snapResult.snapX && snapResult.alignTargetsX) {\n const to = snapResult.point;\n for (const from of snapResult.alignTargetsX) {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n drawXMarker(ctx, from, xSize);\n }\n drawXMarker(ctx, new Point(to.x, to.y), xSize);\n }\n\n if (snapResult.snapY && snapResult.alignTargetsY) {\n const to = snapResult.point;\n for (const from of snapResult.alignTargetsY) {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n drawXMarker(ctx, from, xSize);\n }\n drawXMarker(ctx, new Point(to.x, to.y), xSize);\n }\n\n ctx.restore();\n}\n\nfunction drawXMarker(\n ctx: CanvasRenderingContext2D,\n point: Point,\n size: number,\n): void {\n ctx.beginPath();\n ctx.moveTo(point.x - size, point.y - size);\n ctx.lineTo(point.x + size, point.y + size);\n ctx.moveTo(point.x + size, point.y - size);\n ctx.lineTo(point.x - size, point.y + size);\n ctx.stroke();\n}\n\n/** Clear the canvas top context to remove previous guidelines. */\nexport function clearCursorGuidelines(canvas: FabricCanvas): void {\n canvas.clearContext(canvas.getTopContext());\n}\n","import type { ViewportController } from '../viewport';\n\n/** Restore the viewport to select mode if a controller was provided. */\nexport function restoreViewport(viewport?: ViewportController) {\n if (!viewport) return;\n viewport.setEnabled(true);\n viewport.setMode('select');\n}\n\n/**\n * Track whether the Shift key is currently held.\n * Optionally calls `onChange` whenever the state changes (useful for\n * triggering preview updates while a drag is in progress).\n */\nexport function createShiftKeyTracker(onChange?: (held: boolean) => void): {\n readonly held: boolean;\n cleanup: () => void;\n} {\n let shiftHeld = false;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Shift' && !shiftHeld) {\n shiftHeld = true;\n onChange?.(true);\n }\n };\n const onKeyUp = (e: KeyboardEvent) => {\n if (e.key === 'Shift' && shiftHeld) {\n shiftHeld = false;\n onChange?.(false);\n }\n };\n document.addEventListener('keydown', onKeyDown);\n document.addEventListener('keyup', onKeyUp);\n\n return {\n get held() {\n return shiftHeld;\n },\n cleanup() {\n document.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('keyup', onKeyUp);\n },\n };\n}\n","import { Canvas as FabricCanvas, FabricObject } from 'fabric';\nimport type { Point2D, InteractionModeOptions } from '../types';\nimport { restoreViewport } from './shared';\n\n/**\n * Enable click-to-create mode.\n * Each click calls the factory with the canvas and the click point.\n * The factory creates and adds the object to the canvas, returning it.\n * Returns a cleanup function that disables the mode.\n */\nexport function enableClickToCreate(\n canvas: FabricCanvas,\n factory: (canvas: FabricCanvas, point: Point2D) => FabricObject,\n options?: InteractionModeOptions,\n): () => void {\n options?.viewport?.setEnabled(false);\n\n const handleMouseDown = (event: { scenePoint: Point2D }) => {\n const obj = factory(canvas, event.scenePoint);\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n };\n\n canvas.on('mouse:down', handleMouseDown);\n\n return () => {\n canvas.off('mouse:down', handleMouseDown);\n restoreViewport(options?.viewport);\n };\n}\n","import { Canvas as FabricCanvas, FabricObject, Rect } from 'fabric';\nimport type {\n DragBounds,\n Point2D,\n ShapeStyleOptions,\n SnappableInteractionOptions,\n} from '../types';\nimport { DEFAULT_GUIDELINE_SHAPE_STYLE } from '../styles';\nimport { MIN_DRAG_SIZE } from '../constants';\nimport { restoreViewport, createShiftKeyTracker } from './shared';\nimport { createInteractionSnapping } from './interactionSnapping';\n\nexport interface DragToCreateOptions extends SnappableInteractionOptions {\n /** Style applied to the preview rectangle shown during drag. */\n previewStyle?: ShapeStyleOptions & { rx?: number; ry?: number };\n /** When true, constrain the drag to a 1:1 aspect ratio (square). */\n constrainToSquare?: boolean;\n /**\n * Factory called when the user clicks without dragging (below MIN_DRAG_SIZE).\n * Receives the canvas and the click point. If not provided, clicks are ignored.\n */\n clickFactory?: (canvas: FabricCanvas, point: Point2D) => FabricObject;\n /** Called when the user cancels the drag via Escape. */\n onCancel?: () => void;\n}\n\n/**\n * Enable drag-to-create mode.\n * A preview rectangle is shown while dragging. On mouse-up the factory\n * receives the drag bounds and creates the final object.\n * Holding Shift during drag constrains the shape to a 1:1 aspect ratio.\n * Returns a cleanup function that disables the mode.\n */\nexport function enableDragToCreate(\n canvas: FabricCanvas,\n factory: (canvas: FabricCanvas, bounds: DragBounds) => FabricObject,\n options?: DragToCreateOptions,\n): () => void {\n let isDrawing = false;\n let startX = 0;\n let startY = 0;\n let lastEndX = 0;\n let lastEndY = 0;\n let previewRect: Rect | null = null;\n let previousSelection: boolean;\n\n const snapping = createInteractionSnapping(canvas, options);\n const shiftTracker = createShiftKeyTracker(() => {\n updatePreview(lastEndX, lastEndY);\n });\n\n options?.viewport?.setEnabled(false);\n\n const shouldConstrain = () =>\n !!(options?.constrainToSquare || shiftTracker.held);\n\n const computeDimensions = (\n endX: number,\n endY: number,\n ): { width: number; height: number } => {\n let width = Math.max(0, endX - startX);\n let height = Math.max(0, endY - startY);\n if (shouldConstrain()) {\n const size = Math.max(width, height);\n width = size;\n height = size;\n }\n return { width, height };\n };\n\n const updatePreview = (endX: number, endY: number) => {\n lastEndX = endX;\n lastEndY = endY;\n if (!isDrawing || !previewRect) return;\n\n const { width, height } = computeDimensions(endX, endY);\n previewRect.set({\n left: startX + width / 2,\n top: startY + height / 2,\n width,\n height,\n });\n previewRect.setCoords();\n canvas.requestRenderAll();\n };\n\n const handleMouseDown = (event: { scenePoint: Point2D }) => {\n isDrawing = true;\n\n const snapped = snapping.snap(event.scenePoint.x, event.scenePoint.y);\n startX = snapped.x;\n startY = snapped.y;\n lastEndX = startX;\n lastEndY = startY;\n\n previousSelection = canvas.selection;\n canvas.selection = false;\n\n previewRect = new Rect({\n ...DEFAULT_GUIDELINE_SHAPE_STYLE,\n left: startX,\n top: startY,\n width: 0,\n height: 0,\n ...options?.previewStyle,\n selectable: false,\n evented: false,\n });\n snapping.excludeSet.add(previewRect);\n canvas.add(previewRect);\n };\n\n const handleMouseMove = (event: { scenePoint: Point2D }) => {\n if (!isDrawing || !previewRect) return;\n\n const { x: endX, y: endY } = snapping.snapWithGuidelines(\n event.scenePoint.x,\n event.scenePoint.y,\n );\n updatePreview(endX, endY);\n };\n\n const handleMouseUp = () => {\n if (!isDrawing || !previewRect) return;\n\n isDrawing = false;\n snapping.clearSnapResult();\n canvas.selection = previousSelection;\n\n const { width, height } = computeDimensions(lastEndX, lastEndY);\n\n snapping.excludeSet.delete(previewRect);\n canvas.remove(previewRect);\n\n if (width < MIN_DRAG_SIZE && height < MIN_DRAG_SIZE) {\n canvas.requestRenderAll();\n previewRect = null;\n\n if (options?.clickFactory) {\n const obj = options.clickFactory(canvas, { x: startX, y: startY });\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n }\n return;\n }\n\n const obj = factory(canvas, { startX, startY, width, height });\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n previewRect = null;\n };\n\n // Cancel drag on Escape (capture phase to prevent other handlers)\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopImmediatePropagation();\n e.preventDefault();\n cleanup('cancel');\n }\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n canvas.on('mouse:up', handleMouseUp);\n document.addEventListener('keydown', handleKeyDown, true);\n\n let exited = false;\n\n function cleanup(reason?: 'cancel') {\n if (exited) return;\n exited = true;\n\n canvas.off('mouse:down', handleMouseDown);\n canvas.off('mouse:move', handleMouseMove);\n canvas.off('mouse:up', handleMouseUp);\n document.removeEventListener('keydown', handleKeyDown, true);\n shiftTracker.cleanup();\n\n snapping.cleanup();\n\n if (isDrawing && previewRect) {\n snapping.excludeSet.delete(previewRect);\n canvas.remove(previewRect);\n canvas.selection = previousSelection;\n canvas.requestRenderAll();\n }\n restoreViewport(options?.viewport);\n\n if (reason === 'cancel') {\n options?.onCancel?.();\n }\n }\n\n return () => cleanup();\n}\n","import { alpha } from '@mui/material/styles';\nimport { biampTheme } from '@bwp-web/styles';\n\nconst { palette } = biampTheme();\n\n/** Selection handle appearance (border, corner color). Applied to all objects. */\nexport const DEFAULT_CONTROL_STYLE = {\n borderColor: palette.info.main,\n cornerColor: palette.info.main,\n cornerStrokeColor: palette.info.main,\n transparentCorners: true,\n};\n\n/** Default fill/stroke for rectangles and polygons. */\nexport const DEFAULT_SHAPE_STYLE = {\n fill: alpha(palette.info.main, 0.3),\n stroke: palette.info.main,\n strokeWidth: 2.5,\n strokeUniform: true,\n ...DEFAULT_CONTROL_STYLE,\n};\n\n/** Default fill for circles (Rects with full border-radius). */\nexport const DEFAULT_CIRCLE_STYLE = {\n fill: palette.info.main,\n stroke: palette.info.main,\n strokeWidth: 2.5,\n strokeUniform: true,\n ...DEFAULT_CONTROL_STYLE,\n};\n\n/** Preview rectangle shown during drag-to-create (dashed stroke). */\nexport const DEFAULT_DRAG_SHAPE_STYLE = {\n fill: alpha(palette.info.main, 0.1),\n stroke: palette.info.main,\n strokeWidth: 2.5,\n strokeUniform: true,\n strokeDashArray: [5, 5],\n};\n\n/** Preview polygon shown during draw-to-create (semi-transparent dashed stroke). */\nexport const DEFAULT_GUIDELINE_SHAPE_STYLE = {\n fill: alpha(palette.info.main, 0.1),\n stroke: alpha(palette.info.main, 0.5),\n strokeWidth: 2.5,\n strokeUniform: true,\n strokeDashArray: [5, 5],\n};\n\n/** Alignment guideline appearance (color, width, x-marker size). */\nexport const DEFAULT_ALIGNMENT_STYLE = {\n color: 'rgba(255, 0, 0, 0.9)',\n width: 1,\n xSize: 2.4,\n} as const;\n","import { Canvas as FabricCanvas, FabricObject, Point } from 'fabric';\nimport type { SnappableInteractionOptions } from '../types';\nimport {\n snapCursorPoint,\n getSnapPoints,\n drawCursorGuidelines,\n type CursorSnapResult,\n type GuidelineStyle,\n} from '../alignment';\n\n/**\n * Canvas-level alignment state.\n * Allows `useEditCanvas` to set the master alignment toggle once so that all\n * interaction modes created via `setMode` inherit it automatically.\n */\nconst canvasAlignmentState = new WeakMap<FabricCanvas, boolean | undefined>();\n\n/** Set the canvas-level alignment toggle (called by useEditCanvas). */\nexport function setCanvasAlignmentEnabled(\n canvas: FabricCanvas,\n enabled?: boolean,\n): void {\n canvasAlignmentState.set(canvas, enabled);\n}\n\n/** Context object for managing snapping within an interaction mode. */\nexport interface InteractionSnappingContext {\n /** Whether snapping is enabled. */\n enabled: boolean;\n /** Snap a raw point, returning the snapped coordinates. */\n snap(rawX: number, rawY: number): { x: number; y: number };\n /** Snap a raw point and store the result for guideline rendering on next frame. */\n snapWithGuidelines(rawX: number, rawY: number): { x: number; y: number };\n /** Clear the stored snap result (call when finalizing or on mouse-up). */\n clearSnapResult(): void;\n /** Objects to exclude from snap targets (e.g. preview elements). */\n excludeSet: Set<FabricObject>;\n /** Remove all event listeners and clear state. */\n cleanup(): void;\n}\n\n/**\n * Create a snapping context for an interaction mode.\n * Handles target point caching, snap calculations, and guideline rendering.\n *\n * @param getAdditionalTargets Optional callback that returns extra snap targets\n * (e.g. placed polygon vertices for self-alignment in drawToCreate).\n */\nexport function createInteractionSnapping(\n canvas: FabricCanvas,\n options?: Pick<SnappableInteractionOptions, 'snapping' | 'enableAlignment'>,\n getAdditionalTargets?: () => Point[],\n): InteractionSnappingContext {\n const canvasAlignment = canvasAlignmentState.get(canvas);\n const snapEnabled =\n options?.enableAlignment !== undefined\n ? options.enableAlignment\n : canvasAlignment !== undefined\n ? canvasAlignment\n : options?.snapping !== false;\n\n const snapMargin =\n typeof options?.snapping === 'object' ? options.snapping.margin : undefined;\n const guidelineStyle: GuidelineStyle | undefined =\n typeof options?.snapping === 'object'\n ? options.snapping.guidelineStyle\n : undefined;\n\n const excludeSet = new Set<FabricObject>();\n let cachedTargetPoints: Point[] | null = null;\n let lastSnapResult: CursorSnapResult | null = null;\n\n function getTargetPoints(): Point[] {\n if (cachedTargetPoints) return cachedTargetPoints;\n cachedTargetPoints = [];\n canvas.forEachObject((obj) => {\n if (!obj.visible) return;\n if (excludeSet.has(obj)) return;\n cachedTargetPoints!.push(...getSnapPoints(obj));\n });\n return cachedTargetPoints;\n }\n\n function getAllTargetPoints(): Point[] {\n const base = getTargetPoints();\n if (!getAdditionalTargets) return base;\n const additional = getAdditionalTargets();\n return additional.length > 0 ? [...base, ...additional] : base;\n }\n\n const invalidateCache = () => {\n cachedTargetPoints = null;\n };\n\n const beforeRender = () => {\n canvas.clearContext(canvas.getTopContext());\n };\n\n const afterRender = () => {\n if (lastSnapResult) {\n drawCursorGuidelines(canvas, lastSnapResult, guidelineStyle);\n }\n };\n\n if (snapEnabled) {\n canvas.on('object:added', invalidateCache);\n canvas.on('object:removed', invalidateCache);\n canvas.on('before:render', beforeRender);\n canvas.on('after:render', afterRender);\n }\n\n function snap(rawX: number, rawY: number): { x: number; y: number } {\n if (!snapEnabled) return { x: rawX, y: rawY };\n\n const result = snapCursorPoint(canvas, new Point(rawX, rawY), {\n margin: snapMargin,\n exclude: excludeSet,\n targetPoints: getAllTargetPoints(),\n });\n return { x: result.point.x, y: result.point.y };\n }\n\n function snapWithGuidelines(\n rawX: number,\n rawY: number,\n ): { x: number; y: number } {\n if (!snapEnabled) {\n lastSnapResult = null;\n return { x: rawX, y: rawY };\n }\n\n lastSnapResult = snapCursorPoint(canvas, new Point(rawX, rawY), {\n margin: snapMargin,\n exclude: excludeSet,\n targetPoints: getAllTargetPoints(),\n });\n return { x: lastSnapResult.point.x, y: lastSnapResult.point.y };\n }\n\n return {\n enabled: snapEnabled,\n snap,\n snapWithGuidelines,\n clearSnapResult() {\n lastSnapResult = null;\n },\n excludeSet,\n cleanup() {\n if (snapEnabled) {\n canvas.off('object:added', invalidateCache);\n canvas.off('object:removed', invalidateCache);\n canvas.off('before:render', beforeRender);\n canvas.off('after:render', afterRender);\n // Clear any lingering guideline drawings since the before:render\n // handler that would normally clear them has been removed.\n canvas.clearContext(canvas.getTopContext());\n }\n lastSnapResult = null;\n },\n };\n}\n","import {\n Canvas as FabricCanvas,\n Circle,\n FabricObject,\n Line,\n Point,\n} from 'fabric';\nimport type {\n Point2D,\n ShapeStyleOptions,\n SnappableInteractionOptions,\n} from '../types';\nimport { createPolygonFromVertices } from '../shapes';\nimport {\n DEFAULT_DRAG_SHAPE_STYLE,\n DEFAULT_GUIDELINE_SHAPE_STYLE,\n DEFAULT_SHAPE_STYLE,\n} from '../styles';\nimport {\n POLYGON_CLOSE_THRESHOLD,\n DEFAULT_ANGLE_SNAP_INTERVAL,\n} from '../constants';\nimport { restoreViewport, createShiftKeyTracker } from './shared';\nimport { createInteractionSnapping } from './interactionSnapping';\n\nexport interface DrawToCreateOptions extends SnappableInteractionOptions {\n /** Style applied to the polygon being drawn. */\n style?: ShapeStyleOptions;\n /**\n * Metadata to attach to the created polygon. If provided, this is set on\n * the polygon's `data` property after creation. Takes precedence over\n * `style.data` if both are specified.\n */\n data?: FabricObject['data'];\n /**\n * Factory function to create the final object from placed vertices.\n * Receives the canvas and the array of placed points.\n * Default: creates a polygon via `createPolygonFromVertices`.\n */\n factory?: (canvas: FabricCanvas, points: Point2D[]) => FabricObject;\n /**\n * Snap vertex positions to multiples of `interval` degrees when Shift is\n * held while placing a vertex. The angle is measured relative to the\n * previous vertex.\n *\n * Pass `false` to disable. Default: enabled at 15° intervals.\n */\n angleSnap?: boolean | { interval?: number };\n /** Called when the user cancels drawing via Escape or Backspace. */\n onCancel?: () => void;\n}\n\nfunction snapAngleToInterval(\n point: { x: number; y: number },\n ref: { x: number; y: number },\n intervalDeg: number,\n): { x: number; y: number } {\n const dx = point.x - ref.x;\n const dy = point.y - ref.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist === 0) return point;\n const radInterval = (intervalDeg * Math.PI) / 180;\n const snappedAngle =\n Math.round(Math.atan2(dy, dx) / radInterval) * radInterval;\n return {\n x: ref.x + Math.cos(snappedAngle) * dist,\n y: ref.y + Math.sin(snappedAngle) * dist,\n };\n}\n\n/**\n * Apply cursor snapping while maintaining an angle constraint.\n * For each axis that cursor-snapped, compute the intersection of the angle\n * ray with that axis value (preserving both angle and axis alignment).\n * Pick the candidate closest to the original position and re-snap there\n * so guidelines render from the correct on-ray point.\n */\nfunction snapAlongRay(\n angleSnapped: { x: number; y: number },\n ref: { x: number; y: number },\n doSnap: (x: number, y: number) => { x: number; y: number },\n): { x: number; y: number } {\n const cursorSnapped = doSnap(angleSnapped.x, angleSnapped.y);\n\n const snappedX = cursorSnapped.x !== angleSnapped.x;\n const snappedY = cursorSnapped.y !== angleSnapped.y;\n\n if (!snappedX && !snappedY) return angleSnapped;\n\n const rayDx = angleSnapped.x - ref.x;\n const rayDy = angleSnapped.y - ref.y;\n\n const candidates: Array<{ x: number; y: number; dist: number }> = [];\n\n // X-snap: find where the ray crosses x = cursorSnapped.x\n if (snappedX && Math.abs(rayDx) > 1e-9) {\n const t = (cursorSnapped.x - ref.x) / rayDx;\n const onRayY = ref.y + t * rayDy;\n candidates.push({\n x: cursorSnapped.x,\n y: onRayY,\n dist: Math.abs(onRayY - angleSnapped.y),\n });\n }\n\n // Y-snap: find where the ray crosses y = cursorSnapped.y\n if (snappedY && Math.abs(rayDy) > 1e-9) {\n const t = (cursorSnapped.y - ref.y) / rayDy;\n const onRayX = ref.x + t * rayDx;\n candidates.push({\n x: onRayX,\n y: cursorSnapped.y,\n dist: Math.abs(onRayX - angleSnapped.x),\n });\n }\n\n if (candidates.length === 0) return angleSnapped;\n\n // Pick candidate closest to the original angle-snapped position\n candidates.sort((a, b) => a.dist - b.dist);\n const best = candidates[0];\n\n // Re-snap at the on-ray position so guidelines render correctly\n doSnap(best.x, best.y);\n\n return best;\n}\n\n/**\n * Enable draw mode for polygons.\n * Click to place vertices one by one. A preview shows edges and a tracking line\n * to the cursor. Click near the first vertex (within threshold) to close the\n * polygon once at least 3 points have been placed.\n * Returns a cleanup function that disables the mode.\n */\nexport function enableDrawToCreate(\n canvas: FabricCanvas,\n options?: DrawToCreateOptions,\n): () => void {\n let exited = false;\n\n const angleSnapEnabled = options?.angleSnap !== false;\n const angleInterval =\n typeof options?.angleSnap === 'object'\n ? (options.angleSnap.interval ?? DEFAULT_ANGLE_SNAP_INTERVAL)\n : DEFAULT_ANGLE_SNAP_INTERVAL;\n\n const shiftTracker = createShiftKeyTracker();\n\n const points: Point2D[] = [];\n const markers: Circle[] = [];\n const edgeLines: Line[] = [];\n let trackingLine: Line | null = null;\n let closingLine: Line | null = null;\n let previousSelection: boolean;\n\n // Use shared snapping context, with placed vertices as additional snap targets\n const snapping = createInteractionSnapping(canvas, options, () =>\n points.map((p) => new Point(p.x, p.y)),\n );\n\n /** Track a preview element in the snapping exclude set. */\n function trackPreviewElement(obj: FabricObject) {\n snapping.excludeSet.add(obj);\n }\n function untrackPreviewElement(obj: FabricObject) {\n snapping.excludeSet.delete(obj);\n }\n\n options?.viewport?.setEnabled(false);\n\n const lineStyle = {\n stroke: options?.style?.stroke ?? DEFAULT_DRAG_SHAPE_STYLE.stroke,\n strokeWidth:\n options?.style?.strokeWidth ?? DEFAULT_DRAG_SHAPE_STYLE.strokeWidth,\n strokeUniform: true,\n selectable: false,\n evented: false,\n };\n\n const guideLineStyle = {\n stroke: options?.style?.stroke ?? DEFAULT_GUIDELINE_SHAPE_STYLE.stroke,\n strokeWidth:\n options?.style?.strokeWidth ?? DEFAULT_GUIDELINE_SHAPE_STYLE.strokeWidth,\n strokeUniform: true,\n selectable: false,\n evented: false,\n } as const;\n\n const removePreviewElements = () => {\n for (const marker of markers) {\n canvas.remove(marker);\n untrackPreviewElement(marker);\n }\n markers.length = 0;\n\n for (const line of edgeLines) {\n canvas.remove(line);\n untrackPreviewElement(line);\n }\n edgeLines.length = 0;\n\n if (trackingLine) {\n canvas.remove(trackingLine);\n untrackPreviewElement(trackingLine);\n trackingLine = null;\n }\n if (closingLine) {\n canvas.remove(closingLine);\n untrackPreviewElement(closingLine);\n closingLine = null;\n }\n };\n\n const finalize = () => {\n removePreviewElements();\n snapping.clearSnapResult();\n\n const obj = options?.factory\n ? options.factory(canvas, [...points])\n : createPolygonFromVertices(canvas, points, options?.style);\n if (options?.data) {\n obj.data = options.data;\n }\n canvas.selection = previousSelection;\n canvas.requestRenderAll();\n\n restoreViewport(options?.viewport);\n options?.onCreated?.(obj);\n points.length = 0;\n };\n\n const handleMouseDown = (event: { scenePoint: Point2D }) => {\n let { x, y } = event.scenePoint;\n if (angleSnapEnabled && shiftTracker.held && points.length > 0) {\n const ref = points[points.length - 1];\n const angleSnapped = snapAngleToInterval({ x, y }, ref, angleInterval);\n ({ x, y } = snapAlongRay(angleSnapped, ref, (sx, sy) =>\n snapping.snap(sx, sy),\n ));\n } else {\n ({ x, y } = snapping.snap(x, y));\n }\n snapping.clearSnapResult();\n\n // Close the polygon if clicking near the first vertex with 3+ points\n if (points.length >= 3) {\n const dx = x - points[0].x;\n const dy = y - points[0].y;\n if (Math.sqrt(dx * dx + dy * dy) <= POLYGON_CLOSE_THRESHOLD) {\n finalize();\n return;\n }\n }\n\n // Disable selection on first point\n if (points.length === 0) {\n previousSelection = canvas.selection;\n canvas.selection = false;\n }\n\n points.push({ x, y });\n\n // Add vertex marker\n const marker = new Circle({\n left: x,\n top: y,\n radius: 4,\n fill: DEFAULT_SHAPE_STYLE.stroke,\n stroke: DEFAULT_SHAPE_STYLE.stroke,\n strokeWidth: 1,\n strokeUniform: true,\n selectable: false,\n evented: false,\n });\n markers.push(marker);\n trackPreviewElement(marker);\n canvas.add(marker);\n\n // Add edge line from previous vertex to this one\n if (points.length >= 2) {\n const prev = points[points.length - 2];\n const edge = new Line([prev.x, prev.y, x, y], lineStyle);\n edgeLines.push(edge);\n trackPreviewElement(edge);\n canvas.add(edge);\n }\n\n canvas.requestRenderAll();\n };\n\n const handleMouseMove = (event: { scenePoint: Point2D }) => {\n if (points.length === 0) return;\n\n const lastPoint = points[points.length - 1];\n let { x, y } = event.scenePoint;\n if (angleSnapEnabled && shiftTracker.held) {\n const angleSnapped = snapAngleToInterval(\n { x, y },\n lastPoint,\n angleInterval,\n );\n ({ x, y } = snapAlongRay(angleSnapped, lastPoint, (sx, sy) =>\n snapping.snapWithGuidelines(sx, sy),\n ));\n } else {\n ({ x, y } = snapping.snapWithGuidelines(x, y));\n }\n\n // Update tracking line from last vertex to cursor\n if (trackingLine) {\n untrackPreviewElement(trackingLine);\n canvas.remove(trackingLine);\n }\n trackingLine = new Line([lastPoint.x, lastPoint.y, x, y], {\n ...guideLineStyle,\n strokeDashArray: [5, 5],\n });\n trackPreviewElement(trackingLine);\n canvas.add(trackingLine);\n\n // Show closing line from cursor to first vertex when 3+ points\n if (closingLine) {\n untrackPreviewElement(closingLine);\n canvas.remove(closingLine);\n closingLine = null;\n }\n if (points.length >= 3) {\n closingLine = new Line([x, y, points[0].x, points[0].y], {\n ...guideLineStyle,\n strokeDashArray: [5, 5],\n });\n trackPreviewElement(closingLine);\n canvas.add(closingLine);\n }\n\n canvas.requestRenderAll();\n };\n\n // Cancel drawing on Escape or Backspace (capture phase to prevent other handlers)\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' || e.key === 'Backspace') {\n e.stopImmediatePropagation();\n e.preventDefault();\n cleanup('cancel');\n }\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n document.addEventListener('keydown', handleKeyDown, true);\n\n function cleanup(reason?: 'cancel') {\n if (exited) return;\n exited = true;\n\n canvas.off('mouse:down', handleMouseDown);\n canvas.off('mouse:move', handleMouseMove);\n document.removeEventListener('keydown', handleKeyDown, true);\n shiftTracker.cleanup();\n\n snapping.cleanup();\n removePreviewElements();\n\n if (points.length > 0) {\n canvas.selection = previousSelection;\n }\n points.length = 0;\n canvas.requestRenderAll();\n restoreViewport(options?.viewport);\n\n if (reason === 'cancel') {\n options?.onCancel?.();\n }\n }\n\n return () => cleanup();\n}\n","import { Canvas as FabricCanvas, Rect } from 'fabric';\nimport type { Point2D, ShapeStyleOptions } from '../types';\nimport { DEFAULT_SHAPE_STYLE } from '../styles';\n\nexport interface RectangleOptions extends ShapeStyleOptions {\n left: number;\n top: number;\n width: number;\n height: number;\n rx?: number;\n ry?: number;\n}\n\nexport interface RectangleAtPointOptions extends ShapeStyleOptions {\n width: number;\n height: number;\n rx?: number;\n ry?: number;\n}\n\n/**\n * Create a rectangle and add it to the canvas.\n * Returns the fabric Rect instance for further manipulation.\n */\nexport function createRectangle(\n canvas: FabricCanvas,\n options: RectangleOptions,\n): Rect {\n const rect = new Rect({ ...DEFAULT_SHAPE_STYLE, ...options });\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Create a rectangle at the given scene point and add it to the canvas.\n * Returns the fabric Rect instance.\n */\nexport function createRectangleAtPoint(\n canvas: FabricCanvas,\n point: Point2D,\n options: RectangleAtPointOptions,\n): Rect {\n const { width, height, ...style } = options;\n const rect = new Rect({\n ...DEFAULT_SHAPE_STYLE,\n left: point.x,\n top: point.y,\n width,\n height,\n ...style,\n });\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Edit an existing rectangle's properties (position, dimensions, appearance, etc.).\n */\nexport function editRectangle(\n canvas: FabricCanvas,\n rect: Rect,\n changes: Partial<RectangleOptions>,\n): void {\n rect.set(changes);\n rect.setCoords();\n canvas.requestRenderAll();\n}\n","import { Canvas as FabricCanvas, Rect } from 'fabric';\nimport type { Point2D, ShapeStyleOptions } from '../types';\nimport { DEFAULT_CIRCLE_STYLE } from '../styles';\n\nexport interface CircleOptions extends ShapeStyleOptions {\n left: number;\n top: number;\n size: number;\n}\n\nexport interface CircleAtPointOptions extends ShapeStyleOptions {\n size: number;\n}\n\n/** Constraints applied to every circle: uniform-only scaling, no rotation. */\nconst CIRCLE_CONSTRAINTS = {\n lockRotation: true,\n lockUniScaling: true,\n} as const;\n\n/** Controls hidden on circles: mid-edge handles (non-uniform) and rotation. */\nconst HIDDEN_CIRCLE_CONTROLS = {\n mt: false,\n mb: false,\n ml: false,\n mr: false,\n mtr: false,\n} as const;\n\n/** Apply circle tag and interaction constraints to a Rect. */\nfunction applyCircleConstraints(rect: Rect): void {\n rect.shapeType = 'circle';\n rect.setControlsVisibility(HIDDEN_CIRCLE_CONTROLS);\n}\n\n/**\n * Re-apply circle control visibility after deserialization.\n *\n * Fabric's `loadFromJSON` does not restore `setControlsVisibility` state, so\n * this must be called on any deserialized object whose `shapeType === 'circle'`\n * to restore the correct handle layout (no mid-edge or rotation handles).\n *\n * Not part of the public package API — imported directly by `serialization.ts`.\n */\nexport function restoreCircleConstraints(rect: Rect): void {\n rect.set(CIRCLE_CONSTRAINTS);\n rect.setControlsVisibility(HIDDEN_CIRCLE_CONTROLS);\n}\n\n/**\n * Create a circle (a square Rect with full border-radius) and add it to the canvas.\n * Returns the fabric Rect instance for further manipulation.\n */\nexport function createCircle(\n canvas: FabricCanvas,\n options: CircleOptions,\n): Rect {\n const { size, ...rest } = options;\n const rect = new Rect({\n ...DEFAULT_CIRCLE_STYLE,\n ...CIRCLE_CONSTRAINTS,\n width: size,\n height: size,\n rx: size / 2,\n ry: size / 2,\n ...rest,\n });\n applyCircleConstraints(rect);\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Create a circle at the given scene point and add it to the canvas.\n * Returns the fabric Rect instance.\n */\nexport function createCircleAtPoint(\n canvas: FabricCanvas,\n point: Point2D,\n options: CircleAtPointOptions,\n): Rect {\n const { size, ...style } = options;\n const rect = new Rect({\n ...DEFAULT_CIRCLE_STYLE,\n ...CIRCLE_CONSTRAINTS,\n left: point.x,\n top: point.y,\n width: size,\n height: size,\n rx: size / 2,\n ry: size / 2,\n ...style,\n });\n applyCircleConstraints(rect);\n canvas.add(rect);\n canvas.requestRenderAll();\n return rect;\n}\n\n/**\n * Edit an existing circle's properties (position, size, appearance).\n * When size changes, rx and ry are automatically updated to maintain the circular shape.\n */\nexport function editCircle(\n canvas: FabricCanvas,\n rect: Rect,\n changes: Partial<CircleOptions>,\n): void {\n const { size, ...rest } = changes;\n if (size !== undefined) {\n rect.set({\n width: size,\n height: size,\n rx: size / 2,\n ry: size / 2,\n ...rest,\n });\n } else {\n rect.set(rest);\n }\n rect.setCoords();\n canvas.requestRenderAll();\n}\n","import { Canvas as FabricCanvas, Polygon } from 'fabric';\nimport type { Point2D, ShapeStyleOptions } from '../types';\nimport { DEFAULT_SHAPE_STYLE } from '../styles';\n\nexport interface PolygonOptions extends ShapeStyleOptions {\n points: Point2D[];\n left?: number;\n top?: number;\n}\n\n/** @deprecated Use `ShapeStyleOptions` directly. This alias will be removed in a future major version. */\nexport type PolygonStyleOptions = ShapeStyleOptions;\n\n/**\n * Create a polygon and add it to the canvas.\n * Returns the fabric Polygon instance for further manipulation.\n */\nexport function createPolygon(\n canvas: FabricCanvas,\n options: PolygonOptions,\n): Polygon {\n const { points, ...rest } = options;\n const polygon = new Polygon(points, { ...DEFAULT_SHAPE_STYLE, ...rest });\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Create a rectangular polygon at the given scene point and add it to the canvas.\n * Returns the fabric Polygon instance.\n */\nexport function createPolygonAtPoint(\n canvas: FabricCanvas,\n point: Point2D,\n options: PolygonStyleOptions & { width: number; height: number },\n): Polygon {\n const { width, height, ...style } = options;\n const polygon = new Polygon(\n [\n { x: 0, y: 0 },\n { x: width, y: 0 },\n { x: width, y: height },\n { x: 0, y: height },\n ],\n { ...DEFAULT_SHAPE_STYLE, left: point.x, top: point.y, ...style },\n );\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Create a rectangular polygon from drag bounds (two corner points) and add it to the canvas.\n * Returns the fabric Polygon instance.\n */\nexport function createPolygonFromDrag(\n canvas: FabricCanvas,\n start: Point2D,\n end: Point2D,\n options?: ShapeStyleOptions,\n): Polygon {\n const width = Math.abs(end.x - start.x);\n const height = Math.abs(end.y - start.y);\n const left = Math.min(start.x, end.x) + width / 2;\n const top = Math.min(start.y, end.y) + height / 2;\n const polygon = new Polygon(\n [\n { x: 0, y: 0 },\n { x: width, y: 0 },\n { x: width, y: height },\n { x: 0, y: height },\n ],\n { ...DEFAULT_SHAPE_STYLE, left, top, ...options },\n );\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Create a polygon from an arbitrary array of vertices and add it to the canvas.\n * Returns the fabric Polygon instance.\n */\nexport function createPolygonFromVertices(\n canvas: FabricCanvas,\n points: Point2D[],\n options?: ShapeStyleOptions,\n): Polygon {\n const polygon = new Polygon(\n points.map((p) => ({ x: p.x, y: p.y })),\n { ...DEFAULT_SHAPE_STYLE, ...options },\n );\n canvas.add(polygon);\n canvas.requestRenderAll();\n return polygon;\n}\n\n/**\n * Edit an existing polygon's properties (position, points, appearance, etc.).\n */\nexport function editPolygon(\n canvas: FabricCanvas,\n polygon: Polygon,\n changes: Partial<PolygonOptions>,\n): void {\n const { points, ...rest } = changes;\n if (points) {\n polygon.points = points;\n polygon.setDimensions();\n }\n polygon.set(rest);\n polygon.setCoords();\n canvas.requestRenderAll();\n}\n","import {\n Canvas as FabricCanvas,\n FabricObject,\n Point,\n Polygon,\n util,\n} from 'fabric';\nimport type { Point2D } from '../types';\nimport {\n DEFAULT_VERTEX_HANDLE_RADIUS,\n DEFAULT_VERTEX_HANDLE_FILL,\n DEFAULT_VERTEX_HANDLE_STROKE,\n DEFAULT_VERTEX_HANDLE_STROKE_WIDTH,\n} from '../constants';\nimport { createInteractionSnapping } from './interactionSnapping';\n\nexport interface VertexEditOptions {\n handleRadius?: number;\n handleFill?: string;\n handleStroke?: string;\n handleStrokeWidth?: number;\n /** Called when vertex editing is exited (via Escape, empty-canvas click, or cleanup). */\n onExit?: () => void;\n}\n\n// --- Coordinate helpers ---\n\nfunction localPointToScene(polygon: Polygon, point: Point2D): Point {\n const matrix = polygon.calcTransformMatrix();\n const localPoint = new Point(\n point.x - polygon.pathOffset.x,\n point.y - polygon.pathOffset.y,\n );\n return util.transformPoint(localPoint, matrix);\n}\n\nfunction scenePointToLocal(polygon: Polygon, scenePoint: Point): Point2D {\n const matrix = polygon.calcTransformMatrix();\n const invMatrix = util.invertTransform(matrix);\n const localPoint = util.transformPoint(scenePoint, invMatrix);\n return {\n x: localPoint.x + polygon.pathOffset.x,\n y: localPoint.y + polygon.pathOffset.y,\n };\n}\n\n// --- DOM handle helpers ---\n\nfunction sceneToScreen(scenePoint: Point, canvas: FabricCanvas): Point {\n return util.transformPoint(scenePoint, canvas.viewportTransform!);\n}\n\nfunction screenToScene(\n screenX: number,\n screenY: number,\n canvas: FabricCanvas,\n): Point {\n const inv = util.invertTransform(canvas.viewportTransform!);\n return util.transformPoint(new Point(screenX, screenY), inv);\n}\n\nfunction createHandleElement(\n radius: number,\n fill: string,\n stroke: string,\n strokeWidth: number,\n): HTMLDivElement {\n const el = document.createElement('div');\n const size = radius * 2;\n el.style.cssText = [\n 'position: absolute',\n `width: ${size}px`,\n `height: ${size}px`,\n `margin-left: ${-radius}px`,\n `margin-top: ${-radius}px`,\n 'border-radius: 50%',\n `background: ${fill}`,\n `border: ${strokeWidth}px solid ${stroke}`,\n 'box-sizing: border-box',\n 'pointer-events: auto',\n 'cursor: grab',\n 'touch-action: none',\n ].join('; ');\n return el;\n}\n\nfunction positionHandle(\n handle: HTMLDivElement,\n scenePoint: Point,\n canvas: FabricCanvas,\n): void {\n const screen = sceneToScreen(scenePoint, canvas);\n handle.style.left = `${screen.x}px`;\n handle.style.top = `${screen.y}px`;\n}\n\n// --- Main function ---\n\n/**\n * Enable vertex editing on a polygon.\n * Creates draggable DOM circle handles at each vertex. Dragging a handle\n * updates the polygon's shape in real-time with cursor snapping support.\n *\n * Exit by pressing Escape or clicking on empty canvas.\n *\n * Returns a cleanup function that removes handles and exits edit mode.\n */\nexport function enableVertexEdit(\n canvas: FabricCanvas,\n polygon: Polygon,\n options?: VertexEditOptions,\n /** @deprecated Pass `onExit` in options instead. */\n onExit?: () => void,\n): () => void {\n let exited = false;\n let draggingIndex: number | null = null;\n\n const handleRadius = options?.handleRadius ?? DEFAULT_VERTEX_HANDLE_RADIUS;\n const handleFill = options?.handleFill ?? DEFAULT_VERTEX_HANDLE_FILL;\n const handleStroke = options?.handleStroke ?? DEFAULT_VERTEX_HANDLE_STROKE;\n const handleStrokeWidth =\n options?.handleStrokeWidth ?? DEFAULT_VERTEX_HANDLE_STROKE_WIDTH;\n\n // Save previous state so we can restore on cleanup\n const previousState = {\n selectable: polygon.selectable,\n evented: polygon.evented,\n hasControls: polygon.hasControls,\n canvasSelection: canvas.selection,\n };\n\n const prevObjectStates = new Map<\n FabricObject,\n { selectable: boolean; evented: boolean }\n >();\n\n // Disable polygon interaction and canvas selection\n polygon.selectable = false;\n polygon.evented = false;\n polygon.hasControls = false;\n canvas.selection = false;\n canvas.discardActiveObject();\n\n // Make all other objects non-interactive\n canvas.forEachObject((obj) => {\n if (obj !== polygon) {\n prevObjectStates.set(obj, {\n selectable: obj.selectable,\n evented: obj.evented,\n });\n obj.selectable = false;\n obj.evented = false;\n }\n });\n\n // Set up snapping — provides snap-to-object-points + guideline rendering.\n // Additional targets: the other vertices of this polygon (for self-alignment).\n const snapping = createInteractionSnapping(canvas, undefined, () => {\n if (draggingIndex === null) return [];\n return polygon.points\n .filter((_, i) => i !== draggingIndex)\n .map((pt) => localPointToScene(polygon, pt));\n });\n snapping.excludeSet.add(polygon);\n\n // Create handle container overlay\n const container = document.createElement('div');\n container.style.cssText =\n 'position: absolute; inset: 0; pointer-events: none; overflow: hidden;';\n canvas.wrapperEl.appendChild(container);\n\n // Create DOM handles at each vertex\n const handles: HTMLDivElement[] = [];\n const points = polygon.points;\n\n for (let i = 0; i < points.length; i++) {\n const handle = createHandleElement(\n handleRadius,\n handleFill,\n handleStroke,\n handleStrokeWidth,\n );\n positionHandle(handle, localPointToScene(polygon, points[i]), canvas);\n container.appendChild(handle);\n handles.push(handle);\n\n // --- Pointer event handlers for dragging ---\n handle.addEventListener('pointerdown', (e: PointerEvent) => {\n if (exited) return;\n e.preventDefault();\n e.stopPropagation();\n draggingIndex = i;\n handle.setPointerCapture(e.pointerId);\n handle.style.cursor = 'grabbing';\n });\n\n handle.addEventListener('pointermove', (e: PointerEvent) => {\n if (draggingIndex !== i) return;\n\n // Convert pointer position to canvas-relative coordinates\n const wrapperRect = canvas.wrapperEl.getBoundingClientRect();\n const canvasRelX = e.clientX - wrapperRect.left;\n const canvasRelY = e.clientY - wrapperRect.top;\n\n // Convert to scene space\n const rawScene = screenToScene(canvasRelX, canvasRelY, canvas);\n\n // Apply snapping\n const snapped = snapping.snapWithGuidelines(rawScene.x, rawScene.y);\n const scenePoint = new Point(snapped.x, snapped.y);\n\n // Use a non-dragged vertex as an anchor to measure visual drift\n const anchorIdx = i === 0 ? 1 : 0;\n const anchorBefore = localPointToScene(\n polygon,\n polygon.points[anchorIdx],\n );\n\n const localPoint = scenePointToLocal(polygon, scenePoint);\n polygon.points[i] = localPoint;\n polygon.setDimensions();\n\n // setDimensions recalculates pathOffset, shifting all vertices visually.\n // Compensate by adjusting polygon position so the anchor stays in place.\n const anchorAfter = localPointToScene(polygon, polygon.points[anchorIdx]);\n polygon.left += anchorBefore.x - anchorAfter.x;\n polygon.top += anchorBefore.y - anchorAfter.y;\n polygon.dirty = true;\n polygon.setCoords();\n\n repositionAllHandles();\n canvas.requestRenderAll();\n });\n\n handle.addEventListener('pointerup', (e: PointerEvent) => {\n if (draggingIndex !== i) return;\n handle.releasePointerCapture(e.pointerId);\n draggingIndex = null;\n handle.style.cursor = 'grab';\n snapping.clearSnapResult();\n canvas.requestRenderAll();\n });\n }\n\n // Reposition all handles based on current polygon state\n function repositionAllHandles() {\n const pts = polygon.points;\n for (let j = 0; j < pts.length; j++) {\n positionHandle(handles[j], localPointToScene(polygon, pts[j]), canvas);\n }\n }\n\n // Reposition handles when viewport changes (zoom/pan)\n const afterRender = () => {\n if (draggingIndex === null) {\n repositionAllHandles();\n }\n };\n canvas.on('after:render', afterRender);\n\n // Exit on Escape (capture phase to prevent deletion shortcut)\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopImmediatePropagation();\n cleanup();\n }\n };\n document.addEventListener('keydown', handleKeyDown, true);\n\n // Exit on clicking empty canvas (clicks on DOM handles don't reach fabric)\n const handleMouseDown = () => {\n cleanup();\n };\n canvas.on('mouse:down', handleMouseDown);\n\n function cleanup() {\n if (exited) return;\n exited = true;\n\n snapping.cleanup();\n canvas.off('after:render', afterRender);\n canvas.off('mouse:down', handleMouseDown);\n document.removeEventListener('keydown', handleKeyDown, true);\n\n container.remove();\n\n polygon.selectable = previousState.selectable;\n polygon.evented = previousState.evented;\n polygon.hasControls = previousState.hasControls;\n canvas.selection = previousState.canvasSelection;\n\n prevObjectStates.forEach((state, obj) => {\n obj.selectable = state.selectable;\n obj.evented = state.evented;\n });\n\n canvas.discardActiveObject();\n canvas.requestRenderAll();\n\n (options?.onExit ?? onExit)?.();\n }\n\n return cleanup;\n}\n","import {\n Canvas as FabricCanvas,\n FabricImage,\n Rect,\n type FabricObject,\n type TOriginX,\n type TOriginY,\n} from 'fabric';\nimport { getBackgroundContrast, getBackgroundInverted } from './background';\nimport { restoreCircleConstraints } from './shapes/circle';\nimport { DEFAULT_CONTROL_STYLE } from './styles';\nimport type { CanvasJSON } from './types';\n\n/**\n * Module-level map from FabricObject to its \"base\" (unscaled) stroke width.\n * Shared between enableScaledStrokes and serializeCanvas so they stay in sync\n * without leaking mutable state outside the module.\n */\nconst strokeBaseMap = new WeakMap<FabricObject, number>();\n\n/**\n * Module-level map from Rect to its original rx/ry values before the\n * view border radius was applied. Used by serializeCanvas to strip\n * the visual-only border radius before saving.\n */\nconst borderRadiusBaseMap = new WeakMap<Rect, { rx: number; ry: number }>();\n\nconst DEFAULT_VIEW_BORDER_RADIUS = 4;\n\n/**\n * Enable zoom-independent stroke widths on a canvas.\n *\n * On every render, each object's `strokeWidth` is set to `base / zoom` so\n * strokes always appear as one visual pixel regardless of zoom level. The\n * original (base) stroke width is stored in an internal WeakMap and is\n * automatically restored before serialization by {@link serializeCanvas}.\n *\n * Returns a cleanup function that removes the listener and restores all\n * stroke widths to their base values.\n */\nexport function enableScaledStrokes(canvas: FabricCanvas): () => void {\n function applyScaledStrokes() {\n const zoom = canvas.getZoom();\n canvas.forEachObject((obj) => {\n if (!obj.strokeWidth && obj.strokeWidth !== 0) return;\n if (!strokeBaseMap.has(obj)) {\n // Record the base value on first encounter\n strokeBaseMap.set(obj, obj.strokeWidth ?? 0);\n }\n const base = strokeBaseMap.get(obj)!;\n if (base === 0) return;\n obj.strokeWidth = base / zoom;\n });\n }\n\n canvas.on('before:render', applyScaledStrokes);\n\n return () => {\n canvas.off('before:render', applyScaledStrokes);\n // Restore all objects to their base stroke widths on cleanup\n canvas.forEachObject((obj) => {\n const base = strokeBaseMap.get(obj);\n if (base !== undefined) {\n obj.strokeWidth = base;\n }\n });\n };\n}\n\n/**\n * Keep border radii visually constant on Rects loaded via {@link loadCanvas}.\n *\n * On every render, each tracked Rect's `rx`/`ry` is recalculated as\n * `VIEW_BORDER_RADIUS / scale` so corners always appear circular regardless\n * of non-uniform scaling. The original rx/ry values are stored in an internal\n * WeakMap and automatically restored before serialization by\n * {@link serializeCanvas}.\n *\n * Returns a cleanup function that removes the listener and restores all\n * rx/ry values to their originals.\n */\nexport interface ScaledBorderRadiusOptions {\n /** Visual border radius in pixels. Default: 4. */\n radius?: number;\n}\n\nexport function enableScaledBorderRadius(\n canvas: FabricCanvas,\n options?: ScaledBorderRadiusOptions,\n): () => void {\n const radius = options?.radius ?? DEFAULT_VIEW_BORDER_RADIUS;\n\n function applyScaledBorderRadius() {\n canvas.forEachObject((obj) => {\n if (!(obj instanceof Rect)) return;\n if (!borderRadiusBaseMap.has(obj)) return;\n const rx = radius / (obj.scaleX ?? 1);\n const ry = radius / (obj.scaleY ?? 1);\n obj.set({ rx, ry });\n });\n }\n\n canvas.on('before:render', applyScaledBorderRadius);\n\n return () => {\n canvas.off('before:render', applyScaledBorderRadius);\n canvas.forEachObject((obj) => {\n if (!(obj instanceof Rect)) return;\n const base = borderRadiusBaseMap.get(obj);\n if (base !== undefined) {\n obj.set({ rx: base.rx, ry: base.ry });\n }\n });\n };\n}\n\n/**\n * Options for {@link serializeCanvas}.\n */\nexport interface SerializeOptions {\n /**\n * Additional Fabric object properties to include in the JSON output.\n * The `'data'` and `'shapeType'` properties are always included.\n * Default: `['data', 'shapeType']`.\n */\n properties?: string[];\n}\n\n/**\n * Return the base (unscaled) stroke width for an object.\n *\n * When {@link enableScaledStrokes} is active, `obj.strokeWidth` is a\n * zoom-adjusted value. This function returns the original intended width that\n * was recorded before any zoom adjustment was applied. Falls back to the\n * object's current `strokeWidth` if the object has never been seen by\n * `enableScaledStrokes` (e.g. if the feature is disabled).\n */\nexport function getBaseStrokeWidth(obj: FabricObject): number {\n return strokeBaseMap.get(obj) ?? obj.strokeWidth ?? 0;\n}\n\n// --- Serialization helpers ---\n// Each helper temporarily mutates canvas/object state for serialization\n// and returns a function that restores the original runtime state.\n\ntype SavedOrigin = {\n originX: TOriginX;\n originY: TOriginY;\n left: number;\n top: number;\n};\n\n/** Strip zoom-scaled stroke widths, restoring base values for serialization. */\nfunction prepareStrokeWidths(canvas: FabricCanvas): () => void {\n const scaledWidths = new Map<FabricObject, number>();\n canvas.forEachObject((obj) => {\n const base = strokeBaseMap.get(obj);\n if (base !== undefined && obj.strokeWidth !== base) {\n scaledWidths.set(obj, obj.strokeWidth ?? 0);\n obj.strokeWidth = base;\n }\n });\n return () =>\n scaledWidths.forEach((scaled, obj) => {\n obj.strokeWidth = scaled;\n });\n}\n\n/** Strip visual-only border radii, restoring original rx/ry for serialization. */\nfunction prepareBorderRadii(canvas: FabricCanvas): () => void {\n const appliedRadii = new Map<Rect, { rx: number; ry: number }>();\n canvas.forEachObject((obj) => {\n if (!(obj instanceof Rect)) return;\n const base = borderRadiusBaseMap.get(obj);\n if (base !== undefined) {\n appliedRadii.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });\n obj.set({ rx: base.rx, ry: base.ry });\n }\n });\n return () =>\n appliedRadii.forEach((radii, obj) => {\n obj.set(radii);\n });\n}\n\n/** Convert objects from center/center to left/top origin for Fabric 6 compatibility. */\nfunction prepareObjectOrigins(canvas: FabricCanvas): () => void {\n const savedOrigins = new Map<FabricObject, SavedOrigin>();\n canvas.forEachObject((obj) => {\n if (obj.originX === 'left' && obj.originY === 'top') return;\n savedOrigins.set(obj, {\n originX: obj.originX,\n originY: obj.originY,\n left: obj.left ?? 0,\n top: obj.top ?? 0,\n });\n const leftTop = obj.getPositionByOrigin('left', 'top');\n obj.set({\n originX: 'left',\n originY: 'top',\n left: leftTop.x,\n top: leftTop.y,\n });\n });\n return () =>\n savedOrigins.forEach((saved, obj) => {\n obj.set(saved);\n });\n}\n\n/** Convert background image from center/center to left/top origin. */\nfunction prepareBackgroundOrigin(canvas: FabricCanvas): () => void {\n const bg = canvas.backgroundImage;\n if (\n !(bg instanceof FabricImage) ||\n (bg.originX === 'left' && bg.originY === 'top')\n ) {\n return () => {};\n }\n const saved: SavedOrigin = {\n originX: bg.originX,\n originY: bg.originY,\n left: bg.left ?? 0,\n top: bg.top ?? 0,\n };\n const leftTop = bg.getPositionByOrigin('left', 'top');\n bg.set({ originX: 'left', originY: 'top', left: leftTop.x, top: leftTop.y });\n return () => {\n bg.set(saved);\n };\n}\n\n/** Add strokeWidthBase to obj.data for backward compatibility with old canvas. */\nfunction prepareStrokeWidthBaseData(canvas: FabricCanvas): () => void {\n const savedData = new Map<FabricObject, FabricObject['data']>();\n canvas.forEachObject((obj) => {\n const base = strokeBaseMap.get(obj) ?? obj.strokeWidth;\n if (base !== undefined && base !== 0 && obj.data) {\n savedData.set(obj, obj.data);\n (obj as unknown as { data: Record<string, unknown> }).data = {\n ...(obj.data as Record<string, unknown>),\n strokeWidthBase: base,\n };\n }\n });\n return () =>\n savedData.forEach((originalData, obj) => {\n (obj as unknown as { data: FabricObject['data'] }).data = originalData;\n });\n}\n\n// --- Public serialization API ---\n\n/**\n * Serialize the canvas to a plain object, ready for `JSON.stringify`.\n *\n * The output uses Fabric 6 conventions (`originX: 'left'`, `originY: 'top'`,\n * `backgroundFilters`, `data.strokeWidthBase`) so saved data is readable by\n * both old (Fabric 6) and new (Fabric 7) canvas implementations.\n *\n * Internally, the canvas keeps `center/center` origins at runtime. This\n * function temporarily converts objects to `left/top` origin before calling\n * `toObject()`, then restores the runtime state immediately after.\n */\nexport function serializeCanvas(\n canvas: FabricCanvas,\n options?: SerializeOptions,\n): CanvasJSON {\n const properties = [\n 'data',\n 'shapeType',\n // Control styling — absent from Fabric's default toObject output\n 'borderColor',\n 'cornerColor',\n 'cornerStrokeColor',\n 'transparentCorners',\n // Interaction locks — absent from Fabric's default toObject output\n 'lockRotation',\n 'lockUniScaling',\n ...(options?.properties ?? []),\n ];\n\n // Temporarily mutate canvas state for backward-compatible serialization.\n // Each prepare* call returns a restore function to undo the mutation.\n const restoreStrokeWidths = prepareStrokeWidths(canvas);\n const restoreBorderRadii = prepareBorderRadii(canvas);\n const restoreOrigins = prepareObjectOrigins(canvas);\n const restoreBgOrigin = prepareBackgroundOrigin(canvas);\n const restoreData = prepareStrokeWidthBaseData(canvas);\n\n const json = canvas.toObject(properties) as CanvasJSON;\n\n // Strip backgroundColor — it's theme-dependent, not user data.\n delete json.backgroundColor;\n\n // Add backward-compatible canvas-level properties.\n (json as Record<string, unknown>).backgroundFilters = {\n opacity: getBackgroundContrast(canvas),\n inverted: getBackgroundInverted(canvas),\n };\n if (canvas.lockLightMode !== undefined) {\n (json as Record<string, unknown>).lockLightMode = canvas.lockLightMode;\n }\n\n // Restore all runtime state.\n restoreStrokeWidths();\n restoreBorderRadii();\n restoreOrigins();\n restoreBgOrigin();\n restoreData();\n\n return json;\n}\n\n/**\n * Options for {@link loadCanvas}.\n */\nexport interface LoadCanvasOptions {\n /**\n * If provided, objects for which this function returns `false` are removed\n * from the canvas after loading. Useful for filtering out objects whose IDs\n * no longer exist in the application's data model.\n */\n filter?: (obj: FabricObject) => boolean;\n /**\n * Visual border radius applied to loaded Rects (excluding circles and DEVICE objects).\n * Pass a number to customize, or `false` to skip entirely. Default: `4`.\n */\n borderRadius?: number | false;\n}\n\n/**\n * Load a canvas from a previously serialized JSON object (from {@link serializeCanvas}).\n *\n * Clears the canvas and restores all objects, then requests a re-render.\n * The returned promise resolves once the canvas is fully loaded.\n */\nexport async function loadCanvas(\n canvas: FabricCanvas,\n json: CanvasJSON | object,\n options?: LoadCanvasOptions,\n): Promise<FabricObject[]> {\n await canvas.loadFromJSON(json);\n\n // Strip backgroundColor restored from old canvas data —\n // background color is a runtime/theme concern, not persisted data.\n // Mirrors serializeCanvas which already deletes backgroundColor on save.\n canvas.backgroundColor = '';\n\n // Strip legacy `backgroundFilters` custom property from old canvas data.\n // The old implementation stored contrast/inversion state as a custom canvas\n // property; the new canvas reads actual Fabric filters on the image directly.\n delete (canvas as unknown as Record<string, unknown>).backgroundFilters;\n\n // Restore lockLightMode from serialized data. Fabric's loadFromJSON sets\n // unknown top-level properties on the canvas instance, so we read and\n // re-assign it to the typed augmented property.\n const rawCanvas = canvas as unknown as Record<string, unknown>;\n if (rawCanvas.lockLightMode !== undefined) {\n canvas.lockLightMode = rawCanvas.lockLightMode as boolean;\n }\n\n // Normalize background image origin: old data (Fabric 6) uses originX/Y\n // 'left'/'top' while the new canvas uses 'center'/'center'. Compute the\n // visual center before switching so the image stays in the same position.\n const bg = canvas.backgroundImage;\n if (bg instanceof FabricImage) {\n if (bg.originX !== 'center' || bg.originY !== 'center') {\n const center = bg.getCenterPoint();\n bg.set({\n originX: 'center',\n originY: 'center',\n left: center.x,\n top: center.y,\n });\n bg.setCoords();\n }\n }\n\n // Filter out non-matching objects before applying styles\n if (options?.filter) {\n const toRemove: FabricObject[] = [];\n canvas.forEachObject((obj) => {\n if (!options.filter!(obj)) toRemove.push(obj);\n });\n for (const obj of toRemove) canvas.remove(obj);\n }\n\n // Normalize legacy origin: old data uses originX/Y 'left'/'top', but the\n // new canvas expects 'center'/'center'. Compute the visual center before\n // switching origins so objects stay in the same position.\n canvas.forEachObject((obj) => {\n if (obj.originX === 'center' && obj.originY === 'center') return;\n const center = obj.getCenterPoint();\n obj.set({\n originX: 'center',\n originY: 'center',\n left: center.x,\n top: center.y,\n });\n obj.setCoords();\n });\n\n // Re-apply per-object state that Fabric does not persist through serialization.\n canvas.forEachObject((obj) => {\n // Strip legacy `strokeWidthBase` from obj.data — the old canvas stored the\n // base stroke width by mutating data. The new canvas uses internal WeakMaps.\n const data = obj.data as Record<string, unknown> | undefined;\n if (data?.strokeWidthBase !== undefined) {\n delete data.strokeWidthBase;\n }\n // Control styling (borderColor, cornerColor, etc.) is absent from Fabric's\n // default toObject output, so we restore it explicitly for all objects.\n obj.set(DEFAULT_CONTROL_STYLE);\n // Circle-specific constraints (control visibility, lock flags).\n if (obj.shapeType === 'circle' && obj instanceof Rect) {\n restoreCircleConstraints(obj);\n }\n // Apply visual border radius to Rects (excluding circles and DEVICE objects).\n // Compensate for non-uniform scaling so corners appear circular.\n // Original values are stored so serializeCanvas can strip them before saving.\n const borderRadius = options?.borderRadius ?? DEFAULT_VIEW_BORDER_RADIUS;\n if (\n borderRadius !== false &&\n obj instanceof Rect &&\n obj.shapeType !== 'circle' &&\n obj.data?.type !== 'DEVICE'\n ) {\n borderRadiusBaseMap.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });\n const rx = borderRadius / (obj.scaleX ?? 1);\n const ry = borderRadius / (obj.scaleY ?? 1);\n obj.set({ rx, ry });\n }\n });\n canvas.requestRenderAll();\n\n return canvas.getObjects() as FabricObject[];\n}\n","import { Canvas as FabricCanvas } from 'fabric';\nimport { serializeCanvas, loadCanvas } from './serialization';\nimport type { CanvasJSON } from './types';\n\nexport interface HistoryOptions {\n /** Maximum number of snapshots to keep. Oldest are dropped when exceeded. Default: 50. */\n maxSize?: number;\n /** Debounce delay in milliseconds before capturing a snapshot after a change. Default: 300. */\n debounce?: number;\n}\n\nexport interface HistoryTracker {\n /** Undo the last change. No-op if at the beginning of history. */\n undo: () => Promise<void>;\n /** Redo a previously undone change. No-op if at the end of history. */\n redo: () => Promise<void>;\n /** Whether an undo operation is available. */\n canUndo: () => boolean;\n /** Whether a redo operation is available. */\n canRedo: () => boolean;\n /** Manually push the current canvas state as a snapshot. */\n pushSnapshot: () => void;\n /** Remove all event listeners and clear history. */\n cleanup: () => void;\n}\n\n/**\n * Create a snapshot-based undo/redo tracker for a canvas.\n *\n * Listens to `object:added`, `object:modified`, and `object:removed` events\n * and captures a serialized snapshot of the canvas state (debounced).\n *\n * `undo()` and `redo()` load adjacent snapshots via `loadCanvas`.\n * During undo/redo operations, event-triggered captures are suppressed.\n */\nexport function createHistoryTracker(\n canvas: FabricCanvas,\n options?: HistoryOptions,\n): HistoryTracker {\n const maxSize = options?.maxSize ?? 50;\n const debounceMs = options?.debounce ?? 300;\n\n const snapshots: CanvasJSON[] = [];\n let currentIndex = -1;\n let isUndoRedo = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n function captureSnapshot() {\n if (isUndoRedo) return;\n\n const snapshot = serializeCanvas(canvas);\n\n // Drop any redo history beyond current position\n if (currentIndex < snapshots.length - 1) {\n snapshots.length = currentIndex + 1;\n }\n\n snapshots.push(snapshot);\n\n // Enforce max size\n if (snapshots.length > maxSize) {\n snapshots.shift();\n }\n\n currentIndex = snapshots.length - 1;\n }\n\n function debouncedCapture() {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n }\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n captureSnapshot();\n }, debounceMs);\n }\n\n const onChange = () => {\n if (!isUndoRedo) debouncedCapture();\n };\n\n canvas.on('object:added', onChange);\n canvas.on('object:modified', onChange);\n canvas.on('object:removed', onChange);\n\n async function loadSnapshot(index: number) {\n if (index < 0 || index >= snapshots.length) return;\n\n isUndoRedo = true;\n currentIndex = index;\n\n try {\n await loadCanvas(canvas, snapshots[index]);\n } finally {\n isUndoRedo = false;\n }\n }\n\n return {\n async undo() {\n if (currentIndex <= 0) return;\n await loadSnapshot(currentIndex - 1);\n },\n\n async redo() {\n if (currentIndex >= snapshots.length - 1) return;\n await loadSnapshot(currentIndex + 1);\n },\n\n canUndo() {\n return currentIndex > 0;\n },\n\n canRedo() {\n return currentIndex < snapshots.length - 1;\n },\n\n pushSnapshot() {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n captureSnapshot();\n },\n\n cleanup() {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n canvas.off('object:added', onChange);\n canvas.off('object:modified', onChange);\n canvas.off('object:removed', onChange);\n snapshots.length = 0;\n currentIndex = -1;\n },\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject } from 'fabric';\nimport {\n enablePanAndZoom,\n type PanAndZoomOptions,\n type ViewportController,\n} from '../viewport';\nimport {\n enableScaledStrokes,\n enableScaledBorderRadius,\n type ScaledBorderRadiusOptions,\n} from '../serialization';\nimport { fitViewportToBackground } from '../background';\nimport { useViewportActions, syncZoom } from './shared';\n\n/** Visual properties that can be updated on view-canvas objects. */\nexport interface ViewObjectStyle {\n fill?: string;\n stroke?: string;\n strokeWidth?: number;\n opacity?: number;\n visible?: boolean;\n}\n\nexport interface UseViewCanvasOptions {\n /** Configure pan and zoom. Pass `false` to disable, or options to customize. Default: enabled. */\n panAndZoom?: boolean | PanAndZoomOptions;\n /**\n * Keep stroke widths visually constant as the user zooms in/out.\n * Pass `false` to disable. Default: enabled.\n */\n scaledStrokes?: boolean;\n /** Called after the canvas is initialized and viewport is set up. */\n onReady?: (canvas: FabricCanvas) => void | Promise<void>;\n /**\n * Automatically fit the viewport to the background image after `onReady`\n * completes, if a background image is present. Also applies when\n * `viewport.reset` is called while a background image is set.\n * Pass `false` to disable. Default: enabled.\n */\n autoFitToBackground?: boolean;\n /**\n * Visual border radius applied to loaded Rects (via `loadCanvas`).\n * Pass a number to customize (default: 4), or `false` to disable.\n */\n borderRadius?: number | false;\n}\n\n/** Disable selection on all objects. Border radius is applied by loadCanvas. */\nfunction lockCanvas(canvas: FabricCanvas) {\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n });\n}\n\n/**\n * Hook that provides a view-only canvas experience.\n *\n * Like {@link useEditCanvas} but without create, edit, delete, or selection\n * capabilities. The canvas is always in pan mode — click and drag to pan,\n * scroll to zoom.\n *\n * @example\n * ```tsx\n * const canvas = useViewCanvas();\n *\n * return <Canvas onReady={canvas.onReady} width={800} height={600} />;\n * ```\n */\nexport function useViewCanvas(options?: UseViewCanvasOptions) {\n const canvasRef = useRef<FabricCanvas | null>(null);\n const viewportRef = useRef<ViewportController | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const [zoom, setZoom] = useState(1);\n\n const onReady = useCallback(\n (canvas: FabricCanvas) => {\n canvasRef.current = canvas;\n const opts = optionsRef.current;\n\n if (opts?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (opts?.borderRadius !== false) {\n const borderRadiusOpts: ScaledBorderRadiusOptions | undefined =\n typeof opts?.borderRadius === 'number'\n ? { radius: opts.borderRadius }\n : undefined;\n enableScaledBorderRadius(canvas, borderRadiusOpts);\n }\n\n if (opts?.panAndZoom !== false) {\n const panAndZoomOpts =\n typeof opts?.panAndZoom === 'object' ? opts.panAndZoom : {};\n\n viewportRef.current = enablePanAndZoom(canvas, {\n ...panAndZoomOpts,\n initialMode: 'pan',\n });\n }\n\n lockCanvas(canvas);\n\n // Lock any objects added after initialisation\n canvas.on('object:added', () => {\n lockCanvas(canvas);\n });\n\n canvas.on('mouse:wheel', () => {\n setZoom(canvas.getZoom());\n });\n\n const onReadyResult = opts?.onReady?.(canvas);\n if (opts?.autoFitToBackground !== false) {\n Promise.resolve(onReadyResult).then(() => {\n if (canvas.backgroundImage) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n });\n }\n },\n // onReady and panAndZoom are intentionally excluded — we only initialize once\n [],\n );\n\n const { resetViewport, zoomIn, zoomOut, panToObject, zoomToFit } =\n useViewportActions(canvasRef, viewportRef, setZoom);\n\n /** Find a canvas object by its `data.id`. */\n const findObject = (id: string): FabricObject | undefined => {\n const c = canvasRef.current;\n if (!c) return undefined;\n return c.getObjects().find((o) => o.data?.id === id);\n };\n\n /** Update a single object's visual style by its `data.id`. */\n const setObjectStyle = useCallback((id: string, style: ViewObjectStyle) => {\n const obj = findObject(id);\n if (!obj) return;\n obj.set(style);\n canvasRef.current!.requestRenderAll();\n }, []);\n\n /** Batch-update multiple objects' visual styles. Keyed by `data.id`. */\n const setObjectStyles = useCallback(\n (styles: Record<string, ViewObjectStyle>) => {\n const c = canvasRef.current;\n if (!c) return;\n const objects = c.getObjects();\n const idMap = new Map<string, FabricObject>();\n for (const obj of objects) {\n if (obj.data?.id) idMap.set(obj.data.id, obj);\n }\n let updated = false;\n for (const [id, style] of Object.entries(styles)) {\n const obj = idMap.get(id);\n if (obj) {\n obj.set(style);\n updated = true;\n }\n }\n if (updated) c.requestRenderAll();\n },\n [],\n );\n\n /** Apply a visual style to all objects whose `data.type` matches the given type. */\n const setObjectStyleByType = useCallback(\n (type: string, style: ViewObjectStyle) => {\n const c = canvasRef.current;\n if (!c) return;\n let updated = false;\n for (const obj of c.getObjects()) {\n if (obj.data?.type === type) {\n obj.set(style);\n updated = true;\n }\n }\n if (updated) c.requestRenderAll();\n },\n [],\n );\n\n return {\n /** Pass this to `<Canvas onReady={...} />` */\n onReady,\n /** Ref to the underlying Fabric canvas instance. */\n canvasRef,\n /** Current zoom level (reactive). */\n zoom,\n /** Viewport controls. */\n viewport: {\n /** Reset viewport to default (no pan, zoom = 1), or fit to background if one is set. */\n reset: resetViewport,\n /** Zoom in toward the canvas center. Default step: 0.2. */\n zoomIn,\n /** Zoom out from the canvas center. Default step: 0.2. */\n zoomOut,\n /** Pan the viewport to center on a specific object. */\n panToObject,\n /** Zoom and pan to fit a specific object in the viewport. */\n zoomToFit,\n },\n /** Update a single object's visual style by its `data.id`. */\n setObjectStyle,\n /** Batch-update multiple objects' visual styles in one render. Keyed by `data.id`. */\n setObjectStyles,\n /** Apply a visual style to all objects whose `data.type` matches. */\n setObjectStyleByType,\n };\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport type { Canvas as FabricCanvas, CanvasEvents } from 'fabric';\n\n/**\n * A map of Fabric canvas event names to their handler functions.\n *\n * Each key is a valid `CanvasEvents` event name and each handler receives the\n * correctly-typed event payload. Set a handler to `undefined` to skip it.\n *\n * Common events include:\n * - `'object:added'`, `'object:removed'`, `'object:modified'`\n * - `'mouse:over'`, `'mouse:out'`, `'mouse:down'`, `'mouse:up'`, `'mouse:move'`\n * - `'mouse:wheel'`, `'after:render'`\n * - `'selection:created'`, `'selection:updated'`, `'selection:cleared'`\n */\nexport type CanvasEventHandlers = {\n [K in keyof CanvasEvents]?: (event: CanvasEvents[K]) => void;\n};\n\n/**\n * Subscribe to Fabric canvas events with automatic cleanup.\n *\n * Handlers are stored in a ref so they always call the latest version without\n * re-subscribing. The hook subscribes once when the canvas becomes available\n * (child `<Canvas>` effects fire before parent effects) and cleans up on unmount.\n *\n * @example\n * ```tsx\n * useCanvasEvents(editor.canvasRef, {\n * 'object:added': (e) => updateList(),\n * 'object:modified': () => setDirty(true),\n * 'object:removed': () => updateList(),\n * });\n * ```\n */\nexport function useCanvasEvents(\n canvasRef: RefObject<FabricCanvas | null>,\n events: CanvasEventHandlers,\n): void {\n const eventsRef = useRef(events);\n eventsRef.current = events;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n type EventName = keyof CanvasEvents;\n // Wrapper uses `unknown` because TypeScript can't correlate a dynamic key\n // with its matching handler type inside a loop (correlated-union limitation).\n // The public `CanvasEventHandlers` type is fully typed per-event.\n type EventCallback = (options: unknown) => void;\n const wrappers = new Map<EventName, EventCallback>();\n\n for (const key of Object.keys(eventsRef.current) as EventName[]) {\n if (!eventsRef.current[key]) continue;\n const wrapped: EventCallback = (options) => {\n (eventsRef.current[key] as EventCallback | undefined)?.(options);\n };\n canvas.on(key, wrapped);\n wrappers.set(key, wrapped);\n }\n\n return () => {\n wrappers.forEach((handler, name) => {\n canvas.off(name, handler);\n });\n };\n }, [canvasRef]);\n}\n","import { useEffect, useRef, useState, type RefObject } from 'react';\nimport type {\n Canvas as FabricCanvas,\n CanvasEvents,\n FabricObject,\n} from 'fabric';\n\nexport interface UseCanvasTooltipOptions<T> {\n /** Extract tooltip content from a hovered object. Return `null` to skip the tooltip. */\n getContent: (obj: FabricObject) => T | null;\n}\n\nexport interface CanvasTooltipState<T> {\n /** Whether the tooltip is currently visible. */\n visible: boolean;\n /** The content extracted from the hovered object, or `null` when hidden. */\n content: T | null;\n /** Screen-space position (relative to the canvas container) for the tooltip. */\n position: { x: number; y: number };\n}\n\n/**\n * Track mouse hover over canvas objects and return tooltip state.\n *\n * Listens to `mouse:over`, `mouse:out`, `after:render`, and `mouse:wheel` to\n * maintain a reactive tooltip state. The returned position is in screen-space\n * coordinates relative to the canvas container element — suitable for absolute\n * positioning of a tooltip component.\n *\n * @example\n * ```tsx\n * const tooltip = useCanvasTooltip(view.canvasRef, {\n * getContent: (obj) => obj.data ? { id: obj.data.id, type: obj.data.type } : null,\n * });\n *\n * return (\n * <>\n * <Canvas onReady={view.onReady} />\n * {tooltip.visible && (\n * <div style={{ position: 'absolute', left: tooltip.position.x, top: tooltip.position.y }}>\n * {tooltip.content?.id}\n * </div>\n * )}\n * </>\n * );\n * ```\n */\nexport function useCanvasTooltip<T>(\n canvasRef: RefObject<FabricCanvas | null>,\n options: UseCanvasTooltipOptions<T>,\n): CanvasTooltipState<T> {\n const [state, setState] = useState<CanvasTooltipState<T>>({\n visible: false,\n content: null,\n position: { x: 0, y: 0 },\n });\n\n const hoveredObjectRef = useRef<FabricObject | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n function calculatePosition(\n target: FabricObject,\n ): { x: number; y: number } | null {\n const bounds = target.getBoundingRect();\n const zoom = canvas!.getZoom();\n const vt = canvas!.viewportTransform;\n if (!vt) return null;\n\n return {\n x: (bounds.left + bounds.width / 2) * zoom + vt[4],\n y: bounds.top * zoom + vt[5] - 10,\n };\n }\n\n const handleMouseOver = (e: CanvasEvents['mouse:over']) => {\n const target = e.target;\n if (!target) return;\n\n const content = optionsRef.current.getContent(target);\n if (content === null) return;\n\n if (hoveredObjectRef.current !== target) {\n hoveredObjectRef.current = target;\n const position = calculatePosition(target);\n if (position) {\n setState({ visible: true, content, position });\n }\n }\n };\n\n const handleMouseOut = (e: CanvasEvents['mouse:out']) => {\n if (e.target && hoveredObjectRef.current === e.target) {\n setState((prev) => ({ ...prev, visible: false }));\n hoveredObjectRef.current = null;\n }\n };\n\n const updatePosition = () => {\n if (!hoveredObjectRef.current) return;\n const position = calculatePosition(hoveredObjectRef.current);\n if (position) {\n setState((prev) => (prev.visible ? { ...prev, position } : prev));\n }\n };\n\n canvas.on('mouse:over', handleMouseOver);\n canvas.on('mouse:out', handleMouseOut);\n canvas.on('after:render', updatePosition);\n canvas.on('mouse:wheel', updatePosition);\n\n return () => {\n canvas.off('mouse:over', handleMouseOver);\n canvas.off('mouse:out', handleMouseOut);\n canvas.off('after:render', updatePosition);\n canvas.off('mouse:wheel', updatePosition);\n };\n }, [canvasRef]);\n\n return state;\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport type {\n Canvas as FabricCanvas,\n CanvasEvents,\n FabricObject,\n} from 'fabric';\n\nexport interface UseCanvasClickOptions {\n /** Maximum movement in pixels before the gesture is treated as a pan. Default: 5. */\n threshold?: number;\n /** Maximum duration in milliseconds for the gesture to count as a click. Default: 300. */\n maxDuration?: number;\n}\n\n/**\n * Distinguish clicks from pan gestures on a canvas.\n *\n * On view-mode canvases where pan is always active, a regular Fabric `mouse:up`\n * fires for both clicks and drag-to-pan. This hook tracks pointer movement and\n * timing to determine whether the user intended a click or a pan, then calls\n * `onClick` only for genuine clicks.\n *\n * @example\n * ```tsx\n * useCanvasClick(view.canvasRef, (target) => {\n * if (target?.data?.id) {\n * navigate(`/locations/${target.data.id}`);\n * }\n * });\n * ```\n */\nexport function useCanvasClick(\n canvasRef: RefObject<FabricCanvas | null>,\n onClick: (target: FabricObject | undefined) => void,\n options?: UseCanvasClickOptions,\n): void {\n const onClickRef = useRef(onClick);\n onClickRef.current = onClick;\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n let mouseDown: { x: number; y: number; time: number } | null = null;\n let isPanning = false;\n\n const handleMouseDown = (e: CanvasEvents['mouse:down']) => {\n const native = e.e instanceof TouchEvent ? e.e.touches[0] : e.e;\n if (native) {\n mouseDown = { x: native.clientX, y: native.clientY, time: Date.now() };\n isPanning = false;\n }\n };\n\n const handleMouseMove = (e: CanvasEvents['mouse:move']) => {\n if (!mouseDown) return;\n const native = e.e instanceof TouchEvent ? e.e.touches[0] : e.e;\n if (!native) return;\n\n const threshold = optionsRef.current?.threshold ?? 5;\n const deltaX = Math.abs(native.clientX - mouseDown.x);\n const deltaY = Math.abs(native.clientY - mouseDown.y);\n if (deltaX > threshold || deltaY > threshold) {\n isPanning = true;\n }\n };\n\n const handleMouseUp = (e: CanvasEvents['mouse:up']) => {\n if (!mouseDown) return;\n\n const maxDuration = optionsRef.current?.maxDuration ?? 300;\n const elapsed = Date.now() - mouseDown.time;\n\n if (!isPanning && elapsed < maxDuration) {\n onClickRef.current(e.target);\n }\n\n mouseDown = null;\n isPanning = false;\n };\n\n canvas.on('mouse:down', handleMouseDown);\n canvas.on('mouse:move', handleMouseMove);\n canvas.on('mouse:up', handleMouseUp);\n\n return () => {\n canvas.off('mouse:down', handleMouseDown);\n canvas.off('mouse:move', handleMouseMove);\n canvas.off('mouse:up', handleMouseUp);\n };\n }, [canvasRef]);\n}\n","import { useEffect, useRef, type RefObject, type ReactNode } from 'react';\nimport { Stack } from '@mui/material';\nimport type { StackProps } from '@mui/material';\nimport type { Canvas as FabricCanvas, FabricObject } from 'fabric';\nimport { util } from 'fabric';\n\nexport interface ObjectOverlayProps extends StackProps {\n /** Ref to the Fabric canvas instance. */\n canvasRef: RefObject<FabricCanvas | null>;\n /** The Fabric object to overlay. When `null`/`undefined`, nothing renders. */\n object: FabricObject | null | undefined;\n children?: ReactNode;\n}\n\n/**\n * A MUI `Stack` positioned absolutely over a Fabric canvas object, sized to\n * the object's screen-space dimensions and kept in sync with pan, zoom, move,\n * scale, and rotate transforms.\n *\n * Default styles: `position: absolute`, `pointerEvents: none`,\n * `alignItems: center`, `justifyContent: center`, `zIndex: 1`.\n * All can be overridden via the `sx` prop.\n *\n * Must be rendered inside a container that is `position: relative` and wraps\n * the `<Canvas>` component (e.g. the `canvasOverlay` slot of `DemoLayout`).\n *\n * @example\n * ```tsx\n * <ObjectOverlay canvasRef={canvasRef} object={deskObj}>\n * <OverlayContent>\n * <MyLabel>{desk.name}</MyLabel>\n * </OverlayContent>\n * </ObjectOverlay>\n * ```\n */\nexport function ObjectOverlay({\n canvasRef,\n object,\n sx,\n children,\n ...rest\n}: ObjectOverlayProps) {\n const stackRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas || !object) return;\n\n function update() {\n const el = stackRef.current;\n if (!el || !canvas || !object) return;\n\n const zoom = canvas.getZoom();\n const vt = canvas.viewportTransform;\n if (!vt) return;\n\n const center = object.getCenterPoint();\n const actualWidth = (object.width ?? 0) * (object.scaleX ?? 1);\n const actualHeight = (object.height ?? 0) * (object.scaleY ?? 1);\n const screenCoords = util.transformPoint(center, vt);\n const screenWidth = actualWidth * zoom;\n const screenHeight = actualHeight * zoom;\n const angle = object.angle ?? 0;\n\n el.style.left = `${screenCoords.x - screenWidth / 2}px`;\n el.style.top = `${screenCoords.y - screenHeight / 2}px`;\n el.style.width = `${screenWidth}px`;\n el.style.height = `${screenHeight}px`;\n el.style.transform = angle !== 0 ? `rotate(${angle}deg)` : '';\n }\n\n update();\n\n canvas.on('after:render', update);\n object.on('moving', update);\n object.on('scaling', update);\n object.on('rotating', update);\n\n return () => {\n canvas.off('after:render', update);\n object.off('moving', update);\n object.off('scaling', update);\n object.off('rotating', update);\n };\n }, [canvasRef, object]);\n\n if (!object) return null;\n\n return (\n <Stack\n ref={stackRef}\n sx={{\n position: 'absolute',\n pointerEvents: 'none',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 1,\n overflow: 'hidden',\n ...sx,\n }}\n {...rest}\n >\n {children}\n </Stack>\n );\n}\n","import { Stack, StackProps } from '@mui/material';\nimport { useEffect, useRef, type ReactNode } from 'react';\n\nexport interface OverlayContentProps extends StackProps {\n children?: ReactNode;\n /** Padding in pixels between the content and the parent bounds. Default: 4 */\n padding?: number;\n /** Maximum scale factor applied to the content. Default: 2 */\n maxScale?: number;\n}\n\n/**\n * Scales its children to fit within the parent's bounds (typically an\n * `ObjectOverlay`). Content is measured at its natural size then scaled\n * down or up (capped at `maxScale`) to fill the available space while\n * maintaining aspect ratio.\n *\n * @example\n * ```tsx\n * <ObjectOverlay canvasRef={canvasRef} object={obj}>\n * <OverlayContent padding={4} maxScale={2}>\n * <MyBadge>{label}</MyBadge>\n * </OverlayContent>\n * </ObjectOverlay>\n * ```\n */\nexport function OverlayContent({\n children,\n padding = 4,\n maxScale = 2,\n sx,\n ...rest\n}: OverlayContentProps) {\n const outerRef = useRef<HTMLDivElement>(null);\n const innerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const outer = outerRef.current;\n const inner = innerRef.current;\n if (!outer || !inner) return;\n\n function fit() {\n if (!outer || !inner) return;\n\n const containerW = outer.clientWidth;\n const containerH = outer.clientHeight;\n\n // scrollWidth/scrollHeight give the intrinsic size regardless of\n // CSS transforms, so we always get the natural content dimensions.\n const natW = inner.scrollWidth;\n const natH = inner.scrollHeight;\n\n if (natW === 0 || natH === 0 || containerW <= 0 || containerH <= 0) {\n inner.style.transform = '';\n inner.style.removeProperty('--overlay-scale');\n return;\n }\n\n // Padding is added to the content's effective size so it scales\n // proportionally with the content — never causing overflow.\n const scale = Math.min(\n containerW / (natW + padding * 2),\n containerH / (natH + padding * 2),\n maxScale,\n );\n inner.style.transform = `scale(${scale})`;\n // Expose the scale so `FixedSizeContent` children can counter-scale.\n inner.style.setProperty('--overlay-scale', String(scale));\n }\n\n const observer = new ResizeObserver(fit);\n observer.observe(outer);\n // Also observe the inner Stack so we recalculate when children toggle\n // display (e.g. FixedSizeContent collapsing via display:none).\n observer.observe(inner);\n fit();\n\n return () => observer.disconnect();\n }, [padding, maxScale]);\n\n return (\n <Stack\n ref={outerRef}\n sx={{\n width: '100%',\n height: '100%',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n ...sx,\n }}\n {...rest}\n >\n <Stack\n ref={innerRef}\n sx={{\n alignItems: 'center',\n transformOrigin: 'center center',\n flexShrink: 0,\n width: 'max-content',\n }}\n >\n {children}\n </Stack>\n </Stack>\n );\n}\n","import { Stack, StackProps } from '@mui/material';\nimport { useEffect, useRef, type ReactNode } from 'react';\n\nexport interface FixedSizeContentProps extends StackProps {\n children?: ReactNode;\n /**\n * When `true` (default), content is collapsed (`display: none`) if showing\n * it would cause `OverlayContent` to shrink below its natural size\n * vertically. Horizontal overflow is always handled by truncation (ellipsis).\n * When `false`, no automatic hiding is applied.\n */\n hideOnOverflow?: boolean;\n /**\n * Horizontal padding in pixels subtracted from the available width when\n * calculating the truncation `maxWidth`. Default: 4\n */\n truncationPadding?: number;\n}\n\n/**\n * Keeps children at their natural CSS pixel size inside an `OverlayContent`,\n * counter-scaling against `OverlayContent`'s fit-to-container transform.\n * Icons and other siblings still scale to fill the object bounds as usual,\n * while text wrapped in `FixedSizeContent` stays at a constant screen size.\n *\n * Text that overflows horizontally is truncated with an ellipsis. When\n * `hideOnOverflow` is `true` (default), the element is fully collapsed\n * (`display: none`) as soon as the overlay container is too short to fit\n * both the fixed-size content and its siblings at natural size, so\n * `OverlayContent` recalculates its scale using only the remaining siblings.\n * Once there is enough room again (e.g. zooming in), the element reappears.\n *\n * Uses the `--overlay-scale` CSS custom property set by `OverlayContent`.\n * When used outside `OverlayContent` (e.g. directly inside `ObjectOverlay`),\n * the fallback of 1 means no counter-scaling is applied and children render\n * at their natural size.\n *\n * @example\n * ```tsx\n * <ObjectOverlay canvasRef={canvasRef} object={obj}>\n * <OverlayContent>\n * <Stack alignItems=\"center\">\n * <MyIcon /> {// scales to fit}\n * <FixedSizeContent>\n * <Typography noWrap>Always 14px, truncates</Typography>\n * </FixedSizeContent>\n * </Stack>\n * </OverlayContent>\n * </ObjectOverlay>\n * ```\n */\nexport function FixedSizeContent({\n children,\n hideOnOverflow = true,\n truncationPadding = 4,\n sx,\n ...rest\n}: FixedSizeContentProps) {\n const ref = useRef<HTMLDivElement>(null);\n // Total parent content height (siblings + this text) measured while visible.\n // Used to decide whether showing the text would cause OverlayContent to\n // shrink below scale 1. Stable across show/hide cycles because it is only\n // updated while the element is visible.\n const totalContentHeightRef = useRef(0);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n // Find the nearest ancestor with overflow:hidden (OverlayContent outer\n // Stack or ObjectOverlay's Stack) to use as the clipping boundary.\n // Its size is set by ObjectOverlay based on zoom and object dimensions,\n // independent of content, so there is no feedback loop.\n let clipAncestor: HTMLElement | null = el.parentElement;\n while (clipAncestor) {\n if (getComputedStyle(clipAncestor).overflow === 'hidden') break;\n clipAncestor = clipAncestor.parentElement;\n }\n if (!clipAncestor) return;\n\n const ancestor = clipAncestor;\n\n // Measure total content height while the element is visible on mount.\n totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;\n\n function check() {\n // Defer to the next frame so OverlayContent's ResizeObserver has\n // already updated --overlay-scale.\n requestAnimationFrame(() => {\n if (!el) return;\n const containerRect = ancestor.getBoundingClientRect();\n\n // Constrain width to the container (minus padding) so text truncates\n // with ellipsis before reaching the edge.\n el.style.maxWidth = `${Math.max(0, containerRect.width - truncationPadding * 2)}px`;\n\n if (!hideOnOverflow) return;\n\n // Check whether the total content (siblings + this text) fits\n // vertically at natural size. This comparison is stable because\n // totalContentHeightRef is only updated while the element is visible,\n // so the cached value does not change when the element hides.\n const fits = containerRect.height >= totalContentHeightRef.current;\n\n if (fits && el.style.display === 'none') {\n // Show, then re-measure in case content changed while hidden.\n el.style.display = '';\n totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;\n // Re-check with the updated measurement to prevent showing\n // when the content barely doesn't fit.\n if (containerRect.height < totalContentHeightRef.current) {\n el.style.display = 'none';\n }\n } else if (!fits && el.style.display !== 'none') {\n el.style.display = 'none';\n }\n\n // Keep the cache fresh while visible.\n if (el.style.display !== 'none') {\n totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;\n }\n });\n }\n\n const observer = new ResizeObserver(check);\n observer.observe(ancestor);\n check();\n\n return () => observer.disconnect();\n }, [hideOnOverflow, truncationPadding]);\n\n return (\n <Stack\n ref={ref}\n sx={{\n transform: 'scale(calc(1 / var(--overlay-scale, 1)))',\n transformOrigin: 'center center',\n flexShrink: 0,\n width: 'max-content',\n overflow: 'hidden',\n '& > *': {\n maxWidth: '100%',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n },\n ...sx,\n }}\n {...rest}\n >\n {children}\n </Stack>\n );\n}\n","// Fabric.js module augmentation — adds `shapeType`, `data`, and `lockLightMode`\n// to FabricObject/Canvas. Importing here ensures the augmentation is emitted in\n// the dist and automatically applied for consumers of the package.\nimport './fabricAugmentation';\n\n// --- Component ---\nexport { Canvas } from './Canvas';\nexport type { CanvasProps } from './Canvas';\n\n// --- Hooks ---\nexport { useEditCanvas } from './hooks';\nexport type { UseEditCanvasOptions } from './hooks';\nexport { useViewCanvas } from './hooks';\nexport type { UseViewCanvasOptions, ViewObjectStyle } from './hooks';\nexport { useCanvasEvents } from './hooks';\nexport type { CanvasEventHandlers } from './hooks';\nexport { useCanvasTooltip } from './hooks';\nexport type { UseCanvasTooltipOptions, CanvasTooltipState } from './hooks';\nexport { useCanvasClick } from './hooks';\nexport type { UseCanvasClickOptions } from './hooks';\n// --- Overlay ---\nexport { ObjectOverlay } from './overlay';\nexport type { ObjectOverlayProps } from './overlay';\nexport { OverlayContent } from './overlay';\nexport type { OverlayContentProps } from './overlay';\nexport { FixedSizeContent } from './overlay';\nexport type { FixedSizeContentProps } from './overlay';\n\n// --- Types ---\nexport type {\n Point2D,\n ShapeStyleOptions,\n SnappingOptions,\n InteractionModeOptions,\n SnappableInteractionOptions,\n DragBounds,\n ModeSetup,\n CanvasJSON,\n} from './types';\nexport type { ObjectDataType } from './fabricAugmentation';\n\n// --- Shapes ---\nexport {\n createRectangle,\n createRectangleAtPoint,\n editRectangle,\n} from './shapes';\nexport type { RectangleOptions, RectangleAtPointOptions } from './shapes';\n\nexport { createCircle, createCircleAtPoint, editCircle } from './shapes';\nexport type { CircleOptions, CircleAtPointOptions } from './shapes';\n\nexport {\n createPolygon,\n createPolygonAtPoint,\n createPolygonFromDrag,\n createPolygonFromVertices,\n editPolygon,\n} from './shapes';\nexport type { PolygonOptions } from './shapes';\n\n// --- Interactions ---\nexport { enableClickToCreate } from './interactions';\nexport { enableDragToCreate } from './interactions';\nexport type { DragToCreateOptions } from './interactions';\nexport { enableDrawToCreate } from './interactions';\nexport type { DrawToCreateOptions } from './interactions';\nexport { enableVertexEdit } from './interactions';\nexport type { VertexEditOptions } from './interactions';\n\n// --- Viewport ---\nexport { enablePanAndZoom, resetViewport } from './viewport';\nexport type {\n ViewportController,\n ViewportMode,\n PanAndZoomOptions,\n PanToObjectOptions,\n ZoomToFitOptions,\n} from './viewport';\n\n// --- Alignment ---\nexport { getSnapPoints, registerSnapPointExtractor } from './alignment';\nexport type { SnapPointExtractor } from './alignment';\nexport { enableObjectAlignment } from './alignment';\nexport type { ObjectAlignmentOptions } from './alignment';\nexport { enableRotationSnap } from './alignment';\nexport type { RotationSnapOptions } from './alignment';\nexport { snapCursorPoint } from './alignment';\nexport type {\n CursorSnapResult,\n CursorSnapOptions,\n GuidelineStyle,\n} from './alignment';\n\n// --- Keyboard ---\nexport { deleteObjects, enableKeyboardShortcuts } from './keyboard';\n\n// --- History ---\nexport { createHistoryTracker } from './history';\nexport type { HistoryOptions, HistoryTracker } from './history';\n\n// --- Serialization ---\nexport {\n enableScaledStrokes,\n enableScaledBorderRadius,\n serializeCanvas,\n loadCanvas,\n getBaseStrokeWidth,\n} from './serialization';\nexport type {\n SerializeOptions,\n LoadCanvasOptions,\n ScaledBorderRadiusOptions,\n} from './serialization';\n\n// --- Background ---\nexport {\n fitViewportToBackground,\n getBackgroundSrc,\n setBackgroundContrast,\n getBackgroundContrast,\n setBackgroundInverted,\n getBackgroundInverted,\n resizeImageUrl,\n setBackgroundImage,\n} from './background';\nexport type {\n FitViewportOptions,\n ResizeResult,\n ResizeImageOptions,\n SetBackgroundImageOptions,\n} from './background';\n\n// --- Styles ---\nexport {\n DEFAULT_CONTROL_STYLE,\n DEFAULT_SHAPE_STYLE,\n DEFAULT_CIRCLE_STYLE,\n DEFAULT_DRAG_SHAPE_STYLE,\n DEFAULT_GUIDELINE_SHAPE_STYLE,\n DEFAULT_ALIGNMENT_STYLE,\n} from './styles';\n\n// --- Fabric re-exports ---\n// Re-export commonly used Fabric types so consumers don't need to import\n// from both '@bwp-web/canvas' and 'fabric' (avoids type boundary mismatches).\nexport {\n Canvas as FabricCanvas,\n FabricObject,\n FabricImage,\n Rect,\n Polygon,\n Point,\n util,\n} from 'fabric';\n"],"mappings":"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","names":["useEffect","useRef","Polygon","viewportTransform","resetViewport","Point","Point","Point","util","util","Point","Point","drawXMarker","Rect","Point","Point","Rect","obj","Point","Rect","Rect","Rect","Rect","Polygon","Polygon","Point","Point","util","Point","util","FabricImage","Rect","Rect","FabricImage","useRef","Polygon","useEffect","resetViewport","useCallback","useRef","useState","useRef","useState","useCallback","resetViewport","useEffect","useRef","useEffect","useRef","useState","useEffect","useRef","useEffect","useRef","util","jsx","Stack","useEffect","useRef","jsx","Stack","useEffect","useRef","jsx","Canvas","FabricObject","FabricImage","Rect","Polygon","Point","util"]}