{"version":3,"file":"index.cjs","names":[],"sources":["../src/constants.ts","../src/coordinates.ts","../src/elements.ts","../src/history.ts","../src/snapping.ts","../src/tools/base.ts","../src/tools/draw.ts","../src/tools/ellipse.ts","../src/tools/eraser.ts","../src/viewport.ts","../src/tools/hand.ts","../src/tools/line.ts","../src/tools/media.ts","../src/tools/rectangle.ts","../src/tools/select.ts","../src/canvas.ts"],"sourcesContent":["export const BACKGROUND_COLOR =\n  \"var(--adraw-background, light-dark(#fff, #000))\"\nexport const FILL_COLOR = \"var(--adraw-fill, transparent)\"\nexport const SELECTION_COLOR =\n  \"var(--adraw-selection, light-dark(oklch(0.44 0.14 248), oklch(0.84 0.1 248)))\"\nexport const STROKE_COLOR = \"var(--adraw-stroke, light-dark(#000, #fff))\"\nexport const STROKE_WIDTH = 2\n","import type { Point, Size, ViewportState } from \"./types\"\n\nexport function screenToCanvas(\n  screenPoint: Point,\n  viewport: ViewportState,\n  canvasSize: Size,\n): Point {\n  const centerX = canvasSize.width / 2\n  const centerY = canvasSize.height / 2\n\n  const x = (screenPoint.x - centerX) / viewport.zoom + viewport.x\n  const y = (screenPoint.y - centerY) / viewport.zoom + viewport.y\n\n  return { x, y }\n}\n\nexport function canvasToScreen(\n  canvasPoint: Point,\n  viewport: ViewportState,\n  canvasSize: Size,\n): Point {\n  const centerX = canvasSize.width / 2\n  const centerY = canvasSize.height / 2\n\n  const x = (canvasPoint.x - viewport.x) * viewport.zoom + centerX\n  const y = (canvasPoint.y - viewport.y) * viewport.zoom + centerY\n\n  return { x, y }\n}\n\nexport function getElementBounds(\n  x: number,\n  y: number,\n  width: number,\n  height: number,\n  rotation: number = 0,\n): { left: number; right: number; top: number; bottom: number; center: Point } {\n  if (rotation === 0) {\n    return {\n      bottom: y + height,\n      center: { x: x + width / 2, y: y + height / 2 },\n      left: x,\n      right: x + width,\n      top: y,\n    }\n  }\n\n  const cx = x + width / 2\n  const cy = y + height / 2\n  const rad = (rotation * Math.PI) / 180\n  const cos = Math.cos(rad)\n  const sin = Math.sin(rad)\n\n  const corners = [\n    { x: x - width / 2, y: y - height / 2 },\n    { x: x + width / 2, y: y - height / 2 },\n    { x: x + width / 2, y: y + height / 2 },\n    { x: x - width / 2, y: y + height / 2 },\n  ]\n\n  const rotatedCorners = corners.map((corner) => ({\n    x: cos * (corner.x - cx) - sin * (corner.y - cy) + cx,\n    y: sin * (corner.x - cx) + cos * (corner.y - cy) + cy,\n  }))\n\n  const xs = rotatedCorners.map((c) => c.x)\n  const ys = rotatedCorners.map((c) => c.y)\n\n  return {\n    bottom: Math.max(...ys),\n    center: { x: cx, y: cy },\n    left: Math.min(...xs),\n    right: Math.max(...xs),\n    top: Math.min(...ys),\n  }\n}\n\nexport function pointInBounds(\n  point: Point,\n  bounds: { left: number; right: number; top: number; bottom: number },\n): boolean {\n  return (\n    point.x >= bounds.left &&\n    point.x <= bounds.right &&\n    point.y >= bounds.top &&\n    point.y <= bounds.bottom\n  )\n}\n\nexport function distanceBetweenPoints(p1: Point, p2: Point): number {\n  const dx = p2.x - p1.x\n  const dy = p2.y - p1.y\n  return Math.sqrt(dx * dx + dy * dy)\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n  return Math.min(Math.max(value, min), max)\n}\n\nexport function generateId(): string {\n  return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n}\n","import { generateId } from \"./coordinates\"\nimport type {\n  CanvasElement,\n  ElementId,\n  EllipseElement,\n  GroupElement,\n  LineElement,\n  MediaElement,\n  PathElement,\n  Point,\n  RectangleElement,\n  ResizeAnchor,\n} from \"./types\"\n\nexport type ElementFactory<T extends CanvasElement> = Omit<T, \"id\" | \"type\"> & {\n  id?: string\n}\n\n// Default spline tension for freehand paths (0 = straight segments, 1 = full\n// Catmull-Rom curve). Shared by the draw tool and the renderer so a path always\n// looks the same regardless of how it was constructed.\nexport const DEFAULT_PATH_SMOOTHING = 0.5\n\nexport function createRectangle(\n  factory: ElementFactory<RectangleElement>,\n): RectangleElement {\n  return {\n    ...factory,\n    id: factory.id ?? generateId(),\n    type: \"rectangle\",\n  }\n}\n\nexport function createEllipse(\n  factory: ElementFactory<EllipseElement>,\n): EllipseElement {\n  return {\n    ...factory,\n    id: factory.id ?? generateId(),\n    type: \"ellipse\",\n  }\n}\n\nexport function createLine(factory: ElementFactory<LineElement>): LineElement {\n  return {\n    ...factory,\n    id: factory.id ?? generateId(),\n    type: \"line\",\n  }\n}\n\nexport function createPath(factory: ElementFactory<PathElement>): PathElement {\n  return {\n    ...factory,\n    id: factory.id ?? generateId(),\n    points: factory.points ?? [],\n    smoothing: factory.smoothing ?? DEFAULT_PATH_SMOOTHING,\n    type: \"path\",\n  }\n}\n\nexport function createMedia(\n  factory: ElementFactory<MediaElement>,\n): MediaElement {\n  return {\n    ...factory,\n    id: factory.id ?? generateId(),\n    type: \"media\",\n  }\n}\n\nexport function createGroup(\n  factory: ElementFactory<GroupElement>,\n): GroupElement {\n  return {\n    ...factory,\n    children: factory.children ?? [],\n    id: factory.id ?? generateId(),\n    type: \"group\",\n  }\n}\n\nexport function cloneElement<T extends CanvasElement>(\n  element: T,\n  offset: Point = { x: 20, y: 20 },\n): T {\n  return {\n    ...element,\n    id: generateId(),\n    x: element.x + offset.x,\n    y: element.y + offset.y,\n  }\n}\n\nexport function moveElement(\n  element: CanvasElement,\n  delta: Point,\n): CanvasElement {\n  return {\n    ...element,\n    x: element.x + delta.x,\n    y: element.y + delta.y,\n  }\n}\n\nexport function resizeElement(\n  element: CanvasElement,\n  width: number,\n  height: number,\n  anchor: ResizeAnchor = \"top-left\",\n): CanvasElement {\n  let { x, y } = element\n\n  switch (anchor) {\n    case \"top-right\": {\n      x = element.x + element.width - width\n      break\n    }\n    case \"bottom-left\": {\n      y = element.y + element.height - height\n      break\n    }\n    case \"bottom-right\": {\n      x = element.x + element.width - width\n      y = element.y + element.height - height\n      break\n    }\n    case \"top-center\": {\n      x = element.x + (element.width - width) / 2\n      break\n    }\n    case \"bottom-center\": {\n      x = element.x + (element.width - width) / 2\n      y = element.y + element.height - height\n      break\n    }\n    case \"left-center\": {\n      y = element.y + (element.height - height) / 2\n      break\n    }\n    case \"right-center\": {\n      x = element.x + element.width - width\n      y = element.y + (element.height - height) / 2\n      break\n    }\n    case \"center\": {\n      x = element.x + (element.width - width) / 2\n      y = element.y + (element.height - height) / 2\n      break\n    }\n  }\n\n  return {\n    ...element,\n    height: Math.max(1, height),\n    width: Math.max(1, width),\n    x,\n    y,\n  }\n}\n\nexport function rotateElement(\n  element: CanvasElement,\n  rotation: number,\n): CanvasElement {\n  return {\n    ...element,\n    rotation: rotation % 360,\n  }\n}\n\nexport function getElementsBounds(\n  elements: Map<ElementId, CanvasElement>,\n  ids?: Set<ElementId>,\n): {\n  left: number\n  right: number\n  top: number\n  bottom: number\n  x: number\n  y: number\n  width: number\n  height: number\n} | null {\n  let elementArray = [...elements.values()].filter((el) => el.visible)\n  if (ids) {\n    elementArray = elementArray.filter((el) => ids.has(el.id))\n  }\n\n  if (elementArray.length === 0) {\n    return null\n  }\n\n  let left = Infinity\n  let right = -Infinity\n  let top = Infinity\n  let bottom = -Infinity\n\n  for (const element of elementArray) {\n    left = Math.min(left, element.x)\n    right = Math.max(right, element.x + element.width)\n    top = Math.min(top, element.y)\n    bottom = Math.max(bottom, element.y + element.height)\n  }\n\n  return {\n    bottom,\n    height: bottom - top,\n    left,\n    right,\n    top,\n    width: right - left,\n    x: left,\n    y: top,\n  }\n}\n\nexport function getElementAtPoint(\n  elements: Map<ElementId, CanvasElement>,\n  point: Point,\n): CanvasElement | null {\n  const elementArray = [...elements.values()]\n    .filter((el) => el.visible && !el.locked)\n    .toSorted((a, b) => b.zIndex - a.zIndex)\n\n  for (const element of elementArray) {\n    if (isPointInElement(point, element)) {\n      return element\n    }\n  }\n\n  return null\n}\n\nfunction pointToSegmentDistance(point: Point, a: Point, b: Point): number {\n  const abx = b.x - a.x\n  const aby = b.y - a.y\n  const len2 = abx * abx + aby * aby\n  if (len2 === 0) {\n    return Math.hypot(point.x - a.x, point.y - a.y)\n  }\n  let t = ((point.x - a.x) * abx + (point.y - a.y) * aby) / len2\n  t = Math.max(0, Math.min(1, t))\n  const closest = { x: a.x + t * abx, y: a.y + t * aby }\n  return Math.hypot(point.x - closest.x, point.y - closest.y)\n}\n\nfunction isPointInElement(point: Point, element: CanvasElement): boolean {\n  const { x, y, width, height, rotation, type } = element\n\n  if (type === \"line\") {\n    const line = element as LineElement\n    const halfStroke = Math.max(line.strokeWidth, 4) / 2\n    if (rotation === 0) {\n      return (\n        pointToSegmentDistance(\n          point,\n          { x: line.startX, y: line.startY },\n          { x: line.endX, y: line.endY },\n        ) <=\n        halfStroke + 2\n      )\n    }\n    const cx = x + width / 2\n    const cy = y + height / 2\n    const rad = (-rotation * Math.PI) / 180\n    const cos = Math.cos(rad)\n    const sin = Math.sin(rad)\n    const dx = point.x - cx\n    const dy = point.y - cy\n    const rx = cos * dx - sin * dy\n    const ry = sin * dx + cos * dy\n    const unrotated = { x: rx + cx, y: ry + cy }\n    return (\n      pointToSegmentDistance(\n        unrotated,\n        { x: line.startX, y: line.startY },\n        { x: line.endX, y: line.endY },\n      ) <=\n      halfStroke + 2\n    )\n  }\n\n  if (rotation === 0) {\n    return (\n      point.x >= x &&\n      point.x <= x + width &&\n      point.y >= y &&\n      point.y <= y + height\n    )\n  }\n\n  const cx = x + width / 2\n  const cy = y + height / 2\n  const rad = (-rotation * Math.PI) / 180\n  const cos = Math.cos(rad)\n  const sin = Math.sin(rad)\n\n  const dx = point.x - cx\n  const dy = point.y - cy\n\n  const rx = cos * dx - sin * dy\n  const ry = sin * dx + cos * dy\n\n  return (\n    rx >= -width / 2 && rx <= width / 2 && ry >= -height / 2 && ry <= height / 2\n  )\n}\n","import type { CanvasElement, ElementId } from \"./types\"\n\nexport interface HistoryEntry {\n  elements: Map<ElementId, CanvasElement>\n  selectedIds: Set<ElementId>\n  timestamp: number\n}\n\nexport interface HistoryState {\n  undoStack: HistoryEntry[]\n  redoStack: HistoryEntry[]\n  maxSize: number\n}\n\nexport function createHistoryState(maxSize: number = 100): HistoryState {\n  return {\n    maxSize,\n    redoStack: [],\n    undoStack: [],\n  }\n}\n\nexport function pushHistory(\n  state: HistoryState,\n  elements: Map<ElementId, CanvasElement>,\n  selectedIds: Set<ElementId>,\n): HistoryState {\n  const entry: HistoryEntry = {\n    elements: new Map(elements),\n    selectedIds: new Set(selectedIds),\n    timestamp: Date.now(),\n  }\n\n  const newUndoStack = [...state.undoStack, entry]\n\n  if (newUndoStack.length > state.maxSize) {\n    newUndoStack.shift()\n  }\n\n  return {\n    maxSize: state.maxSize,\n    redoStack: [],\n    undoStack: newUndoStack,\n  }\n}\n\nexport function undo(\n  state: HistoryState,\n  currentElements: Map<ElementId, CanvasElement>,\n  currentSelectedIds: Set<ElementId>,\n): {\n  state: HistoryState\n  elements: Map<ElementId, CanvasElement>\n  selectedIds: Set<ElementId>\n} | null {\n  // The top of the undo stack mirrors the current committed state, so undoing\n  // requires at least one prior checkpoint underneath it to restore.\n  if (state.undoStack.length <= 1) {\n    return null\n  }\n\n  const newUndoStack = [...state.undoStack]\n  newUndoStack.pop()\n  const targetEntry = newUndoStack[newUndoStack.length - 1]\n\n  const currentEntry: HistoryEntry = {\n    elements: new Map(currentElements),\n    selectedIds: new Set(currentSelectedIds),\n    timestamp: Date.now(),\n  }\n\n  return {\n    elements: new Map(targetEntry.elements),\n    selectedIds: new Set(targetEntry.selectedIds),\n    state: {\n      maxSize: state.maxSize,\n      redoStack: [...state.redoStack, currentEntry],\n      undoStack: newUndoStack,\n    },\n  }\n}\n\nexport function redo(\n  state: HistoryState,\n  _currentElements: Map<ElementId, CanvasElement>,\n  _currentSelectedIds: Set<ElementId>,\n): {\n  state: HistoryState\n  elements: Map<ElementId, CanvasElement>\n  selectedIds: Set<ElementId>\n} | null {\n  if (state.redoStack.length === 0) {\n    return null\n  }\n\n  const newRedoStack = [...state.redoStack]\n  const targetEntry = newRedoStack.pop()!\n\n  return {\n    elements: new Map(targetEntry.elements),\n    selectedIds: new Set(targetEntry.selectedIds),\n    state: {\n      maxSize: state.maxSize,\n      redoStack: newRedoStack,\n      // The restored state becomes the new current committed checkpoint.\n      undoStack: [...state.undoStack, targetEntry],\n    },\n  }\n}\n\nexport function canUndo(state: HistoryState): boolean {\n  return state.undoStack.length > 1\n}\n\nexport function canRedo(state: HistoryState): boolean {\n  return state.redoStack.length > 0\n}\n\nexport function clearHistory(state: HistoryState): HistoryState {\n  return {\n    ...state,\n    redoStack: [],\n    undoStack: [],\n  }\n}\n","import type {\n  CanvasElement,\n  ElementId,\n  Point,\n  SnapGuide,\n  SnapResult,\n} from \"./types\"\n\nexport interface SnappingConfig {\n  enabled: boolean\n  threshold: number\n}\n\nconst DEFAULT_SNAPPING_CONFIG: SnappingConfig = {\n  enabled: false,\n  threshold: 5,\n}\n\nexport function createSnappingConfig(\n  partial: Partial<SnappingConfig> = {},\n): SnappingConfig {\n  return {\n    ...DEFAULT_SNAPPING_CONFIG,\n    ...partial,\n  }\n}\n\nexport interface SnapPoint {\n  x: number\n  y: number\n  elementId: ElementId\n  type: \"left\" | \"right\" | \"top\" | \"bottom\" | \"center-x\" | \"center-y\"\n}\n\nexport function getElementSnapPoints(element: CanvasElement): SnapPoint[] {\n  const { x, y, width, height } = element\n  const cx = x + width / 2\n  const cy = y + height / 2\n\n  return [\n    { elementId: element.id, type: \"left\", x, y },\n    { elementId: element.id, type: \"right\", x: x + width, y },\n    { elementId: element.id, type: \"top\", x, y },\n    { elementId: element.id, type: \"bottom\", x, y: y + height },\n    { elementId: element.id, type: \"center-y\", x: cx, y },\n    { elementId: element.id, type: \"center-x\", x, y: cy },\n  ]\n}\n\nexport function getAllSnapPoints(\n  elements: Map<ElementId, CanvasElement>,\n  excludeIds = new Set<ElementId>(),\n): SnapPoint[] {\n  const snapPoints: SnapPoint[] = []\n\n  for (const [id, element] of elements) {\n    if (excludeIds.has(id) || !element.visible || element.locked) {\n      continue\n    }\n    snapPoints.push(...getElementSnapPoints(element))\n  }\n\n  return snapPoints\n}\n\nexport function calculateSnap(\n  point: Point,\n  snapPoints: SnapPoint[],\n  threshold: number,\n): SnapResult {\n  const guides: SnapGuide[] = []\n  let snapped = false\n\n  for (const snapPoint of snapPoints) {\n    const dx = Math.abs(point.x - snapPoint.x)\n    const dy = Math.abs(point.y - snapPoint.y)\n\n    if (dx < threshold) {\n      guides.push({\n        elements: [snapPoint.elementId],\n        position: snapPoint.x,\n        type: \"vertical\",\n      })\n      snapped = true\n    }\n\n    if (dy < threshold) {\n      guides.push({\n        elements: [snapPoint.elementId],\n        position: snapPoint.y,\n        type: \"horizontal\",\n      })\n      snapped = true\n    }\n  }\n\n  return { guides, snapped }\n}\n\nexport function snapPointToGuides(point: Point, guides: SnapGuide[]): Point {\n  let snappedX = point.x\n  let snappedY = point.y\n\n  for (const guide of guides) {\n    if (guide.type === \"vertical\") {\n      snappedX = guide.position\n    } else if (guide.type === \"horizontal\") {\n      snappedY = guide.position\n    }\n  }\n\n  return { x: snappedX, y: snappedY }\n}\n\nexport function snapBoundsToElements(\n  bounds: { x: number; y: number; width: number; height: number },\n  elements: Map<ElementId, CanvasElement>,\n  excludeIds: Set<ElementId>,\n  threshold: number,\n): {\n  x: number\n  y: number\n  width: number\n  height: number\n  guides: SnapGuide[]\n} {\n  const snapPoints = getAllSnapPoints(elements, excludeIds)\n  const guides: SnapGuide[] = []\n\n  const leftPoints = snapPoints.filter((p) => p.type === \"left\")\n  const rightPoints = snapPoints.filter((p) => p.type === \"right\")\n  const topPoints = snapPoints.filter((p) => p.type === \"top\")\n  const bottomPoints = snapPoints.filter((p) => p.type === \"bottom\")\n\n  let newX = bounds.x\n  let newY = bounds.y\n\n  for (const point of leftPoints) {\n    if (Math.abs(bounds.x - point.x) < threshold) {\n      newX = point.x\n      guides.push({\n        elements: [point.elementId],\n        position: point.x,\n        type: \"vertical\",\n      })\n      break\n    }\n  }\n\n  for (const point of rightPoints) {\n    if (Math.abs(bounds.x + bounds.width - point.x) < threshold) {\n      newX = point.x - bounds.width\n      guides.push({\n        elements: [point.elementId],\n        position: point.x,\n        type: \"vertical\",\n      })\n      break\n    }\n  }\n\n  for (const point of topPoints) {\n    if (Math.abs(bounds.y - point.y) < threshold) {\n      newY = point.y\n      guides.push({\n        elements: [point.elementId],\n        position: point.y,\n        type: \"horizontal\",\n      })\n      break\n    }\n  }\n\n  for (const point of bottomPoints) {\n    if (Math.abs(bounds.y + bounds.height - point.y) < threshold) {\n      newY = point.y - bounds.height\n      guides.push({\n        elements: [point.elementId],\n        position: point.y,\n        type: \"horizontal\",\n      })\n      break\n    }\n  }\n\n  return {\n    guides,\n    height: bounds.height,\n    width: bounds.width,\n    x: newX,\n    y: newY,\n  }\n}\n","import { FILL_COLOR, STROKE_COLOR, STROKE_WIDTH } from \"../constants\"\nimport type {\n  BoundingBox,\n  CanvasElement,\n  ElementId,\n  Point,\n  ToolType,\n  ViewportState,\n} from \"../types\"\n\nexport interface ToolContext {\n  getElements: () => Map<ElementId, CanvasElement>\n  setElements: (elements: Map<ElementId, CanvasElement>) => void\n  getSelectedIds: () => Set<ElementId>\n  setSelectedIds: (ids: Set<ElementId>) => void\n  getViewport: () => ViewportState\n  setViewport: (viewport: ViewportState) => void\n  getCanvasSize: () => { width: number; height: number }\n  pushHistory: () => void\n  setActiveTool: (tool: ToolType) => void\n}\n\nexport interface ToolState {\n  isActive: boolean\n  startPoint: Point | null\n  currentPoint: Point | null\n}\n\nexport interface Tool {\n  readonly type: ToolType\n  readonly cursor: string\n  onActivate: (context: ToolContext) => void\n  onDeactivate: (context: ToolContext) => void\n  onPointerDown: (\n    context: ToolContext,\n    point: Point,\n    event: PointerEvent,\n  ) => void\n  onPointerMove: (\n    context: ToolContext,\n    point: Point,\n    event: PointerEvent,\n  ) => void\n  onPointerUp: (context: ToolContext, point: Point, event: PointerEvent) => void\n  getTemporaryElement: () => CanvasElement | null\n  // In-progress marquee (rubber-band) selection box in canvas space, or null\n  // when the tool isn't brushing. Rendered as a dashed overlay, not committed as\n  // an element. Only the select tool implements this.\n  getSelectionBox?: () => BoundingBox | null\n  // True while the tool is actively resizing the selection. The transform\n  // overlay (bounding box + handles) is hidden during the gesture so it\n  // doesn't visually lag the element being transformed. Only the select tool\n  // implements this.\n  isResizing?: () => boolean\n  // True while the tool is actively rotating the selection. Only the select\n  // tool implements this.\n  isRotating?: () => boolean\n}\n\nexport function createBaseToolState(): ToolState {\n  return {\n    currentPoint: null,\n    isActive: false,\n    startPoint: null,\n  }\n}\n\nexport interface ToolOptions {\n  strokeColor?: string\n  fillColor?: string\n  strokeWidth?: number\n}\n\nexport function getDefaultToolOptions(): ToolOptions {\n  return {\n    fillColor: FILL_COLOR,\n    strokeColor: STROKE_COLOR,\n    strokeWidth: STROKE_WIDTH,\n  }\n}\n\nexport function calculateBounds(\n  startPoint: Point,\n  endPoint: Point,\n): { x: number; y: number; width: number; height: number } {\n  const x = Math.min(startPoint.x, endPoint.x)\n  const y = Math.min(startPoint.y, endPoint.y)\n  const width = Math.abs(endPoint.x - startPoint.x)\n  const height = Math.abs(endPoint.y - startPoint.y)\n\n  return { height, width, x, y }\n}\n\nexport function getCenterPoint(startPoint: Point, endPoint: Point): Point {\n  return {\n    x: (startPoint.x + endPoint.x) / 2,\n    y: (startPoint.y + endPoint.y) / 2,\n  }\n}\n","import { STROKE_COLOR, STROKE_WIDTH } from \"../constants\"\nimport {\n  createPath,\n  DEFAULT_PATH_SMOOTHING,\n  type ElementFactory,\n} from \"../elements\"\nimport type { PathElement, Point, ToolType } from \"../types\"\nimport {\n  createBaseToolState,\n  getDefaultToolOptions,\n  type Tool,\n  type ToolContext,\n  type ToolOptions,\n  type ToolState,\n} from \"./base\"\n\nexport interface DrawToolOptions extends ToolOptions {\n  smoothing?: number\n}\n\nfunction perpendicularDistance(\n  point: Point,\n  lineStart: Point,\n  lineEnd: Point,\n): number {\n  const dx = lineEnd.x - lineStart.x\n  const dy = lineEnd.y - lineStart.y\n\n  if (dx === 0 && dy === 0) {\n    return Math.sqrt(\n      (point.x - lineStart.x) ** 2 + (point.y - lineStart.y) ** 2,\n    )\n  }\n\n  const t =\n    ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) /\n    (dx * dx + dy * dy)\n\n  const nearestX = lineStart.x + t * dx\n  const nearestY = lineStart.y + t * dy\n\n  return Math.sqrt((point.x - nearestX) ** 2 + (point.y - nearestY) ** 2)\n}\n\nfunction simplifyPath(points: Point[], tolerance: number): Point[] {\n  if (points.length <= 2) {\n    return points\n  }\n\n  const first = points[0]\n  const last = points[points.length - 1]\n\n  let maxDistance = 0\n  let maxIndex = 0\n\n  for (let i = 1; i < points.length - 1; i++) {\n    const distance = perpendicularDistance(points[i], first, last)\n    if (distance > maxDistance) {\n      maxDistance = distance\n      maxIndex = i\n    }\n  }\n\n  if (maxDistance > tolerance) {\n    const left = simplifyPath(points.slice(0, maxIndex + 1), tolerance)\n    const right = simplifyPath(points.slice(maxIndex), tolerance)\n    return [...left.slice(0, -1), ...right]\n  }\n\n  return [first, last]\n}\n\nfunction getPathBounds(points: Point[]): {\n  x: number\n  y: number\n  width: number\n  height: number\n} | null {\n  if (points.length === 0) {\n    return null\n  }\n\n  let minX = Infinity\n  let maxX = -Infinity\n  let minY = Infinity\n  let maxY = -Infinity\n\n  for (const point of points) {\n    minX = Math.min(minX, point.x)\n    maxX = Math.max(maxX, point.x)\n    minY = Math.min(minY, point.y)\n    maxY = Math.max(maxY, point.y)\n  }\n\n  return {\n    height: maxY - minY,\n    width: maxX - minX,\n    x: minX,\n    y: minY,\n  }\n}\n\nfunction createPathElement(\n  points: Point[],\n  {\n    smoothing = DEFAULT_PATH_SMOOTHING,\n    strokeColor = STROKE_COLOR,\n    strokeWidth = STROKE_WIDTH,\n  }: DrawToolOptions,\n  factory?: Partial<ElementFactory<PathElement>>,\n) {\n  const simplifiedPoints = simplifyPath(points, 1 - smoothing)\n  const bounds = getPathBounds(simplifiedPoints)\n\n  if (!bounds) {\n    return null\n  }\n\n  return createPath({\n    height: Math.max(bounds.height, 1),\n    locked: false,\n    points: simplifiedPoints,\n    rotation: 0,\n    smoothing,\n    strokeColor,\n    strokeWidth,\n    visible: true,\n    width: Math.max(bounds.width, 1),\n    x: bounds.x,\n    y: bounds.y,\n    zIndex: 0,\n    ...factory,\n  })\n}\n\nexport function createDrawTool(options: DrawToolOptions = {}): Tool {\n  const state: ToolState = createBaseToolState()\n  const toolOptions = { ...getDefaultToolOptions(), ...options }\n  let currentPoints: Point[] = []\n  let temporaryElement: PathElement | null = null\n\n  return {\n    cursor: \"crosshair\",\n    getTemporaryElement() {\n      return temporaryElement\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      currentPoints = []\n      temporaryElement = null\n    },\n    onPointerDown(_context: ToolContext, point: Point, _event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n      currentPoints = [point]\n    },\n    onPointerMove(_context: ToolContext, point: Point, _event: PointerEvent) {\n      if (!state.startPoint) {\n        return\n      }\n\n      state.currentPoint = point\n      currentPoints.push(point)\n\n      const element = createPathElement(currentPoints, toolOptions)\n\n      if (element) {\n        temporaryElement = element\n      }\n    },\n    onPointerUp(context: ToolContext, _point: Point, _event: PointerEvent) {\n      if (currentPoints.length < 2) {\n        state.startPoint = null\n        state.currentPoint = null\n        currentPoints = []\n        temporaryElement = null\n        return\n      }\n\n      const element = createPathElement(currentPoints, toolOptions, {\n        zIndex: context.getElements().size,\n      })\n\n      if (element) {\n        const elements = context.getElements()\n        elements.set(element.id, element)\n        context.setElements(elements)\n        context.setSelectedIds(new Set())\n        context.pushHistory()\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n      currentPoints = []\n      temporaryElement = null\n    },\n    type: \"draw\" as ToolType,\n  }\n}\n","import { createEllipse } from \"../elements\"\nimport type { EllipseElement, Point, ToolType } from \"../types\"\nimport {\n  calculateBounds,\n  createBaseToolState,\n  type Tool,\n  type ToolContext,\n  type ToolState,\n} from \"./base\"\n\nexport function createEllipseTool(): Tool {\n  const state: ToolState = createBaseToolState()\n  let temporaryElement: EllipseElement | null = null\n\n  return {\n    cursor: \"crosshair\",\n    getTemporaryElement() {\n      return temporaryElement\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      temporaryElement = null\n    },\n    onPointerDown(_context: ToolContext, point: Point, _event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n    },\n    onPointerMove(_context: ToolContext, point: Point, _event: PointerEvent) {\n      if (!state.startPoint) {\n        return\n      }\n\n      state.currentPoint = point\n\n      const bounds = calculateBounds(state.startPoint, point)\n\n      temporaryElement = createEllipse({\n        height: bounds.height,\n        locked: false,\n        rotation: 0,\n        visible: true,\n        width: bounds.width,\n        x: bounds.x,\n        y: bounds.y,\n        zIndex: 0,\n      })\n    },\n    onPointerUp(context: ToolContext, _point: Point, _event: PointerEvent) {\n      if (!state.startPoint || !state.currentPoint) {\n        return\n      }\n\n      const bounds = calculateBounds(state.startPoint, state.currentPoint)\n\n      if (bounds.width > 5 && bounds.height > 5) {\n        const element = createEllipse({\n          height: bounds.height,\n          locked: false,\n          rotation: 0,\n          visible: true,\n          width: bounds.width,\n          x: bounds.x,\n          y: bounds.y,\n          zIndex: context.getElements().size,\n        })\n\n        const elements = context.getElements()\n        elements.set(element.id, element)\n        context.setElements(elements)\n        context.setSelectedIds(new Set([element.id]))\n        context.pushHistory()\n        context.setActiveTool(\"select\")\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n      temporaryElement = null\n    },\n    type: \"ellipse\" as ToolType,\n  }\n}\n","import { getElementAtPoint } from \"../elements\"\nimport type { Point, ToolType } from \"../types\"\nimport {\n  createBaseToolState,\n  type Tool,\n  type ToolContext,\n  type ToolState,\n} from \"./base\"\n\nexport function createEraserTool(): Tool {\n  const state: ToolState = createBaseToolState()\n  let deletedElements: string[] = []\n\n  return {\n    cursor: \"crosshair\",\n    getTemporaryElement() {\n      return null\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      deletedElements = []\n    },\n    onPointerDown(context: ToolContext, point: Point, _event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n      deletedElements = []\n\n      const element = getElementAtPoint(context.getElements(), point)\n\n      if (element) {\n        const elements = context.getElements()\n        elements.delete(element.id)\n        context.setElements(elements)\n        deletedElements.push(element.id)\n      }\n    },\n    onPointerMove(context: ToolContext, point: Point, _event: PointerEvent) {\n      if (!state.startPoint) {\n        return\n      }\n\n      state.currentPoint = point\n\n      const element = getElementAtPoint(context.getElements(), point)\n\n      if (element && !deletedElements.includes(element.id)) {\n        const elements = context.getElements()\n        elements.delete(element.id)\n        context.setElements(elements)\n        deletedElements.push(element.id)\n      }\n    },\n    onPointerUp(context: ToolContext, _point: Point, _event: PointerEvent) {\n      if (deletedElements.length > 0) {\n        context.pushHistory()\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n      deletedElements = []\n    },\n    type: \"eraser\" as ToolType,\n  }\n}\n","import { clamp } from \"./coordinates\"\nimport type { Point, ViewportState } from \"./types\"\n\nexport interface ViewportConfig {\n  minZoom: number\n  maxZoom: number\n  zoomSensitivity: number\n}\n\nconst DEFAULT_VIEWPORT_CONFIG: ViewportConfig = {\n  maxZoom: 10,\n  minZoom: 0.1,\n  zoomSensitivity: 0.001,\n}\n\nexport function createViewport(\n  initialViewport: ViewportState = { x: 0, y: 0, zoom: 1 },\n  config: Partial<ViewportConfig> = {},\n): ViewportState {\n  const finalConfig = { ...DEFAULT_VIEWPORT_CONFIG, ...config }\n\n  return {\n    ...initialViewport,\n    zoom: clamp(initialViewport.zoom, finalConfig.minZoom, finalConfig.maxZoom),\n  }\n}\n\nexport function panViewport(\n  viewport: ViewportState,\n  delta: Point,\n  _config: ViewportConfig = DEFAULT_VIEWPORT_CONFIG,\n): ViewportState {\n  return {\n    ...viewport,\n    x: viewport.x + delta.x,\n    y: viewport.y + delta.y,\n  }\n}\n\nexport function zoomViewport(\n  viewport: ViewportState,\n  delta: number,\n  centerPoint: Point,\n  config: ViewportConfig = DEFAULT_VIEWPORT_CONFIG,\n): ViewportState {\n  const newZoom = clamp(\n    viewport.zoom * (1 - delta * config.zoomSensitivity),\n    config.minZoom,\n    config.maxZoom,\n  )\n\n  if (newZoom === viewport.zoom) {\n    return viewport\n  }\n\n  const zoomRatio = newZoom / viewport.zoom\n\n  return {\n    x: centerPoint.x - (centerPoint.x - viewport.x) * zoomRatio,\n    y: centerPoint.y - (centerPoint.y - viewport.y) * zoomRatio,\n    zoom: newZoom,\n  }\n}\n\nexport function zoomToFit(\n  viewport: ViewportState,\n  bounds: { left: number; right: number; top: number; bottom: number },\n  canvasSize: { width: number; height: number },\n  padding: number = 50,\n): ViewportState {\n  const contentWidth = bounds.right - bounds.left\n  const contentHeight = bounds.bottom - bounds.top\n\n  if (contentWidth === 0 || contentHeight === 0) {\n    return viewport\n  }\n\n  const availableWidth = canvasSize.width - padding * 2\n  const availableHeight = canvasSize.height - padding * 2\n\n  const scaleX = availableWidth / contentWidth\n  const scaleY = availableHeight / contentHeight\n  const newZoom = Math.min(scaleX, scaleY, DEFAULT_VIEWPORT_CONFIG.maxZoom)\n\n  const centerX = (bounds.left + bounds.right) / 2\n  const centerY = (bounds.top + bounds.bottom) / 2\n\n  return {\n    x: centerX,\n    y: centerY,\n    zoom: clamp(\n      newZoom,\n      DEFAULT_VIEWPORT_CONFIG.minZoom,\n      DEFAULT_VIEWPORT_CONFIG.maxZoom,\n    ),\n  }\n}\n\nexport function resetViewport(): ViewportState {\n  return { x: 0, y: 0, zoom: 1 }\n}\n","import type { Point, ToolType } from \"../types\"\nimport { panViewport } from \"../viewport\"\nimport {\n  createBaseToolState,\n  type Tool,\n  type ToolContext,\n  type ToolState,\n} from \"./base\"\n\nexport function createHandTool(): Tool {\n  const state: ToolState = createBaseToolState()\n  let lastPoint: Point | null = null\n\n  return {\n    cursor: \"grab\",\n    getTemporaryElement() {\n      return null\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      lastPoint = null\n    },\n    onPointerDown(_context: ToolContext, point: Point, event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n      lastPoint = { x: event.clientX, y: event.clientY }\n    },\n    onPointerMove(context: ToolContext, point: Point, event: PointerEvent) {\n      if (lastPoint === null || !state.isActive) {\n        return\n      }\n\n      // Derive movement from the client-position delta so the pan tracks the\n      // pointer exactly. `event.movementX/Y` is unreliable across browsers, and\n      // gating on both axes being non-zero would drop any purely horizontal or\n      // vertical drag.\n      const movementX = event.clientX - lastPoint.x\n      const movementY = event.clientY - lastPoint.y\n\n      if (movementX === 0 && movementY === 0) {\n        return\n      }\n\n      const viewport = context.getViewport()\n      const delta = {\n        x: -movementX / viewport.zoom,\n        y: -movementY / viewport.zoom,\n      }\n\n      const newViewport = panViewport(viewport, delta)\n\n      context.setViewport(newViewport)\n      state.currentPoint = point\n      lastPoint = { x: event.clientX, y: event.clientY }\n    },\n    onPointerUp(_context: ToolContext, _point: Point, _event: PointerEvent) {\n      state.startPoint = null\n      state.currentPoint = null\n      lastPoint = null\n    },\n    type: \"hand\" as ToolType,\n  }\n}\n","import { STROKE_COLOR, STROKE_WIDTH } from \"../constants\"\nimport { createLine } from \"../elements\"\nimport type { LineElement, Point, ToolType } from \"../types\"\nimport {\n  createBaseToolState,\n  getDefaultToolOptions,\n  type Tool,\n  type ToolContext,\n  type ToolOptions,\n  type ToolState,\n} from \"./base\"\n\nexport function createLineTool(options: ToolOptions = {}): Tool {\n  const state: ToolState = createBaseToolState()\n  const toolOptions = { ...getDefaultToolOptions(), ...options }\n  let temporaryElement: LineElement | null = null\n\n  return {\n    cursor: \"crosshair\",\n    getTemporaryElement() {\n      return temporaryElement\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      temporaryElement = null\n    },\n    onPointerDown(_context: ToolContext, point: Point, _event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n    },\n    onPointerMove(_context: ToolContext, point: Point, _event: PointerEvent) {\n      if (!state.startPoint) {\n        return\n      }\n\n      state.currentPoint = point\n\n      const x = Math.min(state.startPoint.x, point.x)\n      const y = Math.min(state.startPoint.y, point.y)\n      const width = Math.abs(point.x - state.startPoint.x)\n      const height = Math.abs(point.y - state.startPoint.y)\n\n      temporaryElement = createLine({\n        endX: point.x,\n        endY: point.y,\n        height: Math.max(height, 1),\n        locked: false,\n        rotation: 0,\n        startX: state.startPoint.x,\n        startY: state.startPoint.y,\n        strokeColor: toolOptions.strokeColor ?? STROKE_COLOR,\n        strokeWidth: toolOptions.strokeWidth ?? STROKE_WIDTH,\n        visible: true,\n        width: Math.max(width, 1),\n        x,\n        y,\n        zIndex: 0,\n      })\n    },\n    onPointerUp(context: ToolContext, _point: Point, _event: PointerEvent) {\n      if (!state.startPoint || !state.currentPoint) {\n        return\n      }\n\n      const dx = state.currentPoint.x - state.startPoint.x\n      const dy = state.currentPoint.y - state.startPoint.y\n\n      if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {\n        const x = Math.min(state.startPoint.x, state.currentPoint.x)\n        const y = Math.min(state.startPoint.y, state.currentPoint.y)\n        const width = Math.abs(dx)\n        const height = Math.abs(dy)\n\n        const element = createLine({\n          endX: state.currentPoint.x,\n          endY: state.currentPoint.y,\n          height: Math.max(height, 1),\n          locked: false,\n          rotation: 0,\n          startX: state.startPoint.x,\n          startY: state.startPoint.y,\n          strokeColor: toolOptions.strokeColor ?? STROKE_COLOR,\n          strokeWidth: toolOptions.strokeWidth ?? STROKE_WIDTH,\n          visible: true,\n          width: Math.max(width, 1),\n          x,\n          y,\n          zIndex: context.getElements().size,\n        })\n\n        const elements = context.getElements()\n        elements.set(element.id, element)\n        context.setElements(elements)\n        context.setSelectedIds(new Set([element.id]))\n        context.pushHistory()\n        context.setActiveTool(\"select\")\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n      temporaryElement = null\n    },\n    type: \"line\" as ToolType,\n  }\n}\n","import { createMedia } from \"../elements\"\nimport type { Point, ToolType } from \"../types\"\nimport {\n  createBaseToolState,\n  type Tool,\n  type ToolContext,\n  type ToolState,\n} from \"./base\"\n\nexport interface MediaToolOptions {\n  maxWidth?: number\n  maxHeight?: number\n}\n\nexport function createMediaTool(options: MediaToolOptions = {}): Tool {\n  const state: ToolState = createBaseToolState()\n  const maxWidth = options.maxWidth ?? 800\n  const maxHeight = options.maxHeight ?? 600\n  let pendingMedia: { src: string; mimeType: string } | null = null\n  let insertPosition: Point | null = null\n\n  async function loadImage(\n    src: string,\n  ): Promise<{ naturalWidth: number; naturalHeight: number }> {\n    return new Promise((resolve, reject) => {\n      const img = new Image()\n      img.onload = () => {\n        resolve({\n          naturalHeight: img.naturalHeight,\n          naturalWidth: img.naturalWidth,\n        })\n      }\n      img.onerror = reject\n      img.src = src\n    })\n  }\n\n  async function insertMedia(\n    context: ToolContext,\n    src: string,\n    mimeType: string,\n    position: Point,\n  ) {\n    try {\n      const { naturalWidth, naturalHeight } = await loadImage(src)\n\n      let width = naturalWidth\n      let height = naturalHeight\n\n      if (width > maxWidth) {\n        const ratio = maxWidth / width\n        width = maxWidth\n        height *= ratio\n      }\n\n      if (height > maxHeight) {\n        const ratio = maxHeight / height\n        height = maxHeight\n        width *= ratio\n      }\n\n      const element = createMedia({\n        height,\n        locked: false,\n        mimeType,\n        naturalHeight,\n        naturalWidth,\n        rotation: 0,\n        src,\n        visible: true,\n        width,\n        x: position.x - width / 2,\n        y: position.y - height / 2,\n        zIndex: context.getElements().size,\n      })\n\n      const elements = context.getElements()\n      elements.set(element.id, element)\n      context.setElements(elements)\n      context.setSelectedIds(new Set([element.id]))\n      context.pushHistory()\n      context.setActiveTool(\"select\")\n    } catch (error) {\n      console.error(\"Failed to load media:\", error)\n    }\n  }\n\n  return {\n    cursor: \"copy\",\n    getTemporaryElement() {\n      return null\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      pendingMedia = null\n      insertPosition = null\n    },\n    onPointerDown(_context: ToolContext, point: Point, _event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n      insertPosition = point\n    },\n    onPointerMove(_context: ToolContext, point: Point, _event: PointerEvent) {\n      state.currentPoint = point\n    },\n    async onPointerUp(\n      context: ToolContext,\n      _point: Point,\n      _event: PointerEvent,\n    ) {\n      if (pendingMedia && insertPosition) {\n        await insertMedia(\n          context,\n          pendingMedia.src,\n          pendingMedia.mimeType,\n          insertPosition,\n        )\n        pendingMedia = null\n        insertPosition = null\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n    },\n    type: \"media\" as ToolType,\n  }\n}\n\nexport interface MediaTool extends Tool {\n  addMedia(src: string, mimeType: string): void\n}\n","import { createRectangle } from \"../elements\"\nimport type { Point, RectangleElement, ToolType } from \"../types\"\nimport {\n  calculateBounds,\n  createBaseToolState,\n  getDefaultToolOptions,\n  type Tool,\n  type ToolContext,\n  type ToolOptions,\n  type ToolState,\n} from \"./base\"\n\nexport interface RectangleToolOptions extends ToolOptions {\n  cornerRadius?: number\n}\n\nexport function createRectangleTool(options: RectangleToolOptions = {}): Tool {\n  const state: ToolState = createBaseToolState()\n  const toolOptions = { ...getDefaultToolOptions(), ...options }\n  let temporaryElement: RectangleElement | null = null\n\n  return {\n    cursor: \"crosshair\",\n    getTemporaryElement() {\n      return temporaryElement\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate() {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      temporaryElement = null\n    },\n    onPointerDown(_context: ToolContext, point: Point, _event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n    },\n    onPointerMove(_context: ToolContext, point: Point, _event: PointerEvent) {\n      if (!state.startPoint) {\n        return\n      }\n\n      state.currentPoint = point\n\n      const bounds = calculateBounds(state.startPoint, point)\n\n      temporaryElement = createRectangle({\n        cornerRadius: toolOptions.cornerRadius ?? 0,\n        height: bounds.height,\n        locked: false,\n        rotation: 0,\n        visible: true,\n        width: bounds.width,\n        x: bounds.x,\n        y: bounds.y,\n        zIndex: 0,\n      })\n    },\n    onPointerUp(context: ToolContext, _point: Point, _event: PointerEvent) {\n      if (!state.startPoint || !state.currentPoint) {\n        return\n      }\n\n      const bounds = calculateBounds(state.startPoint, state.currentPoint)\n\n      if (bounds.width > 5 && bounds.height > 5) {\n        const element = createRectangle({\n          cornerRadius: toolOptions.cornerRadius ?? 0,\n          height: bounds.height,\n          locked: false,\n          rotation: 0,\n          visible: true,\n          width: bounds.width,\n          x: bounds.x,\n          y: bounds.y,\n          zIndex: context.getElements().size,\n        })\n\n        const elements = context.getElements()\n        elements.set(element.id, element)\n        context.setElements(elements)\n        context.setSelectedIds(new Set([element.id]))\n        context.pushHistory()\n        context.setActiveTool(\"select\")\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n      temporaryElement = null\n    },\n    type: \"rectangle\" as ToolType,\n  }\n}\n","import { getElementAtPoint, getElementsBounds } from \"../elements\"\nimport type {\n  BoundingBox,\n  CanvasElement,\n  ElementId,\n  Point,\n  ToolType,\n} from \"../types\"\nimport {\n  calculateBounds,\n  createBaseToolState,\n  type Tool,\n  type ToolContext,\n  type ToolState,\n} from \"./base\"\n\nexport interface SelectToolOptions {\n  multiSelectModifier: \"shift\" | \"ctrl\"\n}\n\nfunction getPointsBounds(points: Point[]) {\n  let minX = Infinity\n  let minY = Infinity\n  let maxX = -Infinity\n  let maxY = -Infinity\n  for (const p of points) {\n    minX = Math.min(minX, p.x)\n    maxX = Math.max(maxX, p.x)\n    minY = Math.min(minY, p.y)\n    maxY = Math.max(maxY, p.y)\n  }\n  return { height: maxY - minY, width: maxX - minX, x: minX, y: minY }\n}\n\n// Axis-aligned bounding-box overlap test. The marquee selects any element whose\n// bounding box it touches (intersection semantics), so rotation is approximated\n// by the element's unrotated box — good enough for a rubber-band selection.\nfunction boxesIntersect(a: BoundingBox, b: BoundingBox): boolean {\n  return (\n    a.x <= b.x + b.width &&\n    a.x + a.width >= b.x &&\n    a.y <= b.y + b.height &&\n    a.y + a.height >= b.y\n  )\n}\n\nexport function createSelectTool(\n  options: SelectToolOptions = { multiSelectModifier: \"shift\" },\n): Tool {\n  const state: ToolState = createBaseToolState()\n  let dragStartElement: CanvasElement | null = null\n  let dragStartPoint: Point | null = null\n  const originalPositions = new Map<\n    ElementId,\n    {\n      x: number\n      y: number\n      width: number\n      height: number\n      rotation: number\n      points?: Point[]\n      lineStart?: Point\n      lineEnd?: Point\n    }\n  >()\n  let dragHandle: string | null = null\n  let rotationCenter: Point | null = null\n  let originalBounds: {\n    x: number\n    y: number\n    width: number\n    height: number\n  } | null = null\n  // Marquee (rubber-band) selection: the anchor point where the brush started,\n  // the current box while dragging, and the selection captured at brush start so\n  // a multi-select modifier can union the brushed elements onto it.\n  let brushStart: Point | null = null\n  let brushBox: BoundingBox | null = null\n  let brushBaseSelection: Set<ElementId> | null = null\n\n  return {\n    cursor: \"default\",\n    getSelectionBox() {\n      return brushBox\n    },\n    getTemporaryElement() {\n      return null\n    },\n    isResizing() {\n      return dragHandle !== null && dragHandle !== \"rotation\"\n    },\n    isRotating() {\n      return dragHandle === \"rotation\"\n    },\n    onActivate() {\n      state.isActive = true\n    },\n    onDeactivate(context: ToolContext) {\n      state.isActive = false\n      state.startPoint = null\n      state.currentPoint = null\n      dragStartElement = null\n      dragStartPoint = null\n      dragHandle = null\n      rotationCenter = null\n      originalBounds = null\n      brushStart = null\n      brushBox = null\n      brushBaseSelection = null\n      originalPositions.clear()\n      context.setSelectedIds(new Set())\n    },\n    onPointerDown(context: ToolContext, point: Point, event: PointerEvent) {\n      state.startPoint = point\n      state.currentPoint = point\n\n      const elements = context.getElements()\n      const selectedIds = context.getSelectedIds()\n\n      // Check if we clicked on a resize handle or rotation handle (only in vanilla for now)\n      // For other frameworks, this would be handled by their event systems\n      const target = event.target as HTMLElement\n      dragHandle = target.getAttribute(\"data-anchor\")\n\n      if (!dragHandle) {\n        const element = getElementAtPoint(elements, point)\n\n        const isMultiSelect =\n          (options.multiSelectModifier === \"shift\" && event.shiftKey) ||\n          (options.multiSelectModifier === \"ctrl\" && event.ctrlKey)\n\n        if (element) {\n          if (isMultiSelect) {\n            if (selectedIds.has(element.id)) {\n              const newSelected = new Set(selectedIds)\n              newSelected.delete(element.id)\n              context.setSelectedIds(newSelected)\n            } else {\n              const newSelected = new Set(selectedIds)\n              newSelected.add(element.id)\n              context.setSelectedIds(newSelected)\n            }\n          } else if (!selectedIds.has(element.id)) {\n            context.setSelectedIds(new Set([element.id]))\n          }\n\n          dragStartElement = element\n          dragStartPoint = point\n        } else {\n          // Empty space: begin a marquee (rubber-band) selection. With the\n          // multi-select modifier held, brushed elements are unioned onto the\n          // existing selection; otherwise start from an empty selection.\n          brushStart = point\n          brushBox = { height: 0, width: 0, x: point.x, y: point.y }\n          brushBaseSelection = isMultiSelect ? new Set(selectedIds) : new Set()\n          if (!isMultiSelect) {\n            context.setSelectedIds(new Set())\n          }\n        }\n      }\n\n      const selectedElements = context.getSelectedIds()\n      for (const id of selectedElements) {\n        const el = elements.get(id)\n        if (el) {\n          originalPositions.set(id, {\n            height: el.height,\n            lineEnd:\n              el.type === \"line\" ? { x: el.endX, y: el.endY } : undefined,\n            lineStart:\n              el.type === \"line\" ? { x: el.startX, y: el.startY } : undefined,\n            // Paths are rendered from their absolute `points`, so a resize/move\n            // must transform the points too. Snapshot them to transform against\n            // a stable source instead of the already-mutated live element.\n            points:\n              el.type === \"path\"\n                ? el.points.map((p) => ({ x: p.x, y: p.y }))\n                : undefined,\n            rotation: el.rotation,\n            width: el.width,\n            x: el.x,\n            y: el.y,\n          })\n        }\n      }\n\n      // Capture the bounding box of the selection at drag start. Both rotation\n      // and resize must reference this constant snapshot, never the live\n      // (already-mutated) elements, otherwise the transform feeds back on itself.\n      if (selectedElements.size > 0) {\n        const bounds = getElementsBounds(elements, selectedElements)\n        if (bounds) {\n          originalBounds = {\n            height: bounds.height,\n            width: bounds.width,\n            x: bounds.x,\n            y: bounds.y,\n          }\n          rotationCenter = {\n            x: bounds.x + bounds.width / 2,\n            y: bounds.y + bounds.height / 2,\n          }\n        }\n      }\n    },\n    onPointerMove(context: ToolContext, point: Point, _event: PointerEvent) {\n      if (!state.startPoint) {\n        return\n      }\n\n      state.currentPoint = point\n      const elements = context.getElements()\n      const selectedIds = context.getSelectedIds()\n\n      if (brushStart) {\n        // Grow the marquee and reselect every element it now touches, unioned\n        // with the selection captured at brush start (empty unless the\n        // multi-select modifier was held).\n        brushBox = calculateBounds(brushStart, point)\n        const next = new Set(brushBaseSelection)\n        for (const el of elements.values()) {\n          if (el.visible && !el.locked && boxesIntersect(brushBox, el)) {\n            next.add(el.id)\n          }\n        }\n        context.setSelectedIds(next)\n        return\n      }\n\n      if (dragHandle === \"rotation\" && rotationCenter) {\n        // Rotate selected elements\n        const startAngle = Math.atan2(\n          state.startPoint.y - rotationCenter.y,\n          state.startPoint.x - rotationCenter.x,\n        )\n        const currentAngle = Math.atan2(\n          point.y - rotationCenter.y,\n          point.x - rotationCenter.x,\n        )\n        const deltaAngle = (currentAngle - startAngle) * (180 / Math.PI)\n        const deltaRad = (deltaAngle * Math.PI) / 180\n        const cos = Math.cos(deltaRad)\n        const sin = Math.sin(deltaRad)\n\n        for (const id of selectedIds) {\n          const original = originalPositions.get(id)\n          if (original) {\n            const element = elements.get(id)\n            if (element) {\n              // Orbit the element's center around the selection center\n              const ecx = original.x + original.width / 2\n              const ecy = original.y + original.height / 2\n              const dx = ecx - rotationCenter.x\n              const dy = ecy - rotationCenter.y\n              const ndx = dx * cos - dy * sin\n              const ndy = dx * sin + dy * cos\n              const ncx = rotationCenter.x + ndx\n              const ncy = rotationCenter.y + ndy\n\n              const newRotation = (original.rotation + deltaAngle) % 360\n\n              if (element.type === \"path\" && original.points) {\n                // Translate path points by the orbital delta\n                const tdx = ncx - ecx\n                const tdy = ncy - ecy\n                const newPoints = original.points.map((p) => ({\n                  x: p.x + tdx,\n                  y: p.y + tdy,\n                }))\n                const nb = getPointsBounds(newPoints)\n                elements.set(id, {\n                  ...element,\n                  height: nb.height,\n                  points: newPoints,\n                  rotation: newRotation,\n                  width: nb.width,\n                  x: nb.x,\n                  y: nb.y,\n                })\n              } else if (\n                element.type === \"line\" &&\n                original.lineStart &&\n                original.lineEnd\n              ) {\n                // Rotate the line's endpoints about the selection center.\n                // The line keeps rotation=0; its visual rotation comes purely\n                // from the changed endpoint coordinates.\n                const cosA = Math.cos(deltaRad)\n                const sinA = Math.sin(deltaRad)\n                const rotatePoint = (p: Point, center: Point) => {\n                  const rx = p.x - center.x\n                  const ry = p.y - center.y\n                  return {\n                    x: center.x + rx * cosA - ry * sinA,\n                    y: center.y + rx * sinA + ry * cosA,\n                  }\n                }\n                const rotatedStart = rotatePoint(\n                  original.lineStart,\n                  rotationCenter,\n                )\n                const rotatedEnd = rotatePoint(original.lineEnd, rotationCenter)\n                const minX = Math.min(rotatedStart.x, rotatedEnd.x)\n                const minY = Math.min(rotatedStart.y, rotatedEnd.y)\n                const maxX = Math.max(rotatedStart.x, rotatedEnd.x)\n                const maxY = Math.max(rotatedStart.y, rotatedEnd.y)\n                elements.set(id, {\n                  ...element,\n                  endX: rotatedEnd.x,\n                  endY: rotatedEnd.y,\n                  height: Math.max(1, maxY - minY),\n                  rotation: 0,\n                  startX: rotatedStart.x,\n                  startY: rotatedStart.y,\n                  width: Math.max(1, maxX - minX),\n                  x: minX,\n                  y: minY,\n                })\n              } else {\n                elements.set(id, {\n                  ...element,\n                  rotation: newRotation,\n                  x: ncx - original.width / 2,\n                  y: ncy - original.height / 2,\n                })\n              }\n            }\n          }\n        }\n        context.setElements(new Map(elements))\n      } else if (dragHandle === \"line-start\" || dragHandle === \"line-end\") {\n        // Drag a line's endpoint handle — move that endpoint and update bbox.\n        for (const id of selectedIds) {\n          const element = elements.get(id)\n          const original = originalPositions.get(id)\n          if (\n            element?.type !== \"line\" ||\n            !original ||\n            !original.lineStart ||\n            !original.lineEnd\n          ) {\n            continue\n          }\n          if (dragHandle === \"line-start\") {\n            const newX = Math.min(point.x, original.lineEnd.x)\n            const newY = Math.min(point.y, original.lineEnd.y)\n            const newW = Math.abs(point.x - original.lineEnd.x)\n            const newH = Math.abs(point.y - original.lineEnd.y)\n            elements.set(id, {\n              ...element,\n              endX: original.lineEnd.x,\n              endY: original.lineEnd.y,\n              height: Math.max(1, newH),\n              startX: point.x,\n              startY: point.y,\n              width: Math.max(1, newW),\n              x: newX,\n              y: newY,\n            })\n          } else {\n            const newX = Math.min(original.lineStart.x, point.x)\n            const newY = Math.min(original.lineStart.y, point.y)\n            const newW = Math.abs(point.x - original.lineStart.x)\n            const newH = Math.abs(point.y - original.lineStart.y)\n            elements.set(id, {\n              ...element,\n              endX: point.x,\n              endY: point.y,\n              height: Math.max(1, newH),\n              startX: original.lineStart.x,\n              startY: original.lineStart.y,\n              width: Math.max(1, newW),\n              x: newX,\n              y: newY,\n            })\n          }\n        }\n        context.setElements(new Map(elements))\n      } else if (dragHandle && dragHandle !== \"rotation\" && originalBounds) {\n        // Resize selected elements relative to the snapshot taken on\n        // pointer-down so the transform tracks the pointer instead of feeding\n        // back on the elements it just mutated.\n        const bounds = originalBounds\n        const movesLeft =\n          dragHandle === \"top-left\" ||\n          dragHandle === \"bottom-left\" ||\n          dragHandle === \"left-center\"\n        const movesTop =\n          dragHandle === \"top-left\" ||\n          dragHandle === \"top-right\" ||\n          dragHandle === \"top-center\"\n        const changesWidth =\n          dragHandle !== \"top-center\" && dragHandle !== \"bottom-center\"\n        const changesHeight =\n          dragHandle !== \"left-center\" && dragHandle !== \"right-center\"\n\n        const [singleId] = selectedIds\n        const singleOriginal =\n          selectedIds.size === 1 ? originalPositions.get(singleId) : undefined\n\n        if (singleOriginal && singleOriginal.rotation % 360 !== 0) {\n          // Rotated element: the handles live in the element's rotated frame, so\n          // the resize must be computed there. We un-rotate the pointer into the\n          // element's local space, size against the fixed (opposite) corner, and\n          // then re-derive the center so that corner stays put in world space.\n          const element = elements.get(singleId)\n          if (element) {\n            const theta = (singleOriginal.rotation * Math.PI) / 180\n            const cos = Math.cos(theta)\n            const sin = Math.sin(theta)\n            const cx = singleOriginal.x + singleOriginal.width / 2\n            const cy = singleOriginal.y + singleOriginal.height / 2\n\n            // Pointer in the element's local (unrotated) frame: R(-theta).\n            const px = point.x - cx\n            const py = point.y - cy\n            const localX = cx + px * cos + py * sin\n            const localY = cy - px * sin + py * cos\n\n            // Opposite edge stays fixed in local space.\n            const anchorX = movesLeft ? bounds.x + bounds.width : bounds.x\n            const anchorY = movesTop ? bounds.y + bounds.height : bounds.y\n\n            // Left signed so a handle dragged past the opposite edge yields a\n            // negative size, which flips the element across the anchor.\n            const newWidth = changesWidth\n              ? movesLeft\n                ? anchorX - localX\n                : localX - anchorX\n              : bounds.width\n            const newHeight = changesHeight\n              ? movesTop\n                ? anchorY - localY\n                : localY - anchorY\n              : bounds.height\n\n            if (element.type === \"path\" && singleOriginal.points) {\n              // Scale the points about the fixed edge in the element's local\n              // (unrotated) frame, then translate so the dragged-opposite corner\n              // stays put in world space. The rotation pivot is the bbox center,\n              // which shifts as the box resizes; t = (I - R(theta)) * (Cold - Cnew)\n              // cancels the world-space drift that shift introduces.\n              const scaleX = changesWidth ? newWidth / bounds.width : 1\n              const scaleY = changesHeight ? newHeight / bounds.height : 1\n              const scaled = singleOriginal.points.map((p) => ({\n                x: anchorX + (p.x - anchorX) * scaleX,\n                y: anchorY + (p.y - anchorY) * scaleY,\n              }))\n              const nb = getPointsBounds(scaled)\n\n              const ddx = cx - (nb.x + nb.width / 2)\n              const ddy = cy - (nb.y + nb.height / 2)\n              const tx = ddx - (ddx * cos - ddy * sin)\n              const ty = ddy - (ddx * sin + ddy * cos)\n\n              elements.set(singleId, {\n                ...element,\n                height: nb.height,\n                points: scaled.map((p) => ({ x: p.x + tx, y: p.y + ty })),\n                width: nb.width,\n                x: nb.x + tx,\n                y: nb.y + ty,\n              })\n              context.setElements(new Map(elements))\n              return\n            }\n\n            if (\n              element.type === \"line\" &&\n              singleOriginal.lineStart &&\n              singleOriginal.lineEnd\n            ) {\n              const scaleX = changesWidth ? newWidth / bounds.width : 1\n              const scaleY = changesHeight ? newHeight / bounds.height : 1\n              const scaledStart = {\n                x: anchorX + (singleOriginal.lineStart.x - anchorX) * scaleX,\n                y: anchorY + (singleOriginal.lineStart.y - anchorY) * scaleY,\n              }\n              const scaledEnd = {\n                x: anchorX + (singleOriginal.lineEnd.x - anchorX) * scaleX,\n                y: anchorY + (singleOriginal.lineEnd.y - anchorY) * scaleY,\n              }\n              const minX = Math.min(scaledStart.x, scaledEnd.x)\n              const minY = Math.min(scaledStart.y, scaledEnd.y)\n              const maxX = Math.max(scaledStart.x, scaledEnd.x)\n              const maxY = Math.max(scaledStart.y, scaledEnd.y)\n              const nb = {\n                height: Math.max(1, maxY - minY),\n                width: Math.max(1, maxX - minX),\n                x: minX,\n                y: minY,\n              }\n\n              const ddx = cx - (nb.x + nb.width / 2)\n              const ddy = cy - (nb.y + nb.height / 2)\n              const tx = ddx - (ddx * cos - ddy * sin)\n              const ty = ddy - (ddx * sin + ddy * cos)\n\n              elements.set(singleId, {\n                ...element,\n                endX: scaledEnd.x + tx,\n                endY: scaledEnd.y + ty,\n                height: nb.height,\n                startX: scaledStart.x + tx,\n                startY: scaledStart.y + ty,\n                width: nb.width,\n                x: nb.x + tx,\n                y: nb.y + ty,\n              })\n              context.setElements(new Map(elements))\n              return\n            }\n\n            // Anchor offset from center, before and after the resize. The\n            // anchor is the corner/edge opposite the dragged handle.\n            const signX = changesWidth ? (movesLeft ? 1 : -1) : 0\n            const signY = changesHeight ? (movesTop ? 1 : -1) : 0\n            const origDx = (signX * bounds.width) / 2\n            const origDy = (signY * bounds.height) / 2\n            const newDx = (signX * newWidth) / 2\n            const newDy = (signY * newHeight) / 2\n\n            // World position of the anchor stays fixed: world = C + R(theta)*d.\n            // newDx/newDy carry the (possibly negative) sign so the center lands\n            // on the correct side when the box flips past the anchor.\n            const anchorWorldX = cx + origDx * cos - origDy * sin\n            const anchorWorldY = cy + origDx * sin + origDy * cos\n            const newCx = anchorWorldX - (newDx * cos - newDy * sin)\n            const newCy = anchorWorldY - (newDx * sin + newDy * cos)\n\n            // Store positive dimensions about the same center; a rectangle\n            // mirrored about its own center is identical, so abs() is all the\n            // flip needs here.\n            const absWidth = Math.max(1, Math.abs(newWidth))\n            const absHeight = Math.max(1, Math.abs(newHeight))\n            elements.set(singleId, {\n              ...element,\n              height: absHeight,\n              width: absWidth,\n              x: newCx - absWidth / 2,\n              y: newCy - absHeight / 2,\n            })\n          }\n          context.setElements(new Map(elements))\n          return\n        }\n\n        // The corner/edge opposite the dragged handle stays fixed.\n        const anchorX = movesLeft ? bounds.x + bounds.width : bounds.x\n        const anchorY = movesTop ? bounds.y + bounds.height : bounds.y\n\n        const newWidth = changesWidth\n          ? movesLeft\n            ? anchorX - point.x\n            : point.x - anchorX\n          : bounds.width\n        const newHeight = changesHeight\n          ? movesTop\n            ? anchorY - point.y\n            : point.y - anchorY\n          : bounds.height\n\n        const scaleX = changesWidth ? newWidth / bounds.width : 1\n        const scaleY = changesHeight ? newHeight / bounds.height : 1\n\n        for (const id of selectedIds) {\n          const original = originalPositions.get(id)\n          if (original) {\n            const element = elements.get(id)\n            if (element) {\n              if (element.type === \"path\" && original.points) {\n                // A path renders from its absolute points, so scale those about\n                // the same anchor instead of only resizing the bounding box. A\n                // negative scale (handle dragged past the anchor) mirrors the\n                // points; re-derive the bbox from the result so it stays valid.\n                const scaledPoints = original.points.map((p) => ({\n                  x: anchorX + (p.x - anchorX) * scaleX,\n                  y: anchorY + (p.y - anchorY) * scaleY,\n                }))\n                const nb = getPointsBounds(scaledPoints)\n                elements.set(id, {\n                  ...element,\n                  height: Math.max(1, nb.height),\n                  points: scaledPoints,\n                  width: Math.max(1, nb.width),\n                  x: nb.x,\n                  y: nb.y,\n                })\n              } else if (\n                element.type === \"line\" &&\n                original.lineStart &&\n                original.lineEnd\n              ) {\n                // Scale the line's absolute endpoints about the same anchor.\n                const scaledStart = {\n                  x: anchorX + (original.lineStart.x - anchorX) * scaleX,\n                  y: anchorY + (original.lineStart.y - anchorY) * scaleY,\n                }\n                const scaledEnd = {\n                  x: anchorX + (original.lineEnd.x - anchorX) * scaleX,\n                  y: anchorY + (original.lineEnd.y - anchorY) * scaleY,\n                }\n                const minX = Math.min(scaledStart.x, scaledEnd.x)\n                const minY = Math.min(scaledStart.y, scaledEnd.y)\n                const maxX = Math.max(scaledStart.x, scaledEnd.x)\n                const maxY = Math.max(scaledStart.y, scaledEnd.y)\n                elements.set(id, {\n                  ...element,\n                  endX: scaledEnd.x,\n                  endY: scaledEnd.y,\n                  height: Math.max(1, maxY - minY),\n                  startX: scaledStart.x,\n                  startY: scaledStart.y,\n                  width: Math.max(1, maxX - minX),\n                  x: minX,\n                  y: minY,\n                })\n              } else {\n                // Scale each element's size and position relative to the fixed\n                // anchor so multi-element selections keep their layout.\n                let newX = anchorX + (original.x - anchorX) * scaleX\n                let newY = anchorY + (original.y - anchorY) * scaleY\n                let newElementWidth = original.width * scaleX\n                let newElementHeight = original.height * scaleY\n\n                // A handle dragged past the opposite edge produces a negative\n                // scale; flip the element across the anchor instead of pinning\n                // it to a 1px sliver.\n                if (newElementWidth < 0) {\n                  newX += newElementWidth\n                  newElementWidth = -newElementWidth\n                }\n                if (newElementHeight < 0) {\n                  newY += newElementHeight\n                  newElementHeight = -newElementHeight\n                }\n\n                elements.set(id, {\n                  ...element,\n                  height: Math.max(1, newElementHeight),\n                  width: Math.max(1, newElementWidth),\n                  x: newX,\n                  y: newY,\n                })\n              }\n            }\n          }\n        }\n        context.setElements(new Map(elements))\n      } else if (dragStartElement && dragStartPoint) {\n        // Move selected elements\n        const delta = {\n          x: point.x - dragStartPoint.x,\n          y: point.y - dragStartPoint.y,\n        }\n\n        for (const id of selectedIds) {\n          const original = originalPositions.get(id)\n          if (original) {\n            const element = elements.get(id)\n            if (element) {\n              if (element.type === \"path\" && original.points) {\n                // Paths render from absolute points, so move them alongside the\n                // bounding box rather than relying on a separate DOM-layer shift.\n                elements.set(id, {\n                  ...element,\n                  points: original.points.map((p) => ({\n                    x: p.x + delta.x,\n                    y: p.y + delta.y,\n                  })),\n                  x: original.x + delta.x,\n                  y: original.y + delta.y,\n                })\n              } else if (\n                element.type === \"line\" &&\n                original.lineStart &&\n                original.lineEnd\n              ) {\n                // Lines also render from absolute start/end coordinates.\n                elements.set(id, {\n                  ...element,\n                  endX: original.lineEnd.x + delta.x,\n                  endY: original.lineEnd.y + delta.y,\n                  startX: original.lineStart.x + delta.x,\n                  startY: original.lineStart.y + delta.y,\n                  x: original.x + delta.x,\n                  y: original.y + delta.y,\n                })\n              } else {\n                elements.set(id, {\n                  ...element,\n                  x: original.x + delta.x,\n                  y: original.y + delta.y,\n                })\n              }\n            }\n          }\n        }\n\n        context.setElements(new Map(elements))\n      }\n    },\n    onPointerUp(context: ToolContext, _point: Point, _event: PointerEvent) {\n      // A marquee only changes selection, never geometry, so it must not push a\n      // history entry even when it started from a non-empty selection.\n      if (!brushStart && originalPositions.size > 0) {\n        context.pushHistory()\n      }\n\n      state.startPoint = null\n      state.currentPoint = null\n      dragStartElement = null\n      dragStartPoint = null\n      dragHandle = null\n      rotationCenter = null\n      originalBounds = null\n      brushStart = null\n      brushBox = null\n      brushBaseSelection = null\n      originalPositions.clear()\n    },\n    type: \"select\" as ToolType,\n  }\n}\n","import {\n  BACKGROUND_COLOR,\n  FILL_COLOR,\n  SELECTION_COLOR,\n  STROKE_COLOR,\n  STROKE_WIDTH,\n} from \"./constants\"\nimport { screenToCanvas } from \"./coordinates\"\nimport { DEFAULT_PATH_SMOOTHING, getElementsBounds } from \"./elements\"\nimport {\n  canRedo,\n  canUndo,\n  createHistoryState,\n  pushHistory,\n  redo,\n  undo,\n} from \"./history\"\nimport { createSnappingConfig, type SnappingConfig } from \"./snapping\"\nimport {\n  createDrawTool,\n  createEllipseTool,\n  createEraserTool,\n  createHandTool,\n  createLineTool,\n  createMediaTool,\n  createRectangleTool,\n  createSelectTool,\n} from \"./tools\"\nimport type { Tool, ToolContext } from \"./tools/base\"\nimport type {\n  CanvasElement,\n  ElementId,\n  LineElement,\n  Point,\n  ToolType,\n  ViewportState,\n} from \"./types\"\nimport {\n  createViewport,\n  panViewport,\n  resetViewport,\n  zoomViewport,\n} from \"./viewport\"\n\nexport interface CanvasOptions {\n  snapping?: Partial<SnappingConfig>\n  initialViewport?: ViewportState\n  // Hide the selection bounding box + resize/rotation handles while a\n  // resize/rotation gesture is in progress, so the overlay doesn't lag the\n  // element mid-gesture; it reappears on pointer up. Defaults to `true`.\n  hideOverlayWhileTransforming?: boolean\n}\n\nexport interface AdrawCanvasOptions extends CanvasOptions {\n  // When provided, the canvas mounts into this container immediately. Omit it to\n  // create a headless instance (state only) and call `mount(container)` later.\n  container?: HTMLElement\n}\n\nexport interface CanvasEventMap {\n  change: { elements: Map<ElementId, CanvasElement> }\n  viewportChange: { viewport: ViewportState }\n  toolChange: { tool: ToolType }\n  selectionChange: { selectedIds: Set<ElementId> }\n}\n\ntype EventListener<K extends keyof CanvasEventMap> = (\n  event: CanvasEventMap[K],\n) => void\n\nconst svgNamespaceURI = \"http://www.w3.org/2000/svg\"\nconst elementsGroupClass = \"adraw-elements-group\"\nconst elementClass = \"adraw-element\"\nconst temporaryClass = \"adraw-temporary\"\nconst guidesGroupClass = \"adraw-guides-group\"\nconst selectedClass = \"adraw-selected\"\nconst transformOverlayClass = \"adraw-transform-overlay\"\nconst rotationHandleClass = \"adraw-rotation-handle\"\nconst resizeHandleClass = \"adraw-resize-handle\"\nconst resizeEdgeClass = \"adraw-resize-edge\"\nconst selectionBoxClass = \"adraw-selection-box\"\n\nconst boundingBoxStrokeWidth = 2\nconst resizeHandleSize = 12\nconst rotationHandleRadio = 6\nconst rotationHandleSpacing = 30\n\n// Cursor for each resize/rotation handle, keyed by its `data-anchor` value.\nconst handleCursorMap: Record<string, string> = {\n  \"bottom-center\": \"s-resize\",\n  \"bottom-left\": \"sw-resize\",\n  \"bottom-right\": \"se-resize\",\n  \"left-center\": \"w-resize\",\n  \"right-center\": \"e-resize\",\n  rotation: \"crosshair\",\n  \"top-center\": \"n-resize\",\n  \"top-left\": \"nw-resize\",\n  \"top-right\": \"ne-resize\",\n}\n\nfunction getTransformElementAttribute(element: CanvasElement) {\n  // Paths are drawn from absolute coordinates (no translate), so they must\n  // rotate about their absolute bbox center. Lines never use rotate() — their\n  // visual rotation comes purely from changed endpoint coordinates. Other\n  // elements are translated to (x, y) first, so their pivot is the local center\n  // (width/2, height/2).\n  if (element.type === \"line\") {\n    return \"\"\n  }\n  if (element.type === \"path\") {\n    const cx = element.x + element.width / 2\n    const cy = element.y + element.height / 2\n    return `rotate(${element.rotation}, ${cx}, ${cy})`\n  }\n  const translate = `translate(${element.x}, ${element.y})`\n  const rotate = `rotate(${element.rotation}, ${element.width / 2}, ${element.height / 2})`\n  return `${translate} ${rotate}`\n}\n\n// Render the path through every point with a Cardinal/Catmull-Rom spline\n// expressed as cubic Béziers. This yields a smooth curve that still\n// interpolates each point, unlike straight line segments which look jagged for\n// freehand strokes. `tension` scales the control-point tangents: 0 collapses to\n// straight segments, 1 gives a full Catmull-Rom curve.\nexport function pointsToPath(\n  points: Point[],\n  tension = DEFAULT_PATH_SMOOTHING,\n): string {\n  if (points.length === 0) {\n    return \"\"\n  }\n\n  let d = `M ${points[0].x} ${points[0].y}`\n\n  if (points.length < 3 || tension <= 0) {\n    for (let i = 1; i < points.length; i++) {\n      d += ` L ${points[i].x} ${points[i].y}`\n    }\n    return d\n  }\n\n  // Catmull-Rom tangents are (p2 - p0) / 2 scaled to thirds for the Bézier\n  // control points, i.e. /6. Folding tension in gives tension / 6.\n  const k = tension / 6\n\n  for (let i = 0; i < points.length - 1; i++) {\n    const p0 = points[i === 0 ? 0 : i - 1]\n    const p1 = points[i]\n    const p2 = points[i + 1]\n    const p3 = points[i + 2 < points.length ? i + 2 : points.length - 1]\n\n    const cp1x = p1.x + (p2.x - p0.x) * k\n    const cp1y = p1.y + (p2.y - p0.y) * k\n    const cp2x = p2.x - (p3.x - p1.x) * k\n    const cp2y = p2.y - (p3.y - p1.y) * k\n\n    d += ` C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${p2.x} ${p2.y}`\n  }\n\n  return d\n}\n\nexport function createElementGroup(element: CanvasElement): SVGGElement {\n  const group = document.createElementNS(svgNamespaceURI, \"g\")\n  group.id = element.id\n  group.setAttribute(\"transform\", getTransformElementAttribute(element))\n\n  switch (element.type) {\n    case \"rectangle\": {\n      const rect = document.createElementNS(svgNamespaceURI, \"rect\")\n      rect.setAttribute(\"width\", `${element.width}`)\n      rect.setAttribute(\"height\", `${element.height}`)\n      rect.setAttribute(\"rx\", `${element.cornerRadius}`)\n      rect.setAttribute(\"fill\", FILL_COLOR)\n      rect.setAttribute(\"stroke\", STROKE_COLOR)\n      rect.setAttribute(\"stroke-width\", `${STROKE_WIDTH}`)\n      group.appendChild(rect)\n      break\n    }\n\n    case \"ellipse\": {\n      const ellipse = document.createElementNS(svgNamespaceURI, \"ellipse\")\n      ellipse.setAttribute(\"cx\", `${element.width / 2}`)\n      ellipse.setAttribute(\"cy\", `${element.height / 2}`)\n      ellipse.setAttribute(\"rx\", `${element.width / 2}`)\n      ellipse.setAttribute(\"ry\", `${element.height / 2}`)\n      ellipse.setAttribute(\"fill\", FILL_COLOR)\n      ellipse.setAttribute(\"stroke\", STROKE_COLOR)\n      ellipse.setAttribute(\"stroke-width\", `${STROKE_WIDTH}`)\n      group.appendChild(ellipse)\n      break\n    }\n\n    case \"line\": {\n      const line = document.createElementNS(svgNamespaceURI, \"line\")\n      line.setAttribute(\"x1\", `${element.startX}`)\n      line.setAttribute(\"y1\", `${element.startY}`)\n      line.setAttribute(\"x2\", `${element.endX}`)\n      line.setAttribute(\"y2\", `${element.endY}`)\n      line.setAttribute(\"stroke\", element.strokeColor || STROKE_COLOR)\n      line.setAttribute(\n        \"stroke-width\",\n        `${element.strokeWidth || STROKE_WIDTH}`,\n      )\n      group.appendChild(line)\n      break\n    }\n\n    case \"path\": {\n      const pathData = pointsToPath(element.points, element.smoothing)\n      const path = document.createElementNS(svgNamespaceURI, \"path\")\n      path.setAttribute(\"d\", pathData)\n      path.setAttribute(\"fill\", \"none\")\n      path.setAttribute(\"stroke\", element.strokeColor || STROKE_COLOR)\n      path.setAttribute(\n        \"stroke-width\",\n        `${element.strokeWidth || STROKE_WIDTH}`,\n      )\n      group.appendChild(path)\n      break\n    }\n\n    case \"media\": {\n      const image = document.createElementNS(svgNamespaceURI, \"image\")\n      image.setAttribute(\"href\", element.src)\n      image.setAttribute(\"width\", `${element.width}`)\n      image.setAttribute(\"height\", `${element.height}`)\n      image.setAttribute(\"preserveAspectRatio\", \"none\")\n      group.appendChild(image)\n      break\n    }\n  }\n\n  return group\n}\n\nexport class AdrawCanvas {\n  // ── Canvas state (pure logic, available headless) ──\n  private elements = new Map<ElementId, CanvasElement>()\n  private selectedIds = new Set<ElementId>()\n  private viewport: ViewportState\n  private activeTool: Tool\n  private snappingConfig: SnappingConfig\n  private hideOverlayWhileTransforming: boolean\n  private history = createHistoryState()\n  private listeners = new Map<keyof CanvasEventMap, Set<EventListener<any>>>()\n  private canvasSize: { width: number; height: number } = {\n    height: 0,\n    width: 0,\n  }\n  private tools = new Map<ToolType, Tool>()\n\n  // ── DOM adapter (populated by mount) ──\n  private container: HTMLElement | null = null\n  private svgElement: SVGSVGElement | null = null\n  private elementsGroup: SVGGElement | null = null\n  // The in-progress element (from the active tool) is rendered directly into\n  // `elementsGroup`; this tracks its node so it can be updated/removed in place.\n  private temporaryNode: SVGGElement | null = null\n  // Element type the current `temporaryNode` was built for, so `renderTemporary`\n  // can update it in place while the type is unchanged instead of recreating it.\n  private temporaryType: CanvasElement[\"type\"] | null = null\n  private guidesGroup: SVGGElement | null = null\n  private transformOverlay: SVGGElement | null = null\n  // Persistent transform-overlay nodes. Built once and updated in place on every\n  // render (rather than wiping `transformOverlay` and recreating ~10 SVG nodes\n  // per pointer move). The cached `group` is detached from the DOM when there's\n  // no selection and re-attached otherwise, so it stays absent (not just hidden)\n  // when nothing is selected.\n  private overlayNodes: {\n    group: SVGGElement\n    boundingBox: SVGRectElement\n    edges: SVGLineElement[]\n    rotationHandle: SVGCircleElement\n    resizeHandles: SVGRectElement[]\n    lineHandles?: SVGRectElement[]\n  } | null = null\n  // Persistent marquee (rubber-band) node, likewise reused across renders.\n  private selectionBoxNode: SVGRectElement | null = null\n  private resizeObserver: ResizeObserver | null = null\n\n  // Touch gesture state\n  private pinchStartDistance: number | null = null\n  private pinchStartCenter: Point | null = null\n  private pinchViewportState: ViewportState | null = null\n\n  constructor(options: AdrawCanvasOptions = {}) {\n    this.viewport = createViewport(options.initialViewport)\n    this.snappingConfig = createSnappingConfig(options.snapping)\n    this.hideOverlayWhileTransforming =\n      options.hideOverlayWhileTransforming ?? false\n\n    this.tools.set(\"select\", createSelectTool())\n    this.tools.set(\"hand\", createHandTool())\n    this.tools.set(\"rectangle\", createRectangleTool())\n    this.tools.set(\"ellipse\", createEllipseTool())\n    this.tools.set(\"line\", createLineTool())\n    this.tools.set(\"draw\", createDrawTool())\n    this.tools.set(\"eraser\", createEraserTool())\n    this.tools.set(\"media\", createMediaTool())\n\n    this.activeTool = this.tools.get(\"select\")!\n    this.activeTool.onActivate(this.getToolContext())\n\n    // Seed the baseline checkpoint so the first edit can be undone back to the\n    // empty canvas. The top of the undo stack always mirrors the current\n    // committed state.\n    this.history = pushHistory(this.history, this.elements, this.selectedIds)\n\n    if (options.container) {\n      this.mount(options.container)\n    }\n  }\n\n  private getToolContext(): ToolContext {\n    return {\n      getCanvasSize: () => this.canvasSize,\n      getElements: () => this.elements,\n      getSelectedIds: () => this.selectedIds,\n      getViewport: () => this.viewport,\n      pushHistory: () => {\n        this.history = pushHistory(\n          this.history,\n          this.elements,\n          this.selectedIds,\n        )\n      },\n      setActiveTool: (tool) => this.setActiveTool(tool),\n      setElements: (elements) => {\n        this.elements = elements\n        this.emit(\"change\", { elements: this.elements })\n      },\n      setSelectedIds: (ids) => {\n        this.selectedIds = ids\n        this.emit(\"selectionChange\", { selectedIds: this.selectedIds })\n      },\n      setViewport: (viewport) => {\n        this.viewport = viewport\n        this.emit(\"viewportChange\", { viewport: this.viewport })\n      },\n    }\n  }\n\n  setCanvasSize(width: number, height: number): void {\n    this.canvasSize = { height, width }\n  }\n\n  setActiveTool(toolType: ToolType): void {\n    const newTool = this.tools.get(toolType)\n    if (!newTool || newTool === this.activeTool) {\n      return\n    }\n\n    this.activeTool.onDeactivate(this.getToolContext())\n    this.activeTool = newTool\n    this.activeTool.onActivate(this.getToolContext())\n    this.emit(\"toolChange\", { tool: toolType })\n  }\n\n  getActiveTool(): ToolType {\n    return this.activeTool.type\n  }\n\n  getViewport(): ViewportState {\n    return this.viewport\n  }\n\n  setViewport(viewport: ViewportState): void {\n    this.viewport = viewport\n    this.emit(\"viewportChange\", { viewport: this.viewport })\n  }\n\n  getElements(): Map<ElementId, CanvasElement> {\n    return this.elements\n  }\n\n  getSelectedIds(): Set<ElementId> {\n    return this.selectedIds\n  }\n\n  getSnappingConfig(): SnappingConfig {\n    return this.snappingConfig\n  }\n\n  setSnappingConfig(config: Partial<SnappingConfig>): void {\n    this.snappingConfig = { ...this.snappingConfig, ...config }\n  }\n\n  getHideOverlayWhileTransforming(): boolean {\n    return this.hideOverlayWhileTransforming\n  }\n\n  setHideOverlayWhileTransforming(hide: boolean): void {\n    this.hideOverlayWhileTransforming = hide\n    // Re-render so the overlay reflects the change immediately (e.g. toggled\n    // mid-gesture).\n    this.render()\n  }\n\n  canUndo(): boolean {\n    return canUndo(this.history)\n  }\n\n  canRedo(): boolean {\n    return canRedo(this.history)\n  }\n\n  undo(): boolean {\n    const result = undo(this.history, this.elements, this.selectedIds)\n    if (result) {\n      this.elements = result.elements\n      this.selectedIds = result.selectedIds\n      this.history = result.state\n      this.emit(\"change\", { elements: this.elements })\n      this.emit(\"selectionChange\", { selectedIds: this.selectedIds })\n      return true\n    }\n    return false\n  }\n\n  redo(): boolean {\n    const result = redo(this.history, this.elements, this.selectedIds)\n    if (result) {\n      this.elements = result.elements\n      this.selectedIds = result.selectedIds\n      this.history = result.state\n      this.emit(\"change\", { elements: this.elements })\n      this.emit(\"selectionChange\", { selectedIds: this.selectedIds })\n      return true\n    }\n    return false\n  }\n\n  handlePointerDown(\n    screenX: number,\n    screenY: number,\n    event: PointerEvent,\n  ): void {\n    const point = screenToCanvas(\n      { x: screenX, y: screenY },\n      this.viewport,\n      this.canvasSize,\n    )\n    this.activeTool.onPointerDown(this.getToolContext(), point, event)\n  }\n\n  handlePointerMove(\n    screenX: number,\n    screenY: number,\n    event: PointerEvent,\n  ): void {\n    const point = screenToCanvas(\n      { x: screenX, y: screenY },\n      this.viewport,\n      this.canvasSize,\n    )\n    this.activeTool.onPointerMove(this.getToolContext(), point, event)\n  }\n\n  handlePointerUp(screenX: number, screenY: number, event: PointerEvent): void {\n    const point = screenToCanvas(\n      { x: screenX, y: screenY },\n      this.viewport,\n      this.canvasSize,\n    )\n    this.activeTool.onPointerUp(this.getToolContext(), point, event)\n  }\n\n  handleWheel(event: WheelEvent, screenX?: number, screenY?: number): void {\n    event.preventDefault()\n\n    if (event.ctrlKey || event.metaKey) {\n      let x = screenX ?? event.clientX\n      let y = screenY ?? event.clientY\n\n      if (screenX === undefined || screenY === undefined) {\n        const currentTarget = event.currentTarget as HTMLElement | null\n        if (currentTarget) {\n          const rect = currentTarget.getBoundingClientRect()\n          x = event.clientX - rect.left\n          y = event.clientY - rect.top\n        }\n      }\n\n      const centerPoint = {\n        x:\n          (this.canvasSize.width / 2 - x) / this.viewport.zoom +\n          this.viewport.x,\n        y:\n          (this.canvasSize.height / 2 - y) / this.viewport.zoom +\n          this.viewport.y,\n      }\n      this.viewport = zoomViewport(this.viewport, event.deltaY, centerPoint)\n    } else {\n      this.viewport = panViewport(this.viewport, {\n        x: event.deltaX / this.viewport.zoom,\n        y: event.deltaY / this.viewport.zoom,\n      })\n    }\n\n    this.emit(\"viewportChange\", { viewport: this.viewport })\n  }\n\n  handleKeyDown(event: KeyboardEvent): void {\n    if (\n      event.target instanceof HTMLInputElement ||\n      event.target instanceof HTMLTextAreaElement\n    ) {\n      return\n    }\n\n    const key = event.key.toLowerCase()\n\n    if (event.ctrlKey || event.metaKey) {\n      if (key === \"z\" && !event.shiftKey) {\n        event.preventDefault()\n        this.undo()\n      } else if ((key === \"z\" && event.shiftKey) || key === \"y\") {\n        event.preventDefault()\n        this.redo()\n      } else if (key === \"a\") {\n        event.preventDefault()\n        this.selectAll()\n      }\n    } else {\n      switch (key) {\n        case \"v\": {\n          this.setActiveTool(\"select\")\n          break\n        }\n        case \"h\": {\n          this.setActiveTool(\"hand\")\n          break\n        }\n        case \"d\": {\n          this.setActiveTool(\"draw\")\n          break\n        }\n        case \"e\": {\n          this.setActiveTool(\"eraser\")\n          break\n        }\n        case \"r\": {\n          this.setActiveTool(\"rectangle\")\n          break\n        }\n        case \"l\": {\n          this.setActiveTool(\"line\")\n          break\n        }\n        case \"m\": {\n          this.setActiveTool(\"media\")\n          break\n        }\n        case \"delete\":\n        case \"backspace\": {\n          this.deleteSelected()\n          break\n        }\n        case \"escape\": {\n          this.clearSelection()\n          break\n        }\n      }\n    }\n  }\n\n  selectAll(): void {\n    this.selectedIds = new Set(this.elements.keys())\n    this.emit(\"selectionChange\", { selectedIds: this.selectedIds })\n  }\n\n  clearSelection(): void {\n    this.selectedIds = new Set()\n    this.emit(\"selectionChange\", { selectedIds: this.selectedIds })\n  }\n\n  deleteSelected(): void {\n    if (this.selectedIds.size === 0) {\n      return\n    }\n\n    for (const id of this.selectedIds) {\n      this.elements.delete(id)\n    }\n\n    this.history = pushHistory(this.history, this.elements, this.selectedIds)\n    this.selectedIds = new Set()\n    this.emit(\"change\", { elements: this.elements })\n    this.emit(\"selectionChange\", { selectedIds: this.selectedIds })\n  }\n\n  zoomIn(): void {\n    const center = { x: 0, y: 0 }\n    this.viewport = zoomViewport(this.viewport, -100, center)\n    this.emit(\"viewportChange\", { viewport: this.viewport })\n  }\n\n  zoomOut(): void {\n    const center = { x: 0, y: 0 }\n    this.viewport = zoomViewport(this.viewport, 100, center)\n    this.emit(\"viewportChange\", { viewport: this.viewport })\n  }\n\n  resetZoom(): void {\n    this.viewport = resetViewport()\n    this.emit(\"viewportChange\", { viewport: this.viewport })\n  }\n\n  zoomToFit(): void {\n    const bounds = getElementsBounds(this.elements)\n    if (!bounds) {\n      return\n    }\n\n    const contentWidth = bounds.right - bounds.left\n    const contentHeight = bounds.bottom - bounds.top\n\n    if (contentWidth === 0 || contentHeight === 0) {\n      return\n    }\n\n    const availableWidth = this.canvasSize.width - 100\n    const availableHeight = this.canvasSize.height - 100\n\n    const scaleX = availableWidth / contentWidth\n    const scaleY = availableHeight / contentHeight\n    const newZoom = Math.min(scaleX, scaleY, 10)\n\n    this.viewport = {\n      x: (bounds.left + bounds.right) / 2,\n      y: (bounds.top + bounds.bottom) / 2,\n      zoom: Math.max(0.1, newZoom),\n    }\n\n    this.emit(\"viewportChange\", { viewport: this.viewport })\n  }\n\n  getTemporaryElement(): CanvasElement | null {\n    return this.activeTool.getTemporaryElement()\n  }\n\n  on<K extends keyof CanvasEventMap>(\n    event: K,\n    listener: EventListener<K>,\n  ): void {\n    if (!this.listeners.has(event)) {\n      this.listeners.set(event, new Set())\n    }\n    this.listeners.get(event)!.add(listener)\n  }\n\n  off<K extends keyof CanvasEventMap>(\n    event: K,\n    listener: EventListener<K>,\n  ): void {\n    this.listeners.get(event)?.delete(listener)\n  }\n\n  private emit<K extends keyof CanvasEventMap>(\n    event: K,\n    data: CanvasEventMap[K],\n  ): void {\n    this.listeners.get(event)?.forEach((listener) => {\n      listener(data)\n    })\n  }\n\n  // ── DOM adapter ──\n\n  // Attach the canvas to a DOM container, creating the SVG layers and wiring up\n  // pointer/wheel/touch/keyboard events. Safe to call once; no-op if mounted.\n  mount(container: HTMLElement): void {\n    if (this.svgElement) {\n      return\n    }\n    this.container = container\n    this.init()\n    this.setupEventListeners()\n    this.render()\n  }\n\n  private init(): void {\n    if (!this.container) {\n      return\n    }\n\n    this.svgElement = document.createElementNS(svgNamespaceURI, \"svg\")\n    this.svgElement.setAttribute(\"width\", \"100%\")\n    this.svgElement.setAttribute(\"height\", \"100%\")\n    this.svgElement.style.display = \"block\"\n    this.svgElement.style.background = BACKGROUND_COLOR\n    this.svgElement.style.touchAction = \"none\"\n\n    this.elementsGroup = document.createElementNS(svgNamespaceURI, \"g\")\n    this.elementsGroup.classList.add(elementsGroupClass)\n\n    this.guidesGroup = document.createElementNS(svgNamespaceURI, \"g\")\n    this.guidesGroup.classList.add(guidesGroupClass)\n\n    this.transformOverlay = document.createElementNS(svgNamespaceURI, \"g\")\n    this.transformOverlay.classList.add(transformOverlayClass)\n    // Overlay children belong to the fresh `transformOverlay`; drop stale refs\n    // so they're rebuilt into it on the next render (e.g. after a re-mount).\n    this.overlayNodes = null\n    this.selectionBoxNode = null\n\n    this.svgElement.appendChild(this.elementsGroup)\n    this.svgElement.appendChild(this.guidesGroup)\n    this.svgElement.appendChild(this.transformOverlay)\n    this.container.appendChild(this.svgElement)\n\n    this.resizeObserver = new ResizeObserver((entries) => {\n      for (const entry of entries) {\n        const { width, height } = entry.contentRect\n        this.setCanvasSize(width, height)\n        this.render()\n      }\n    })\n    this.resizeObserver.observe(this.container)\n  }\n\n  private getRelativePoint(event: MouseEvent | PointerEvent | WheelEvent): {\n    x: number\n    y: number\n  } {\n    if (!this.svgElement) {\n      return { x: event.clientX, y: event.clientY }\n    }\n    const rect = this.svgElement.getBoundingClientRect()\n    return {\n      x: event.clientX - rect.left,\n      y: event.clientY - rect.top,\n    }\n  }\n\n  private setupEventListeners(): void {\n    if (!this.svgElement) {\n      return\n    }\n\n    this.svgElement.addEventListener(\"pointerdown\", (event) => {\n      const { x, y } = this.getRelativePoint(event)\n      this.handlePointerDown(x, y, event)\n      this.render()\n    })\n\n    this.svgElement.addEventListener(\"pointermove\", (event) => {\n      const { x, y } = this.getRelativePoint(event)\n      this.handlePointerMove(x, y, event)\n      this.render()\n    })\n\n    this.svgElement.addEventListener(\"pointerup\", (event) => {\n      const { x, y } = this.getRelativePoint(event)\n      this.handlePointerUp(x, y, event)\n      this.render()\n    })\n\n    // Set cursor based on hovered handle\n    this.svgElement.addEventListener(\"pointermove\", (event) => {\n      if (!this.svgElement) {\n        return\n      }\n      const target = event.target as HTMLElement\n      const anchor = target.getAttribute(\"data-anchor\")\n      if (anchor && handleCursorMap[anchor]) {\n        this.svgElement.style.cursor = handleCursorMap[anchor]\n      } else if (!this.svgElement.style.cursor.startsWith(\"grab\")) {\n        this.svgElement.style.cursor = \"default\"\n      }\n    })\n\n    this.svgElement.addEventListener(\n      \"wheel\",\n      (event) => {\n        const { x, y } = this.getRelativePoint(event)\n        this.handleWheel(event, x, y)\n        this.render()\n      },\n      { passive: false },\n    )\n\n    this.svgElement.addEventListener(\n      \"touchstart\",\n      (event) => {\n        if (event.touches.length === 2) {\n          event.preventDefault()\n          this.pinchStartDistance = Math.hypot(\n            event.touches[0].clientX - event.touches[1].clientX,\n            event.touches[0].clientY - event.touches[1].clientY,\n          )\n          this.pinchStartCenter = {\n            x: (event.touches[0].clientX + event.touches[1].clientX) / 2,\n            y: (event.touches[0].clientY + event.touches[1].clientY) / 2,\n          }\n          this.pinchViewportState = { ...this.getViewport() }\n        } else if (event.touches.length === 1) {\n          const touch = event.touches[0] as unknown as PointerEvent\n          const { x, y } = this.getRelativePoint(touch)\n          this.handlePointerDown(x, y, touch)\n          this.render()\n        }\n      },\n      { passive: false },\n    )\n\n    this.svgElement.addEventListener(\n      \"touchmove\",\n      (event) => {\n        if (\n          event.touches.length === 2 &&\n          this.pinchStartDistance !== null &&\n          this.pinchStartCenter &&\n          this.pinchViewportState\n        ) {\n          event.preventDefault()\n\n          const currentDistance = Math.hypot(\n            event.touches[0].clientX - event.touches[1].clientX,\n            event.touches[0].clientY - event.touches[1].clientY,\n          )\n          const currentCenter = {\n            x: (event.touches[0].clientX + event.touches[1].clientX) / 2,\n            y: (event.touches[0].clientY + event.touches[1].clientY) / 2,\n          }\n\n          const scale = currentDistance / this.pinchStartDistance\n\n          // Apply scaling relative to the start state and start center point\n          const rect = this.svgElement?.getBoundingClientRect()\n          if (!rect) {\n            return\n          }\n\n          const canvasCenter = {\n            x:\n              (this.pinchStartCenter.x - rect.left - rect.width / 2) /\n                this.pinchViewportState.zoom +\n              this.pinchViewportState.x,\n            y:\n              (this.pinchStartCenter.y - rect.top - rect.height / 2) /\n                this.pinchViewportState.zoom +\n              this.pinchViewportState.y,\n          }\n\n          let newViewport = { ...this.pinchViewportState }\n\n          // Set zoom directly from the pinch scale ratio (clamped), rather than\n          // routing through zoomViewport which expects a wheel deltaY.\n          newViewport.zoom = Math.max(\n            0.1,\n            Math.min(this.pinchViewportState.zoom * scale, 10),\n          )\n\n          // Adjust position so the center stays exactly where it was\n          newViewport.x =\n            canvasCenter.x -\n            (canvasCenter.x - this.pinchViewportState.x) *\n              (this.pinchViewportState.zoom / newViewport.zoom)\n          newViewport.y =\n            canvasCenter.y -\n            (canvasCenter.y - this.pinchViewportState.y) *\n              (this.pinchViewportState.zoom / newViewport.zoom)\n\n          newViewport = panViewport(newViewport, {\n            x: (this.pinchStartCenter.x - currentCenter.x) / newViewport.zoom,\n            y: (this.pinchStartCenter.y - currentCenter.y) / newViewport.zoom,\n          })\n\n          this.setViewport(newViewport)\n          this.render()\n        } else if (event.touches.length === 1 && !this.pinchStartDistance) {\n          const touch = event.touches[0] as unknown as PointerEvent\n          const { x, y } = this.getRelativePoint(touch)\n          this.handlePointerMove(x, y, touch)\n          this.render()\n        }\n      },\n      { passive: false },\n    )\n\n    this.svgElement.addEventListener(\"touchend\", (event) => {\n      this.handleTouchEnd(event)\n    })\n\n    this.svgElement.addEventListener(\"touchcancel\", (event) => {\n      this.handleTouchEnd(event)\n    })\n\n    document.addEventListener(\"keydown\", (event) => {\n      this.handleKeyDown(event)\n      this.render()\n    })\n\n    this.on(\"change\", () => {\n      if (this.getActiveTool() === \"select\") {\n        this.renderSelectElements()\n      } else {\n        this.reconcileElements()\n      }\n    })\n\n    this.on(\"viewportChange\", () => {\n      this.render()\n    })\n\n    this.on(\"toolChange\", ({ tool }) => {\n      this.renderTransformOverlay()\n      this.updateCursor(tool)\n    })\n\n    this.on(\"selectionChange\", () => {\n      this.renderSelectElements()\n    })\n  }\n\n  private handleTouchEnd(event: TouchEvent) {\n    if (event.touches.length < 2) {\n      this.pinchStartDistance = null\n      this.pinchStartCenter = null\n      this.pinchViewportState = null\n    }\n\n    if (event.touches.length === 0) {\n      const touch = event.changedTouches[0] as unknown as PointerEvent\n      const { x, y } = this.getRelativePoint(touch)\n      this.handlePointerUp(x, y, touch)\n      this.render()\n    }\n  }\n\n  private updateCursor(tool: ToolType): void {\n    const cursors: Record<ToolType, string> = {\n      draw: \"crosshair\",\n      ellipse: \"crosshair\",\n      eraser: \"crosshair\",\n      hand: \"grab\",\n      line: \"crosshair\",\n      media: \"copy\",\n      rectangle: \"crosshair\",\n      select: \"default\",\n    }\n    if (this.svgElement) {\n      this.svgElement.style.cursor = cursors[tool] || \"default\"\n    }\n  }\n\n  render(): void {\n    if (!this.container) {\n      return\n    }\n\n    const viewport = this.getViewport()\n    const transform = `translate(${this.container.clientWidth / 2}, ${this.container.clientHeight / 2}) scale(${viewport.zoom}) translate(${-viewport.x}, ${-viewport.y})`\n\n    for (const group of [this.elementsGroup, this.transformOverlay]) {\n      group?.setAttribute(\"transform\", transform)\n    }\n\n    this.renderTemporary()\n    this.renderTransformOverlay()\n    this.renderSelectionBox()\n  }\n\n  // Draw the active tool's in-progress marquee (rubber-band) selection as a\n  // dashed box in the overlay layer. Reuses a single persistent node, hidden\n  // via `display` when there's no marquee, instead of recreating it per render.\n  private renderSelectionBox(): void {\n    if (!this.transformOverlay) {\n      return\n    }\n\n    const box = this.activeTool.getSelectionBox?.()\n    if (!box || (box.width === 0 && box.height === 0)) {\n      this.selectionBoxNode?.remove()\n      return\n    }\n\n    if (!this.selectionBoxNode) {\n      const rect = document.createElementNS(svgNamespaceURI, \"rect\")\n      rect.classList.add(selectionBoxClass)\n      rect.setAttribute(\"fill\", SELECTION_COLOR)\n      rect.setAttribute(\"fill-opacity\", \"0.1\")\n      rect.setAttribute(\"stroke\", SELECTION_COLOR)\n      rect.setAttribute(\"stroke-opacity\", \"0.5\")\n      rect.setAttribute(\"stroke-width\", `${boundingBoxStrokeWidth}`)\n      rect.setAttribute(\"vector-effect\", \"non-scaling-stroke\")\n      this.selectionBoxNode = rect\n    }\n\n    // Append after the overlay group (rendered first) so the marquee stays on\n    // top; no-op re-append while it's already attached.\n    if (this.selectionBoxNode.parentNode !== this.transformOverlay) {\n      this.transformOverlay.appendChild(this.selectionBoxNode)\n    }\n\n    const handleSize = resizeHandleSize / this.viewport.zoom\n    const rect = this.selectionBoxNode\n    rect.setAttribute(\"x\", `${box.x}`)\n    rect.setAttribute(\"y\", `${box.y}`)\n    rect.setAttribute(\"rx\", `${handleSize / 4}`)\n    rect.setAttribute(\"width\", `${box.width}`)\n    rect.setAttribute(\"height\", `${box.height}`)\n  }\n\n  // Build the overlay's bounding box, edge bands, rotation handle and resize\n  // handles once. Subsequent renders update these nodes in place (see\n  // `renderTransformOverlay`) rather than recreating them.\n  private ensureOverlayNodes(): NonNullable<AdrawCanvas[\"overlayNodes\"]> {\n    if (this.overlayNodes) {\n      return this.overlayNodes\n    }\n\n    const group = document.createElementNS(svgNamespaceURI, \"g\")\n\n    // Main bounding box\n    const boundingBox = document.createElementNS(svgNamespaceURI, \"rect\")\n    boundingBox.setAttribute(\"fill\", \"none\")\n    boundingBox.setAttribute(\"stroke\", SELECTION_COLOR)\n    boundingBox.setAttribute(\"stroke-width\", `${boundingBoxStrokeWidth}`)\n    boundingBox.setAttribute(\"vector-effect\", \"non-scaling-stroke\")\n    group.appendChild(boundingBox)\n\n    // Invisible edge bands — dragging an edge resizes along that axis. They\n    // carry the `*-center` anchors so the select tool's existing resize logic\n    // handles them exactly like the old edge-center handles did.\n    const edgeAnchors = [\n      \"top-center\",\n      \"right-center\",\n      \"bottom-center\",\n      \"left-center\",\n    ]\n    const edges = edgeAnchors.map((anchor) => {\n      const line = document.createElementNS(svgNamespaceURI, \"line\")\n      line.classList.add(resizeEdgeClass)\n      line.setAttribute(\"stroke\", \"transparent\")\n      line.setAttribute(\"pointer-events\", \"stroke\")\n      line.setAttribute(\"data-anchor\", anchor)\n      group.appendChild(line)\n      return line\n    })\n\n    // Rotation handle\n    const rotationHandle = document.createElementNS(svgNamespaceURI, \"circle\")\n    rotationHandle.classList.add(rotationHandleClass)\n    rotationHandle.setAttribute(\"fill\", BACKGROUND_COLOR)\n    rotationHandle.setAttribute(\"stroke\", SELECTION_COLOR)\n    rotationHandle.setAttribute(\"stroke-width\", `${boundingBoxStrokeWidth}`)\n    rotationHandle.setAttribute(\"vector-effect\", \"non-scaling-stroke\")\n    rotationHandle.setAttribute(\"data-anchor\", \"rotation\")\n    group.appendChild(rotationHandle)\n\n    // Resize handles (corners)\n    const handleAnchors = [\n      \"top-left\",\n      \"top-right\",\n      \"bottom-right\",\n      \"bottom-left\",\n    ]\n    const resizeHandles = handleAnchors.map((anchor) => {\n      const square = document.createElementNS(svgNamespaceURI, \"rect\")\n      square.classList.add(resizeHandleClass)\n      square.setAttribute(\"fill\", BACKGROUND_COLOR)\n      square.setAttribute(\"stroke\", SELECTION_COLOR)\n      square.setAttribute(\"stroke-width\", `${boundingBoxStrokeWidth}`)\n      square.setAttribute(\"vector-effect\", \"non-scaling-stroke\")\n      square.setAttribute(\"data-anchor\", anchor)\n      group.appendChild(square)\n      return square\n    })\n\n    // Line endpoint handles\n    const lineAnchors = [\"line-start\", \"line-end\"]\n    const lineHandles = lineAnchors.map((anchor) => {\n      const square = document.createElementNS(svgNamespaceURI, \"rect\")\n      square.classList.add(resizeHandleClass)\n      square.setAttribute(\"fill\", BACKGROUND_COLOR)\n      square.setAttribute(\"stroke\", SELECTION_COLOR)\n      square.setAttribute(\"stroke-width\", `${boundingBoxStrokeWidth}`)\n      square.setAttribute(\"vector-effect\", \"non-scaling-stroke\")\n      square.setAttribute(\"data-anchor\", anchor)\n      group.appendChild(square)\n      return square\n    })\n\n    // Not appended here — `renderTransformOverlay` attaches the group only when\n    // there's a selection and detaches it otherwise.\n    this.overlayNodes = {\n      boundingBox,\n      edges,\n      group,\n      lineHandles,\n      resizeHandles,\n      rotationHandle,\n    }\n    return this.overlayNodes\n  }\n\n  private renderTransformOverlay(): void {\n    if (!this.transformOverlay) {\n      return\n    }\n\n    const nodes = this.ensureOverlayNodes()\n    const selectedIds = this.getSelectedIds()\n    const elements = this.getElements()\n\n    // Hide the bounding box + handles while actively resizing/rotating so the\n    // overlay doesn't lag the element mid-gesture; it reappears on pointer up.\n    // Opt out via the `hideOverlayWhileTransforming` option.\n    const transforming =\n      this.activeTool.isResizing?.() || this.activeTool.isRotating?.()\n    const suppressed =\n      (this.hideOverlayWhileTransforming && transforming) ||\n      (this.activeTool.isRotating?.() && selectedIds.size > 1)\n\n    const bounds =\n      selectedIds.size === 0 || suppressed\n        ? null\n        : getElementsBounds(elements, selectedIds)\n    if (!bounds) {\n      nodes.group.remove()\n      return\n    }\n    // Attach the cached group when needed; no-op while it's already attached.\n    if (nodes.group.parentNode !== this.transformOverlay) {\n      this.transformOverlay.appendChild(nodes.group)\n    }\n\n    const { x, y, width, height } = bounds\n\n    // Rotate the overlay to match a single selected element's rotation.\n    let transform = \"\"\n    if (selectedIds.size === 1) {\n      const [onlyId] = selectedIds\n      const element = elements.get(onlyId)\n      if (element && element.rotation) {\n        transform = `rotate(${element.rotation}, ${x + width / 2}, ${y + height / 2})`\n      }\n    }\n    if (transform) {\n      nodes.group.setAttribute(\"transform\", transform)\n    } else {\n      nodes.group.removeAttribute(\"transform\")\n    }\n\n    const handleSize = resizeHandleSize / this.viewport.zoom\n\n    // Check if the single selected element is a line\n    const isLine =\n      selectedIds.size === 1 &&\n      elements.get([...selectedIds][0])?.type === \"line\"\n\n    if (isLine) {\n      const lineEl = elements.get([...selectedIds][0]) as LineElement\n\n      // Hide standard overlay elements\n      nodes.boundingBox.setAttribute(\"display\", \"none\")\n      for (const edge of nodes.edges) {\n        edge.setAttribute(\"display\", \"none\")\n      }\n      nodes.rotationHandle.setAttribute(\"display\", \"none\")\n      for (const handle of nodes.resizeHandles) {\n        handle.setAttribute(\"display\", \"none\")\n      }\n\n      // Show line endpoint handles\n      const anchors: Point[] = [\n        { x: lineEl.startX, y: lineEl.startY },\n        { x: lineEl.endX, y: lineEl.endY },\n      ]\n      for (let i = 0; i < anchors.length; i++) {\n        const h = anchors[i]\n        const node = nodes.lineHandles![i]\n        node.setAttribute(\"display\", \"inline\")\n        node.setAttribute(\"x\", `${h.x - handleSize / 2}`)\n        node.setAttribute(\"y\", `${h.y - handleSize / 2}`)\n        node.setAttribute(\"rx\", `${handleSize}`)\n        node.setAttribute(\"width\", `${handleSize}`)\n        node.setAttribute(\"height\", `${handleSize}`)\n      }\n    } else {\n      // Show standard overlay, hide line handles\n      nodes.boundingBox.setAttribute(\"display\", \"inline\")\n      for (const edge of nodes.edges) {\n        edge.setAttribute(\"display\", \"inline\")\n      }\n      nodes.rotationHandle.setAttribute(\"display\", \"inline\")\n      for (const handle of nodes.resizeHandles) {\n        handle.setAttribute(\"display\", \"inline\")\n      }\n      for (const handle of nodes.lineHandles!) {\n        handle.setAttribute(\"display\", \"none\")\n      }\n\n      // Main bounding box\n      const rect = nodes.boundingBox\n      rect.setAttribute(\"x\", `${x}`)\n      rect.setAttribute(\"y\", `${y}`)\n      rect.setAttribute(\"rx\", `${handleSize / 4}`)\n      rect.setAttribute(\"width\", `${width}`)\n      rect.setAttribute(\"height\", `${height}`)\n\n      // Edge bands — same order as `edgeAnchors` in `ensureOverlayNodes`.\n      const edgeGeom = [\n        { x1: x, x2: x + width, y1: y, y2: y },\n        { x1: x + width, x2: x + width, y1: y, y2: y + height },\n        { x1: x, x2: x + width, y1: y + height, y2: y + height },\n        { x1: x, x2: x, y1: y, y2: y + height },\n      ]\n      for (let i = 0; i < edgeGeom.length; i++) {\n        const line = nodes.edges[i]\n        const g = edgeGeom[i]\n        line.setAttribute(\"x1\", `${g.x1}`)\n        line.setAttribute(\"y1\", `${g.y1}`)\n        line.setAttribute(\"x2\", `${g.x2}`)\n        line.setAttribute(\"y2\", `${g.y2}`)\n        line.setAttribute(\"stroke-width\", `${handleSize}`)\n      }\n\n      // Rotation handle\n      const rotationHandleY = y - rotationHandleSpacing / this.viewport.zoom\n      const rotationHandleR = rotationHandleRadio / this.viewport.zoom\n      nodes.rotationHandle.setAttribute(\"cx\", `${x + width / 2}`)\n      nodes.rotationHandle.setAttribute(\"cy\", `${rotationHandleY}`)\n      nodes.rotationHandle.setAttribute(\"r\", `${rotationHandleR}`)\n\n      // Resize handle — same order as `handleAnchors` in `ensureOverlayNodes`.\n      const handleGeom = [\n        { x, y },\n        { x: x + width, y },\n        { x: x + width, y: y + height },\n        { x, y: y + height },\n      ]\n      for (let i = 0; i < handleGeom.length; i++) {\n        const square = nodes.resizeHandles[i]\n        const h = handleGeom[i]\n        square.setAttribute(\"x\", `${h.x - handleSize / 2}`)\n        square.setAttribute(\"y\", `${h.y - handleSize / 2}`)\n        square.setAttribute(\"rx\", `${handleSize / 4}`)\n        square.setAttribute(\"width\", `${handleSize}`)\n        square.setAttribute(\"height\", `${handleSize}`)\n      }\n    }\n  }\n\n  // Reconcile `elementsGroup` with the current elements without wiping it: add\n  // nodes for new elements, update existing ones in place, and drop nodes for\n  // elements that no longer exist. This keeps untouched elements' DOM nodes\n  // intact when a new element is added (rather than rebuilding the whole group).\n  private reconcileElements(): void {\n    if (!this.elementsGroup) {\n      return\n    }\n\n    const elements = this.getElements()\n    const selectedIds = this.getSelectedIds()\n\n    // Drop nodes for elements that no longer exist, leaving the temporary node\n    // (which has no matching entry in `elements`) untouched. Iterate backwards:\n    // `children` is live, so removing during a forward loop would skip nodes.\n    const children = this.elementsGroup.children\n    for (let i = children.length - 1; i >= 0; i--) {\n      const child = children[i]\n      if (child === this.temporaryNode) {\n        continue\n      }\n      if (!elements.has(child.id)) {\n        child.remove()\n      }\n    }\n\n    for (const [, element] of elements) {\n      let group = document.getElementById(element.id) as SVGGElement | null\n\n      if (!element.visible) {\n        group?.remove()\n        continue\n      }\n\n      if (group) {\n        this.updateElementGeometry(group, element)\n      } else {\n        group = createElementGroup(element)\n        group.classList.add(elementClass)\n        this.elementsGroup.appendChild(group)\n      }\n\n      group.classList.toggle(selectedClass, selectedIds.has(element.id))\n    }\n  }\n\n  private renderTemporary(): void {\n    if (!this.elementsGroup) {\n      return\n    }\n\n    const tempElement = this.getTemporaryElement()\n\n    // No in-progress element: drop the temporary node if one is lingering.\n    if (!tempElement) {\n      this.temporaryNode?.remove()\n      this.temporaryNode = null\n      this.temporaryType = null\n      return\n    }\n\n    // The temporary element lives inside `elementsGroup` (on top, as the last\n    // child). Reuse its node across pointer moves — while the type is unchanged,\n    // update it in place rather than recreating it. Only build a fresh node when\n    // there is none yet or the element type changed.\n    if (!this.temporaryNode || this.temporaryType !== tempElement.type) {\n      this.temporaryNode?.remove()\n      const group = createElementGroup(tempElement)\n      group.classList.add(temporaryClass)\n      this.temporaryNode = group\n      this.temporaryType = tempElement.type\n      this.elementsGroup.appendChild(group)\n      return\n    }\n\n    this.updateElementGeometry(this.temporaryNode, tempElement)\n  }\n\n  // Update an existing element's DOM node (transform + type-specific geometry)\n  // in place, without recreating it.\n  private updateElementGeometry(\n    group: SVGGElement,\n    element: CanvasElement,\n  ): void {\n    group.setAttribute(\"transform\", getTransformElementAttribute(element))\n\n    switch (element.type) {\n      case \"line\": {\n        const lineElement = group.getElementsByTagName(\"line\")[0]\n        lineElement.setAttribute(\"x1\", `${element.startX}`)\n        lineElement.setAttribute(\"y1\", `${element.startY}`)\n        lineElement.setAttribute(\"x2\", `${element.endX}`)\n        lineElement.setAttribute(\"y2\", `${element.endY}`)\n        break\n      }\n      case \"path\": {\n        const pathElement = group.getElementsByTagName(\"path\")[0]\n        pathElement.setAttribute(\n          \"d\",\n          pointsToPath(element.points, element.smoothing),\n        )\n        break\n      }\n      case \"rectangle\": {\n        const rectElement = group.getElementsByTagName(\"rect\")[0]\n        rectElement.setAttribute(\"width\", `${element.width}`)\n        rectElement.setAttribute(\"height\", `${element.height}`)\n        break\n      }\n      case \"ellipse\": {\n        const ellipseElement = group.getElementsByTagName(\"ellipse\")[0]\n        ellipseElement.setAttribute(\"cx\", `${element.width / 2}`)\n        ellipseElement.setAttribute(\"cy\", `${element.height / 2}`)\n        ellipseElement.setAttribute(\"rx\", `${element.width / 2}`)\n        ellipseElement.setAttribute(\"ry\", `${element.height / 2}`)\n        break\n      }\n      case \"media\": {\n        const imageElement = group.getElementsByTagName(\"image\")[0]\n        imageElement.setAttribute(\"width\", `${element.width}`)\n        imageElement.setAttribute(\"height\", `${element.height}`)\n        break\n      }\n    }\n  }\n\n  private renderSelectElements(): void {\n    if (!this.elementsGroup) {\n      return\n    }\n\n    const elements = this.getElements()\n    const selectedIds = this.getSelectedIds()\n\n    // This is the incremental path (used while the select tool is active), so it\n    // updates existing nodes in place rather than rebuilding. It must still drop\n    // DOM nodes for elements that no longer exist — e.g. when a selected element\n    // is deleted, the \"change\" handler routes here instead of reconcileElements().\n    // Snapshot into an array: `children` is a live collection and removing\n    // during iteration would skip nodes.\n    for (const child of this.elementsGroup.children) {\n      if (!elements.has(child.id)) {\n        child.remove()\n      }\n    }\n\n    for (const [, element] of elements) {\n      if (!element.visible) {\n        continue\n      }\n\n      const group = document.getElementById(element.id) as SVGGElement | null\n      if (!group) {\n        continue\n      }\n      const isSelected = selectedIds.has(element.id)\n      group.classList.toggle(selectedClass, isSelected)\n\n      if (!isSelected) {\n        continue\n      }\n\n      // The select tool already transforms geometry in canvas space, so just\n      // re-render each node from the current element state.\n      this.updateElementGeometry(group, element)\n    }\n  }\n\n  destroy(): void {\n    this.resizeObserver?.disconnect()\n    this.svgElement?.remove()\n  }\n}\n"],"mappings":"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"}