@embedpdf/plugin-selection 2.14.4 → 3.0.0-next.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.
Files changed (90) hide show
  1. package/LICENSE +192 -21
  2. package/dist/index.cjs +575 -2
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +167 -0
  5. package/dist/index.d.ts +167 -1
  6. package/dist/index.js +549 -1375
  7. package/dist/index.js.map +1 -1
  8. package/package.json +21 -62
  9. package/dist/lib/actions.d.ts +0 -103
  10. package/dist/lib/handlers/index.d.ts +0 -2
  11. package/dist/lib/handlers/marquee-selection.handler.d.ts +0 -30
  12. package/dist/lib/handlers/text-selection.handler.d.ts +0 -52
  13. package/dist/lib/index.d.ts +0 -9
  14. package/dist/lib/manifest.d.ts +0 -4
  15. package/dist/lib/reducer.d.ts +0 -5
  16. package/dist/lib/selection-plugin.d.ts +0 -92
  17. package/dist/lib/selectors.d.ts +0 -14
  18. package/dist/lib/types.d.ts +0 -266
  19. package/dist/lib/utils.d.ts +0 -108
  20. package/dist/preact/adapter.d.ts +0 -4
  21. package/dist/preact/core.d.ts +0 -1
  22. package/dist/preact/index.cjs +0 -2
  23. package/dist/preact/index.cjs.map +0 -1
  24. package/dist/preact/index.d.ts +0 -1
  25. package/dist/preact/index.js +0 -218
  26. package/dist/preact/index.js.map +0 -1
  27. package/dist/preact/interaction-manager.d.ts +0 -1
  28. package/dist/preact/utils.d.ts +0 -1
  29. package/dist/react/adapter.d.ts +0 -2
  30. package/dist/react/core.d.ts +0 -1
  31. package/dist/react/index.cjs +0 -2
  32. package/dist/react/index.cjs.map +0 -1
  33. package/dist/react/index.d.ts +0 -1
  34. package/dist/react/index.js +0 -217
  35. package/dist/react/index.js.map +0 -1
  36. package/dist/react/interaction-manager.d.ts +0 -1
  37. package/dist/react/utils.d.ts +0 -1
  38. package/dist/shared/components/copy-to-clipboard.d.ts +0 -1
  39. package/dist/shared/components/index.d.ts +0 -4
  40. package/dist/shared/components/marquee-selection.d.ts +0 -36
  41. package/dist/shared/components/selection-layer.d.ts +0 -29
  42. package/dist/shared/components/text-selection.d.ts +0 -21
  43. package/dist/shared/hooks/index.d.ts +0 -1
  44. package/dist/shared/hooks/use-selection.d.ts +0 -11
  45. package/dist/shared/index.d.ts +0 -5
  46. package/dist/shared/types.d.ts +0 -7
  47. package/dist/shared-preact/components/copy-to-clipboard.d.ts +0 -1
  48. package/dist/shared-preact/components/index.d.ts +0 -4
  49. package/dist/shared-preact/components/marquee-selection.d.ts +0 -36
  50. package/dist/shared-preact/components/selection-layer.d.ts +0 -29
  51. package/dist/shared-preact/components/text-selection.d.ts +0 -21
  52. package/dist/shared-preact/hooks/index.d.ts +0 -1
  53. package/dist/shared-preact/hooks/use-selection.d.ts +0 -11
  54. package/dist/shared-preact/index.d.ts +0 -5
  55. package/dist/shared-preact/types.d.ts +0 -7
  56. package/dist/shared-react/components/copy-to-clipboard.d.ts +0 -1
  57. package/dist/shared-react/components/index.d.ts +0 -4
  58. package/dist/shared-react/components/marquee-selection.d.ts +0 -36
  59. package/dist/shared-react/components/selection-layer.d.ts +0 -29
  60. package/dist/shared-react/components/text-selection.d.ts +0 -21
  61. package/dist/shared-react/hooks/index.d.ts +0 -1
  62. package/dist/shared-react/hooks/use-selection.d.ts +0 -11
  63. package/dist/shared-react/index.d.ts +0 -5
  64. package/dist/shared-react/types.d.ts +0 -7
  65. package/dist/svelte/components/CopyToClipboard.svelte.d.ts +0 -3
  66. package/dist/svelte/components/MarqueeSelection.svelte.d.ts +0 -27
  67. package/dist/svelte/components/SelectionLayer.svelte.d.ts +0 -32
  68. package/dist/svelte/components/TextSelection.svelte.d.ts +0 -22
  69. package/dist/svelte/components/index.d.ts +0 -4
  70. package/dist/svelte/hooks/index.d.ts +0 -1
  71. package/dist/svelte/hooks/use-selection.svelte.d.ts +0 -19
  72. package/dist/svelte/index.cjs +0 -2
  73. package/dist/svelte/index.cjs.map +0 -1
  74. package/dist/svelte/index.d.ts +0 -5
  75. package/dist/svelte/index.js +0 -359
  76. package/dist/svelte/index.js.map +0 -1
  77. package/dist/svelte/types.d.ts +0 -7
  78. package/dist/vue/components/copy-to-clipboard.vue.d.ts +0 -3
  79. package/dist/vue/components/index.d.ts +0 -4
  80. package/dist/vue/components/marquee-selection.vue.d.ts +0 -29
  81. package/dist/vue/components/selection-layer.vue.d.ts +0 -60
  82. package/dist/vue/components/text-selection.vue.d.ts +0 -51
  83. package/dist/vue/hooks/index.d.ts +0 -1
  84. package/dist/vue/hooks/use-selection.d.ts +0 -11
  85. package/dist/vue/index.cjs +0 -2
  86. package/dist/vue/index.cjs.map +0 -1
  87. package/dist/vue/index.d.ts +0 -5
  88. package/dist/vue/index.js +0 -311
  89. package/dist/vue/index.js.map +0 -1
  90. package/dist/vue/types.d.ts +0 -7
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/selectors.ts","../src/lib/utils.ts","../src/lib/handlers/text-selection.handler.ts","../src/lib/handlers/marquee-selection.handler.ts","../src/lib/selection-plugin.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 { 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 {\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 { 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":["boundingRect","selector.getFormattedSelection","selector.getFormattedSelectionForPage","selector.selectRectsForPage","selector.selectBoundingRectForPage","selector.selectBoundingRectsForAllPages","_a","text"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/geometry.ts","../src/capability.ts","../src/handler.ts","../src/reducer.ts","../src/types.ts","../src/selection.plugin.ts"],"sourcesContent":["/**\n * The pure selection geometry: engine text geometry (PDF user space, y-up) →\n * viewer content space (y-down), glyph hit-testing, word/line expansion, and the\n * line-merge. No DOM, no engine, no React.\n *\n * The non-trivial bits are ports of battle-tested algorithms, adapted to v3's\n * content-space `Rect {x,y,width,height}`:\n * - `glyphAt` — PDFium `GetIndexAtPos` (exact tight box, then a tolerance pass).\n * - `rectsForRange` — Chromium `pdfium_range.cc` `MergeAdjacentRects` (one rect per line).\n * - `expandToWord/Line` — Chromium `OnMultipleClick` (double = word, triple = line).\n * The PDF↔content y-flip is the geometry package's `pageGeometry` (the one place\n * that math lives), never hand-rolled here.\n */\nimport {\n applyRect,\n boundsOfRects,\n pageGeometry,\n type Point,\n type Rect,\n type RectIn,\n} from '@embedpdf/core-geometry';\nimport type { PageGeometrySnapshot, PdfRect } from '@embedpdf/engine-core/runtime';\n\nconst FLAG_SPACE = 1;\nconst FLAG_EMPTY = 2;\nconst isBoundary = (flags: number): boolean => (flags & (FLAG_SPACE | FLAG_EMPTY)) !== 0;\nconst isEmpty = (flags: number): boolean => (flags & FLAG_EMPTY) !== 0;\n\n/** One glyph in content space. `loose` builds selection rects; `tight` hit-tests. */\nexport interface GlyphInfo {\n loose: Rect;\n tight?: Rect;\n flags: number;\n}\n\n/** A contiguous run of glyphs (a text object). `rect` is its loose enclosing box. */\nexport interface RunInfo {\n start: number; // index into PageText.glyphs (the selection coordinate)\n count: number;\n rect: Rect;\n fontSize?: number;\n}\n\n/** A page's text laid out in content space: a flat glyph list + the run structure. */\nexport interface PageText {\n glyphs: GlyphInfo[];\n runs: RunInfo[];\n}\n\nconst toContent = (m: Parameters<typeof applyRect>[0], b: PdfRect): Rect =>\n applyRect(\n m as never,\n {\n x: b.left,\n y: b.bottom,\n width: b.right - b.left,\n height: b.top - b.bottom,\n } as RectIn<'pdf'>,\n ) as Rect;\n\n/**\n * Flatten a page's text geometry into content-space glyphs + runs. Rotation does\n * NOT enter here — the overlay rides the page's CSS rotation, so glyphs stay in\n * un-rotated content space (the crop-relative y-flip is the only conversion).\n */\nexport function buildPageText(\n snapshot: PageGeometrySnapshot,\n crop: PdfRect,\n rotation: 0 | 90 | 180 | 270,\n userUnit: number,\n): PageText {\n // zoom = 1: pdfToContent is scale-free (just the y-flip); the viewer's zoom is\n // applied later by PageTransform.pageToContent, not baked in.\n const { pdfToContent } = pageGeometry({ crop, rotation, userUnit }, 1);\n const glyphs: GlyphInfo[] = [];\n const runs: RunInfo[] = [];\n for (const run of snapshot.runs) {\n const start = glyphs.length;\n for (const g of run.glyphs) {\n glyphs.push({\n loose: toContent(pdfToContent, g.looseBox),\n tight: g.tightBox ? toContent(pdfToContent, g.tightBox) : undefined,\n flags: g.flags,\n });\n }\n runs.push({\n start,\n count: run.glyphs.length,\n rect: toContent(pdfToContent, run.rect),\n fontSize: run.fontSize,\n });\n }\n return { glyphs, runs };\n}\n\nfunction avgGlyphHeight(text: PageText): number {\n let total = 0;\n let count = 0;\n for (const g of text.glyphs) {\n if (isEmpty(g.flags)) continue;\n total += g.loose.height;\n count++;\n }\n return count === 0 ? 0 : total / count;\n}\n\n/**\n * The glyph index at a content-space point, or null when nothing is near (so the\n * caller can show the pointer cursor off-text). PDFium `GetIndexAtPos`: exact\n * tight-box containment first, then a tolerance pass (closest by Manhattan\n * distance within `toleranceFactor × average glyph height`).\n */\nexport function glyphAt(text: PageText, p: Point, toleranceFactor = 1.5): number | null {\n for (const run of text.runs) {\n const r = run.rect;\n if (p.x < r.x || p.x > r.x + r.width || p.y < r.y || p.y > r.y + r.height) continue;\n for (let i = 0; i < run.count; i++) {\n const b = text.glyphs[run.start + i].tight ?? text.glyphs[run.start + i].loose;\n if (p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height) {\n return run.start + i;\n }\n }\n }\n if (toleranceFactor <= 0) return null;\n\n const half = (avgGlyphHeight(text) * toleranceFactor) / 2;\n let best = -1;\n let bestDist = Infinity;\n for (const run of text.runs) {\n const r = run.rect;\n if (\n p.y < r.y - half ||\n p.y > r.y + r.height + half ||\n p.x < r.x - half ||\n p.x > r.x + r.width + half\n ) {\n continue;\n }\n for (let i = 0; i < run.count; i++) {\n const g = text.glyphs[run.start + i];\n if (isEmpty(g.flags)) continue;\n const b = g.tight ?? g.loose;\n if (\n p.x < b.x - half ||\n p.x > b.x + b.width + half ||\n p.y < b.y - half ||\n p.y > b.y + b.height + half\n ) {\n continue;\n }\n const dx = Math.min(Math.abs(p.x - b.x), Math.abs(p.x - (b.x + b.width)));\n const dy = Math.min(Math.abs(p.y - b.y), Math.abs(p.y - (b.y + b.height)));\n if (dx + dy < bestDist) {\n bestDist = dx + dy;\n best = run.start + i;\n }\n }\n }\n return best >= 0 ? best : null;\n}\n\n/** Double-click: the word around `i` (walk to space/empty glyphs both ways). */\nexport function expandToWord(text: PageText, i: number): [number, number] {\n const n = text.glyphs.length;\n if (i < 0 || i >= n) return [i, i];\n let from = i;\n while (from > 0 && !isBoundary(text.glyphs[from - 1].flags)) from--;\n let to = i;\n while (to < n - 1 && !isBoundary(text.glyphs[to + 1].flags)) to++;\n return [from, to];\n}\n\n/** Triple-click: the full visual line — runs whose vertical extent overlaps the anchor run's. */\nexport function expandToLine(text: PageText, i: number): [number, number] {\n const ri = text.runs.findIndex((r) => i >= r.start && i < r.start + r.count);\n if (ri < 0) return [i, i];\n const anchor = text.runs[ri];\n const top = anchor.rect.y;\n const bottom = anchor.rect.y + anchor.rect.height;\n let from = anchor.start;\n let to = anchor.start + anchor.count - 1;\n for (let r = ri - 1; r >= 0; r--) {\n const run = text.runs[r];\n if (isZero(run.rect)) continue;\n if (!overlapV(run.rect.y, run.rect.y + run.rect.height, top, bottom)) break;\n from = run.start;\n }\n for (let r = ri + 1; r < text.runs.length; r++) {\n const run = text.runs[r];\n if (isZero(run.rect)) continue;\n if (!overlapV(run.rect.y, run.rect.y + run.rect.height, top, bottom)) break;\n to = run.start + run.count - 1;\n }\n return [from, to];\n}\n\n/* ── line-merge ────────────────────────────────────────────────────────────\n * Adapted from Chromium's pdf/pdfium/pdfium_range.cc (BSD-licensed,\n * Copyright 2010 The Chromium Authors). Glyphs → sub-runs (split on big intra-run\n * gaps) → merged line rects. */\n\nconst CHAR_DISTANCE_FACTOR = 2.5;\nconst FONT_SIZE_RATIO_THRESHOLD = 1.5;\nconst VERTICAL_OVERLAP_THRESHOLD = 0.8;\nconst LINE_OVERLAP_THRESHOLD = 0.5;\n\ninterface SubRun {\n rect: Rect;\n charCount: number;\n fontSize?: number;\n}\n\nexport function rectsForRange(text: PageText, from: number, to: number): Rect[] {\n const lo = Math.max(0, Math.min(from, to));\n const hi = Math.min(text.glyphs.length - 1, Math.max(from, to));\n const subRuns: SubRun[] = [];\n\n for (const run of text.runs) {\n const runEnd = run.start + run.count - 1;\n if (runEnd < lo || run.start > hi) continue;\n const s = Math.max(lo, run.start);\n const e = Math.min(hi, runEnd);\n\n let minX = Infinity;\n let maxX = -Infinity;\n let minY = Infinity;\n let maxY = -Infinity;\n let charCount = 0;\n let widthSum = 0;\n let prevRight = -Infinity;\n const flush = () => {\n if (minX !== Infinity && charCount > 0) {\n subRuns.push({\n rect: { x: minX, y: minY, width: maxX - minX, height: maxY - minY },\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 gi = s; gi <= e; gi++) {\n const g = text.glyphs[gi];\n if (isEmpty(g.flags)) continue;\n const b = g.loose;\n if (charCount > 0 && prevRight > -Infinity) {\n const avg = widthSum / charCount;\n if (avg > 0 && Math.abs(b.x - prevRight) > CHAR_DISTANCE_FACTOR * avg) flush();\n }\n minX = Math.min(minX, b.x);\n maxX = Math.max(maxX, b.x + b.width);\n minY = Math.min(minY, b.y);\n maxY = Math.max(maxY, b.y + b.height);\n charCount++;\n widthSum += b.width;\n prevRight = b.x + b.width;\n }\n flush();\n }\n\n return mergeAdjacentRects(subRuns);\n}\n\nfunction mergeAdjacentRects(runs: SubRun[]): Rect[] {\n const out: Rect[] = [];\n let prev: SubRun | null = null;\n let cur: Rect | null = null;\n for (const run of runs) {\n if (prev && cur && shouldMerge(prev, run)) {\n cur = boundsOfRects([cur, run.rect])!;\n } else {\n if (cur) out.push(cur);\n cur = run.rect;\n }\n prev = run;\n }\n if (cur && cur.width > 0 && cur.height > 0) out.push(cur);\n return out;\n}\n\nfunction shouldMerge(a: SubRun, b: SubRun): boolean {\n if (a.fontSize != null && b.fontSize != null && a.fontSize > 0 && b.fontSize > 0) {\n const ratio = Math.max(a.fontSize, b.fontSize) / Math.min(a.fontSize, b.fontSize);\n if (ratio > FONT_SIZE_RATIO_THRESHOLD) return false;\n }\n if (verticalOverlap(a.rect, b.rect) < VERTICAL_OVERLAP_THRESHOLD) return false;\n const aw = a.rect.width / a.charCount;\n const bw = b.rect.width / b.charCount;\n const aL = a.rect.x - aw;\n const aR = a.rect.x + a.rect.width + aw;\n const bL = b.rect.x - bw;\n const bR = b.rect.x + b.rect.width + bw;\n return aL < bR && aR > bL;\n}\n\nfunction verticalOverlap(a: Rect, b: Rect): number {\n if (a.height <= 0 || b.height <= 0) return 0;\n const u = Math.max(a.y + a.height, b.y + b.height) - Math.min(a.y, b.y);\n if (u === a.height || u === b.height) return 1;\n const i = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));\n return i / u;\n}\n\nconst isZero = (r: Rect): boolean => r.width === 0 && r.height === 0;\n\nfunction overlapV(top1: number, bottom1: number, top2: number, bottom2: number): boolean {\n const u = Math.max(bottom1, bottom2) - Math.min(top1, top2);\n if (u === 0) return false;\n const i = Math.max(0, Math.min(bottom1, bottom2) - Math.max(top1, top2));\n return i / u >= LINE_OVERLAP_THRESHOLD;\n}\n","import type { PageObjectNumber, PluginContext } from '@embedpdf/core';\nimport type { Point, Rect } from '@embedpdf/core-geometry';\nimport {\n buildPageText,\n expandToLine,\n expandToWord,\n glyphAt,\n rectsForRange,\n type PageText,\n} from './geometry';\nimport type {\n GlyphPointer,\n SelectionAction,\n SelectionCapability,\n SelectionEndpoint,\n SelectionRange,\n SelectionSnapshot,\n SelectionState,\n} from './types';\n\nconst EMPTY: Rect[] = [];\n\n/**\n * The selection capability. The reducer state holds the selection range, the\n * derived content-space rects (per page), and per-page loaded flags; the (large,\n * non-serializable) per-page text geometry is cached HERE in the closure.\n *\n * Selection is cross-page: glyphs are ordered globally by (pageIndex, glyph), so\n * a drag from page 2 into page 4 selects the tail of 2, all of 3, and the head of\n * 4. `recompute` rebuilds the merged line rects for every loaded page in the span\n * and re-runs whenever a mid-span page finishes loading.\n */\nexport function createSelectionCapability(\n ctx: PluginContext<SelectionState, SelectionAction>,\n): SelectionCapability {\n const cache = new Map<number, PageText>();\n const pending = new Set<number>();\n // Consumers (e.g. text-markup) observe the selection without selection knowing\n // about them: `change` fires whenever the rects change, `commit` when a gesture\n // ends (pointer-up). One typed callback each — not an event bus.\n const changeCbs = new Set<() => void>();\n const commitCbs = new Set<() => void>();\n const fireChange = (): void => changeCbs.forEach((cb) => cb());\n\n const pageIndexOf = (pon: PageObjectNumber): number =>\n ctx.document()?.pages.findIndex((p) => p.pageObjectNumber === pon) ?? -1;\n const ponAtIndex = (i: number): PageObjectNumber | undefined =>\n ctx.document()?.pages[i]?.pageObjectNumber;\n\n // Order the two ends of a selection by document position (page, then glyph).\n function orderedEnds(sel: SelectionRange): {\n start: GlyphPointer;\n end: GlyphPointer;\n direction: 'forward' | 'backward';\n } {\n const ai = pageIndexOf(sel.anchor.pon);\n const fi = pageIndexOf(sel.focus.pon);\n const anchorFirst = ai < fi || (ai === fi && sel.anchor.glyph <= sel.focus.glyph);\n return anchorFirst\n ? { start: sel.anchor, end: sel.focus, direction: 'forward' }\n : { start: sel.focus, end: sel.anchor, direction: 'backward' };\n }\n\n function endpointFor(ptr: GlyphPointer, which: 'start' | 'end'): SelectionEndpoint | null {\n const rects = ctx.getState().rects[ptr.pon] ?? EMPTY;\n if (!rects.length) return null;\n return { pon: ptr.pon, rect: which === 'start' ? rects[0] : rects[rects.length - 1] };\n }\n\n function snapshot(): SelectionSnapshot {\n const { selection, rects } = ctx.getState();\n const pages = Object.keys(rects)\n .map(Number)\n .filter((pon) => (rects[pon]?.length ?? 0) > 0)\n .map((pon) => ({ pon: pon as PageObjectNumber, rects: rects[pon] }));\n if (!selection) return { pages, start: null, end: null, direction: 'forward' };\n const { start, end, direction } = orderedEnds(selection);\n return {\n pages,\n start: endpointFor(start, 'start'),\n end: endpointFor(end, 'end'),\n direction,\n };\n }\n\n function ensurePage(pon: PageObjectNumber): void {\n if (cache.has(pon) || pending.has(pon)) return;\n const doc = ctx.doc;\n const layout = ctx.document()?.pages.find((p) => p.pageObjectNumber === pon);\n if (!doc || !layout) return;\n pending.add(pon);\n doc\n .page(pon)\n .geometry.read()\n .then(\n (snapshot) => {\n pending.delete(pon);\n cache.set(\n pon,\n buildPageText(snapshot, layout.boxes.crop, layout.rotation, layout.userUnit),\n );\n ctx.dispatch({ type: 'PAGE_LOADED', pon });\n if (ctx.getState().selection) recompute(); // a mid-span page arrived → fill its rects\n },\n () => {\n pending.delete(pon); // doc closed / read aborted — ignore\n },\n );\n }\n\n // Rebuild merged line rects for every loaded page in the span; ensure the rest.\n function recompute(sel: SelectionRange | null = ctx.getState().selection): void {\n if (!sel) return;\n const { start, end } = orderedEnds(sel);\n const si = pageIndexOf(start.pon);\n const ei = pageIndexOf(end.pon);\n if (si < 0 || ei < 0) return;\n const rects: Record<number, Rect[]> = {};\n for (let i = si; i <= ei; i++) {\n const pon = ponAtIndex(i);\n if (pon == null) continue;\n const text = cache.get(pon);\n if (!text) {\n ensurePage(pon); // not loaded yet — it'll recompute when ready\n continue;\n }\n const from = i === si ? start.glyph : 0;\n const to = i === ei ? end.glyph : text.glyphs.length - 1;\n rects[pon] = rectsForRange(text, from, to);\n }\n ctx.dispatch({ type: 'SET', selection: sel, rects });\n fireChange();\n }\n\n // Set the selection to a flat [from,to] glyph span on one page (word/line).\n function selectSpan(pon: PageObjectNumber, point: Point, expand: 'word' | 'line'): void {\n const text = cache.get(pon);\n if (!text) return;\n const i = glyphAt(text, point);\n if (i == null) return;\n const [from, to] = expand === 'word' ? expandToWord(text, i) : expandToLine(text, i);\n recompute({ anchor: { pon, glyph: from }, focus: { pon, glyph: to } });\n }\n\n return {\n ensurePage,\n\n isLoaded: (pon) => !!ctx.getState().loaded[pon],\n\n isOverText: (pon, point: Point) => {\n const text = cache.get(pon);\n return text ? glyphAt(text, point) != null : false;\n },\n\n beginAt: (pon, point: Point) => {\n const text = cache.get(pon);\n if (!text) return false;\n const i = glyphAt(text, point);\n if (i == null) return false; // not near text — caller deselects, doesn't capture\n recompute({ anchor: { pon, glyph: i }, focus: { pon, glyph: i } });\n return true;\n },\n\n selectWord: (pon, point: Point) => selectSpan(pon, point, 'word'),\n selectLine: (pon, point: Point) => selectSpan(pon, point, 'line'),\n\n extendTo: (pon, point: Point) => {\n const cur = ctx.getState().selection;\n if (!cur) return;\n const text = cache.get(pon);\n if (!text) {\n ensurePage(pon); // dragged onto a not-yet-loaded page — warm it, recompute on load\n return;\n }\n const i = glyphAt(text, point);\n if (i == null) return; // off-text — keep the last focus\n recompute({ anchor: cur.anchor, focus: { pon, glyph: i } });\n },\n\n end: () => commitCbs.forEach((cb) => cb()), // the gesture ended (pointer-up) → notify consumers\n\n clear: () => {\n ctx.dispatch({ type: 'CLEAR' });\n fireChange();\n },\n\n snapshot,\n\n rectsForPage: (pon) => ctx.getState().rects[pon] ?? EMPTY,\n\n hasSelection: () => ctx.getState().selection != null,\n\n selectedPages: () => {\n const { rects } = ctx.getState();\n return Object.keys(rects)\n .map(Number)\n .filter((pon) => (rects[pon]?.length ?? 0) > 0) as PageObjectNumber[];\n },\n\n onChange: (cb) => {\n changeCbs.add(cb);\n return () => changeCbs.delete(cb);\n },\n onCommit: (cb) => {\n commitCbs.add(cb);\n return () => commitCbs.delete(cb);\n },\n\n setHighlightVisible: (visible) =>\n ctx.dispatch({ type: 'SET_HIGHLIGHT_HIDDEN', hidden: !visible }),\n highlightVisible: () => !ctx.getState().highlightHidden,\n };\n}\n","import type { InteractionCapability, InteractionHandler } from '@embedpdf/plugin-interaction';\nimport type { SelectionCapability } from './types';\n\nconst CURSOR_TOKEN = 'selection-text';\nconst DRAG_THRESHOLD_PX = 4; // viewport px the pointer must move before a drag-select begins\n\n/**\n * The text-selection pointer handler. It is live only under tools that enable the\n * `'text-select'` tag (the built-in `pointer` tool does; `pan` does not — so\n * switching to pan disables text selection with zero special-casing here).\n *\n * - single click → deselect; records an anchor but selects NOTHING until\n * the pointer moves past the drag threshold (Chrome/Acrobat feel)\n * - single-click + drag → caret selection from the anchor (extends across pages)\n * - double-click → select the word\n * - triple-click → select the visual line\n * - down off-text → deselect, and DON'T capture (so it can't block)\n * - hover → I-beam only when over text, else the pointer cursor\n */\nexport function createTextSelectHandler(\n selection: SelectionCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // Per-gesture drag-threshold state (one active gesture at a time — the hub owner).\n let anchor: { pon: number; point: { x: number; y: number }; vx: number; vy: number } | null =\n null;\n let dragging = false;\n\n return {\n id: 'text-select',\n priority: 60,\n enabledFor: (tool) => tool.enables.has('text-select'),\n onDown: (s) => {\n anchor = null;\n dragging = false;\n if (!s.page) return false; // over a gap — let a lower handler (e.g. scroll) try\n const { pon, point } = s.page;\n const clicks = s.clickCount ?? 1;\n if (clicks >= 3) {\n selection.selectLine(pon, point);\n return true;\n }\n if (clicks === 2) {\n selection.selectWord(pon, point);\n return true;\n }\n // Single click: clear immediately (clicking deselects), then record an anchor\n // ONLY if over text — but begin no selection until the drag threshold is met.\n selection.clear();\n if (!selection.isOverText(pon, point)) return false; // empty space → don't capture\n anchor = { pon, point, vx: s.viewport.x, vy: s.viewport.y };\n return true;\n },\n onMove: (s) => {\n if (!s.page || !anchor) return;\n if (!dragging) {\n if (Math.hypot(s.viewport.x - anchor.vx, s.viewport.y - anchor.vy) < DRAG_THRESHOLD_PX) {\n return; // still a click, not a drag — select nothing yet\n }\n dragging = true;\n selection.beginAt(anchor.pon, anchor.point); // open the selection at the anchor\n }\n selection.extendTo(s.page.pon, s.page.point);\n },\n onUp: () => {\n anchor = null;\n dragging = false;\n selection.end();\n },\n onHover: (s) => {\n if (!s.page) {\n interaction.setCursor(CURSOR_TOKEN, null); // off the page → pointer\n return;\n }\n // Warm geometry on first hover; show the I-beam ONLY when actually over text.\n selection.ensurePage(s.page.pon);\n const overText = selection.isOverText(s.page.pon, s.page.point);\n interaction.setCursor(CURSOR_TOKEN, overText ? 'text' : null, 10);\n },\n };\n}\n","import type { SelectionAction, SelectionState } from './types';\n\nexport const initialSelectionState: SelectionState = {\n selection: null,\n rects: {},\n loaded: {},\n highlightHidden: false,\n};\n\nexport const selectionReducer = (\n state: SelectionState,\n action: SelectionAction,\n): SelectionState => {\n switch (action.type) {\n case 'PAGE_LOADED':\n return state.loaded[action.pon]\n ? state\n : { ...state, loaded: { ...state.loaded, [action.pon]: true } };\n case 'SET':\n return { ...state, selection: action.selection, rects: action.rects };\n case 'CLEAR':\n return state.selection === null && Object.keys(state.rects).length === 0\n ? state\n : { ...state, selection: null, rects: {} };\n case 'SET_HIGHLIGHT_HIDDEN':\n return state.highlightHidden === action.hidden\n ? state\n : { ...state, highlightHidden: action.hidden };\n default:\n return state;\n }\n};\n","import { createCapabilityToken, type PageObjectNumber } from '@embedpdf/core';\nimport type { Point, Rect } from '@embedpdf/core-geometry';\n\n/** A glyph address: a page + a flat glyph index within that page's geometry. */\nexport interface GlyphPointer {\n pon: PageObjectNumber;\n glyph: number;\n}\n\n/** Anchor = where the drag began, focus = the current end. Inclusive. */\nexport interface SelectionRange {\n anchor: GlyphPointer;\n focus: GlyphPointer;\n}\n\nexport interface SelectionEndpoint {\n pon: PageObjectNumber;\n rect: Rect;\n}\n\nexport interface SelectionSnapshot {\n pages: Array<{ pon: PageObjectNumber; rects: Rect[] }>;\n start: SelectionEndpoint | null;\n end: SelectionEndpoint | null;\n direction: 'forward' | 'backward';\n}\n\nexport interface SelectionState {\n selection: SelectionRange | null;\n /** Derived highlight rects per page, in CONTENT space (y-down, PDF units). */\n rects: Record<number, Rect[]>;\n /** Pages whose text geometry has loaded (so the layer re-renders when ready). */\n loaded: Record<number, boolean>;\n /** When a consumer owns the selection visual (e.g. a markup tool draws its own\n * preview), the default highlight rects are suppressed. */\n highlightHidden: boolean;\n}\n\nexport type SelectionAction =\n | { type: 'PAGE_LOADED'; pon: PageObjectNumber }\n | { type: 'SET'; selection: SelectionRange; rects: Record<number, Rect[]> }\n | { type: 'CLEAR' }\n | { type: 'SET_HIGHLIGHT_HIDDEN'; hidden: boolean };\n\nexport interface SelectionCapability {\n /** Warm a page's text geometry (idempotent). Layers call this when a page mounts. */\n ensurePage(pon: PageObjectNumber): void;\n isLoaded(pon: PageObjectNumber): boolean;\n /** Is a content-space point on (or near) text? Drives the I-beam vs pointer cursor. */\n isOverText(pon: PageObjectNumber, point: Point): boolean;\n /** Begin a caret selection at a page point. Returns false if not near any text. */\n beginAt(pon: PageObjectNumber, point: Point): boolean;\n /** Double-click: select the word around the point. */\n selectWord(pon: PageObjectNumber, point: Point): void;\n /** Triple-click: select the whole visual line around the point. */\n selectLine(pon: PageObjectNumber, point: Point): void;\n /** Extend the current selection to a page point (drag). */\n extendTo(pon: PageObjectNumber, point: Point): void;\n end(): void;\n clear(): void;\n /** Coherent read-model for consumers that create annotations or selection UI. */\n snapshot(): SelectionSnapshot;\n /** Highlight rects for a page, in content space — the layer's only input. */\n rectsForPage(pon: PageObjectNumber): Rect[];\n hasSelection(): boolean;\n /** The pages the current selection covers (those with at least one rect) — so a\n * cross-page action (e.g. text-markup creation) can fan out per page. */\n selectedPages(): PageObjectNumber[];\n /** Fires whenever the selection rects change (e.g. drag-extend) — for live preview. */\n onChange(cb: () => void): () => void;\n /** Fires when a selection gesture ends (pointer-up) — the commit point. */\n onCommit(cb: () => void): () => void;\n /** Suppress / restore the default highlight visual (a consumer drawing its own). */\n setHighlightVisible(visible: boolean): void;\n highlightVisible(): boolean;\n}\n\nexport const SelectionToken = createCapabilityToken<SelectionCapability>('selection');\n","import { definePlugin } from '@embedpdf/core';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { createSelectionCapability } from './capability';\nimport { createTextSelectHandler } from './handler';\nimport { initialSelectionState, selectionReducer } from './reducer';\nimport { SelectionToken } from './types';\nimport type { SelectionAction, SelectionCapability, SelectionState } from './types';\n\n/**\n * Text selection — document-scoped, requires the interaction hub. In `init` it\n * registers ITS pointer handler with the hub; the hub owns the pointer stream\n * and arbitration. Works with `<Stage>` or a standalone `<PageView>` — selection\n * only needs the page coordinate context + the engine's text geometry.\n */\nexport const selectionPlugin = () =>\n definePlugin<SelectionState, SelectionAction, SelectionCapability>({\n id: 'selection',\n token: SelectionToken,\n scope: 'document',\n requires: [InteractionToken],\n initialState: initialSelectionState,\n reduce: selectionReducer,\n capability: createSelectionCapability,\n init: (ctx) => {\n const interaction = ctx.get(InteractionToken);\n const selection = ctx.get(SelectionToken); // our own capability (built before init)\n interaction.registerHandler(createTextSelectHandler(selection, interaction));\n },\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,85 +1,44 @@
1
1
  {
2
2
  "name": "@embedpdf/plugin-selection",
3
- "version": "2.14.4",
3
+ "version": "3.0.0-next.0",
4
+ "private": false,
5
+ "description": "Text selection: reads engine text geometry (PDF y-up), bridges it to viewer content space, hit-tests glyphs, and renders highlight rects. The first client of the interaction hub.",
4
6
  "type": "module",
5
- "license": "MIT",
6
- "main": "./dist/index.cjs",
7
+ "main": "./dist/index.js",
7
8
  "module": "./dist/index.js",
8
9
  "types": "./dist/index.d.ts",
9
10
  "exports": {
10
11
  ".": {
11
- "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
14
  },
15
- "./preact": {
16
- "types": "./dist/preact/index.d.ts",
17
- "import": "./dist/preact/index.js",
18
- "require": "./dist/preact/index.cjs"
19
- },
20
- "./react": {
21
- "types": "./dist/react/index.d.ts",
22
- "import": "./dist/react/index.js",
23
- "require": "./dist/react/index.cjs"
24
- },
25
- "./vue": {
26
- "types": "./dist/vue/index.d.ts",
27
- "import": "./dist/vue/index.js",
28
- "require": "./dist/vue/index.cjs"
29
- },
30
- "./svelte": {
31
- "types": "./dist/svelte/index.d.ts",
32
- "import": "./dist/svelte/index.js",
33
- "require": "./dist/svelte/index.cjs"
34
- }
15
+ "./package.json": "./package.json"
35
16
  },
17
+ "files": [
18
+ "dist"
19
+ ],
36
20
  "dependencies": {
37
- "@embedpdf/models": "2.14.4",
38
- "@embedpdf/utils": "2.14.4"
21
+ "@embedpdf/core": "3.0.0-next.0",
22
+ "@embedpdf/core-geometry": "3.0.0-next.0",
23
+ "@embedpdf/plugin-interaction": "3.0.0-next.0",
24
+ "@embedpdf/engine-core": "3.0.0-next.0"
39
25
  },
40
26
  "devDependencies": {
41
- "@types/react": "^18.2.0",
42
27
  "typescript": "^5.0.0",
43
- "@embedpdf/core": "2.14.4",
44
- "@embedpdf/plugin-viewport": "2.14.4",
45
- "@embedpdf/plugin-interaction-manager": "2.14.4",
46
- "@embedpdf/build": "1.1.0",
47
- "@embedpdf/plugin-scroll": "2.14.4"
28
+ "vitest": "^2.1.9",
29
+ "@embedpdf/tooling-build": "0.0.0"
48
30
  },
49
- "peerDependencies": {
50
- "react": ">=16.8.0",
51
- "react-dom": ">=16.8.0",
52
- "preact": "^10.26.4",
53
- "vue": ">=3.2.0",
54
- "svelte": ">=5 <6",
55
- "@embedpdf/core": "2.14.4",
56
- "@embedpdf/plugin-interaction-manager": "2.14.4"
57
- },
58
- "files": [
59
- "dist",
60
- "README.md"
61
- ],
31
+ "license": "Apache-2.0",
32
+ "sideEffects": false,
62
33
  "repository": {
63
34
  "type": "git",
64
- "url": "https://github.com/embedpdf/embed-pdf-viewer",
65
- "directory": "packages/plugin-selection"
66
- },
67
- "homepage": "https://www.embedpdf.com/docs",
68
- "bugs": {
69
- "url": "https://github.com/embedpdf/embed-pdf-viewer/issues"
70
- },
71
- "publishConfig": {
72
- "access": "public"
35
+ "url": "git+https://github.com/embedpdf/embed-pdf-viewer.git",
36
+ "directory": "packages/plugin/selection"
73
37
  },
74
38
  "scripts": {
75
- "build:base": "vite build --mode base",
76
- "build:react": "vite build --mode react",
77
- "build:preact": "vite build --mode preact",
78
- "build:vue": "vite build --mode vue",
79
- "build:svelte": "vite build --mode svelte",
80
- "build": "pnpm run clean && concurrently -c auto -n base,react,preact,vue,svelte \"vite build --mode base\" \"vite build --mode react\" \"vite build --mode preact\" \"vite build --mode vue\" \"vite build --mode svelte\"",
39
+ "build": "epdf-build",
81
40
  "clean": "rimraf dist",
82
- "lint": "eslint src --color",
83
- "lint:fix": "eslint src --color --fix"
41
+ "typecheck": "tsc -p tsconfig.json --noEmit",
42
+ "test": "vitest run"
84
43
  }
85
44
  }
@@ -1,103 +0,0 @@
1
- import { Action } from '@embedpdf/core';
2
- import { PdfPageGeometry, Rect } from '@embedpdf/models';
3
- import { SelectionDocumentState, SelectionRangeX } from './types';
4
- export declare const INIT_SELECTION_STATE = "SELECTION/INIT_STATE";
5
- export declare const CLEANUP_SELECTION_STATE = "SELECTION/CLEANUP_STATE";
6
- export declare const CACHE_PAGE_GEOMETRY = "SELECTION/CACHE_PAGE_GEOMETRY";
7
- export declare const SET_SELECTION = "SELECTION/SET_SELECTION";
8
- export declare const START_SELECTION = "SELECTION/START_SELECTION";
9
- export declare const END_SELECTION = "SELECTION/END_SELECTION";
10
- export declare const CLEAR_SELECTION = "SELECTION/CLEAR_SELECTION";
11
- export declare const SET_RECTS = "SELECTION/SET_RECTS";
12
- export declare const SET_SLICES = "SELECTION/SET_SLICES";
13
- export declare const EVICT_PAGE_GEOMETRY = "SELECTION/EVICT_PAGE_GEOMETRY";
14
- export declare const RESET = "SELECTION/RESET";
15
- export interface InitSelectionStateAction extends Action {
16
- type: typeof INIT_SELECTION_STATE;
17
- payload: {
18
- documentId: string;
19
- state: SelectionDocumentState;
20
- };
21
- }
22
- export interface CleanupSelectionStateAction extends Action {
23
- type: typeof CLEANUP_SELECTION_STATE;
24
- payload: string;
25
- }
26
- export interface CachePageGeometryAction extends Action {
27
- type: typeof CACHE_PAGE_GEOMETRY;
28
- payload: {
29
- documentId: string;
30
- page: number;
31
- geo: PdfPageGeometry;
32
- };
33
- }
34
- export interface SetSelectionAction extends Action {
35
- type: typeof SET_SELECTION;
36
- payload: {
37
- documentId: string;
38
- selection: SelectionRangeX | null;
39
- };
40
- }
41
- export interface StartSelectionAction extends Action {
42
- type: typeof START_SELECTION;
43
- payload: {
44
- documentId: string;
45
- };
46
- }
47
- export interface EndSelectionAction extends Action {
48
- type: typeof END_SELECTION;
49
- payload: {
50
- documentId: string;
51
- };
52
- }
53
- export interface ClearSelectionAction extends Action {
54
- type: typeof CLEAR_SELECTION;
55
- payload: {
56
- documentId: string;
57
- };
58
- }
59
- export interface SetRectsAction extends Action {
60
- type: typeof SET_RECTS;
61
- payload: {
62
- documentId: string;
63
- rects: Record<number, Rect[]>;
64
- };
65
- }
66
- export interface SetSlicesAction extends Action {
67
- type: typeof SET_SLICES;
68
- payload: {
69
- documentId: string;
70
- slices: Record<number, {
71
- start: number;
72
- count: number;
73
- }>;
74
- };
75
- }
76
- export interface EvictPageGeometryAction extends Action {
77
- type: typeof EVICT_PAGE_GEOMETRY;
78
- payload: {
79
- documentId: string;
80
- pages: number[];
81
- };
82
- }
83
- export interface ResetAction extends Action {
84
- type: typeof RESET;
85
- payload: {
86
- documentId: string;
87
- };
88
- }
89
- export type SelectionAction = InitSelectionStateAction | CleanupSelectionStateAction | CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | SetSlicesAction | EvictPageGeometryAction | ResetAction;
90
- export declare const initSelectionState: (documentId: string, state: SelectionDocumentState) => InitSelectionStateAction;
91
- export declare const cleanupSelectionState: (documentId: string) => CleanupSelectionStateAction;
92
- export declare const cachePageGeometry: (documentId: string, page: number, geo: PdfPageGeometry) => CachePageGeometryAction;
93
- export declare const setSelection: (documentId: string, sel: SelectionRangeX | null) => SetSelectionAction;
94
- export declare const startSelection: (documentId: string) => StartSelectionAction;
95
- export declare const endSelection: (documentId: string) => EndSelectionAction;
96
- export declare const clearSelection: (documentId: string) => ClearSelectionAction;
97
- export declare const setRects: (documentId: string, allRects: Record<number, Rect[]>) => SetRectsAction;
98
- export declare const setSlices: (documentId: string, slices: Record<number, {
99
- start: number;
100
- count: number;
101
- }>) => SetSlicesAction;
102
- export declare const evictPageGeometry: (documentId: string, pages: number[]) => EvictPageGeometryAction;
103
- export declare const reset: (documentId: string) => ResetAction;
@@ -1,2 +0,0 @@
1
- export * from './text-selection.handler';
2
- export * from './marquee-selection.handler';
@@ -1,30 +0,0 @@
1
- import { Position, Rect, Size } from '@embedpdf/models';
2
- import { EmbedPdfPointerEvent, PointerEventHandlersWithLifecycle } from '@embedpdf/plugin-interaction-manager';
3
- export interface MarqueeSelectionHandlerOptions {
4
- /** The page size for clamping */
5
- pageSize: Size;
6
- /** Current scale factor for min drag threshold calculation */
7
- scale: number;
8
- /** Minimum drag distance in pixels before considering it a marquee (default: 5) */
9
- minDragPx?: number;
10
- /** Check if marquee selection is enabled for this mode */
11
- isEnabled: (modeId: string) => boolean;
12
- /** Returns whether text selection is currently active (skip marquee if so) */
13
- isTextSelecting?: () => boolean;
14
- /** Called when marquee selection begins */
15
- onBegin: (startPos: Position, modeId: string) => void;
16
- /** Called during drag with the current marquee rect */
17
- onChange: (rect: Rect, modeId: string) => void;
18
- /** Called when marquee selection completes (drag was large enough) */
19
- onEnd: (rect: Rect, modeId: string) => void;
20
- /** Called when marquee selection is cancelled (drag too small or cancelled) */
21
- onCancel: (modeId: string) => void;
22
- }
23
- /**
24
- * Creates a marquee selection handler that allows users to drag a selection rectangle.
25
- *
26
- * This handler is meant to be combined with the text selection handler. When text is hit,
27
- * the text selection handler sets its selecting state, and this handler checks via
28
- * `isTextSelecting` to avoid activating during text selection.
29
- */
30
- export declare function createMarqueeSelectionHandler(opts: MarqueeSelectionHandlerOptions): PointerEventHandlersWithLifecycle<EmbedPdfPointerEvent>;