@bwp-web/canvas 0.4.2 → 0.4.3
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/Canvas/Canvas.d.ts +11 -1
- package/dist/Canvas/Canvas.d.ts.map +1 -1
- package/dist/background.d.ts +5 -0
- package/dist/background.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/shared.d.ts +3 -2
- package/dist/hooks/shared.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts +2 -0
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/hooks/useObjectOverlay.d.ts +49 -0
- package/dist/hooks/useObjectOverlay.d.ts.map +1 -0
- package/dist/hooks/useViewCanvas.d.ts +3 -1
- package/dist/hooks/useViewCanvas.d.ts.map +1 -1
- package/dist/index.cjs +201 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +196 -17
- package/dist/index.js.map +1 -1
- package/dist/interactions/dragToCreate.d.ts +2 -0
- package/dist/interactions/dragToCreate.d.ts.map +1 -1
- package/dist/interactions/drawToCreate.d.ts +7 -1
- package/dist/interactions/drawToCreate.d.ts.map +1 -1
- package/dist/serialization.d.ts +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/viewport.d.ts +12 -1
- package/dist/viewport.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/fabricAugmentation.ts","../src/Canvas/Canvas.tsx","../src/keyboard.ts","../src/hooks/useEditCanvas.ts","../src/viewport.ts","../src/constants.ts","../src/background.ts","../src/hooks/shared.ts","../src/alignment/snapPoints.ts","../src/alignment/objectAlignmentUtils.ts","../src/alignment/objectAlignment.ts","../src/alignment/objectAlignmentRendering.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/hooks/useViewCanvas.ts","../src/hooks/useCanvasEvents.ts","../src/hooks/useCanvasTooltip.ts","../src/hooks/useCanvasClick.ts"],"sourcesContent":["// 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\n// --- Types ---\nexport type {\n Point2D,\n ShapeStyleOptions,\n SnappingOptions,\n InteractionModeOptions,\n SnappableInteractionOptions,\n DragBounds,\n ModeSetup,\n} from './types';\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, PolygonStyleOptions } 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} 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// --- Serialization ---\nexport {\n enableScaledStrokes,\n serializeCanvas,\n loadCanvas,\n getBaseStrokeWidth,\n} from './serialization';\nexport type { SerializeOptions, LoadCanvasOptions } from './serialization';\n\n// --- Background ---\nexport {\n fitViewportToBackground,\n setBackgroundOpacity,\n getBackgroundOpacity,\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} from 'fabric';\n","import 'fabric';\n\nexport type ShapeType = 'circle';\n\ndeclare module 'fabric' {\n interface FabricObject {\n shapeType?: ShapeType;\n data?: {\n type: 'PLACE' | 'DEVICE' | 'DESK' | 'PARKING_SPACE' | 'FACILITY';\n id: string;\n };\n }\n interface Canvas {\n lockLightMode?: boolean;\n }\n}\n","import { Canvas as FabricCanvas } from 'fabric';\nimport { CSSProperties, useEffect, useRef } from 'react';\nimport { enableKeyboardShortcuts } from '../keyboard';\n\nexport interface CanvasProps {\n width?: number;\n height?: number;\n className?: string;\n style?: CSSProperties;\n onReady?: (canvas: FabricCanvas) => void;\n}\n\nexport function Canvas({\n width = 800,\n height = 600,\n className,\n style,\n onReady,\n}: CanvasProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n useEffect(() => {\n const el = canvasRef.current;\n if (!el) {\n return;\n }\n\n const fabricCanvas = new FabricCanvas(el, { width, height });\n onReady?.(fabricCanvas);\n\n const cleanupShortcuts = enableKeyboardShortcuts(fabricCanvas);\n\n return () => {\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 return (\n <div className={className} style={style}>\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 createViewportActions,\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 { enableScaledStrokes } from '../serialization';\nimport { enableKeyboardShortcuts } from '../keyboard';\nimport {\n fitViewportToBackground,\n setBackgroundImage as setBackgroundImageFn,\n type ResizeImageOptions,\n type SetBackgroundImageOptions,\n} from '../background';\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\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\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\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 * - Disables object selectability\n * - Calls `setup(canvas, viewport)` and stores its cleanup\n *\n * When `null` is passed, 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 canvas.selection = true;\n canvas.forEachObject((obj) => {\n obj.selectable = true;\n obj.evented = true;\n });\n } else {\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\n if (options?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (options?.keyboardShortcuts !== false) {\n keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);\n }\n\n // Set canvas-level alignment state so interaction modes inherit it\n setCanvasAlignmentEnabled(canvas, options?.enableAlignment);\n\n if (options?.panAndZoom !== false) {\n viewportRef.current = enablePanAndZoom(\n canvas,\n typeof options?.panAndZoom === 'object'\n ? options.panAndZoom\n : undefined,\n );\n }\n\n const alignmentEnabled = resolveAlignmentEnabled(\n options?.enableAlignment,\n options?.alignment,\n );\n\n if (alignmentEnabled) {\n alignmentCleanupRef.current = enableObjectAlignment(\n canvas,\n typeof options?.alignment === 'object'\n ? options.alignment\n : undefined,\n );\n }\n\n if (options?.rotationSnap !== false) {\n rotationSnapCleanupRef.current = enableRotationSnap(\n canvas,\n typeof options?.rotationSnap === 'object'\n ? options.rotationSnap\n : undefined,\n );\n }\n\n canvas.on('mouse:wheel', () => {\n setZoom(canvas.getZoom());\n });\n\n canvas.on('selection:created', (e) => {\n setSelected(e.selected ?? []);\n });\n\n canvas.on('selection:updated', (e) => {\n setSelected(e.selected ?? []);\n });\n\n canvas.on('selection:cleared', () => {\n setSelected([]);\n });\n\n // Dirty tracking — mark canvas as modified on any object mutation\n if (options?.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 // Auto-setup vertex edit on double-click for polygons\n if (options?.vertexEdit !== false) {\n const vertexOpts =\n typeof options?.vertexEdit === 'object'\n ? options.vertexEdit\n : undefined;\n\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 vertexOpts,\n () => {\n vertexEditCleanupRef.current = null;\n setIsEditingVertices(false);\n },\n );\n setIsEditingVertices(true);\n }\n });\n }\n\n const onReadyResult = options?.onReady?.(canvas);\n if (options?.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 // eslint-disable-next-line react-hooks/exhaustive-deps\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 } = createViewportActions(\n canvasRef,\n viewportRef,\n setZoom,\n );\n\n const setBackground = useCallback(\n async (url: string, bgOpts?: { preserveOpacity?: boolean }) => {\n const canvas = canvasRef.current;\n if (!canvas) throw new Error('Canvas not ready');\n\n const resizeOpts: SetBackgroundImageOptions | undefined =\n options?.backgroundResize !== false\n ? typeof options?.backgroundResize === 'object'\n ? { ...options.backgroundResize, ...bgOpts }\n : { ...bgOpts }\n : bgOpts?.preserveOpacity\n ? { preserveOpacity: true }\n : undefined;\n\n const img = await setBackgroundImageFn(canvas, url, resizeOpts);\n\n if (options?.autoFitToBackground !== false) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n\n return img;\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\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 },\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 `{ preserveOpacity: true }` to keep the current background opacity\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 };\n}\n","import { Canvas as FabricCanvas, Point, TPointerEvent } 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 PanAndZoomOptions {\n /** Minimum zoom level (default: 0.2) */\n minZoom?: number;\n /** Maximum zoom level (default: 10) */\n maxZoom?: number;\n /** Zoom sensitivity — larger values zoom faster (default: 1.03) */\n zoomFactor?: number;\n /** Initial viewport mode (default: 'select') */\n initialMode?: ViewportMode;\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 `step` zoom units.\n * Respects the configured min/max zoom bounds. Default step: 0.2.\n */\n zoomIn: (step?: number) => void;\n /**\n * Zoom out from the canvas center by `step` zoom units.\n * Respects the configured min/max zoom bounds. Default step: 0.2.\n */\n zoomOut: (step?: number) => 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 = delta < 0 ? zoom * zoomFactor : zoom / zoomFactor;\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 const isEnabled = () => enabled;\n const getMode = () => mode;\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(step = DEFAULT_ZOOM_STEP) {\n const z = Math.min(canvas.getZoom() + step, bounds.maxZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n zoomOut(step = DEFAULT_ZOOM_STEP) {\n const z = Math.max(canvas.getZoom() - step, bounds.minZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n cleanup() {\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 multiplier (applied per wheel tick). */\nexport const DEFAULT_ZOOM_FACTOR = 1.03;\n/** Default step for programmatic zoomIn/zoomOut. */\nexport const DEFAULT_ZOOM_STEP = 0.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// --- 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 { 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// --- 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// --- Opacity ---\n\n/**\n * Set the opacity of the canvas background image.\n * Value is clamped to the 0–1 range.\n */\nexport function setBackgroundOpacity(\n canvas: FabricCanvas,\n opacity: number,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n bg.set('opacity', Math.min(1, Math.max(0, opacity)));\n canvas.requestRenderAll();\n}\n\n/**\n * Get the current opacity of the canvas background image.\n * Returns 1 if no background image is set.\n */\nexport function getBackgroundOpacity(canvas: FabricCanvas): number {\n const bg = getBackgroundImage(canvas);\n return bg?.opacity ?? 1;\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 opacity when replacing the image. */\n preserveOpacity?: 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 opacity 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 prevOpacity = options?.preserveOpacity\n ? getBackgroundOpacity(canvas)\n : undefined;\n\n let imageUrl = url;\n if (options !== undefined) {\n const result = await resizeImageUrl(url, options);\n imageUrl = result.url;\n }\n const img = await FabricImage.fromURL(imageUrl, { crossOrigin: 'anonymous' });\n canvas.backgroundImage = img;\n\n if (prevOpacity !== undefined && prevOpacity !== 1) {\n img.set('opacity', prevOpacity);\n }\n\n canvas.requestRenderAll();\n return img;\n}\n","import { type RefObject } from 'react';\nimport { Canvas as FabricCanvas } from 'fabric';\nimport {\n resetViewport as resetViewportFn,\n type ViewportController,\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 resetViewport, zoomIn, and zoomOut actions shared between\n * useEditCanvas and useViewCanvas.\n */\nexport function createViewportActions(\n canvasRef: RefObject<FabricCanvas | null>,\n viewportRef: RefObject<ViewportController | null>,\n setZoom: (z: number) => void,\n) {\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 return { resetViewport, zoomIn, zoomOut };\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 { 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 { BASE_CANVAS_SIZE, 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 type AlignmentRenderConfig,\n drawMarkerList,\n drawVerticalAlignmentLines,\n drawHorizontalAlignmentLines,\n} from './objectAlignmentRendering';\nimport {\n collectMovingAlignmentLines,\n collectVerticalSnapOffset,\n collectHorizontalSnapOffset,\n} from './objectAlignmentMath';\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 const zoom = this.canvas.getZoom();\n const sizeScale = this.scaleWithCanvasSize\n ? Math.max(this.canvas.width ?? 800, this.canvas.height ?? 600) /\n BASE_CANVAS_SIZE\n : 1;\n return (this.margin * sizeScale) / zoom;\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 { Canvas, Point } from 'fabric';\nimport type { AlignmentLine } from './objectAlignmentUtils';\n\n/** Style configuration for alignment guideline rendering. */\nexport interface AlignmentRenderConfig {\n canvas: Canvas;\n width: number;\n color: string;\n xSize: number;\n lineDash?: number[];\n}\n\n/** Draw a single alignment line between two points, with X markers at each end. */\nexport function 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\n/** Draw an X marker at a point. */\nexport function 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\n/** Draw X markers only (no lines) for a list of alignment lines. */\nexport function 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\n/** Draw vertical alignment lines (lines connecting points at the same X coordinate). */\nexport function 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\n/** Draw horizontal alignment lines (lines connecting points at the same Y coordinate). */\nexport function 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","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 { DEFAULT_SNAP_MARGIN, BASE_CANVAS_SIZE } 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 zoom = canvas.getZoom();\n const scaleWithSize = options?.scaleWithCanvasSize !== false;\n const sizeScale = scaleWithSize\n ? Math.max(canvas.width ?? 800, canvas.height ?? 600) / BASE_CANVAS_SIZE\n : 1;\n const margin = ((options?.margin ?? DEFAULT_SNAP_MARGIN) * sizeScale) / zoom;\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\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 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 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 shiftTracker.cleanup();\n\n snapping.cleanup();\n\n if (isDrawing && previewRect) {\n canvas.remove(previewRect);\n canvas.requestRenderAll();\n }\n restoreViewport(options?.viewport);\n };\n}\n","import { alpha } from '@mui/material/styles';\nimport { biampTheme } from '@bwp-web/styles';\n\nconst { palette } = biampTheme();\n\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\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\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\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\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\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 { Point2D, SnappableInteractionOptions } from '../types';\nimport { createPolygonFromVertices, type PolygonStyleOptions } 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?: PolygonStyleOptions;\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 polygon = createPolygonFromVertices(canvas, points, options?.style);\n canvas.selection = previousSelection;\n canvas.requestRenderAll();\n\n restoreViewport(options?.viewport);\n options?.onCreated?.(polygon);\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 style?: PolygonStyleOptions,\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, ...style },\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 style?: PolygonStyleOptions,\n): Polygon {\n const polygon = new Polygon(\n points.map((p) => ({ x: p.x, y: p.y })),\n { ...DEFAULT_SHAPE_STYLE, ...style },\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}\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 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 onExit?.();\n }\n\n return cleanup;\n}\n","import { Canvas as FabricCanvas, Rect, type FabricObject } from 'fabric';\nimport { restoreCircleConstraints } from './shapes/circle';\nimport { DEFAULT_CONTROL_STYLE } from './styles';\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 * 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 * 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/**\n * Serialize the canvas to a plain object, ready for `JSON.stringify`.\n *\n * - Always includes the `'data'` and `'shapeType'` custom properties.\n * - If {@link enableScaledStrokes} is active, temporarily restores all stroke\n * widths to their base values before serializing, then reapplies the\n * zoom-scaled values. This ensures the saved JSON always contains the\n * intended visual stroke width, not the zoom-adjusted one.\n */\nexport function serializeCanvas(\n canvas: FabricCanvas,\n options?: SerializeOptions,\n): object {\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 // Save any currently scaled stroke widths and restore base values\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\n const json = canvas.toObject(properties);\n\n // Reapply the zoom-scaled values\n scaledWidths.forEach((scaled, obj) => {\n obj.strokeWidth = scaled;\n });\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\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: object,\n options?: LoadCanvasOptions,\n): Promise<void> {\n await canvas.loadFromJSON(json);\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 // Re-apply per-object state that Fabric does not persist through serialization.\n canvas.forEachObject((obj) => {\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 });\n canvas.requestRenderAll();\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject, Rect } from 'fabric';\nimport {\n enablePanAndZoom,\n type PanAndZoomOptions,\n type ViewportController,\n} from '../viewport';\nimport { enableScaledStrokes } from '../serialization';\nimport { fitViewportToBackground } from '../background';\nimport { createViewportActions, 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\nconst VIEW_BORDER_RADIUS = 4;\n\n/** Disable selection and interactivity on all objects, and apply view-only styles. */\nfunction lockCanvas(canvas: FabricCanvas) {\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n obj.evented = false;\n if (\n obj instanceof Rect &&\n obj.shapeType !== 'circle' &&\n obj.data?.type !== 'DEVICE'\n ) {\n // Compensate for non-uniform scaling so corners appear circular.\n const rx = VIEW_BORDER_RADIUS / (obj.scaleX ?? 1);\n const ry = VIEW_BORDER_RADIUS / (obj.scaleY ?? 1);\n obj.set({ rx, ry });\n }\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\n const [zoom, setZoom] = useState(1);\n\n const onReady = useCallback(\n (canvas: FabricCanvas) => {\n canvasRef.current = canvas;\n\n if (options?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (options?.panAndZoom !== false) {\n const panAndZoomOpts =\n typeof options?.panAndZoom === 'object' ? options.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 = options?.onReady?.(canvas);\n if (options?.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 // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n const { resetViewport, zoomIn, zoomOut } = createViewportActions(\n canvasRef,\n viewportRef,\n setZoom,\n );\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 },\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"],"mappings":"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","names":["import_fabric","FabricCanvas","import_react","import_fabric","import_fabric","import_fabric","resetViewport","import_fabric","import_fabric","import_fabric","import_fabric","drawXMarker","import_fabric","import_styles","import_fabric","import_fabric","import_fabric","import_fabric","import_fabric","import_fabric","import_fabric","resetViewport","import_react","import_fabric","resetViewport","import_react","import_react","import_react","import_fabric"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/fabricAugmentation.ts","../src/Canvas/Canvas.tsx","../src/keyboard.ts","../src/hooks/useEditCanvas.ts","../src/viewport.ts","../src/constants.ts","../src/background.ts","../src/hooks/shared.ts","../src/alignment/snapPoints.ts","../src/alignment/objectAlignmentUtils.ts","../src/alignment/objectAlignment.ts","../src/alignment/objectAlignmentRendering.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/hooks/useViewCanvas.ts","../src/hooks/useCanvasEvents.ts","../src/hooks/useCanvasTooltip.ts","../src/hooks/useCanvasClick.ts","../src/hooks/useObjectOverlay.ts"],"sourcesContent":["// 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} from './types';\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, PolygonStyleOptions } 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} 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// --- Serialization ---\nexport {\n enableScaledStrokes,\n serializeCanvas,\n loadCanvas,\n getBaseStrokeWidth,\n} from './serialization';\nexport type { SerializeOptions, LoadCanvasOptions } from './serialization';\n\n// --- Background ---\nexport {\n fitViewportToBackground,\n getBackgroundSrc,\n setBackgroundOpacity,\n getBackgroundOpacity,\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","import 'fabric';\n\nexport type ShapeType = 'circle';\n\ndeclare module 'fabric' {\n interface FabricObject {\n shapeType?: ShapeType;\n data?: {\n type: 'PLACE' | 'DEVICE' | 'DESK' | 'PARKING_SPACE' | 'FACILITY';\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\nexport function Canvas({\n width,\n height,\n className,\n style,\n onReady,\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 width: initialWidth,\n height: initialHeight,\n });\n\n onReady?.(fabricCanvas);\n\n const cleanupShortcuts = enableKeyboardShortcuts(fabricCanvas);\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 createViewportActions,\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 { enableScaledStrokes } from '../serialization';\nimport { enableKeyboardShortcuts } from '../keyboard';\nimport {\n fitViewportToBackground,\n setBackgroundImage as setBackgroundImageFn,\n type ResizeImageOptions,\n type SetBackgroundImageOptions,\n} from '../background';\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\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\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\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 * - Disables object selectability\n * - Calls `setup(canvas, viewport)` and stores its cleanup\n *\n * When `null` is passed, 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 canvas.selection = true;\n canvas.forEachObject((obj) => {\n obj.selectable = true;\n obj.evented = true;\n });\n } else {\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\n if (options?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (options?.keyboardShortcuts !== false) {\n keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);\n }\n\n // Set canvas-level alignment state so interaction modes inherit it\n setCanvasAlignmentEnabled(canvas, options?.enableAlignment);\n\n if (options?.panAndZoom !== false) {\n viewportRef.current = enablePanAndZoom(\n canvas,\n typeof options?.panAndZoom === 'object'\n ? options.panAndZoom\n : undefined,\n );\n }\n\n const alignmentEnabled = resolveAlignmentEnabled(\n options?.enableAlignment,\n options?.alignment,\n );\n\n if (alignmentEnabled) {\n alignmentCleanupRef.current = enableObjectAlignment(\n canvas,\n typeof options?.alignment === 'object'\n ? options.alignment\n : undefined,\n );\n }\n\n if (options?.rotationSnap !== false) {\n rotationSnapCleanupRef.current = enableRotationSnap(\n canvas,\n typeof options?.rotationSnap === 'object'\n ? options.rotationSnap\n : undefined,\n );\n }\n\n canvas.on('mouse:wheel', () => {\n setZoom(canvas.getZoom());\n });\n\n canvas.on('selection:created', (e) => {\n setSelected(e.selected ?? []);\n });\n\n canvas.on('selection:updated', (e) => {\n setSelected(e.selected ?? []);\n });\n\n canvas.on('selection:cleared', () => {\n setSelected([]);\n });\n\n // Dirty tracking — mark canvas as modified on any object mutation\n if (options?.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 // Auto-setup vertex edit on double-click for polygons\n if (options?.vertexEdit !== false) {\n const vertexOpts =\n typeof options?.vertexEdit === 'object'\n ? options.vertexEdit\n : undefined;\n\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 vertexOpts,\n () => {\n vertexEditCleanupRef.current = null;\n setIsEditingVertices(false);\n },\n );\n setIsEditingVertices(true);\n }\n });\n }\n\n const onReadyResult = options?.onReady?.(canvas);\n if (options?.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 // eslint-disable-next-line react-hooks/exhaustive-deps\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 } = createViewportActions(\n canvasRef,\n viewportRef,\n setZoom,\n );\n\n const setBackground = useCallback(\n async (url: string, bgOpts?: { preserveOpacity?: boolean }) => {\n const canvas = canvasRef.current;\n if (!canvas) throw new Error('Canvas not ready');\n\n const resizeOpts: SetBackgroundImageOptions | undefined =\n options?.backgroundResize !== false\n ? typeof options?.backgroundResize === 'object'\n ? { ...options.backgroundResize, ...bgOpts }\n : { ...bgOpts }\n : bgOpts?.preserveOpacity\n ? { preserveOpacity: true }\n : undefined;\n\n const img = await setBackgroundImageFn(canvas, url, resizeOpts);\n\n if (options?.autoFitToBackground !== false) {\n fitViewportToBackground(canvas);\n syncZoom(canvasRef, setZoom);\n }\n\n return img;\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\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 },\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 `{ preserveOpacity: true }` to keep the current background opacity\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 };\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 — larger values zoom faster (default: 1.03) */\n zoomFactor?: number;\n /** Initial viewport mode (default: 'select') */\n initialMode?: ViewportMode;\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 `step` zoom units.\n * Respects the configured min/max zoom bounds. Default step: 0.2.\n */\n zoomIn: (step?: number) => void;\n /**\n * Zoom out from the canvas center by `step` zoom units.\n * Respects the configured min/max zoom bounds. Default step: 0.2.\n */\n zoomOut: (step?: number) => void;\n /**\n * Pan the viewport so the given object is centered on the canvas.\n * Optionally animate the transition.\n */\n panToObject: (object: FabricObject, options?: PanToObjectOptions) => 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 = delta < 0 ? zoom * zoomFactor : zoom / zoomFactor;\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 const isEnabled = () => enabled;\n const getMode = () => mode;\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(step = DEFAULT_ZOOM_STEP) {\n const z = Math.min(canvas.getZoom() + step, bounds.maxZoom);\n canvas.zoomToPoint(\n new Point(canvas.getWidth() / 2, canvas.getHeight() / 2),\n z,\n );\n },\n\n zoomOut(step = DEFAULT_ZOOM_STEP) {\n const z = Math.max(canvas.getZoom() - step, 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 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 vt = canvas.viewportTransform;\n if (!vt) return;\n canvas.setViewportTransform([\n vt[0],\n vt[1],\n vt[2],\n vt[3],\n targetX,\n targetY,\n ]);\n return;\n }\n\n const duration = panOpts.duration ?? 300;\n const vt = canvas.viewportTransform;\n if (!vt) return;\n const startX = vt[4];\n const startY = vt[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 currentVt = canvas.viewportTransform;\n if (!currentVt) return;\n canvas.setViewportTransform([\n currentVt[0],\n currentVt[1],\n currentVt[2],\n currentVt[3],\n currentX,\n currentY,\n ]);\n\n if (t < 1) {\n requestAnimationFrame(step);\n }\n }\n\n requestAnimationFrame(step);\n },\n\n cleanup() {\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 multiplier (applied per wheel tick). */\nexport const DEFAULT_ZOOM_FACTOR = 1.03;\n/** Default step for programmatic zoomIn/zoomOut. */\nexport const DEFAULT_ZOOM_STEP = 0.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// --- 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 { 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// --- Opacity ---\n\n/**\n * Set the opacity of the canvas background image.\n * Value is clamped to the 0–1 range.\n */\nexport function setBackgroundOpacity(\n canvas: FabricCanvas,\n opacity: number,\n): void {\n const bg = getBackgroundImage(canvas);\n if (!bg) return;\n bg.set('opacity', Math.min(1, Math.max(0, opacity)));\n canvas.requestRenderAll();\n}\n\n/**\n * Get the current opacity of the canvas background image.\n * Returns 1 if no background image is set.\n */\nexport function getBackgroundOpacity(canvas: FabricCanvas): number {\n const bg = getBackgroundImage(canvas);\n return bg?.opacity ?? 1;\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 opacity when replacing the image. */\n preserveOpacity?: 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 opacity 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 prevOpacity = options?.preserveOpacity\n ? getBackgroundOpacity(canvas)\n : undefined;\n\n let imageUrl = url;\n if (options !== undefined) {\n const result = await resizeImageUrl(url, options);\n imageUrl = result.url;\n }\n const img = await FabricImage.fromURL(imageUrl, { crossOrigin: 'anonymous' });\n canvas.backgroundImage = img;\n\n if (prevOpacity !== undefined && prevOpacity !== 1) {\n img.set('opacity', prevOpacity);\n }\n\n canvas.requestRenderAll();\n return img;\n}\n","import { type RefObject } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject } from 'fabric';\nimport {\n resetViewport as resetViewportFn,\n type PanToObjectOptions,\n type ViewportController,\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 resetViewport, zoomIn, and zoomOut actions shared between\n * useEditCanvas and useViewCanvas.\n */\nexport function createViewportActions(\n canvasRef: RefObject<FabricCanvas | null>,\n viewportRef: RefObject<ViewportController | null>,\n setZoom: (z: number) => void,\n) {\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 = (object: FabricObject, panOpts?: PanToObjectOptions) => {\n viewportRef.current?.panToObject(object, panOpts);\n };\n\n return { resetViewport, zoomIn, zoomOut, panToObject };\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 { 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 { BASE_CANVAS_SIZE, 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 type AlignmentRenderConfig,\n drawMarkerList,\n drawVerticalAlignmentLines,\n drawHorizontalAlignmentLines,\n} from './objectAlignmentRendering';\nimport {\n collectMovingAlignmentLines,\n collectVerticalSnapOffset,\n collectHorizontalSnapOffset,\n} from './objectAlignmentMath';\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 const zoom = this.canvas.getZoom();\n const sizeScale = this.scaleWithCanvasSize\n ? Math.max(this.canvas.width ?? 800, this.canvas.height ?? 600) /\n BASE_CANVAS_SIZE\n : 1;\n return (this.margin * sizeScale) / zoom;\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 { Canvas, Point } from 'fabric';\nimport type { AlignmentLine } from './objectAlignmentUtils';\n\n/** Style configuration for alignment guideline rendering. */\nexport interface AlignmentRenderConfig {\n canvas: Canvas;\n width: number;\n color: string;\n xSize: number;\n lineDash?: number[];\n}\n\n/** Draw a single alignment line between two points, with X markers at each end. */\nexport function 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\n/** Draw an X marker at a point. */\nexport function 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\n/** Draw X markers only (no lines) for a list of alignment lines. */\nexport function 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\n/** Draw vertical alignment lines (lines connecting points at the same X coordinate). */\nexport function 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\n/** Draw horizontal alignment lines (lines connecting points at the same Y coordinate). */\nexport function 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","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 { DEFAULT_SNAP_MARGIN, BASE_CANVAS_SIZE } 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 zoom = canvas.getZoom();\n const scaleWithSize = options?.scaleWithCanvasSize !== false;\n const sizeScale = scaleWithSize\n ? Math.max(canvas.width ?? 800, canvas.height ?? 600) / BASE_CANVAS_SIZE\n : 1;\n const margin = ((options?.margin ?? DEFAULT_SNAP_MARGIN) * sizeScale) / zoom;\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 /** 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 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\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\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\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\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\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\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 { Point2D, SnappableInteractionOptions } from '../types';\nimport { createPolygonFromVertices, type PolygonStyleOptions } 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?: PolygonStyleOptions;\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 * 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 polygon = createPolygonFromVertices(canvas, points, options?.style);\n if (options?.data) {\n polygon.data = options.data;\n }\n canvas.selection = previousSelection;\n canvas.requestRenderAll();\n\n restoreViewport(options?.viewport);\n options?.onCreated?.(polygon);\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 style?: PolygonStyleOptions,\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, ...style },\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 style?: PolygonStyleOptions,\n): Polygon {\n const polygon = new Polygon(\n points.map((p) => ({ x: p.x, y: p.y })),\n { ...DEFAULT_SHAPE_STYLE, ...style },\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}\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 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 onExit?.();\n }\n\n return cleanup;\n}\n","import { Canvas as FabricCanvas, Rect, type FabricObject } from 'fabric';\nimport { restoreCircleConstraints } from './shapes/circle';\nimport { DEFAULT_CONTROL_STYLE } from './styles';\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 * 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 * 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/**\n * Serialize the canvas to a plain object, ready for `JSON.stringify`.\n *\n * - Always includes the `'data'` and `'shapeType'` custom properties.\n * - If {@link enableScaledStrokes} is active, temporarily restores all stroke\n * widths to their base values before serializing, then reapplies the\n * zoom-scaled values. This ensures the saved JSON always contains the\n * intended visual stroke width, not the zoom-adjusted one.\n */\nexport function serializeCanvas(\n canvas: FabricCanvas,\n options?: SerializeOptions,\n): object {\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 // Save any currently scaled stroke widths and restore base values\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\n const json = canvas.toObject(properties);\n\n // Reapply the zoom-scaled values\n scaledWidths.forEach((scaled, obj) => {\n obj.strokeWidth = scaled;\n });\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\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: object,\n options?: LoadCanvasOptions,\n): Promise<FabricObject[]> {\n await canvas.loadFromJSON(json);\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 // Re-apply per-object state that Fabric does not persist through serialization.\n canvas.forEachObject((obj) => {\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 });\n canvas.requestRenderAll();\n\n return canvas.getObjects() as FabricObject[];\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { Canvas as FabricCanvas, type FabricObject, Rect } from 'fabric';\nimport {\n enablePanAndZoom,\n type PanAndZoomOptions,\n type ViewportController,\n} from '../viewport';\nimport { enableScaledStrokes } from '../serialization';\nimport { fitViewportToBackground } from '../background';\nimport { createViewportActions, 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\nconst VIEW_BORDER_RADIUS = 4;\n\n/** Disable selection and interactivity on all objects, and apply view-only styles. */\nfunction lockCanvas(canvas: FabricCanvas) {\n canvas.selection = false;\n canvas.forEachObject((obj) => {\n obj.selectable = false;\n obj.evented = false;\n if (\n obj instanceof Rect &&\n obj.shapeType !== 'circle' &&\n obj.data?.type !== 'DEVICE'\n ) {\n // Compensate for non-uniform scaling so corners appear circular.\n const rx = VIEW_BORDER_RADIUS / (obj.scaleX ?? 1);\n const ry = VIEW_BORDER_RADIUS / (obj.scaleY ?? 1);\n obj.set({ rx, ry });\n }\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\n const [zoom, setZoom] = useState(1);\n\n const onReady = useCallback(\n (canvas: FabricCanvas) => {\n canvasRef.current = canvas;\n\n if (options?.scaledStrokes !== false) {\n enableScaledStrokes(canvas);\n }\n\n if (options?.panAndZoom !== false) {\n const panAndZoomOpts =\n typeof options?.panAndZoom === 'object' ? options.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 = options?.onReady?.(canvas);\n if (options?.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 // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n const { resetViewport, zoomIn, zoomOut, panToObject } = createViewportActions(\n canvasRef,\n viewportRef,\n setZoom,\n );\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 },\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 * Automatically scale the overlay content so it fits within the object\n * bounds at the current zoom level.\n * Default: false.\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 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\n const angle = object.angle ?? 0;\n el.style.rotate = angle !== 0 ? `${angle}deg` : '';\n\n const opts = optionsRef.current;\n if (opts?.autoScaleContent) {\n // Scale content based on the smaller dimension\n const contentScale = Math.min(screenWidth, screenHeight) / 100;\n const clampedScale = Math.max(0.1, Math.min(contentScale, 2));\n el.style.setProperty('--overlay-scale', String(clampedScale));\n\n if (opts.textSelector) {\n const textMinScale = opts.textMinScale ?? 0.5;\n const textEls = el.querySelectorAll<HTMLElement>(opts.textSelector);\n const display = clampedScale < textMinScale ? 'none' : '';\n textEls.forEach((t) => {\n t.style.display = display;\n });\n }\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 canvas.on('mouse:wheel', 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 canvas.off('mouse:wheel', 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"],"mappings":"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","names":["import_fabric","FabricCanvas","import_react","import_fabric","import_fabric","vt","import_fabric","resetViewport","import_fabric","import_fabric","import_fabric","import_fabric","drawXMarker","import_fabric","import_styles","import_fabric","import_fabric","import_fabric","import_fabric","import_fabric","import_fabric","import_fabric","resetViewport","import_react","import_fabric","resetViewport","import_react","import_react","import_react","import_react","import_fabric","import_fabric"]}
|