{"version":3,"file":"index.cjs","sources":["../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/selectors.ts","../src/lib/utils.ts","../src/lib/selection-plugin.ts","../src/lib/handlers/text-selection.handler.ts","../src/lib/handlers/marquee-selection.handler.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { SelectionPluginConfig } from './types';\n\nexport const SELECTION_PLUGIN_ID = 'selection';\n\nexport const manifest: PluginManifest<SelectionPluginConfig> = {\n  id: SELECTION_PLUGIN_ID,\n  name: 'Selection Plugin',\n  version: '1.0.0',\n  provides: ['selection'],\n  requires: ['interaction-manager'],\n  optional: ['viewport', 'scroll'],\n  defaultConfig: {\n    menuHeight: 40,\n  },\n};\n","import { Action } from '@embedpdf/core';\nimport { PdfPageGeometry, Rect } from '@embedpdf/models';\nimport { SelectionDocumentState, SelectionRangeX } from './types';\n\nexport const INIT_SELECTION_STATE = 'SELECTION/INIT_STATE';\nexport const CLEANUP_SELECTION_STATE = 'SELECTION/CLEANUP_STATE';\nexport const CACHE_PAGE_GEOMETRY = 'SELECTION/CACHE_PAGE_GEOMETRY';\nexport const SET_SELECTION = 'SELECTION/SET_SELECTION';\nexport const START_SELECTION = 'SELECTION/START_SELECTION';\nexport const END_SELECTION = 'SELECTION/END_SELECTION';\nexport const CLEAR_SELECTION = 'SELECTION/CLEAR_SELECTION';\nexport const SET_RECTS = 'SELECTION/SET_RECTS';\nexport const SET_SLICES = 'SELECTION/SET_SLICES';\nexport const EVICT_PAGE_GEOMETRY = 'SELECTION/EVICT_PAGE_GEOMETRY';\nexport const RESET = 'SELECTION/RESET'; // This might be obsolete, but we'll keep it for now\n\nexport interface InitSelectionStateAction extends Action {\n  type: typeof INIT_SELECTION_STATE;\n  payload: {\n    documentId: string;\n    state: SelectionDocumentState;\n  };\n}\n\nexport interface CleanupSelectionStateAction extends Action {\n  type: typeof CLEANUP_SELECTION_STATE;\n  payload: string; // documentId\n}\n\nexport interface CachePageGeometryAction extends Action {\n  type: typeof CACHE_PAGE_GEOMETRY;\n  payload: { documentId: string; page: number; geo: PdfPageGeometry };\n}\nexport interface SetSelectionAction extends Action {\n  type: typeof SET_SELECTION;\n  payload: { documentId: string; selection: SelectionRangeX | null };\n}\n\nexport interface StartSelectionAction extends Action {\n  type: typeof START_SELECTION;\n  payload: { documentId: string };\n}\n\nexport interface EndSelectionAction extends Action {\n  type: typeof END_SELECTION;\n  payload: { documentId: string };\n}\n\nexport interface ClearSelectionAction extends Action {\n  type: typeof CLEAR_SELECTION;\n  payload: { documentId: string };\n}\n\nexport interface SetRectsAction extends Action {\n  type: typeof SET_RECTS;\n  payload: { documentId: string; rects: Record<number, Rect[]> };\n}\n\nexport interface SetSlicesAction extends Action {\n  type: typeof SET_SLICES;\n  payload: { documentId: string; slices: Record<number, { start: number; count: number }> };\n}\n\nexport interface EvictPageGeometryAction extends Action {\n  type: typeof EVICT_PAGE_GEOMETRY;\n  payload: { documentId: string; pages: number[] };\n}\n\nexport interface ResetAction extends Action {\n  type: typeof RESET;\n  payload: { documentId: string };\n}\n\nexport type SelectionAction =\n  | InitSelectionStateAction\n  | CleanupSelectionStateAction\n  | CachePageGeometryAction\n  | SetSelectionAction\n  | StartSelectionAction\n  | EndSelectionAction\n  | ClearSelectionAction\n  | SetRectsAction\n  | SetSlicesAction\n  | EvictPageGeometryAction\n  | ResetAction;\n\nexport const initSelectionState = (\n  documentId: string,\n  state: SelectionDocumentState,\n): InitSelectionStateAction => ({\n  type: INIT_SELECTION_STATE,\n  payload: { documentId, state },\n});\n\nexport const cleanupSelectionState = (documentId: string): CleanupSelectionStateAction => ({\n  type: CLEANUP_SELECTION_STATE,\n  payload: documentId,\n});\n\nexport const cachePageGeometry = (\n  documentId: string,\n  page: number,\n  geo: PdfPageGeometry,\n): CachePageGeometryAction => ({\n  type: CACHE_PAGE_GEOMETRY,\n  payload: { documentId, page, geo },\n});\n\nexport const setSelection = (\n  documentId: string,\n  sel: SelectionRangeX | null,\n): SetSelectionAction => ({\n  type: SET_SELECTION,\n  payload: { documentId, selection: sel },\n});\n\nexport const startSelection = (documentId: string): StartSelectionAction => ({\n  type: START_SELECTION,\n  payload: { documentId },\n});\n\nexport const endSelection = (documentId: string): EndSelectionAction => ({\n  type: END_SELECTION,\n  payload: { documentId },\n});\n\nexport const clearSelection = (documentId: string): ClearSelectionAction => ({\n  type: CLEAR_SELECTION,\n  payload: { documentId },\n});\n\nexport const setRects = (documentId: string, allRects: Record<number, Rect[]>): SetRectsAction => ({\n  type: SET_RECTS,\n  payload: { documentId, rects: allRects },\n});\n\nexport const setSlices = (\n  documentId: string,\n  slices: Record<number, { start: number; count: number }>,\n): SetSlicesAction => ({ type: SET_SLICES, payload: { documentId, slices } });\n\nexport const evictPageGeometry = (\n  documentId: string,\n  pages: number[],\n): EvictPageGeometryAction => ({\n  type: EVICT_PAGE_GEOMETRY,\n  payload: { documentId, pages },\n});\n\nexport const reset = (documentId: string): ResetAction => ({\n  type: RESET,\n  payload: { documentId },\n});\n","import { SelectionDocumentState, SelectionState } from './types';\nimport {\n  SelectionAction,\n  CACHE_PAGE_GEOMETRY,\n  SET_SELECTION,\n  START_SELECTION,\n  END_SELECTION,\n  CLEAR_SELECTION,\n  RESET,\n  SET_SLICES,\n  SET_RECTS,\n  INIT_SELECTION_STATE,\n  CLEANUP_SELECTION_STATE,\n  EVICT_PAGE_GEOMETRY,\n} from './actions';\n\nexport const initialSelectionDocumentState: SelectionDocumentState = {\n  geometry: {},\n  rects: {},\n  slices: {},\n  selection: null,\n  active: false,\n  selecting: false,\n};\n\nexport const initialState: SelectionState = {\n  documents: {},\n};\n\nconst updateDocState = (\n  state: SelectionState,\n  documentId: string,\n  newDocState: SelectionDocumentState,\n): SelectionState => ({\n  ...state,\n  documents: {\n    ...state.documents,\n    [documentId]: newDocState,\n  },\n});\n\nexport const selectionReducer = (state = initialState, action: SelectionAction): SelectionState => {\n  switch (action.type) {\n    case INIT_SELECTION_STATE: {\n      const { documentId, state: docState } = action.payload;\n      return updateDocState(state, documentId, docState);\n    }\n\n    case CLEANUP_SELECTION_STATE: {\n      const documentId = action.payload;\n      const { [documentId]: removed, ...remaining } = state.documents;\n      return {\n        ...state,\n        documents: remaining,\n      };\n    }\n\n    case CACHE_PAGE_GEOMETRY: {\n      const { documentId, page, geo } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, {\n        ...docState,\n        geometry: { ...docState.geometry, [page]: geo },\n      });\n    }\n\n    case SET_SELECTION: {\n      const { documentId, selection } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, {\n        ...docState,\n        selection,\n        active: true,\n      });\n    }\n\n    case START_SELECTION: {\n      const { documentId } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, {\n        ...docState,\n        selecting: true,\n        selection: null,\n        rects: {},\n      });\n    }\n\n    case END_SELECTION: {\n      const { documentId } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, { ...docState, selecting: false });\n    }\n\n    case CLEAR_SELECTION: {\n      const { documentId } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, {\n        ...docState,\n        selecting: false,\n        selection: null,\n        rects: {},\n        active: false,\n      });\n    }\n\n    case SET_RECTS: {\n      const { documentId, rects } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, { ...docState, rects });\n    }\n\n    case SET_SLICES: {\n      const { documentId, slices } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, { ...docState, slices });\n    }\n\n    case EVICT_PAGE_GEOMETRY: {\n      const { documentId, pages } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      const geometry = { ...docState.geometry };\n      const rects = { ...docState.rects };\n      const slices = { ...docState.slices };\n      for (const p of pages) {\n        delete geometry[p];\n        delete rects[p];\n        delete slices[p];\n      }\n      return updateDocState(state, documentId, { ...docState, geometry, rects, slices });\n    }\n\n    case RESET: {\n      const { documentId } = action.payload;\n      const docState = state.documents[documentId];\n      if (!docState) return state;\n      return updateDocState(state, documentId, initialSelectionDocumentState);\n    }\n\n    default:\n      return state;\n  }\n};\n","import { Rect, boundingRect } from '@embedpdf/models';\nimport { FormattedSelection, SelectionDocumentState } from './types';\n\nexport function selectRectsForPage(state: SelectionDocumentState, page: number) {\n  return state.rects[page] ?? [];\n}\n\nexport function selectBoundingRectForPage(state: SelectionDocumentState, page: number) {\n  return boundingRect(selectRectsForPage(state, page));\n}\n\nexport function selectRectsAndBoundingRectForPage(state: SelectionDocumentState, page: number) {\n  return {\n    rects: selectRectsForPage(state, page),\n    boundingRect: selectBoundingRectForPage(state, page),\n  };\n}\n\nexport function selectBoundingRectsForAllPages(state: SelectionDocumentState) {\n  const out: { page: number; rect: Rect }[] = [];\n  const rectMap = state.rects;\n\n  for (const key in rectMap) {\n    const page = Number(key);\n    const bRect = boundingRect(rectMap[page]);\n    if (bRect) out.push({ page, rect: bRect });\n  }\n  return out;\n}\n\nexport function getFormattedSelectionForPage(\n  state: SelectionDocumentState,\n  page: number,\n): FormattedSelection | null {\n  const segmentRects = state.rects[page] || [];\n  if (segmentRects.length === 0) return null;\n  const boundingRect = selectBoundingRectForPage(state, page);\n  if (!boundingRect) return null;\n  return { pageIndex: page, rect: boundingRect, segmentRects };\n}\n\nexport function getFormattedSelection(state: SelectionDocumentState) {\n  const result: FormattedSelection[] = [];\n\n  // Get all pages that have rects\n  const pages = Object.keys(state.rects).map(Number);\n\n  for (const pageIndex of pages) {\n    const segmentRects = state.rects[pageIndex] || [];\n\n    if (segmentRects.length === 0) continue;\n\n    // Calculate bounding rect for this page\n    const boundingRect = selectBoundingRectForPage(state, pageIndex);\n\n    if (boundingRect) {\n      result.push({\n        pageIndex,\n        rect: boundingRect,\n        segmentRects,\n      });\n    }\n  }\n\n  return result;\n}\n","import { PdfPageGeometry, PdfRun, Position, Rect } from '@embedpdf/models';\nimport { SelectionRangeX } from './types';\n\n/**\n * Hit-test helper using runs, with tolerance-based fallback.\n *\n * Adapted from PDFium's FPDFText_GetCharIndexAtPos / CPDF_TextPage::GetIndexAtPos:\n *  1. Exact match: return the glyph whose bounding box contains the point.\n *  2. Tolerance expansion: expand each glyph box by tolerance/2 on every side,\n *     then pick the closest glyph by Manhattan distance.\n *\n * @param geo - page geometry\n * @param pt - point in page coordinates\n * @param toleranceFactor - multiplied by average glyph height to derive the\n *                          tolerance radius. 0 disables the fallback.\n *                          Default 1.5 (see Chromium's pdfium-page.cc kTolerance).\n * @returns glyph index, or -1 if nothing was hit\n */\nexport function glyphAt(geo: PdfPageGeometry, pt: Position, toleranceFactor = 1.5): number {\n  // --- Pass 1: exact bounding-box match using tight bounds (char_box) ---\n  // Matches PDFium's GetIndexAtPos first-pass check at cpdf_textpage.cpp:494\n  for (const run of geo.runs) {\n    const inRun =\n      pt.y >= run.rect.y &&\n      pt.y <= run.rect.y + run.rect.height &&\n      pt.x >= run.rect.x &&\n      pt.x <= run.rect.x + run.rect.width;\n\n    if (!inRun) continue;\n\n    const rel = run.glyphs.findIndex((g) => {\n      const gx = g.tightX ?? g.x;\n      const gy = g.tightY ?? g.y;\n      const gw = g.tightWidth ?? g.width;\n      const gh = g.tightHeight ?? g.height;\n      return pt.x >= gx && pt.x <= gx + gw && pt.y >= gy && pt.y <= gy + gh;\n    });\n\n    if (rel !== -1) {\n      return run.charStart + rel;\n    }\n  }\n\n  if (toleranceFactor <= 0) return -1;\n\n  // --- Pass 2: tolerance-expanded match using tight bounds ---\n  // Matches PDFium's GetIndexAtPos tolerance pass at cpdf_textpage.cpp:502-520\n  const tolerance = computeTolerance(geo, toleranceFactor);\n  const halfTol = tolerance / 2;\n\n  let bestIndex = -1;\n  let bestDist = Infinity;\n\n  for (const run of geo.runs) {\n    if (\n      pt.y < run.rect.y - halfTol ||\n      pt.y > run.rect.y + run.rect.height + halfTol ||\n      pt.x < run.rect.x - halfTol ||\n      pt.x > run.rect.x + run.rect.width + halfTol\n    ) {\n      continue;\n    }\n\n    for (let i = 0; i < run.glyphs.length; i++) {\n      const g = run.glyphs[i];\n      if (g.flags === 2) continue;\n\n      const gx = g.tightX ?? g.x;\n      const gy = g.tightY ?? g.y;\n      const gw = g.tightWidth ?? g.width;\n      const gh = g.tightHeight ?? g.height;\n\n      const expandedLeft = gx - halfTol;\n      const expandedRight = gx + gw + halfTol;\n      const expandedTop = gy - halfTol;\n      const expandedBottom = gy + gh + halfTol;\n\n      if (\n        pt.x < expandedLeft ||\n        pt.x > expandedRight ||\n        pt.y < expandedTop ||\n        pt.y > expandedBottom\n      ) {\n        continue;\n      }\n\n      const curXdif = Math.min(Math.abs(pt.x - gx), Math.abs(pt.x - (gx + gw)));\n      const curYdif = Math.min(Math.abs(pt.y - gy), Math.abs(pt.y - (gy + gh)));\n      const dist = curXdif + curYdif;\n\n      if (dist < bestDist) {\n        bestDist = dist;\n        bestIndex = run.charStart + i;\n      }\n    }\n  }\n\n  return bestIndex;\n}\n\n/**\n * Derive a tolerance value from the average non-empty glyph height on the page.\n */\nfunction computeTolerance(geo: PdfPageGeometry, factor: number): number {\n  let totalHeight = 0;\n  let count = 0;\n\n  for (const run of geo.runs) {\n    for (const g of run.glyphs) {\n      if (g.flags === 2) continue;\n      totalHeight += g.height;\n      count++;\n    }\n  }\n\n  if (count === 0) return 0;\n  return (totalHeight / count) * factor;\n}\n\n/**\n * Helper: min/max glyph indices on `page` for current sel\n * @param sel - selection range\n * @param geo - page geometry\n * @param page - page index\n * @returns { from: number; to: number } | null\n */\nexport function sliceBounds(\n  sel: SelectionRangeX | null,\n  geo: PdfPageGeometry | undefined,\n  page: number,\n): { from: number; to: number } | null {\n  if (!sel || !geo) return null;\n  if (page < sel.start.page || page > sel.end.page) return null;\n\n  const from = page === sel.start.page ? sel.start.index : 0;\n\n  const lastRun = geo.runs[geo.runs.length - 1];\n  const lastCharOnPage = lastRun.charStart + lastRun.glyphs.length - 1;\n\n  const to = page === sel.end.page ? sel.end.index : lastCharOnPage;\n\n  return { from, to };\n}\n\n/**\n * Helper: build rects for a slice of the page\n * @param geo - page geometry\n * @param from - from index\n * @param to - to index\n * @param merge - whether to merge adjacent rects (default: true)\n * @returns rects\n */\nexport function rectsWithinSlice(\n  geo: PdfPageGeometry,\n  from: number,\n  to: number,\n  merge: boolean = true,\n): Rect[] {\n  const textRuns: TextRunInfo[] = [];\n\n  const CHAR_DISTANCE_FACTOR = 2.5;\n\n  for (const run of geo.runs) {\n    const runStart = run.charStart;\n    const runEnd = runStart + run.glyphs.length - 1;\n    if (runEnd < from || runStart > to) continue;\n\n    const sIdx = Math.max(from, runStart) - runStart;\n    const eIdx = Math.min(to, runEnd) - runStart;\n\n    let minX = Infinity,\n      maxX = -Infinity;\n    let minY = Infinity,\n      maxY = -Infinity;\n    let charCount = 0;\n    let widthSum = 0;\n    let prevRight = -Infinity;\n\n    const flushSubRun = () => {\n      if (minX !== Infinity && charCount > 0) {\n        textRuns.push({\n          rect: {\n            origin: { x: minX, y: minY },\n            size: { width: maxX - minX, height: maxY - minY },\n          },\n          charCount,\n          fontSize: run.fontSize,\n        });\n      }\n      minX = Infinity;\n      maxX = -Infinity;\n      minY = Infinity;\n      maxY = -Infinity;\n      charCount = 0;\n      widthSum = 0;\n      prevRight = -Infinity;\n    };\n\n    for (let i = sIdx; i <= eIdx; i++) {\n      const g = run.glyphs[i];\n      if (g.flags === 2) continue;\n\n      if (charCount > 0 && prevRight > -Infinity) {\n        const gap = Math.abs(g.x - prevRight);\n        const avgWidth = widthSum / charCount;\n        if (avgWidth > 0 && gap > CHAR_DISTANCE_FACTOR * avgWidth) {\n          flushSubRun();\n        }\n      }\n\n      minX = Math.min(minX, g.x);\n      maxX = Math.max(maxX, g.x + g.width);\n      minY = Math.min(minY, g.y);\n      maxY = Math.max(maxY, g.y + g.height);\n\n      charCount++;\n      widthSum += g.width;\n      prevRight = g.x + g.width;\n    }\n\n    flushSubRun();\n  }\n\n  // If merge is false, just return the individual rects\n  if (!merge) {\n    return textRuns.map((run) => run.rect);\n  }\n\n  // Otherwise merge adjacent rects\n  return mergeAdjacentRects(textRuns);\n}\n\n/**\n * ============================================================================\n * Rectangle Merging Algorithm\n * ============================================================================\n *\n * The following code is adapted from Chromium's PDF text selection implementation.\n *\n * Copyright 2010 The Chromium Authors\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file: https://source.chromium.org/chromium/chromium/src/+/main:LICENSE\n *\n * Original source:\n * https://source.chromium.org/chromium/chromium/src/+/main:pdf/pdfium/pdfium_range.cc\n *\n * Adapted for TypeScript and this project's Rect/geometry types.\n */\n\n/**\n * Text run info for rect merging (similar to Chromium's PdfRectTextRunInfo)\n */\nexport interface TextRunInfo {\n  rect: Rect;\n  charCount: number;\n  fontSize?: number;\n}\n\n/**\n * Helper functions for Rect operations\n */\nexport function rectUnion(rect1: Rect, rect2: Rect): Rect {\n  const left = Math.min(rect1.origin.x, rect2.origin.x);\n  const top = Math.min(rect1.origin.y, rect2.origin.y);\n  const right = Math.max(rect1.origin.x + rect1.size.width, rect2.origin.x + rect2.size.width);\n  const bottom = Math.max(rect1.origin.y + rect1.size.height, rect2.origin.y + rect2.size.height);\n\n  return {\n    origin: { x: left, y: top },\n    size: { width: right - left, height: bottom - top },\n  };\n}\n\nexport function rectIntersect(rect1: Rect, rect2: Rect): Rect {\n  const left = Math.max(rect1.origin.x, rect2.origin.x);\n  const top = Math.max(rect1.origin.y, rect2.origin.y);\n  const right = Math.min(rect1.origin.x + rect1.size.width, rect2.origin.x + rect2.size.width);\n  const bottom = Math.min(rect1.origin.y + rect1.size.height, rect2.origin.y + rect2.size.height);\n\n  const width = Math.max(0, right - left);\n  const height = Math.max(0, bottom - top);\n\n  return {\n    origin: { x: left, y: top },\n    size: { width, height },\n  };\n}\n\nexport function rectIsEmpty(rect: Rect): boolean {\n  return rect.size.width <= 0 || rect.size.height <= 0;\n}\n\n/**\n * Returns a ratio between [0, 1] representing vertical overlap\n */\nexport function getVerticalOverlap(rect1: Rect, rect2: Rect): number {\n  if (rectIsEmpty(rect1) || rectIsEmpty(rect2)) return 0;\n\n  const unionRect = rectUnion(rect1, rect2);\n\n  if (unionRect.size.height === rect1.size.height || unionRect.size.height === rect2.size.height) {\n    return 1.0;\n  }\n\n  const intersectRect = rectIntersect(rect1, rect2);\n  return intersectRect.size.height / unionRect.size.height;\n}\n\n/**\n * Returns true if there is sufficient horizontal and vertical overlap\n */\nexport function shouldMergeHorizontalRects(textRun1: TextRunInfo, textRun2: TextRunInfo): boolean {\n  const FONT_SIZE_RATIO_THRESHOLD = 1.5;\n  if (\n    textRun1.fontSize != null &&\n    textRun2.fontSize != null &&\n    textRun1.fontSize > 0 &&\n    textRun2.fontSize > 0\n  ) {\n    const ratio =\n      Math.max(textRun1.fontSize, textRun2.fontSize) /\n      Math.min(textRun1.fontSize, textRun2.fontSize);\n    if (ratio > FONT_SIZE_RATIO_THRESHOLD) {\n      return false;\n    }\n  }\n\n  const VERTICAL_OVERLAP_THRESHOLD = 0.8;\n  const rect1 = textRun1.rect;\n  const rect2 = textRun2.rect;\n\n  if (getVerticalOverlap(rect1, rect2) < VERTICAL_OVERLAP_THRESHOLD) {\n    return false;\n  }\n\n  const HORIZONTAL_WIDTH_FACTOR = 1.0;\n  const averageWidth1 = (HORIZONTAL_WIDTH_FACTOR * rect1.size.width) / textRun1.charCount;\n  const averageWidth2 = (HORIZONTAL_WIDTH_FACTOR * rect2.size.width) / textRun2.charCount;\n\n  const rect1Left = rect1.origin.x - averageWidth1;\n  const rect1Right = rect1.origin.x + rect1.size.width + averageWidth1;\n  const rect2Left = rect2.origin.x - averageWidth2;\n  const rect2Right = rect2.origin.x + rect2.size.width + averageWidth2;\n\n  return rect1Left < rect2Right && rect1Right > rect2Left;\n}\n\n/**\n * Merge adjacent rectangles based on proximity and overlap.\n *\n * Adapted from Chromium's MergeAdjacentRects (pdfium_range.cc):\n *  - The merge DECISION uses the loose `rect` (via shouldMergeHorizontalRects).\n *  - The OUTPUT rect always uses loose bounds.\n */\nexport function mergeAdjacentRects(textRuns: TextRunInfo[]): Rect[] {\n  const results: Rect[] = [];\n  let previousTextRun: TextRunInfo | null = null;\n  let currentRect: Rect | null = null;\n\n  for (const textRun of textRuns) {\n    if (previousTextRun && currentRect) {\n      if (shouldMergeHorizontalRects(previousTextRun, textRun)) {\n        currentRect = rectUnion(currentRect, textRun.rect);\n      } else {\n        results.push(currentRect);\n        currentRect = textRun.rect;\n      }\n    } else {\n      currentRect = textRun.rect;\n    }\n    previousTextRun = textRun;\n  }\n\n  if (currentRect && !rectIsEmpty(currentRect)) {\n    results.push(currentRect);\n  }\n\n  return results;\n}\n\n/**\n * ============================================================================\n * Word / Line Boundary Expansion\n * ============================================================================\n *\n * Adapted from Chromium's PDFiumEngine::OnMultipleClick (pdfium-engine.cc lines 1658-1694).\n *\n * Double-click: expand to word boundaries (spaces / empty glyphs).\n * Triple-click: expand to line boundaries (runs on the same visual row).\n */\n\nconst VERTICAL_OVERLAP_THRESHOLD_LINE = 0.5;\n\n/**\n * Resolve a character index to the run and local offset within that run.\n */\nfunction resolveCharIndex(\n  geo: PdfPageGeometry,\n  charIndex: number,\n): { runIdx: number; localIdx: number } | null {\n  for (let r = 0; r < geo.runs.length; r++) {\n    const run = geo.runs[r];\n    const localIdx = charIndex - run.charStart;\n    if (localIdx >= 0 && localIdx < run.glyphs.length) {\n      return { runIdx: r, localIdx };\n    }\n  }\n  return null;\n}\n\n/**\n * Check if a glyph acts as a word boundary (space or empty).\n * Mirrors PDFium's `IsWordBoundary(ch)` which returns true for whitespace/punctuation.\n */\nfunction isGlyphWordBoundary(flags: number): boolean {\n  return flags === 1 || flags === 2; // 1 = space, 2 = empty\n}\n\n/**\n * Expand a character index to the word surrounding it.\n *\n * Walks backward and forward from `charIndex` within the page geometry\n * until a word-boundary glyph (space or empty) is encountered.\n *\n * @returns `{ from, to }` inclusive indices, or null if charIndex is invalid.\n */\nexport function expandToWordBoundary(\n  geo: PdfPageGeometry,\n  charIndex: number,\n): { from: number; to: number } | null {\n  const resolved = resolveCharIndex(geo, charIndex);\n  if (!resolved) return null;\n\n  const totalChars = getTotalCharCount(geo);\n  if (totalChars === 0) return null;\n\n  // Walk backward\n  let from = charIndex;\n  while (from > 0) {\n    const prev = resolveCharIndex(geo, from - 1);\n    if (!prev) break;\n    if (isGlyphWordBoundary(geo.runs[prev.runIdx].glyphs[prev.localIdx].flags)) break;\n    from--;\n  }\n\n  // Walk forward\n  let to = charIndex;\n  while (to < totalChars - 1) {\n    const next = resolveCharIndex(geo, to + 1);\n    if (!next) break;\n    if (isGlyphWordBoundary(geo.runs[next.runIdx].glyphs[next.localIdx].flags)) break;\n    to++;\n  }\n\n  return { from, to };\n}\n\n/**\n * Expand a character index to the full visual line (row) it belongs to.\n *\n * Finds all runs whose vertical extent overlaps with the run containing `charIndex`,\n * then returns the first-to-last character span across those runs.\n *\n * @returns `{ from, to }` inclusive indices, or null if charIndex is invalid.\n */\nexport function expandToLineBoundary(\n  geo: PdfPageGeometry,\n  charIndex: number,\n): { from: number; to: number } | null {\n  const resolved = resolveCharIndex(geo, charIndex);\n  if (!resolved) return null;\n\n  const anchorRun = geo.runs[resolved.runIdx];\n  const anchorTop = anchorRun.rect.y;\n  const anchorBottom = anchorRun.rect.y + anchorRun.rect.height;\n\n  let from = anchorRun.charStart;\n  let to = anchorRun.charStart + anchorRun.glyphs.length - 1;\n\n  // Expand backward through runs on the same visual row\n  for (let r = resolved.runIdx - 1; r >= 0; r--) {\n    const run = geo.runs[r];\n    if (isZeroSizeRun(run)) continue;\n    if (!runsOverlapVertically(run.rect.y, run.rect.y + run.rect.height, anchorTop, anchorBottom)) {\n      break;\n    }\n    from = run.charStart;\n  }\n\n  // Expand forward through runs on the same visual row\n  for (let r = resolved.runIdx + 1; r < geo.runs.length; r++) {\n    const run = geo.runs[r];\n    if (isZeroSizeRun(run)) continue;\n    if (!runsOverlapVertically(run.rect.y, run.rect.y + run.rect.height, anchorTop, anchorBottom)) {\n      break;\n    }\n    to = run.charStart + run.glyphs.length - 1;\n  }\n\n  return { from, to };\n}\n\nfunction isZeroSizeRun(run: PdfRun): boolean {\n  return run.rect.width === 0 && run.rect.height === 0;\n}\n\nfunction runsOverlapVertically(\n  top1: number,\n  bottom1: number,\n  top2: number,\n  bottom2: number,\n): boolean {\n  const unionHeight = Math.max(bottom1, bottom2) - Math.min(top1, top2);\n  const intersectHeight = Math.max(0, Math.min(bottom1, bottom2) - Math.max(top1, top2));\n  if (unionHeight === 0) return false;\n  return intersectHeight / unionHeight >= VERTICAL_OVERLAP_THRESHOLD_LINE;\n}\n\nfunction getTotalCharCount(geo: PdfPageGeometry): number {\n  if (geo.runs.length === 0) return 0;\n  const lastRun = geo.runs[geo.runs.length - 1];\n  return lastRun.charStart + lastRun.glyphs.length;\n}\n","import {\n  BasePlugin,\n  Listener,\n  PluginRegistry,\n  REFRESH_PAGES,\n  createScopedEmitter,\n} from '@embedpdf/core';\nimport {\n  PdfPageGeometry,\n  Rect,\n  PdfTask,\n  PdfTaskHelper,\n  PdfErrorCode,\n  PdfPermissionFlag,\n  ignore,\n  PageTextSlice,\n  Task,\n  Position,\n} from '@embedpdf/models';\nimport {\n  InteractionManagerCapability,\n  InteractionManagerPlugin,\n  PointerEventHandlersWithLifecycle,\n  EmbedPdfPointerEvent,\n} from '@embedpdf/plugin-interaction-manager';\nimport { ViewportCapability, ViewportMetrics, ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { ScrollCapability, ScrollPlugin } from '@embedpdf/plugin-scroll';\n\nimport {\n  cachePageGeometry,\n  evictPageGeometry,\n  setSelection,\n  SelectionAction,\n  endSelection,\n  startSelection,\n  clearSelection,\n  setRects,\n  setSlices,\n  initSelectionState,\n  cleanupSelectionState,\n} from './actions';\nimport { initialSelectionDocumentState } from './reducer';\nimport * as selector from './selectors';\nimport {\n  SelectionCapability,\n  SelectionPluginConfig,\n  SelectionRangeX,\n  SelectionState,\n  RegisterSelectionOnPageOptions,\n  RegisterMarqueeOnPageOptions,\n  SelectionRectsCallback,\n  SelectionScope,\n  SelectionChangeEvent,\n  TextRetrievedEvent,\n  CopyToClipboardEvent,\n  BeginSelectionEvent,\n  EndSelectionEvent,\n  SelectionDocumentState,\n  SelectionMenuPlacement,\n  SelectionMenuPlacementEvent,\n  EnableForModeOptions,\n  MarqueeChangeEvent,\n  MarqueeEndEvent,\n  MarqueeScopeEvent,\n  MarqueeEndScopeEvent,\n  EmptySpaceClickEvent,\n  EmptySpaceClickScopeEvent,\n} from './types';\nimport { sliceBounds, rectsWithinSlice, expandToWordBoundary, expandToLineBoundary } from './utils';\nimport { createTextSelectionHandler } from './handlers/text-selection.handler';\nimport { createMarqueeSelectionHandler } from './handlers/marquee-selection.handler';\n\nexport class SelectionPlugin extends BasePlugin<\n  SelectionPluginConfig,\n  SelectionCapability,\n  SelectionState,\n  SelectionAction\n> {\n  static readonly id = 'selection' as const;\n\n  /** Modes that should trigger text-selection logic, per document (mode -> config) */\n  private enabledModesPerDoc = new Map<string, Map<string, EnableForModeOptions>>();\n\n  /* interactive state, per document */\n  private selecting = new Map<string, boolean>();\n  private anchor = new Map<string, { page: number; index: number } | undefined>();\n\n  /** Whether the text handler has a pending anchor (before drag threshold is met) */\n  private hasTextAnchor = new Map<string, boolean>();\n\n  /** Tracks the page a marquee drag started on, per document */\n  private marqueePage = new Map<string, number>();\n\n  /** Page callbacks for rect updates, per document */\n  private pageCallbacks = new Map<string, Map<number, (data: SelectionRectsCallback) => void>>();\n\n  /** LRU access order for geometry cache, per document (oldest first) */\n  private geoAccessOrder = new Map<string, number[]>();\n\n  private readonly menuPlacement$ = createScopedEmitter<\n    SelectionMenuPlacement | null,\n    SelectionMenuPlacementEvent,\n    string\n  >((documentId, placement) => ({ documentId, placement }));\n  private readonly selChange$ = createScopedEmitter<\n    SelectionRangeX | null,\n    SelectionChangeEvent,\n    string\n  >((documentId, selection) => ({\n    documentId,\n    selection,\n    modeId: this.interactionManagerCapability.forDocument(documentId).getActiveMode(),\n  }));\n  private readonly textRetrieved$ = createScopedEmitter<string[], TextRetrievedEvent, string>(\n    (documentId, text) => ({ documentId, text }),\n  );\n  private readonly copyToClipboard$ = createScopedEmitter<string, CopyToClipboardEvent, string>(\n    (documentId, text) => ({ documentId, text }),\n    { cache: false },\n  );\n  private readonly beginSelection$ = createScopedEmitter<\n    { page: number; index: number; modeId: string },\n    BeginSelectionEvent,\n    string\n  >(\n    (documentId, data) => ({\n      documentId,\n      page: data.page,\n      index: data.index,\n      modeId: data.modeId,\n    }),\n    { cache: false },\n  );\n  private readonly endSelection$ = createScopedEmitter<\n    { modeId: string },\n    EndSelectionEvent,\n    string\n  >((documentId, data) => ({ documentId, modeId: data.modeId }), { cache: false });\n\n  // Marquee selection emitters\n  private readonly marqueeChange$ = createScopedEmitter<\n    MarqueeScopeEvent,\n    MarqueeChangeEvent,\n    string\n  >(\n    (documentId, data) => ({\n      documentId,\n      pageIndex: data.pageIndex,\n      rect: data.rect,\n      modeId: data.modeId,\n    }),\n    { cache: false },\n  );\n  private readonly marqueeEnd$ = createScopedEmitter<MarqueeEndScopeEvent, MarqueeEndEvent, string>(\n    (documentId, data) => ({\n      documentId,\n      pageIndex: data.pageIndex,\n      rect: data.rect,\n      modeId: data.modeId,\n    }),\n    { cache: false },\n  );\n  private readonly emptySpaceClick$ = createScopedEmitter<\n    EmptySpaceClickScopeEvent,\n    EmptySpaceClickEvent,\n    string\n  >(\n    (documentId, data) => ({\n      documentId,\n      pageIndex: data.pageIndex,\n      modeId: data.modeId,\n    }),\n    { cache: false },\n  );\n\n  private interactionManagerCapability: InteractionManagerCapability;\n  private viewportCapability: ViewportCapability | null = null;\n  private scrollCapability: ScrollCapability | null = null;\n\n  private readonly menuHeight: number;\n  private readonly config: SelectionPluginConfig;\n\n  constructor(id: string, registry: PluginRegistry, config: SelectionPluginConfig) {\n    super(id, registry);\n    this.config = config;\n    this.menuHeight = config.menuHeight ?? 40;\n\n    const imPlugin = registry.getPlugin<InteractionManagerPlugin>('interaction-manager');\n    if (!imPlugin) {\n      throw new Error('SelectionPlugin: InteractionManagerPlugin is required.');\n    }\n    this.interactionManagerCapability = imPlugin.provides();\n    this.viewportCapability = registry.getPlugin<ViewportPlugin>('viewport')?.provides() ?? null;\n    this.scrollCapability = registry.getPlugin<ScrollPlugin>('scroll')?.provides() ?? null;\n\n    this.coreStore.onAction(REFRESH_PAGES, (action) => {\n      const { documentId, pageIndexes } = action.payload;\n      const tasks = pageIndexes.map((pageIndex) =>\n        this.getNewPageGeometryAndCache(documentId, pageIndex),\n      );\n      Task.all(tasks).wait(() => {\n        // Notify affected pages about geometry updates\n        pageIndexes.forEach((pageIndex) => {\n          this.notifyPage(documentId, pageIndex);\n        });\n      }, ignore);\n    });\n\n    this.viewportCapability?.onViewportChange(\n      (event) => {\n        this.recalculateMenuPlacement(event.documentId);\n      },\n      { mode: 'throttle', wait: 100 },\n    );\n  }\n\n  /* ── life-cycle ────────────────────────────────────────── */\n  protected override onDocumentLoadingStarted(documentId: string): void {\n    this.dispatch(initSelectionState(documentId, initialSelectionDocumentState));\n    const marqueeEnabled = this.config.marquee?.enabled !== false;\n    this.enabledModesPerDoc.set(\n      documentId,\n      new Map<string, EnableForModeOptions>([\n        [\n          'pointerMode',\n          {\n            enableSelection: true,\n            showSelectionRects: true,\n            enableMarquee: marqueeEnabled,\n            showMarqueeRects: true,\n          },\n        ],\n      ]),\n    );\n    this.pageCallbacks.set(documentId, new Map());\n    this.geoAccessOrder.set(documentId, []);\n    this.selecting.set(documentId, false);\n    this.anchor.set(documentId, undefined);\n    this.hasTextAnchor.set(documentId, false);\n  }\n\n  protected override onDocumentClosed(documentId: string): void {\n    this.dispatch(cleanupSelectionState(documentId));\n    this.enabledModesPerDoc.delete(documentId);\n    this.pageCallbacks.delete(documentId);\n    this.geoAccessOrder.delete(documentId);\n    this.selecting.delete(documentId);\n    this.hasTextAnchor.delete(documentId);\n    this.anchor.delete(documentId);\n    this.marqueePage.delete(documentId);\n    this.selChange$.clearScope(documentId);\n    this.textRetrieved$.clearScope(documentId);\n    this.copyToClipboard$.clearScope(documentId);\n    this.beginSelection$.clearScope(documentId);\n    this.endSelection$.clearScope(documentId);\n    this.menuPlacement$.clearScope(documentId);\n    this.marqueeChange$.clearScope(documentId);\n    this.marqueeEnd$.clearScope(documentId);\n    this.emptySpaceClick$.clearScope(documentId);\n  }\n\n  async initialize() {}\n  async destroy() {\n    this.selChange$.clear();\n    this.textRetrieved$.clear();\n    this.copyToClipboard$.clear();\n    this.beginSelection$.clear();\n    this.endSelection$.clear();\n    this.menuPlacement$.clear();\n    this.marqueeChange$.clear();\n    this.marqueeEnd$.clear();\n    this.emptySpaceClick$.clear();\n    super.destroy();\n  }\n\n  /* ── capability exposed to UI / other plugins ─────────── */\n  buildCapability(): SelectionCapability {\n    const getDocId = (documentId?: string) => documentId ?? this.getActiveDocumentId();\n\n    return {\n      // Active document operations\n      getFormattedSelection: (docId) =>\n        selector.getFormattedSelection(this.getDocumentState(getDocId(docId))),\n      getFormattedSelectionForPage: (p, docId) =>\n        selector.getFormattedSelectionForPage(this.getDocumentState(getDocId(docId)), p),\n      getHighlightRectsForPage: (p, docId) =>\n        selector.selectRectsForPage(this.getDocumentState(getDocId(docId)), p),\n      getHighlightRects: (docId) => this.getDocumentState(getDocId(docId)).rects,\n      getBoundingRectForPage: (p, docId) =>\n        selector.selectBoundingRectForPage(this.getDocumentState(getDocId(docId)), p),\n      getBoundingRects: (docId) =>\n        selector.selectBoundingRectsForAllPages(this.getDocumentState(getDocId(docId))),\n      getSelectedText: (docId) => this.getSelectedText(getDocId(docId)),\n      clear: (docId) => this.clearSelection(getDocId(docId)),\n      copyToClipboard: (docId) => this.copyToClipboard(getDocId(docId)),\n      getState: (docId) => this.getDocumentState(getDocId(docId)),\n      enableForMode: (modeId, options, docId) =>\n        this.enabledModesPerDoc.get(getDocId(docId))?.set(modeId, { ...options }),\n      isEnabledForMode: (modeId, docId) =>\n        this.enabledModesPerDoc.get(getDocId(docId))?.has(modeId) ?? false,\n      setMarqueeEnabled: (enabled, docId) => this.setMarqueeEnabled(getDocId(docId), enabled),\n      isMarqueeEnabled: (docId) => this.isMarqueeEnabled(getDocId(docId)),\n\n      // Document-scoped operations\n      forDocument: this.createSelectionScope.bind(this),\n\n      // Global events\n      onCopyToClipboard: this.copyToClipboard$.onGlobal,\n      onSelectionChange: this.selChange$.onGlobal,\n      onTextRetrieved: this.textRetrieved$.onGlobal,\n      onBeginSelection: this.beginSelection$.onGlobal,\n      onEndSelection: this.endSelection$.onGlobal,\n\n      // Marquee selection events\n      onMarqueeChange: this.marqueeChange$.onGlobal,\n      onMarqueeEnd: this.marqueeEnd$.onGlobal,\n\n      // Empty space click event\n      onEmptySpaceClick: this.emptySpaceClick$.onGlobal,\n    };\n  }\n\n  private createSelectionScope(documentId: string): SelectionScope {\n    return {\n      getFormattedSelection: () =>\n        selector.getFormattedSelection(this.getDocumentState(documentId)),\n      getFormattedSelectionForPage: (p) =>\n        selector.getFormattedSelectionForPage(this.getDocumentState(documentId), p),\n      getHighlightRectsForPage: (p) =>\n        selector.selectRectsForPage(this.getDocumentState(documentId), p),\n      getHighlightRects: () => this.getDocumentState(documentId).rects,\n      getBoundingRectForPage: (p) =>\n        selector.selectBoundingRectForPage(this.getDocumentState(documentId), p),\n      getBoundingRects: () =>\n        selector.selectBoundingRectsForAllPages(this.getDocumentState(documentId)),\n      getSelectedText: () => this.getSelectedText(documentId),\n      clear: () => this.clearSelection(documentId),\n      copyToClipboard: () => this.copyToClipboard(documentId),\n      getState: () => this.getDocumentState(documentId),\n      setMarqueeEnabled: (enabled) => this.setMarqueeEnabled(documentId, enabled),\n      isMarqueeEnabled: () => this.isMarqueeEnabled(documentId),\n      onSelectionChange: this.selChange$.forScope(documentId),\n      onTextRetrieved: this.textRetrieved$.forScope(documentId),\n      onCopyToClipboard: this.copyToClipboard$.forScope(documentId),\n      onBeginSelection: this.beginSelection$.forScope(documentId),\n      onEndSelection: this.endSelection$.forScope(documentId),\n      onMarqueeChange: this.marqueeChange$.forScope(documentId),\n      onMarqueeEnd: this.marqueeEnd$.forScope(documentId),\n      onEmptySpaceClick: this.emptySpaceClick$.forScope(documentId),\n    };\n  }\n\n  private getDocumentState(documentId: string): SelectionDocumentState {\n    const state = this.state.documents[documentId];\n    if (!state) {\n      throw new Error(`Selection state not found for document: ${documentId}`);\n    }\n    return state;\n  }\n\n  /**\n   * Subscribe to menu placement changes for a specific document\n   * @param documentId - The document ID to subscribe to\n   * @param listener - Callback function that receives placement updates\n   * @returns Unsubscribe function\n   */\n  public onMenuPlacement(\n    documentId: string,\n    listener: (placement: SelectionMenuPlacement | null) => void,\n  ) {\n    return this.menuPlacement$.forScope(documentId)(listener);\n  }\n\n  /**\n   * Register text selection on a page. Uses `registerAlways` so any plugin\n   * can enable text selection for their mode via `enableForMode()`.\n   */\n  public registerSelectionOnPage(opts: RegisterSelectionOnPageOptions) {\n    const { documentId, pageIndex, onRectsChange } = opts;\n    const docState = this.state.documents[documentId];\n\n    if (!docState) {\n      this.logger.warn(\n        'SelectionPlugin',\n        'RegisterFailed',\n        `Cannot register selection on page ${pageIndex} for document ${documentId}: document state not initialized.`,\n      );\n      return () => {};\n    }\n\n    // Track this callback for the page\n    this.pageCallbacks.get(documentId)?.set(pageIndex, onRectsChange);\n\n    const geoTask = this.getOrLoadGeometry(documentId, pageIndex);\n    const interactionScope = this.interactionManagerCapability.forDocument(documentId);\n    const enabledModes = this.enabledModesPerDoc.get(documentId);\n\n    // Send initial state\n    onRectsChange({\n      rects: selector.selectRectsForPage(docState, pageIndex),\n      boundingRect: selector.selectBoundingRectForPage(docState, pageIndex),\n    });\n\n    // When geometry arrives (possibly re-fetched after eviction), recompute\n    // rects for this page if it falls within an active selection.\n    geoTask.wait((geo) => {\n      const currentState = this.getDocumentState(documentId);\n      const sel = currentState.selection;\n      if (!sel || pageIndex < sel.start.page || pageIndex > sel.end.page) return;\n\n      const sb = sliceBounds(sel, geo, pageIndex);\n      if (!sb) return;\n\n      const pageRects = rectsWithinSlice(geo, sb.from, sb.to);\n      this.dispatch(setRects(documentId, { ...currentState.rects, [pageIndex]: pageRects }));\n      this.dispatch(\n        setSlices(documentId, {\n          ...currentState.slices,\n          [pageIndex]: { start: sb.from, count: sb.to - sb.from + 1 },\n        }),\n      );\n      this.notifyPage(documentId, pageIndex);\n    }, ignore);\n\n    // Create text selection handler\n    const textHandler = createTextSelectionHandler({\n      getGeometry: () => this.getDocumentState(documentId).geometry[pageIndex],\n      isEnabled: (modeId) => {\n        const config = enabledModes?.get(modeId);\n        if (!config) return false;\n        return config.enableSelection !== false;\n      },\n      onBegin: (g, modeId) => this.beginSelection(documentId, pageIndex, g, modeId),\n      onUpdate: (g, modeId) => this.updateSelection(documentId, pageIndex, g, modeId),\n      onEnd: (modeId) => this.endSelection(documentId, modeId),\n      onClear: (modeId) => this.clearSelection(documentId, modeId),\n      isSelecting: () => this.selecting.get(documentId) ?? false,\n      setCursor: (cursor) =>\n        cursor\n          ? interactionScope.setCursor('selection-text', cursor, 10)\n          : interactionScope.removeCursor('selection-text'),\n      onEmptySpaceClick: (modeId) => this.emptySpaceClick$.emit(documentId, { pageIndex, modeId }),\n      onWordSelect: (g, modeId) => this.selectWord(documentId, pageIndex, g, modeId),\n      onLineSelect: (g, modeId) => this.selectLine(documentId, pageIndex, g, modeId),\n      setHasTextAnchor: (active) => this.hasTextAnchor.set(documentId, active),\n      minDragDistance: this.config.minSelectionDragDistance,\n      toleranceFactor: this.config.toleranceFactor,\n    });\n\n    // Register text selection with registerAlways - any plugin can enable it for their mode\n    const unregisterHandlers = this.interactionManagerCapability.registerAlways({\n      scope: { type: 'page', documentId, pageIndex },\n      handlers: textHandler,\n    });\n\n    // Return cleanup function\n    return () => {\n      unregisterHandlers();\n      this.pageCallbacks.get(documentId)?.delete(pageIndex);\n      geoTask.abort({ code: PdfErrorCode.Cancelled, message: 'Cleanup' });\n    };\n  }\n\n  /**\n   * Register marquee selection on a page. Uses `registerAlways` so any plugin\n   * can enable marquee selection for their mode via `enableForMode({ enableMarquee: true })`.\n   */\n  public registerMarqueeOnPage(opts: RegisterMarqueeOnPageOptions) {\n    const { documentId, pageIndex, scale, onRectChange } = opts;\n    const docState = this.state.documents[documentId];\n\n    if (!docState) {\n      this.logger.warn(\n        'SelectionPlugin',\n        'RegisterMarqueeFailed',\n        `Cannot register marquee on page ${pageIndex} for document ${documentId}: document state not initialized.`,\n      );\n      return () => {};\n    }\n\n    // Get page size from core state (same pattern as ZoomPlugin)\n    const coreDoc = this.coreState.core.documents[documentId];\n    if (!coreDoc || !coreDoc.document) {\n      this.logger.warn(\n        'SelectionPlugin',\n        'DocumentNotFound',\n        `Cannot register marquee on page ${pageIndex}: document not found`,\n      );\n      return () => {};\n    }\n\n    const page = coreDoc.document.pages[pageIndex];\n    if (!page) {\n      this.logger.warn(\n        'SelectionPlugin',\n        'PageNotFound',\n        `Cannot register marquee on page ${pageIndex}: page not found`,\n      );\n      return () => {};\n    }\n\n    const pageSize = page.size;\n    const minDragPx = this.config.marquee?.minDragPx ?? 5;\n\n    const shouldShowRect = () => {\n      const mode = this.interactionManagerCapability.forDocument(documentId).getActiveMode();\n      const config = this.enabledModesPerDoc.get(documentId)?.get(mode);\n      return config?.showMarqueeRects !== false;\n    };\n\n    // Create marquee selection handler\n    const marqueeHandler = createMarqueeSelectionHandler({\n      pageSize,\n      scale,\n      minDragPx,\n      isEnabled: (modeId) => {\n        const config = this.enabledModesPerDoc.get(documentId)?.get(modeId);\n        return config?.enableMarquee === true;\n      },\n      isTextSelecting: () =>\n        (this.selecting.get(documentId) ?? false) || (this.hasTextAnchor.get(documentId) ?? false),\n      onBegin: (pos, modeId) => this.beginMarquee(documentId, pageIndex, pos, modeId),\n      onChange: (rect, modeId) => {\n        this.updateMarquee(documentId, pageIndex, rect, modeId);\n        onRectChange(shouldShowRect() ? rect : null);\n      },\n      onEnd: (rect, modeId) => {\n        this.endMarquee(documentId, pageIndex, rect, modeId);\n        onRectChange(null);\n      },\n      onCancel: (modeId) => {\n        this.cancelMarquee(documentId, modeId);\n        onRectChange(null);\n      },\n    });\n\n    // Register marquee with registerAlways - any plugin can enable it for their mode\n    const unregisterHandlers = this.interactionManagerCapability.registerAlways({\n      scope: { type: 'page', documentId, pageIndex },\n      handlers: marqueeHandler,\n    });\n\n    return unregisterHandlers;\n  }\n\n  /**\n   * Helper to calculate viewport relative metrics for a page rect.\n   * Returns null if the rect cannot be converted to viewport space.\n   */\n  private getPlacementMetrics(\n    documentId: string,\n    pageIndex: number,\n    rect: Rect,\n    vpMetrics: ViewportMetrics,\n  ) {\n    // 1. Convert Page Coordinate -> Viewport Coordinate\n    // We use the scroll capability to handle rotation, scale, and scroll offset automatically\n    const scrollScope = this.scrollCapability?.forDocument(documentId);\n    const viewportRect = scrollScope?.getRectPositionForPage(pageIndex, rect);\n\n    if (!viewportRect) return null;\n\n    // 2. Calculate relative positions\n    const rectTopInView = viewportRect.origin.y - vpMetrics.scrollTop;\n    const rectBottomInView = viewportRect.origin.y + viewportRect.size.height - vpMetrics.scrollTop;\n\n    return {\n      pageIndex,\n      rect, // Original Page Rect\n      spaceAbove: rectTopInView,\n      spaceBelow: vpMetrics.clientHeight - rectBottomInView,\n      isBottomVisible: rectBottomInView > 0 && rectBottomInView <= vpMetrics.clientHeight,\n      isTopVisible: rectTopInView >= 0 && rectTopInView < vpMetrics.clientHeight,\n    };\n  }\n\n  private emitMenuPlacement(documentId: string, placement: SelectionMenuPlacement | null) {\n    this.menuPlacement$.emit(documentId, placement);\n\n    // Update page activity for the selection menu\n    if (placement) {\n      this.interactionManagerCapability.claimPageActivity(\n        documentId,\n        'selection-menu',\n        placement.pageIndex,\n      );\n    } else {\n      this.interactionManagerCapability.releasePageActivity(documentId, 'selection-menu');\n    }\n  }\n\n  private recalculateMenuPlacement(documentId: string) {\n    const docState = this.state.documents[documentId];\n    if (!docState) return;\n\n    // Only show menu when not actively selecting\n    if (docState.selecting || docState.selection === null) {\n      this.emitMenuPlacement(documentId, null);\n      return;\n    }\n\n    // 1. Get Bounding Rects for all pages involved in selection.\n    // These are implicitly sorted by pageIndex because updateRectsAndSlices\n    // populates the map in ascending page order.\n    const bounds = selector.selectBoundingRectsForAllPages(docState);\n\n    if (bounds.length === 0) {\n      this.emitMenuPlacement(documentId, null);\n      return;\n    }\n\n    const tail = bounds[bounds.length - 1];\n\n    // Fallback: If viewport/scroll plugins are missing, always default to bottom of the last rect\n    if (!this.viewportCapability || !this.scrollCapability) {\n      this.emitMenuPlacement(documentId, {\n        pageIndex: tail.page,\n        rect: tail.rect,\n        spaceAbove: 0,\n        spaceBelow: Infinity, // Pretend we have infinite space below to prevent auto-flipping\n        suggestTop: false,\n        isVisible: true, // Assume visible\n      });\n      return;\n    }\n\n    // Use document-scoped viewport to get metrics for this specific document\n    const viewportScope = this.viewportCapability.forDocument(documentId);\n    const vpMetrics = viewportScope.getMetrics();\n\n    // 2. Calculate metrics for Head (Start) and Tail (End)\n    const head = bounds[0];\n\n    const tailMetrics = this.getPlacementMetrics(documentId, tail.page, tail.rect, vpMetrics);\n    const headMetrics = this.getPlacementMetrics(documentId, head.page, head.rect, vpMetrics);\n\n    // 3. Apply Heuristic Logic\n\n    // Priority A: Bottom of Tail (Standard selection end)\n    // If the bottom of the selection is visible and we have space below.\n    if (tailMetrics) {\n      if (tailMetrics.isBottomVisible && tailMetrics.spaceBelow > this.menuHeight) {\n        this.emitMenuPlacement(documentId, {\n          ...tailMetrics,\n          suggestTop: false,\n          isVisible: true,\n        });\n        return;\n      }\n    }\n\n    // Priority B: Top of Head (Selection start, if user scrolled up)\n    // If the top of the start selection is visible, put the menu there.\n    if (headMetrics) {\n      if (headMetrics.isTopVisible) {\n        this.emitMenuPlacement(documentId, {\n          ...headMetrics,\n          suggestTop: true,\n          isVisible: true,\n        });\n        return;\n      }\n    }\n\n    // Priority C: Fallback to Tail Bottom if visible (even if tight space)\n    // It's better to stick to the cursor end than jump to the top if space is tight.\n    if (tailMetrics && tailMetrics.isBottomVisible) {\n      this.emitMenuPlacement(documentId, {\n        ...tailMetrics,\n        suggestTop: false,\n        isVisible: true,\n      });\n      return;\n    }\n\n    // If completely off screen\n    this.emitMenuPlacement(documentId, null);\n  }\n\n  private notifyPage(documentId: string, pageIndex: number) {\n    const callback = this.pageCallbacks.get(documentId)?.get(pageIndex);\n    if (callback) {\n      const docState = this.getDocumentState(documentId);\n      const mode = this.interactionManagerCapability.forDocument(documentId).getActiveMode();\n      const modeConfig = this.enabledModesPerDoc.get(documentId)?.get(mode);\n\n      // Show rects if mode is enabled and showSelectionRects/showRects is not explicitly false\n      const shouldShowRects =\n        modeConfig && (modeConfig.showSelectionRects ?? modeConfig.showRects) !== false;\n\n      if (shouldShowRects) {\n        callback({\n          rects: selector.selectRectsForPage(docState, pageIndex),\n          boundingRect: selector.selectBoundingRectForPage(docState, pageIndex),\n        });\n      } else {\n        callback({ rects: [], boundingRect: null });\n      }\n    }\n  }\n\n  private notifyAllPages(documentId: string) {\n    this.pageCallbacks.get(documentId)?.forEach((_, pageIndex) => {\n      this.notifyPage(documentId, pageIndex);\n    });\n  }\n\n  private getNewPageGeometryAndCache(\n    documentId: string,\n    pageIdx: number,\n  ): PdfTask<PdfPageGeometry> {\n    const coreDoc = this.getCoreDocument(documentId);\n    if (!coreDoc || !coreDoc.document)\n      return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: 'Doc Not Found' });\n\n    const page = coreDoc.document.pages.find((p) => p.index === pageIdx)!;\n    const task = this.engine.getPageGeometry(coreDoc.document, page);\n    task.wait((geo) => {\n      this.dispatch(cachePageGeometry(documentId, pageIdx, geo));\n      this.touchGeometry(documentId, pageIdx);\n    }, ignore);\n    return task;\n  }\n\n  /* ── geometry cache ───────────────────────────────────── */\n  private getOrLoadGeometry(documentId: string, pageIdx: number): PdfTask<PdfPageGeometry> {\n    const cached = this.getDocumentState(documentId).geometry[pageIdx];\n    if (cached) {\n      this.touchGeometry(documentId, pageIdx);\n      return PdfTaskHelper.resolve(cached);\n    }\n\n    return this.getNewPageGeometryAndCache(documentId, pageIdx);\n  }\n\n  /* ── geometry LRU eviction ──────────────────────────────── */\n\n  private touchGeometry(documentId: string, pageIdx: number): void {\n    const order = this.geoAccessOrder.get(documentId);\n    if (!order) return;\n\n    const idx = order.indexOf(pageIdx);\n    if (idx > -1) order.splice(idx, 1);\n    order.push(pageIdx);\n\n    this.evictGeometryIfNeeded(documentId);\n  }\n\n  private evictGeometryIfNeeded(documentId: string): void {\n    const max = this.config.maxCachedGeometries ?? 50;\n    const order = this.geoAccessOrder.get(documentId);\n    if (!order || order.length <= max) return;\n\n    const pinned = this.pageCallbacks.get(documentId);\n    const toEvict: number[] = [];\n\n    while (order.length - toEvict.length > max) {\n      const candidate = order.find((p) => !toEvict.includes(p) && !pinned?.has(p));\n      if (candidate === undefined) break;\n      toEvict.push(candidate);\n    }\n\n    if (toEvict.length === 0) return;\n\n    for (const p of toEvict) {\n      const idx = order.indexOf(p);\n      if (idx > -1) order.splice(idx, 1);\n    }\n\n    this.dispatch(evictPageGeometry(documentId, toEvict));\n  }\n\n  /* ── selection state updates ───────────────────────────── */\n  private beginSelection(documentId: string, page: number, index: number, modeId: string) {\n    this.selecting.set(documentId, true);\n    this.anchor.set(documentId, { page, index });\n    this.dispatch(startSelection(documentId));\n    this.beginSelection$.emit(documentId, { page, index, modeId });\n    this.recalculateMenuPlacement(documentId);\n  }\n\n  private endSelection(documentId: string, modeId: string) {\n    this.selecting.set(documentId, false);\n    this.anchor.set(documentId, undefined);\n    this.dispatch(endSelection(documentId));\n    this.endSelection$.emit(documentId, { modeId });\n    this.recalculateMenuPlacement(documentId);\n  }\n\n  private clearSelection(documentId: string, _modeId?: string) {\n    this.selecting.set(documentId, false);\n    this.anchor.set(documentId, undefined);\n    this.dispatch(clearSelection(documentId));\n    this.selChange$.emit(documentId, null);\n    this.emitMenuPlacement(documentId, null);\n    this.notifyAllPages(documentId);\n  }\n\n  private selectWord(documentId: string, page: number, charIndex: number, modeId: string) {\n    const geo = this.getDocumentState(documentId).geometry[page];\n    if (!geo) return;\n\n    const bounds = expandToWordBoundary(geo, charIndex);\n    if (!bounds) return;\n\n    this.applyInstantSelection(documentId, page, bounds.from, bounds.to, modeId);\n  }\n\n  private selectLine(documentId: string, page: number, charIndex: number, modeId: string) {\n    const geo = this.getDocumentState(documentId).geometry[page];\n    if (!geo) return;\n\n    const bounds = expandToLineBoundary(geo, charIndex);\n    if (!bounds) return;\n\n    this.applyInstantSelection(documentId, page, bounds.from, bounds.to, modeId);\n  }\n\n  /**\n   * Set a selection range without going through the drag begin/update/end flow.\n   * Used by double-click (word) and triple-click (line) selection.\n   */\n  private applyInstantSelection(\n    documentId: string,\n    page: number,\n    from: number,\n    to: number,\n    modeId: string,\n  ) {\n    const range: SelectionRangeX = {\n      start: { page, index: from },\n      end: { page, index: to },\n    };\n\n    this.selecting.set(documentId, false);\n    this.anchor.set(documentId, undefined);\n    this.dispatch(startSelection(documentId));\n    this.dispatch(setSelection(documentId, range));\n    this.updateRectsAndSlices(documentId, range);\n    this.dispatch(endSelection(documentId));\n\n    this.selChange$.emit(documentId, range);\n    this.beginSelection$.emit(documentId, { page, index: from, modeId });\n    this.endSelection$.emit(documentId, { modeId });\n\n    for (let p = range.start.page; p <= range.end.page; p++) {\n      this.notifyPage(documentId, p);\n    }\n    this.recalculateMenuPlacement(documentId);\n  }\n\n  private updateSelection(documentId: string, page: number, index: number, modeId: string) {\n    if (!this.selecting.get(documentId) || !this.anchor.get(documentId)) return;\n\n    const a = this.anchor.get(documentId)!;\n    const forward = page > a.page || (page === a.page && index >= a.index);\n\n    const start = forward ? a : { page, index };\n    const end = forward ? { page, index } : a;\n\n    const range = { start, end };\n    this.dispatch(setSelection(documentId, range));\n    this.updateRectsAndSlices(documentId, range);\n    this.selChange$.emit(documentId, range);\n\n    // Notify affected pages\n    for (let p = range.start.page; p <= range.end.page; p++) {\n      this.notifyPage(documentId, p);\n    }\n  }\n\n  private updateRectsAndSlices(documentId: string, range: SelectionRangeX) {\n    const docState = this.getDocumentState(documentId);\n    const allRects: Record<number, Rect[]> = {};\n    const allSlices: Record<number, { start: number; count: number }> = {};\n\n    for (let p = range.start.page; p <= range.end.page; p++) {\n      const geo = docState.geometry[p];\n      const sb = sliceBounds(range, geo, p);\n      if (!sb) continue;\n\n      allRects[p] = rectsWithinSlice(geo!, sb.from, sb.to);\n      allSlices[p] = { start: sb.from, count: sb.to - sb.from + 1 };\n    }\n\n    this.dispatch(setRects(documentId, allRects));\n    this.dispatch(setSlices(documentId, allSlices));\n  }\n\n  private getSelectedText(documentId: string): PdfTask<string[]> {\n    // Prevent extracting text without permission\n    if (!this.checkPermission(documentId, PdfPermissionFlag.CopyContents)) {\n      this.logger.debug(\n        'SelectionPlugin',\n        'GetSelectedText',\n        `Cannot get selected text: document ${documentId} lacks CopyContents permission`,\n      );\n      return PdfTaskHelper.reject({\n        code: PdfErrorCode.Security,\n        message: 'Document lacks CopyContents permission',\n      });\n    }\n\n    const coreDoc = this.getCoreDocument(documentId);\n    const docState = this.getDocumentState(documentId);\n\n    if (!coreDoc?.document || !docState.selection) {\n      return PdfTaskHelper.reject({\n        code: PdfErrorCode.NotFound,\n        message: 'Doc Not Found or No Selection',\n      });\n    }\n\n    const sel = docState.selection;\n    const req: PageTextSlice[] = [];\n\n    for (let p = sel.start.page; p <= sel.end.page; p++) {\n      const s = docState.slices[p];\n      if (s) req.push({ pageIndex: p, charIndex: s.start, charCount: s.count });\n    }\n\n    if (req.length === 0) return PdfTaskHelper.resolve([] as string[]);\n\n    const task = this.engine.getTextSlices(coreDoc.document, req);\n\n    // Emit the text when it's retrieved\n    task.wait((text) => {\n      this.textRetrieved$.emit(documentId, text);\n    }, ignore);\n\n    return task;\n  }\n\n  private copyToClipboard(documentId: string) {\n    // Prevent copying text without permission\n    if (!this.checkPermission(documentId, PdfPermissionFlag.CopyContents)) {\n      this.logger.debug(\n        'SelectionPlugin',\n        'CopyToClipboard',\n        `Cannot copy to clipboard: document ${documentId} lacks CopyContents permission`,\n      );\n      return;\n    }\n\n    const text = this.getSelectedText(documentId);\n    text.wait((text) => {\n      this.copyToClipboard$.emit(documentId, text.join('\\n'));\n    }, ignore);\n  }\n\n  /* ── marquee selection state updates ─────────────────────── */\n  private beginMarquee(\n    documentId: string,\n    pageIndex: number,\n    _startPos: Position,\n    _modeId: string,\n  ) {\n    this.marqueePage.set(documentId, pageIndex);\n  }\n\n  private updateMarquee(documentId: string, pageIndex: number, rect: Rect, modeId: string) {\n    this.marqueeChange$.emit(documentId, { pageIndex, rect, modeId });\n  }\n\n  private endMarquee(documentId: string, pageIndex: number, rect: Rect, modeId: string) {\n    this.marqueeEnd$.emit(documentId, { pageIndex, rect, modeId });\n    this.marqueeChange$.emit(documentId, { pageIndex, rect: null, modeId });\n    this.marqueePage.delete(documentId);\n  }\n\n  private cancelMarquee(documentId: string, modeId: string) {\n    const pageIndex = this.marqueePage.get(documentId);\n    if (pageIndex !== undefined) {\n      this.marqueeChange$.emit(documentId, { pageIndex, rect: null, modeId });\n      this.marqueePage.delete(documentId);\n    }\n  }\n\n  /** @deprecated — shim for backward compat; delegates to pointerMode config */\n  private setMarqueeEnabled(documentId: string, enabled: boolean) {\n    const modes = this.enabledModesPerDoc.get(documentId);\n    if (!modes) return;\n    const current = modes.get('pointerMode');\n    if (current) {\n      current.enableMarquee = enabled;\n    } else if (enabled) {\n      modes.set('pointerMode', { enableMarquee: true });\n    }\n  }\n\n  /** @deprecated — shim for backward compat; reads pointerMode config */\n  private isMarqueeEnabled(documentId: string): boolean {\n    const config = this.enabledModesPerDoc.get(documentId)?.get('pointerMode');\n    return config?.enableMarquee !== false;\n  }\n}\n","import { PdfPageGeometry, Position } from '@embedpdf/models';\nimport {\n  EmbedPdfPointerEvent,\n  PointerEventHandlersWithLifecycle,\n} from '@embedpdf/plugin-interaction-manager';\nimport { glyphAt } from '../utils';\n\nexport interface TextSelectionHandlerOptions {\n  /** Returns the page geometry, or undefined if not loaded yet */\n  getGeometry: () => PdfPageGeometry | undefined;\n  /** Check if selection is enabled for this mode */\n  isEnabled: (modeId: string) => boolean;\n  /** Called when drag-selection begins on a glyph */\n  onBegin: (glyphIndex: number, modeId: string) => void;\n  /** Called when drag-selection updates to a new glyph */\n  onUpdate: (glyphIndex: number, modeId: string) => void;\n  /** Called when drag-selection ends (pointer up) */\n  onEnd: (modeId: string) => void;\n  /** Called to clear the current selection */\n  onClear: (modeId: string) => void;\n  /** Returns whether text selection is currently in progress */\n  isSelecting: () => boolean;\n  /** Set or remove the text cursor */\n  setCursor: (cursor: string | null) => void;\n  /** Called when the user clicks directly on empty page space (target === currentTarget) */\n  onEmptySpaceClick?: (modeId: string) => void;\n  /** Called on double-click over a glyph; receives the char index */\n  onWordSelect?: (glyphIndex: number, modeId: string) => void;\n  /** Called on triple-click over a glyph; receives the char index */\n  onLineSelect?: (glyphIndex: number, modeId: string) => void;\n  /**\n   * Signals whether the text handler has claimed a pointer-down (anchor set)\n   * even before the drag threshold is met. Used to prevent the marquee handler\n   * from activating concurrently.\n   */\n  setHasTextAnchor?: (active: boolean) => void;\n  /**\n   * Minimum drag distance (in page-coordinate units) before a pointer-down\n   * starts an actual selection drag. Default: 3.\n   */\n  minDragDistance?: number;\n  /** Tolerance factor passed through to glyphAt. Default: 0.9. */\n  toleranceFactor?: number;\n}\n\nconst TRIPLE_CLICK_INTERVAL_MS = 500;\n\n/**\n * Creates a text selection handler that manages pointer-based text selection,\n * double-click word selection, triple-click line selection, and a drag threshold.\n *\n * Behaviour modelled after Chromium's PDFiumEngine (pdfium-engine.cc):\n *  - Single pointer-down records an anchor but does NOT begin selection until\n *    the pointer has moved beyond `minDragDistance`.\n *  - Double-click selects the word around the clicked glyph.\n *  - Triple-click selects the full visual line.\n *  - The marquee handler coordinates via `isTextSelecting` / `hasTextAnchor`\n *    to avoid activating during text selection.\n */\nexport function createTextSelectionHandler(\n  opts: TextSelectionHandlerOptions,\n): PointerEventHandlersWithLifecycle<EmbedPdfPointerEvent> {\n  const minDrag = opts.minDragDistance ?? 3;\n  const tolFactor = opts.toleranceFactor ?? 1.5;\n\n  // Drag-threshold state\n  let anchorGlyph: number | null = null;\n  let anchorPos: Position | null = null;\n  let dragStarted = false;\n\n  // Triple-click detection: timestamp of the most recent dblclick\n  let lastDblClickTime = 0;\n\n  function reset() {\n    anchorGlyph = null;\n    anchorPos = null;\n    dragStarted = false;\n    opts.setHasTextAnchor?.(false);\n  }\n\n  return {\n    onPointerDown: (point: Position, evt, modeId) => {\n      // Detect click on empty page space (fires for ALL modes)\n      if (evt.target === evt.currentTarget) {\n        opts.onEmptySpaceClick?.(modeId);\n      }\n\n      if (!opts.isEnabled(modeId)) return;\n\n      // Skip clearing if we're in the triple-click window — the onClick\n      // handler will expand the existing word selection to a line selection.\n      const now = Date.now();\n      if (lastDblClickTime === 0 || now - lastDblClickTime >= TRIPLE_CLICK_INTERVAL_MS) {\n        opts.onClear(modeId);\n      }\n\n      const geo = opts.getGeometry();\n      if (!geo) return;\n\n      const g = glyphAt(geo, point, tolFactor);\n      if (g !== -1) {\n        anchorGlyph = g;\n        anchorPos = point;\n        dragStarted = false;\n        opts.setHasTextAnchor?.(true);\n      }\n    },\n\n    onPointerMove: (point: Position, _evt, modeId) => {\n      if (!opts.isEnabled(modeId)) return;\n\n      const geo = opts.getGeometry();\n      if (!geo) return;\n\n      const g = glyphAt(geo, point, tolFactor);\n\n      // Update cursor based on whether we're over text\n      opts.setCursor(g !== -1 ? 'text' : null);\n\n      // If we have an anchor but haven't started dragging, check threshold\n      if (anchorGlyph !== null && anchorPos && !dragStarted) {\n        const dx = point.x - anchorPos.x;\n        const dy = point.y - anchorPos.y;\n        const dist = Math.sqrt(dx * dx + dy * dy);\n\n        if (dist >= minDrag) {\n          dragStarted = true;\n          opts.onBegin(anchorGlyph, modeId);\n          if (g !== -1) {\n            opts.onUpdate(g, modeId);\n          }\n        }\n        return;\n      }\n\n      // Continue extending the selection during an active drag\n      if (opts.isSelecting() && g !== -1) {\n        opts.onUpdate(g, modeId);\n      }\n    },\n\n    onPointerUp: (_point: Position, _evt, modeId) => {\n      if (!opts.isEnabled(modeId)) {\n        reset();\n        return;\n      }\n\n      if (dragStarted) {\n        opts.onEnd(modeId);\n      }\n\n      reset();\n    },\n\n    onDoubleClick: (point: Position, _evt, modeId) => {\n      if (!opts.isEnabled(modeId)) return;\n\n      const geo = opts.getGeometry();\n      if (!geo) return;\n\n      const g = glyphAt(geo, point, tolFactor);\n      if (g === -1) return;\n\n      lastDblClickTime = Date.now();\n      opts.onWordSelect?.(g, modeId);\n    },\n\n    onClick: (point: Position, _evt, modeId) => {\n      if (!opts.isEnabled(modeId)) return;\n\n      // Triple-click detection: a click arriving shortly after a dblclick\n      if (lastDblClickTime === 0) return;\n\n      const now = Date.now();\n      if (now - lastDblClickTime > TRIPLE_CLICK_INTERVAL_MS) {\n        lastDblClickTime = 0;\n        return;\n      }\n\n      lastDblClickTime = 0;\n\n      const geo = opts.getGeometry();\n      if (!geo) return;\n\n      const g = glyphAt(geo, point, tolFactor);\n      if (g === -1) return;\n\n      opts.onLineSelect?.(g, modeId);\n    },\n\n    onHandlerActiveEnd: (modeId) => {\n      reset();\n      if (!opts.isEnabled(modeId)) return;\n      opts.onClear(modeId);\n    },\n  };\n}\n","import { Position, Rect, Size } from '@embedpdf/models';\nimport { clamp } from '@embedpdf/core';\nimport {\n  EmbedPdfPointerEvent,\n  PointerEventHandlersWithLifecycle,\n} from '@embedpdf/plugin-interaction-manager';\n\nexport interface MarqueeSelectionHandlerOptions {\n  /** The page size for clamping */\n  pageSize: Size;\n  /** Current scale factor for min drag threshold calculation */\n  scale: number;\n  /** Minimum drag distance in pixels before considering it a marquee (default: 5) */\n  minDragPx?: number;\n  /** Check if marquee selection is enabled for this mode */\n  isEnabled: (modeId: string) => boolean;\n  /** Returns whether text selection is currently active (skip marquee if so) */\n  isTextSelecting?: () => boolean;\n  /** Called when marquee selection begins */\n  onBegin: (startPos: Position, modeId: string) => void;\n  /** Called during drag with the current marquee rect */\n  onChange: (rect: Rect, modeId: string) => void;\n  /** Called when marquee selection completes (drag was large enough) */\n  onEnd: (rect: Rect, modeId: string) => void;\n  /** Called when marquee selection is cancelled (drag too small or cancelled) */\n  onCancel: (modeId: string) => void;\n}\n\n/**\n * Creates a marquee selection handler that allows users to drag a selection rectangle.\n *\n * This handler is meant to be combined with the text selection handler. When text is hit,\n * the text selection handler sets its selecting state, and this handler checks via\n * `isTextSelecting` to avoid activating during text selection.\n */\nexport function createMarqueeSelectionHandler(\n  opts: MarqueeSelectionHandlerOptions,\n): PointerEventHandlersWithLifecycle<EmbedPdfPointerEvent> {\n  const { pageSize, scale, minDragPx = 5 } = opts;\n\n  let start: Position | null = null;\n  let last: Rect | null = null;\n\n  return {\n    onPointerDown: (pos, evt, modeId) => {\n      if (!opts.isEnabled(modeId)) return;\n      if (opts.isTextSelecting?.()) return;\n\n      start = pos;\n      last = { origin: { x: pos.x, y: pos.y }, size: { width: 0, height: 0 } };\n      opts.onBegin(pos, modeId);\n      evt.setPointerCapture?.();\n    },\n\n    onPointerMove: (pos, _evt, modeId) => {\n      if (!start || !opts.isEnabled(modeId)) return;\n\n      // Clamp position to page bounds\n      const x = clamp(pos.x, 0, pageSize.width);\n      const y = clamp(pos.y, 0, pageSize.height);\n\n      // Build the marquee rect (handle negative drag directions)\n      last = {\n        origin: { x: Math.min(start.x, x), y: Math.min(start.y, y) },\n        size: { width: Math.abs(x - start.x), height: Math.abs(y - start.y) },\n      };\n\n      opts.onChange(last, modeId);\n    },\n\n    onPointerUp: (_pos, evt, modeId) => {\n      if (!opts.isEnabled(modeId)) return;\n\n      if (last && start) {\n        // Only commit if the drag was large enough\n        const dragPx = Math.max(last.size.width, last.size.height) * scale;\n        if (dragPx > minDragPx) {\n          opts.onEnd(last, modeId);\n        } else {\n          opts.onCancel(modeId);\n        }\n      }\n\n      start = null;\n      last = null;\n      evt.releasePointerCapture?.();\n    },\n\n    onPointerCancel: (_pos, evt, modeId) => {\n      if (!opts.isEnabled(modeId)) return;\n\n      start = null;\n      last = null;\n      opts.onCancel(modeId);\n      evt.releasePointerCapture?.();\n    },\n  };\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, SELECTION_PLUGIN_ID } from './manifest';\nimport { SelectionPluginConfig, SelectionState } from './types';\n\nimport { SelectionPlugin } from './selection-plugin';\nimport { SelectionAction } from './actions';\nimport { selectionReducer, initialState } from './reducer';\n\nexport const SelectionPluginPackage: PluginPackage<\n  SelectionPlugin,\n  SelectionPluginConfig,\n  SelectionState,\n  SelectionAction\n> = {\n  manifest,\n  create: (registry, config) => new SelectionPlugin(SELECTION_PLUGIN_ID, registry, config),\n  reducer: selectionReducer,\n  initialState,\n};\n\nexport * from './selection-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './utils';\n"],"names":["SELECTION_PLUGIN_ID","manifest","id","name","version","provides","requires","optional","defaultConfig","menuHeight","INIT_SELECTION_STATE","CLEANUP_SELECTION_STATE","CACHE_PAGE_GEOMETRY","SET_SELECTION","START_SELECTION","END_SELECTION","CLEAR_SELECTION","SET_RECTS","SET_SLICES","EVICT_PAGE_GEOMETRY","setSelection","documentId","sel","type","payload","selection","startSelection","endSelection","setRects","allRects","rects","setSlices","slices","initialSelectionDocumentState","geometry","active","selecting","initialState","documents","updateDocState","state","newDocState","selectRectsForPage","page","selectBoundingRectForPage","boundingRect","selectBoundingRectsForAllPages","out","rectMap","key","Number","bRect","push","rect","getFormattedSelectionForPage","segmentRects","length","pageIndex","getFormattedSelection","result","pages","Object","keys","map","glyphAt","geo","pt","toleranceFactor","run","runs","y","height","x","width","rel","glyphs","findIndex","g","gx","tightX","gy","tightY","gw","tightWidth","gh","tightHeight","charStart","tolerance","factor","totalHeight","count","flags","computeTolerance","halfTol","bestIndex","bestDist","Infinity","i","expandedLeft","expandedRight","expandedTop","expandedBottom","dist","Math","min","abs","sliceBounds","start","end","from","index","lastRun","lastCharOnPage","to","rectsWithinSlice","merge","textRuns","runStart","runEnd","sIdx","max","eIdx","minX","maxX","minY","maxY","charCount","widthSum","prevRight","flushSubRun","origin","size","fontSize","gap","avgWidth","mergeAdjacentRects","rectUnion","rect1","rect2","left","top","rectIntersect","right","bottom","rectIsEmpty","getVerticalOverlap","unionRect","shouldMergeHorizontalRects","textRun1","textRun2","averageWidth1","averageWidth2","rect1Left","rect1Right","rect2Left","results","previousTextRun","currentRect","textRun","resolveCharIndex","charIndex","r","localIdx","runIdx","isGlyphWordBoundary","expandToWordBoundary","totalChars","getTotalCharCount","prev","next","expandToLineBoundary","resolved","anchorRun","anchorTop","anchorBottom","isZeroSizeRun","runsOverlapVertically","top1","bottom1","top2","bottom2","unionHeight","intersectHeight","_SelectionPlugin","BasePlugin","constructor","registry","config","super","this","enabledModesPerDoc","Map","anchor","hasTextAnchor","marqueePage","pageCallbacks","geoAccessOrder","menuPlacement$","createScopedEmitter","placement","selChange$","modeId","interactionManagerCapability","forDocument","getActiveMode","textRetrieved$","text","copyToClipboard$","cache","beginSelection$","data","endSelection$","marqueeChange$","marqueeEnd$","emptySpaceClick$","viewportCapability","scrollCapability","imPlugin","getPlugin","Error","_a","_b","coreStore","onAction","REFRESH_PAGES","action","pageIndexes","tasks","getNewPageGeometryAndCache","Task","all","wait","forEach","notifyPage","ignore","_c","onViewportChange","event","recalculateMenuPlacement","mode","onDocumentLoadingStarted","dispatch","initSelectionState","marqueeEnabled","marquee","enabled","set","enableSelection","showSelectionRects","enableMarquee","showMarqueeRects","onDocumentClosed","cleanupSelectionState","delete","clearScope","initialize","destroy","clear","buildCapability","getDocId","getActiveDocumentId","docId","selector.getFormattedSelection","getDocumentState","p","selector.getFormattedSelectionForPage","getHighlightRectsForPage","selector.selectRectsForPage","getHighlightRects","getBoundingRectForPage","selector.selectBoundingRectForPage","getBoundingRects","selector.selectBoundingRectsForAllPages","getSelectedText","clearSelection","copyToClipboard","getState","enableForMode","options","get","isEnabledForMode","has","setMarqueeEnabled","isMarqueeEnabled","createSelectionScope","bind","onCopyToClipboard","onGlobal","onSelectionChange","onTextRetrieved","onBeginSelection","onEndSelection","onMarqueeChange","onMarqueeEnd","onEmptySpaceClick","forScope","onMenuPlacement","listener","registerSelectionOnPage","opts","onRectsChange","docState","logger","warn","geoTask","getOrLoadGeometry","interactionScope","enabledModes","currentState","sb","pageRects","textHandler","minDrag","minDragDistance","tolFactor","anchorGlyph","anchorPos","dragStarted","lastDblClickTime","reset","setHasTextAnchor","call","onPointerDown","point","evt","target","currentTarget","isEnabled","now","Date","onClear","getGeometry","onPointerMove","_evt","setCursor","dx","dy","sqrt","onBegin","onUpdate","isSelecting","onPointerUp","_point","onEnd","onDoubleClick","onWordSelect","onClick","onLineSelect","onHandlerActiveEnd","createTextSelectionHandler","beginSelection","updateSelection","cursor","removeCursor","emit","selectWord","selectLine","minSelectionDragDistance","unregisterHandlers","registerAlways","scope","handlers","abort","code","PdfErrorCode","Cancelled","message","registerMarqueeOnPage","scale","onRectChange","coreDoc","coreState","core","document","pageSize","minDragPx","shouldShowRect","marqueeHandler","last","pos","isTextSelecting","setPointerCapture","clamp","onChange","_pos","onCancel","releasePointerCapture","onPointerCancel","createMarqueeSelectionHandler","beginMarquee","updateMarquee","endMarquee","cancelMarquee","getPlacementMetrics","vpMetrics","scrollScope","viewportRect","getRectPositionForPage","rectTopInView","scrollTop","rectBottomInView","spaceAbove","spaceBelow","clientHeight","isBottomVisible","isTopVisible","emitMenuPlacement","claimPageActivity","releasePageActivity","bounds","tail","suggestTop","isVisible","getMetrics","head","tailMetrics","headMetrics","callback","modeConfig","showRects","notifyAllPages","_","pageIdx","getCoreDocument","PdfTaskHelper","reject","NotFound","find","task","engine","getPageGeometry","cachePageGeometry","touchGeometry","cached","resolve","order","idx","indexOf","splice","evictGeometryIfNeeded","maxCachedGeometries","pinned","toEvict","candidate","includes","evictPageGeometry","_modeId","applyInstantSelection","range","updateRectsAndSlices","a","forward","allSlices","checkPermission","PdfPermissionFlag","CopyContents","debug","Security","req","s","getTextSlices","join","_startPos","modes","current","SelectionPlugin","SelectionPluginPackage","create","reducer","removed","remaining"],"mappings":"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"}