@embedpdf/plugin-selection 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 CloudPDF
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SELECTION_PLUGIN_ID: () => SELECTION_PLUGIN_ID,
24
+ SelectionPlugin: () => SelectionPlugin,
25
+ SelectionPluginPackage: () => SelectionPluginPackage,
26
+ glyphAt: () => glyphAt,
27
+ manifest: () => manifest
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/lib/manifest.ts
32
+ var SELECTION_PLUGIN_ID = "selection";
33
+ var manifest = {
34
+ id: SELECTION_PLUGIN_ID,
35
+ name: "Selection Plugin",
36
+ version: "1.0.0",
37
+ provides: ["selection"],
38
+ requires: ["interaction-manager"],
39
+ optional: [],
40
+ defaultConfig: {
41
+ enabled: true
42
+ }
43
+ };
44
+
45
+ // src/lib/selection-plugin.ts
46
+ var import_core = require("@embedpdf/core");
47
+
48
+ // src/lib/actions.ts
49
+ var CACHE_PAGE_GEOMETRY = "CACHE_PAGE_GEOMETRY";
50
+ var SET_SELECTION = "SET_SELECTION";
51
+ var START_SELECTION = "START_SELECTION";
52
+ var END_SELECTION = "END_SELECTION";
53
+ var CLEAR_SELECTION = "CLEAR_SELECTION";
54
+ var SET_RECTS = "SET_RECTS";
55
+ var RESET = "RESET";
56
+ var cachePageGeometry = (page, geo) => ({
57
+ type: CACHE_PAGE_GEOMETRY,
58
+ payload: { page, geo }
59
+ });
60
+ var setSelection = (sel) => ({
61
+ type: SET_SELECTION,
62
+ payload: sel
63
+ });
64
+ var startSelection = () => ({ type: START_SELECTION });
65
+ var endSelection = () => ({ type: END_SELECTION });
66
+ var clearSelection = () => ({ type: CLEAR_SELECTION });
67
+ var setRects = (page, rects) => ({
68
+ type: SET_RECTS,
69
+ payload: { page, rects }
70
+ });
71
+ var reset = () => ({ type: RESET });
72
+
73
+ // src/lib/selection-plugin.ts
74
+ var import_core2 = require("@embedpdf/core");
75
+
76
+ // src/lib/selectors.ts
77
+ var import_models = require("@embedpdf/models");
78
+ function selectRectsForPage(state, page) {
79
+ return state.rects[page] ?? [];
80
+ }
81
+ function selectBoundingRectForPage(state, page) {
82
+ return (0, import_models.boundingRect)(selectRectsForPage(state, page));
83
+ }
84
+ function selectBoundingRectsForAllPages(state) {
85
+ const out = [];
86
+ const rectMap = state.rects;
87
+ for (const key in rectMap) {
88
+ const page = Number(key);
89
+ const bRect = (0, import_models.boundingRect)(rectMap[page]);
90
+ if (bRect) out.push({ page, rect: bRect });
91
+ }
92
+ return out;
93
+ }
94
+
95
+ // src/lib/selection-plugin.ts
96
+ var SelectionPlugin = class extends import_core.BasePlugin {
97
+ constructor(id, registry, engine) {
98
+ super(id, registry);
99
+ this.engine = engine;
100
+ /* interactive state */
101
+ this.selecting = false;
102
+ this.selChange$ = (0, import_core2.createBehaviorEmitter)();
103
+ this.coreStore.onAction(import_core.SET_DOCUMENT, (_action, state) => {
104
+ this.dispatch(reset());
105
+ this.doc = state.core.document ?? void 0;
106
+ });
107
+ }
108
+ /* ── life-cycle ────────────────────────────────────────── */
109
+ async initialize() {
110
+ }
111
+ async destroy() {
112
+ this.selChange$.clear();
113
+ }
114
+ /* ── capability exposed to UI / other plugins ─────────── */
115
+ buildCapability() {
116
+ return {
117
+ getGeometry: (p) => this.getOrLoadGeometry(p),
118
+ getHighlightRects: (p) => selectRectsForPage(this.state, p),
119
+ getBoundingRect: (p) => selectBoundingRectForPage(this.state, p),
120
+ getBoundingRects: () => selectBoundingRectsForAllPages(this.state),
121
+ begin: (p, i) => this.beginSelection(p, i),
122
+ update: (p, i) => this.updateSelection(p, i),
123
+ end: () => this.endSelection(),
124
+ clear: () => this.clearSelection(),
125
+ onSelectionChange: this.selChange$.on
126
+ };
127
+ }
128
+ /* ── geometry cache ───────────────────────────────────── */
129
+ getOrLoadGeometry(pageIdx) {
130
+ const cached = this.state.geometry[pageIdx];
131
+ if (cached) return Promise.resolve(cached);
132
+ if (!this.doc) return Promise.reject("doc closed");
133
+ const page = this.doc.pages.find((p) => p.index === pageIdx);
134
+ return new Promise((res, rej) => {
135
+ this.engine.getPageGeometry(this.doc, page).wait(
136
+ (geo) => {
137
+ this.dispatch(cachePageGeometry(pageIdx, geo));
138
+ res(geo);
139
+ },
140
+ (e) => rej(e)
141
+ );
142
+ });
143
+ }
144
+ /* ── selection state updates ───────────────────────────── */
145
+ beginSelection(page, index) {
146
+ this.selecting = true;
147
+ this.anchor = { page, index };
148
+ this.dispatch(startSelection());
149
+ }
150
+ endSelection() {
151
+ this.selecting = false;
152
+ this.anchor = void 0;
153
+ this.dispatch(endSelection());
154
+ }
155
+ clearSelection() {
156
+ this.selecting = false;
157
+ this.anchor = void 0;
158
+ this.dispatch(clearSelection());
159
+ this.selChange$.emit(null);
160
+ }
161
+ updateRectsForRange(range) {
162
+ for (let p = range.start.page; p <= range.end.page; p++) {
163
+ const rects = this.buildRectsForPage(p);
164
+ this.dispatch(setRects(p, rects));
165
+ }
166
+ }
167
+ updateSelection(page, index) {
168
+ if (!this.selecting || !this.anchor) return;
169
+ const a = this.anchor;
170
+ const forward = page > a.page || page === a.page && index >= a.index;
171
+ const start = forward ? a : { page, index };
172
+ const end = forward ? { page, index } : a;
173
+ const range = { start, end };
174
+ this.dispatch(setSelection(range));
175
+ this.updateRectsForRange(range);
176
+ this.selChange$.emit(range);
177
+ }
178
+ /* ── rect builder: 1 div per run slice ─────────────────── */
179
+ buildRectsForPage(page) {
180
+ const sel = this.state.selection;
181
+ if (!sel) return [];
182
+ if (page < sel.start.page || page > sel.end.page) return [];
183
+ const geo = this.state.geometry[page];
184
+ if (!geo) return [];
185
+ const from = page === sel.start.page ? sel.start.index : 0;
186
+ const to = page === sel.end.page ? sel.end.index : geo.runs[geo.runs.length - 1].charStart + geo.runs[geo.runs.length - 1].glyphs.length - 1;
187
+ const rects = [];
188
+ for (const run of geo.runs) {
189
+ const runStart = run.charStart;
190
+ const runEnd = runStart + run.glyphs.length - 1;
191
+ if (runEnd < from || runStart > to) continue;
192
+ const sIdx = Math.max(from, runStart) - runStart;
193
+ const eIdx = Math.min(to, runEnd) - runStart;
194
+ const left = run.glyphs[sIdx].x;
195
+ const right = run.glyphs[eIdx].x + run.glyphs[eIdx].width;
196
+ const top = run.glyphs[sIdx].y;
197
+ const bottom = run.glyphs[eIdx].y + run.glyphs[eIdx].height;
198
+ rects.push({
199
+ origin: { x: left, y: top },
200
+ size: { width: right - left, height: bottom - top }
201
+ });
202
+ }
203
+ return rects;
204
+ }
205
+ };
206
+ SelectionPlugin.id = "selection";
207
+
208
+ // src/lib/reducer.ts
209
+ var initialState = {
210
+ geometry: {},
211
+ rects: {},
212
+ selection: null,
213
+ active: false,
214
+ selecting: false
215
+ };
216
+ var selectionReducer = (state = initialState, action) => {
217
+ switch (action.type) {
218
+ case CACHE_PAGE_GEOMETRY: {
219
+ const { page, geo } = action.payload;
220
+ return { ...state, geometry: { ...state.geometry, [page]: geo } };
221
+ }
222
+ case SET_SELECTION:
223
+ return { ...state, selection: action.payload, active: true };
224
+ case START_SELECTION:
225
+ return { ...state, selecting: true, selection: null, rects: {} };
226
+ case END_SELECTION:
227
+ return { ...state, selecting: false };
228
+ case CLEAR_SELECTION:
229
+ return { ...state, selecting: false, selection: null, rects: {}, active: false };
230
+ case SET_RECTS:
231
+ return { ...state, rects: { ...state.rects, [action.payload.page]: action.payload.rects } };
232
+ case RESET:
233
+ return initialState;
234
+ default:
235
+ return state;
236
+ }
237
+ };
238
+
239
+ // src/lib/utils.ts
240
+ function glyphAt(geo, pt) {
241
+ for (const run of geo.runs) {
242
+ const inRun = pt.y >= run.rect.y && pt.y <= run.rect.y + run.rect.height && pt.x >= run.rect.x && pt.x <= run.rect.x + run.rect.width;
243
+ if (!inRun) continue;
244
+ const rel = run.glyphs.findIndex(
245
+ (g) => pt.x >= g.x && pt.x <= g.x + g.width && pt.y >= g.y && pt.y <= g.y + g.height
246
+ );
247
+ if (rel !== -1) {
248
+ return run.charStart + rel;
249
+ }
250
+ }
251
+ return -1;
252
+ }
253
+
254
+ // src/lib/index.ts
255
+ var SelectionPluginPackage = {
256
+ manifest,
257
+ create: (registry, engine) => new SelectionPlugin(SELECTION_PLUGIN_ID, registry, engine),
258
+ reducer: selectionReducer,
259
+ initialState
260
+ };
261
+ // Annotate the CommonJS export names for ESM import in node:
262
+ 0 && (module.exports = {
263
+ SELECTION_PLUGIN_ID,
264
+ SelectionPlugin,
265
+ SelectionPluginPackage,
266
+ glyphAt,
267
+ manifest
268
+ });
269
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/manifest.ts","../src/lib/selection-plugin.ts","../src/lib/actions.ts","../src/lib/selectors.ts","../src/lib/reducer.ts","../src/lib/utils.ts","../src/lib/index.ts"],"sourcesContent":["export * from './lib';\n","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: [],\n defaultConfig: {\n enabled: true,\n },\n};\n","import { BasePlugin, PluginRegistry, SET_DOCUMENT } from '@embedpdf/core';\nimport {\n SelectionCapability,\n SelectionPluginConfig,\n SelectionRangeX,\n SelectionState,\n} from './types';\nimport {\n cachePageGeometry,\n setSelection,\n SelectionAction,\n endSelection,\n startSelection,\n setRects,\n clearSelection,\n reset,\n} from './actions';\nimport { PdfEngine, PdfDocumentObject, PdfPageGeometry, TaskError, Rect } from '@embedpdf/models';\nimport { createBehaviorEmitter } from '@embedpdf/core';\nimport * as selector from './selectors';\n\nexport class SelectionPlugin extends BasePlugin<\n SelectionPluginConfig,\n SelectionCapability,\n SelectionState,\n SelectionAction\n> {\n static readonly id = 'selection' as const;\n private doc?: PdfDocumentObject;\n\n /* interactive state */\n private selecting = false;\n private anchor?: { page: number; index: number };\n\n private readonly selChange$ = createBehaviorEmitter<SelectionState['selection']>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n private engine: PdfEngine,\n ) {\n super(id, registry);\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.dispatch(reset());\n this.doc = state.core.document ?? undefined;\n });\n }\n\n /* ── life-cycle ────────────────────────────────────────── */\n async initialize() {}\n async destroy() {\n this.selChange$.clear();\n }\n\n /* ── capability exposed to UI / other plugins ─────────── */\n buildCapability(): SelectionCapability {\n return {\n getGeometry: (p) => this.getOrLoadGeometry(p),\n getHighlightRects: (p) => selector.selectRectsForPage(this.state, p),\n getBoundingRect: (p) => selector.selectBoundingRectForPage(this.state, p),\n getBoundingRects: () => selector.selectBoundingRectsForAllPages(this.state),\n begin: (p, i) => this.beginSelection(p, i),\n update: (p, i) => this.updateSelection(p, i),\n end: () => this.endSelection(),\n clear: () => this.clearSelection(),\n\n onSelectionChange: this.selChange$.on,\n };\n }\n\n /* ── geometry cache ───────────────────────────────────── */\n private getOrLoadGeometry(pageIdx: number): Promise<PdfPageGeometry> {\n const cached = this.state.geometry[pageIdx];\n if (cached) return Promise.resolve(cached);\n\n if (!this.doc) return Promise.reject('doc closed');\n const page = this.doc.pages.find((p) => p.index === pageIdx)!;\n\n return new Promise((res, rej) => {\n this.engine.getPageGeometry(this.doc!, page).wait(\n (geo) => {\n this.dispatch(cachePageGeometry(pageIdx, geo));\n res(geo);\n },\n (e: TaskError<any>) => rej(e),\n );\n });\n }\n\n /* ── selection state updates ───────────────────────────── */\n private beginSelection(page: number, index: number) {\n this.selecting = true;\n this.anchor = { page, index };\n this.dispatch(startSelection());\n }\n\n private endSelection() {\n this.selecting = false;\n this.anchor = undefined;\n this.dispatch(endSelection());\n }\n\n private clearSelection() {\n this.selecting = false;\n this.anchor = undefined;\n this.dispatch(clearSelection());\n this.selChange$.emit(null);\n }\n\n private updateRectsForRange(range: SelectionRangeX) {\n for (let p = range.start.page; p <= range.end.page; p++) {\n const rects = this.buildRectsForPage(p); // existing pure fn\n this.dispatch(setRects(p, rects));\n }\n }\n\n private updateSelection(page: number, index: number) {\n if (!this.selecting || !this.anchor) return;\n\n const a = this.anchor;\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(range));\n this.updateRectsForRange(range);\n this.selChange$.emit(range);\n }\n\n /* ── rect builder: 1 div per run slice ─────────────────── */\n private buildRectsForPage(page: number): Rect[] {\n const sel = this.state.selection;\n if (!sel) return [];\n\n /* page not covered by the current selection */\n if (page < sel.start.page || page > sel.end.page) return [];\n\n const geo = this.state.geometry[page];\n if (!geo) return [];\n\n const from = page === sel.start.page ? sel.start.index : 0;\n const to =\n page === sel.end.page\n ? sel.end.index\n : geo.runs[geo.runs.length - 1].charStart + geo.runs[geo.runs.length - 1].glyphs.length - 1;\n\n const rects = [];\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 const left = run.glyphs[sIdx].x;\n const right = run.glyphs[eIdx].x + run.glyphs[eIdx].width;\n const top = run.glyphs[sIdx].y;\n const bottom = run.glyphs[eIdx].y + run.glyphs[eIdx].height;\n\n rects.push({\n origin: { x: left, y: top },\n size: { width: right - left, height: bottom - top },\n });\n }\n return rects;\n }\n}\n","import { Action } from '@embedpdf/core';\nimport { PdfPageGeometry, Rect } from '@embedpdf/models';\nimport { SelectionRangeX } from './types';\n\nexport const CACHE_PAGE_GEOMETRY = 'CACHE_PAGE_GEOMETRY';\nexport const SET_SELECTION = 'SET_SELECTION';\nexport const START_SELECTION = 'START_SELECTION';\nexport const END_SELECTION = 'END_SELECTION';\nexport const CLEAR_SELECTION = 'CLEAR_SELECTION';\nexport const SET_RECTS = 'SET_RECTS';\nexport const RESET = 'RESET';\n\nexport interface CachePageGeometryAction extends Action {\n type: typeof CACHE_PAGE_GEOMETRY;\n payload: { page: number; geo: PdfPageGeometry };\n}\nexport interface SetSelectionAction extends Action {\n type: typeof SET_SELECTION;\n payload: SelectionRangeX | null;\n}\n\nexport interface StartSelectionAction extends Action {\n type: typeof START_SELECTION;\n}\n\nexport interface EndSelectionAction extends Action {\n type: typeof END_SELECTION;\n}\n\nexport interface ClearSelectionAction extends Action {\n type: typeof CLEAR_SELECTION;\n}\n\nexport interface SetRectsAction extends Action {\n type: typeof SET_RECTS;\n payload: { page: number; rects: Rect[] };\n}\n\nexport interface ResetAction extends Action {\n type: typeof RESET;\n}\n\nexport type SelectionAction =\n | CachePageGeometryAction\n | SetSelectionAction\n | StartSelectionAction\n | EndSelectionAction\n | ClearSelectionAction\n | SetRectsAction\n | ResetAction;\n\nexport const cachePageGeometry = (page: number, geo: PdfPageGeometry): CachePageGeometryAction => ({\n type: CACHE_PAGE_GEOMETRY,\n payload: { page, geo },\n});\n\nexport const setSelection = (sel: SelectionRangeX): SetSelectionAction => ({\n type: SET_SELECTION,\n payload: sel,\n});\n\nexport const startSelection = (): StartSelectionAction => ({ type: START_SELECTION });\n\nexport const endSelection = (): EndSelectionAction => ({ type: END_SELECTION });\n\nexport const clearSelection = (): ClearSelectionAction => ({ type: CLEAR_SELECTION });\n\nexport const setRects = (page: number, rects: Rect[]): SetRectsAction => ({\n type: SET_RECTS,\n payload: { page, rects },\n});\n\nexport const reset = (): ResetAction => ({ type: RESET });\n","import { Rect, boundingRect } from '@embedpdf/models';\nimport { SelectionState } from './types';\n\nexport function selectRectsForPage(state: SelectionState, page: number) {\n return state.rects[page] ?? [];\n}\n\nexport function selectBoundingRectForPage(state: SelectionState, page: number) {\n return boundingRect(selectRectsForPage(state, page));\n}\n\nexport function selectBoundingRectsForAllPages(state: SelectionState) {\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","import { SelectionState } from './types';\nimport {\n SelectionAction,\n CACHE_PAGE_GEOMETRY,\n SET_SELECTION,\n START_SELECTION,\n END_SELECTION,\n SET_RECTS,\n CLEAR_SELECTION,\n RESET,\n} from './actions';\n\nexport const initialState: SelectionState = {\n geometry: {},\n rects: {},\n selection: null,\n active: false,\n selecting: false,\n};\n\nexport const selectionReducer = (state = initialState, action: SelectionAction): SelectionState => {\n switch (action.type) {\n case CACHE_PAGE_GEOMETRY: {\n const { page, geo } = action.payload;\n return { ...state, geometry: { ...state.geometry, [page]: geo } };\n }\n case SET_SELECTION:\n return { ...state, selection: action.payload, active: true };\n case START_SELECTION:\n return { ...state, selecting: true, selection: null, rects: {} };\n case END_SELECTION:\n return { ...state, selecting: false };\n case CLEAR_SELECTION:\n return { ...state, selecting: false, selection: null, rects: {}, active: false };\n case SET_RECTS:\n return { ...state, rects: { ...state.rects, [action.payload.page]: action.payload.rects } };\n case RESET:\n return initialState;\n default:\n return state;\n }\n};\n","import { PdfPageGeometry, Rect } from '@embedpdf/models';\n\n/**\n * Hit-test helper using runs\n * @param geo - page geometry\n * @param pt - point\n * @returns glyph index\n */\nexport function glyphAt(geo: PdfPageGeometry, pt: { x: number; y: number }) {\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 // Simply check if the point is within any glyph's bounding box\n const rel = run.glyphs.findIndex(\n (g) => pt.x >= g.x && pt.x <= g.x + g.width && pt.y >= g.y && pt.y <= g.y + g.height,\n );\n\n if (rel !== -1) {\n return run.charStart + rel;\n }\n }\n return -1;\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, engine) => new SelectionPlugin(SELECTION_PLUGIN_ID, registry, engine),\n reducer: selectionReducer,\n initialState,\n};\n\nexport * from './selection-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './utils';\n"],"mappings":"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","names":["import_core"]}
@@ -0,0 +1,113 @@
1
+ import { BasePluginConfig, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
2
+ import { PdfPageGeometry, Rect, PdfEngine } from '@embedpdf/models';
3
+
4
+ interface SelectionPluginConfig extends BasePluginConfig {
5
+ }
6
+ interface GlyphPointer {
7
+ page: number;
8
+ index: number;
9
+ }
10
+ interface SelectionRangeX {
11
+ start: GlyphPointer;
12
+ end: GlyphPointer;
13
+ }
14
+ interface SelectionState {
15
+ /** page → geometry cache */
16
+ geometry: Record<number, PdfPageGeometry>;
17
+ /** current selection or null */
18
+ rects: Record<number, Rect[]>;
19
+ selection: SelectionRangeX | null;
20
+ active: boolean;
21
+ selecting: boolean;
22
+ }
23
+ interface SelectionCapability {
24
+ getGeometry(page: number): Promise<PdfPageGeometry>;
25
+ getHighlightRects(page: number): Rect[];
26
+ getBoundingRect(page: number): Rect | null;
27
+ getBoundingRects(): {
28
+ page: number;
29
+ rect: Rect;
30
+ }[];
31
+ begin(page: number, glyphIdx: number): void;
32
+ update(page: number, glyphIdx: number): void;
33
+ end(): void;
34
+ clear(): void;
35
+ onSelectionChange(cb: (r: SelectionRangeX | null) => void): () => void;
36
+ }
37
+
38
+ declare const CACHE_PAGE_GEOMETRY = "CACHE_PAGE_GEOMETRY";
39
+ declare const SET_SELECTION = "SET_SELECTION";
40
+ declare const START_SELECTION = "START_SELECTION";
41
+ declare const END_SELECTION = "END_SELECTION";
42
+ declare const CLEAR_SELECTION = "CLEAR_SELECTION";
43
+ declare const SET_RECTS = "SET_RECTS";
44
+ declare const RESET = "RESET";
45
+ interface CachePageGeometryAction extends Action {
46
+ type: typeof CACHE_PAGE_GEOMETRY;
47
+ payload: {
48
+ page: number;
49
+ geo: PdfPageGeometry;
50
+ };
51
+ }
52
+ interface SetSelectionAction extends Action {
53
+ type: typeof SET_SELECTION;
54
+ payload: SelectionRangeX | null;
55
+ }
56
+ interface StartSelectionAction extends Action {
57
+ type: typeof START_SELECTION;
58
+ }
59
+ interface EndSelectionAction extends Action {
60
+ type: typeof END_SELECTION;
61
+ }
62
+ interface ClearSelectionAction extends Action {
63
+ type: typeof CLEAR_SELECTION;
64
+ }
65
+ interface SetRectsAction extends Action {
66
+ type: typeof SET_RECTS;
67
+ payload: {
68
+ page: number;
69
+ rects: Rect[];
70
+ };
71
+ }
72
+ interface ResetAction extends Action {
73
+ type: typeof RESET;
74
+ }
75
+ type SelectionAction = CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | ResetAction;
76
+
77
+ declare class SelectionPlugin extends BasePlugin<SelectionPluginConfig, SelectionCapability, SelectionState, SelectionAction> {
78
+ private engine;
79
+ static readonly id: "selection";
80
+ private doc?;
81
+ private selecting;
82
+ private anchor?;
83
+ private readonly selChange$;
84
+ constructor(id: string, registry: PluginRegistry, engine: PdfEngine);
85
+ initialize(): Promise<void>;
86
+ destroy(): Promise<void>;
87
+ buildCapability(): SelectionCapability;
88
+ private getOrLoadGeometry;
89
+ private beginSelection;
90
+ private endSelection;
91
+ private clearSelection;
92
+ private updateRectsForRange;
93
+ private updateSelection;
94
+ private buildRectsForPage;
95
+ }
96
+
97
+ declare const SELECTION_PLUGIN_ID = "selection";
98
+ declare const manifest: PluginManifest<SelectionPluginConfig>;
99
+
100
+ /**
101
+ * Hit-test helper using runs
102
+ * @param geo - page geometry
103
+ * @param pt - point
104
+ * @returns glyph index
105
+ */
106
+ declare function glyphAt(geo: PdfPageGeometry, pt: {
107
+ x: number;
108
+ y: number;
109
+ }): number;
110
+
111
+ declare const SelectionPluginPackage: PluginPackage<SelectionPlugin, SelectionPluginConfig, SelectionState, SelectionAction>;
112
+
113
+ export { type GlyphPointer, SELECTION_PLUGIN_ID, type SelectionCapability, SelectionPlugin, type SelectionPluginConfig, SelectionPluginPackage, type SelectionRangeX, type SelectionState, glyphAt, manifest };
@@ -0,0 +1,113 @@
1
+ import { BasePluginConfig, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
2
+ import { PdfPageGeometry, Rect, PdfEngine } from '@embedpdf/models';
3
+
4
+ interface SelectionPluginConfig extends BasePluginConfig {
5
+ }
6
+ interface GlyphPointer {
7
+ page: number;
8
+ index: number;
9
+ }
10
+ interface SelectionRangeX {
11
+ start: GlyphPointer;
12
+ end: GlyphPointer;
13
+ }
14
+ interface SelectionState {
15
+ /** page → geometry cache */
16
+ geometry: Record<number, PdfPageGeometry>;
17
+ /** current selection or null */
18
+ rects: Record<number, Rect[]>;
19
+ selection: SelectionRangeX | null;
20
+ active: boolean;
21
+ selecting: boolean;
22
+ }
23
+ interface SelectionCapability {
24
+ getGeometry(page: number): Promise<PdfPageGeometry>;
25
+ getHighlightRects(page: number): Rect[];
26
+ getBoundingRect(page: number): Rect | null;
27
+ getBoundingRects(): {
28
+ page: number;
29
+ rect: Rect;
30
+ }[];
31
+ begin(page: number, glyphIdx: number): void;
32
+ update(page: number, glyphIdx: number): void;
33
+ end(): void;
34
+ clear(): void;
35
+ onSelectionChange(cb: (r: SelectionRangeX | null) => void): () => void;
36
+ }
37
+
38
+ declare const CACHE_PAGE_GEOMETRY = "CACHE_PAGE_GEOMETRY";
39
+ declare const SET_SELECTION = "SET_SELECTION";
40
+ declare const START_SELECTION = "START_SELECTION";
41
+ declare const END_SELECTION = "END_SELECTION";
42
+ declare const CLEAR_SELECTION = "CLEAR_SELECTION";
43
+ declare const SET_RECTS = "SET_RECTS";
44
+ declare const RESET = "RESET";
45
+ interface CachePageGeometryAction extends Action {
46
+ type: typeof CACHE_PAGE_GEOMETRY;
47
+ payload: {
48
+ page: number;
49
+ geo: PdfPageGeometry;
50
+ };
51
+ }
52
+ interface SetSelectionAction extends Action {
53
+ type: typeof SET_SELECTION;
54
+ payload: SelectionRangeX | null;
55
+ }
56
+ interface StartSelectionAction extends Action {
57
+ type: typeof START_SELECTION;
58
+ }
59
+ interface EndSelectionAction extends Action {
60
+ type: typeof END_SELECTION;
61
+ }
62
+ interface ClearSelectionAction extends Action {
63
+ type: typeof CLEAR_SELECTION;
64
+ }
65
+ interface SetRectsAction extends Action {
66
+ type: typeof SET_RECTS;
67
+ payload: {
68
+ page: number;
69
+ rects: Rect[];
70
+ };
71
+ }
72
+ interface ResetAction extends Action {
73
+ type: typeof RESET;
74
+ }
75
+ type SelectionAction = CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | ResetAction;
76
+
77
+ declare class SelectionPlugin extends BasePlugin<SelectionPluginConfig, SelectionCapability, SelectionState, SelectionAction> {
78
+ private engine;
79
+ static readonly id: "selection";
80
+ private doc?;
81
+ private selecting;
82
+ private anchor?;
83
+ private readonly selChange$;
84
+ constructor(id: string, registry: PluginRegistry, engine: PdfEngine);
85
+ initialize(): Promise<void>;
86
+ destroy(): Promise<void>;
87
+ buildCapability(): SelectionCapability;
88
+ private getOrLoadGeometry;
89
+ private beginSelection;
90
+ private endSelection;
91
+ private clearSelection;
92
+ private updateRectsForRange;
93
+ private updateSelection;
94
+ private buildRectsForPage;
95
+ }
96
+
97
+ declare const SELECTION_PLUGIN_ID = "selection";
98
+ declare const manifest: PluginManifest<SelectionPluginConfig>;
99
+
100
+ /**
101
+ * Hit-test helper using runs
102
+ * @param geo - page geometry
103
+ * @param pt - point
104
+ * @returns glyph index
105
+ */
106
+ declare function glyphAt(geo: PdfPageGeometry, pt: {
107
+ x: number;
108
+ y: number;
109
+ }): number;
110
+
111
+ declare const SelectionPluginPackage: PluginPackage<SelectionPlugin, SelectionPluginConfig, SelectionState, SelectionAction>;
112
+
113
+ export { type GlyphPointer, SELECTION_PLUGIN_ID, type SelectionCapability, SelectionPlugin, type SelectionPluginConfig, SelectionPluginPackage, type SelectionRangeX, type SelectionState, glyphAt, manifest };
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ // src/lib/manifest.ts
2
+ var SELECTION_PLUGIN_ID = "selection";
3
+ var manifest = {
4
+ id: SELECTION_PLUGIN_ID,
5
+ name: "Selection Plugin",
6
+ version: "1.0.0",
7
+ provides: ["selection"],
8
+ requires: ["interaction-manager"],
9
+ optional: [],
10
+ defaultConfig: {
11
+ enabled: true
12
+ }
13
+ };
14
+
15
+ // src/lib/selection-plugin.ts
16
+ import { BasePlugin, SET_DOCUMENT } from "@embedpdf/core";
17
+
18
+ // src/lib/actions.ts
19
+ var CACHE_PAGE_GEOMETRY = "CACHE_PAGE_GEOMETRY";
20
+ var SET_SELECTION = "SET_SELECTION";
21
+ var START_SELECTION = "START_SELECTION";
22
+ var END_SELECTION = "END_SELECTION";
23
+ var CLEAR_SELECTION = "CLEAR_SELECTION";
24
+ var SET_RECTS = "SET_RECTS";
25
+ var RESET = "RESET";
26
+ var cachePageGeometry = (page, geo) => ({
27
+ type: CACHE_PAGE_GEOMETRY,
28
+ payload: { page, geo }
29
+ });
30
+ var setSelection = (sel) => ({
31
+ type: SET_SELECTION,
32
+ payload: sel
33
+ });
34
+ var startSelection = () => ({ type: START_SELECTION });
35
+ var endSelection = () => ({ type: END_SELECTION });
36
+ var clearSelection = () => ({ type: CLEAR_SELECTION });
37
+ var setRects = (page, rects) => ({
38
+ type: SET_RECTS,
39
+ payload: { page, rects }
40
+ });
41
+ var reset = () => ({ type: RESET });
42
+
43
+ // src/lib/selection-plugin.ts
44
+ import { createBehaviorEmitter } from "@embedpdf/core";
45
+
46
+ // src/lib/selectors.ts
47
+ import { boundingRect } from "@embedpdf/models";
48
+ function selectRectsForPage(state, page) {
49
+ return state.rects[page] ?? [];
50
+ }
51
+ function selectBoundingRectForPage(state, page) {
52
+ return boundingRect(selectRectsForPage(state, page));
53
+ }
54
+ function selectBoundingRectsForAllPages(state) {
55
+ const out = [];
56
+ const rectMap = state.rects;
57
+ for (const key in rectMap) {
58
+ const page = Number(key);
59
+ const bRect = boundingRect(rectMap[page]);
60
+ if (bRect) out.push({ page, rect: bRect });
61
+ }
62
+ return out;
63
+ }
64
+
65
+ // src/lib/selection-plugin.ts
66
+ var SelectionPlugin = class extends BasePlugin {
67
+ constructor(id, registry, engine) {
68
+ super(id, registry);
69
+ this.engine = engine;
70
+ /* interactive state */
71
+ this.selecting = false;
72
+ this.selChange$ = createBehaviorEmitter();
73
+ this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {
74
+ this.dispatch(reset());
75
+ this.doc = state.core.document ?? void 0;
76
+ });
77
+ }
78
+ /* ── life-cycle ────────────────────────────────────────── */
79
+ async initialize() {
80
+ }
81
+ async destroy() {
82
+ this.selChange$.clear();
83
+ }
84
+ /* ── capability exposed to UI / other plugins ─────────── */
85
+ buildCapability() {
86
+ return {
87
+ getGeometry: (p) => this.getOrLoadGeometry(p),
88
+ getHighlightRects: (p) => selectRectsForPage(this.state, p),
89
+ getBoundingRect: (p) => selectBoundingRectForPage(this.state, p),
90
+ getBoundingRects: () => selectBoundingRectsForAllPages(this.state),
91
+ begin: (p, i) => this.beginSelection(p, i),
92
+ update: (p, i) => this.updateSelection(p, i),
93
+ end: () => this.endSelection(),
94
+ clear: () => this.clearSelection(),
95
+ onSelectionChange: this.selChange$.on
96
+ };
97
+ }
98
+ /* ── geometry cache ───────────────────────────────────── */
99
+ getOrLoadGeometry(pageIdx) {
100
+ const cached = this.state.geometry[pageIdx];
101
+ if (cached) return Promise.resolve(cached);
102
+ if (!this.doc) return Promise.reject("doc closed");
103
+ const page = this.doc.pages.find((p) => p.index === pageIdx);
104
+ return new Promise((res, rej) => {
105
+ this.engine.getPageGeometry(this.doc, page).wait(
106
+ (geo) => {
107
+ this.dispatch(cachePageGeometry(pageIdx, geo));
108
+ res(geo);
109
+ },
110
+ (e) => rej(e)
111
+ );
112
+ });
113
+ }
114
+ /* ── selection state updates ───────────────────────────── */
115
+ beginSelection(page, index) {
116
+ this.selecting = true;
117
+ this.anchor = { page, index };
118
+ this.dispatch(startSelection());
119
+ }
120
+ endSelection() {
121
+ this.selecting = false;
122
+ this.anchor = void 0;
123
+ this.dispatch(endSelection());
124
+ }
125
+ clearSelection() {
126
+ this.selecting = false;
127
+ this.anchor = void 0;
128
+ this.dispatch(clearSelection());
129
+ this.selChange$.emit(null);
130
+ }
131
+ updateRectsForRange(range) {
132
+ for (let p = range.start.page; p <= range.end.page; p++) {
133
+ const rects = this.buildRectsForPage(p);
134
+ this.dispatch(setRects(p, rects));
135
+ }
136
+ }
137
+ updateSelection(page, index) {
138
+ if (!this.selecting || !this.anchor) return;
139
+ const a = this.anchor;
140
+ const forward = page > a.page || page === a.page && index >= a.index;
141
+ const start = forward ? a : { page, index };
142
+ const end = forward ? { page, index } : a;
143
+ const range = { start, end };
144
+ this.dispatch(setSelection(range));
145
+ this.updateRectsForRange(range);
146
+ this.selChange$.emit(range);
147
+ }
148
+ /* ── rect builder: 1 div per run slice ─────────────────── */
149
+ buildRectsForPage(page) {
150
+ const sel = this.state.selection;
151
+ if (!sel) return [];
152
+ if (page < sel.start.page || page > sel.end.page) return [];
153
+ const geo = this.state.geometry[page];
154
+ if (!geo) return [];
155
+ const from = page === sel.start.page ? sel.start.index : 0;
156
+ const to = page === sel.end.page ? sel.end.index : geo.runs[geo.runs.length - 1].charStart + geo.runs[geo.runs.length - 1].glyphs.length - 1;
157
+ const rects = [];
158
+ for (const run of geo.runs) {
159
+ const runStart = run.charStart;
160
+ const runEnd = runStart + run.glyphs.length - 1;
161
+ if (runEnd < from || runStart > to) continue;
162
+ const sIdx = Math.max(from, runStart) - runStart;
163
+ const eIdx = Math.min(to, runEnd) - runStart;
164
+ const left = run.glyphs[sIdx].x;
165
+ const right = run.glyphs[eIdx].x + run.glyphs[eIdx].width;
166
+ const top = run.glyphs[sIdx].y;
167
+ const bottom = run.glyphs[eIdx].y + run.glyphs[eIdx].height;
168
+ rects.push({
169
+ origin: { x: left, y: top },
170
+ size: { width: right - left, height: bottom - top }
171
+ });
172
+ }
173
+ return rects;
174
+ }
175
+ };
176
+ SelectionPlugin.id = "selection";
177
+
178
+ // src/lib/reducer.ts
179
+ var initialState = {
180
+ geometry: {},
181
+ rects: {},
182
+ selection: null,
183
+ active: false,
184
+ selecting: false
185
+ };
186
+ var selectionReducer = (state = initialState, action) => {
187
+ switch (action.type) {
188
+ case CACHE_PAGE_GEOMETRY: {
189
+ const { page, geo } = action.payload;
190
+ return { ...state, geometry: { ...state.geometry, [page]: geo } };
191
+ }
192
+ case SET_SELECTION:
193
+ return { ...state, selection: action.payload, active: true };
194
+ case START_SELECTION:
195
+ return { ...state, selecting: true, selection: null, rects: {} };
196
+ case END_SELECTION:
197
+ return { ...state, selecting: false };
198
+ case CLEAR_SELECTION:
199
+ return { ...state, selecting: false, selection: null, rects: {}, active: false };
200
+ case SET_RECTS:
201
+ return { ...state, rects: { ...state.rects, [action.payload.page]: action.payload.rects } };
202
+ case RESET:
203
+ return initialState;
204
+ default:
205
+ return state;
206
+ }
207
+ };
208
+
209
+ // src/lib/utils.ts
210
+ function glyphAt(geo, pt) {
211
+ for (const run of geo.runs) {
212
+ const inRun = pt.y >= run.rect.y && pt.y <= run.rect.y + run.rect.height && pt.x >= run.rect.x && pt.x <= run.rect.x + run.rect.width;
213
+ if (!inRun) continue;
214
+ const rel = run.glyphs.findIndex(
215
+ (g) => pt.x >= g.x && pt.x <= g.x + g.width && pt.y >= g.y && pt.y <= g.y + g.height
216
+ );
217
+ if (rel !== -1) {
218
+ return run.charStart + rel;
219
+ }
220
+ }
221
+ return -1;
222
+ }
223
+
224
+ // src/lib/index.ts
225
+ var SelectionPluginPackage = {
226
+ manifest,
227
+ create: (registry, engine) => new SelectionPlugin(SELECTION_PLUGIN_ID, registry, engine),
228
+ reducer: selectionReducer,
229
+ initialState
230
+ };
231
+ export {
232
+ SELECTION_PLUGIN_ID,
233
+ SelectionPlugin,
234
+ SelectionPluginPackage,
235
+ glyphAt,
236
+ manifest
237
+ };
238
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/manifest.ts","../src/lib/selection-plugin.ts","../src/lib/actions.ts","../src/lib/selectors.ts","../src/lib/reducer.ts","../src/lib/utils.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: [],\n defaultConfig: {\n enabled: true,\n },\n};\n","import { BasePlugin, PluginRegistry, SET_DOCUMENT } from '@embedpdf/core';\nimport {\n SelectionCapability,\n SelectionPluginConfig,\n SelectionRangeX,\n SelectionState,\n} from './types';\nimport {\n cachePageGeometry,\n setSelection,\n SelectionAction,\n endSelection,\n startSelection,\n setRects,\n clearSelection,\n reset,\n} from './actions';\nimport { PdfEngine, PdfDocumentObject, PdfPageGeometry, TaskError, Rect } from '@embedpdf/models';\nimport { createBehaviorEmitter } from '@embedpdf/core';\nimport * as selector from './selectors';\n\nexport class SelectionPlugin extends BasePlugin<\n SelectionPluginConfig,\n SelectionCapability,\n SelectionState,\n SelectionAction\n> {\n static readonly id = 'selection' as const;\n private doc?: PdfDocumentObject;\n\n /* interactive state */\n private selecting = false;\n private anchor?: { page: number; index: number };\n\n private readonly selChange$ = createBehaviorEmitter<SelectionState['selection']>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n private engine: PdfEngine,\n ) {\n super(id, registry);\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.dispatch(reset());\n this.doc = state.core.document ?? undefined;\n });\n }\n\n /* ── life-cycle ────────────────────────────────────────── */\n async initialize() {}\n async destroy() {\n this.selChange$.clear();\n }\n\n /* ── capability exposed to UI / other plugins ─────────── */\n buildCapability(): SelectionCapability {\n return {\n getGeometry: (p) => this.getOrLoadGeometry(p),\n getHighlightRects: (p) => selector.selectRectsForPage(this.state, p),\n getBoundingRect: (p) => selector.selectBoundingRectForPage(this.state, p),\n getBoundingRects: () => selector.selectBoundingRectsForAllPages(this.state),\n begin: (p, i) => this.beginSelection(p, i),\n update: (p, i) => this.updateSelection(p, i),\n end: () => this.endSelection(),\n clear: () => this.clearSelection(),\n\n onSelectionChange: this.selChange$.on,\n };\n }\n\n /* ── geometry cache ───────────────────────────────────── */\n private getOrLoadGeometry(pageIdx: number): Promise<PdfPageGeometry> {\n const cached = this.state.geometry[pageIdx];\n if (cached) return Promise.resolve(cached);\n\n if (!this.doc) return Promise.reject('doc closed');\n const page = this.doc.pages.find((p) => p.index === pageIdx)!;\n\n return new Promise((res, rej) => {\n this.engine.getPageGeometry(this.doc!, page).wait(\n (geo) => {\n this.dispatch(cachePageGeometry(pageIdx, geo));\n res(geo);\n },\n (e: TaskError<any>) => rej(e),\n );\n });\n }\n\n /* ── selection state updates ───────────────────────────── */\n private beginSelection(page: number, index: number) {\n this.selecting = true;\n this.anchor = { page, index };\n this.dispatch(startSelection());\n }\n\n private endSelection() {\n this.selecting = false;\n this.anchor = undefined;\n this.dispatch(endSelection());\n }\n\n private clearSelection() {\n this.selecting = false;\n this.anchor = undefined;\n this.dispatch(clearSelection());\n this.selChange$.emit(null);\n }\n\n private updateRectsForRange(range: SelectionRangeX) {\n for (let p = range.start.page; p <= range.end.page; p++) {\n const rects = this.buildRectsForPage(p); // existing pure fn\n this.dispatch(setRects(p, rects));\n }\n }\n\n private updateSelection(page: number, index: number) {\n if (!this.selecting || !this.anchor) return;\n\n const a = this.anchor;\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(range));\n this.updateRectsForRange(range);\n this.selChange$.emit(range);\n }\n\n /* ── rect builder: 1 div per run slice ─────────────────── */\n private buildRectsForPage(page: number): Rect[] {\n const sel = this.state.selection;\n if (!sel) return [];\n\n /* page not covered by the current selection */\n if (page < sel.start.page || page > sel.end.page) return [];\n\n const geo = this.state.geometry[page];\n if (!geo) return [];\n\n const from = page === sel.start.page ? sel.start.index : 0;\n const to =\n page === sel.end.page\n ? sel.end.index\n : geo.runs[geo.runs.length - 1].charStart + geo.runs[geo.runs.length - 1].glyphs.length - 1;\n\n const rects = [];\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 const left = run.glyphs[sIdx].x;\n const right = run.glyphs[eIdx].x + run.glyphs[eIdx].width;\n const top = run.glyphs[sIdx].y;\n const bottom = run.glyphs[eIdx].y + run.glyphs[eIdx].height;\n\n rects.push({\n origin: { x: left, y: top },\n size: { width: right - left, height: bottom - top },\n });\n }\n return rects;\n }\n}\n","import { Action } from '@embedpdf/core';\nimport { PdfPageGeometry, Rect } from '@embedpdf/models';\nimport { SelectionRangeX } from './types';\n\nexport const CACHE_PAGE_GEOMETRY = 'CACHE_PAGE_GEOMETRY';\nexport const SET_SELECTION = 'SET_SELECTION';\nexport const START_SELECTION = 'START_SELECTION';\nexport const END_SELECTION = 'END_SELECTION';\nexport const CLEAR_SELECTION = 'CLEAR_SELECTION';\nexport const SET_RECTS = 'SET_RECTS';\nexport const RESET = 'RESET';\n\nexport interface CachePageGeometryAction extends Action {\n type: typeof CACHE_PAGE_GEOMETRY;\n payload: { page: number; geo: PdfPageGeometry };\n}\nexport interface SetSelectionAction extends Action {\n type: typeof SET_SELECTION;\n payload: SelectionRangeX | null;\n}\n\nexport interface StartSelectionAction extends Action {\n type: typeof START_SELECTION;\n}\n\nexport interface EndSelectionAction extends Action {\n type: typeof END_SELECTION;\n}\n\nexport interface ClearSelectionAction extends Action {\n type: typeof CLEAR_SELECTION;\n}\n\nexport interface SetRectsAction extends Action {\n type: typeof SET_RECTS;\n payload: { page: number; rects: Rect[] };\n}\n\nexport interface ResetAction extends Action {\n type: typeof RESET;\n}\n\nexport type SelectionAction =\n | CachePageGeometryAction\n | SetSelectionAction\n | StartSelectionAction\n | EndSelectionAction\n | ClearSelectionAction\n | SetRectsAction\n | ResetAction;\n\nexport const cachePageGeometry = (page: number, geo: PdfPageGeometry): CachePageGeometryAction => ({\n type: CACHE_PAGE_GEOMETRY,\n payload: { page, geo },\n});\n\nexport const setSelection = (sel: SelectionRangeX): SetSelectionAction => ({\n type: SET_SELECTION,\n payload: sel,\n});\n\nexport const startSelection = (): StartSelectionAction => ({ type: START_SELECTION });\n\nexport const endSelection = (): EndSelectionAction => ({ type: END_SELECTION });\n\nexport const clearSelection = (): ClearSelectionAction => ({ type: CLEAR_SELECTION });\n\nexport const setRects = (page: number, rects: Rect[]): SetRectsAction => ({\n type: SET_RECTS,\n payload: { page, rects },\n});\n\nexport const reset = (): ResetAction => ({ type: RESET });\n","import { Rect, boundingRect } from '@embedpdf/models';\nimport { SelectionState } from './types';\n\nexport function selectRectsForPage(state: SelectionState, page: number) {\n return state.rects[page] ?? [];\n}\n\nexport function selectBoundingRectForPage(state: SelectionState, page: number) {\n return boundingRect(selectRectsForPage(state, page));\n}\n\nexport function selectBoundingRectsForAllPages(state: SelectionState) {\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","import { SelectionState } from './types';\nimport {\n SelectionAction,\n CACHE_PAGE_GEOMETRY,\n SET_SELECTION,\n START_SELECTION,\n END_SELECTION,\n SET_RECTS,\n CLEAR_SELECTION,\n RESET,\n} from './actions';\n\nexport const initialState: SelectionState = {\n geometry: {},\n rects: {},\n selection: null,\n active: false,\n selecting: false,\n};\n\nexport const selectionReducer = (state = initialState, action: SelectionAction): SelectionState => {\n switch (action.type) {\n case CACHE_PAGE_GEOMETRY: {\n const { page, geo } = action.payload;\n return { ...state, geometry: { ...state.geometry, [page]: geo } };\n }\n case SET_SELECTION:\n return { ...state, selection: action.payload, active: true };\n case START_SELECTION:\n return { ...state, selecting: true, selection: null, rects: {} };\n case END_SELECTION:\n return { ...state, selecting: false };\n case CLEAR_SELECTION:\n return { ...state, selecting: false, selection: null, rects: {}, active: false };\n case SET_RECTS:\n return { ...state, rects: { ...state.rects, [action.payload.page]: action.payload.rects } };\n case RESET:\n return initialState;\n default:\n return state;\n }\n};\n","import { PdfPageGeometry, Rect } from '@embedpdf/models';\n\n/**\n * Hit-test helper using runs\n * @param geo - page geometry\n * @param pt - point\n * @returns glyph index\n */\nexport function glyphAt(geo: PdfPageGeometry, pt: { x: number; y: number }) {\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 // Simply check if the point is within any glyph's bounding box\n const rel = run.glyphs.findIndex(\n (g) => pt.x >= g.x && pt.x <= g.x + g.width && pt.y >= g.y && pt.y <= g.y + g.height,\n );\n\n if (rel !== -1) {\n return run.charStart + rel;\n }\n }\n return -1;\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, engine) => new SelectionPlugin(SELECTION_PLUGIN_ID, registry, engine),\n reducer: selectionReducer,\n initialState,\n};\n\nexport * from './selection-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './utils';\n"],"mappings":"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","names":[]}
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/preact/index.ts
21
+ var preact_exports = {};
22
+ __export(preact_exports, {
23
+ SelectionLayer: () => SelectionLayer,
24
+ useSelection: () => useSelection,
25
+ useSelectionCapability: () => useSelectionCapability
26
+ });
27
+ module.exports = __toCommonJS(preact_exports);
28
+
29
+ // src/preact/hooks/use-selection.ts
30
+ var import_preact = require("@embedpdf/core/preact");
31
+ var import_plugin_selection = require("@embedpdf/plugin-selection");
32
+ var useSelectionCapability = () => (0, import_preact.useCapability)(import_plugin_selection.SelectionPlugin.id);
33
+ var useSelection = () => (0, import_preact.usePlugin)(import_plugin_selection.SelectionPlugin.id);
34
+
35
+ // src/preact/components/selection-layer.tsx
36
+ var import_hooks = require("preact/hooks");
37
+ var import_plugin_selection2 = require("@embedpdf/plugin-selection");
38
+ var import_preact2 = require("@embedpdf/plugin-interaction-manager/preact");
39
+ var import_jsx_runtime = require("preact/jsx-runtime");
40
+ function SelectionLayer({ pageIndex, scale }) {
41
+ const { provides: sel } = useSelectionCapability();
42
+ const { register } = (0, import_preact2.usePointerHandlers)({ modeId: "default", pageIndex });
43
+ const [rects, setRects] = (0, import_hooks.useState)([]);
44
+ const { setCursor, removeCursor } = (0, import_preact2.useCursor)();
45
+ (0, import_hooks.useEffect)(() => {
46
+ if (!sel) return;
47
+ return sel.onSelectionChange(() => {
48
+ setRects(sel.getHighlightRects(pageIndex));
49
+ });
50
+ }, [sel, pageIndex]);
51
+ let geoCache;
52
+ const cachedGlyphAt = (0, import_hooks.useCallback)((pt) => {
53
+ if (!geoCache) return -1;
54
+ return (0, import_plugin_selection2.glyphAt)(geoCache, pt);
55
+ }, []);
56
+ (0, import_hooks.useEffect)(() => {
57
+ if (!sel) return;
58
+ sel.getGeometry(pageIndex).then((g) => geoCache = g);
59
+ }, [sel, pageIndex]);
60
+ const handlers = (0, import_hooks.useMemo)(
61
+ () => ({
62
+ onPointerDown: (point) => {
63
+ if (!sel) return;
64
+ sel.clear();
65
+ sel.getGeometry(pageIndex).then((geo) => {
66
+ const g = (0, import_plugin_selection2.glyphAt)(geo, point);
67
+ if (g !== -1) sel.begin(pageIndex, g);
68
+ });
69
+ },
70
+ onPointerMove: (point) => {
71
+ if (!sel) return;
72
+ const g = cachedGlyphAt(point);
73
+ if (g !== -1) {
74
+ setCursor("selection-text", "text", 10);
75
+ } else {
76
+ removeCursor("selection-text");
77
+ }
78
+ if (g !== -1) sel.update(pageIndex, g);
79
+ },
80
+ onPointerUp: () => {
81
+ if (!sel) return;
82
+ sel.end();
83
+ }
84
+ }),
85
+ [sel, pageIndex, cachedGlyphAt]
86
+ );
87
+ (0, import_hooks.useEffect)(() => {
88
+ if (!register) return;
89
+ return register(handlers);
90
+ }, [register, handlers]);
91
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: rects.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
92
+ "div",
93
+ {
94
+ style: {
95
+ position: "absolute",
96
+ left: b.origin.x * scale,
97
+ top: b.origin.y * scale,
98
+ width: b.size.width * scale,
99
+ height: b.size.height * scale,
100
+ background: "rgba(33,150,243)",
101
+ pointerEvents: "none"
102
+ }
103
+ },
104
+ i
105
+ )) });
106
+ }
107
+ // Annotate the CommonJS export names for ESM import in node:
108
+ 0 && (module.exports = {
109
+ SelectionLayer,
110
+ useSelection,
111
+ useSelectionCapability
112
+ });
113
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/preact/index.ts","../../src/preact/hooks/use-selection.ts","../../src/preact/components/selection-layer.tsx"],"sourcesContent":["export * from './hooks';\nexport * from './components';\n","import { useCapability, usePlugin } from '@embedpdf/core/preact';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelection = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","/** @jsxImportSource preact */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport { useSelectionCapability } from '../hooks';\nimport { glyphAt } from '@embedpdf/plugin-selection';\nimport { PdfPageGeometry, Rect } from '@embedpdf/models';\nimport { useCursor, usePointerHandlers } from '@embedpdf/plugin-interaction-manager/preact';\nimport { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\n\ntype Props = { pageIndex: number; scale: number };\n\nexport function SelectionLayer({ pageIndex, scale }: Props) {\n const { provides: sel } = useSelectionCapability();\n const { register } = usePointerHandlers({ modeId: 'default', pageIndex });\n const [rects, setRects] = useState<Array<Rect>>([]);\n const { setCursor, removeCursor } = useCursor();\n\n /* subscribe to rect updates */\n useEffect(() => {\n if (!sel) return;\n return sel.onSelectionChange(() => {\n setRects(sel.getHighlightRects(pageIndex));\n });\n }, [sel, pageIndex]);\n\n /* cheap glyphAt cache for the active page */\n let geoCache: PdfPageGeometry | undefined;\n const cachedGlyphAt = useCallback((pt: { x: number; y: number }) => {\n if (!geoCache) return -1;\n return glyphAt(geoCache, pt);\n }, []);\n\n // Initialize geometry cache\n useEffect(() => {\n if (!sel) return;\n sel.getGeometry(pageIndex).then((g) => (geoCache = g));\n }, [sel, pageIndex]);\n\n const handlers = useMemo(\n (): PointerEventHandlers => ({\n onPointerDown: (point) => {\n if (!sel) return;\n\n // clear the selection\n sel.clear();\n sel.getGeometry(pageIndex).then((geo) => {\n const g = glyphAt(geo, point);\n if (g !== -1) sel.begin(pageIndex, g);\n });\n },\n onPointerMove: (point) => {\n if (!sel) return;\n const g = cachedGlyphAt(point);\n if (g !== -1) {\n setCursor('selection-text', 'text', 10);\n } else {\n removeCursor('selection-text');\n }\n if (g !== -1) sel.update(pageIndex, g);\n },\n onPointerUp: () => {\n if (!sel) return;\n sel.end();\n },\n }),\n [sel, pageIndex, cachedGlyphAt],\n );\n\n useEffect(() => {\n if (!register) return;\n return register(handlers);\n }, [register, handlers]);\n\n return (\n <>\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: b.origin.x * scale,\n top: b.origin.y * scale,\n width: b.size.width * scale,\n height: b.size.height * scale,\n background: 'rgba(33,150,243)',\n pointerEvents: 'none',\n }}\n />\n ))}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAyC;AACzC,8BAAgC;AAEzB,IAAM,yBAAyB,UAAM,6BAA+B,wCAAgB,EAAE;AACtF,IAAM,eAAe,UAAM,yBAA2B,wCAAgB,EAAE;;;ACH/E,mBAAkE;AAElE,IAAAA,2BAAwB;AAExB,IAAAC,iBAA8C;AAoE1C;AA/DG,SAAS,eAAe,EAAE,WAAW,MAAM,GAAU;AAC1D,QAAM,EAAE,UAAU,IAAI,IAAI,uBAAuB;AACjD,QAAM,EAAE,SAAS,QAAI,mCAAmB,EAAE,QAAQ,WAAW,UAAU,CAAC;AACxE,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAsB,CAAC,CAAC;AAClD,QAAM,EAAE,WAAW,aAAa,QAAI,0BAAU;AAG9C,8BAAU,MAAM;AACd,QAAI,CAAC,IAAK;AACV,WAAO,IAAI,kBAAkB,MAAM;AACjC,eAAS,IAAI,kBAAkB,SAAS,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH,GAAG,CAAC,KAAK,SAAS,CAAC;AAGnB,MAAI;AACJ,QAAM,oBAAgB,0BAAY,CAAC,OAAiC;AAClE,QAAI,CAAC,SAAU,QAAO;AACtB,eAAO,kCAAQ,UAAU,EAAE;AAAA,EAC7B,GAAG,CAAC,CAAC;AAGL,8BAAU,MAAM;AACd,QAAI,CAAC,IAAK;AACV,QAAI,YAAY,SAAS,EAAE,KAAK,CAAC,MAAO,WAAW,CAAE;AAAA,EACvD,GAAG,CAAC,KAAK,SAAS,CAAC;AAEnB,QAAM,eAAW;AAAA,IACf,OAA6B;AAAA,MAC3B,eAAe,CAAC,UAAU;AACxB,YAAI,CAAC,IAAK;AAGV,YAAI,MAAM;AACV,YAAI,YAAY,SAAS,EAAE,KAAK,CAAC,QAAQ;AACvC,gBAAM,QAAI,kCAAQ,KAAK,KAAK;AAC5B,cAAI,MAAM,GAAI,KAAI,MAAM,WAAW,CAAC;AAAA,QACtC,CAAC;AAAA,MACH;AAAA,MACA,eAAe,CAAC,UAAU;AACxB,YAAI,CAAC,IAAK;AACV,cAAM,IAAI,cAAc,KAAK;AAC7B,YAAI,MAAM,IAAI;AACZ,oBAAU,kBAAkB,QAAQ,EAAE;AAAA,QACxC,OAAO;AACL,uBAAa,gBAAgB;AAAA,QAC/B;AACA,YAAI,MAAM,GAAI,KAAI,OAAO,WAAW,CAAC;AAAA,MACvC;AAAA,MACA,aAAa,MAAM;AACjB,YAAI,CAAC,IAAK;AACV,YAAI,IAAI;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC,KAAK,WAAW,aAAa;AAAA,EAChC;AAEA,8BAAU,MAAM;AACd,QAAI,CAAC,SAAU;AACf,WAAO,SAAS,QAAQ;AAAA,EAC1B,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,SACE,2EACG,gBAAM,IAAI,CAAC,GAAG,MACb;AAAA,IAAC;AAAA;AAAA,MAEC,OAAO;AAAA,QACL,UAAU;AAAA,QACV,MAAM,EAAE,OAAO,IAAI;AAAA,QACnB,KAAK,EAAE,OAAO,IAAI;AAAA,QAClB,OAAO,EAAE,KAAK,QAAQ;AAAA,QACtB,QAAQ,EAAE,KAAK,SAAS;AAAA,QACxB,YAAY;AAAA,QACZ,eAAe;AAAA,MACjB;AAAA;AAAA,IATK;AAAA,EAUP,CACD,GACH;AAEJ;","names":["import_plugin_selection","import_preact"]}
@@ -0,0 +1,22 @@
1
+ import * as _embedpdf_plugin_selection from '@embedpdf/plugin-selection';
2
+ import { SelectionPlugin } from '@embedpdf/plugin-selection';
3
+ import * as preact from 'preact';
4
+
5
+ declare const useSelectionCapability: () => {
6
+ provides: Readonly<_embedpdf_plugin_selection.SelectionCapability> | null;
7
+ isLoading: boolean;
8
+ ready: Promise<void>;
9
+ };
10
+ declare const useSelection: () => {
11
+ plugin: SelectionPlugin | null;
12
+ isLoading: boolean;
13
+ ready: Promise<void>;
14
+ };
15
+
16
+ type Props = {
17
+ pageIndex: number;
18
+ scale: number;
19
+ };
20
+ declare function SelectionLayer({ pageIndex, scale }: Props): preact.JSX.Element;
21
+
22
+ export { SelectionLayer, useSelection, useSelectionCapability };
@@ -0,0 +1,22 @@
1
+ import * as _embedpdf_plugin_selection from '@embedpdf/plugin-selection';
2
+ import { SelectionPlugin } from '@embedpdf/plugin-selection';
3
+ import * as preact from 'preact';
4
+
5
+ declare const useSelectionCapability: () => {
6
+ provides: Readonly<_embedpdf_plugin_selection.SelectionCapability> | null;
7
+ isLoading: boolean;
8
+ ready: Promise<void>;
9
+ };
10
+ declare const useSelection: () => {
11
+ plugin: SelectionPlugin | null;
12
+ isLoading: boolean;
13
+ ready: Promise<void>;
14
+ };
15
+
16
+ type Props = {
17
+ pageIndex: number;
18
+ scale: number;
19
+ };
20
+ declare function SelectionLayer({ pageIndex, scale }: Props): preact.JSX.Element;
21
+
22
+ export { SelectionLayer, useSelection, useSelectionCapability };
@@ -0,0 +1,84 @@
1
+ // src/preact/hooks/use-selection.ts
2
+ import { useCapability, usePlugin } from "@embedpdf/core/preact";
3
+ import { SelectionPlugin } from "@embedpdf/plugin-selection";
4
+ var useSelectionCapability = () => useCapability(SelectionPlugin.id);
5
+ var useSelection = () => usePlugin(SelectionPlugin.id);
6
+
7
+ // src/preact/components/selection-layer.tsx
8
+ import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
9
+ import { glyphAt } from "@embedpdf/plugin-selection";
10
+ import { useCursor, usePointerHandlers } from "@embedpdf/plugin-interaction-manager/preact";
11
+ import { Fragment, jsx } from "preact/jsx-runtime";
12
+ function SelectionLayer({ pageIndex, scale }) {
13
+ const { provides: sel } = useSelectionCapability();
14
+ const { register } = usePointerHandlers({ modeId: "default", pageIndex });
15
+ const [rects, setRects] = useState([]);
16
+ const { setCursor, removeCursor } = useCursor();
17
+ useEffect(() => {
18
+ if (!sel) return;
19
+ return sel.onSelectionChange(() => {
20
+ setRects(sel.getHighlightRects(pageIndex));
21
+ });
22
+ }, [sel, pageIndex]);
23
+ let geoCache;
24
+ const cachedGlyphAt = useCallback((pt) => {
25
+ if (!geoCache) return -1;
26
+ return glyphAt(geoCache, pt);
27
+ }, []);
28
+ useEffect(() => {
29
+ if (!sel) return;
30
+ sel.getGeometry(pageIndex).then((g) => geoCache = g);
31
+ }, [sel, pageIndex]);
32
+ const handlers = useMemo(
33
+ () => ({
34
+ onPointerDown: (point) => {
35
+ if (!sel) return;
36
+ sel.clear();
37
+ sel.getGeometry(pageIndex).then((geo) => {
38
+ const g = glyphAt(geo, point);
39
+ if (g !== -1) sel.begin(pageIndex, g);
40
+ });
41
+ },
42
+ onPointerMove: (point) => {
43
+ if (!sel) return;
44
+ const g = cachedGlyphAt(point);
45
+ if (g !== -1) {
46
+ setCursor("selection-text", "text", 10);
47
+ } else {
48
+ removeCursor("selection-text");
49
+ }
50
+ if (g !== -1) sel.update(pageIndex, g);
51
+ },
52
+ onPointerUp: () => {
53
+ if (!sel) return;
54
+ sel.end();
55
+ }
56
+ }),
57
+ [sel, pageIndex, cachedGlyphAt]
58
+ );
59
+ useEffect(() => {
60
+ if (!register) return;
61
+ return register(handlers);
62
+ }, [register, handlers]);
63
+ return /* @__PURE__ */ jsx(Fragment, { children: rects.map((b, i) => /* @__PURE__ */ jsx(
64
+ "div",
65
+ {
66
+ style: {
67
+ position: "absolute",
68
+ left: b.origin.x * scale,
69
+ top: b.origin.y * scale,
70
+ width: b.size.width * scale,
71
+ height: b.size.height * scale,
72
+ background: "rgba(33,150,243)",
73
+ pointerEvents: "none"
74
+ }
75
+ },
76
+ i
77
+ )) });
78
+ }
79
+ export {
80
+ SelectionLayer,
81
+ useSelection,
82
+ useSelectionCapability
83
+ };
84
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/preact/hooks/use-selection.ts","../../src/preact/components/selection-layer.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/preact';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelection = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","/** @jsxImportSource preact */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport { useSelectionCapability } from '../hooks';\nimport { glyphAt } from '@embedpdf/plugin-selection';\nimport { PdfPageGeometry, Rect } from '@embedpdf/models';\nimport { useCursor, usePointerHandlers } from '@embedpdf/plugin-interaction-manager/preact';\nimport { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\n\ntype Props = { pageIndex: number; scale: number };\n\nexport function SelectionLayer({ pageIndex, scale }: Props) {\n const { provides: sel } = useSelectionCapability();\n const { register } = usePointerHandlers({ modeId: 'default', pageIndex });\n const [rects, setRects] = useState<Array<Rect>>([]);\n const { setCursor, removeCursor } = useCursor();\n\n /* subscribe to rect updates */\n useEffect(() => {\n if (!sel) return;\n return sel.onSelectionChange(() => {\n setRects(sel.getHighlightRects(pageIndex));\n });\n }, [sel, pageIndex]);\n\n /* cheap glyphAt cache for the active page */\n let geoCache: PdfPageGeometry | undefined;\n const cachedGlyphAt = useCallback((pt: { x: number; y: number }) => {\n if (!geoCache) return -1;\n return glyphAt(geoCache, pt);\n }, []);\n\n // Initialize geometry cache\n useEffect(() => {\n if (!sel) return;\n sel.getGeometry(pageIndex).then((g) => (geoCache = g));\n }, [sel, pageIndex]);\n\n const handlers = useMemo(\n (): PointerEventHandlers => ({\n onPointerDown: (point) => {\n if (!sel) return;\n\n // clear the selection\n sel.clear();\n sel.getGeometry(pageIndex).then((geo) => {\n const g = glyphAt(geo, point);\n if (g !== -1) sel.begin(pageIndex, g);\n });\n },\n onPointerMove: (point) => {\n if (!sel) return;\n const g = cachedGlyphAt(point);\n if (g !== -1) {\n setCursor('selection-text', 'text', 10);\n } else {\n removeCursor('selection-text');\n }\n if (g !== -1) sel.update(pageIndex, g);\n },\n onPointerUp: () => {\n if (!sel) return;\n sel.end();\n },\n }),\n [sel, pageIndex, cachedGlyphAt],\n );\n\n useEffect(() => {\n if (!register) return;\n return register(handlers);\n }, [register, handlers]);\n\n return (\n <>\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: b.origin.x * scale,\n top: b.origin.y * scale,\n width: b.size.width * scale,\n height: b.size.height * scale,\n background: 'rgba(33,150,243)',\n pointerEvents: 'none',\n }}\n />\n ))}\n </>\n );\n}\n"],"mappings":";AAAA,SAAS,eAAe,iBAAiB;AACzC,SAAS,uBAAuB;AAEzB,IAAM,yBAAyB,MAAM,cAA+B,gBAAgB,EAAE;AACtF,IAAM,eAAe,MAAM,UAA2B,gBAAgB,EAAE;;;ACH/E,SAAS,aAAa,WAAW,SAAiB,gBAAgB;AAElE,SAAS,eAAe;AAExB,SAAS,WAAW,0BAA0B;AAoE1C,mBAEI,WAFJ;AA/DG,SAAS,eAAe,EAAE,WAAW,MAAM,GAAU;AAC1D,QAAM,EAAE,UAAU,IAAI,IAAI,uBAAuB;AACjD,QAAM,EAAE,SAAS,IAAI,mBAAmB,EAAE,QAAQ,WAAW,UAAU,CAAC;AACxE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAsB,CAAC,CAAC;AAClD,QAAM,EAAE,WAAW,aAAa,IAAI,UAAU;AAG9C,YAAU,MAAM;AACd,QAAI,CAAC,IAAK;AACV,WAAO,IAAI,kBAAkB,MAAM;AACjC,eAAS,IAAI,kBAAkB,SAAS,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH,GAAG,CAAC,KAAK,SAAS,CAAC;AAGnB,MAAI;AACJ,QAAM,gBAAgB,YAAY,CAAC,OAAiC;AAClE,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO,QAAQ,UAAU,EAAE;AAAA,EAC7B,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,CAAC,IAAK;AACV,QAAI,YAAY,SAAS,EAAE,KAAK,CAAC,MAAO,WAAW,CAAE;AAAA,EACvD,GAAG,CAAC,KAAK,SAAS,CAAC;AAEnB,QAAM,WAAW;AAAA,IACf,OAA6B;AAAA,MAC3B,eAAe,CAAC,UAAU;AACxB,YAAI,CAAC,IAAK;AAGV,YAAI,MAAM;AACV,YAAI,YAAY,SAAS,EAAE,KAAK,CAAC,QAAQ;AACvC,gBAAM,IAAI,QAAQ,KAAK,KAAK;AAC5B,cAAI,MAAM,GAAI,KAAI,MAAM,WAAW,CAAC;AAAA,QACtC,CAAC;AAAA,MACH;AAAA,MACA,eAAe,CAAC,UAAU;AACxB,YAAI,CAAC,IAAK;AACV,cAAM,IAAI,cAAc,KAAK;AAC7B,YAAI,MAAM,IAAI;AACZ,oBAAU,kBAAkB,QAAQ,EAAE;AAAA,QACxC,OAAO;AACL,uBAAa,gBAAgB;AAAA,QAC/B;AACA,YAAI,MAAM,GAAI,KAAI,OAAO,WAAW,CAAC;AAAA,MACvC;AAAA,MACA,aAAa,MAAM;AACjB,YAAI,CAAC,IAAK;AACV,YAAI,IAAI;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC,KAAK,WAAW,aAAa;AAAA,EAChC;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,SAAU;AACf,WAAO,SAAS,QAAQ;AAAA,EAC1B,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,SACE,gCACG,gBAAM,IAAI,CAAC,GAAG,MACb;AAAA,IAAC;AAAA;AAAA,MAEC,OAAO;AAAA,QACL,UAAU;AAAA,QACV,MAAM,EAAE,OAAO,IAAI;AAAA,QACnB,KAAK,EAAE,OAAO,IAAI;AAAA,QAClB,OAAO,EAAE,KAAK,QAAQ;AAAA,QACtB,QAAQ,EAAE,KAAK,SAAS;AAAA,QACxB,YAAY;AAAA,QACZ,eAAe;AAAA,MACjB;AAAA;AAAA,IATK;AAAA,EAUP,CACD,GACH;AAEJ;","names":[]}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@embedpdf/plugin-selection",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ },
14
+ "./preact": {
15
+ "types": "./dist/preact/index.d.ts",
16
+ "import": "./dist/preact/index.js",
17
+ "require": "./dist/preact/index.cjs"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "@embedpdf/models": "1.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/react": "^18.2.0",
25
+ "tsup": "^8.0.0",
26
+ "typescript": "^5.0.0",
27
+ "@embedpdf/plugin-viewport": "1.0.0",
28
+ "@embedpdf/plugin-interaction-manager": "1.0.0"
29
+ },
30
+ "peerDependencies": {
31
+ "react": ">=16.8.0",
32
+ "react-dom": ">=16.8.0",
33
+ "preact": "^10.26.4",
34
+ "@embedpdf/core": "1.0.0",
35
+ "@embedpdf/plugin-viewport": "1.0.0",
36
+ "@embedpdf/plugin-interaction-manager": "1.0.0"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/embedpdf/embed-pdf-viewer",
45
+ "directory": "packages/plugin-selection"
46
+ },
47
+ "homepage": "https://www.embedpdf.com/docs",
48
+ "bugs": {
49
+ "url": "https://github.com/embedpdf/embed-pdf-viewer/issues"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "scripts": {
55
+ "build": "PROJECT_CWD=$(pwd) pnpm -w p:build",
56
+ "build:watch": "PROJECT_CWD=$(pwd) pnpm -w p:build:watch",
57
+ "clean": "PROJECT_CWD=$(pwd) pnpm -w p:clean",
58
+ "lint": "PROJECT_CWD=$(pwd) pnpm -w p:lint",
59
+ "lint:fix": "PROJECT_CWD=$(pwd) pnpm -w p:lint:fix",
60
+ "typecheck": "PROJECT_CWD=$(pwd) pnpm -w p:typecheck"
61
+ }
62
+ }