@embedpdf/utils 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/preact/adapter.ts","../../src/shared/plugin-interaction-primitives/utils.ts","../../src/shared/plugin-interaction-primitives/resize-geometry.ts","../../src/shared/plugin-interaction-primitives/drag-resize-controller.ts","../../src/shared/hooks/use-drag-resize.ts","../../src/shared/components/counter-rotate-container.tsx","../../src/shared/hooks/use-double-press-props.ts","../../src/shared/hooks/use-interaction-handles.ts"],"sourcesContent":["import { createContext, JSX, Fragment, FunctionComponent } from 'preact';\nexport { useEffect, useRef, useState, useCallback, useMemo, useContext } from 'preact/hooks';\nexport type { ComponentChildren as ReactNode, JSX } from 'preact';\n\nexport { createContext, Fragment };\n\nexport type CSSProperties = import('preact').JSX.CSSProperties;\nexport type HTMLAttributes<T = any> = import('preact').JSX.HTMLAttributes<\n T extends EventTarget ? T : never\n>;\n\nexport type MouseEvent<T = Element> = JSX.TargetedMouseEvent<T extends EventTarget ? T : never>;\nexport type PointerEvent<T = Element> = JSX.TargetedPointerEvent<T extends EventTarget ? T : never>;\nexport type ChangeEvent<T = Element> = JSX.TargetedInputEvent<T extends EventTarget ? T : never>;\nexport type TouchEvent<T = Element> = JSX.TargetedTouchEvent<T extends EventTarget ? T : never>;\nexport type ComponentType = FunctionComponent;\n\nexport const dblClickProp = 'onDblClick' as const;\n","import type { Position, Rect } from '@embedpdf/models';\nimport type { ResizeHandle, DragResizeConfig } from './drag-resize-controller';\n\nexport type QuarterTurns = 0 | 1 | 2 | 3;\n\nexport interface ResizeUI {\n handleSize?: number; // px (default 8)\n spacing?: number; // px distance from the box edge (default 1)\n offsetMode?: 'outside' | 'inside' | 'center'; // default 'outside'\n includeSides?: boolean; // default false\n zIndex?: number; // default 3\n rotationAwareCursor?: boolean; // default true\n}\n\nexport interface VertexUI {\n vertexSize?: number; // px (default 12)\n zIndex?: number; // default 4\n}\n\nexport interface RotationUI {\n /** Handle size in px (default 32) */\n handleSize?: number;\n /** Screen-pixel gap between bounding box edge and handle center (default 30) */\n margin?: number;\n /** z-index of the rotation handle (default 5) */\n zIndex?: number;\n /** Whether to show the connector line from center to handle (default true) */\n showConnector?: boolean;\n /** Connector line width in px (default 1) */\n connectorWidth?: number;\n}\n\n/** Screen-pixel gap between the rect edge and the rotation handle center (default orbit margin). */\nexport const ROTATION_HANDLE_MARGIN = 35;\n\nexport interface HandleDescriptor {\n handle: ResizeHandle;\n style: Record<string, number | string>;\n attrs?: Record<string, any>;\n}\n\nexport interface RotationHandleDescriptor {\n /** Style for the rotation handle itself */\n handleStyle: Record<string, number | string>;\n /** Style for the connector line (if shown) */\n connectorStyle: Record<string, number | string>;\n /** Orbit radius in screen pixels used to position the handle. */\n radius: number;\n /** Attributes for the handle element */\n attrs?: Record<string, any>;\n}\n\n/**\n * Base angle (degrees, clockwise from north) for each resize handle.\n * Used to compute the effective angle after page + annotation rotation.\n */\nconst HANDLE_BASE_ANGLE: Record<ResizeHandle, number> = {\n n: 0,\n ne: 45,\n e: 90,\n se: 135,\n s: 180,\n sw: 225,\n w: 270,\n nw: 315,\n};\n\n/**\n * Cursor names mapped to 45-degree sectors.\n * Sector 0 = north (337.5..22.5), sector 1 = NE (22.5..67.5), etc.\n */\nconst SECTOR_CURSORS: string[] = [\n 'ns-resize', // 0: north\n 'nesw-resize', // 1: NE\n 'ew-resize', // 2: east\n 'nwse-resize', // 3: SE\n 'ns-resize', // 4: south\n 'nesw-resize', // 5: SW\n 'ew-resize', // 6: west\n 'nwse-resize', // 7: NW\n];\n\nfunction diagonalCursor(\n handle: ResizeHandle,\n pageQuarterTurns: QuarterTurns,\n annotationRotation: number = 0,\n): string {\n const pageAngle = pageQuarterTurns * 90;\n const totalAngle = HANDLE_BASE_ANGLE[handle] + pageAngle + annotationRotation;\n // Normalize to [0, 360)\n const normalized = ((totalAngle % 360) + 360) % 360;\n // Map to 45-degree sector (0..7)\n const sector = Math.round(normalized / 45) % 8;\n return SECTOR_CURSORS[sector];\n}\n\nfunction edgeOffset(k: number, spacing: number, mode: 'outside' | 'inside' | 'center') {\n // Base puts the handle centered on the edge\n const base = -k / 2;\n if (mode === 'center') return base;\n // outside moves further out (more negative), inside moves in (less negative)\n return mode === 'outside' ? base - spacing : base + spacing;\n}\n\nexport function describeResizeFromConfig(\n cfg: DragResizeConfig,\n ui: ResizeUI = {},\n): HandleDescriptor[] {\n const {\n handleSize = 8,\n spacing = 1,\n offsetMode = 'outside',\n includeSides = false,\n zIndex = 3,\n rotationAwareCursor = true,\n } = ui;\n\n const pageQuarterTurns = ((cfg.pageRotation ?? 0) % 4) as QuarterTurns;\n const annotationRot = cfg.annotationRotation ?? 0;\n\n const off = (edge: 'top' | 'right' | 'bottom' | 'left') => ({\n [edge]: edgeOffset(handleSize, spacing, offsetMode) + 'px',\n });\n\n const corners: Array<[ResizeHandle, Record<string, number | string>]> = [\n ['nw', { ...off('top'), ...off('left') }],\n ['ne', { ...off('top'), ...off('right') }],\n ['sw', { ...off('bottom'), ...off('left') }],\n ['se', { ...off('bottom'), ...off('right') }],\n ];\n const sides: Array<[ResizeHandle, Record<string, number | string>]> = includeSides\n ? [\n ['n', { ...off('top'), left: `calc(50% - ${handleSize / 2}px)` }],\n ['s', { ...off('bottom'), left: `calc(50% - ${handleSize / 2}px)` }],\n ['w', { ...off('left'), top: `calc(50% - ${handleSize / 2}px)` }],\n ['e', { ...off('right'), top: `calc(50% - ${handleSize / 2}px)` }],\n ]\n : [];\n\n const all = [...corners, ...sides];\n\n return all.map(([handle, pos]) => ({\n handle,\n style: {\n position: 'absolute',\n width: handleSize + 'px',\n height: handleSize + 'px',\n borderRadius: '50%',\n zIndex,\n cursor: rotationAwareCursor\n ? diagonalCursor(handle, pageQuarterTurns, annotationRot)\n : 'default',\n pointerEvents: 'auto',\n touchAction: 'none',\n ...(pos as any),\n },\n attrs: { 'data-epdf-handle': handle },\n }));\n}\n\nexport function describeVerticesFromConfig(\n cfg: DragResizeConfig,\n ui: VertexUI = {},\n liveVertices?: Position[],\n): HandleDescriptor[] {\n const { vertexSize = 12, zIndex = 4 } = ui;\n const rect: Rect = cfg.element;\n const scale = cfg.scale ?? 1;\n const verts = liveVertices ?? cfg.vertices ?? [];\n\n return verts.map((v, i) => {\n const left = (v.x - rect.origin.x) * scale - vertexSize / 2;\n const top = (v.y - rect.origin.y) * scale - vertexSize / 2;\n return {\n handle: 'nw', // not used; kept for type\n style: {\n position: 'absolute',\n left: left + 'px',\n top: top + 'px',\n width: vertexSize + 'px',\n height: vertexSize + 'px',\n borderRadius: '50%',\n cursor: 'pointer',\n zIndex,\n pointerEvents: 'auto',\n touchAction: 'none',\n },\n attrs: { 'data-epdf-vertex': i },\n };\n });\n}\n\n/**\n * Describe the rotation handle position and style.\n * The rotation handle orbits around the center of the bounding box based on the current angle.\n *\n * @param cfg - The drag/resize config containing the element rect and scale\n * @param ui - UI customization options\n * @param currentAngle - The current rotation angle in degrees (0 = top, clockwise positive)\n */\nexport function describeRotationFromConfig(\n cfg: DragResizeConfig,\n ui: RotationUI = {},\n currentAngle: number = 0,\n): RotationHandleDescriptor {\n const { handleSize = 16, zIndex = 5, showConnector = true, connectorWidth = 1 } = ui;\n\n const scale = cfg.scale ?? 1;\n const rect = cfg.element;\n\n const orbitRect = cfg.rotationElement ?? rect;\n const orbitCenter = cfg.rotationCenter ?? {\n x: rect.origin.x + rect.size.width / 2,\n y: rect.origin.y + rect.size.height / 2,\n };\n\n // Center in scaled coordinates, relative to the orbit rect's origin.\n const scaledWidth = orbitRect.size.width * scale;\n const scaledHeight = orbitRect.size.height * scale;\n const centerX = (orbitCenter.x - orbitRect.origin.x) * scale;\n const centerY = (orbitCenter.y - orbitRect.origin.y) * scale;\n\n // Handle orbits at currentAngle (0° = top, clockwise positive)\n const angleRad = (currentAngle * Math.PI) / 180;\n\n // Calculate radius - distance from center to handle.\n // The handle always sits at the shape's local \"top\" because currentAngle\n // matches the annotation rotation and the shape rotates with it. So the\n // distance from center to the nearest edge in the handle's direction is\n // always halfHeight of the unrotated rect, regardless of the angle.\n const margin = ui.margin ?? ROTATION_HANDLE_MARGIN;\n const radius = (rect.size.height * scale) / 2 + margin;\n\n const handleCenterX = centerX + radius * Math.sin(angleRad);\n const handleCenterY = centerY - radius * Math.cos(angleRad);\n const handleLeft = handleCenterX - handleSize / 2;\n const handleTop = handleCenterY - handleSize / 2;\n\n return {\n handleStyle: {\n position: 'absolute',\n left: handleLeft + 'px',\n top: handleTop + 'px',\n width: handleSize + 'px',\n height: handleSize + 'px',\n borderRadius: '50%',\n cursor: 'grab',\n zIndex,\n pointerEvents: 'auto',\n touchAction: 'none',\n },\n connectorStyle: showConnector\n ? {\n position: 'absolute',\n left: centerX - connectorWidth / 2 + 'px',\n top: centerY - radius + 'px',\n width: connectorWidth + 'px',\n height: radius + 'px',\n transformOrigin: 'center bottom',\n transform: `rotate(${currentAngle}deg)`,\n zIndex: zIndex - 1,\n pointerEvents: 'none',\n }\n : {},\n radius,\n attrs: { 'data-epdf-rotation-handle': true },\n };\n}\n","/**\n * Pure geometric functions for the resize pipeline.\n *\n * Extracted from DragResizeController so that:\n * 1. The resize math is independently testable\n * 2. The controller stays focused on state machine + coordinate transformation\n */\nimport { Position, Rect, rotatePointAround, calculateRotatedRectAABB } from '@embedpdf/models';\n\nimport type { ResizeHandle } from './drag-resize-controller';\n\n// ---------------------------------------------------------------------------\n// Anchor helpers\n// ---------------------------------------------------------------------------\n\n/** Anchor describes which edges stay fixed when resizing. */\nexport type Anchor = {\n x: 'left' | 'right' | 'center';\n y: 'top' | 'bottom' | 'center';\n};\n\n/**\n * Derive anchor from handle.\n * - 'e' means we're dragging east → left edge is anchored\n * - 'nw' means we're dragging north-west → bottom-right corner is anchored\n */\nexport function getAnchor(handle: ResizeHandle): Anchor {\n return {\n x: handle.includes('e') ? 'left' : handle.includes('w') ? 'right' : 'center',\n y: handle.includes('s') ? 'top' : handle.includes('n') ? 'bottom' : 'center',\n };\n}\n\n/** Get the anchor point (the visually fixed point) in page space for a given rect and anchor. */\nexport function getAnchorPoint(rect: Rect, anchor: Anchor): Position {\n const x =\n anchor.x === 'left'\n ? rect.origin.x\n : anchor.x === 'right'\n ? rect.origin.x + rect.size.width\n : rect.origin.x + rect.size.width / 2;\n const y =\n anchor.y === 'top'\n ? rect.origin.y\n : anchor.y === 'bottom'\n ? rect.origin.y + rect.size.height\n : rect.origin.y + rect.size.height / 2;\n return { x, y };\n}\n\n// ---------------------------------------------------------------------------\n// Constraint types\n// ---------------------------------------------------------------------------\n\nexport interface ResizeConstraints {\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n boundingBox?: { width: number; height: number };\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline stages (pure functions)\n// ---------------------------------------------------------------------------\n\n/**\n * Apply the mouse delta to produce a raw (unconstrained) resized rect.\n */\nexport function applyResizeDelta(startRect: Rect, delta: Position, anchor: Anchor): Rect {\n let x = startRect.origin.x;\n let y = startRect.origin.y;\n let width = startRect.size.width;\n let height = startRect.size.height;\n\n if (anchor.x === 'left') {\n width += delta.x;\n } else if (anchor.x === 'right') {\n x += delta.x;\n width -= delta.x;\n }\n\n if (anchor.y === 'top') {\n height += delta.y;\n } else if (anchor.y === 'bottom') {\n y += delta.y;\n height -= delta.y;\n }\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Enforce aspect ratio while respecting the anchor.\n * For edge handles (center anchor on one axis), the rect expands symmetrically on that axis.\n * For corner handles, the anchor corner stays fixed.\n */\nexport function enforceAspectRatio(\n rect: Rect,\n startRect: Rect,\n anchor: Anchor,\n aspectRatio: number,\n): Rect {\n let { x, y } = rect.origin;\n let { width, height } = rect.size;\n\n const isEdgeHandle = anchor.x === 'center' || anchor.y === 'center';\n\n if (isEdgeHandle) {\n if (anchor.y === 'center') {\n height = width / aspectRatio;\n y = startRect.origin.y + (startRect.size.height - height) / 2;\n } else {\n width = height * aspectRatio;\n x = startRect.origin.x + (startRect.size.width - width) / 2;\n }\n } else {\n const dw = Math.abs(width - startRect.size.width);\n const dh = Math.abs(height - startRect.size.height);\n const total = dw + dh;\n\n if (total === 0) {\n width = startRect.size.width;\n height = startRect.size.height;\n } else {\n // Smooth weighted blend between the width-driven and height-driven\n // results. Both candidate pairs lie on the line w/h = aspectRatio\n // through the origin, so any linear combination preserves the ratio\n // exactly — while eliminating the discontinuous jump that the old\n // hard `dw >= dh` switch caused at the boundary.\n const wWeight = dw / total;\n const hWeight = dh / total;\n const wFromW = width;\n const hFromW = width / aspectRatio;\n const wFromH = height * aspectRatio;\n const hFromH = height;\n width = wWeight * wFromW + hWeight * wFromH;\n height = wWeight * hFromW + hWeight * hFromH;\n }\n }\n\n if (anchor.x === 'right') {\n x = startRect.origin.x + startRect.size.width - width;\n }\n if (anchor.y === 'bottom') {\n y = startRect.origin.y + startRect.size.height - height;\n }\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Clamp rect to bounding box while respecting anchor.\n */\nexport function clampToBounds(\n rect: Rect,\n startRect: Rect,\n anchor: Anchor,\n bbox: { width: number; height: number } | undefined,\n maintainAspectRatio: boolean,\n): Rect {\n if (!bbox) return rect;\n\n let { x, y } = rect.origin;\n let { width, height } = rect.size;\n\n width = Math.max(1, width);\n height = Math.max(1, height);\n\n const anchorX =\n anchor.x === 'left' ? startRect.origin.x : startRect.origin.x + startRect.size.width;\n const anchorY =\n anchor.y === 'top' ? startRect.origin.y : startRect.origin.y + startRect.size.height;\n\n const maxW =\n anchor.x === 'left'\n ? bbox.width - anchorX\n : anchor.x === 'right'\n ? anchorX\n : Math.min(startRect.origin.x, bbox.width - startRect.origin.x - startRect.size.width) * 2 +\n startRect.size.width;\n\n const maxH =\n anchor.y === 'top'\n ? bbox.height - anchorY\n : anchor.y === 'bottom'\n ? anchorY\n : Math.min(startRect.origin.y, bbox.height - startRect.origin.y - startRect.size.height) *\n 2 +\n startRect.size.height;\n\n if (maintainAspectRatio) {\n const scaleW = width > maxW ? maxW / width : 1;\n const scaleH = height > maxH ? maxH / height : 1;\n const scale = Math.min(scaleW, scaleH);\n\n if (scale < 1) {\n width *= scale;\n height *= scale;\n }\n } else {\n width = Math.min(width, maxW);\n height = Math.min(height, maxH);\n }\n\n if (anchor.x === 'left') {\n x = anchorX;\n } else if (anchor.x === 'right') {\n x = anchorX - width;\n } else {\n x = startRect.origin.x + (startRect.size.width - width) / 2;\n }\n\n if (anchor.y === 'top') {\n y = anchorY;\n } else if (anchor.y === 'bottom') {\n y = anchorY - height;\n } else {\n y = startRect.origin.y + (startRect.size.height - height) / 2;\n }\n\n x = Math.max(0, Math.min(x, bbox.width - width));\n y = Math.max(0, Math.min(y, bbox.height - height));\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Reposition rect from current size so the start-gesture anchor remains fixed.\n * This prevents translation drift when constraints clamp width/height.\n */\nexport function reanchorRect(rect: Rect, startRect: Rect, anchor: Anchor): Rect {\n let x: number;\n let y: number;\n\n if (anchor.x === 'left') {\n x = startRect.origin.x;\n } else if (anchor.x === 'right') {\n x = startRect.origin.x + startRect.size.width - rect.size.width;\n } else {\n x = startRect.origin.x + (startRect.size.width - rect.size.width) / 2;\n }\n\n if (anchor.y === 'top') {\n y = startRect.origin.y;\n } else if (anchor.y === 'bottom') {\n y = startRect.origin.y + startRect.size.height - rect.size.height;\n } else {\n y = startRect.origin.y + (startRect.size.height - rect.size.height) / 2;\n }\n\n return { origin: { x, y }, size: rect.size };\n}\n\n/**\n * Apply min/max constraints. Also used by the drag pipeline for position clamping.\n */\nexport function applyConstraints(\n position: Rect,\n constraints: ResizeConstraints | undefined,\n maintainAspectRatio: boolean,\n skipBoundingClamp: boolean = false,\n): Rect {\n if (!constraints) return position;\n\n let {\n origin: { x, y },\n size: { width, height },\n } = position;\n\n const minW = constraints.minWidth ?? 1;\n const minH = constraints.minHeight ?? 1;\n const maxW = constraints.maxWidth;\n const maxH = constraints.maxHeight;\n\n if (maintainAspectRatio && width > 0 && height > 0) {\n const ratio = width / height;\n\n if (width < minW) {\n width = minW;\n height = width / ratio;\n }\n if (height < minH) {\n height = minH;\n width = height * ratio;\n }\n\n if (maxW !== undefined && width > maxW) {\n width = maxW;\n height = width / ratio;\n }\n if (maxH !== undefined && height > maxH) {\n height = maxH;\n width = height * ratio;\n }\n } else {\n width = Math.max(minW, width);\n height = Math.max(minH, height);\n if (maxW !== undefined) width = Math.min(maxW, width);\n if (maxH !== undefined) height = Math.min(maxH, height);\n }\n\n if (constraints.boundingBox && !skipBoundingClamp) {\n x = Math.max(0, Math.min(x, constraints.boundingBox.width - width));\n y = Math.max(0, Math.min(y, constraints.boundingBox.height - height));\n }\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Check if a rect, when rotated, fits within the given page bounds.\n */\nexport function isRectWithinRotatedBounds(\n rect: Rect,\n angleDegrees: number,\n bbox: { width: number; height: number },\n): boolean {\n const eps = 1e-6;\n const aabb = calculateRotatedRectAABB(rect, angleDegrees);\n return (\n aabb.origin.x >= -eps &&\n aabb.origin.y >= -eps &&\n aabb.origin.x + aabb.size.width <= bbox.width + eps &&\n aabb.origin.y + aabb.size.height <= bbox.height + eps\n );\n}\n\n// ---------------------------------------------------------------------------\n// Full resize pipeline\n// ---------------------------------------------------------------------------\n\nexport interface ResizeConfig {\n startRect: Rect;\n maintainAspectRatio?: boolean;\n annotationRotation?: number;\n constraints?: ResizeConstraints;\n}\n\n/**\n * Run the full resize pipeline for a single step:\n * delta → anchor → raw resize → aspect ratio → bounds clamp → constraints → anchor compensation\n */\nfunction computeResizeStep(\n delta: Position,\n handle: ResizeHandle,\n config: ResizeConfig,\n clampLocalBounds: boolean,\n skipConstraintBoundingClamp: boolean,\n): Rect {\n const { startRect, maintainAspectRatio = false, annotationRotation = 0, constraints } = config;\n const anchor = getAnchor(handle);\n const aspectRatio = startRect.size.width / startRect.size.height || 1;\n\n // Step 1: Apply delta to get raw resize\n let rect = applyResizeDelta(startRect, delta, anchor);\n\n // Step 2: Enforce aspect ratio if enabled\n if (maintainAspectRatio) {\n rect = enforceAspectRatio(rect, startRect, anchor, aspectRatio);\n }\n\n // Step 3: Clamp in local/unrotated frame when requested\n if (clampLocalBounds) {\n rect = clampToBounds(rect, startRect, anchor, constraints?.boundingBox, maintainAspectRatio);\n }\n\n // Step 4: Apply min/max constraints\n rect = applyConstraints(rect, constraints, maintainAspectRatio, skipConstraintBoundingClamp);\n\n // In rotated resize mode we skip axis-aligned bounding clamps and solve\n // against visual bounds separately. Re-anchor after size constraints so\n // dragging past min/max does not keep translating the rect.\n if (skipConstraintBoundingClamp) {\n rect = reanchorRect(rect, startRect, anchor);\n }\n\n // Step 5: Compensate for visual anchor drift when the annotation is rotated.\n if (annotationRotation !== 0) {\n const anchorPt = getAnchorPoint(startRect, anchor);\n const oldCenter: Position = {\n x: startRect.origin.x + startRect.size.width / 2,\n y: startRect.origin.y + startRect.size.height / 2,\n };\n const newCenter: Position = {\n x: rect.origin.x + rect.size.width / 2,\n y: rect.origin.y + rect.size.height / 2,\n };\n const oldVisual = rotatePointAround(anchorPt, oldCenter, annotationRotation);\n const newVisual = rotatePointAround(anchorPt, newCenter, annotationRotation);\n rect = {\n origin: {\n x: rect.origin.x + (oldVisual.x - newVisual.x),\n y: rect.origin.y + (oldVisual.y - newVisual.y),\n },\n size: rect.size,\n };\n }\n\n return rect;\n}\n\n/**\n * Calculate the new rect after a resize operation.\n *\n * For non-rotated annotations, runs the pipeline once with local bound clamping.\n * For rotated annotations, uses a binary search to find the largest delta that\n * keeps the visual AABB within page bounds.\n */\nexport function computeResizedRect(\n delta: Position,\n handle: ResizeHandle,\n config: ResizeConfig,\n): Rect {\n const { annotationRotation = 0, constraints } = config;\n const bbox = constraints?.boundingBox;\n\n // For rotated annotations, clamp using visual AABB bounds via binary search.\n if (annotationRotation !== 0 && bbox) {\n const target = computeResizeStep(delta, handle, config, false, true);\n if (isRectWithinRotatedBounds(target, annotationRotation, bbox)) {\n return target;\n }\n\n let best = computeResizeStep({ x: 0, y: 0 }, handle, config, false, true);\n let low = 0;\n let high = 1;\n for (let i = 0; i < 20; i += 1) {\n const mid = (low + high) / 2;\n const trial = computeResizeStep(\n { x: delta.x * mid, y: delta.y * mid },\n handle,\n config,\n false,\n true,\n );\n if (isRectWithinRotatedBounds(trial, annotationRotation, bbox)) {\n best = trial;\n low = mid;\n } else {\n high = mid;\n }\n }\n\n return best;\n }\n\n return computeResizeStep(delta, handle, config, true, false);\n}\n","import { Position, Rect, rotatePointAround, normalizeAngle } from '@embedpdf/models';\nimport { ROTATION_HANDLE_MARGIN } from './utils';\nimport { computeResizedRect, applyConstraints } from './resize-geometry';\n\nexport interface DragResizeConfig {\n element: Rect;\n /**\n * Optional world-space pivot to use for rotation interactions.\n * Defaults to the center of `element`.\n */\n rotationCenter?: Position;\n /**\n * Optional rect used for rotation-handle orbit layout (typically the visible AABB container).\n * Defaults to `element`.\n */\n rotationElement?: Rect;\n vertices?: Position[];\n constraints?: {\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n boundingBox?: { width: number; height: number }; // page bounds\n };\n maintainAspectRatio?: boolean;\n pageRotation?: number;\n /** Rotation of the annotation itself in degrees (used to project mouse deltas into local space for resize/vertex-edit) */\n annotationRotation?: number;\n scale?: number;\n rotationSnapAngles?: number[];\n rotationSnapThreshold?: number;\n}\n\nexport type InteractionState = 'idle' | 'dragging' | 'resizing' | 'vertex-editing' | 'rotating';\nexport type ResizeHandle = 'nw' | 'ne' | 'sw' | 'se' | 'n' | 'e' | 's' | 'w';\n\nexport interface TransformData {\n type: 'move' | 'resize' | 'vertex-edit' | 'rotate';\n changes: {\n rect?: Rect;\n vertices?: Position[];\n rotation?: number;\n };\n metadata?: {\n handle?: ResizeHandle;\n vertexIndex?: number;\n maintainAspectRatio?: boolean;\n /** The rotation angle in degrees */\n rotationAngle?: number;\n /** The center point used for rotation */\n rotationCenter?: Position;\n rotationDelta?: number;\n isSnapped?: boolean;\n snappedAngle?: number;\n /** Screen-space cursor position during the gesture */\n cursorPosition?: { clientX: number; clientY: number };\n };\n}\n\nexport interface InteractionEvent {\n state: 'start' | 'move' | 'end';\n transformData?: TransformData;\n}\n\n/**\n * Pure geometric controller that manages drag/resize/vertex-edit/rotate logic.\n */\nexport class DragResizeController {\n private state: InteractionState = 'idle';\n private startPoint: Position | null = null;\n private startElement: Rect | null = null;\n private startRotationElement: Rect | null = null;\n private gestureRotationCenter: Position | null = null;\n private activeHandle: ResizeHandle | null = null;\n private currentPosition: Rect | null = null;\n\n // Vertex editing state - pure geometric\n private activeVertexIndex: number | null = null;\n private startVertices: Position[] = [];\n private currentVertices: Position[] = [];\n\n // Rotation state\n private rotationCenter: Position | null = null;\n private centerScreen: Position | null = null; // Cached center in screen coords\n private initialRotation: number = 0; // The rotation value when interaction started\n private lastComputedRotation: number = 0; // The last computed rotation during move\n private rotationDelta: number = 0;\n private rotationSnappedAngle: number | null = null;\n\n constructor(\n private config: DragResizeConfig,\n private onUpdate: (event: InteractionEvent) => void,\n ) {\n this.currentVertices = config.vertices || [];\n }\n\n updateConfig(config: Partial<DragResizeConfig>) {\n this.config = { ...this.config, ...config };\n // Keep the gesture buffer stable during active vertex editing.\n // Otherwise rerendered preview vertices can overwrite `currentVertices`\n // and cause end() to emit compensated vertices a second time.\n if (this.state !== 'vertex-editing') {\n this.currentVertices = config.vertices || [];\n }\n }\n\n // ---------------------------------------------------------------------------\n // Gesture start\n // ---------------------------------------------------------------------------\n\n startDrag(clientX: number, clientY: number) {\n this.state = 'dragging';\n this.startPoint = { x: clientX, y: clientY };\n this.startElement = { ...this.config.element };\n this.startRotationElement = this.config.rotationElement\n ? { ...this.config.rotationElement }\n : null;\n this.currentPosition = { ...this.config.element };\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'move',\n changes: { rect: this.startElement },\n },\n });\n }\n\n startResize(handle: ResizeHandle, clientX: number, clientY: number) {\n this.state = 'resizing';\n this.activeHandle = handle;\n this.startPoint = { x: clientX, y: clientY };\n this.startElement = { ...this.config.element };\n this.currentPosition = { ...this.config.element };\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'resize',\n changes: { rect: this.startElement },\n metadata: {\n handle: this.activeHandle,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n }\n\n startVertexEdit(vertexIndex: number, clientX: number, clientY: number) {\n // Refresh vertices from latest config before validating index\n this.currentVertices = [...(this.config.vertices ?? this.currentVertices)];\n if (vertexIndex < 0 || vertexIndex >= this.currentVertices.length) return;\n\n this.state = 'vertex-editing';\n this.activeVertexIndex = vertexIndex;\n this.startPoint = { x: clientX, y: clientY };\n this.startVertices = [...this.currentVertices];\n this.gestureRotationCenter = this.config.rotationCenter ?? {\n x: this.config.element.origin.x + this.config.element.size.width / 2,\n y: this.config.element.origin.y + this.config.element.size.height / 2,\n };\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices: this.startVertices },\n metadata: { vertexIndex },\n },\n });\n }\n\n startRotation(\n clientX: number,\n clientY: number,\n initialRotation: number = 0,\n orbitRadiusPx?: number,\n ) {\n this.state = 'rotating';\n this.startPoint = { x: clientX, y: clientY };\n this.startElement = { ...this.config.element };\n\n // Use explicit rotation center when provided (keeps pivot stable after vertex edits).\n this.rotationCenter = this.config.rotationCenter ?? {\n x: this.config.element.origin.x + this.config.element.size.width / 2,\n y: this.config.element.origin.y + this.config.element.size.height / 2,\n };\n\n // Cache the center in screen coordinates, derived from the handle's DOM center\n const { scale = 1 } = this.config;\n const orbitRect = this.config.rotationElement ?? this.config.element;\n const sw = orbitRect.size.width * scale;\n const sh = orbitRect.size.height * scale;\n const radius = orbitRadiusPx ?? Math.max(sw, sh) / 2 + ROTATION_HANDLE_MARGIN;\n const pageRotOffset = (this.config.pageRotation ?? 0) * 90;\n const screenAngleRad = ((initialRotation + pageRotOffset) * Math.PI) / 180;\n this.centerScreen = {\n x: clientX - radius * Math.sin(screenAngleRad),\n y: clientY + radius * Math.cos(screenAngleRad),\n };\n\n this.initialRotation = initialRotation;\n this.lastComputedRotation = initialRotation;\n this.rotationDelta = 0;\n this.rotationSnappedAngle = null;\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'rotate',\n changes: { rotation: initialRotation },\n metadata: {\n rotationAngle: initialRotation,\n rotationDelta: 0,\n rotationCenter: this.rotationCenter,\n isSnapped: false,\n },\n },\n });\n }\n\n // ---------------------------------------------------------------------------\n // Gesture move\n // ---------------------------------------------------------------------------\n\n move(clientX: number, clientY: number, buttons?: number) {\n if (this.state === 'idle' || !this.startPoint) return;\n\n // Safety net: if the button is no longer pressed but we never received\n // pointerup/pointercancel, finalize the gesture to avoid a \"stuck\" drag.\n if (buttons !== undefined && buttons === 0) {\n this.end();\n return;\n }\n\n if (this.state === 'dragging' && this.startElement) {\n const delta = this.calculateDelta(clientX, clientY);\n const position = this.calculateDragPosition(delta);\n this.currentPosition = position;\n\n this.onUpdate({\n state: 'move',\n transformData: { type: 'move', changes: { rect: position } },\n });\n } else if (this.state === 'resizing' && this.activeHandle && this.startElement) {\n const delta = this.calculateLocalDelta(clientX, clientY);\n const position = computeResizedRect(delta, this.activeHandle, {\n startRect: this.startElement,\n maintainAspectRatio: this.config.maintainAspectRatio,\n annotationRotation: this.config.annotationRotation,\n constraints: this.config.constraints,\n });\n this.currentPosition = position;\n\n this.onUpdate({\n state: 'move',\n transformData: {\n type: 'resize',\n changes: { rect: position },\n metadata: {\n handle: this.activeHandle,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n } else if (this.state === 'vertex-editing' && this.activeVertexIndex !== null) {\n const vertices = this.calculateVertexPosition(clientX, clientY);\n this.currentVertices = vertices;\n\n this.onUpdate({\n state: 'move',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices },\n metadata: { vertexIndex: this.activeVertexIndex },\n },\n });\n } else if (this.state === 'rotating' && this.rotationCenter) {\n const absoluteAngle = this.calculateAngleFromMouse(clientX, clientY);\n const snapResult = this.applyRotationSnapping(absoluteAngle);\n const snappedAngle = normalizeAngle(snapResult.angle);\n const previousAngle = this.lastComputedRotation;\n const rawDelta = snappedAngle - previousAngle;\n const adjustedDelta =\n rawDelta > 180 ? rawDelta - 360 : rawDelta < -180 ? rawDelta + 360 : rawDelta;\n\n this.rotationDelta += adjustedDelta;\n this.lastComputedRotation = snappedAngle;\n this.rotationSnappedAngle = snapResult.isSnapped ? snappedAngle : null;\n\n this.onUpdate({\n state: 'move',\n transformData: {\n type: 'rotate',\n changes: { rotation: snappedAngle },\n metadata: {\n rotationAngle: snappedAngle,\n rotationDelta: this.rotationDelta,\n rotationCenter: this.rotationCenter,\n isSnapped: snapResult.isSnapped,\n snappedAngle: this.rotationSnappedAngle ?? undefined,\n cursorPosition: { clientX, clientY },\n },\n },\n });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Gesture end / cancel\n // ---------------------------------------------------------------------------\n\n end() {\n if (this.state === 'idle') return;\n\n const wasState = this.state;\n const handle = this.activeHandle;\n const vertexIndex = this.activeVertexIndex;\n\n if (wasState === 'vertex-editing') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices: this.currentVertices },\n metadata: { vertexIndex: vertexIndex || undefined },\n },\n });\n } else if (wasState === 'rotating') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'rotate',\n changes: { rotation: this.lastComputedRotation },\n metadata: {\n rotationAngle: this.lastComputedRotation,\n rotationDelta: this.rotationDelta,\n rotationCenter: this.rotationCenter || undefined,\n isSnapped: this.rotationSnappedAngle !== null,\n snappedAngle: this.rotationSnappedAngle ?? undefined,\n },\n },\n });\n } else {\n const finalPosition = this.currentPosition || this.config.element;\n this.onUpdate({\n state: 'end',\n transformData: {\n type: wasState === 'dragging' ? 'move' : 'resize',\n changes: { rect: finalPosition },\n metadata:\n wasState === 'dragging'\n ? undefined\n : {\n handle: handle || undefined,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n }\n\n this.reset();\n }\n\n cancel() {\n if (this.state === 'idle') return;\n\n if (this.state === 'vertex-editing') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices: this.startVertices },\n metadata: { vertexIndex: this.activeVertexIndex || undefined },\n },\n });\n } else if (this.state === 'rotating') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'rotate',\n changes: { rotation: this.initialRotation },\n metadata: {\n rotationAngle: this.initialRotation,\n rotationDelta: 0,\n rotationCenter: this.rotationCenter || undefined,\n isSnapped: false,\n },\n },\n });\n } else if (this.startElement) {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: this.state === 'dragging' ? 'move' : 'resize',\n changes: { rect: this.startElement },\n metadata:\n this.state === 'dragging'\n ? undefined\n : {\n handle: this.activeHandle || undefined,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n }\n\n this.reset();\n }\n\n // ---------------------------------------------------------------------------\n // Private: state management\n // ---------------------------------------------------------------------------\n\n private reset() {\n this.state = 'idle';\n this.startPoint = null;\n this.startElement = null;\n this.startRotationElement = null;\n this.gestureRotationCenter = null;\n this.activeHandle = null;\n this.currentPosition = null;\n this.activeVertexIndex = null;\n this.startVertices = [];\n // Reset rotation state\n this.rotationCenter = null;\n this.centerScreen = null;\n this.initialRotation = 0;\n this.lastComputedRotation = 0;\n this.rotationDelta = 0;\n this.rotationSnappedAngle = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private: coordinate transformation (screen → page → local)\n // ---------------------------------------------------------------------------\n\n private calculateDelta(clientX: number, clientY: number): Position {\n if (!this.startPoint) return { x: 0, y: 0 };\n\n const rawDelta: Position = {\n x: clientX - this.startPoint.x,\n y: clientY - this.startPoint.y,\n };\n\n return this.transformDelta(rawDelta);\n }\n\n private transformDelta(delta: Position): Position {\n const { pageRotation = 0, scale = 1 } = this.config;\n\n const rad = (pageRotation * Math.PI) / 2;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n\n const scaledX = delta.x / scale;\n const scaledY = delta.y / scale;\n\n return {\n x: cos * scaledX + sin * scaledY,\n y: -sin * scaledX + cos * scaledY,\n };\n }\n\n /**\n * Calculate delta projected into the annotation's local (unrotated) coordinate space.\n * Used for resize and vertex-edit where mouse movement must be mapped to the\n * annotation's own axes, accounting for its rotation.\n */\n private calculateLocalDelta(clientX: number, clientY: number): Position {\n const pageDelta = this.calculateDelta(clientX, clientY);\n const { annotationRotation = 0 } = this.config;\n if (annotationRotation === 0) return pageDelta;\n\n const rad = (annotationRotation * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n return {\n x: cos * pageDelta.x + sin * pageDelta.y,\n y: -sin * pageDelta.x + cos * pageDelta.y,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Private: vertex clamping\n // ---------------------------------------------------------------------------\n\n private clampPoint(p: Position): Position {\n const bbox = this.config.constraints?.boundingBox;\n if (!bbox) return p;\n\n const { annotationRotation = 0 } = this.config;\n if (annotationRotation === 0) {\n return {\n x: Math.max(0, Math.min(p.x, bbox.width)),\n y: Math.max(0, Math.min(p.y, bbox.height)),\n };\n }\n\n // When rotated, vertices live in unrotated space. Transform to visual\n // (page) space, clamp to page bounds, then inverse-rotate back.\n const center = this.gestureRotationCenter ??\n this.config.rotationCenter ?? {\n x: this.config.element.origin.x + this.config.element.size.width / 2,\n y: this.config.element.origin.y + this.config.element.size.height / 2,\n };\n const visual = rotatePointAround(p, center, annotationRotation);\n\n const clampedX = Math.max(0, Math.min(visual.x, bbox.width));\n const clampedY = Math.max(0, Math.min(visual.y, bbox.height));\n\n if (clampedX === visual.x && clampedY === visual.y) return p;\n\n return rotatePointAround({ x: clampedX, y: clampedY }, center, -annotationRotation);\n }\n\n private calculateVertexPosition(clientX: number, clientY: number): Position[] {\n if (this.activeVertexIndex === null) return this.startVertices;\n\n const delta = this.calculateLocalDelta(clientX, clientY);\n const newVertices = [...this.startVertices];\n const currentVertex = newVertices[this.activeVertexIndex];\n\n const moved = {\n x: currentVertex.x + delta.x,\n y: currentVertex.y + delta.y,\n };\n newVertices[this.activeVertexIndex] = this.clampPoint(moved);\n\n return newVertices;\n }\n\n // ---------------------------------------------------------------------------\n // Private: drag position\n // ---------------------------------------------------------------------------\n\n private calculateDragPosition(delta: Position): Rect {\n if (!this.startElement) return this.config.element;\n\n const position: Rect = {\n origin: {\n x: this.startElement.origin.x + delta.x,\n y: this.startElement.origin.y + delta.y,\n },\n size: {\n width: this.startElement.size.width,\n height: this.startElement.size.height,\n },\n };\n\n // When the annotation is rotated, the visible footprint is the AABB, not\n // the unrotatedRect. Clamp based on AABB dimensions so the annotation can\n // move freely within the page bounds.\n const { annotationRotation = 0, constraints } = this.config;\n const bbox = constraints?.boundingBox;\n if (annotationRotation !== 0 && bbox) {\n let aabbW: number;\n let aabbH: number;\n let offsetX: number;\n let offsetY: number;\n\n if (this.startRotationElement) {\n aabbW = this.startRotationElement.size.width;\n aabbH = this.startRotationElement.size.height;\n offsetX = this.startRotationElement.origin.x - this.startElement.origin.x;\n offsetY = this.startRotationElement.origin.y - this.startElement.origin.y;\n } else {\n const rad = Math.abs((annotationRotation * Math.PI) / 180);\n const cos = Math.abs(Math.cos(rad));\n const sin = Math.abs(Math.sin(rad));\n const w = position.size.width;\n const h = position.size.height;\n aabbW = w * cos + h * sin;\n aabbH = w * sin + h * cos;\n offsetX = (w - aabbW) / 2;\n offsetY = (h - aabbH) / 2;\n }\n\n let { x, y } = position.origin;\n x = Math.max(-offsetX, Math.min(x, bbox.width - aabbW - offsetX));\n y = Math.max(-offsetY, Math.min(y, bbox.height - aabbH - offsetY));\n\n return { origin: { x, y }, size: position.size };\n }\n\n return applyConstraints(position, constraints, this.config.maintainAspectRatio ?? false);\n }\n\n // ---------------------------------------------------------------------------\n // Private: rotation\n // ---------------------------------------------------------------------------\n\n /**\n * Calculate the angle from the center to a point in screen coordinates.\n */\n private calculateAngleFromMouse(clientX: number, clientY: number): number {\n if (!this.centerScreen) return this.initialRotation;\n\n const dx = clientX - this.centerScreen.x;\n const dy = clientY - this.centerScreen.y;\n\n // Dead zone: when the mouse is very close to the center, atan2 becomes\n // extremely sensitive to sub-pixel movements. Hold the last stable angle.\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < 10) return this.lastComputedRotation;\n\n // atan2 gives angle from +X axis; rotate +90° so 0° = top (north).\n // Subtract the page rotation offset to convert the screen-space angle\n // back to the page-space angle used by the annotation model.\n const pageRotOffset = (this.config.pageRotation ?? 0) * 90;\n const angleDeg = Math.atan2(dy, dx) * (180 / Math.PI) + 90 - pageRotOffset;\n\n return normalizeAngle(Math.round(angleDeg));\n }\n\n private applyRotationSnapping(angle: number): {\n angle: number;\n isSnapped: boolean;\n snapTarget?: number;\n } {\n const snapAngles = this.config.rotationSnapAngles ?? [0, 90, 180, 270];\n const threshold = this.config.rotationSnapThreshold ?? 4;\n const normalizedAngle = normalizeAngle(angle);\n\n for (const candidate of snapAngles) {\n const normalizedCandidate = normalizeAngle(candidate);\n const diff = Math.abs(normalizedAngle - normalizedCandidate);\n const minimalDiff = Math.min(diff, 360 - diff);\n if (minimalDiff <= threshold) {\n return {\n angle: normalizedCandidate,\n isSnapped: true,\n snapTarget: normalizedCandidate,\n };\n }\n }\n\n return { angle: normalizedAngle, isSnapped: false };\n }\n}\n","import { useRef, useCallback, useEffect, PointerEvent } from '@framework';\nimport {\n DragResizeController,\n DragResizeConfig,\n InteractionEvent,\n ResizeHandle,\n} from '../plugin-interaction-primitives';\n\nexport interface UseDragResizeOptions extends DragResizeConfig {\n onUpdate?: (event: InteractionEvent) => void;\n enabled?: boolean;\n}\n\nexport interface ResizeHandleEventProps {\n onPointerDown: (e: PointerEvent) => void;\n onPointerMove: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onPointerCancel: (e: PointerEvent) => void;\n onLostPointerCapture?: (e: PointerEvent) => void;\n}\n\nexport function useDragResize(options: UseDragResizeOptions) {\n const { onUpdate, enabled = true, ...config } = options;\n const controllerRef = useRef<DragResizeController | null>(null);\n const onUpdateRef = useRef<typeof onUpdate>(onUpdate);\n const activePointerIdRef = useRef<number | null>(null);\n const activeTargetRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n onUpdateRef.current = onUpdate;\n }, [onUpdate]);\n\n // Initialize or update controller\n useEffect(() => {\n if (!controllerRef.current) {\n controllerRef.current = new DragResizeController(config, (event) =>\n onUpdateRef.current?.(event),\n );\n } else {\n controllerRef.current.updateConfig(config);\n }\n }, [\n config.element,\n config.rotationCenter,\n config.rotationElement,\n config.constraints,\n config.maintainAspectRatio,\n config.pageRotation,\n config.annotationRotation,\n config.scale,\n config.vertices,\n ]);\n\n const endPointerSession = useCallback((pointerId?: number) => {\n const activePointerId = activePointerIdRef.current;\n const target = activeTargetRef.current;\n const id = pointerId ?? activePointerId;\n\n if (target && id !== null) {\n try {\n if (target.hasPointerCapture?.(id)) {\n target.releasePointerCapture?.(id);\n }\n } catch {\n // Ignore release failures when capture is already gone.\n }\n }\n\n activePointerIdRef.current = null;\n activeTargetRef.current = null;\n }, []);\n\n const startPointerSession = useCallback(\n (e: PointerEvent) => {\n // Defensive: if a previous interaction got stuck, close it before starting a new one.\n if (activePointerIdRef.current !== null && activePointerIdRef.current !== e.pointerId) {\n controllerRef.current?.end();\n endPointerSession(activePointerIdRef.current);\n }\n\n const target = e.currentTarget as HTMLElement;\n activePointerIdRef.current = e.pointerId;\n activeTargetRef.current = target;\n try {\n target.setPointerCapture(e.pointerId);\n } catch {\n // Ignore capture failures - global listeners still provide a fallback.\n }\n },\n [endPointerSession],\n );\n\n useEffect(() => {\n const eventTarget = globalThis as unknown as Window;\n const handleGlobalPointerEnd = (e: globalThis.PointerEvent) => {\n const activePointerId = activePointerIdRef.current;\n if (activePointerId === null || e.pointerId !== activePointerId) return;\n controllerRef.current?.end();\n endPointerSession(e.pointerId);\n };\n\n const handleWindowBlur = () => {\n if (activePointerIdRef.current === null) return;\n controllerRef.current?.end();\n endPointerSession();\n };\n\n eventTarget.addEventListener('pointerup', handleGlobalPointerEnd, true);\n eventTarget.addEventListener('pointercancel', handleGlobalPointerEnd, true);\n eventTarget.addEventListener('blur', handleWindowBlur, true);\n\n return () => {\n eventTarget.removeEventListener('pointerup', handleGlobalPointerEnd, true);\n eventTarget.removeEventListener('pointercancel', handleGlobalPointerEnd, true);\n eventTarget.removeEventListener('blur', handleWindowBlur, true);\n };\n }, [endPointerSession]);\n\n useEffect(() => {\n return () => {\n if (activePointerIdRef.current !== null) {\n controllerRef.current?.end();\n endPointerSession();\n }\n };\n }, [endPointerSession]);\n\n const handleDragStart = useCallback(\n (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n controllerRef.current?.startDrag(e.clientX, e.clientY);\n startPointerSession(e);\n },\n [enabled, startPointerSession],\n );\n\n const handleMove = useCallback(\n (e: PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const activePointerId = activePointerIdRef.current;\n if (activePointerId !== null && e.pointerId !== activePointerId) return;\n controllerRef.current?.move(e.clientX, e.clientY, e.buttons);\n\n // Extra guard for environments where pointerup gets swallowed.\n if (activePointerIdRef.current === e.pointerId && e.buttons === 0) {\n endPointerSession(e.pointerId);\n }\n },\n [endPointerSession],\n );\n\n const handleEndLike = useCallback(\n (e: PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const activePointerId = activePointerIdRef.current;\n if (activePointerId !== null && e.pointerId !== activePointerId) return;\n controllerRef.current?.end();\n endPointerSession(e.pointerId);\n },\n [endPointerSession],\n );\n\n const handleLostPointerCapture = useCallback(\n (e: PointerEvent) => {\n const activePointerId = activePointerIdRef.current;\n if (activePointerId === null || e.pointerId !== activePointerId) return;\n controllerRef.current?.end();\n endPointerSession(e.pointerId);\n },\n [endPointerSession],\n );\n\n const createResizeHandler = useCallback(\n (handle: ResizeHandle): ResizeHandleEventProps => ({\n onPointerDown: (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n controllerRef.current?.startResize(handle, e.clientX, e.clientY);\n startPointerSession(e);\n },\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }),\n [enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession],\n );\n\n const createVertexHandler = useCallback(\n (vertexIndex: number): ResizeHandleEventProps => ({\n onPointerDown: (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n controllerRef.current?.startVertexEdit(vertexIndex, e.clientX, e.clientY);\n startPointerSession(e);\n },\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }),\n [enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession],\n );\n\n const createRotationHandler = useCallback(\n (initialRotation: number = 0, orbitRadiusPx?: number): ResizeHandleEventProps => ({\n onPointerDown: (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n // Use the handle's actual DOM center, not the raw click position.\n // This avoids up to handleSize/2 px error when the user clicks\n // near the edge of the handle circle, which would shift the\n // reverse-engineered center and distort angles near the center.\n const handleRect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const handleCenterX = handleRect.left + handleRect.width / 2;\n const handleCenterY = handleRect.top + handleRect.height / 2;\n controllerRef.current?.startRotation(\n handleCenterX,\n handleCenterY,\n initialRotation,\n orbitRadiusPx,\n );\n startPointerSession(e);\n },\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }),\n [enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession],\n );\n\n return {\n dragProps: enabled\n ? {\n onPointerDown: handleDragStart,\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }\n : {},\n createResizeProps: createResizeHandler,\n createVertexProps: createVertexHandler,\n createRotationProps: createRotationHandler,\n };\n}\n","import { Rect, Rotation } from '@embedpdf/models';\nimport { getCounterRotation } from '@embedpdf/utils';\nimport { ReactNode, CSSProperties, Fragment, useRef, useEffect } from '@framework';\n\ninterface CounterRotateProps {\n rect: Rect;\n rotation: Rotation;\n}\n\nexport interface MenuWrapperProps {\n style: CSSProperties;\n ref: (el: HTMLDivElement | null) => void;\n}\n\ninterface CounterRotateComponentProps extends CounterRotateProps {\n children: (props: {\n matrix: string;\n rect: Rect;\n menuWrapperProps: MenuWrapperProps;\n }) => ReactNode;\n}\n\nexport function CounterRotate({ children, ...props }: CounterRotateComponentProps) {\n const { rect, rotation } = props;\n const { matrix, width, height } = getCounterRotation(rect, rotation);\n const elementRef = useRef<HTMLDivElement | null>(null);\n\n // Use native event listeners with capture phase to prevent event propagation\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const handlePointerDown = (e: Event) => {\n // Stop propagation to prevent underlying layers from receiving the event\n e.stopPropagation();\n // DO NOT use e.preventDefault() here - it breaks click events on mobile/tablet!\n // preventDefault() stops the browser from generating click events from touch,\n // which makes buttons inside this container non-functional on touch devices.\n };\n\n const handleTouchStart = (e: Event) => {\n // Stop propagation to prevent underlying layers from receiving the event\n e.stopPropagation();\n // DO NOT use e.preventDefault() here - it breaks click events on mobile/tablet!\n // preventDefault() stops the browser from generating click events from touch,\n // which makes buttons inside this container non-functional on touch devices.\n };\n\n // Use capture phase to intercept before synthetic events\n element.addEventListener('pointerdown', handlePointerDown, { capture: true });\n element.addEventListener('touchstart', handleTouchStart, { capture: true });\n\n return () => {\n element.removeEventListener('pointerdown', handlePointerDown, { capture: true });\n element.removeEventListener('touchstart', handleTouchStart, { capture: true });\n };\n }, []);\n\n const menuWrapperStyle: CSSProperties = {\n position: 'absolute',\n left: rect.origin.x,\n top: rect.origin.y,\n transform: matrix,\n transformOrigin: '0 0',\n width: width,\n height: height,\n pointerEvents: 'none',\n zIndex: 3,\n };\n\n const menuWrapperProps: MenuWrapperProps = {\n style: menuWrapperStyle,\n ref: (el: HTMLDivElement | null) => {\n elementRef.current = el;\n },\n };\n\n return (\n <Fragment>\n {children({\n menuWrapperProps,\n matrix,\n rect: {\n origin: { x: rect.origin.x, y: rect.origin.y },\n size: { width: width, height: height },\n },\n })}\n </Fragment>\n );\n}\n","import { useRef, useCallback, dblClickProp } from '@framework';\nimport type { PointerEvent } from '@framework';\n\ntype DoublePressOptions = {\n delay?: number; // ms between taps\n tolerancePx?: number; // spatial tolerance\n};\n\ntype DoubleHandler<T extends Element> = ((e: PointerEvent<T> | MouseEvent) => void) | undefined;\n\ntype DoubleProps<K extends string> = Partial<Record<K, (e: any) => void>> & {\n onPointerUp?: (e: any) => void;\n};\n\nexport function useDoublePressProps<\n T extends Element = Element,\n K extends string = typeof dblClickProp,\n>(\n onDouble?: DoubleHandler<T>,\n { delay = 300, tolerancePx = 18 }: DoublePressOptions = {},\n): DoubleProps<K> {\n const last = useRef({ t: 0, x: 0, y: 0 });\n\n const handlePointerUp = useCallback(\n (e: any) => {\n if (!onDouble) return;\n\n // Ignore mouse (it will use native dblclick),\n // and ignore non-primary pointers (multi-touch, etc.)\n if (e.pointerType === 'mouse' || e.isPrimary === false) return;\n\n const now = performance.now();\n const x = e.clientX as number;\n const y = e.clientY as number;\n\n const withinTime = now - last.current.t <= delay;\n const dx = x - last.current.x;\n const dy = y - last.current.y;\n const withinDist = dx * dx + dy * dy <= tolerancePx * tolerancePx;\n\n if (withinTime && withinDist) onDouble?.(e as PointerEvent<T>);\n\n last.current = { t: now, x, y };\n },\n [onDouble, delay, tolerancePx],\n );\n\n const handleDouble = useCallback(\n (e: any) => {\n onDouble?.(e);\n },\n [onDouble],\n );\n\n return onDouble\n ? ({\n // Computed property uses the framework’s name ('onDoubleClick' or 'onDblClick')\n [dblClickProp]: handleDouble,\n onPointerUpCapture: handlePointerUp,\n } as DoubleProps<K>)\n : {};\n}\n","import { useMemo, PointerEvent } from '@framework';\nimport type { CSSProperties } from '@framework';\nimport { useDragResize, UseDragResizeOptions } from './use-drag-resize';\nimport {\n describeResizeFromConfig,\n describeVerticesFromConfig,\n describeRotationFromConfig,\n type ResizeUI,\n type VertexUI,\n type RotationUI,\n} from '../plugin-interaction-primitives/utils';\n\nexport type HandleElementProps = {\n key?: string | number;\n style: CSSProperties;\n onPointerDown: (e: PointerEvent) => void;\n onPointerMove: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onPointerCancel: (e: PointerEvent) => void;\n} & Record<string, any>;\n\nexport type RotationHandleProps = {\n /** Props for the rotation handle element */\n handle: HandleElementProps;\n /** Props for the connector line element (if shown) */\n connector: {\n style: CSSProperties;\n } & Record<string, any>;\n};\n\nexport function useInteractionHandles(opts: {\n controller: UseDragResizeOptions; // SINGLE config (rect/scale/rotation/vertices/…)\n resizeUI?: ResizeUI; // purely visual knobs\n vertexUI?: VertexUI; // purely visual knobs\n rotationUI?: RotationUI; // purely visual knobs for rotation handle\n includeVertices?: boolean; // default false\n includeRotation?: boolean; // default false\n /** Current rotation angle of the annotation (for initializing rotation interaction) */\n currentRotation?: number;\n handleAttrs?: (\n h: 'nw' | 'ne' | 'sw' | 'se' | 'n' | 'e' | 's' | 'w',\n ) => Record<string, any> | void;\n vertexAttrs?: (i: number) => Record<string, any> | void;\n rotationAttrs?: () => Record<string, any> | void;\n}) {\n const {\n controller,\n resizeUI,\n vertexUI,\n rotationUI,\n includeVertices = false,\n includeRotation = false,\n currentRotation = 0,\n handleAttrs,\n vertexAttrs,\n rotationAttrs,\n } = opts;\n\n const { dragProps, createResizeProps, createVertexProps, createRotationProps } =\n useDragResize(controller);\n\n // Resize handles: only uses data from the SAME controller config.\n const resize: HandleElementProps[] = useMemo(() => {\n const desc = describeResizeFromConfig(controller, resizeUI);\n return desc.map((d) => ({\n key: d.attrs?.['data-epdf-handle'] as string,\n style: d.style as CSSProperties,\n ...createResizeProps(d.handle),\n ...(d.attrs ?? {}),\n ...(handleAttrs?.(d.handle) ?? {}),\n }));\n // deps: controller geometry knobs + UI knobs + handler factory\n }, [\n controller.element.origin.x,\n controller.element.origin.y,\n controller.element.size.width,\n controller.element.size.height,\n controller.scale,\n controller.pageRotation,\n controller.annotationRotation,\n controller.maintainAspectRatio,\n resizeUI?.handleSize,\n resizeUI?.spacing,\n resizeUI?.offsetMode,\n resizeUI?.includeSides,\n resizeUI?.zIndex,\n resizeUI?.rotationAwareCursor,\n createResizeProps,\n handleAttrs,\n ]);\n\n // Vertex handles: same source; prefer live vertices if parent rerenders with updated cfg.vertices\n const vertices: HandleElementProps[] = useMemo(() => {\n if (!includeVertices) return [];\n const desc = describeVerticesFromConfig(controller, vertexUI, controller.vertices);\n return desc.map((d, i) => ({\n key: i,\n style: d.style as CSSProperties,\n ...createVertexProps(i),\n ...(d.attrs ?? {}),\n ...(vertexAttrs?.(i) ?? {}),\n }));\n }, [\n includeVertices,\n controller.element.origin.x,\n controller.element.origin.y,\n controller.element.size.width,\n controller.element.size.height,\n controller.scale,\n controller.vertices, // identity/content drives recalculation\n vertexUI?.vertexSize,\n vertexUI?.zIndex,\n createVertexProps,\n vertexAttrs,\n ]);\n\n // Rotation handle: orbits around the center of the element based on current angle\n const rotation: RotationHandleProps | null = useMemo(() => {\n if (!includeRotation) return null;\n // Pass the current rotation angle so the handle is positioned correctly\n const desc = describeRotationFromConfig(controller, rotationUI, currentRotation);\n return {\n handle: {\n style: desc.handleStyle as CSSProperties,\n ...createRotationProps(currentRotation, desc.radius),\n ...(desc.attrs ?? {}),\n ...(rotationAttrs?.() ?? {}),\n },\n connector: {\n style: desc.connectorStyle as CSSProperties,\n 'data-epdf-rotation-connector': true,\n },\n };\n }, [\n includeRotation,\n controller.element.origin.x,\n controller.element.origin.y,\n controller.element.size.width,\n controller.element.size.height,\n controller.rotationCenter?.x,\n controller.rotationCenter?.y,\n controller.rotationElement?.origin.x,\n controller.rotationElement?.origin.y,\n controller.rotationElement?.size.width,\n controller.rotationElement?.size.height,\n controller.scale,\n currentRotation,\n rotationUI?.handleSize,\n rotationUI?.margin,\n rotationUI?.zIndex,\n rotationUI?.showConnector,\n rotationUI?.connectorWidth,\n createRotationProps,\n rotationAttrs,\n ]);\n\n return { dragProps, resize, vertices, rotation };\n}\n"],"names":["dblClickProp","HANDLE_BASE_ANGLE","n","ne","e","se","s","sw","w","nw","SECTOR_CURSORS","diagonalCursor","handle","pageQuarterTurns","annotationRotation","normalized","sector","Math","round","edgeOffset","k","spacing","mode","base","applyConstraints","position","constraints","maintainAspectRatio","skipBoundingClamp","origin","x","y","size","width","height","minW","minWidth","minH","minHeight","maxW","maxWidth","maxH","maxHeight","ratio","max","min","boundingBox","isRectWithinRotatedBounds","rect","angleDegrees","bbox","eps","aabb","calculateRotatedRectAABB","computeResizeStep","delta","config","clampLocalBounds","skipConstraintBoundingClamp","startRect","anchor","includes","getAnchor","aspectRatio","applyResizeDelta","dw","abs","dh","total","wWeight","hWeight","hFromW","enforceAspectRatio","anchorX","anchorY","scaleW","scaleH","scale","clampToBounds","reanchorRect","anchorPt","getAnchorPoint","oldCenter","newCenter","oldVisual","rotatePointAround","newVisual","DragResizeController","constructor","onUpdate","this","state","startPoint","startElement","startRotationElement","gestureRotationCenter","activeHandle","currentPosition","activeVertexIndex","startVertices","currentVertices","rotationCenter","centerScreen","initialRotation","lastComputedRotation","rotationDelta","rotationSnappedAngle","vertices","updateConfig","startDrag","clientX","clientY","element","rotationElement","transformData","type","changes","startResize","metadata","startVertexEdit","vertexIndex","length","startRotation","orbitRadiusPx","orbitRect","sh","radius","screenAngleRad","pageRotation","PI","sin","cos","rotation","rotationAngle","isSnapped","move","buttons","calculateDelta","calculateDragPosition","target","best","low","high","i","mid","trial","computeResizedRect","calculateLocalDelta","calculateVertexPosition","absoluteAngle","calculateAngleFromMouse","snapResult","applyRotationSnapping","snappedAngle","normalizeAngle","angle","rawDelta","adjustedDelta","cursorPosition","end","wasState","finalPosition","reset","cancel","transformDelta","rad","scaledX","scaledY","pageDelta","clampPoint","p","_a","center","visual","clampedX","clampedY","newVertices","currentVertex","moved","aabbW","aabbH","offsetX","offsetY","h","dx","dy","sqrt","pageRotOffset","angleDeg","atan2","snapAngles","rotationSnapAngles","threshold","rotationSnapThreshold","normalizedAngle","candidate","normalizedCandidate","diff","snapTarget","useDragResize","options","enabled","controllerRef","useRef","onUpdateRef","activePointerIdRef","activeTargetRef","useEffect","current","event","call","endPointerSession","useCallback","pointerId","activePointerId","id","hasPointerCapture","_b","releasePointerCapture","startPointerSession","currentTarget","setPointerCapture","eventTarget","globalThis","handleGlobalPointerEnd","handleWindowBlur","addEventListener","removeEventListener","handleDragStart","preventDefault","stopPropagation","handleMove","handleEndLike","handleLostPointerCapture","createResizeHandler","onPointerDown","onPointerMove","onPointerUp","onPointerCancel","onLostPointerCapture","createVertexHandler","createRotationHandler","handleRect","getBoundingClientRect","handleCenterX","left","handleCenterY","top","dragProps","createResizeProps","createVertexProps","createRotationProps","children","props","matrix","getCounterRotation","elementRef","handlePointerDown","handleTouchStart","capture","menuWrapperProps","style","transform","transformOrigin","pointerEvents","zIndex","ref","el","Fragment","onDouble","delay","tolerancePx","last","t","handlePointerUp","pointerType","isPrimary","now","performance","withinTime","handleDouble","onPointerUpCapture","opts","controller","resizeUI","vertexUI","rotationUI","includeVertices","includeRotation","currentRotation","handleAttrs","vertexAttrs","rotationAttrs","resize","useMemo","cfg","ui","handleSize","offsetMode","includeSides","rotationAwareCursor","annotationRot","off","edge","map","pos","borderRadius","cursor","touchAction","attrs","describeResizeFromConfig","d","key","liveVertices","vertexSize","v","describeVerticesFromConfig","desc","currentAngle","showConnector","connectorWidth","orbitCenter","centerX","centerY","angleRad","margin","handleStyle","connectorStyle","describeRotationFromConfig","connector","_c","_d","_e","_f"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/preact/adapter.ts","../../src/shared/plugin-interaction-primitives/resize-geometry.ts","../../src/shared/plugin-interaction-primitives/utils.ts","../../src/shared/plugin-interaction-primitives/drag-resize-controller.ts","../../src/shared/hooks/use-drag-resize.ts","../../src/shared/components/counter-rotate-container.tsx","../../src/shared/hooks/use-double-press-props.ts","../../src/shared/hooks/use-interaction-handles.ts"],"sourcesContent":["import { createContext, JSX, Fragment, FunctionComponent } from 'preact';\nexport { useEffect, useRef, useState, useCallback, useMemo, useContext } from 'preact/hooks';\nexport type { ComponentChildren as ReactNode, JSX } from 'preact';\n\nexport { createContext, Fragment };\n\nexport type CSSProperties = import('preact').JSX.CSSProperties;\nexport type HTMLAttributes<T = any> = import('preact').JSX.HTMLAttributes<\n T extends EventTarget ? T : never\n>;\n\nexport type MouseEvent<T = Element> = JSX.TargetedMouseEvent<T extends EventTarget ? T : never>;\nexport type PointerEvent<T = Element> = JSX.TargetedPointerEvent<T extends EventTarget ? T : never>;\nexport type ChangeEvent<T = Element> = JSX.TargetedInputEvent<T extends EventTarget ? T : never>;\nexport type TouchEvent<T = Element> = JSX.TargetedTouchEvent<T extends EventTarget ? T : never>;\nexport type ComponentType = FunctionComponent;\n\nexport const dblClickProp = 'onDblClick' as const;\n","/**\n * Pure geometric functions for the resize pipeline.\n *\n * Extracted from DragResizeController so that:\n * 1. The resize math is independently testable\n * 2. The controller stays focused on state machine + coordinate transformation\n */\nimport { Position, Rect, rotatePointAround, calculateRotatedRectAABB } from '@embedpdf/models';\n\nimport type { ResizeHandle } from './drag-resize-controller';\n\n// ---------------------------------------------------------------------------\n// Anchor helpers\n// ---------------------------------------------------------------------------\n\n/** Anchor describes which edges stay fixed when resizing. */\nexport type Anchor = {\n x: 'left' | 'right' | 'center';\n y: 'top' | 'bottom' | 'center';\n};\n\n/**\n * Derive anchor from handle.\n * - 'e' means we're dragging east → left edge is anchored\n * - 'nw' means we're dragging north-west → bottom-right corner is anchored\n */\nexport function getAnchor(handle: ResizeHandle): Anchor {\n return {\n x: handle.includes('e') ? 'left' : handle.includes('w') ? 'right' : 'center',\n y: handle.includes('s') ? 'top' : handle.includes('n') ? 'bottom' : 'center',\n };\n}\n\n/** Get the anchor point (the visually fixed point) in page space for a given rect and anchor. */\nexport function getAnchorPoint(rect: Rect, anchor: Anchor): Position {\n const x =\n anchor.x === 'left'\n ? rect.origin.x\n : anchor.x === 'right'\n ? rect.origin.x + rect.size.width\n : rect.origin.x + rect.size.width / 2;\n const y =\n anchor.y === 'top'\n ? rect.origin.y\n : anchor.y === 'bottom'\n ? rect.origin.y + rect.size.height\n : rect.origin.y + rect.size.height / 2;\n return { x, y };\n}\n\n// ---------------------------------------------------------------------------\n// Constraint types\n// ---------------------------------------------------------------------------\n\nexport interface ResizeConstraints {\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n boundingBox?: { width: number; height: number };\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline stages (pure functions)\n// ---------------------------------------------------------------------------\n\n/**\n * Apply the mouse delta to produce a raw (unconstrained) resized rect.\n */\nexport function applyResizeDelta(startRect: Rect, delta: Position, anchor: Anchor): Rect {\n let x = startRect.origin.x;\n let y = startRect.origin.y;\n let width = startRect.size.width;\n let height = startRect.size.height;\n\n if (anchor.x === 'left') {\n width += delta.x;\n } else if (anchor.x === 'right') {\n x += delta.x;\n width -= delta.x;\n }\n\n if (anchor.y === 'top') {\n height += delta.y;\n } else if (anchor.y === 'bottom') {\n y += delta.y;\n height -= delta.y;\n }\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Enforce aspect ratio while respecting the anchor.\n * For edge handles (center anchor on one axis), the rect expands symmetrically on that axis.\n * For corner handles, the anchor corner stays fixed.\n */\nexport function enforceAspectRatio(\n rect: Rect,\n startRect: Rect,\n anchor: Anchor,\n aspectRatio: number,\n): Rect {\n let { x, y } = rect.origin;\n let { width, height } = rect.size;\n\n const isEdgeHandle = anchor.x === 'center' || anchor.y === 'center';\n\n if (isEdgeHandle) {\n if (anchor.y === 'center') {\n height = width / aspectRatio;\n y = startRect.origin.y + (startRect.size.height - height) / 2;\n } else {\n width = height * aspectRatio;\n x = startRect.origin.x + (startRect.size.width - width) / 2;\n }\n } else {\n const dw = Math.abs(width - startRect.size.width);\n const dh = Math.abs(height - startRect.size.height);\n const total = dw + dh;\n\n if (total === 0) {\n width = startRect.size.width;\n height = startRect.size.height;\n } else {\n // Smooth weighted blend between the width-driven and height-driven\n // results. Both candidate pairs lie on the line w/h = aspectRatio\n // through the origin, so any linear combination preserves the ratio\n // exactly — while eliminating the discontinuous jump that the old\n // hard `dw >= dh` switch caused at the boundary.\n const wWeight = dw / total;\n const hWeight = dh / total;\n const wFromW = width;\n const hFromW = width / aspectRatio;\n const wFromH = height * aspectRatio;\n const hFromH = height;\n width = wWeight * wFromW + hWeight * wFromH;\n height = wWeight * hFromW + hWeight * hFromH;\n }\n }\n\n if (anchor.x === 'right') {\n x = startRect.origin.x + startRect.size.width - width;\n }\n if (anchor.y === 'bottom') {\n y = startRect.origin.y + startRect.size.height - height;\n }\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Clamp rect to bounding box while respecting anchor.\n */\nexport function clampToBounds(\n rect: Rect,\n startRect: Rect,\n anchor: Anchor,\n bbox: { width: number; height: number } | undefined,\n maintainAspectRatio: boolean,\n): Rect {\n if (!bbox) return rect;\n\n let { x, y } = rect.origin;\n let { width, height } = rect.size;\n\n width = Math.max(1, width);\n height = Math.max(1, height);\n\n const anchorX =\n anchor.x === 'left' ? startRect.origin.x : startRect.origin.x + startRect.size.width;\n const anchorY =\n anchor.y === 'top' ? startRect.origin.y : startRect.origin.y + startRect.size.height;\n\n const maxW =\n anchor.x === 'left'\n ? bbox.width - anchorX\n : anchor.x === 'right'\n ? anchorX\n : Math.min(startRect.origin.x, bbox.width - startRect.origin.x - startRect.size.width) * 2 +\n startRect.size.width;\n\n const maxH =\n anchor.y === 'top'\n ? bbox.height - anchorY\n : anchor.y === 'bottom'\n ? anchorY\n : Math.min(startRect.origin.y, bbox.height - startRect.origin.y - startRect.size.height) *\n 2 +\n startRect.size.height;\n\n if (maintainAspectRatio) {\n const scaleW = width > maxW ? maxW / width : 1;\n const scaleH = height > maxH ? maxH / height : 1;\n const scale = Math.min(scaleW, scaleH);\n\n if (scale < 1) {\n width *= scale;\n height *= scale;\n }\n } else {\n width = Math.min(width, maxW);\n height = Math.min(height, maxH);\n }\n\n if (anchor.x === 'left') {\n x = anchorX;\n } else if (anchor.x === 'right') {\n x = anchorX - width;\n } else {\n x = startRect.origin.x + (startRect.size.width - width) / 2;\n }\n\n if (anchor.y === 'top') {\n y = anchorY;\n } else if (anchor.y === 'bottom') {\n y = anchorY - height;\n } else {\n y = startRect.origin.y + (startRect.size.height - height) / 2;\n }\n\n x = Math.max(0, Math.min(x, bbox.width - width));\n y = Math.max(0, Math.min(y, bbox.height - height));\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Reposition rect from current size so the start-gesture anchor remains fixed.\n * This prevents translation drift when constraints clamp width/height.\n */\nexport function reanchorRect(rect: Rect, startRect: Rect, anchor: Anchor): Rect {\n let x: number;\n let y: number;\n\n if (anchor.x === 'left') {\n x = startRect.origin.x;\n } else if (anchor.x === 'right') {\n x = startRect.origin.x + startRect.size.width - rect.size.width;\n } else {\n x = startRect.origin.x + (startRect.size.width - rect.size.width) / 2;\n }\n\n if (anchor.y === 'top') {\n y = startRect.origin.y;\n } else if (anchor.y === 'bottom') {\n y = startRect.origin.y + startRect.size.height - rect.size.height;\n } else {\n y = startRect.origin.y + (startRect.size.height - rect.size.height) / 2;\n }\n\n return { origin: { x, y }, size: rect.size };\n}\n\n/**\n * Apply min/max constraints. Also used by the drag pipeline for position clamping.\n */\nexport function applyConstraints(\n position: Rect,\n constraints: ResizeConstraints | undefined,\n maintainAspectRatio: boolean,\n skipBoundingClamp: boolean = false,\n): Rect {\n if (!constraints) return position;\n\n let {\n origin: { x, y },\n size: { width, height },\n } = position;\n\n const minW = constraints.minWidth ?? 1;\n const minH = constraints.minHeight ?? 1;\n const maxW = constraints.maxWidth;\n const maxH = constraints.maxHeight;\n\n if (maintainAspectRatio && width > 0 && height > 0) {\n const ratio = width / height;\n\n if (width < minW) {\n width = minW;\n height = width / ratio;\n }\n if (height < minH) {\n height = minH;\n width = height * ratio;\n }\n\n if (maxW !== undefined && width > maxW) {\n width = maxW;\n height = width / ratio;\n }\n if (maxH !== undefined && height > maxH) {\n height = maxH;\n width = height * ratio;\n }\n } else {\n width = Math.max(minW, width);\n height = Math.max(minH, height);\n if (maxW !== undefined) width = Math.min(maxW, width);\n if (maxH !== undefined) height = Math.min(maxH, height);\n }\n\n if (constraints.boundingBox && !skipBoundingClamp) {\n x = Math.max(0, Math.min(x, constraints.boundingBox.width - width));\n y = Math.max(0, Math.min(y, constraints.boundingBox.height - height));\n }\n\n return { origin: { x, y }, size: { width, height } };\n}\n\n/**\n * Check if a rect, when rotated, fits within the given page bounds.\n */\nexport function isRectWithinRotatedBounds(\n rect: Rect,\n angleDegrees: number,\n bbox: { width: number; height: number },\n): boolean {\n const eps = 1e-6;\n const aabb = calculateRotatedRectAABB(rect, angleDegrees);\n return (\n aabb.origin.x >= -eps &&\n aabb.origin.y >= -eps &&\n aabb.origin.x + aabb.size.width <= bbox.width + eps &&\n aabb.origin.y + aabb.size.height <= bbox.height + eps\n );\n}\n\n// ---------------------------------------------------------------------------\n// Full resize pipeline\n// ---------------------------------------------------------------------------\n\nexport interface ResizeConfig {\n startRect: Rect;\n maintainAspectRatio?: boolean;\n annotationRotation?: number;\n constraints?: ResizeConstraints;\n}\n\n/**\n * Run the full resize pipeline for a single step:\n * delta → anchor → raw resize → aspect ratio → bounds clamp → constraints → anchor compensation\n */\nfunction computeResizeStep(\n delta: Position,\n handle: ResizeHandle,\n config: ResizeConfig,\n clampLocalBounds: boolean,\n skipConstraintBoundingClamp: boolean,\n): Rect {\n const { startRect, maintainAspectRatio = false, annotationRotation = 0, constraints } = config;\n const anchor = getAnchor(handle);\n const aspectRatio = startRect.size.width / startRect.size.height || 1;\n\n // Step 1: Apply delta to get raw resize\n let rect = applyResizeDelta(startRect, delta, anchor);\n\n // Step 2: Enforce aspect ratio if enabled\n if (maintainAspectRatio) {\n rect = enforceAspectRatio(rect, startRect, anchor, aspectRatio);\n }\n\n // Step 3: Clamp in local/unrotated frame when requested\n if (clampLocalBounds) {\n rect = clampToBounds(rect, startRect, anchor, constraints?.boundingBox, maintainAspectRatio);\n }\n\n // Step 4: Apply min/max constraints\n rect = applyConstraints(rect, constraints, maintainAspectRatio, skipConstraintBoundingClamp);\n\n // In rotated resize mode we skip axis-aligned bounding clamps and solve\n // against visual bounds separately. Re-anchor after size constraints so\n // dragging past min/max does not keep translating the rect.\n if (skipConstraintBoundingClamp) {\n rect = reanchorRect(rect, startRect, anchor);\n }\n\n // Step 5: Compensate for visual anchor drift when the annotation is rotated.\n if (annotationRotation !== 0) {\n const anchorPt = getAnchorPoint(startRect, anchor);\n const oldCenter: Position = {\n x: startRect.origin.x + startRect.size.width / 2,\n y: startRect.origin.y + startRect.size.height / 2,\n };\n const newCenter: Position = {\n x: rect.origin.x + rect.size.width / 2,\n y: rect.origin.y + rect.size.height / 2,\n };\n const oldVisual = rotatePointAround(anchorPt, oldCenter, annotationRotation);\n const newVisual = rotatePointAround(anchorPt, newCenter, annotationRotation);\n rect = {\n origin: {\n x: rect.origin.x + (oldVisual.x - newVisual.x),\n y: rect.origin.y + (oldVisual.y - newVisual.y),\n },\n size: rect.size,\n };\n }\n\n return rect;\n}\n\n/**\n * Calculate the new rect after a resize operation.\n *\n * For non-rotated annotations, runs the pipeline once with local bound clamping.\n * For rotated annotations, uses a binary search to find the largest delta that\n * keeps the visual AABB within page bounds.\n */\nexport function computeResizedRect(\n delta: Position,\n handle: ResizeHandle,\n config: ResizeConfig,\n): Rect {\n const { annotationRotation = 0, constraints } = config;\n const bbox = constraints?.boundingBox;\n\n // For rotated annotations, clamp using visual AABB bounds via binary search.\n if (annotationRotation !== 0 && bbox) {\n const target = computeResizeStep(delta, handle, config, false, true);\n if (isRectWithinRotatedBounds(target, annotationRotation, bbox)) {\n return target;\n }\n\n let best = computeResizeStep({ x: 0, y: 0 }, handle, config, false, true);\n let low = 0;\n let high = 1;\n for (let i = 0; i < 20; i += 1) {\n const mid = (low + high) / 2;\n const trial = computeResizeStep(\n { x: delta.x * mid, y: delta.y * mid },\n handle,\n config,\n false,\n true,\n );\n if (isRectWithinRotatedBounds(trial, annotationRotation, bbox)) {\n best = trial;\n low = mid;\n } else {\n high = mid;\n }\n }\n\n return best;\n }\n\n return computeResizeStep(delta, handle, config, true, false);\n}\n","import type { Position, Rect } from '@embedpdf/models';\nimport type { ResizeHandle, DragResizeConfig } from './drag-resize-controller';\n\nexport type QuarterTurns = 0 | 1 | 2 | 3;\n\nexport interface ResizeUI {\n handleSize?: number; // px (default 8)\n spacing?: number; // px distance from the box edge (default 1)\n offsetMode?: 'outside' | 'inside' | 'center'; // default 'outside'\n includeSides?: boolean; // default false\n zIndex?: number; // default 3\n rotationAwareCursor?: boolean; // default true\n}\n\nexport interface VertexUI {\n vertexSize?: number; // px (default 12)\n zIndex?: number; // default 4\n}\n\nexport interface RotationUI {\n /** Handle size in px (default 32) */\n handleSize?: number;\n /** Screen-pixel gap between bounding box edge and handle center (default 30) */\n margin?: number;\n /** z-index of the rotation handle (default 5) */\n zIndex?: number;\n /** Whether to show the connector line from center to handle (default true) */\n showConnector?: boolean;\n /** Connector line width in px (default 1) */\n connectorWidth?: number;\n}\n\n/** Screen-pixel gap between the rect edge and the rotation handle center (default orbit margin). */\nexport const ROTATION_HANDLE_MARGIN = 35;\n\nexport interface HandleDescriptor {\n handle: ResizeHandle;\n style: Record<string, number | string>;\n attrs?: Record<string, any>;\n}\n\nexport interface RotationHandleDescriptor {\n /** Style for the rotation handle itself */\n handleStyle: Record<string, number | string>;\n /** Style for the connector line (if shown) */\n connectorStyle: Record<string, number | string>;\n /** Orbit radius in screen pixels used to position the handle. */\n radius: number;\n /** Attributes for the handle element */\n attrs?: Record<string, any>;\n}\n\n/**\n * Base angle (degrees, clockwise from north) for each resize handle.\n * Used to compute the effective angle after page + annotation rotation.\n */\nconst HANDLE_BASE_ANGLE: Record<ResizeHandle, number> = {\n n: 0,\n ne: 45,\n e: 90,\n se: 135,\n s: 180,\n sw: 225,\n w: 270,\n nw: 315,\n};\n\n/**\n * Cursor names mapped to 45-degree sectors.\n * Sector 0 = north (337.5..22.5), sector 1 = NE (22.5..67.5), etc.\n */\nconst SECTOR_CURSORS: string[] = [\n 'ns-resize', // 0: north\n 'nesw-resize', // 1: NE\n 'ew-resize', // 2: east\n 'nwse-resize', // 3: SE\n 'ns-resize', // 4: south\n 'nesw-resize', // 5: SW\n 'ew-resize', // 6: west\n 'nwse-resize', // 7: NW\n];\n\nfunction diagonalCursor(\n handle: ResizeHandle,\n pageQuarterTurns: QuarterTurns,\n annotationRotation: number = 0,\n): string {\n const pageAngle = pageQuarterTurns * 90;\n const totalAngle = HANDLE_BASE_ANGLE[handle] + pageAngle + annotationRotation;\n // Normalize to [0, 360)\n const normalized = ((totalAngle % 360) + 360) % 360;\n // Map to 45-degree sector (0..7)\n const sector = Math.round(normalized / 45) % 8;\n return SECTOR_CURSORS[sector];\n}\n\nfunction edgeOffset(k: number, spacing: number, mode: 'outside' | 'inside' | 'center') {\n // Base puts the handle centered on the edge\n const base = -k / 2;\n if (mode === 'center') return base;\n // outside moves further out (more negative), inside moves in (less negative)\n return mode === 'outside' ? base - spacing : base + spacing;\n}\n\nexport function describeResizeFromConfig(\n cfg: DragResizeConfig,\n ui: ResizeUI = {},\n): HandleDescriptor[] {\n const {\n handleSize = 8,\n spacing = 1,\n offsetMode = 'outside',\n includeSides = false,\n zIndex = 3,\n rotationAwareCursor = true,\n } = ui;\n\n const pageQuarterTurns = ((cfg.pageRotation ?? 0) % 4) as QuarterTurns;\n const annotationRot = cfg.annotationRotation ?? 0;\n\n const off = (edge: 'top' | 'right' | 'bottom' | 'left') => ({\n [edge]: edgeOffset(handleSize, spacing, offsetMode) + 'px',\n });\n\n const corners: Array<[ResizeHandle, Record<string, number | string>]> = [\n ['nw', { ...off('top'), ...off('left') }],\n ['ne', { ...off('top'), ...off('right') }],\n ['sw', { ...off('bottom'), ...off('left') }],\n ['se', { ...off('bottom'), ...off('right') }],\n ];\n const sides: Array<[ResizeHandle, Record<string, number | string>]> = includeSides\n ? [\n ['n', { ...off('top'), left: `calc(50% - ${handleSize / 2}px)` }],\n ['s', { ...off('bottom'), left: `calc(50% - ${handleSize / 2}px)` }],\n ['w', { ...off('left'), top: `calc(50% - ${handleSize / 2}px)` }],\n ['e', { ...off('right'), top: `calc(50% - ${handleSize / 2}px)` }],\n ]\n : [];\n\n const all = [...corners, ...sides];\n\n return all.map(([handle, pos]) => ({\n handle,\n style: {\n position: 'absolute',\n width: handleSize + 'px',\n height: handleSize + 'px',\n borderRadius: '50%',\n zIndex,\n cursor: rotationAwareCursor\n ? diagonalCursor(handle, pageQuarterTurns, annotationRot)\n : 'default',\n pointerEvents: 'auto',\n touchAction: 'none',\n ...(pos as any),\n },\n attrs: { 'data-epdf-handle': handle },\n }));\n}\n\nexport function describeVerticesFromConfig(\n cfg: DragResizeConfig,\n ui: VertexUI = {},\n liveVertices?: Position[],\n): HandleDescriptor[] {\n const { vertexSize = 12, zIndex = 4 } = ui;\n const rect: Rect = cfg.element;\n const scale = cfg.scale ?? 1;\n const verts = liveVertices ?? cfg.vertices ?? [];\n\n return verts.map((v, i) => {\n const left = (v.x - rect.origin.x) * scale - vertexSize / 2;\n const top = (v.y - rect.origin.y) * scale - vertexSize / 2;\n return {\n handle: 'nw', // not used; kept for type\n style: {\n position: 'absolute',\n left: left + 'px',\n top: top + 'px',\n width: vertexSize + 'px',\n height: vertexSize + 'px',\n borderRadius: '50%',\n cursor: 'pointer',\n zIndex,\n pointerEvents: 'auto',\n touchAction: 'none',\n },\n attrs: { 'data-epdf-vertex': i },\n };\n });\n}\n\n/**\n * Describe the rotation handle position and style.\n * The rotation handle orbits around the center of the bounding box based on the current angle.\n *\n * @param cfg - The drag/resize config containing the element rect and scale\n * @param ui - UI customization options\n * @param currentAngle - The current rotation angle in degrees (0 = top, clockwise positive)\n */\nexport function describeRotationFromConfig(\n cfg: DragResizeConfig,\n ui: RotationUI = {},\n currentAngle: number = 0,\n): RotationHandleDescriptor {\n const { handleSize = 16, zIndex = 5, showConnector = true, connectorWidth = 1 } = ui;\n\n const scale = cfg.scale ?? 1;\n const rect = cfg.element;\n\n const orbitRect = cfg.rotationElement ?? rect;\n const orbitCenter = cfg.rotationCenter ?? {\n x: rect.origin.x + rect.size.width / 2,\n y: rect.origin.y + rect.size.height / 2,\n };\n\n // Center in scaled coordinates, relative to the orbit rect's origin.\n const scaledWidth = orbitRect.size.width * scale;\n const scaledHeight = orbitRect.size.height * scale;\n const centerX = (orbitCenter.x - orbitRect.origin.x) * scale;\n const centerY = (orbitCenter.y - orbitRect.origin.y) * scale;\n\n // Handle orbits at currentAngle (0° = top, clockwise positive)\n const angleRad = (currentAngle * Math.PI) / 180;\n\n // Calculate radius - distance from center to handle.\n // The handle always sits at the shape's local \"top\" because currentAngle\n // matches the annotation rotation and the shape rotates with it. So the\n // distance from center to the nearest edge in the handle's direction is\n // always halfHeight of the unrotated rect, regardless of the angle.\n const margin = ui.margin ?? ROTATION_HANDLE_MARGIN;\n const radius = (rect.size.height * scale) / 2 + margin;\n\n const handleCenterX = centerX + radius * Math.sin(angleRad);\n const handleCenterY = centerY - radius * Math.cos(angleRad);\n const handleLeft = handleCenterX - handleSize / 2;\n const handleTop = handleCenterY - handleSize / 2;\n\n return {\n handleStyle: {\n position: 'absolute',\n left: handleLeft + 'px',\n top: handleTop + 'px',\n width: handleSize + 'px',\n height: handleSize + 'px',\n borderRadius: '50%',\n cursor: 'grab',\n zIndex,\n pointerEvents: 'auto',\n touchAction: 'none',\n },\n connectorStyle: showConnector\n ? {\n position: 'absolute',\n left: centerX - connectorWidth / 2 + 'px',\n top: centerY - radius + 'px',\n width: connectorWidth + 'px',\n height: radius + 'px',\n transformOrigin: 'center bottom',\n transform: `rotate(${currentAngle}deg)`,\n zIndex: zIndex - 1,\n pointerEvents: 'none',\n }\n : {},\n radius,\n attrs: { 'data-epdf-rotation-handle': true },\n };\n}\n","import { normalizeAngle, Position, Rect, rotatePointAround } from '@embedpdf/models';\nimport { applyConstraints, computeResizedRect } from './resize-geometry';\nimport { ROTATION_HANDLE_MARGIN } from './utils';\n\nexport interface DragResizeConfig {\n element: Rect;\n /**\n * Optional world-space pivot to use for rotation interactions.\n * Defaults to the center of `element`.\n */\n rotationCenter?: Position;\n /**\n * Optional rect used for rotation-handle orbit layout (typically the visible AABB container).\n * Defaults to `element`.\n */\n rotationElement?: Rect;\n vertices?: Position[];\n constraints?: {\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n boundingBox?: { width: number; height: number }; // page bounds\n };\n maintainAspectRatio?: boolean;\n pageRotation?: number;\n /** Rotation of the annotation itself in degrees (used to project mouse deltas into local space for resize/vertex-edit) */\n annotationRotation?: number;\n scale?: number;\n rotationSnapAngles?: number[];\n rotationSnapThreshold?: number;\n}\n\nexport type InteractionState = 'idle' | 'dragging' | 'resizing' | 'vertex-editing' | 'rotating';\nexport type ResizeHandle = 'nw' | 'ne' | 'sw' | 'se' | 'n' | 'e' | 's' | 'w';\n\nexport interface TransformData {\n type: 'move' | 'resize' | 'vertex-edit' | 'rotate';\n changes: {\n rect?: Rect;\n vertices?: Position[];\n rotation?: number;\n };\n metadata?: {\n handle?: ResizeHandle;\n vertexIndex?: number;\n maintainAspectRatio?: boolean;\n /** The rotation angle in degrees */\n rotationAngle?: number;\n /** The center point used for rotation */\n rotationCenter?: Position;\n rotationDelta?: number;\n isSnapped?: boolean;\n snappedAngle?: number;\n /** Screen-space cursor position during the gesture */\n cursorPosition?: { clientX: number; clientY: number };\n };\n}\n\nexport interface InteractionEvent {\n state: 'start' | 'move' | 'end';\n transformData?: TransformData;\n}\n\n/**\n * Pure geometric controller that manages drag/resize/vertex-edit/rotate logic.\n */\nexport class DragResizeController {\n private state: InteractionState = 'idle';\n private startPoint: Position | null = null;\n private startElement: Rect | null = null;\n private startRotationElement: Rect | null = null;\n private gestureRotationCenter: Position | null = null;\n private activeHandle: ResizeHandle | null = null;\n private currentPosition: Rect | null = null;\n\n // Vertex editing state - pure geometric\n private activeVertexIndex: number | null = null;\n private startVertices: Position[] = [];\n private currentVertices: Position[] = [];\n\n // Rotation state\n private rotationCenter: Position | null = null;\n private centerScreen: Position | null = null; // Cached center in screen coords\n private initialRotation: number = 0; // The rotation value when interaction started\n private lastComputedRotation: number = 0; // The last computed rotation during move\n private rotationDelta: number = 0;\n private rotationSnappedAngle: number | null = null;\n\n constructor(\n private config: DragResizeConfig,\n private onUpdate: (event: InteractionEvent) => void,\n ) {\n this.currentVertices = config.vertices || [];\n }\n\n updateConfig(config: Partial<DragResizeConfig>) {\n this.config = { ...this.config, ...config };\n // Keep the gesture buffer stable during active vertex editing.\n // Otherwise rerendered preview vertices can overwrite `currentVertices`\n // and cause end() to emit compensated vertices a second time.\n if (this.state !== 'vertex-editing') {\n this.currentVertices = config.vertices || [];\n }\n }\n\n // ---------------------------------------------------------------------------\n // Gesture start\n // ---------------------------------------------------------------------------\n\n startDrag(clientX: number, clientY: number) {\n this.state = 'dragging';\n this.startPoint = { x: clientX, y: clientY };\n this.startElement = { ...this.config.element };\n this.startRotationElement = this.config.rotationElement\n ? { ...this.config.rotationElement }\n : null;\n this.currentPosition = { ...this.config.element };\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'move',\n changes: { rect: this.startElement },\n },\n });\n }\n\n startResize(handle: ResizeHandle, clientX: number, clientY: number) {\n this.state = 'resizing';\n this.activeHandle = handle;\n this.startPoint = { x: clientX, y: clientY };\n this.startElement = { ...this.config.element };\n this.currentPosition = { ...this.config.element };\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'resize',\n changes: { rect: this.startElement },\n metadata: {\n handle: this.activeHandle,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n }\n\n startVertexEdit(vertexIndex: number, clientX: number, clientY: number) {\n // Refresh vertices from latest config before validating index\n this.currentVertices = [...(this.config.vertices ?? this.currentVertices)];\n if (vertexIndex < 0 || vertexIndex >= this.currentVertices.length) return;\n\n this.state = 'vertex-editing';\n this.activeVertexIndex = vertexIndex;\n this.startPoint = { x: clientX, y: clientY };\n this.startVertices = [...this.currentVertices];\n this.gestureRotationCenter = this.config.rotationCenter ?? {\n x: this.config.element.origin.x + this.config.element.size.width / 2,\n y: this.config.element.origin.y + this.config.element.size.height / 2,\n };\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices: this.startVertices },\n metadata: { vertexIndex },\n },\n });\n }\n\n startRotation(\n clientX: number,\n clientY: number,\n initialRotation: number = 0,\n orbitRadiusPx?: number,\n ) {\n this.state = 'rotating';\n this.startPoint = { x: clientX, y: clientY };\n this.startElement = { ...this.config.element };\n\n // Use explicit rotation center when provided (keeps pivot stable after vertex edits).\n this.rotationCenter = this.config.rotationCenter ?? {\n x: this.config.element.origin.x + this.config.element.size.width / 2,\n y: this.config.element.origin.y + this.config.element.size.height / 2,\n };\n\n // Cache the center in screen coordinates, derived from the handle's DOM center\n const { scale = 1 } = this.config;\n const orbitRect = this.config.rotationElement ?? this.config.element;\n const sw = orbitRect.size.width * scale;\n const sh = orbitRect.size.height * scale;\n const radius = orbitRadiusPx ?? Math.max(sw, sh) / 2 + ROTATION_HANDLE_MARGIN;\n const pageRotOffset = (this.config.pageRotation ?? 0) * 90;\n const screenAngleRad = ((initialRotation + pageRotOffset) * Math.PI) / 180;\n this.centerScreen = {\n x: clientX - radius * Math.sin(screenAngleRad),\n y: clientY + radius * Math.cos(screenAngleRad),\n };\n\n this.initialRotation = initialRotation;\n this.lastComputedRotation = initialRotation;\n this.rotationDelta = 0;\n this.rotationSnappedAngle = null;\n\n this.onUpdate({\n state: 'start',\n transformData: {\n type: 'rotate',\n changes: { rotation: initialRotation },\n metadata: {\n rotationAngle: initialRotation,\n rotationDelta: 0,\n rotationCenter: this.rotationCenter,\n isSnapped: false,\n },\n },\n });\n }\n\n // ---------------------------------------------------------------------------\n // Gesture move\n // ---------------------------------------------------------------------------\n\n move(clientX: number, clientY: number, buttons?: number, lockAspectRatio?: boolean) {\n if (this.state === 'idle' || !this.startPoint) return;\n\n // Safety net: if the button is no longer pressed but we never received\n // pointerup/pointercancel, finalize the gesture to avoid a \"stuck\" drag.\n if (buttons !== undefined && buttons === 0) {\n this.end();\n return;\n }\n\n if (this.state === 'dragging' && this.startElement) {\n const delta = this.calculateDelta(clientX, clientY);\n const position = this.calculateDragPosition(delta);\n this.currentPosition = position;\n\n this.onUpdate({\n state: 'move',\n transformData: { type: 'move', changes: { rect: position } },\n });\n } else if (this.state === 'resizing' && this.activeHandle && this.startElement) {\n const delta = this.calculateLocalDelta(clientX, clientY);\n const position = computeResizedRect(delta, this.activeHandle, {\n startRect: this.startElement,\n maintainAspectRatio: this.config.maintainAspectRatio || !!lockAspectRatio,\n annotationRotation: this.config.annotationRotation,\n constraints: this.config.constraints,\n });\n this.currentPosition = position;\n\n this.onUpdate({\n state: 'move',\n transformData: {\n type: 'resize',\n changes: { rect: position },\n metadata: {\n handle: this.activeHandle,\n maintainAspectRatio: this.config.maintainAspectRatio || !!lockAspectRatio,\n },\n },\n });\n } else if (this.state === 'vertex-editing' && this.activeVertexIndex !== null) {\n const vertices = this.calculateVertexPosition(clientX, clientY);\n this.currentVertices = vertices;\n\n this.onUpdate({\n state: 'move',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices },\n metadata: { vertexIndex: this.activeVertexIndex },\n },\n });\n } else if (this.state === 'rotating' && this.rotationCenter) {\n const absoluteAngle = this.calculateAngleFromMouse(clientX, clientY);\n const snapResult = this.applyRotationSnapping(absoluteAngle);\n const snappedAngle = normalizeAngle(snapResult.angle);\n const previousAngle = this.lastComputedRotation;\n const rawDelta = snappedAngle - previousAngle;\n const adjustedDelta =\n rawDelta > 180 ? rawDelta - 360 : rawDelta < -180 ? rawDelta + 360 : rawDelta;\n\n this.rotationDelta += adjustedDelta;\n this.lastComputedRotation = snappedAngle;\n this.rotationSnappedAngle = snapResult.isSnapped ? snappedAngle : null;\n\n this.onUpdate({\n state: 'move',\n transformData: {\n type: 'rotate',\n changes: { rotation: snappedAngle },\n metadata: {\n rotationAngle: snappedAngle,\n rotationDelta: this.rotationDelta,\n rotationCenter: this.rotationCenter,\n isSnapped: snapResult.isSnapped,\n snappedAngle: this.rotationSnappedAngle ?? undefined,\n cursorPosition: { clientX, clientY },\n },\n },\n });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Gesture end / cancel\n // ---------------------------------------------------------------------------\n\n end() {\n if (this.state === 'idle') return;\n\n const wasState = this.state;\n const handle = this.activeHandle;\n const vertexIndex = this.activeVertexIndex;\n\n if (wasState === 'vertex-editing') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices: this.currentVertices },\n metadata: { vertexIndex: vertexIndex || undefined },\n },\n });\n } else if (wasState === 'rotating') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'rotate',\n changes: { rotation: this.lastComputedRotation },\n metadata: {\n rotationAngle: this.lastComputedRotation,\n rotationDelta: this.rotationDelta,\n rotationCenter: this.rotationCenter || undefined,\n isSnapped: this.rotationSnappedAngle !== null,\n snappedAngle: this.rotationSnappedAngle ?? undefined,\n },\n },\n });\n } else {\n const finalPosition = this.currentPosition || this.config.element;\n this.onUpdate({\n state: 'end',\n transformData: {\n type: wasState === 'dragging' ? 'move' : 'resize',\n changes: { rect: finalPosition },\n metadata:\n wasState === 'dragging'\n ? undefined\n : {\n handle: handle || undefined,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n }\n\n this.reset();\n }\n\n cancel() {\n if (this.state === 'idle') return;\n\n if (this.state === 'vertex-editing') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'vertex-edit',\n changes: { vertices: this.startVertices },\n metadata: { vertexIndex: this.activeVertexIndex || undefined },\n },\n });\n } else if (this.state === 'rotating') {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: 'rotate',\n changes: { rotation: this.initialRotation },\n metadata: {\n rotationAngle: this.initialRotation,\n rotationDelta: 0,\n rotationCenter: this.rotationCenter || undefined,\n isSnapped: false,\n },\n },\n });\n } else if (this.startElement) {\n this.onUpdate({\n state: 'end',\n transformData: {\n type: this.state === 'dragging' ? 'move' : 'resize',\n changes: { rect: this.startElement },\n metadata:\n this.state === 'dragging'\n ? undefined\n : {\n handle: this.activeHandle || undefined,\n maintainAspectRatio: this.config.maintainAspectRatio,\n },\n },\n });\n }\n\n this.reset();\n }\n\n // ---------------------------------------------------------------------------\n // Private: state management\n // ---------------------------------------------------------------------------\n\n private reset() {\n this.state = 'idle';\n this.startPoint = null;\n this.startElement = null;\n this.startRotationElement = null;\n this.gestureRotationCenter = null;\n this.activeHandle = null;\n this.currentPosition = null;\n this.activeVertexIndex = null;\n this.startVertices = [];\n // Reset rotation state\n this.rotationCenter = null;\n this.centerScreen = null;\n this.initialRotation = 0;\n this.lastComputedRotation = 0;\n this.rotationDelta = 0;\n this.rotationSnappedAngle = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private: coordinate transformation (screen → page → local)\n // ---------------------------------------------------------------------------\n\n private calculateDelta(clientX: number, clientY: number): Position {\n if (!this.startPoint) return { x: 0, y: 0 };\n\n const rawDelta: Position = {\n x: clientX - this.startPoint.x,\n y: clientY - this.startPoint.y,\n };\n\n return this.transformDelta(rawDelta);\n }\n\n private transformDelta(delta: Position): Position {\n const { pageRotation = 0, scale = 1 } = this.config;\n\n const rad = (pageRotation * Math.PI) / 2;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n\n const scaledX = delta.x / scale;\n const scaledY = delta.y / scale;\n\n return {\n x: cos * scaledX + sin * scaledY,\n y: -sin * scaledX + cos * scaledY,\n };\n }\n\n /**\n * Calculate delta projected into the annotation's local (unrotated) coordinate space.\n * Used for resize and vertex-edit where mouse movement must be mapped to the\n * annotation's own axes, accounting for its rotation.\n */\n private calculateLocalDelta(clientX: number, clientY: number): Position {\n const pageDelta = this.calculateDelta(clientX, clientY);\n const { annotationRotation = 0 } = this.config;\n if (annotationRotation === 0) return pageDelta;\n\n const rad = (annotationRotation * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n return {\n x: cos * pageDelta.x + sin * pageDelta.y,\n y: -sin * pageDelta.x + cos * pageDelta.y,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Private: vertex clamping\n // ---------------------------------------------------------------------------\n\n private clampPoint(p: Position): Position {\n const bbox = this.config.constraints?.boundingBox;\n if (!bbox) return p;\n\n const { annotationRotation = 0 } = this.config;\n if (annotationRotation === 0) {\n return {\n x: Math.max(0, Math.min(p.x, bbox.width)),\n y: Math.max(0, Math.min(p.y, bbox.height)),\n };\n }\n\n // When rotated, vertices live in unrotated space. Transform to visual\n // (page) space, clamp to page bounds, then inverse-rotate back.\n const center = this.gestureRotationCenter ??\n this.config.rotationCenter ?? {\n x: this.config.element.origin.x + this.config.element.size.width / 2,\n y: this.config.element.origin.y + this.config.element.size.height / 2,\n };\n const visual = rotatePointAround(p, center, annotationRotation);\n\n const clampedX = Math.max(0, Math.min(visual.x, bbox.width));\n const clampedY = Math.max(0, Math.min(visual.y, bbox.height));\n\n if (clampedX === visual.x && clampedY === visual.y) return p;\n\n return rotatePointAround({ x: clampedX, y: clampedY }, center, -annotationRotation);\n }\n\n private calculateVertexPosition(clientX: number, clientY: number): Position[] {\n if (this.activeVertexIndex === null) return this.startVertices;\n\n const delta = this.calculateLocalDelta(clientX, clientY);\n const newVertices = [...this.startVertices];\n const currentVertex = newVertices[this.activeVertexIndex];\n\n const moved = {\n x: currentVertex.x + delta.x,\n y: currentVertex.y + delta.y,\n };\n newVertices[this.activeVertexIndex] = this.clampPoint(moved);\n\n return newVertices;\n }\n\n // ---------------------------------------------------------------------------\n // Private: drag position\n // ---------------------------------------------------------------------------\n\n private calculateDragPosition(delta: Position): Rect {\n if (!this.startElement) return this.config.element;\n\n const position: Rect = {\n origin: {\n x: this.startElement.origin.x + delta.x,\n y: this.startElement.origin.y + delta.y,\n },\n size: {\n width: this.startElement.size.width,\n height: this.startElement.size.height,\n },\n };\n\n // When the annotation is rotated, the visible footprint is the AABB, not\n // the unrotatedRect. Clamp based on AABB dimensions so the annotation can\n // move freely within the page bounds.\n const { annotationRotation = 0, constraints } = this.config;\n const bbox = constraints?.boundingBox;\n if (annotationRotation !== 0 && bbox) {\n let aabbW: number;\n let aabbH: number;\n let offsetX: number;\n let offsetY: number;\n\n if (this.startRotationElement) {\n aabbW = this.startRotationElement.size.width;\n aabbH = this.startRotationElement.size.height;\n offsetX = this.startRotationElement.origin.x - this.startElement.origin.x;\n offsetY = this.startRotationElement.origin.y - this.startElement.origin.y;\n } else {\n const rad = Math.abs((annotationRotation * Math.PI) / 180);\n const cos = Math.abs(Math.cos(rad));\n const sin = Math.abs(Math.sin(rad));\n const w = position.size.width;\n const h = position.size.height;\n aabbW = w * cos + h * sin;\n aabbH = w * sin + h * cos;\n offsetX = (w - aabbW) / 2;\n offsetY = (h - aabbH) / 2;\n }\n\n let { x, y } = position.origin;\n x = Math.max(-offsetX, Math.min(x, bbox.width - aabbW - offsetX));\n y = Math.max(-offsetY, Math.min(y, bbox.height - aabbH - offsetY));\n\n return { origin: { x, y }, size: position.size };\n }\n\n return applyConstraints(position, constraints, this.config.maintainAspectRatio ?? false);\n }\n\n // ---------------------------------------------------------------------------\n // Private: rotation\n // ---------------------------------------------------------------------------\n\n /**\n * Calculate the angle from the center to a point in screen coordinates.\n */\n private calculateAngleFromMouse(clientX: number, clientY: number): number {\n if (!this.centerScreen) return this.initialRotation;\n\n const dx = clientX - this.centerScreen.x;\n const dy = clientY - this.centerScreen.y;\n\n // Dead zone: when the mouse is very close to the center, atan2 becomes\n // extremely sensitive to sub-pixel movements. Hold the last stable angle.\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < 10) return this.lastComputedRotation;\n\n // atan2 gives angle from +X axis; rotate +90° so 0° = top (north).\n // Subtract the page rotation offset to convert the screen-space angle\n // back to the page-space angle used by the annotation model.\n const pageRotOffset = (this.config.pageRotation ?? 0) * 90;\n const angleDeg = Math.atan2(dy, dx) * (180 / Math.PI) + 90 - pageRotOffset;\n\n return normalizeAngle(Math.round(angleDeg));\n }\n\n private applyRotationSnapping(angle: number): {\n angle: number;\n isSnapped: boolean;\n snapTarget?: number;\n } {\n const snapAngles = this.config.rotationSnapAngles ?? [0, 90, 180, 270];\n const threshold = this.config.rotationSnapThreshold ?? 4;\n const normalizedAngle = normalizeAngle(angle);\n\n for (const candidate of snapAngles) {\n const normalizedCandidate = normalizeAngle(candidate);\n const diff = Math.abs(normalizedAngle - normalizedCandidate);\n const minimalDiff = Math.min(diff, 360 - diff);\n if (minimalDiff <= threshold) {\n return {\n angle: normalizedCandidate,\n isSnapped: true,\n snapTarget: normalizedCandidate,\n };\n }\n }\n\n return { angle: normalizedAngle, isSnapped: false };\n }\n}\n","import { PointerEvent, useCallback, useEffect, useRef } from '@framework';\nimport {\n DragResizeConfig,\n DragResizeController,\n InteractionEvent,\n ResizeHandle,\n} from '../plugin-interaction-primitives';\n\nexport interface UseDragResizeOptions extends DragResizeConfig {\n onUpdate?: (event: InteractionEvent) => void;\n enabled?: boolean;\n}\n\nexport interface ResizeHandleEventProps {\n onPointerDown: (e: PointerEvent) => void;\n onPointerMove: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onPointerCancel: (e: PointerEvent) => void;\n onLostPointerCapture?: (e: PointerEvent) => void;\n}\n\nexport function useDragResize(options: UseDragResizeOptions) {\n const { onUpdate, enabled = true, ...config } = options;\n const controllerRef = useRef<DragResizeController | null>(null);\n const onUpdateRef = useRef<typeof onUpdate>(onUpdate);\n const activePointerIdRef = useRef<number | null>(null);\n const activeTargetRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n onUpdateRef.current = onUpdate;\n }, [onUpdate]);\n\n // Initialize or update controller\n useEffect(() => {\n if (!controllerRef.current) {\n controllerRef.current = new DragResizeController(config, (event) =>\n onUpdateRef.current?.(event),\n );\n } else {\n controllerRef.current.updateConfig(config);\n }\n }, [\n config.element,\n config.rotationCenter,\n config.rotationElement,\n config.constraints,\n config.maintainAspectRatio,\n config.pageRotation,\n config.annotationRotation,\n config.scale,\n config.vertices,\n ]);\n\n const endPointerSession = useCallback((pointerId?: number) => {\n const activePointerId = activePointerIdRef.current;\n const target = activeTargetRef.current;\n const id = pointerId ?? activePointerId;\n\n if (target && id !== null) {\n try {\n if (target.hasPointerCapture?.(id)) {\n target.releasePointerCapture?.(id);\n }\n } catch {\n // Ignore release failures when capture is already gone.\n }\n }\n\n activePointerIdRef.current = null;\n activeTargetRef.current = null;\n }, []);\n\n const startPointerSession = useCallback(\n (e: PointerEvent) => {\n // Defensive: if a previous interaction got stuck, close it before starting a new one.\n if (activePointerIdRef.current !== null && activePointerIdRef.current !== e.pointerId) {\n controllerRef.current?.end();\n endPointerSession(activePointerIdRef.current);\n }\n\n const target = e.currentTarget as HTMLElement;\n activePointerIdRef.current = e.pointerId;\n activeTargetRef.current = target;\n try {\n target.setPointerCapture(e.pointerId);\n } catch {\n // Ignore capture failures - global listeners still provide a fallback.\n }\n },\n [endPointerSession],\n );\n\n useEffect(() => {\n const eventTarget = globalThis as unknown as Window;\n const handleGlobalPointerEnd = (e: globalThis.PointerEvent) => {\n const activePointerId = activePointerIdRef.current;\n if (activePointerId === null || e.pointerId !== activePointerId) return;\n controllerRef.current?.end();\n endPointerSession(e.pointerId);\n };\n\n const handleWindowBlur = () => {\n if (activePointerIdRef.current === null) return;\n controllerRef.current?.end();\n endPointerSession();\n };\n\n eventTarget.addEventListener('pointerup', handleGlobalPointerEnd, true);\n eventTarget.addEventListener('pointercancel', handleGlobalPointerEnd, true);\n eventTarget.addEventListener('blur', handleWindowBlur, true);\n\n return () => {\n eventTarget.removeEventListener('pointerup', handleGlobalPointerEnd, true);\n eventTarget.removeEventListener('pointercancel', handleGlobalPointerEnd, true);\n eventTarget.removeEventListener('blur', handleWindowBlur, true);\n };\n }, [endPointerSession]);\n\n useEffect(() => {\n return () => {\n if (activePointerIdRef.current !== null) {\n controllerRef.current?.end();\n endPointerSession();\n }\n };\n }, [endPointerSession]);\n\n const handleDragStart = useCallback(\n (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n controllerRef.current?.startDrag(e.clientX, e.clientY);\n startPointerSession(e);\n },\n [enabled, startPointerSession],\n );\n\n const handleMove = useCallback(\n (e: PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const activePointerId = activePointerIdRef.current;\n if (activePointerId !== null && e.pointerId !== activePointerId) return;\n controllerRef.current?.move(e.clientX, e.clientY, e.buttons, e.shiftKey);\n\n // Extra guard for environments where pointerup gets swallowed.\n if (activePointerIdRef.current === e.pointerId && e.buttons === 0) {\n endPointerSession(e.pointerId);\n }\n },\n [endPointerSession],\n );\n\n const handleEndLike = useCallback(\n (e: PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const activePointerId = activePointerIdRef.current;\n if (activePointerId !== null && e.pointerId !== activePointerId) return;\n controllerRef.current?.end();\n endPointerSession(e.pointerId);\n },\n [endPointerSession],\n );\n\n const handleLostPointerCapture = useCallback(\n (e: PointerEvent) => {\n const activePointerId = activePointerIdRef.current;\n if (activePointerId === null || e.pointerId !== activePointerId) return;\n controllerRef.current?.end();\n endPointerSession(e.pointerId);\n },\n [endPointerSession],\n );\n\n const createResizeHandler = useCallback(\n (handle: ResizeHandle): ResizeHandleEventProps => ({\n onPointerDown: (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n controllerRef.current?.startResize(handle, e.clientX, e.clientY);\n startPointerSession(e);\n },\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }),\n [enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession],\n );\n\n const createVertexHandler = useCallback(\n (vertexIndex: number): ResizeHandleEventProps => ({\n onPointerDown: (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n controllerRef.current?.startVertexEdit(vertexIndex, e.clientX, e.clientY);\n startPointerSession(e);\n },\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }),\n [enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession],\n );\n\n const createRotationHandler = useCallback(\n (initialRotation: number = 0, orbitRadiusPx?: number): ResizeHandleEventProps => ({\n onPointerDown: (e: PointerEvent) => {\n if (!enabled) return;\n e.preventDefault();\n e.stopPropagation();\n // Use the handle's actual DOM center, not the raw click position.\n // This avoids up to handleSize/2 px error when the user clicks\n // near the edge of the handle circle, which would shift the\n // reverse-engineered center and distort angles near the center.\n const handleRect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const handleCenterX = handleRect.left + handleRect.width / 2;\n const handleCenterY = handleRect.top + handleRect.height / 2;\n controllerRef.current?.startRotation(\n handleCenterX,\n handleCenterY,\n initialRotation,\n orbitRadiusPx,\n );\n startPointerSession(e);\n },\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }),\n [enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession],\n );\n\n return {\n dragProps: enabled\n ? {\n onPointerDown: handleDragStart,\n onPointerMove: handleMove,\n onPointerUp: handleEndLike,\n onPointerCancel: handleEndLike,\n onLostPointerCapture: handleLostPointerCapture,\n }\n : {},\n createResizeProps: createResizeHandler,\n createVertexProps: createVertexHandler,\n createRotationProps: createRotationHandler,\n };\n}\n","import { Rect, Rotation } from '@embedpdf/models';\nimport { getCounterRotation } from '@embedpdf/utils';\nimport { ReactNode, CSSProperties, Fragment, useRef, useEffect } from '@framework';\n\ninterface CounterRotateProps {\n rect: Rect;\n rotation: Rotation;\n}\n\nexport interface MenuWrapperProps {\n style: CSSProperties;\n ref: (el: HTMLDivElement | null) => void;\n}\n\ninterface CounterRotateComponentProps extends CounterRotateProps {\n children: (props: {\n matrix: string;\n rect: Rect;\n menuWrapperProps: MenuWrapperProps;\n }) => ReactNode;\n}\n\nexport function CounterRotate({ children, ...props }: CounterRotateComponentProps) {\n const { rect, rotation } = props;\n const { matrix, width, height } = getCounterRotation(rect, rotation);\n const elementRef = useRef<HTMLDivElement | null>(null);\n\n // Use native event listeners with capture phase to prevent event propagation\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const handlePointerDown = (e: Event) => {\n // Stop propagation to prevent underlying layers from receiving the event\n e.stopPropagation();\n // DO NOT use e.preventDefault() here - it breaks click events on mobile/tablet!\n // preventDefault() stops the browser from generating click events from touch,\n // which makes buttons inside this container non-functional on touch devices.\n };\n\n const handleTouchStart = (e: Event) => {\n // Stop propagation to prevent underlying layers from receiving the event\n e.stopPropagation();\n // DO NOT use e.preventDefault() here - it breaks click events on mobile/tablet!\n // preventDefault() stops the browser from generating click events from touch,\n // which makes buttons inside this container non-functional on touch devices.\n };\n\n // Use capture phase to intercept before synthetic events\n element.addEventListener('pointerdown', handlePointerDown, { capture: true });\n element.addEventListener('touchstart', handleTouchStart, { capture: true, passive: true });\n\n return () => {\n element.removeEventListener('pointerdown', handlePointerDown, { capture: true });\n element.removeEventListener('touchstart', handleTouchStart, { capture: true });\n };\n }, []);\n\n const menuWrapperStyle: CSSProperties = {\n position: 'absolute',\n left: rect.origin.x,\n top: rect.origin.y,\n transform: matrix,\n transformOrigin: '0 0',\n width: width,\n height: height,\n pointerEvents: 'none',\n zIndex: 3,\n };\n\n const menuWrapperProps: MenuWrapperProps = {\n style: menuWrapperStyle,\n ref: (el: HTMLDivElement | null) => {\n elementRef.current = el;\n },\n };\n\n return (\n <Fragment>\n {children({\n menuWrapperProps,\n matrix,\n rect: {\n origin: { x: rect.origin.x, y: rect.origin.y },\n size: { width: width, height: height },\n },\n })}\n </Fragment>\n );\n}\n","import { useRef, useCallback, dblClickProp } from '@framework';\nimport type { PointerEvent } from '@framework';\n\ntype DoublePressOptions = {\n delay?: number; // ms between taps\n tolerancePx?: number; // spatial tolerance\n};\n\ntype DoubleHandler<T extends Element> = ((e: PointerEvent<T> | MouseEvent) => void) | undefined;\n\ntype DoubleProps<K extends string> = Partial<Record<K, (e: any) => void>> & {\n onPointerUp?: (e: any) => void;\n};\n\nexport function useDoublePressProps<\n T extends Element = Element,\n K extends string = typeof dblClickProp,\n>(\n onDouble?: DoubleHandler<T>,\n { delay = 300, tolerancePx = 18 }: DoublePressOptions = {},\n): DoubleProps<K> {\n const last = useRef({ t: 0, x: 0, y: 0 });\n\n const handlePointerUp = useCallback(\n (e: any) => {\n if (!onDouble) return;\n\n // Ignore mouse (it will use native dblclick),\n // and ignore non-primary pointers (multi-touch, etc.)\n if (e.pointerType === 'mouse' || e.isPrimary === false) return;\n\n const now = performance.now();\n const x = e.clientX as number;\n const y = e.clientY as number;\n\n const withinTime = now - last.current.t <= delay;\n const dx = x - last.current.x;\n const dy = y - last.current.y;\n const withinDist = dx * dx + dy * dy <= tolerancePx * tolerancePx;\n\n if (withinTime && withinDist) onDouble?.(e as PointerEvent<T>);\n\n last.current = { t: now, x, y };\n },\n [onDouble, delay, tolerancePx],\n );\n\n const handleDouble = useCallback(\n (e: any) => {\n onDouble?.(e);\n },\n [onDouble],\n );\n\n return onDouble\n ? ({\n // Computed property uses the framework’s name ('onDoubleClick' or 'onDblClick')\n [dblClickProp]: handleDouble,\n onPointerUpCapture: handlePointerUp,\n } as DoubleProps<K>)\n : {};\n}\n","import { useMemo, PointerEvent } from '@framework';\nimport type { CSSProperties } from '@framework';\nimport { useDragResize, UseDragResizeOptions } from './use-drag-resize';\nimport {\n describeResizeFromConfig,\n describeVerticesFromConfig,\n describeRotationFromConfig,\n type ResizeUI,\n type VertexUI,\n type RotationUI,\n} from '../plugin-interaction-primitives/utils';\n\nexport type HandleElementProps = {\n key?: string | number;\n style: CSSProperties;\n onPointerDown: (e: PointerEvent) => void;\n onPointerMove: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onPointerCancel: (e: PointerEvent) => void;\n} & Record<string, any>;\n\nexport type RotationHandleProps = {\n /** Props for the rotation handle element */\n handle: HandleElementProps;\n /** Props for the connector line element (if shown) */\n connector: {\n style: CSSProperties;\n } & Record<string, any>;\n};\n\nexport function useInteractionHandles(opts: {\n controller: UseDragResizeOptions; // SINGLE config (rect/scale/rotation/vertices/…)\n resizeUI?: ResizeUI; // purely visual knobs\n vertexUI?: VertexUI; // purely visual knobs\n rotationUI?: RotationUI; // purely visual knobs for rotation handle\n includeVertices?: boolean; // default false\n includeRotation?: boolean; // default false\n /** Current rotation angle of the annotation (for initializing rotation interaction) */\n currentRotation?: number;\n handleAttrs?: (\n h: 'nw' | 'ne' | 'sw' | 'se' | 'n' | 'e' | 's' | 'w',\n ) => Record<string, any> | void;\n vertexAttrs?: (i: number) => Record<string, any> | void;\n rotationAttrs?: () => Record<string, any> | void;\n}) {\n const {\n controller,\n resizeUI,\n vertexUI,\n rotationUI,\n includeVertices = false,\n includeRotation = false,\n currentRotation = 0,\n handleAttrs,\n vertexAttrs,\n rotationAttrs,\n } = opts;\n\n const { dragProps, createResizeProps, createVertexProps, createRotationProps } =\n useDragResize(controller);\n\n // Resize handles: only uses data from the SAME controller config.\n const resize: HandleElementProps[] = useMemo(() => {\n const desc = describeResizeFromConfig(controller, resizeUI);\n return desc.map((d) => ({\n key: d.attrs?.['data-epdf-handle'] as string,\n style: d.style as CSSProperties,\n ...createResizeProps(d.handle),\n ...(d.attrs ?? {}),\n ...(handleAttrs?.(d.handle) ?? {}),\n }));\n // deps: controller geometry knobs + UI knobs + handler factory\n }, [\n controller.element.origin.x,\n controller.element.origin.y,\n controller.element.size.width,\n controller.element.size.height,\n controller.scale,\n controller.pageRotation,\n controller.annotationRotation,\n controller.maintainAspectRatio,\n resizeUI?.handleSize,\n resizeUI?.spacing,\n resizeUI?.offsetMode,\n resizeUI?.includeSides,\n resizeUI?.zIndex,\n resizeUI?.rotationAwareCursor,\n createResizeProps,\n handleAttrs,\n ]);\n\n // Vertex handles: same source; prefer live vertices if parent rerenders with updated cfg.vertices\n const vertices: HandleElementProps[] = useMemo(() => {\n if (!includeVertices) return [];\n const desc = describeVerticesFromConfig(controller, vertexUI, controller.vertices);\n return desc.map((d, i) => ({\n key: i,\n style: d.style as CSSProperties,\n ...createVertexProps(i),\n ...(d.attrs ?? {}),\n ...(vertexAttrs?.(i) ?? {}),\n }));\n }, [\n includeVertices,\n controller.element.origin.x,\n controller.element.origin.y,\n controller.element.size.width,\n controller.element.size.height,\n controller.scale,\n controller.vertices, // identity/content drives recalculation\n vertexUI?.vertexSize,\n vertexUI?.zIndex,\n createVertexProps,\n vertexAttrs,\n ]);\n\n // Rotation handle: orbits around the center of the element based on current angle\n const rotation: RotationHandleProps | null = useMemo(() => {\n if (!includeRotation) return null;\n // Pass the current rotation angle so the handle is positioned correctly\n const desc = describeRotationFromConfig(controller, rotationUI, currentRotation);\n return {\n handle: {\n style: desc.handleStyle as CSSProperties,\n ...createRotationProps(currentRotation, desc.radius),\n ...(desc.attrs ?? {}),\n ...(rotationAttrs?.() ?? {}),\n },\n connector: {\n style: desc.connectorStyle as CSSProperties,\n 'data-epdf-rotation-connector': true,\n },\n };\n }, [\n includeRotation,\n controller.element.origin.x,\n controller.element.origin.y,\n controller.element.size.width,\n controller.element.size.height,\n controller.rotationCenter?.x,\n controller.rotationCenter?.y,\n controller.rotationElement?.origin.x,\n controller.rotationElement?.origin.y,\n controller.rotationElement?.size.width,\n controller.rotationElement?.size.height,\n controller.scale,\n currentRotation,\n rotationUI?.handleSize,\n rotationUI?.margin,\n rotationUI?.zIndex,\n rotationUI?.showConnector,\n rotationUI?.connectorWidth,\n createRotationProps,\n rotationAttrs,\n ]);\n\n return { dragProps, resize, vertices, rotation };\n}\n"],"names":["dblClickProp","applyConstraints","position","constraints","maintainAspectRatio","skipBoundingClamp","origin","x","y","size","width","height","minW","minWidth","minH","minHeight","maxW","maxWidth","maxH","maxHeight","ratio","Math","max","min","boundingBox","isRectWithinRotatedBounds","rect","angleDegrees","bbox","eps","aabb","calculateRotatedRectAABB","computeResizeStep","delta","handle","config","clampLocalBounds","skipConstraintBoundingClamp","startRect","annotationRotation","anchor","includes","getAnchor","aspectRatio","applyResizeDelta","dw","abs","dh","total","wWeight","hWeight","hFromW","enforceAspectRatio","anchorX","anchorY","scaleW","scaleH","scale","clampToBounds","reanchorRect","anchorPt","getAnchorPoint","oldCenter","newCenter","oldVisual","rotatePointAround","newVisual","HANDLE_BASE_ANGLE","n","ne","e","se","s","sw","w","nw","SECTOR_CURSORS","diagonalCursor","pageQuarterTurns","normalized","sector","round","edgeOffset","k","spacing","mode","base","DragResizeController","constructor","onUpdate","this","state","startPoint","startElement","startRotationElement","gestureRotationCenter","activeHandle","currentPosition","activeVertexIndex","startVertices","currentVertices","rotationCenter","centerScreen","initialRotation","lastComputedRotation","rotationDelta","rotationSnappedAngle","vertices","updateConfig","startDrag","clientX","clientY","element","rotationElement","transformData","type","changes","startResize","metadata","startVertexEdit","vertexIndex","length","startRotation","orbitRadiusPx","orbitRect","sh","radius","screenAngleRad","pageRotation","PI","sin","cos","rotation","rotationAngle","isSnapped","move","buttons","lockAspectRatio","calculateDelta","calculateDragPosition","target","best","low","high","i","mid","trial","computeResizedRect","calculateLocalDelta","calculateVertexPosition","absoluteAngle","calculateAngleFromMouse","snapResult","applyRotationSnapping","snappedAngle","normalizeAngle","angle","rawDelta","adjustedDelta","cursorPosition","end","wasState","finalPosition","reset","cancel","transformDelta","rad","scaledX","scaledY","pageDelta","clampPoint","p","_a","center","visual","clampedX","clampedY","newVertices","currentVertex","moved","aabbW","aabbH","offsetX","offsetY","h","dx","dy","sqrt","pageRotOffset","angleDeg","atan2","snapAngles","rotationSnapAngles","threshold","rotationSnapThreshold","normalizedAngle","candidate","normalizedCandidate","diff","snapTarget","useDragResize","options","enabled","controllerRef","useRef","onUpdateRef","activePointerIdRef","activeTargetRef","useEffect","current","event","call","endPointerSession","useCallback","pointerId","activePointerId","id","hasPointerCapture","_b","releasePointerCapture","startPointerSession","currentTarget","setPointerCapture","eventTarget","globalThis","handleGlobalPointerEnd","handleWindowBlur","addEventListener","removeEventListener","handleDragStart","preventDefault","stopPropagation","handleMove","shiftKey","handleEndLike","handleLostPointerCapture","createResizeHandler","onPointerDown","onPointerMove","onPointerUp","onPointerCancel","onLostPointerCapture","createVertexHandler","createRotationHandler","handleRect","getBoundingClientRect","handleCenterX","left","handleCenterY","top","dragProps","createResizeProps","createVertexProps","createRotationProps","children","props","matrix","getCounterRotation","elementRef","handlePointerDown","handleTouchStart","capture","passive","menuWrapperProps","style","transform","transformOrigin","pointerEvents","zIndex","ref","el","Fragment","onDouble","delay","tolerancePx","last","t","handlePointerUp","pointerType","isPrimary","now","performance","withinTime","handleDouble","onPointerUpCapture","opts","controller","resizeUI","vertexUI","rotationUI","includeVertices","includeRotation","currentRotation","handleAttrs","vertexAttrs","rotationAttrs","resize","useMemo","cfg","ui","handleSize","offsetMode","includeSides","rotationAwareCursor","annotationRot","off","edge","map","pos","borderRadius","cursor","touchAction","attrs","describeResizeFromConfig","d","key","liveVertices","vertexSize","v","describeVerticesFromConfig","desc","currentAngle","showConnector","connectorWidth","orbitCenter","centerX","centerY","angleRad","margin","handleStyle","connectorStyle","describeRotationFromConfig","connector","_c","_d","_e","_f"],"mappings":"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"}